Update May 21 by Elvis

This commit is contained in:
belviskhoremk
2026-05-21 22:24:22 +00:00
parent 57f3311278
commit 342ba2c867
12 changed files with 71 additions and 59 deletions

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback, startTransition } from "react";
import { Card } from "@/components/ui/card"; import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -34,7 +34,7 @@ export default function AdminClients() {
} }
}, [query]); }, [query]);
useEffect(() => { load(); }, [load]); useEffect(() => { startTransition(() => load()); }, [load]);
const handleSearch = (e: React.FormEvent) => { const handleSearch = (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback, startTransition } from "react";
import { Card } from "@/components/ui/card"; import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -56,7 +56,7 @@ export default function AdminCommandes() {
} }
}, [filter]); }, [filter]);
useEffect(() => { load(); }, [load]); useEffect(() => { startTransition(() => load()); }, [load]);
const handleStatus = async (id: string, status: string) => { const handleStatus = async (id: string, status: string) => {
setUpdating(id); setUpdating(id);

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState, startTransition } from "react";
import { Package, CalendarCheck, Clock, TrendingUp, ShoppingBag, Users, AlertTriangle, Euro } from "lucide-react"; import { Package, CalendarCheck, Clock, TrendingUp, ShoppingBag, Users, AlertTriangle, Euro } from "lucide-react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
@@ -8,6 +8,41 @@ import { useAdmin } from "@/contexts/AdminContext";
import { useLanguage } from "@/contexts/LanguageContext"; import { useLanguage } from "@/contexts/LanguageContext";
import { getDashboardStats, DashboardStats } from "@/lib/api/admin"; import { getDashboardStats, DashboardStats } from "@/lib/api/admin";
function Skeleton() {
return (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{Array.from({ length: 4 }).map((_, i) => (
<Card key={i}>
<CardContent className="p-5">
<div className="h-4 bg-muted animate-pulse rounded w-2/3 mb-3" />
<div className="h-7 bg-muted animate-pulse rounded w-1/3" />
</CardContent>
</Card>
))}
</div>
);
}
function StatGrid({ cards }: { cards: { label: string; value: string | number; icon: React.ElementType; color: string; bg: string }[] }) {
return (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{cards.map((s) => (
<Card key={s.label}>
<CardContent className="p-5 flex items-center justify-between">
<div>
<p className="text-sm text-muted-foreground">{s.label}</p>
<p className="text-2xl font-semibold mt-1">{s.value}</p>
</div>
<div className={`h-10 w-10 rounded-full ${s.bg} flex items-center justify-center`}>
<s.icon className={`h-5 w-5 ${s.color}`} />
</div>
</CardContent>
</Card>
))}
</div>
);
}
export default function AdminOverview() { export default function AdminOverview() {
const { reservations } = useAdmin(); const { reservations } = useAdmin();
const { t, locale } = useLanguage(); const { t, locale } = useLanguage();
@@ -40,37 +75,6 @@ export default function AdminOverview() {
] ]
: []; : [];
const Skeleton = () => (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{Array.from({ length: 4 }).map((_, i) => (
<Card key={i}>
<CardContent className="p-5">
<div className="h-4 bg-muted animate-pulse rounded w-2/3 mb-3" />
<div className="h-7 bg-muted animate-pulse rounded w-1/3" />
</CardContent>
</Card>
))}
</div>
);
const StatGrid = ({ cards }: { cards: { label: string; value: string | number; icon: React.ElementType; color: string; bg: string }[] }) => (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{cards.map((s) => (
<Card key={s.label}>
<CardContent className="p-5 flex items-center justify-between">
<div>
<p className="text-sm text-muted-foreground">{s.label}</p>
<p className="text-2xl font-semibold mt-1">{s.value}</p>
</div>
<div className={`h-10 w-10 rounded-full ${s.bg} flex items-center justify-center`}>
<s.icon className={`h-5 w-5 ${s.color}`} />
</div>
</CardContent>
</Card>
))}
</div>
);
return ( return (
<div className="space-y-8"> <div className="space-y-8">
<div> <div>

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback, startTransition } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@@ -263,7 +263,7 @@ function CalendarTab() {
.finally(() => setLoadingSlots(false)); .finally(() => setLoadingSlots(false));
}, [year, month]); }, [year, month]);
useEffect(() => { loadSlots(); }, [loadSlots]); useEffect(() => { startTransition(() => loadSlots()); }, [loadSlots]);
const prevMonth = () => { const prevMonth = () => {
if (month === 0) { setYear((y) => y - 1); setMonth(11); } if (month === 0) { setYear((y) => y - 1); setMonth(11); }

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect, startTransition } from "react";
import { Card } from "@/components/ui/card"; import { Card } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@@ -54,7 +54,7 @@ export default function AdminServices() {
} }
}; };
useEffect(() => { load(); }, []); useEffect(() => { startTransition(() => load()); }, []);
const openCreate = () => { const openCreate = () => {
setEditing(null); setEditing(null);

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { Suspense, useState, useEffect } from "react"; import { Suspense, useState, useEffect, startTransition } from "react";
import { useLanguage } from "@/contexts/LanguageContext"; import { useLanguage } from "@/contexts/LanguageContext";
import { useSearchParams, useRouter } from "next/navigation"; import { useSearchParams, useRouter } from "next/navigation";
import { categories } from "@/data/products"; import { categories } from "@/data/products";
@@ -21,7 +21,7 @@ function ShopContent() {
}; };
useEffect(() => { useEffect(() => {
setLoading(true); startTransition(() => setLoading(true));
listProducts({ listProducts({
per_page: 100, per_page: 100,
category: selectedCategory === "all" ? undefined : selectedCategory, category: selectedCategory === "all" ? undefined : selectedCategory,

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect, startTransition } from "react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useAuth } from "@/contexts/AuthContext"; import { useAuth } from "@/contexts/AuthContext";
import { useLanguage } from "@/contexts/LanguageContext"; import { useLanguage } from "@/contexts/LanguageContext";
@@ -40,8 +40,10 @@ export default function MonCompte() {
useEffect(() => { useEffect(() => {
if (user) { if (user) {
setName(user.full_name ?? ""); startTransition(() => {
setPhone(user.phone ?? ""); setName(user.full_name ?? "");
setPhone(user.phone ?? "");
});
} }
}, [user]); }, [user]);

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect, startTransition } from "react";
import { Star, ChevronLeft, Check } from "lucide-react"; import { Star, ChevronLeft, Check } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useLanguage } from "@/contexts/LanguageContext"; import { useLanguage } from "@/contexts/LanguageContext";
@@ -26,7 +26,7 @@ export default function ProductDetail() {
useEffect(() => { useEffect(() => {
if (!id) return; if (!id) return;
setLoading(true); startTransition(() => setLoading(true));
getProduct(id as string) getProduct(id as string)
.then((p) => { .then((p) => {
setProduct(p); setProduct(p);

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect, startTransition } from "react";
import { Calendar } from "@/components/ui/calendar"; import { Calendar } from "@/components/ui/calendar";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@@ -43,16 +43,20 @@ export default function Booking() {
useEffect(() => { useEffect(() => {
if (user) { if (user) {
setName(user.full_name ?? ""); startTransition(() => {
setEmail(user.email ?? ""); setName(user.full_name ?? "");
setPhone(user.phone ?? ""); setEmail(user.email ?? "");
setPhone(user.phone ?? "");
});
} }
}, [user]); }, [user]);
useEffect(() => { useEffect(() => {
if (!selectedDate) return; if (!selectedDate) return;
setSlotsLoading(true); startTransition(() => {
setSelectedSlot(null); setSlotsLoading(true);
setSelectedSlot(null);
});
const dateStr = toDateStr(selectedDate); const dateStr = toDateStr(selectedDate);
getAvailableSlots(dateStr, dateStr) getAvailableSlots(dateStr, dateStr)
.then(setSlots) .then(setSlots)

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import React, { useContext, useState, useEffect, ReactNode, createContext } from "react"; import React, { useContext, useState, useEffect, startTransition, ReactNode, createContext } from "react";
import { useAuth } from "@/contexts/AuthContext"; import { useAuth } from "@/contexts/AuthContext";
import * as productsApi from "@/lib/api/products"; import * as productsApi from "@/lib/api/products";
import * as bookingsApi from "@/lib/api/bookings"; import * as bookingsApi from "@/lib/api/bookings";
@@ -82,8 +82,10 @@ export const AdminProvider = ({ children }: { children: ReactNode }) => {
useEffect(() => { useEffect(() => {
if (isAdmin) { if (isAdmin) {
refreshProducts(); startTransition(() => {
refreshReservations(); refreshProducts();
refreshReservations();
});
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [isAdmin]); }, [isAdmin]);

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import React, { createContext, useContext, useState, useEffect, ReactNode } from "react"; import React, { createContext, useContext, useState, useEffect, startTransition, ReactNode } from "react";
import { getToken } from "@/lib/api"; import { getToken } from "@/lib/api";
import * as authApi from "@/lib/api/auth"; import * as authApi from "@/lib/api/auth";
@@ -24,7 +24,7 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
useEffect(() => { useEffect(() => {
const token = getToken(); const token = getToken();
if (!token) { if (!token) {
setIsLoading(false); startTransition(() => setIsLoading(false));
return; return;
} }
authApi.getMe() authApi.getMe()

View File

@@ -11,7 +11,7 @@ export function useIsMobile() {
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
} }
mql.addEventListener("change", onChange) mql.addEventListener("change", onChange)
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) React.startTransition(() => setIsMobile(window.innerWidth < MOBILE_BREAKPOINT))
return () => mql.removeEventListener("change", onChange) return () => mql.removeEventListener("change", onChange)
}, []) }, [])