Merge pull request #4 from Tria-plc/feat/customer_registration

add customers page
This commit is contained in:
yaschalew10
2026-05-15 14:44:00 +03:00
committed by GitHub
4 changed files with 228 additions and 0 deletions

View File

@@ -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",

View File

@@ -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 />} />

View File

@@ -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
View File

@@ -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