feat: implement beautiful toast notifications with Sonner

- Install sonner package for toast notifications
- Add Toaster component to root layout with dark theme styling
- Replace all alert() calls with toast.success() and toast.error()
- Add descriptive messages for success states
- Toast notifications match the app's dark theme design
This commit is contained in:
Millian Lamiaux
2026-03-17 11:45:29 +01:00
parent 554ad2a352
commit 3d026b68ee
7 changed files with 39 additions and 5 deletions

View File

@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Geist } from "next/font/google";
import "./globals.css";
import { Sidebar } from "@/components/sidebar";
import { Toaster } from "sonner";
const geistSans = Geist({
variable: "--font-geist-sans",
@@ -27,6 +28,17 @@ export default function RootLayout({
{children}
</main>
</div>
<Toaster
theme="dark"
position="top-right"
toastOptions={{
style: {
background: '#171717',
border: '1px solid #262626',
color: '#fff',
},
}}
/>
</body>
</html>
);

View File

@@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Upload, Film, Image as ImageIcon, User, Loader2 } from "lucide-react";
import { toast } from "sonner";
const BUCKETS = [
{ id: "videos", label: "Videos", icon: Film, types: "video/*" },
@@ -34,10 +35,10 @@ export default function MediaPage() {
if (uploadError) throw uploadError;
alert("File uploaded successfully!");
toast.success("File uploaded successfully!");
} catch (error) {
console.error("Upload failed:", error);
alert("Upload failed: " + (error instanceof Error ? error.message : "Unknown error"));
toast.error("Upload failed: " + (error instanceof Error ? error.message : "Unknown error"));
} finally {
setUploading(false);
if (fileInputRef.current) {

View File

@@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Plus, Trash2, Edit, Loader2, Users, AlertCircle } from "lucide-react";
import Link from "next/link";
import { toast } from "sonner";
import type { Database } from "@/lib/supabase";
type Trainer = Database["public"]["Tables"]["trainers"]["Row"];
@@ -56,9 +57,12 @@ export default function TrainersPage() {
const { error } = await supabase.from("trainers").delete().eq("id", id);
if (error) throw error;
setTrainers(trainers.filter((t) => t.id !== id));
toast.success("Trainer deleted", {
description: "The trainer has been removed successfully."
});
} catch (error) {
console.error("Failed to delete trainer:", error);
alert("Failed to delete trainer");
toast.error("Failed to delete trainer");
} finally {
setDeletingId(null);
}

View File

@@ -16,6 +16,7 @@ import {
TableRow,
} from "@/components/ui/table";
import { Plus, Trash2, Edit, Loader2, Eye } from "lucide-react";
import { toast } from "sonner";
import type { Database } from "@/lib/supabase";
type Workout = Database["public"]["Tables"]["workouts"]["Row"];
@@ -54,9 +55,12 @@ export default function WorkoutsPage() {
const { error } = await supabase.from("workouts").delete().eq("id", id);
if (error) throw error;
setWorkouts(workouts.filter((w) => w.id !== id));
toast.success("Workout deleted", {
description: "The workout has been removed successfully."
});
} catch (error) {
console.error("Failed to delete workout:", error);
alert("Failed to delete workout");
toast.error("Failed to delete workout");
} finally {
setDeletingId(null);
}