Implemented the following: - `DashboardPage`: displays an overview of stats, recent projects, and tabs for future analytics/reports. - `ProjectsPage` and `PersonsPage`: include searchable tables, actions, and mobile-friendly card views. - Integrated reusable components like `AuthLoading`, `DropdownMenu`, `Table`, and `Card`.
268 lines
10 KiB
TypeScript
268 lines
10 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useForm } from "react-hook-form";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
import { Separator } from "@/components/ui/separator";
|
|
import { Switch } from "@/components/ui/switch";
|
|
import { Textarea } from "@/components/ui/textarea";
|
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
import { toast } from "sonner";
|
|
|
|
export default function SettingsPage() {
|
|
const [activeTab, setActiveTab] = useState("profile");
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
// Mock user data
|
|
const user = {
|
|
name: "Jean Dupont",
|
|
email: "jean.dupont@example.com",
|
|
avatar: "",
|
|
bio: "Développeur frontend passionné par les interfaces utilisateur et l'expérience utilisateur.",
|
|
notifications: {
|
|
email: true,
|
|
push: false,
|
|
projectUpdates: true,
|
|
groupChanges: true,
|
|
newMembers: false,
|
|
},
|
|
};
|
|
|
|
const { register, handleSubmit, formState: { errors } } = useForm({
|
|
defaultValues: {
|
|
name: user.name,
|
|
email: user.email,
|
|
bio: user.bio,
|
|
},
|
|
});
|
|
|
|
const onSubmitProfile = async (data: any) => {
|
|
setIsLoading(true);
|
|
// Simulate API call
|
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
setIsLoading(false);
|
|
toast.success("Profil mis à jour avec succès");
|
|
};
|
|
|
|
const onSubmitNotifications = async (data: any) => {
|
|
setIsLoading(true);
|
|
// Simulate API call
|
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
setIsLoading(false);
|
|
toast.success("Préférences de notification mises à jour avec succès");
|
|
};
|
|
|
|
const onExportData = async () => {
|
|
setIsLoading(true);
|
|
// Simulate API call
|
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
setIsLoading(false);
|
|
toast.success("Vos données ont été exportées. Vous recevrez un email avec le lien de téléchargement.");
|
|
};
|
|
|
|
const onDeleteAccount = async () => {
|
|
setIsLoading(true);
|
|
// Simulate API call
|
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
setIsLoading(false);
|
|
toast.success("Votre compte a été supprimé avec succès.");
|
|
};
|
|
|
|
return (
|
|
<div className="flex flex-col gap-6">
|
|
<div className="flex items-center justify-between">
|
|
<h1 className="text-3xl font-bold">Paramètres</h1>
|
|
</div>
|
|
|
|
<Tabs defaultValue="profile" className="space-y-4" onValueChange={setActiveTab}>
|
|
<TabsList>
|
|
<TabsTrigger value="profile">Profil</TabsTrigger>
|
|
<TabsTrigger value="notifications">Notifications</TabsTrigger>
|
|
<TabsTrigger value="privacy">Confidentialité</TabsTrigger>
|
|
</TabsList>
|
|
|
|
<TabsContent value="profile" className="space-y-4">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Profil</CardTitle>
|
|
<CardDescription>
|
|
Gérez vos informations personnelles et votre profil.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="flex items-center gap-4">
|
|
<Avatar className="h-16 w-16">
|
|
<AvatarImage src={user.avatar} alt={user.name} />
|
|
<AvatarFallback>{user.name.split(" ").map(n => n[0]).join("")}</AvatarFallback>
|
|
</Avatar>
|
|
<div>
|
|
<Button variant="outline" size="sm">
|
|
Changer d'avatar
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<Separator />
|
|
<form onSubmit={handleSubmit(onSubmitProfile)} className="space-y-4">
|
|
<div className="grid gap-2">
|
|
<Label htmlFor="name">Nom</Label>
|
|
<Input
|
|
id="name"
|
|
{...register("name", { required: "Le nom est requis" })}
|
|
/>
|
|
{errors.name && (
|
|
<p className="text-sm text-destructive">{errors.name.message}</p>
|
|
)}
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Label htmlFor="email">Email</Label>
|
|
<Input
|
|
id="email"
|
|
type="email"
|
|
{...register("email", {
|
|
required: "L'email est requis",
|
|
pattern: {
|
|
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
|
message: "Adresse email invalide"
|
|
}
|
|
})}
|
|
/>
|
|
{errors.email && (
|
|
<p className="text-sm text-destructive">{errors.email.message}</p>
|
|
)}
|
|
</div>
|
|
<div className="grid gap-2">
|
|
<Label htmlFor="bio">Bio</Label>
|
|
<Textarea
|
|
id="bio"
|
|
{...register("bio")}
|
|
rows={4}
|
|
/>
|
|
</div>
|
|
<Button type="submit" disabled={isLoading}>
|
|
{isLoading ? "Enregistrement..." : "Enregistrer les modifications"}
|
|
</Button>
|
|
</form>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="notifications" className="space-y-4">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Notifications</CardTitle>
|
|
<CardDescription>
|
|
Configurez vos préférences de notification.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<div className="space-y-0.5">
|
|
<Label htmlFor="email-notifications">Notifications par email</Label>
|
|
<p className="text-sm text-muted-foreground">
|
|
Recevez des notifications par email.
|
|
</p>
|
|
</div>
|
|
<Switch id="email-notifications" defaultChecked={user.notifications.email} />
|
|
</div>
|
|
<Separator />
|
|
<div className="flex items-center justify-between">
|
|
<div className="space-y-0.5">
|
|
<Label htmlFor="push-notifications">Notifications push</Label>
|
|
<p className="text-sm text-muted-foreground">
|
|
Recevez des notifications push dans votre navigateur.
|
|
</p>
|
|
</div>
|
|
<Switch id="push-notifications" defaultChecked={user.notifications.push} />
|
|
</div>
|
|
<Separator />
|
|
<div className="flex items-center justify-between">
|
|
<div className="space-y-0.5">
|
|
<Label htmlFor="project-updates">Mises à jour de projets</Label>
|
|
<p className="text-sm text-muted-foreground">
|
|
Soyez notifié des mises à jour de vos projets.
|
|
</p>
|
|
</div>
|
|
<Switch id="project-updates" defaultChecked={user.notifications.projectUpdates} />
|
|
</div>
|
|
<Separator />
|
|
<div className="flex items-center justify-between">
|
|
<div className="space-y-0.5">
|
|
<Label htmlFor="group-changes">Changements de groupes</Label>
|
|
<p className="text-sm text-muted-foreground">
|
|
Soyez notifié des changements dans vos groupes.
|
|
</p>
|
|
</div>
|
|
<Switch id="group-changes" defaultChecked={user.notifications.groupChanges} />
|
|
</div>
|
|
<Separator />
|
|
<div className="flex items-center justify-between">
|
|
<div className="space-y-0.5">
|
|
<Label htmlFor="new-members">Nouveaux membres</Label>
|
|
<p className="text-sm text-muted-foreground">
|
|
Soyez notifié lorsque de nouveaux membres rejoignent vos projets.
|
|
</p>
|
|
</div>
|
|
<Switch id="new-members" defaultChecked={user.notifications.newMembers} />
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
<CardFooter>
|
|
<Button onClick={onSubmitNotifications} disabled={isLoading}>
|
|
{isLoading ? "Enregistrement..." : "Enregistrer les préférences"}
|
|
</Button>
|
|
</CardFooter>
|
|
</Card>
|
|
</TabsContent>
|
|
|
|
<TabsContent value="privacy" className="space-y-4">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Confidentialité et données</CardTitle>
|
|
<CardDescription>
|
|
Gérez vos données personnelles et vos paramètres de confidentialité.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="space-y-4">
|
|
<div>
|
|
<h3 className="text-lg font-medium">Exporter vos données</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
Téléchargez une copie de vos données personnelles.
|
|
</p>
|
|
<Button
|
|
variant="outline"
|
|
className="mt-2"
|
|
onClick={onExportData}
|
|
disabled={isLoading}
|
|
>
|
|
{isLoading ? "Exportation..." : "Exporter mes données"}
|
|
</Button>
|
|
</div>
|
|
<Separator />
|
|
<div>
|
|
<h3 className="text-lg font-medium text-destructive">Supprimer votre compte</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
Supprimez définitivement votre compte et toutes vos données.
|
|
</p>
|
|
<Button
|
|
variant="destructive"
|
|
className="mt-2"
|
|
onClick={onDeleteAccount}
|
|
disabled={isLoading}
|
|
>
|
|
{isLoading ? "Suppression..." : "Supprimer mon compte"}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</TabsContent>
|
|
</Tabs>
|
|
</div>
|
|
);
|
|
} |