wip: profile edit

This commit is contained in:
Nuwan 2024-01-14 13:32:15 +05:30
parent 4accb2296f
commit b563f22a32
6 changed files with 704 additions and 0 deletions

View File

@ -32,6 +32,8 @@ import JKMusicSessions from '../page/JKMusicSessions';
import JKNewMusicSession from '../page/JKNewMusicSession';
import JKMusicSessionsLobby from '../page/JKMusicSessionsLobby';
import JKEditProfile from '../page/JKEditProfile';
//import loadable from '@loadable/component';
//const DashboardRoutes = loadable(() => import('../../layouts/JKDashboardRoutes'));
//const PublicRoutes = loadable(() => import('../../layouts/JKPublicRoutes'))
@ -195,6 +197,7 @@ function JKDashboardMain() {
<PrivateRoute path="/sessions/lobby" component={JKMusicSessionsLobby} />
<PrivateRoute path="/sessions" component={JKMusicSessions} />
<PrivateRoute path="/notifications" component={JKNotifications} />
<PrivateRoute path="/profile" component={JKEditProfile} />
{/*Redirect*/}
<Redirect to="/errors/404" />

View File

@ -0,0 +1,654 @@
import React, { useRef, useEffect, useState, useReducer } from 'react';
import { Card, CardBody, Col, Row, CardHeader, Form, FormGroup, Label, Input, Button } from 'reactstrap';
import Select from 'react-select';
import FalconCardHeader from '../common/FalconCardHeader';
import { useTranslation } from 'react-i18next';
import JKCurrentUserAvatar from '../navbar/JKCurrentUserAvatar';
import { useAuth } from '../../context/UserAuth';
import { useForm, Controller } from 'react-hook-form';
import {
getPersonById,
getInstruments,
getGenres,
updateUser,
getCountries,
getRegions,
getCities
} from '../../helpers/rest';
import { set } from 'lodash';
function JKEditProfile() {
const { t } = useTranslation();
const { currentUser } = useAuth();
const [musicInstruments, setMusicInstruments] = useState([]);
const [genres, setGenres] = useState([]);
const [instrumentsInitialLoadingDone, setInstrumentsInitialLoadingDone] = useState(false);
const [genreInitialLoadingDone, setGenreInitialLoadingDone] = useState(false);
const [countries, setCountries] = useState([]);
const [regions, setRegions] = useState([]);
const [cities, setCities] = useState([]);
const [_, forceUpdate] = useReducer(x => x + 1, 0);
const saveTimeoutRef = useRef(null);
const cityRef = useRef(null);
const regionRef = useRef(null);
const PROFICIENCIES = [
{ value: '1', label: 'Beginner' },
{ value: '2', label: 'Intermediate' },
{ value: '3', label: 'Advanced' }
];
const { register, control, handleSubmit, setValue, getValues } = useForm({
defaultValues: {
firstName: '',
lastName: '',
country: '',
state: '',
city: '',
biography: '',
subscribeEmail: false,
virtualBand: false,
traditionalBand: false,
cowriting: false,
instruments: [],
genres: []
}
});
useEffect(() => {
if (currentUser) {
fetchCurentUser().then(userData => {
console.log('_DEBUG userData', userData);
fetchInstruments();
fetchGenres();
fetchCountries();
});
}
}, [currentUser]);
const fetchCurentUser = () => {
return new Promise((resolve, reject) => {
getPersonById(currentUser.id)
.then(response => {
if (response.ok) {
return response.json();
}
})
.then(data => {
setValue('firstName', data.first_name);
setValue('lastName', data.last_name);
setValue('country', data.country);
setValue('state', data.state);
setValue('city', data.city);
setValue('biography', data.biography);
setValue('subscribeEmail', data.subscribe_email);
setValue('virtualBand', data.virtual_band);
setValue('traditionalBand', data.traditional_band);
setValue('cowriting', data.cowriting);
setValue('instruments', data.instruments);
setValue('genres', data.genres);
if (data.country) {
fetchRegions(data.country);
}
if (data.country && data.state) {
fetchCities(data.country, data.state);
}
resolve(getValues());
})
.catch(error => reject(error));
});
};
const fetchInstruments = () => {
getInstruments()
.then(response => {
if (response.ok) {
return response.json();
}
})
.then(data => {
setMusicInstruments(data);
})
.catch(error => console.log(error));
};
useEffect(() => {
if (!musicInstruments.length || !getValues('instruments') || instrumentsInitialLoadingDone) return;
const updatedMusicInstruments = musicInstruments.map(musicInstrument => {
const instrument = getValues('instruments').find(instrument => instrument.instrument_id === musicInstrument.id);
if (instrument) {
musicInstrument.proficiency_level = instrument.proficiency_level;
musicInstrument.checked = true;
musicInstrument.instrument_id = instrument.instrument_id;
} else {
musicInstrument.proficiency_level = null;
musicInstrument.checked = false;
musicInstrument.instrument_id = null;
}
return musicInstrument;
});
setMusicInstruments(updatedMusicInstruments);
setInstrumentsInitialLoadingDone(true);
}, [musicInstruments, getValues('instruments')]);
const fetchGenres = () => {
getGenres()
.then(response => {
if (response.ok) {
return response.json();
}
})
.then(data => {
setGenres(data);
})
.catch(error => {
console.log(error);
});
};
useEffect(() => {
if (!genres.length || !getValues('genres') || genreInitialLoadingDone) return;
const updatedGenres = genres.map(genre => {
const userGenre = getValues('genres').find(userGenre => userGenre.genre_id === genre.id);
if (userGenre) {
genre.checked = true;
} else {
genre.checked = false;
}
genre.genre_id = genre.id;
return genre;
});
setGenres(updatedGenres);
setGenreInitialLoadingDone(true);
}, [genres, getValues('genres')]);
const fetchCountries = () => {
getCountries()
.then(response => {
if (response.ok) {
return response.json();
}
})
.then(data => {
setCountries(data.countriesx);
})
.catch(error => console.log(error));
};
const fetchRegions = countryCode => {
getRegions(countryCode)
.then(response => {
if (response.ok) {
return response.json();
}
})
.then(data => {
setRegions(data.regions);
})
.catch(error => console.log(error));
};
const fetchCities = (country, region) => {
getCities(country, region)
.then(response => {
if (response.ok) {
return response.json();
}
})
.then(data => {
setCities(data.cities);
})
.catch(error => console.log(error));
};
const onSubmit = data => console.log(data);
const handleInstrumentSelect = (e, musicInstrument) => {
//alert(e.target.checked)
if (e.target.checked) {
const userInstruments = getValues('instruments');
const thisInstrument = userInstruments.find(
instrument => instrument.instrument_id === musicInstrument.instrument_id
);
if (thisInstrument) return;
const { id } = musicInstrument;
const updatedInstruments = [...userInstruments, { ...musicInstrument, instrument_id: id }];
setValue('instruments', updatedInstruments);
} else {
const updatedInstruments = getValues('instruments').filter(
instrument => instrument.instrument_id !== musicInstrument.instrument_id
);
setValue('instruments', updatedInstruments);
}
const updatedMusicInstruments = musicInstruments.map(instrument => {
if (instrument.id === musicInstrument.id) {
instrument.checked = e.target.checked;
} else {
instrument.checked = instrument.checked;
}
return instrument;
});
setMusicInstruments(updatedMusicInstruments);
handleChange();
};
const handleInstrumentProficiencyChange = (option, musicInstrument) => {
const userInstrument = getValues('instruments').find(instrument => instrument.instrument_id === musicInstrument.id);
if (!userInstrument) return;
userInstrument.proficiency_level = option.value;
forceUpdate();
handleChange();
};
const handleGenreChange = (e, genre) => {
if (e.target.checked) {
const userGenres = getValues('genres');
const thisGenre = userGenres.find(userGenre => userGenre.genre_id === genre.genre_id);
if (thisGenre) return;
const updatedGenres = [...userGenres, { ...genre }];
setValue('genres', updatedGenres);
} else {
const updatedGenres = getValues('genres').filter(userGenre => userGenre.genre_id !== genre.genre_id);
setValue('genres', updatedGenres);
}
const updatedGenres = genres.map(genreItem => {
if (genreItem.genre_id === genre.genre_id) {
genreItem.checked = e.target.checked;
} else {
genreItem.checked = genreItem.checked;
}
return genreItem;
});
setGenres(updatedGenres);
handleChange();
};
const handleTextInputChage = () => {
clearTimeout(saveTimeoutRef.current);
saveTimeoutRef.current = setTimeout(() => {
handleChange();
}, 2000);
};
const skipRegionChange = useRef(false);
const handleCountryChange = selectedOpt => {
const country = selectedOpt.value;
setValue('country', country);
setValue('state', null);
setValue('city', null);
setRegions([]);
skipRegionChange.current = true;
regionRef.current.select.clearValue();
setCities([]);
cityRef.current.select.clearValue();
fetchRegions(country.value);
forceUpdate();
handleChange();
};
const handleRegionChange = selectedOpt => {
if (skipRegionChange.current) {
skipRegionChange.current = false;
return;
}
const state = selectedOpt.value;
const country = getValues('country');
setValue('state', state);
setValue('city', null);
setCities([]);
cityRef.current.select.clearValue();
fetchCities(country, state);
handleChange();
};
const handleChange = () => {
const params = getValues();
const data = {
first_name: params.firstName,
last_name: params.lastName,
country: params.country,
state: params.state,
city: params.city,
biography: params.biography,
subscribe_email: params.subscribeEmail,
virtual_band: params.virtualBand,
traditional_band: params.traditionalBand,
cowriting: params.cowriting,
instruments: params.instruments,
genres: params.genres
};
console.log('_DEBUG data', data);
// updateUser(currentUser.id, data).then(response => {
// if (response.ok) {
// return response.json();
// }
// }).then(data => {
// console.log('_DEBUG data', data);
// }
// ).catch(error => console.log(error));
};
return (
<Card>
<FalconCardHeader title={t('page_title', { ns: 'profile' })} titleClass="font-weight-bold" />
<CardBody className="pt-3" style={{ backgroundColor: '#edf2f9' }}>
<Form>
<Row>
<Col>
<Card>
<CardHeader>
<h5>Basics</h5>
</CardHeader>
<CardBody className="bg-light">
<Row>
<Col md={4}>
<FormGroup>
<Label for="firstName">First Name</Label>
<Controller
name="firstName"
control={control}
render={({ field: { onChange, value } }) => (
<Input
value={value}
onChange={e => {
onChange(e);
handleTextInputChage();
}}
/>
)}
/>
</FormGroup>
</Col>
<Col md={4}>
<FormGroup>
<Label for="lastName">Last Name</Label>
<Controller
name="lastName"
control={control}
render={({ field: { onChange, value } }) => (
<Input
value={value}
onChange={e => {
onChange(e);
handleTextInputChage();
}}
/>
)}
/>
</FormGroup>
</Col>
<Col md={4}>
<div className="d-flex align-items-center">
<div>
<JKCurrentUserAvatar size="sm" />
</div>
<div>
<a href="#">Change Photo</a>
</div>
</div>
</Col>
</Row>
<Row>
<Col md={4}>
<FormGroup>
<Label for="country">Country</Label>
{countries.length > 0 && (
<Controller
name="country"
control={control}
render={({ field: { onChange, value } }) => {
const country = countries.find(country => country.countrycode === value);
return (
<Select
value={{ value: country.countrycode, label: country.countryname }}
onChange={handleCountryChange}
options={countries.map(country => {
return { value: country.countrycode, label: country.countryname };
})}
/>
);
}}
/>
)}
</FormGroup>
</Col>
<Col md={4}>
<FormGroup>
<Label for="state">State / Province</Label>
{regions.length > 0 && (
<Controller
name="state"
control={control}
render={({ field: { onChange, value } }) => {
const region = regions.find(region => region.region === value);
return (
<Select
value={{ value: region.region, label: region.region }}
ref={regionRef}
onChange={handleRegionChange}
options={regions.map(region => {
return { value: region.region, label: region.region };
})}
/>
);
}}
/>
)}
</FormGroup>
</Col>
<Col md={4}>
<FormGroup>
<Label for="city">City</Label>
<Controller
name="city"
control={control}
render={({ field: { onChange, value } }) => (
<Select
value={{ value: value, label: value }}
ref={cityRef}
onChange={e => {
onChange(e);
handleChange();
}}
options={cities.map(city => {
return { value: city, label: city };
})}
/>
)}
/>
</FormGroup>
</Col>
</Row>
<FormGroup>
<Label for="biography">Musical biography</Label>
<Controller
name="biography"
control={control}
render={({ field: { onChange, value } }) => (
<Input
style={{ height: 200 }}
type="textarea"
value={value}
onChange={e => {
onChange(e);
handleTextInputChage();
}}
/>
)}
/>
</FormGroup>
<FormGroup check>
<Controller
name="subscribeEmail"
control={control}
render={({ field: { onChange, value } }) => (
<Input
checked={value}
type="checkbox"
onChange={e => {
onChange(e);
handleChange();
}}
/>
)}
/>
<Label check for="subscribeEmail">
Accept emails from JamKazam about the JamKazam services
</Label>
</FormGroup>
</CardBody>
</Card>
<Card className="mt-3">
<CardHeader>
<h5>Interests</h5>
</CardHeader>
<CardBody className="bg-light">
<FormGroup check>
<Controller
name="virtualBand"
control={control}
render={({ field: { onChange, value } }) => (
<Input
checked={value}
onChange={e => {
onChange(e);
handleChange();
}}
type="checkbox"
/>
)}
/>
<Label check for="check">
I'm interested in joining a virtual/online band
</Label>
</FormGroup>
<FormGroup check>
<Controller
name="traditionalBand"
control={control}
render={({ field: { onChange, value } }) => (
<Input
checked={value}
onChange={e => {
onChange(e);
handleChange();
}}
type="checkbox"
/>
)}
/>
<Label check for="check">
I'm interested in joining an in-person band
</Label>
</FormGroup>
<FormGroup check>
<Controller
name="cowriting"
control={control}
render={({ field: { onChange, value } }) => (
<Input
checked={value}
onChange={e => {
onChange(e);
handleChange();
}}
type="checkbox"
/>
)}
/>
<Label check for="check">
I'm interested in co-writing songs with others
</Label>
</FormGroup>
</CardBody>
</Card>
</Col>
<Col>
<Card>
<CardHeader>
<h5>Instruments</h5>
</CardHeader>
<CardBody className="bg-light" style={{ overflowY: 'scroll', height: 300 }}>
<FormGroup check>
{instrumentsInitialLoadingDone &&
musicInstruments.map((musicInstrument, index) => {
return (
<Row key={musicInstrument.id} className="mb-1">
<Col md={4}>
<Input
onChange={e => {
handleInstrumentSelect(e, musicInstrument);
}}
type="checkbox"
checked={musicInstrument.checked}
/>
<Label check for="check">
{musicInstrument.description}
</Label>
</Col>
<Col md={3}>
<Select
value={
musicInstrument.checked
? PROFICIENCIES.find(p => parseInt(p.value) === musicInstrument.proficiency_level)
: null
}
onChange={e => {
handleInstrumentProficiencyChange(e, musicInstrument);
}}
options={PROFICIENCIES}
isDisabled={!musicInstrument.checked}
/>
</Col>
</Row>
);
})}
</FormGroup>
</CardBody>
</Card>
<Card className="mt-3">
<CardHeader>
<h5>Genres</h5>
</CardHeader>
<CardBody className="bg-light" style={{ overflowY: 'scroll', height: 300 }}>
<FormGroup check>
{genreInitialLoadingDone &&
genres.map((genre, index) => {
return (
<Row key={genre.genre_id}>
<Col md={4}>
<Input
onChange={e => {
handleGenreChange(e, genre);
}}
type="checkbox"
checked={genre.checked}
/>
<Label check for="check">
{genre.description}
</Label>
</Col>
</Row>
);
})}
</FormGroup>
</CardBody>
</Card>
</Col>
</Row>
</Form>
</CardBody>
</Card>
);
}
export default JKEditProfile;

View File

@ -182,3 +182,40 @@ export const getLobbyChatMessages = (options = {}) => {
.catch(error => reject(error))
})
}
export const updateUser = (userId, options) => {
return new Promise((resolve, reject) => {
apiFetch(`/users/${userId}`, {
method: 'PATCH',
body: JSON.stringify(options)
})
.then(response => resolve(response))
.catch(error => reject(error))
})
}
export const getCountries = () => {
return new Promise((resolve, reject) => {
apiFetch(`/countries`)
.then(response => resolve(response))
.catch(error => reject(error))
})
}
export const getRegions = (countryId) => {
return new Promise((resolve, reject) => {
apiFetch(`/regions?country=${countryId}`)
.then(response => resolve(response))
.catch(error => reject(error))
})
}
export const getCities = (countryId, regionId) => {
return new Promise((resolve, reject) => {
apiFetch(`/cities?country=${countryId}&region=${regionId}`)
.then(response => resolve(response))
.catch(error => reject(error))
})
}

