feat(app): add dashboard pages for settings, admin, and public user profiles
Introduce new pages for profile settings, admin dashboard (users, contents, categories), and public user profiles. Enhance profile functionality with avatar uploads and bio updates. Include help and improved content trends/recent pages. Streamline content display using `HomeContent`.
This commit is contained in:
185
frontend/src/app/(dashboard)/settings/page.tsx
Normal file
185
frontend/src/app/(dashboard)/settings/page.tsx
Normal file
@@ -0,0 +1,185 @@
|
||||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Loader2, Save, User as UserIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
import * as z from "zod";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import { useAuth } from "@/providers/auth-provider";
|
||||
import { UserService } from "@/services/user.service";
|
||||
|
||||
const settingsSchema = z.object({
|
||||
displayName: z.string().max(32, "Le nom d'affichage est trop long").optional(),
|
||||
bio: z.string().max(255, "La bio est trop longue").optional(),
|
||||
});
|
||||
|
||||
type SettingsFormValues = z.infer<typeof settingsSchema>;
|
||||
|
||||
export default function SettingsPage() {
|
||||
const { user, isLoading, refreshUser } = useAuth();
|
||||
const [isSaving, setIsSaving] = React.useState(false);
|
||||
|
||||
const form = useForm<SettingsFormValues>({
|
||||
resolver: zodResolver(settingsSchema),
|
||||
defaultValues: {
|
||||
displayName: "",
|
||||
bio: "",
|
||||
},
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
if (user) {
|
||||
form.reset({
|
||||
displayName: user.displayName || "",
|
||||
bio: (user as any).bio || "",
|
||||
});
|
||||
}
|
||||
}, [user, form]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex h-[400px] items-center justify-center">
|
||||
<Spinner className="h-8 w-8 text-primary" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto py-8 px-4 text-center">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Accès refusé</CardTitle>
|
||||
<CardDescription>
|
||||
Vous devez être connecté pour accéder aux paramètres.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const onSubmit = async (values: SettingsFormValues) => {
|
||||
setIsSaving(true);
|
||||
try {
|
||||
await UserService.updateMe(values);
|
||||
toast.success("Paramètres mis à jour !");
|
||||
await refreshUser();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error("Erreur lors de la mise à jour des paramètres.");
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto py-12 px-4">
|
||||
<div className="flex items-center gap-3 mb-8">
|
||||
<div className="bg-primary/10 p-3 rounded-xl">
|
||||
<UserIcon className="h-6 w-6 text-primary" />
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold">Paramètres du profil</h1>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Informations personnelles</CardTitle>
|
||||
<CardDescription>
|
||||
Mettez à jour vos informations publiques. Ces données seront visibles par les autres utilisateurs.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||
<div className="grid gap-4">
|
||||
<FormItem>
|
||||
<FormLabel>Nom d'utilisateur</FormLabel>
|
||||
<FormControl>
|
||||
<Input value={user.username} disabled className="bg-zinc-50 dark:bg-zinc-900" />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Le nom d'utilisateur ne peut pas être modifié.
|
||||
</FormDescription>
|
||||
</FormItem>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="displayName"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nom d'affichage</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Votre nom" {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Le nom qui sera affiché sur votre profil et vos mèmes.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="bio"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Bio</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
placeholder="Racontez-nous quelque chose sur vous..."
|
||||
className="resize-none"
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Une courte description de vous (max 255 caractères).
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button type="submit" disabled={isSaving} className="w-full sm:w-auto">
|
||||
{isSaving ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
Enregistrement...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Save className="mr-2 h-4 w-4" />
|
||||
Enregistrer les modifications
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user