synchronisation du contenu avec la DB et amélioration du CMS
- Migration du blog et des interviews des données mockées vers PostgreSQL (Prisma). - Refonte des pages publiques en Server Components pour de meilleures performances/SEO. - Intégration d'un éditeur de texte riche (React Quill) avec titres H1-H6 et séparateurs. - Correction des erreurs de validation Prisma liées aux paramètres asynchrones (Next.js 15+). - Correction des problèmes d'affichage des modales de suspension via React Portals. - Installation de @tailwindcss/typography et correction du débordement de texte (break-words). - Implémentation des actions de modération (suspension/révocation) pour les utilisateurs et boutiques.
This commit is contained in:
110
admin/src/app/clients/page.tsx
Normal file
110
admin/src/app/clients/page.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import { getClients } from "@/app/actions/user";
|
||||
import { User, Mail, Phone, MapPin } from "lucide-react";
|
||||
import ClientActions from "@/components/ClientActions";
|
||||
|
||||
export default async function ClientsPage() {
|
||||
const result = await getClients();
|
||||
const clients = result.success ? (result.data || []) : [];
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-white mb-2">Gestion des Clients</h1>
|
||||
<p className="text-slate-400">Liste des utilisateurs n'ayant pas encore de boutique active.</p>
|
||||
</div>
|
||||
<div className="bg-slate-800/50 px-4 py-2 rounded-lg border border-slate-700/50">
|
||||
<span className="text-slate-400 text-sm">Total Clients: </span>
|
||||
<span className="text-indigo-400 font-bold ml-1">{clients.length}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Utilisateur</th>
|
||||
<th>Contact</th>
|
||||
<th>Localisation</th>
|
||||
<th>Statut</th>
|
||||
<th className="text-right">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-800">
|
||||
{clients.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={5} className="py-12 text-center text-slate-500">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<User className="w-8 h-8 opacity-20" />
|
||||
<p>Aucun client trouvé.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
clients.map((client: any) => (
|
||||
<tr key={client.id} className={`hover:bg-slate-800/30 transition-colors ${client.isSuspended ? 'opacity-60 grayscale-[0.5]' : ''}`}>
|
||||
<td className="py-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-full bg-slate-800 flex items-center justify-center text-slate-400 font-bold text-sm overflow-hidden border border-slate-700">
|
||||
{client.avatar ? (
|
||||
<img src={client.avatar} alt={client.name} className="w-full h-full object-cover" />
|
||||
) : (
|
||||
client.name.charAt(0)
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-white font-semibold">{client.name}</p>
|
||||
<p className="text-slate-500 text-xs">ID: {client.id.split('-')[0]}...</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-1.5 text-xs text-slate-300">
|
||||
<Mail className="w-3 h-3 text-slate-500" />
|
||||
{client.email}
|
||||
</div>
|
||||
{client.phone && (
|
||||
<div className="flex items-center gap-1.5 text-xs text-slate-300">
|
||||
<Phone className="w-3 h-3 text-slate-500" />
|
||||
{client.phone}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="flex items-center gap-1.5 text-xs text-slate-400">
|
||||
<MapPin className="w-3 h-3" />
|
||||
{client.location || "Non spécifié"}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="flex flex-col gap-1">
|
||||
{client.isSuspended ? (
|
||||
<>
|
||||
<span className="badge badge-error">Compte Suspendu</span>
|
||||
<span className="text-[10px] text-rose-500/70 truncate max-w-[150px]" title={client.suspensionReason}>
|
||||
{client.suspensionReason}
|
||||
</span>
|
||||
</>
|
||||
) : client.businesses ? (
|
||||
<span className="badge badge-pending">Shop Inactif</span>
|
||||
) : (
|
||||
<span className="badge bg-slate-800 text-slate-400">Actif</span>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-right">
|
||||
<ClientActions userId={client.id} userName={client.name} isSuspended={!!client.isSuspended} />
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user