View File

@ -7,6 +7,7 @@ import peopleTranslationsEN from './locales/en/people.json'
import authTranslationsEN from './locales/en/auth.json'
import sessTranslationsEN from './locales/en/sessions.json'
import unsubscribeTranslationsEN from './locales/en/unsubscribe.json'
import profileEN from './locales/en/profile.json'
import commonTranslationsES from './locales/es/common.json'
import homeTranslationsES from './locales/es/home.json'
@ -14,6 +15,7 @@ import peopleTranslationsES from './locales/es/people.json'
import authTranslationsES from './locales/es/auth.json'
import sessTranslationsES from './locales/es/sessions.json'
import unsubscribeTranslationsES from './locales/es/unsubscribe.json'
import profileES from './locales/es/profile.json'
i18n.use(initReactI18next).init({
fallbackLng: 'en',
@ -27,6 +29,7 @@ i18n.use(initReactI18next).init({
auth: authTranslationsEN,
sessions: sessTranslationsEN,
unsubscribe: unsubscribeTranslationsEN,
profile: profileEN
},
es: {
//translations: require('./locales/es/translations.json')
@ -36,6 +39,7 @@ i18n.use(initReactI18next).init({
auth: authTranslationsES,
sessions: sessTranslationsES,
unsubscribe: unsubscribeTranslationsES,
profile: profileES
}
},
//ns: ['translations'],

View File

@ -0,0 +1,3 @@
{
"page_title": "Profile"
}

View File

@ -0,0 +1,3 @@
{
"page_title": "Profile"
}