redesign the homepage
Build & Deploy / Build & Push Docker Image (push) Failing after 47s
Build & Deploy / Deploy to VPS (push) Has been skipped
Test / API Unit Tests (push) Failing after 5m4s
Test / Marketplace Unit Tests (push) Failing after 4m55s
Test / Admin Unit Tests (push) Successful in 9m37s
Test / Dashboard Unit Tests (push) Successful in 9m37s
Test / API Integration Tests (push) Successful in 9m54s

This commit is contained in:
root
2026-06-26 16:27:21 -04:00
parent 256ff0814e
commit d7fb7b7a7b
1030 changed files with 107374 additions and 2657 deletions
@@ -0,0 +1,124 @@
'use client'
import { useEffect, useState } from 'react'
import { apiFetch } from '@/lib/api'
import { useDashboardI18n } from '@/components/I18nProvider'
interface CustomerRow {
id: string
firstName: string
lastName: string
email: string
phone: string | null
flagged: boolean
licenseValidationStatus: string
licenseImageUrl: string | null
}
export default function CustomersPage() {
const { language } = useDashboardI18n()
const [rows, setRows] = useState<CustomerRow[]>([])
const [error, setError] = useState<string | null>(null)
const copy = {
en: {
title: 'Customers',
subtitle: 'A company CRM with license validation and risk flags.',
customer: 'Customer',
contact: 'Contact',
license: 'License',
flags: 'Flags',
imageUploaded: 'Image uploaded',
imageMissing: 'No image',
noPhone: 'No phone',
flagged: 'Flagged',
clear: 'Clear',
empty: 'No customers yet.',
},
fr: {
title: 'Clients',
subtitle: 'CRM de lentreprise avec validation des permis et indicateurs de risque.',
customer: 'Client',
contact: 'Contact',
license: 'Permis',
flags: 'Signalements',
imageUploaded: 'Image téléversée',
imageMissing: 'Aucune image',
noPhone: 'Pas de téléphone',
flagged: 'Signalé',
clear: 'Aucun risque',
empty: 'Aucun client pour le moment.',
},
ar: {
title: 'العملاء',
subtitle: 'نظام إدارة عملاء خاص بالشركة مع التحقق من الرخص ومؤشرات المخاطر.',
customer: 'العميل',
contact: 'التواصل',
license: 'الرخصة',
flags: 'العلامات',
imageUploaded: 'تم رفع الصورة',
imageMissing: 'لا توجد صورة',
noPhone: 'لا يوجد هاتف',
flagged: 'تم تمييزه',
clear: 'لا توجد مخاطر',
empty: 'لا يوجد عملاء حتى الآن.',
},
}[language]
useEffect(() => {
apiFetch<CustomerRow[]>('/customers?pageSize=100')
.then((result) => setRows(result ?? []))
.catch((err) => setError(err.message))
}, [])
return (
<div className="space-y-6">
<div className="rdg-page-heading">
<h2 className="text-xl font-semibold text-slate-900">{copy.title}</h2>
<p className="text-sm text-slate-500 mt-1">{copy.subtitle}</p>
</div>
<div className="card overflow-hidden">
{error ? (
<div className="p-8 text-sm text-red-600">{error}</div>
) : (
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="bg-slate-50 border-b border-slate-200">
<th className="text-left px-6 py-3 text-xs font-medium text-slate-500 uppercase tracking-wide">{copy.customer}</th>
<th className="text-left px-6 py-3 text-xs font-medium text-slate-500 uppercase tracking-wide">{copy.contact}</th>
<th className="text-left px-6 py-3 text-xs font-medium text-slate-500 uppercase tracking-wide">{copy.license}</th>
<th className="text-left px-6 py-3 text-xs font-medium text-slate-500 uppercase tracking-wide">{copy.flags}</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100">
{rows.map((row) => (
<tr key={row.id}>
<td className="px-6 py-4 text-sm font-semibold text-slate-900">{row.firstName} {row.lastName}</td>
<td className="px-6 py-4">
<p className="text-sm text-slate-700">{row.email}</p>
<p className="text-xs text-slate-500">{row.phone ?? copy.noPhone}</p>
</td>
<td className="px-6 py-4">
<div className="flex flex-wrap items-center gap-2">
<span className="badge-gray">{row.licenseValidationStatus}</span>
<span className={row.licenseImageUrl ? 'badge-green' : 'badge-gray'}>
{row.licenseImageUrl ? copy.imageUploaded : copy.imageMissing}
</span>
</div>
</td>
<td className="px-6 py-4">{row.flagged ? <span className="badge-red">{copy.flagged}</span> : <span className="badge-green">{copy.clear}</span>}</td>
</tr>
))}
{rows.length === 0 && (
<tr>
<td colSpan={4} className="px-6 py-10 text-center text-sm text-slate-400">{copy.empty}</td>
</tr>
)}
</tbody>
</table>
</div>
)}
</div>
</div>
)
}