mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
Refactor UserProfileContext
This commit is contained in:
parent
f03e61e3c1
commit
ae8b4afa88
|
@ -7,7 +7,8 @@ interface RequireAuthProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function RequireAuth({ children }: RequireAuthProps) {
|
function RequireAuth({ children }: RequireAuthProps) {
|
||||||
const { user } = useAuth()
|
const { user } = useAuth();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{user && children}
|
{user && children}
|
||||||
|
|
77
rsconcept/frontend/src/context/UserProfileContext.tsx
Normal file
77
rsconcept/frontend/src/context/UserProfileContext.tsx
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import { createContext, useCallback, useContext, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { ErrorInfo } from '../components/BackendError';
|
||||||
|
import { DataCallback, getProfile, patchProfile } from '../utils/backendAPI';
|
||||||
|
import { IUserProfile, IUserUpdateData } from '../utils/models';
|
||||||
|
|
||||||
|
interface IUserProfileContextContext {
|
||||||
|
user: IUserProfile | undefined
|
||||||
|
loading: boolean
|
||||||
|
processing: boolean
|
||||||
|
error: ErrorInfo
|
||||||
|
setError: (error: ErrorInfo) => void
|
||||||
|
updateUser: (data: IUserUpdateData, callback?: DataCallback<IUserProfile>) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProfileContext = createContext<IUserProfileContextContext | null>(null);
|
||||||
|
export const useUserProfile = () => {
|
||||||
|
const context = useContext(ProfileContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(
|
||||||
|
'useUserProfile has to be used within <UserProfileState.Provider>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UserProfileStateProps {
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UserProfileState = ({ children }: UserProfileStateProps) => {
|
||||||
|
const [user, setUser] = useState<IUserProfile | undefined>(undefined);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [processing, setProcessing] = useState(false);
|
||||||
|
const [error, setError] = useState<ErrorInfo>(undefined);
|
||||||
|
|
||||||
|
const reload = useCallback(
|
||||||
|
() => {
|
||||||
|
setError(undefined);
|
||||||
|
setUser(undefined);
|
||||||
|
getProfile({
|
||||||
|
showError: true,
|
||||||
|
setLoading: setLoading,
|
||||||
|
onError: error => { setError(error); },
|
||||||
|
onSuccess: newData => { setUser(newData); }
|
||||||
|
});
|
||||||
|
}, [setUser]
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateUser = useCallback(
|
||||||
|
(data: IUserUpdateData, callback?: DataCallback<IUserProfile>) => {
|
||||||
|
setError(undefined);
|
||||||
|
patchProfile({
|
||||||
|
data: data,
|
||||||
|
showError: true,
|
||||||
|
setLoading: setProcessing,
|
||||||
|
onError: error => { setError(error); },
|
||||||
|
onSuccess: newData => {
|
||||||
|
setUser(newData);
|
||||||
|
if (callback) callback(newData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [setUser]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
reload();
|
||||||
|
}, [reload]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProfileContext.Provider
|
||||||
|
value={{user, updateUser, error, loading, setError, processing}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ProfileContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
|
@ -1,45 +0,0 @@
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
|
||||||
|
|
||||||
import { type ErrorInfo } from '../components/BackendError'
|
|
||||||
import { DataCallback, getProfile, patchProfile } from '../utils/backendAPI'
|
|
||||||
import { type IUserProfile,IUserUpdateData } from '../utils/models'
|
|
||||||
|
|
||||||
export function useUserProfile() {
|
|
||||||
const [user, setUser] = useState<IUserProfile | undefined>(undefined);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [error, setError] = useState<ErrorInfo>(undefined);
|
|
||||||
|
|
||||||
const reload = useCallback(
|
|
||||||
() => {
|
|
||||||
setError(undefined);
|
|
||||||
setUser(undefined);
|
|
||||||
getProfile({
|
|
||||||
showError: true,
|
|
||||||
setLoading: setLoading,
|
|
||||||
onError: error => { setError(error); },
|
|
||||||
onSuccess: newData => { setUser(newData); }
|
|
||||||
});
|
|
||||||
}, [setUser]
|
|
||||||
)
|
|
||||||
const updateUser = useCallback(
|
|
||||||
(data: IUserUpdateData, callback?: DataCallback<IUserProfile>) => {
|
|
||||||
setError(undefined);
|
|
||||||
patchProfile({
|
|
||||||
data: data,
|
|
||||||
showError: true,
|
|
||||||
setLoading: setLoading,
|
|
||||||
onError: error => { setError(error); },
|
|
||||||
onSuccess: newData => {
|
|
||||||
setUser(newData);
|
|
||||||
if (callback) callback(newData);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [setUser]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
reload();
|
|
||||||
}, [reload])
|
|
||||||
|
|
||||||
return { user, updateUser, error, loading };
|
|
||||||
}
|
|
|
@ -15,6 +15,7 @@ import { IRSFormCreateData, IRSFormMeta } from '../utils/models';
|
||||||
|
|
||||||
function CreateRSFormPage() {
|
function CreateRSFormPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { createSchema, error, setError, loading } = useNewRSForm()
|
||||||
|
|
||||||
const [title, setTitle] = useState('');
|
const [title, setTitle] = useState('');
|
||||||
const [alias, setAlias] = useState('');
|
const [alias, setAlias] = useState('');
|
||||||
|
@ -22,25 +23,24 @@ function CreateRSFormPage() {
|
||||||
const [common, setCommon] = useState(false);
|
const [common, setCommon] = useState(false);
|
||||||
const [file, setFile] = useState<File | undefined>()
|
const [file, setFile] = useState<File | undefined>()
|
||||||
|
|
||||||
const handleFile = (event: React.ChangeEvent<HTMLInputElement>) => {
|
useEffect(() => {
|
||||||
|
setError(undefined);
|
||||||
|
}, [title, alias, setError]);
|
||||||
|
|
||||||
|
function handleFile(event: React.ChangeEvent<HTMLInputElement>) {
|
||||||
if (event.target.files && event.target.files.length > 0) {
|
if (event.target.files && event.target.files.length > 0) {
|
||||||
setFile(event.target.files[0]);
|
setFile(event.target.files[0]);
|
||||||
} else {
|
} else {
|
||||||
setFile(undefined)
|
setFile(undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSuccess = (newSchema: IRSFormMeta) => {
|
function onSuccess(newSchema: IRSFormMeta) {
|
||||||
toast.success('Схема успешно создана');
|
toast.success('Схема успешно создана');
|
||||||
navigate(`/rsforms/${newSchema.id}`);
|
navigate(`/rsforms/${newSchema.id}`);
|
||||||
}
|
}
|
||||||
const { createSchema, error, setError, loading } = useNewRSForm()
|
|
||||||
|
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||||
useEffect(() => {
|
|
||||||
setError(undefined)
|
|
||||||
}, [title, alias, setError]);
|
|
||||||
|
|
||||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return;
|
return;
|
||||||
|
@ -54,43 +54,49 @@ function CreateRSFormPage() {
|
||||||
fileName: file?.name
|
fileName: file?.name
|
||||||
};
|
};
|
||||||
createSchema(data, onSuccess);
|
createSchema(data, onSuccess);
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RequireAuth>
|
<RequireAuth>
|
||||||
<Form title='Создание концептуальной схемы' onSubmit={handleSubmit} widthClass='max-w-lg mt-4'>
|
<Form title='Создание концептуальной схемы'
|
||||||
<TextInput id='title' label='Полное название' type='text'
|
onSubmit={handleSubmit}
|
||||||
required={!file}
|
widthClass='max-w-lg mt-4'
|
||||||
placeholder={file && 'Загрузить из файла'}
|
>
|
||||||
value={title}
|
<TextInput id='title' label='Полное название' type='text'
|
||||||
onChange={event => { setTitle(event.target.value); }}
|
required={!file}
|
||||||
/>
|
placeholder={file && 'Загрузить из файла'}
|
||||||
<TextInput id='alias' label='Сокращение' type='text'
|
value={title}
|
||||||
required={!file}
|
onChange={event => setTitle(event.target.value)}
|
||||||
value={alias}
|
/>
|
||||||
placeholder={file && 'Загрузить из файла'}
|
<TextInput id='alias' label='Сокращение' type='text'
|
||||||
widthClass='max-w-sm'
|
required={!file}
|
||||||
onChange={event => { setAlias(event.target.value); }}
|
value={alias}
|
||||||
/>
|
placeholder={file && 'Загрузить из файла'}
|
||||||
<TextArea id='comment' label='Комментарий'
|
widthClass='max-w-sm'
|
||||||
value={comment}
|
onChange={event => setAlias(event.target.value)}
|
||||||
placeholder={file && 'Загрузить из файла'}
|
/>
|
||||||
onChange={event => { setComment(event.target.value); }}
|
<TextArea id='comment' label='Комментарий'
|
||||||
/>
|
value={comment}
|
||||||
<Checkbox id='common' label='Общедоступная схема'
|
placeholder={file && 'Загрузить из файла'}
|
||||||
value={common}
|
onChange={event => setComment(event.target.value)}
|
||||||
onChange={event => { setCommon(event.target.checked); }}
|
/>
|
||||||
/>
|
<Checkbox id='common' label='Общедоступная схема'
|
||||||
<FileInput id='trs' label='Загрузить *.trs'
|
value={common}
|
||||||
acceptType='.trs'
|
onChange={event => setCommon(event.target.checked)}
|
||||||
onChange={handleFile}
|
/>
|
||||||
/>
|
<FileInput id='trs' label='Загрузить *.trs'
|
||||||
|
acceptType='.trs'
|
||||||
|
onChange={handleFile}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className='flex items-center justify-center py-2 mt-4'>
|
<div className='flex items-center justify-center py-2 mt-4'>
|
||||||
<SubmitButton text='Создать схему' loading={loading} />
|
<SubmitButton
|
||||||
</div>
|
text='Создать схему'
|
||||||
{ error && <BackendError error={error} />}
|
loading={loading}
|
||||||
</Form>
|
/>
|
||||||
|
</div>
|
||||||
|
{ error && <BackendError error={error} />}
|
||||||
|
</Form>
|
||||||
</RequireAuth>
|
</RequireAuth>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,12 @@ import { useAuth } from '../context/AuthContext';
|
||||||
import { IUserLoginData } from '../utils/models';
|
import { IUserLoginData } from '../utils/models';
|
||||||
|
|
||||||
function LoginPage() {
|
function LoginPage() {
|
||||||
const [username, setUsername] = useState('');
|
|
||||||
const [password, setPassword] = useState('');
|
|
||||||
const { user, login, loading, error, setError } = useAuth()
|
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const search = useLocation().search;
|
const search = useLocation().search;
|
||||||
|
const { user, login, loading, error, setError } = useAuth();
|
||||||
|
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const name = new URLSearchParams(search).get('username');
|
const name = new URLSearchParams(search).get('username');
|
||||||
|
@ -28,7 +28,7 @@ function LoginPage() {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
}, [username, password, setError]);
|
}, [username, password, setError]);
|
||||||
|
|
||||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
const data: IUserLoginData = {
|
const data: IUserLoginData = {
|
||||||
|
@ -37,7 +37,7 @@ function LoginPage() {
|
||||||
};
|
};
|
||||||
login(data, () => { navigate('/library?filter=personal'); });
|
login(data, () => { navigate('/library?filter=personal'); });
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full py-2'> { user
|
<div className='w-full py-2'> { user
|
||||||
|
|
|
@ -32,13 +32,13 @@ export enum RSTabsList {
|
||||||
function RSTabs() {
|
function RSTabs() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { setActiveID, activeID, error, schema, loading, cstCreate } = useRSForm();
|
const { setActiveID, activeID, error, schema, loading, cstCreate } = useRSForm();
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useLocalStorage('rsform_edit_tab', RSTabsList.CARD);
|
const [activeTab, setActiveTab] = useLocalStorage('rsform_edit_tab', RSTabsList.CARD);
|
||||||
|
|
||||||
const [init, setInit] = useState(false);
|
const [init, setInit] = useState(false);
|
||||||
|
|
||||||
const [showUpload, setShowUpload] = useState(false);
|
const [showUpload, setShowUpload] = useState(false);
|
||||||
|
|
||||||
const [showClone, setShowClone] = useState(false);
|
const [showClone, setShowClone] = useState(false);
|
||||||
|
|
||||||
const [syntaxTree, setSyntaxTree] = useState<SyntaxTree>([]);
|
const [syntaxTree, setSyntaxTree] = useState<SyntaxTree>([]);
|
||||||
const [showAST, setShowAST] = useState(false);
|
const [showAST, setShowAST] = useState(false);
|
||||||
|
|
||||||
|
@ -46,60 +46,6 @@ function RSTabs() {
|
||||||
const [insertWhere, setInsertWhere] = useState<number | undefined>(undefined);
|
const [insertWhere, setInsertWhere] = useState<number | undefined>(undefined);
|
||||||
const [showCreateCst, setShowCreateCst] = useState(false);
|
const [showCreateCst, setShowCreateCst] = useState(false);
|
||||||
|
|
||||||
const handleAddNew = useCallback(
|
|
||||||
(type: CstType, selectedCst?: number) => {
|
|
||||||
if (!schema?.items) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const data: ICstCreateData = {
|
|
||||||
cst_type: type,
|
|
||||||
alias: createAliasFor(type, schema),
|
|
||||||
insert_after: selectedCst ?? insertWhere ?? null
|
|
||||||
}
|
|
||||||
cstCreate(data, newCst => {
|
|
||||||
toast.success(`Конституента добавлена: ${newCst.alias}`);
|
|
||||||
navigate(`/rsforms/${schema.id}?tab=${activeTab}&active=${newCst.id}`);
|
|
||||||
if (activeTab === RSTabsList.CST_EDIT || activeTab == RSTabsList.CST_LIST) {
|
|
||||||
setTimeout(() => {
|
|
||||||
const element = document.getElementById(`${prefixes.cst_list}${newCst.alias}`);
|
|
||||||
if (element) {
|
|
||||||
element.scrollIntoView({
|
|
||||||
behavior: 'smooth',
|
|
||||||
block: "end",
|
|
||||||
inline: "nearest"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, timeout_updateUI);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [schema, cstCreate, insertWhere, navigate, activeTab]);
|
|
||||||
|
|
||||||
const onShowCreateCst = useCallback(
|
|
||||||
(selectedID: number | undefined, type: CstType | undefined, skipDialog?: boolean) => {
|
|
||||||
if (skipDialog && type) {
|
|
||||||
handleAddNew(type, selectedID);
|
|
||||||
} else {
|
|
||||||
setDefaultType(type);
|
|
||||||
setInsertWhere(selectedID);
|
|
||||||
setShowCreateCst(true);
|
|
||||||
}
|
|
||||||
}, [handleAddNew]);
|
|
||||||
|
|
||||||
const onShowAST = useCallback(
|
|
||||||
(ast: SyntaxTree) => {
|
|
||||||
setSyntaxTree(ast);
|
|
||||||
setShowAST(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onEditCst = (cst: IConstituenta) => {
|
|
||||||
setActiveID(cst.id);
|
|
||||||
setActiveTab(RSTabsList.CST_EDIT)
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSelectTab = (index: number) => {
|
|
||||||
setActiveTab(index);
|
|
||||||
};
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (schema) {
|
if (schema) {
|
||||||
const url = new URL(window.location.href);
|
const url = new URL(window.location.href);
|
||||||
|
@ -146,6 +92,61 @@ function RSTabs() {
|
||||||
}
|
}
|
||||||
}, [activeTab, activeID, init]);
|
}, [activeTab, activeID, init]);
|
||||||
|
|
||||||
|
function onSelectTab(index: number) {
|
||||||
|
setActiveTab(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAddNew = useCallback(
|
||||||
|
(type: CstType, selectedCst?: number) => {
|
||||||
|
if (!schema?.items) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data: ICstCreateData = {
|
||||||
|
cst_type: type,
|
||||||
|
alias: createAliasFor(type, schema),
|
||||||
|
insert_after: selectedCst ?? insertWhere ?? null
|
||||||
|
}
|
||||||
|
cstCreate(data, newCst => {
|
||||||
|
toast.success(`Конституента добавлена: ${newCst.alias}`);
|
||||||
|
navigate(`/rsforms/${schema.id}?tab=${activeTab}&active=${newCst.id}`);
|
||||||
|
if (activeTab === RSTabsList.CST_EDIT || activeTab == RSTabsList.CST_LIST) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const element = document.getElementById(`${prefixes.cst_list}${newCst.alias}`);
|
||||||
|
if (element) {
|
||||||
|
element.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: "end",
|
||||||
|
inline: "nearest"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, timeout_updateUI);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [schema, cstCreate, insertWhere, navigate, activeTab]);
|
||||||
|
|
||||||
|
const onShowCreateCst = useCallback(
|
||||||
|
(selectedID: number | undefined, type: CstType | undefined, skipDialog?: boolean) => {
|
||||||
|
if (skipDialog && type) {
|
||||||
|
handleAddNew(type, selectedID);
|
||||||
|
} else {
|
||||||
|
setDefaultType(type);
|
||||||
|
setInsertWhere(selectedID);
|
||||||
|
setShowCreateCst(true);
|
||||||
|
}
|
||||||
|
}, [handleAddNew]);
|
||||||
|
|
||||||
|
const onShowAST = useCallback(
|
||||||
|
(ast: SyntaxTree) => {
|
||||||
|
setSyntaxTree(ast);
|
||||||
|
setShowAST(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onEditCst = useCallback(
|
||||||
|
(cst: IConstituenta) => {
|
||||||
|
setActiveID(cst.id);
|
||||||
|
setActiveTab(RSTabsList.CST_EDIT)
|
||||||
|
}, [setActiveID, setActiveTab]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
{ loading && <Loader /> }
|
{ loading && <Loader /> }
|
||||||
|
|
|
@ -12,6 +12,8 @@ import { type IUserSignupData } from '../utils/models';
|
||||||
|
|
||||||
function RegisterPage() {
|
function RegisterPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { user, signup, loading, error, setError } = useAuth();
|
||||||
|
|
||||||
const [username, setUsername] = useState('');
|
const [username, setUsername] = useState('');
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
|
@ -19,13 +21,11 @@ function RegisterPage() {
|
||||||
const [firstName, setFirstName] = useState('');
|
const [firstName, setFirstName] = useState('');
|
||||||
const [lastName, setLastName] = useState('');
|
const [lastName, setLastName] = useState('');
|
||||||
|
|
||||||
const { user, signup, loading, error, setError } = useAuth()
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setError(undefined);
|
setError(undefined);
|
||||||
}, [username, email, password, password2, setError]);
|
}, [username, email, password, password2, setError]);
|
||||||
|
|
||||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
const data: IUserSignupData = {
|
const data: IUserSignupData = {
|
||||||
|
@ -41,7 +41,7 @@ function RegisterPage() {
|
||||||
toast.success(`Пользователь успешно создан: ${createdUser.username}`);
|
toast.success(`Пользователь успешно создан: ${createdUser.username}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full py-2'>
|
<div className='w-full py-2'>
|
||||||
|
|
|
@ -2,12 +2,12 @@ import { useLayoutEffect, useState } from 'react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
import TextInput from '../../components/Common/TextInput';
|
import TextInput from '../../components/Common/TextInput';
|
||||||
import { useUserProfile } from '../../hooks/useUserProfile';
|
import { useUserProfile } from '../../context/UserProfileContext';
|
||||||
import { IUserUpdateData } from '../../utils/models';
|
import { IUserUpdateData } from '../../utils/models';
|
||||||
|
|
||||||
|
|
||||||
export function UserProfile() {
|
export function UserProfile() {
|
||||||
const { updateUser, user} = useUserProfile();
|
const { updateUser, user, processing } = useUserProfile();
|
||||||
|
|
||||||
const [username, setUsername] = useState('');
|
const [username, setUsername] = useState('');
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
|
@ -23,7 +23,7 @@ export function UserProfile() {
|
||||||
}
|
}
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const data: IUserUpdateData = {
|
const data: IUserUpdateData = {
|
||||||
username: username,
|
username: username,
|
||||||
|
@ -32,18 +32,30 @@ export function UserProfile() {
|
||||||
last_name: last_name,
|
last_name: last_name,
|
||||||
};
|
};
|
||||||
updateUser(data, () => toast.success('Изменения сохранены'));
|
updateUser(data, () => toast.success('Изменения сохранены'));
|
||||||
};
|
}
|
||||||
|
|
||||||
// console.log(user)
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit} className='flex-grow max-w-xl px-4 py-2 border min-w-fit'>
|
<form onSubmit={handleSubmit} className='flex-grow max-w-xl px-4 py-2 border min-w-fit'>
|
||||||
<div className='flex flex-col items-center justify-center px-2 py-2 border'>
|
<div className='flex flex-col items-center justify-center px-2 py-2 border'>
|
||||||
<TextInput id='username' label="Логин:" value={username} onChange={event => { setUsername(event.target.value); }}/>
|
<TextInput id='username'
|
||||||
<TextInput id='first_name' label="Имя:" value={first_name} onChange={event => { setFirstName(event.target.value); }}/>
|
label='Логин:'
|
||||||
<TextInput id='last_name' label="Фамилия:" value={last_name} onChange={event => { setLastName(event.target.value); }}/>
|
value={username}
|
||||||
<TextInput id='email' label="Электронная почта:" value={email} onChange={event => { setEmail(event.target.value); }}/>
|
onChange={event => setUsername(event.target.value)}
|
||||||
|
/>
|
||||||
|
<TextInput id='first_name'
|
||||||
|
label="Имя:"
|
||||||
|
value={first_name}
|
||||||
|
onChange={event => setFirstName(event.target.value)}
|
||||||
|
/>
|
||||||
|
<TextInput id='last_name' label="Фамилия:" value={last_name} onChange={event => setLastName(event.target.value)}/>
|
||||||
|
<TextInput id='email' label="Электронная почта:" value={email} onChange={event => setEmail(event.target.value)}/>
|
||||||
<div className='flex items-center justify-between my-4'>
|
<div className='flex items-center justify-between my-4'>
|
||||||
<button className='px-2 py-1 bg-green-500 border' type='submit'>Сохранить</button>
|
<button
|
||||||
|
type='submit'
|
||||||
|
className='px-2 py-1 bg-green-500 border'
|
||||||
|
disabled={processing}>
|
||||||
|
<span>Сохранить</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
18
rsconcept/frontend/src/pages/UserProfilePage/UserTabs.tsx
Normal file
18
rsconcept/frontend/src/pages/UserProfilePage/UserTabs.tsx
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import BackendError from '../../components/BackendError';
|
||||||
|
import { Loader } from '../../components/Common/Loader';
|
||||||
|
import { useUserProfile } from '../../context/UserProfileContext';
|
||||||
|
import { UserProfile } from './UserProfile';
|
||||||
|
|
||||||
|
function UserTabs() {
|
||||||
|
const { user, error, loading } = useUserProfile();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='w-full'>
|
||||||
|
{ loading && <Loader /> }
|
||||||
|
{ error && <BackendError error={error} />}
|
||||||
|
{ user && <UserProfile /> }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserTabs;
|
|
@ -1,17 +1,14 @@
|
||||||
import BackendError from '../../components/BackendError';
|
import RequireAuth from '../../components/RequireAuth';
|
||||||
import { Loader } from '../../components/Common/Loader';
|
import { UserProfileState } from '../../context/UserProfileContext';
|
||||||
import { useUserProfile } from '../../hooks/useUserProfile';
|
import UserTabs from './UserTabs';
|
||||||
import { UserProfile } from './UserProfile';
|
|
||||||
|
|
||||||
function UserProfilePage() {
|
function UserProfilePage() {
|
||||||
const { user, error, loading } = useUserProfile();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full'>
|
<RequireAuth>
|
||||||
{ loading && <Loader /> }
|
<UserProfileState>
|
||||||
{ error && <BackendError error={error} />}
|
<UserTabs />
|
||||||
{ user && <UserProfile /> }
|
</UserProfileState>
|
||||||
</div>
|
</RequireAuth>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user