Delete Post
This commit is contained in:
parent
0929371c10
commit
a515c447e0
@ -139,6 +139,7 @@ class Post implements JsonSerializable
|
|||||||
$db = Database::getInstance();
|
$db = Database::getInstance();
|
||||||
$stmt = $db->prepare("DELETE FROM egb_gaestebuch WHERE id = :ID");
|
$stmt = $db->prepare("DELETE FROM egb_gaestebuch WHERE id = :ID");
|
||||||
$stmt->bindValue(":ID", $this->id);
|
$stmt->bindValue(":ID", $this->id);
|
||||||
|
$stmt->execute();
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"NotFound_user:login": "Benutzer existiert nicht",
|
"NotFound_user:login": "Benutzer existiert nicht",
|
||||||
"Unauthorized_login": "Ungültige E-Mail oder Passwort",
|
"Unauthorized_login": "Ungültige E-Mail oder Passwort",
|
||||||
|
"Unauthorized_delete": "Keine Berechtigung",
|
||||||
|
"NotFound_post:delete": "Post nicht gefunden",
|
||||||
|
|
||||||
"GuestBook": "Gästebuch",
|
"GuestBook": "Gästebuch",
|
||||||
|
|
||||||
@ -18,5 +20,13 @@
|
|||||||
|
|
||||||
"Username": "Benutzername",
|
"Username": "Benutzername",
|
||||||
"Member since": "Mitglied seit",
|
"Member since": "Mitglied seit",
|
||||||
"Post count": "Anzahl Posts"
|
"Post count": "Anzahl Posts",
|
||||||
|
|
||||||
|
"Edit": "Bearbeiten",
|
||||||
|
"Delete": "Löschen",
|
||||||
|
"Yes": "Ja",
|
||||||
|
"No": "Nein",
|
||||||
|
|
||||||
|
"Confirm post delete title": "Diesen Post löschen?",
|
||||||
|
"Confirm post delete body": "Möchtest du diesen Post von {{name}} wirklich Löschen?"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,9 @@
|
|||||||
"NotFound_user:login": "User does not exist",
|
"NotFound_user:login": "User does not exist",
|
||||||
"Unauthorized_login": "Invalid email or password",
|
"Unauthorized_login": "Invalid email or password",
|
||||||
|
|
||||||
|
"Unauthorized_delete": "Unauthorized",
|
||||||
|
"NotFound_post:delete": "Post not found",
|
||||||
|
|
||||||
"GuestBook": "GuestBook",
|
"GuestBook": "GuestBook",
|
||||||
|
|
||||||
"Email": "Email",
|
"Email": "Email",
|
||||||
@ -18,5 +21,13 @@
|
|||||||
|
|
||||||
"Username": "Username",
|
"Username": "Username",
|
||||||
"Member since": "Member since",
|
"Member since": "Member since",
|
||||||
"Post count": "Post count"
|
"Post count": "Post count",
|
||||||
|
|
||||||
|
"Edit": "Edit",
|
||||||
|
"Delete": "Delete",
|
||||||
|
"Yes": "Yes",
|
||||||
|
"No": "No",
|
||||||
|
|
||||||
|
"Confirm post delete title": "Delete this post?",
|
||||||
|
"Confirm post delete body": "Do you really want to delete this post by {{name}}?"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { PostListAuth, PostListNonAuth } from '../types/Post';
|
import { PostAuth, PostListAuth, PostListNonAuth } from '../types/Post';
|
||||||
import { User } from '../types/User';
|
import { User } from '../types/User';
|
||||||
|
|
||||||
const BASE = 'https://khofmann.userpage.fu-berlin.de/phpCourse/exam/api/';
|
const BASE = 'https://khofmann.userpage.fu-berlin.de/phpCourse/exam/api/';
|
||||||
@ -27,7 +27,6 @@ class ApiImpl {
|
|||||||
public logIn = async (email: string, password: string): Promise<void> => {
|
public logIn = async (email: string, password: string): Promise<void> => {
|
||||||
const { user, token } = await (await this.post('login', { email, password })).json();
|
const { user, token } = await (await this.post('login', { email, password })).json();
|
||||||
this.self = user;
|
this.self = user;
|
||||||
this.isAdmin = user.isAdmin;
|
|
||||||
this.token = token;
|
this.token = token;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -50,15 +49,17 @@ class ApiImpl {
|
|||||||
return await (await this.get(url)).json();
|
return await (await this.get(url)).json();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public deletePost = async (id: number): Promise<PostAuth> => {
|
||||||
|
return await (await this.delete(`posts/${id}`)).json();
|
||||||
|
};
|
||||||
|
|
||||||
public user = async (id?: number): Promise<User> => {
|
public user = async (id?: number): Promise<User> => {
|
||||||
return await (await this.getAuth(`users/${id ?? this.self?.id}`)).json();
|
return await (await this.getAuth(`users/${id ?? this.self?.id}`)).json();
|
||||||
};
|
};
|
||||||
|
|
||||||
private post = async (
|
/* Internal */
|
||||||
endpoint: string,
|
|
||||||
body: Record<string, unknown> | undefined = undefined,
|
private post = async (endpoint: string, body?: Record<string, unknown>, headers?: HeadersInit) => {
|
||||||
headers: HeadersInit | undefined = undefined
|
|
||||||
) => {
|
|
||||||
const response = await fetch(`${BASE}${endpoint}`, {
|
const response = await fetch(`${BASE}${endpoint}`, {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
@ -69,11 +70,7 @@ class ApiImpl {
|
|||||||
throw await response.json();
|
throw await response.json();
|
||||||
};
|
};
|
||||||
|
|
||||||
private postAuth = async (
|
private postAuth = async (endpoint: string, body?: Record<string, unknown>, headers?: HeadersInit) => {
|
||||||
endpoint: string,
|
|
||||||
body: Record<string, unknown> | undefined = undefined,
|
|
||||||
headers: HeadersInit | undefined = undefined
|
|
||||||
) => {
|
|
||||||
const response = await fetch(`${BASE}${endpoint}`, {
|
const response = await fetch(`${BASE}${endpoint}`, {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
@ -84,7 +81,7 @@ class ApiImpl {
|
|||||||
throw await response.json();
|
throw await response.json();
|
||||||
};
|
};
|
||||||
|
|
||||||
private get = async (endpoint: string, headers: HeadersInit | undefined = undefined) => {
|
private get = async (endpoint: string, headers?: HeadersInit) => {
|
||||||
const response = await fetch(`${BASE}${endpoint}`, {
|
const response = await fetch(`${BASE}${endpoint}`, {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -94,7 +91,7 @@ class ApiImpl {
|
|||||||
throw await response.json();
|
throw await response.json();
|
||||||
};
|
};
|
||||||
|
|
||||||
private getAuth = async (endpoint: string, headers: HeadersInit | undefined = undefined) => {
|
private getAuth = async (endpoint: string, headers?: HeadersInit) => {
|
||||||
const response = await fetch(`${BASE}${endpoint}`, {
|
const response = await fetch(`${BASE}${endpoint}`, {
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -103,6 +100,16 @@ class ApiImpl {
|
|||||||
if (response.ok) return response;
|
if (response.ok) return response;
|
||||||
throw await response.json();
|
throw await response.json();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private delete = async (endpoint: string, headers?: HeadersInit) => {
|
||||||
|
const response = await fetch(`${BASE}${endpoint}`, {
|
||||||
|
mode: 'cors',
|
||||||
|
method: 'delete',
|
||||||
|
headers: { token: this.token ?? '', ...headers },
|
||||||
|
});
|
||||||
|
if (response.ok) return response;
|
||||||
|
throw await response.json();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const Api = new ApiImpl();
|
const Api = new ApiImpl();
|
||||||
|
|||||||
@ -52,10 +52,9 @@ const LoginForm: FC<Props> = ({ handleClose }) => {
|
|||||||
name="email"
|
name="email"
|
||||||
validators={{
|
validators={{
|
||||||
onChange: ({ value }) => (!value ? t('Email required') : undefined),
|
onChange: ({ value }) => (!value ? t('Email required') : undefined),
|
||||||
onChangeAsyncDebounceMs: 500,
|
onChangeAsyncDebounceMs: 250,
|
||||||
onChangeAsync: async ({ value }) => {
|
onChangeAsync: async ({ value }) => {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
return !value && t('Email required');
|
||||||
return value.includes('error') && 'No "error" allowed in email';
|
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
children={(field) => {
|
children={(field) => {
|
||||||
@ -84,10 +83,9 @@ const LoginForm: FC<Props> = ({ handleClose }) => {
|
|||||||
name="password"
|
name="password"
|
||||||
validators={{
|
validators={{
|
||||||
onChange: ({ value }) => (!value ? t('Password required') : undefined),
|
onChange: ({ value }) => (!value ? t('Password required') : undefined),
|
||||||
onChangeAsyncDebounceMs: 500,
|
onChangeAsyncDebounceMs: 250,
|
||||||
onChangeAsync: async ({ value }) => {
|
onChangeAsync: async ({ value }) => {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
return !value && t('Password required');
|
||||||
return value.includes('error') && 'No "error" allowed in password';
|
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
children={(field) => {
|
children={(field) => {
|
||||||
@ -121,7 +119,7 @@ const LoginForm: FC<Props> = ({ handleClose }) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{error && <Typography color="error.main">{handleError(error, 'login', t)}</Typography>}
|
{error && <Typography color="error.main">{handleError(error, t, 'login')}</Typography>}
|
||||||
</Box>
|
</Box>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { AccountCircle, Translate } from '@mui/icons-material';
|
import { AccountCircle, Person, Translate } from '@mui/icons-material';
|
||||||
import {
|
import {
|
||||||
AppBar,
|
AppBar,
|
||||||
Avatar,
|
Avatar,
|
||||||
@ -57,7 +57,9 @@ const Header: FC = () => {
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
{user ? (
|
{user ? (
|
||||||
<IconButton onClick={(event) => setAnchorUserMenu(event.currentTarget)} sx={{ p: 0 }}>
|
<IconButton onClick={(event) => setAnchorUserMenu(event.currentTarget)} sx={{ p: 0 }}>
|
||||||
<Avatar alt={user.username} src={`storage/${user.image}`} />
|
<Avatar alt={user.username} src={`storage/${user.image}`}>
|
||||||
|
<Person />
|
||||||
|
</Avatar>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
) : (
|
) : (
|
||||||
<IconButton size="large" onClick={(event) => setAnchorUserMenu(event.currentTarget)} color="inherit">
|
<IconButton size="large" onClick={(event) => setAnchorUserMenu(event.currentTarget)} color="inherit">
|
||||||
|
|||||||
@ -1,25 +1,67 @@
|
|||||||
import { Avatar, Card, CardContent, CardHeader, Link as MUILink, Typography } from '@mui/material';
|
import { Person } from '@mui/icons-material';
|
||||||
|
import {
|
||||||
|
Alert,
|
||||||
|
Avatar,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardActions,
|
||||||
|
CardContent,
|
||||||
|
CardHeader,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogContentText,
|
||||||
|
DialogTitle,
|
||||||
|
Link as MUILink,
|
||||||
|
Snackbar,
|
||||||
|
Typography,
|
||||||
|
} from '@mui/material';
|
||||||
|
import { useMutation } from '@tanstack/react-query';
|
||||||
import { Link } from '@tanstack/react-router';
|
import { Link } from '@tanstack/react-router';
|
||||||
import { FC } from 'react';
|
import { FC, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import Api from '../../api/Api';
|
import Api from '../../api/Api';
|
||||||
import { PostAuth, PostNonAuth } from '../../types/Post';
|
import { PostAuth, PostNonAuth } from '../../types/Post';
|
||||||
import convertDate from '../../utils/date';
|
import convertDate from '../../utils/date';
|
||||||
|
import handleError from '../../utils/errors';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
post: PostNonAuth | PostAuth;
|
post: PostNonAuth | PostAuth;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Post: FC<Props> = ({ post }) => {
|
const Post: FC<Props> = ({ post }) => {
|
||||||
|
const deleteMutation = useMutation({
|
||||||
|
mutationFn: (id: number) => {
|
||||||
|
return Api.deletePost(id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card sx={{ display: 'flex', flexDirection: 'column', flexGrow: 1 }}>
|
<Card>
|
||||||
<CardHeader
|
<CardHeader
|
||||||
avatar={
|
avatar={
|
||||||
'id' in post.user ? (
|
'id' in post.user ? (
|
||||||
<MUILink component={Link} to="/profile/$id" params={{ id: post.user.id }} underline="none">
|
post.user.id !== Api.getAuthenticatedUser()?.id ? (
|
||||||
<Avatar alt={post.user.username} src={`storage/${post.user.image}`} />
|
<MUILink component={Link} to="/profile/$id" params={{ id: post.user.id }}>
|
||||||
|
<Avatar alt={post.user.username} src={`storage/${post.user.image}`}>
|
||||||
|
<Person />
|
||||||
|
</Avatar>
|
||||||
</MUILink>
|
</MUILink>
|
||||||
) : (
|
) : (
|
||||||
<Avatar alt={post.user.username} src={`storage/${post.user.image}`} />
|
<MUILink component={Link} to="/profile">
|
||||||
|
<Avatar alt={post.user.username} src={`storage/${post.user.image}`}>
|
||||||
|
<Person />
|
||||||
|
</Avatar>
|
||||||
|
</MUILink>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<Avatar alt={post.user.username} src={`storage/${post.user.image}`}>
|
||||||
|
<Person />
|
||||||
|
</Avatar>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
title={
|
title={
|
||||||
@ -42,6 +84,42 @@ const Post: FC<Props> = ({ post }) => {
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
<Typography>{post.content}</Typography>
|
<Typography>{post.content}</Typography>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
||||||
|
<CardActions>
|
||||||
|
{Api.isAdmin() && (
|
||||||
|
<>
|
||||||
|
<Button size="small" color="error" onClick={() => setOpen(true)}>
|
||||||
|
{t('Delete')}
|
||||||
|
</Button>
|
||||||
|
<Dialog open={open} onClose={() => setOpen(false)}>
|
||||||
|
<DialogTitle>{t('Confirm post delete title')}</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText>{t('Confirm post delete body', { name: post.user.username })}</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={() => setOpen(false)} autoFocus variant="contained">
|
||||||
|
{t('No')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
color="error"
|
||||||
|
onClick={() => {
|
||||||
|
deleteMutation.mutate(post.id);
|
||||||
|
setOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('Yes')}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</CardActions>
|
||||||
|
<Snackbar open={deleteMutation.isError} autoHideDuration={2000} onClose={() => deleteMutation.reset()}>
|
||||||
|
<Alert severity="error" variant="filled" sx={{ width: '100%' }}>
|
||||||
|
{deleteMutation.isError && handleError(deleteMutation.error, t, 'delete')}
|
||||||
|
</Alert>
|
||||||
|
</Snackbar>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Avatar, Box, Grid, Typography } from '@mui/material';
|
import { Person } from '@mui/icons-material';
|
||||||
|
import { Avatar, Box, Button, Card, CardActions, CardContent, Grid, Typography } from '@mui/material';
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { User } from '../../types/User';
|
import { User } from '../../types/User';
|
||||||
@ -6,28 +7,37 @@ import convertDate from '../../utils/date';
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
user: User;
|
user: User;
|
||||||
|
canEdit?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Profile: FC<Props> = ({ user }) => {
|
const Profile: FC<Props> = ({ user, canEdit }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Card>
|
||||||
|
<CardContent>
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
<Grid item sx={{ display: 'grid', gridTemplateColumns: 'fit-content(100%) 1fr', columnGap: 2, rowGap: 1 }}>
|
<Grid item sx={{ display: 'flex', flexGrow: 1, justifyContent: 'center' }}>
|
||||||
<Box sx={{ gridColumn: '1/3', display: 'flex', justifyContent: 'center' }}>
|
<Avatar alt={user.username} src={`storage/${user.image}`} sx={{ width: '100px', height: '100px' }}>
|
||||||
<Avatar alt={user.username} src={`storage/${user.image}`} sx={{ width: 100, height: 100 }} />
|
<Person sx={{ width: '60px', height: '60px' }} />
|
||||||
</Box>
|
</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 fontWeight="bold">{t('Username')}:</Typography>
|
||||||
<Typography>{user.username}</Typography>
|
<Typography>{user.username}</Typography>
|
||||||
|
<Typography fontWeight="bold">{t('Email')}:</Typography>
|
||||||
|
<Typography>{user.email}</Typography>
|
||||||
<Typography fontWeight="bold">{t('Member since')}:</Typography>
|
<Typography fontWeight="bold">{t('Member since')}:</Typography>
|
||||||
<Typography>{convertDate(user.memberSince)}</Typography>
|
<Typography>{convertDate(user.memberSince)}</Typography>
|
||||||
<Typography fontWeight="bold">{t('Post count')}:</Typography>
|
<Typography fontWeight="bold">{t('Post count')}:</Typography>
|
||||||
<Typography>{user.postCount}</Typography>
|
<Typography>{user.postCount}</Typography>
|
||||||
</Grid>
|
|
||||||
<Grid item></Grid>
|
|
||||||
</Grid>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</CardContent>
|
||||||
|
<CardActions>{canEdit && <Button size="small">{t('Edit')}</Button>}</CardActions>
|
||||||
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { Box } from '@mui/material';
|
||||||
import { QueryClient, useQueryErrorResetBoundary } from '@tanstack/react-query';
|
import { QueryClient, useQueryErrorResetBoundary } from '@tanstack/react-query';
|
||||||
import { createRootRouteWithContext, ErrorRouteComponent, Outlet, redirect, useRouter } from '@tanstack/react-router';
|
import { createRootRouteWithContext, ErrorRouteComponent, Outlet, redirect, useRouter } from '@tanstack/react-router';
|
||||||
import { TanStackRouterDevtools } from '@tanstack/router-devtools';
|
import { TanStackRouterDevtools } from '@tanstack/router-devtools';
|
||||||
@ -13,7 +14,11 @@ const Root = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header />
|
<Header />
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
|
||||||
|
<Box sx={{ maxWidth: '800px' }}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
{process.env.NODE_ENV === 'development' && <TanStackRouterDevtools />}
|
{process.env.NODE_ENV === 'development' && <TanStackRouterDevtools />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -22,7 +22,7 @@ const Home = () => {
|
|||||||
<Snackbar open={isFetching} message={t('Updating')} />
|
<Snackbar open={isFetching} message={t('Updating')} />
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
{postsQuery.data.map((post) => (
|
{postsQuery.data.map((post) => (
|
||||||
<Grid key={post.id} item xs={12} md={6} lg={4} sx={{ display: 'flex' }}>
|
<Grid item xs={12} key={post.id}>
|
||||||
<Post post={post} />
|
<Post post={post} />
|
||||||
</Grid>
|
</Grid>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ const ProfilePage = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Snackbar open={isFetching} message={t('Updating')} />
|
<Snackbar open={isFetching} message={t('Updating')} />
|
||||||
<Profile user={profileQuery} />
|
<Profile user={profileQuery} canEdit={Api.isAdmin()} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -30,8 +30,9 @@ export const Route = createFileRoute(`${ROUTES.PROFILE}/$id`)({
|
|||||||
stringify: ({ id }) => ({ id: id.toString() }),
|
stringify: ({ id }) => ({ id: id.toString() }),
|
||||||
},
|
},
|
||||||
loader: ({ context: { queryClient }, params: { id } }) => queryClient.ensureQueryData(profileQueryOptions(id)),
|
loader: ({ context: { queryClient }, params: { id } }) => queryClient.ensureQueryData(profileQueryOptions(id)),
|
||||||
beforeLoad: () => {
|
beforeLoad: ({ params: { id } }) => {
|
||||||
if (!Api.hasAuth()) throw redirect({ to: ROUTES.INDEX });
|
if (!Api.hasAuth()) throw redirect({ to: ROUTES.INDEX });
|
||||||
|
if (id === Api.getAuthenticatedUser()?.id) throw redirect({ to: ROUTES.PROFILE });
|
||||||
},
|
},
|
||||||
component: ProfilePage,
|
component: ProfilePage,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const ProfilePage = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Snackbar open={isFetching} message={t('Updating')} />
|
<Snackbar open={isFetching} message={t('Updating')} />
|
||||||
<Profile user={profileQuery} />
|
<Profile user={profileQuery} canEdit={true} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -15,10 +15,11 @@ export enum ERRORS {
|
|||||||
const handleError = (
|
const handleError = (
|
||||||
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
error: any,
|
error: any,
|
||||||
context?: string,
|
|
||||||
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
t: TFunction<'translation', undefined> | ((..._in: any) => any) = (..._in: any) => _in
|
t: TFunction<'translation', undefined> | ((..._in: any) => any) = (..._in: any) => _in,
|
||||||
|
context?: string
|
||||||
): string => {
|
): string => {
|
||||||
|
console.log(context);
|
||||||
if (!error) return t('', {});
|
if (!error) return t('', {});
|
||||||
|
|
||||||
if (error.code) {
|
if (error.code) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user