Email Edit

This commit is contained in:
2024-07-27 03:39:52 +02:00
parent 12f7176467
commit d71eaf2ef2
15 changed files with 229 additions and 175 deletions
@@ -9,7 +9,8 @@
"Unauthorized_userUpdate": "Keine Berechtigung",
"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",
"email": "E-Mail",
@@ -47,5 +48,7 @@
"Confirm post delete body": "Möchtest du diesen Post von {{name}} wirklich Löschen?",
"Deleting": "Löscht...",
"Edit data": "Daten ändern"
"Edit data": "Daten ändern",
"Recent posts": "Letzten Posts"
}
@@ -9,7 +9,8 @@
"Unauthorized_userUpdate": "Unauthorized",
"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",
"email": "email",
@@ -48,5 +49,7 @@
"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:
return <Typography color={color}>{t(error.code, { context })}</Typography>;
case ERRORS.FAILEDUPDATE:
return error.fields.map((field: string) => (
return error.fields.map((field: string, index: number) => (
<Typography key={`error_${field}`} color={color}>
{t(error.code, { context, name: t(field) })}
{t(error.code, { context: `${error.reasons[index]}:${field}:${context}` })}
</Typography>
));
}
@@ -36,6 +36,7 @@ const UserEditDialog: FC<Props> = ({ user, open, onClose }) => {
const form = useForm<UserUpdate>({
defaultValues: {
username: user.username,
email: user.email,
},
onSubmit: async ({ value }) => {
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>
<DialogActions>
<form.Subscribe
@@ -1,5 +1,5 @@
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 { FC } from 'react';
import Api from '../../../api/Api';
@@ -14,6 +14,7 @@ interface Props {
const UserMenu: FC<Props> = ({ anchorEl, handleClose }) => {
const navigate = useNavigate();
const router = useRouter();
const match = useMatch({ from: '/profile/', strict: true, shouldThrow: false });
const user = Api.getAuthenticatedUser();
@@ -40,6 +41,7 @@ const UserMenu: FC<Props> = ({ anchorEl, handleClose }) => {
{user ? (
[
<MenuItem
selected={!!match}
key="profile"
onClick={() => {
navigate({ to: ROUTES.PROFILE });
+41 -32
View File
@@ -1,5 +1,5 @@
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 { useTranslation } from 'react-i18next';
import { User } from '../../types/User';
@@ -17,37 +17,46 @@ const Profile: FC<Props> = ({ user, canEdit }) => {
const { t } = useTranslation();
return (
<Card>
<CardContent>
<Grid container spacing={2}>
<Grid item sx={{ display: 'flex', flexGrow: 1, justifyContent: 'center' }}>
<Avatar alt={user.username} src={`storage/${user.image}`} sx={{ width: '100px', height: '100px' }}>
<Person sx={{ width: '60px', height: '60px' }} />
</Avatar>
</Grid>
<Grid item sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ display: 'grid', gridTemplateColumns: '120px 1fr', columnGap: 1 }}>
<Typography fontWeight="bold">{t('Username')}:</Typography>
<Typography>{user.username}</Typography>
<Typography fontWeight="bold">{t('Email')}:</Typography>
<Typography>{user.email}</Typography>
<Typography fontWeight="bold">{t('Member since')}:</Typography>
<Typography>{convertDate(user.memberSince)}</Typography>
<Typography fontWeight="bold">{t('Post count')}:</Typography>
<Typography>{user.postCount}</Typography>
</Box>
</Grid>
</Grid>
</CardContent>
<CardActions>
{canEdit && (
<Button size="small" onClick={() => setEditOpen(true)}>
{t('Edit')}
</Button>
)}
</CardActions>
<UserEditDialog user={user} open={editOpen} onClose={() => setEditOpen(false)} />
</Card>
<Grid container sx={{ justifyContent: 'center' }} spacing={2}>
<Grid item>
<Card>
<CardContent>
<Grid container spacing={2}>
<Grid item sx={{ display: 'flex', flexGrow: 1, justifyContent: 'center' }}>
<Avatar alt={user.username} src={`storage/${user.image}`} sx={{ width: '100px', height: '100px' }}>
<Person sx={{ width: '60px', height: '60px' }} />
</Avatar>
</Grid>
<Grid item sx={{ display: 'flex', alignItems: 'center' }}>
<Box sx={{ display: 'grid', gridTemplateColumns: '120px 1fr', columnGap: 1 }}>
<Typography fontWeight="bold">{t('Username')}:</Typography>
<Typography>{user.username}</Typography>
<Typography fontWeight="bold">{t('Email')}:</Typography>
<Typography>{user.email}</Typography>
<Typography fontWeight="bold">{t('Member since')}:</Typography>
<Typography>{convertDate(user.memberSince)}</Typography>
<Typography fontWeight="bold">{t('Post count')}:</Typography>
<Typography>{user.postCount}</Typography>
</Box>
</Grid>
</Grid>
</CardContent>
<CardActions>
{canEdit && (
<Button size="small" onClick={() => setEditOpen(true)}>
{t('Edit')}
</Button>
)}
</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>
);
};