mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
Merge pull request #4 from Tria-plc/feat/customer_registration
add customers page
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
"@tria-plc/iamui-common": "1.1.1",
|
"@tria-plc/iamui-common": "1.1.1",
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"lucide-react": "^1.14.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-router-dom": "^6.27.0",
|
"react-router-dom": "^6.27.0",
|
||||||
|
|||||||
@@ -17,9 +17,11 @@ import BillingPage from "./pages/billing/BillingPage";
|
|||||||
import TrainsPage from "./pages/trains/TrainsPage";
|
import TrainsPage from "./pages/trains/TrainsPage";
|
||||||
import DashboardPage from "./pages/dashboard/DashboardPage";
|
import DashboardPage from "./pages/dashboard/DashboardPage";
|
||||||
import { IamLoginPage } from "@tria-plc/iamui-common";
|
import { IamLoginPage } from "@tria-plc/iamui-common";
|
||||||
|
import CustomersPage from "./pages/customers/CustomersPage";
|
||||||
|
|
||||||
const sidebarItems: SidebarItem[] = [
|
const sidebarItems: SidebarItem[] = [
|
||||||
{ label: "Dashboard", href: "/" },
|
{ label: "Dashboard", href: "/" },
|
||||||
|
{ label: "Customers", href: "/Customers" },
|
||||||
{ label: "Bookings", href: "/bookings" },
|
{ label: "Bookings", href: "/bookings" },
|
||||||
{ label: "Consignments", href: "/consignments" },
|
{ label: "Consignments", href: "/consignments" },
|
||||||
{ label: "Tracking", href: "/tracking" },
|
{ label: "Tracking", href: "/tracking" },
|
||||||
@@ -50,6 +52,7 @@ const App = () => {
|
|||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<DashboardPage />} />
|
<Route path="/" element={<DashboardPage />} />
|
||||||
<Route path="/bookings" element={<BookingsPage />} />
|
<Route path="/bookings" element={<BookingsPage />} />
|
||||||
|
<Route path="/customers" element={<CustomersPage />} />
|
||||||
<Route path="/bookings/new" element={<CreateBookingPage />} />
|
<Route path="/bookings/new" element={<CreateBookingPage />} />
|
||||||
<Route path="/bookings/:id" element={<BookingDetailPage />} />
|
<Route path="/bookings/:id" element={<BookingDetailPage />} />
|
||||||
<Route path="/consignments" element={<ConsignmentsPage />} />
|
<Route path="/consignments" element={<ConsignmentsPage />} />
|
||||||
|
|||||||
@@ -0,0 +1,221 @@
|
|||||||
|
export default function CustomerPage() {
|
||||||
|
const customers = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'Abel Tesfaye',
|
||||||
|
email: 'abel@example.com',
|
||||||
|
company: 'Addis Logistics',
|
||||||
|
status: 'Active',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'Sara Bekele',
|
||||||
|
email: 'sara@example.com',
|
||||||
|
company: 'Blue Nile Trading',
|
||||||
|
status: 'Pending',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: 'Henok Alemu',
|
||||||
|
email: 'henok@example.com',
|
||||||
|
company: 'Ethio Freight',
|
||||||
|
status: 'Inactive',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-slate-50 p-6">
|
||||||
|
<div className="mx-auto max-w-7xl space-y-6">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex flex-col gap-4 rounded-3xl bg-white p-6 shadow-sm md:flex-row md:items-center md:justify-between">
|
||||||
|
<div>
|
||||||
|
<h1 className="text-3xl font-bold tracking-tight text-slate-900">
|
||||||
|
Customers
|
||||||
|
</h1>
|
||||||
|
<p className="mt-1 text-sm text-slate-500">
|
||||||
|
Manage and monitor your customer records.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<button className="inline-flex items-center gap-2 rounded-2xl border border-slate-200 bg-white px-4 py-2 text-sm font-medium text-slate-700 transition hover:bg-slate-100">
|
||||||
|
<Search className="h-4 w-4" />
|
||||||
|
Search
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button className="inline-flex items-center gap-2 rounded-2xl bg-slate-900 px-4 py-2 text-sm font-medium text-white transition hover:bg-slate-800">
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
Add Customer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Stats */}
|
||||||
|
<div className="grid gap-4 md:grid-cols-3">
|
||||||
|
<StatCard
|
||||||
|
title="Total Customers"
|
||||||
|
value="1,284"
|
||||||
|
icon={<Users className="h-5 w-5" />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<StatCard
|
||||||
|
title="Active Accounts"
|
||||||
|
value="964"
|
||||||
|
icon={<UserCheck className="h-5 w-5" />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<StatCard
|
||||||
|
title="Pending Requests"
|
||||||
|
value="48"
|
||||||
|
icon={<Clock3 className="h-5 w-5" />}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Customer Table */}
|
||||||
|
<div className="overflow-hidden rounded-3xl bg-white shadow-sm">
|
||||||
|
<div className="flex items-center justify-between border-b border-slate-100 px-6 py-4">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-lg font-semibold text-slate-900">
|
||||||
|
Customer List
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm text-slate-500">
|
||||||
|
Recent customer activities and records.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button className="inline-flex items-center gap-2 rounded-2xl border border-slate-200 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-100">
|
||||||
|
<Filter className="h-4 w-4" />
|
||||||
|
Filter
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full min-w-[700px] text-left">
|
||||||
|
<thead className="bg-slate-50 text-sm text-slate-500">
|
||||||
|
<tr>
|
||||||
|
<th className="px-6 py-4 font-medium">Customer</th>
|
||||||
|
<th className="px-6 py-4 font-medium">Company</th>
|
||||||
|
<th className="px-6 py-4 font-medium">Email</th>
|
||||||
|
<th className="px-6 py-4 font-medium">Status</th>
|
||||||
|
<th className="px-6 py-4 font-medium text-right">
|
||||||
|
Actions
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{customers.map((customer) => (
|
||||||
|
<tr
|
||||||
|
key={customer.id}
|
||||||
|
className="border-t border-slate-100 transition hover:bg-slate-50"
|
||||||
|
>
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-slate-100">
|
||||||
|
<User className="h-5 w-5 text-slate-600" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p className="font-medium text-slate-900">
|
||||||
|
{customer.name}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-slate-500">
|
||||||
|
ID #{customer.id}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td className="px-6 py-4 text-sm text-slate-700">
|
||||||
|
{customer.company}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td className="px-6 py-4 text-sm text-slate-700">
|
||||||
|
{customer.email}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
<StatusBadge status={customer.status} />
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
<div className="flex justify-end gap-2">
|
||||||
|
<button className="rounded-xl border border-slate-200 p-2 text-slate-600 hover:bg-slate-100">
|
||||||
|
<Eye className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button className="rounded-xl border border-slate-200 p-2 text-slate-600 hover:bg-slate-100">
|
||||||
|
<Pencil className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button className="rounded-xl border border-red-200 p-2 text-red-600 hover:bg-red-50">
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatCard({
|
||||||
|
title,
|
||||||
|
value,
|
||||||
|
icon,
|
||||||
|
}: {
|
||||||
|
title: string;
|
||||||
|
value: string;
|
||||||
|
icon: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="rounded-3xl bg-white p-6 shadow-sm">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-slate-500">{title}</p>
|
||||||
|
<h3 className="mt-2 text-3xl font-bold text-slate-900">
|
||||||
|
{value}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-slate-100 text-slate-700">
|
||||||
|
{icon}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatusBadge({ status }: { status: string }) {
|
||||||
|
const styles: Record<string, string> = {
|
||||||
|
Active: 'bg-emerald-100 text-emerald-700',
|
||||||
|
Pending: 'bg-amber-100 text-amber-700',
|
||||||
|
Inactive: 'bg-red-100 text-red-700',
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={`inline-flex rounded-full px-3 py-1 text-xs font-medium ${styles[status]}`}
|
||||||
|
>
|
||||||
|
{status}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
import {
|
||||||
|
Clock3,
|
||||||
|
Eye,
|
||||||
|
Filter,
|
||||||
|
Pencil,
|
||||||
|
Plus,
|
||||||
|
Search,
|
||||||
|
Trash2,
|
||||||
|
User,
|
||||||
|
UserCheck,
|
||||||
|
Users,
|
||||||
|
} from 'lucide-react';
|
||||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -229,6 +229,9 @@ importers:
|
|||||||
clsx:
|
clsx:
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
|
lucide-react:
|
||||||
|
specifier: ^1.14.0
|
||||||
|
version: 1.14.0(react@18.3.1)
|
||||||
react:
|
react:
|
||||||
specifier: 18.3.1
|
specifier: 18.3.1
|
||||||
version: 18.3.1
|
version: 18.3.1
|
||||||
|
|||||||
Reference in New Issue
Block a user