First Commit
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
import Image from 'next/image';
|
||||
import { lusitana } from '@/app/ui/fonts';
|
||||
import Search from '@/app/ui/search';
|
||||
import {
|
||||
CustomersTableType,
|
||||
FormattedCustomersTable,
|
||||
} from '@/app/lib/definitions';
|
||||
import Search from '../search';
|
||||
import { CustomersTable, FormattedCustomersTable } from '@/app/lib/definitions';
|
||||
|
||||
export default async function CustomersTable({
|
||||
customers,
|
||||
|
||||
@@ -13,10 +13,10 @@ const iconMap = {
|
||||
invoices: InboxIcon,
|
||||
};
|
||||
|
||||
export default async function CardWrapper() {
|
||||
export default async function Cards() {
|
||||
return (
|
||||
<>
|
||||
{/* NOTE: Uncomment this code in Chapter 9 */}
|
||||
{/* NOTE: comment in this code when you get to this point in the course */}
|
||||
|
||||
{/* <Card title="Collected" value={totalPaidInvoices} type="collected" />
|
||||
<Card title="Pending" value={totalPendingInvoices} type="pending" />
|
||||
|
||||
@@ -9,12 +9,12 @@ export default async function LatestInvoices({
|
||||
latestInvoices: LatestInvoice[];
|
||||
}) {
|
||||
return (
|
||||
<div className="flex w-full flex-col md:col-span-4">
|
||||
<div className="flex w-full flex-col md:col-span-4 lg:col-span-4">
|
||||
<h2 className={`${lusitana.className} mb-4 text-xl md:text-2xl`}>
|
||||
Latest Invoices
|
||||
</h2>
|
||||
<div className="flex grow flex-col justify-between rounded-xl bg-gray-50 p-4">
|
||||
{/* NOTE: Uncomment this code in Chapter 7 */}
|
||||
{/* NOTE: comment in this code when you get to this point in the course */}
|
||||
|
||||
{/* <div className="bg-white px-6">
|
||||
{latestInvoices.map((invoice, i) => {
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
UserGroupIcon,
|
||||
HomeIcon,
|
||||
DocumentDuplicateIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import clsx from 'clsx';
|
||||
|
||||
// Map of links to display in the side navigation.
|
||||
// Depending on the size of the application, this would be stored in a database.
|
||||
@@ -17,19 +22,25 @@ const links = [
|
||||
];
|
||||
|
||||
export default function NavLinks() {
|
||||
return (
|
||||
const pathname = usePathname();
|
||||
return (
|
||||
<>
|
||||
{links.map((link) => {
|
||||
const LinkIcon = link.icon;
|
||||
return (
|
||||
<a
|
||||
<Link
|
||||
key={link.name}
|
||||
href={link.href}
|
||||
className="flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3"
|
||||
className={clsx(
|
||||
'flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3',
|
||||
{
|
||||
'bg-sky-100 text-blue-600': pathname === link.href,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<LinkIcon className="w-6" />
|
||||
<p className="hidden md:block">{link.name}</p>
|
||||
</a>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
|
||||
@@ -15,7 +15,7 @@ export default async function RevenueChart({
|
||||
revenue: Revenue[];
|
||||
}) {
|
||||
const chartHeight = 350;
|
||||
// NOTE: Uncomment this code in Chapter 7
|
||||
// NOTE: comment in this code when you get to this point in the course
|
||||
|
||||
// const { yAxisLabels, topLabel } = generateYAxis(revenue);
|
||||
|
||||
@@ -28,7 +28,7 @@ export default async function RevenueChart({
|
||||
<h2 className={`${lusitana.className} mb-4 text-xl md:text-2xl`}>
|
||||
Recent Revenue
|
||||
</h2>
|
||||
{/* NOTE: Uncomment this code in Chapter 7 */}
|
||||
{/* NOTE: comment in this code when you get to this point in the course */}
|
||||
|
||||
{/* <div className="rounded-xl bg-gray-50 p-4">
|
||||
<div className="sm:grid-cols-13 mt-0 grid grid-cols-12 items-end gap-2 rounded-md bg-white p-4 md:gap-4">
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function SideNav() {
|
||||
<NavLinks />
|
||||
<div className="hidden h-auto w-full grow rounded-md bg-gray-50 md:block"></div>
|
||||
<form>
|
||||
<button className="flex h-[48px] w-full grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3">
|
||||
<button className="flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3">
|
||||
<PowerIcon className="w-6" />
|
||||
<div className="hidden md:block">Sign Out</div>
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Inter, Lusitana } from 'next/font/google';
|
||||
|
||||
export const inter = Inter({ subsets: ['latin'] });
|
||||
|
||||
export const lusitana = Lusitana({
|
||||
weight: ['400', '700'],
|
||||
subsets: ['latin'],
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
.shape {
|
||||
height: 0;
|
||||
width: 0;
|
||||
border-bottom: 30px solid black;
|
||||
border-left: 20px solid transparent;
|
||||
border-right: 20px solid transparent;
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export function UpdateInvoice({ id }: { id: string }) {
|
||||
export function DeleteInvoice({ id }: { id: string }) {
|
||||
return (
|
||||
<>
|
||||
<button type="submit" className="rounded-md border p-2 hover:bg-gray-100">
|
||||
<button className="rounded-md border p-2 hover:bg-gray-100">
|
||||
<span className="sr-only">Delete</span>
|
||||
<TrashIcon className="w-5" />
|
||||
</button>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import { CustomerField } from '@/app/lib/definitions';
|
||||
import Link from 'next/link';
|
||||
import {
|
||||
@@ -6,7 +8,7 @@ import {
|
||||
CurrencyDollarIcon,
|
||||
UserCircleIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { Button } from '@/app/ui/button';
|
||||
import { Button } from '../button';
|
||||
|
||||
export default function Form({ customers }: { customers: CustomerField[] }) {
|
||||
return (
|
||||
@@ -21,7 +23,7 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
|
||||
<select
|
||||
id="customer"
|
||||
name="customerId"
|
||||
className="peer block w-full cursor-pointer rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500"
|
||||
className="peer block w-full rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500"
|
||||
defaultValue=""
|
||||
>
|
||||
<option value="" disabled>
|
||||
@@ -55,13 +57,14 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
|
||||
<CurrencyDollarIcon className="pointer-events-none absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500 peer-focus:text-gray-900" />
|
||||
</div>
|
||||
</div>
|
||||
s
|
||||
</div>
|
||||
|
||||
{/* Invoice Status */}
|
||||
<fieldset>
|
||||
<legend className="mb-2 block text-sm font-medium">
|
||||
<div>
|
||||
<label htmlFor="status" className="mb-2 block text-sm font-medium">
|
||||
Set the invoice status
|
||||
</legend>
|
||||
</label>
|
||||
<div className="rounded-md border border-gray-200 bg-white px-[14px] py-3">
|
||||
<div className="flex gap-4">
|
||||
<div className="flex items-center">
|
||||
@@ -70,11 +73,11 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
|
||||
name="status"
|
||||
type="radio"
|
||||
value="pending"
|
||||
className="h-4 w-4 cursor-pointer border-gray-300 bg-gray-100 text-gray-600 focus:ring-2"
|
||||
className="h-4 w-4 border-gray-300 bg-gray-100 text-gray-600 focus:ring-2 focus:ring-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-gray-600"
|
||||
/>
|
||||
<label
|
||||
htmlFor="pending"
|
||||
className="ml-2 flex cursor-pointer items-center gap-1.5 rounded-full bg-gray-100 px-3 py-1.5 text-xs font-medium text-gray-600"
|
||||
className="ml-2 flex items-center gap-1.5 rounded-full bg-gray-100 px-3 py-1.5 text-xs font-medium text-gray-600 dark:text-gray-300"
|
||||
>
|
||||
Pending <ClockIcon className="h-4 w-4" />
|
||||
</label>
|
||||
@@ -85,18 +88,18 @@ export default function Form({ customers }: { customers: CustomerField[] }) {
|
||||
name="status"
|
||||
type="radio"
|
||||
value="paid"
|
||||
className="h-4 w-4 cursor-pointer border-gray-300 bg-gray-100 text-gray-600 focus:ring-2"
|
||||
className="h-4 w-4 border-gray-300 bg-gray-100 text-gray-600 focus:ring-2 focus:ring-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-gray-600"
|
||||
/>
|
||||
<label
|
||||
htmlFor="paid"
|
||||
className="ml-2 flex cursor-pointer items-center gap-1.5 rounded-full bg-green-500 px-3 py-1.5 text-xs font-medium text-white"
|
||||
className="ml-2 flex items-center gap-1.5 rounded-full bg-green-500 px-3 py-1.5 text-xs font-medium text-white dark:text-gray-300"
|
||||
>
|
||||
Paid <CheckIcon className="h-4 w-4" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-6 flex justify-end gap-4">
|
||||
<Link
|
||||
|
||||
@@ -20,6 +20,8 @@ export default function EditInvoiceForm({
|
||||
return (
|
||||
<form>
|
||||
<div className="rounded-md bg-gray-50 p-4 md:p-6">
|
||||
{/* Invoice ID */}
|
||||
<input type="hidden" name="id" value={invoice.id} />
|
||||
{/* Customer Name */}
|
||||
<div className="mb-4">
|
||||
<label htmlFor="customer" className="mb-2 block text-sm font-medium">
|
||||
@@ -29,7 +31,7 @@ export default function EditInvoiceForm({
|
||||
<select
|
||||
id="customer"
|
||||
name="customerId"
|
||||
className="peer block w-full cursor-pointer rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500"
|
||||
className="peer block w-full rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500"
|
||||
defaultValue={invoice.customer_id}
|
||||
>
|
||||
<option value="" disabled>
|
||||
@@ -56,7 +58,6 @@ export default function EditInvoiceForm({
|
||||
id="amount"
|
||||
name="amount"
|
||||
type="number"
|
||||
step="0.01"
|
||||
defaultValue={invoice.amount}
|
||||
placeholder="Enter USD amount"
|
||||
className="peer block w-full rounded-md border border-gray-200 py-2 pl-10 text-sm outline-2 placeholder:text-gray-500"
|
||||
@@ -67,10 +68,10 @@ export default function EditInvoiceForm({
|
||||
</div>
|
||||
|
||||
{/* Invoice Status */}
|
||||
<fieldset>
|
||||
<legend className="mb-2 block text-sm font-medium">
|
||||
<div>
|
||||
<label htmlFor="status" className="mb-2 block text-sm font-medium">
|
||||
Set the invoice status
|
||||
</legend>
|
||||
</label>
|
||||
<div className="rounded-md border border-gray-200 bg-white px-[14px] py-3">
|
||||
<div className="flex gap-4">
|
||||
<div className="flex items-center">
|
||||
@@ -80,11 +81,11 @@ export default function EditInvoiceForm({
|
||||
type="radio"
|
||||
value="pending"
|
||||
defaultChecked={invoice.status === 'pending'}
|
||||
className="h-4 w-4 cursor-pointer border-gray-300 bg-gray-100 text-gray-600 focus:ring-2"
|
||||
className="h-4 w-4 border-gray-300 bg-gray-100 text-gray-600 focus:ring-2 focus:ring-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-gray-600"
|
||||
/>
|
||||
<label
|
||||
htmlFor="pending"
|
||||
className="ml-2 flex cursor-pointer items-center gap-1.5 rounded-full bg-gray-100 px-3 py-1.5 text-xs font-medium text-gray-600"
|
||||
className="ml-2 flex items-center gap-1.5 rounded-full bg-gray-100 px-3 py-1.5 text-xs font-medium text-gray-600 dark:text-gray-300"
|
||||
>
|
||||
Pending <ClockIcon className="h-4 w-4" />
|
||||
</label>
|
||||
@@ -96,18 +97,18 @@ export default function EditInvoiceForm({
|
||||
type="radio"
|
||||
value="paid"
|
||||
defaultChecked={invoice.status === 'paid'}
|
||||
className="h-4 w-4 cursor-pointer border-gray-300 bg-gray-100 text-gray-600 focus:ring-2"
|
||||
className="h-4 w-4 border-gray-300 bg-gray-100 text-gray-600 focus:ring-2 focus:ring-gray-500 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-gray-600"
|
||||
/>
|
||||
<label
|
||||
htmlFor="paid"
|
||||
className="ml-2 flex cursor-pointer items-center gap-1.5 rounded-full bg-green-500 px-3 py-1.5 text-xs font-medium text-white"
|
||||
className="ml-2 flex items-center gap-1.5 rounded-full bg-green-500 px-3 py-1.5 text-xs font-medium text-white dark:text-gray-300"
|
||||
>
|
||||
Paid <CheckIcon className="h-4 w-4" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-6 flex justify-end gap-4">
|
||||
<Link
|
||||
|
||||
@@ -6,13 +6,13 @@ import Link from 'next/link';
|
||||
import { generatePagination } from '@/app/lib/utils';
|
||||
|
||||
export default function Pagination({ totalPages }: { totalPages: number }) {
|
||||
// NOTE: Uncomment this code in Chapter 10
|
||||
// NOTE: comment in this code when you get to this point in the course
|
||||
|
||||
// const allPages = generatePagination(currentPage, totalPages);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* NOTE: Uncomment this code in Chapter 10 */}
|
||||
{/* NOTE: comment in this code when you get to this point in the course */}
|
||||
|
||||
{/* <div className="inline-flex">
|
||||
<PaginationArrow
|
||||
@@ -32,7 +32,7 @@ export default function Pagination({ totalPages }: { totalPages: number }) {
|
||||
|
||||
return (
|
||||
<PaginationNumber
|
||||
key={`${page}-${index}`}
|
||||
key={page}
|
||||
href={createPageURL(page)}
|
||||
page={page}
|
||||
position={position}
|
||||
|
||||
@@ -55,9 +55,7 @@ export default function LoginForm() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button className="mt-4 w-full">
|
||||
Log in <ArrowRightIcon className="ml-auto h-5 w-5 text-gray-50" />
|
||||
</Button>
|
||||
<LoginButton />
|
||||
<div className="flex h-8 items-end space-x-1">
|
||||
{/* Add form errors here */}
|
||||
</div>
|
||||
@@ -65,3 +63,11 @@ export default function LoginForm() {
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
function LoginButton() {
|
||||
return (
|
||||
<Button className="mt-4 w-full">
|
||||
Log in <ArrowRightIcon className="ml-auto h-5 w-5 text-gray-50" />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export function RevenueChartSkeleton() {
|
||||
<div className={`${shimmer} relative w-full overflow-hidden md:col-span-4`}>
|
||||
<div className="mb-4 h-8 w-36 rounded-md bg-gray-100" />
|
||||
<div className="rounded-xl bg-gray-100 p-4">
|
||||
<div className="sm:grid-cols-13 mt-0 grid h-[410px] grid-cols-12 items-end gap-2 rounded-md bg-white p-4 md:gap-4" />
|
||||
<div className="sm:grid-cols-13 mt-0 grid h-[410px] grid-cols-12 items-end gap-2 rounded-md bg-white p-4 md:gap-4" />
|
||||
<div className="flex items-center pb-2 pt-6">
|
||||
<div className="h-5 w-5 rounded-full bg-gray-200" />
|
||||
<div className="ml-2 h-4 w-20 rounded-md bg-gray-200" />
|
||||
@@ -62,7 +62,7 @@ export function InvoiceSkeleton() {
|
||||
export function LatestInvoicesSkeleton() {
|
||||
return (
|
||||
<div
|
||||
className={`${shimmer} relative flex w-full flex-col overflow-hidden md:col-span-4`}
|
||||
className={`${shimmer} relative flex w-full flex-col overflow-hidden md:col-span-4 lg:col-span-4`}
|
||||
>
|
||||
<div className="mb-4 h-8 w-36 rounded-md bg-gray-100" />
|
||||
<div className="flex grow flex-col justify-between rounded-xl bg-gray-100 p-4">
|
||||
@@ -72,10 +72,10 @@ export function LatestInvoicesSkeleton() {
|
||||
<InvoiceSkeleton />
|
||||
<InvoiceSkeleton />
|
||||
<InvoiceSkeleton />
|
||||
</div>
|
||||
<div className="flex items-center pb-2 pt-6">
|
||||
<div className="h-5 w-5 rounded-full bg-gray-200" />
|
||||
<div className="ml-2 h-4 w-20 rounded-md bg-gray-200" />
|
||||
<div className="flex items-center pb-2 pt-6">
|
||||
<div className="h-5 w-5 rounded-full bg-gray-200" />
|
||||
<div className="ml-2 h-4 w-20 rounded-md bg-gray-200" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user