Email Edit
This commit is contained in:
parent
12f7176467
commit
d71eaf2ef2
1
exam/dist/assets/index-BhgwoArk.js
vendored
Normal file
1
exam/dist/assets/index-BhgwoArk.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
exam/dist/assets/index-DNzu9OIf.js
vendored
1
exam/dist/assets/index-DNzu9OIf.js
vendored
File diff suppressed because one or more lines are too long
125
exam/dist/assets/mui-BZej3Yg3.js
vendored
125
exam/dist/assets/mui-BZej3Yg3.js
vendored
File diff suppressed because one or more lines are too long
125
exam/dist/assets/mui-CsmJ6if2.js
vendored
Normal file
125
exam/dist/assets/mui-CsmJ6if2.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6
exam/dist/index.html
vendored
6
exam/dist/index.html
vendored
@ -5,10 +5,10 @@
|
|||||||
<link rel="icon" type="image/svg+xml" href="/phpCourse/exam/dist/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/phpCourse/exam/dist/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Vite + React + TS</title>
|
<title>Vite + React + TS</title>
|
||||||
<script type="module" crossorigin src="/phpCourse/exam/dist/assets/index-DNzu9OIf.js"></script>
|
<script type="module" crossorigin src="/phpCourse/exam/dist/assets/index-BhgwoArk.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/phpCourse/exam/dist/assets/react-DXd9vB-a.js">
|
<link rel="modulepreload" crossorigin href="/phpCourse/exam/dist/assets/react-DXd9vB-a.js">
|
||||||
<link rel="modulepreload" crossorigin href="/phpCourse/exam/dist/assets/mui-BZej3Yg3.js">
|
<link rel="modulepreload" crossorigin href="/phpCourse/exam/dist/assets/mui-CsmJ6if2.js">
|
||||||
<link rel="modulepreload" crossorigin href="/phpCourse/exam/dist/assets/tanstack-DeUNQvBN.js">
|
<link rel="modulepreload" crossorigin href="/phpCourse/exam/dist/assets/tanstack-DXVkKUs1.js">
|
||||||
<link rel="modulepreload" crossorigin href="/phpCourse/exam/dist/assets/i18n-DJgSTqOl.js">
|
<link rel="modulepreload" crossorigin href="/phpCourse/exam/dist/assets/i18n-DJgSTqOl.js">
|
||||||
<link rel="stylesheet" crossorigin href="/phpCourse/exam/dist/assets/mui-CKDNpdid.css">
|
<link rel="stylesheet" crossorigin href="/phpCourse/exam/dist/assets/mui-CKDNpdid.css">
|
||||||
<link rel="stylesheet" crossorigin href="/phpCourse/exam/dist/assets/index-D83Ey19k.css">
|
<link rel="stylesheet" crossorigin href="/phpCourse/exam/dist/assets/index-D83Ey19k.css">
|
||||||
|
|||||||
7
exam/dist/locales/de/translation.json
vendored
7
exam/dist/locales/de/translation.json
vendored
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
"Unauthorized_userUpdate": "Keine Berechtigung",
|
"Unauthorized_userUpdate": "Keine Berechtigung",
|
||||||
"NotFound_user:userUpdate": "Benutzer nicht gefunden",
|
"NotFound_user:userUpdate": "Benutzer nicht gefunden",
|
||||||
"FailedUpdate_userUpdate": "{{name}} konnte nicht aktualisiert werden",
|
"FailedUpdate_Duplicate:username:userUpdate": "Ein Benutzer mit diesem Benutzernamen existiert schon",
|
||||||
|
"FailedUpdate_Duplicate:email:userUpdate": "Ein Benutzer mit dieser E-Mail existiert schon",
|
||||||
|
|
||||||
"username": "Benutzername",
|
"username": "Benutzername",
|
||||||
"email": "E-Mail",
|
"email": "E-Mail",
|
||||||
@ -47,5 +48,7 @@
|
|||||||
"Confirm post delete body": "Möchtest du diesen Post von {{name}} wirklich Löschen?",
|
"Confirm post delete body": "Möchtest du diesen Post von {{name}} wirklich Löschen?",
|
||||||
|
|
||||||
"Deleting": "Löscht...",
|
"Deleting": "Löscht...",
|
||||||
"Edit data": "Daten ändern"
|
"Edit data": "Daten ändern",
|
||||||
|
|
||||||
|
"Recent posts": "Letzten Posts"
|
||||||
}
|
}
|
||||||
|
|||||||
7
exam/dist/locales/en/translation.json
vendored
7
exam/dist/locales/en/translation.json
vendored
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
"Unauthorized_userUpdate": "Unauthorized",
|
"Unauthorized_userUpdate": "Unauthorized",
|
||||||
"NotFound_user:userUpdate": "User not found",
|
"NotFound_user:userUpdate": "User not found",
|
||||||
"FailedUpdate_userUpdate": "Failed to update {{name}}",
|
"FailedUpdate_Duplicate:userUpdate": "A user with this username already exists",
|
||||||
|
"FailedUpdate_Duplicate:email:userUpdate": "A user with this email already exists",
|
||||||
|
|
||||||
"username": "username",
|
"username": "username",
|
||||||
"email": "email",
|
"email": "email",
|
||||||
@ -48,5 +49,7 @@
|
|||||||
|
|
||||||
"Deleting": "Deleting...",
|
"Deleting": "Deleting...",
|
||||||
|
|
||||||
"Edit data": "Edit date"
|
"Edit data": "Edit date",
|
||||||
|
|
||||||
|
"Recent posts": "Recent posts"
|
||||||
}
|
}
|
||||||
|
|||||||
2
exam/dist/stats.html
vendored
2
exam/dist/stats.html
vendored
File diff suppressed because one or more lines are too long
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
"Unauthorized_userUpdate": "Keine Berechtigung",
|
"Unauthorized_userUpdate": "Keine Berechtigung",
|
||||||
"NotFound_user:userUpdate": "Benutzer nicht gefunden",
|
"NotFound_user:userUpdate": "Benutzer nicht gefunden",
|
||||||
"FailedUpdate_userUpdate": "{{name}} konnte nicht aktualisiert werden",
|
"FailedUpdate_Duplicate:username:userUpdate": "Ein Benutzer mit diesem Benutzernamen existiert schon",
|
||||||
|
"FailedUpdate_Duplicate:email:userUpdate": "Ein Benutzer mit dieser E-Mail existiert schon",
|
||||||
|
|
||||||
"username": "Benutzername",
|
"username": "Benutzername",
|
||||||
"email": "E-Mail",
|
"email": "E-Mail",
|
||||||
@ -47,5 +48,7 @@
|
|||||||
"Confirm post delete body": "Möchtest du diesen Post von {{name}} wirklich Löschen?",
|
"Confirm post delete body": "Möchtest du diesen Post von {{name}} wirklich Löschen?",
|
||||||
|
|
||||||
"Deleting": "Löscht...",
|
"Deleting": "Löscht...",
|
||||||
"Edit data": "Daten ändern"
|
"Edit data": "Daten ändern",
|
||||||
|
|
||||||
|
"Recent posts": "Letzten Posts"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
"Unauthorized_userUpdate": "Unauthorized",
|
"Unauthorized_userUpdate": "Unauthorized",
|
||||||
"NotFound_user:userUpdate": "User not found",
|
"NotFound_user:userUpdate": "User not found",
|
||||||
"FailedUpdate_userUpdate": "Failed to update {{name}}",
|
"FailedUpdate_Duplicate:userUpdate": "A user with this username already exists",
|
||||||
|
"FailedUpdate_Duplicate:email:userUpdate": "A user with this email already exists",
|
||||||
|
|
||||||
"username": "username",
|
"username": "username",
|
||||||
"email": "email",
|
"email": "email",
|
||||||
@ -48,5 +49,7 @@
|
|||||||
|
|
||||||
"Deleting": "Deleting...",
|
"Deleting": "Deleting...",
|
||||||
|
|
||||||
"Edit data": "Edit date"
|
"Edit data": "Edit date",
|
||||||
|
|
||||||
|
"Recent posts": "Recent posts"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,9 +22,9 @@ const ErrorComponent: FC<Props> = ({ error, context, color = 'error.main' }) =>
|
|||||||
case ERRORS.UNAUTHORIZED:
|
case ERRORS.UNAUTHORIZED:
|
||||||
return <Typography color={color}>{t(error.code, { context })}</Typography>;
|
return <Typography color={color}>{t(error.code, { context })}</Typography>;
|
||||||
case ERRORS.FAILEDUPDATE:
|
case ERRORS.FAILEDUPDATE:
|
||||||
return error.fields.map((field: string) => (
|
return error.fields.map((field: string, index: number) => (
|
||||||
<Typography key={`error_${field}`} color={color}>
|
<Typography key={`error_${field}`} color={color}>
|
||||||
{t(error.code, { context, name: t(field) })}
|
{t(error.code, { context: `${error.reasons[index]}:${field}:${context}` })}
|
||||||
</Typography>
|
</Typography>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,7 @@ const UserEditDialog: FC<Props> = ({ user, open, onClose }) => {
|
|||||||
const form = useForm<UserUpdate>({
|
const form = useForm<UserUpdate>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
username: user.username,
|
username: user.username,
|
||||||
|
email: user.email,
|
||||||
},
|
},
|
||||||
onSubmit: async ({ value }) => {
|
onSubmit: async ({ value }) => {
|
||||||
try {
|
try {
|
||||||
@ -121,6 +122,36 @@ const UserEditDialog: FC<Props> = ({ user, open, onClose }) => {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<form.Field
|
||||||
|
name="email"
|
||||||
|
validators={{
|
||||||
|
onChange: ({ value }) => (!value ? t('Email required') : undefined),
|
||||||
|
onChangeAsyncDebounceMs: 250,
|
||||||
|
onChangeAsync: async ({ value }) => {
|
||||||
|
return !value && t('Email required');
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
children={(field) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TextField
|
||||||
|
name={field.name}
|
||||||
|
value={field.state.value}
|
||||||
|
onBlur={field.handleBlur}
|
||||||
|
onChange={(e) => field.handleChange(e.target.value)}
|
||||||
|
size="small"
|
||||||
|
label={t('Email')}
|
||||||
|
required
|
||||||
|
margin="dense"
|
||||||
|
autoComplete="new-username"
|
||||||
|
fullWidth
|
||||||
|
error={field.state.meta.isTouched && field.state.meta.errors.length > 0}
|
||||||
|
helperText={field.state.meta.isTouched ? field.state.meta.errors.join(',') : ''}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<form.Subscribe
|
<form.Subscribe
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Menu, MenuItem } from '@mui/material';
|
import { Menu, MenuItem } from '@mui/material';
|
||||||
import { useNavigate, useRouter } from '@tanstack/react-router';
|
import { useMatch, useNavigate, useRouter } from '@tanstack/react-router';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import Api from '../../../api/Api';
|
import Api from '../../../api/Api';
|
||||||
@ -14,6 +14,7 @@ interface Props {
|
|||||||
const UserMenu: FC<Props> = ({ anchorEl, handleClose }) => {
|
const UserMenu: FC<Props> = ({ anchorEl, handleClose }) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const match = useMatch({ from: '/profile/', strict: true, shouldThrow: false });
|
||||||
|
|
||||||
const user = Api.getAuthenticatedUser();
|
const user = Api.getAuthenticatedUser();
|
||||||
|
|
||||||
@ -40,6 +41,7 @@ const UserMenu: FC<Props> = ({ anchorEl, handleClose }) => {
|
|||||||
{user ? (
|
{user ? (
|
||||||
[
|
[
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
selected={!!match}
|
||||||
key="profile"
|
key="profile"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate({ to: ROUTES.PROFILE });
|
navigate({ to: ROUTES.PROFILE });
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Person } from '@mui/icons-material';
|
import { Person } from '@mui/icons-material';
|
||||||
import { Avatar, Box, Button, Card, CardActions, CardContent, Grid, Typography } from '@mui/material';
|
import { Avatar, Box, Button, Card, CardActions, CardContent, Divider, Grid, Typography } from '@mui/material';
|
||||||
import { FC, useState } from 'react';
|
import { FC, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { User } from '../../types/User';
|
import { User } from '../../types/User';
|
||||||
@ -17,37 +17,46 @@ const Profile: FC<Props> = ({ user, canEdit }) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Grid container sx={{ justifyContent: 'center' }} spacing={2}>
|
||||||
<CardContent>
|
<Grid item>
|
||||||
<Grid container spacing={2}>
|
<Card>
|
||||||
<Grid item sx={{ display: 'flex', flexGrow: 1, justifyContent: 'center' }}>
|
<CardContent>
|
||||||
<Avatar alt={user.username} src={`storage/${user.image}`} sx={{ width: '100px', height: '100px' }}>
|
<Grid container spacing={2}>
|
||||||
<Person sx={{ width: '60px', height: '60px' }} />
|
<Grid item sx={{ display: 'flex', flexGrow: 1, justifyContent: 'center' }}>
|
||||||
</Avatar>
|
<Avatar alt={user.username} src={`storage/${user.image}`} sx={{ width: '100px', height: '100px' }}>
|
||||||
</Grid>
|
<Person sx={{ width: '60px', height: '60px' }} />
|
||||||
<Grid item sx={{ display: 'flex', alignItems: 'center' }}>
|
</Avatar>
|
||||||
<Box sx={{ display: 'grid', gridTemplateColumns: '120px 1fr', columnGap: 1 }}>
|
</Grid>
|
||||||
<Typography fontWeight="bold">{t('Username')}:</Typography>
|
<Grid item sx={{ display: 'flex', alignItems: 'center' }}>
|
||||||
<Typography>{user.username}</Typography>
|
<Box sx={{ display: 'grid', gridTemplateColumns: '120px 1fr', columnGap: 1 }}>
|
||||||
<Typography fontWeight="bold">{t('Email')}:</Typography>
|
<Typography fontWeight="bold">{t('Username')}:</Typography>
|
||||||
<Typography>{user.email}</Typography>
|
<Typography>{user.username}</Typography>
|
||||||
<Typography fontWeight="bold">{t('Member since')}:</Typography>
|
<Typography fontWeight="bold">{t('Email')}:</Typography>
|
||||||
<Typography>{convertDate(user.memberSince)}</Typography>
|
<Typography>{user.email}</Typography>
|
||||||
<Typography fontWeight="bold">{t('Post count')}:</Typography>
|
<Typography fontWeight="bold">{t('Member since')}:</Typography>
|
||||||
<Typography>{user.postCount}</Typography>
|
<Typography>{convertDate(user.memberSince)}</Typography>
|
||||||
</Box>
|
<Typography fontWeight="bold">{t('Post count')}:</Typography>
|
||||||
</Grid>
|
<Typography>{user.postCount}</Typography>
|
||||||
</Grid>
|
</Box>
|
||||||
</CardContent>
|
</Grid>
|
||||||
<CardActions>
|
</Grid>
|
||||||
{canEdit && (
|
</CardContent>
|
||||||
<Button size="small" onClick={() => setEditOpen(true)}>
|
<CardActions>
|
||||||
{t('Edit')}
|
{canEdit && (
|
||||||
</Button>
|
<Button size="small" onClick={() => setEditOpen(true)}>
|
||||||
)}
|
{t('Edit')}
|
||||||
</CardActions>
|
</Button>
|
||||||
<UserEditDialog user={user} open={editOpen} onClose={() => setEditOpen(false)} />
|
)}
|
||||||
</Card>
|
</CardActions>
|
||||||
|
<UserEditDialog user={user} open={editOpen} onClose={() => setEditOpen(false)} />
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Divider variant="middle">
|
||||||
|
<Typography sx={{ opacity: 0.36 }}>{t('Recent posts')}</Typography>
|
||||||
|
</Divider>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user