Skip to content

Commit

Permalink
✨ added customers table with skeletons
Browse files Browse the repository at this point in the history
  • Loading branch information
MammaSonnim committed Jan 13, 2025
1 parent cd08d44 commit 9737805
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 102 deletions.
29 changes: 27 additions & 2 deletions app/dashboard/customers/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
import { Metadata } from 'next';
import { Suspense } from 'react';
import Search from '@/app/ui/search';
import CustomersTable from '@/app/ui/customers/table';
import { lusitana } from '@/app/ui/fonts';
import { CustomersTableSkeleton } from '@/app/ui/skeletons';

export const metadata: Metadata = {
title: 'Customers',
};

export default function Page() {
return <p>Customers Page</p>;
export default async function Page(props: {
searchParams?: Promise<{
query?: string;
page?: string;
}>;
}) {
const searchParams = await props.searchParams;
const query = searchParams?.query ?? '';

return (
<div className="w-full">
<div className="flex w-full items-center justify-between">
<h1 className={`${lusitana.className} text-2xl`}>Customers</h1>
</div>
<div className="mt-4 flex items-center justify-between gap-2 md:mt-8">
<Search placeholder="Search customers..." />
</div>
<Suspense key={query} fallback={<CustomersTableSkeleton />}>
<CustomersTable query={query} />
</Suspense>
</div>
);
}
188 changes: 88 additions & 100 deletions app/ui/customers/table.tsx
Original file line number Diff line number Diff line change
@@ -1,89 +1,23 @@
import Image from 'next/image';
import { lusitana } from '@/app/ui/fonts';
import Search from '@/app/ui/search';
import { FormattedCustomersTable } from '@/app/lib/definitions';
import { fetchFilteredCustomers } from '@/app/lib/data';

export default function CustomersTable({
customers,
}: {
customers: FormattedCustomersTable[];
}) {
return (
<div className="w-full">
<h1 className={`${lusitana.className} mb-8 text-xl md:text-2xl`}>
Customers
</h1>
<Search placeholder="Search customers..." />
<div className="mt-6 flow-root">
<div className="overflow-x-auto">
<div className="inline-block min-w-full align-middle">
<div className="overflow-hidden rounded-md bg-gray-50 p-2 md:pt-0">
<div className="md:hidden">
{customers.map((customer) => (
<div
key={customer.id}
className="mb-2 w-full rounded-md bg-white p-4"
>
<div className="flex items-center justify-between border-b pb-4">
<div>
<div className="mb-2 flex items-center">
<div className="flex items-center gap-3">
<Image
src={customer.image_url}
className="rounded-full"
alt={`${customer.name}'s profile picture`}
width={28}
height={28}
/>
<p>{customer.name}</p>
</div>
</div>
<p className="text-sm text-gray-500">
{customer.email}
</p>
</div>
</div>
<div className="flex w-full items-center justify-between border-b py-5">
<div className="flex w-1/2 flex-col">
<p className="text-xs">Pending</p>
<p className="font-medium">{customer.total_pending}</p>
</div>
<div className="flex w-1/2 flex-col">
<p className="text-xs">Paid</p>
<p className="font-medium">{customer.total_paid}</p>
</div>
</div>
<div className="pt-4 text-sm">
<p>{customer.total_invoices} invoices</p>
</div>
</div>
))}
</div>
<table className="hidden min-w-full rounded-md text-gray-900 md:table">
<thead className="rounded-md bg-gray-50 text-left text-sm font-normal">
<tr>
<th scope="col" className="px-4 py-5 font-medium sm:pl-6">
Name
</th>
<th scope="col" className="px-3 py-5 font-medium">
Email
</th>
<th scope="col" className="px-3 py-5 font-medium">
Total Invoices
</th>
<th scope="col" className="px-3 py-5 font-medium">
Total Pending
</th>
<th scope="col" className="px-4 py-5 font-medium">
Total Paid
</th>
</tr>
</thead>
export default async function CustomersTable({ query }: { query: string }) {
const customers = await fetchFilteredCustomers(query);

<tbody className="divide-y divide-gray-200 text-gray-900">
{customers.map((customer) => (
<tr key={customer.id} className="group">
<td className="whitespace-nowrap bg-white py-5 pl-4 pr-3 text-sm text-black group-first-of-type:rounded-md group-last-of-type:rounded-md sm:pl-6">
return (
<div className="mt-6 flow-root">
<div className="overflow-x-auto">
<div className="inline-block min-w-full align-middle">
<div className="overflow-hidden rounded-md bg-gray-50 p-2 md:pt-0">
<div className="md:hidden">
{customers.map((customer) => (
<div
key={customer.id}
className="mb-2 w-full rounded-md bg-white p-4"
>
<div className="flex items-center justify-between border-b pb-4">
<div>
<div className="mb-2 flex items-center">
<div className="flex items-center gap-3">
<Image
src={customer.image_url}
Expand All @@ -94,24 +28,78 @@ export default function CustomersTable({
/>
<p>{customer.name}</p>
</div>
</td>
<td className="whitespace-nowrap bg-white px-4 py-5 text-sm">
{customer.email}
</td>
<td className="whitespace-nowrap bg-white px-4 py-5 text-sm">
{customer.total_invoices}
</td>
<td className="whitespace-nowrap bg-white px-4 py-5 text-sm">
{customer.total_pending}
</td>
<td className="whitespace-nowrap bg-white px-4 py-5 text-sm group-first-of-type:rounded-md group-last-of-type:rounded-md">
{customer.total_paid}
</td>
</tr>
))}
</tbody>
</table>
</div>
<p className="text-sm text-gray-500">{customer.email}</p>
</div>
</div>
<div className="flex w-full items-center justify-between border-b py-5">
<div className="flex w-1/2 flex-col">
<p className="text-xs">Pending</p>
<p className="font-medium">{customer.total_pending}</p>
</div>
<div className="flex w-1/2 flex-col">
<p className="text-xs">Paid</p>
<p className="font-medium">{customer.total_paid}</p>
</div>
</div>
<div className="pt-4 text-sm">
<p>{customer.total_invoices} invoices</p>
</div>
</div>
))}
</div>
<table className="hidden min-w-full rounded-md text-gray-900 md:table">
<thead className="rounded-md bg-gray-50 text-left text-sm font-normal">
<tr>
<th scope="col" className="px-4 py-5 font-medium sm:pl-6">
Name
</th>
<th scope="col" className="px-3 py-5 font-medium">
Email
</th>
<th scope="col" className="px-3 py-5 font-medium">
Total Invoices
</th>
<th scope="col" className="px-3 py-5 font-medium">
Total Pending
</th>
<th scope="col" className="px-4 py-5 font-medium">
Total Paid
</th>
</tr>
</thead>

<tbody className="divide-y divide-gray-200 text-gray-900">
{customers.map((customer) => (
<tr key={customer.id} className="group">
<td className="whitespace-nowrap bg-white py-5 pl-4 pr-3 text-sm text-black group-first-of-type:rounded-md group-last-of-type:rounded-md sm:pl-6">
<div className="flex items-center gap-3">
<Image
src={customer.image_url}
className="rounded-full"
alt={`${customer.name}'s profile picture`}
width={28}
height={28}
/>
<p>{customer.name}</p>
</div>
</td>
<td className="whitespace-nowrap bg-white px-4 py-5 text-sm">
{customer.email}
</td>
<td className="whitespace-nowrap bg-white px-4 py-5 text-sm">
{customer.total_invoices}
</td>
<td className="whitespace-nowrap bg-white px-4 py-5 text-sm">
{customer.total_pending}
</td>
<td className="whitespace-nowrap bg-white px-4 py-5 text-sm group-first-of-type:rounded-md group-last-of-type:rounded-md">
{customer.total_paid}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
Expand Down
98 changes: 98 additions & 0 deletions app/ui/skeletons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,101 @@ export function InvoicesTableSkeleton() {
</div>
);
}

export function CustomersTableRowSkeleton() {
return (
<tr className="w-full border-b border-gray-100 last-of-type:border-none [&:first-child>td:first-child]:rounded-tl-lg [&:first-child>td:last-child]:rounded-tr-lg [&:last-child>td:first-child]:rounded-bl-lg [&:last-child>td:last-child]:rounded-br-lg">
{/* Customer Name and Image */}
<td className="relative overflow-hidden whitespace-nowrap py-3 pl-6 pr-3">
<div className="flex items-center gap-3">
<div className="h-8 w-8 rounded-full bg-gray-100"></div>
<div className="h-6 w-24 rounded bg-gray-100"></div>
</div>
</td>
{/* Email */}
<td className="whitespace-nowrap px-3 py-3">
<div className="h-6 w-32 rounded bg-gray-100"></div>
</td>
{/* Total Invoices */}
<td className="whitespace-nowrap px-3 py-3">
<div className="h-6 w-16 rounded bg-gray-100"></div>
</td>
{/* Total Pending */}
<td className="whitespace-nowrap px-3 py-3">
<div className="h-6 w-16 rounded bg-gray-100"></div>
</td>
{/* Total Paid */}
<td className="whitespace-nowrap px-3 py-3">
<div className="h-6 w-16 rounded bg-gray-100"></div>
</td>
</tr>
);
}

export function CustomersMobileSkeleton() {
return (
<div className="mb-2 w-full rounded-md bg-white p-4">
<div className="flex items-center justify-between border-b border-gray-100 pb-8">
<div className="flex items-center">
<div className="mr-2 h-8 w-8 rounded-full bg-gray-100"></div>
<div className="h-6 w-16 rounded bg-gray-100"></div>
</div>
</div>
<div className="flex w-full items-center border-b border-gray-100 pb-6 pt-6">
<div className="w-1/2">
<div className="h-6 w-24 flex-col rounded bg-gray-100"></div>
</div>
<div className="h-6 w-32 flex-col rounded bg-gray-100"></div>
</div>
<div className="flex w-full items-center justify-between pt-4">
<div className="h-6 w-16 rounded bg-gray-100"></div>
</div>
</div>
);
}

export function CustomersTableSkeleton() {
return (
<div className="mt-6 flow-root">
<div className="inline-block min-w-full align-middle">
<div className="rounded-lg bg-gray-50 p-2 md:pt-0">
<div className="md:hidden">
<CustomersMobileSkeleton />
<CustomersMobileSkeleton />
<CustomersMobileSkeleton />
<CustomersMobileSkeleton />
<CustomersMobileSkeleton />
</div>
<table className="hidden min-w-full text-gray-900 md:table">
<thead className="rounded-lg text-left text-sm font-normal">
<tr>
<th scope="col" className="px-4 py-5 font-medium sm:pl-6">
Name
</th>
<th scope="col" className="px-3 py-5 font-medium">
Email
</th>
<th scope="col" className="px-3 py-5 font-medium">
Total Invoices
</th>
<th scope="col" className="px-3 py-5 font-medium">
Total Pending
</th>
<th scope="col" className="px-3 py-5 font-medium">
Total Paid
</th>
</tr>
</thead>
<tbody className="bg-white">
<CustomersTableRowSkeleton />
<CustomersTableRowSkeleton />
<CustomersTableRowSkeleton />
<CustomersTableRowSkeleton />
<CustomersTableRowSkeleton />
</tbody>
</table>
</div>
</div>
</div>
);
}

0 comments on commit 9737805

Please sign in to comment.