From e3f3e62482440f0935ae5fec31e9d9094dadc1eb Mon Sep 17 00:00:00 2001 From: Teoto Date: Tue, 12 May 2026 17:35:38 +0300 Subject: [PATCH] update --- app/dashboard/(overview)/loading.tsx | 6 +- app/dashboard/(overview)/page.tsx | 74 +- app/dashboard/customers/page.tsx | 18 +- .../invoices/[id]/edit/not-found.tsx | 8 +- app/dashboard/invoices/[id]/edit/page.tsx | 24 +- app/dashboard/invoices/create/page.tsx | 22 +- app/dashboard/invoices/error.tsx | 12 +- app/dashboard/invoices/page.tsx | 86 +- app/dashboard/layout.tsx | 24 +- app/layout.tsx | 18 +- app/lib/actions.ts | 82 +- app/lib/data.ts | 65 +- app/lib/definitions.ts | 8 +- app/lib/placeholder-data.js | 172 +- app/lib/placeholder-data.ts | 134 +- app/lib/utils.ts | 24 +- app/login/page.tsx | 14 +- app/page.tsx | 50 +- app/query/route.ts | 14 +- app/seed/route.ts | 12 +- app/ui/acme-logo.tsx | 4 +- app/ui/button.tsx | 4 +- app/ui/customers/table.tsx | 8 +- app/ui/dashboard/cards.tsx | 12 +- app/ui/dashboard/latest-invoices.tsx | 90 +- app/ui/dashboard/nav-links.tsx | 30 +- app/ui/dashboard/revenue-chart.tsx | 71 +- app/ui/dashboard/sidenav.tsx | 23 +- app/ui/fonts.ts | 16 +- app/ui/global.css | 6 +- app/ui/home.module.css | 14 +- app/ui/invoices/breadcrumbs.tsx | 10 +- app/ui/invoices/buttons.tsx | 17 +- app/ui/invoices/create-form.tsx | 31 +- app/ui/invoices/edit-form.tsx | 14 +- app/ui/invoices/pagination.tsx | 111 +- app/ui/invoices/status.tsx | 14 +- app/ui/invoices/table.tsx | 10 +- app/ui/login-form.tsx | 34 +- app/ui/search.tsx | 23 +- app/ui/skeletons.tsx | 2 +- auth.config.ts | 12 +- auth.ts | 34 +- biome.json | 42 + eslint.config.mjs | 24 - next.config.ts | 2 +- package.json | 3 +- pnpm-lock.yaml | 2719 +---------------- proxy.ts | 12 +- tailwind.config.ts | 22 +- tsconfig.json | 14 +- 51 files changed, 882 insertions(+), 3413 deletions(-) create mode 100644 biome.json delete mode 100644 eslint.config.mjs diff --git a/app/dashboard/(overview)/loading.tsx b/app/dashboard/(overview)/loading.tsx index 01e6c609..1d3b06ec 100644 --- a/app/dashboard/(overview)/loading.tsx +++ b/app/dashboard/(overview)/loading.tsx @@ -1,5 +1,5 @@ -import DashboardSkeleton from '@/app/ui/skeletons'; - +import DashboardSkeleton from "@/app/ui/skeletons"; + export default function Loading() { return ; -} \ No newline at end of file +} diff --git a/app/dashboard/(overview)/page.tsx b/app/dashboard/(overview)/page.tsx index 26f287bb..13acd308 100644 --- a/app/dashboard/(overview)/page.tsx +++ b/app/dashboard/(overview)/page.tsx @@ -1,36 +1,38 @@ -import CardWrapper, { Card } from '@/app/ui/dashboard/cards'; -import RevenueChart from '@/app/ui/dashboard/revenue-chart'; -import LatestInvoices from '@/app/ui/dashboard/latest-invoices'; -import { lusitana } from '@/app/ui/fonts'; -import { Suspense } from 'react'; -import { CardSkeleton, LatestInvoicesSkeleton, RevenueChartSkeleton } from '@/app/ui/skeletons'; -import { Metadata } from 'next'; - -export const metadata: Metadata = { - title: 'Dashboard', -}; -export default async function Page() { - - return ( -
-

- Dashboard -

-
- }> - - -
-
- - }> - - - - }> - - -
-
- ); -} \ No newline at end of file +import type { Metadata } from "next"; +import { Suspense } from "react"; +import CardWrapper from "@/app/ui/dashboard/cards"; +import LatestInvoices from "@/app/ui/dashboard/latest-invoices"; +import RevenueChart from "@/app/ui/dashboard/revenue-chart"; +import { lusitana } from "@/app/ui/fonts"; +import { + CardSkeleton, + LatestInvoicesSkeleton, + RevenueChartSkeleton, +} from "@/app/ui/skeletons"; + +export const metadata: Metadata = { + title: "Dashboard", +}; +export default async function Page() { + return ( +
+

+ Dashboard +

+
+ }> + + +
+
+ }> + + + + }> + + +
+
+ ); +} diff --git a/app/dashboard/customers/page.tsx b/app/dashboard/customers/page.tsx index e308725b..60071ea9 100644 --- a/app/dashboard/customers/page.tsx +++ b/app/dashboard/customers/page.tsx @@ -1,9 +1,9 @@ -export default function Page() { - return

Customers Page

; - -} -import { Metadata } from 'next'; - -export const metadata: Metadata = { - title: 'Customers', -}; \ No newline at end of file +export default function Page() { + return

Customers Page

; +} + +import type { Metadata } from "next"; + +export const metadata: Metadata = { + title: "Customers", +}; diff --git a/app/dashboard/invoices/[id]/edit/not-found.tsx b/app/dashboard/invoices/[id]/edit/not-found.tsx index 13733063..6b246e13 100644 --- a/app/dashboard/invoices/[id]/edit/not-found.tsx +++ b/app/dashboard/invoices/[id]/edit/not-found.tsx @@ -1,6 +1,6 @@ -import Link from 'next/link'; -import { FaceFrownIcon } from '@heroicons/react/24/outline'; - +import { FaceFrownIcon } from "@heroicons/react/24/outline"; +import Link from "next/link"; + export default function NotFound() { return (
@@ -15,4 +15,4 @@ export default function NotFound() {
); -} \ No newline at end of file +} diff --git a/app/dashboard/invoices/[id]/edit/page.tsx b/app/dashboard/invoices/[id]/edit/page.tsx index c10042ac..122e4265 100644 --- a/app/dashboard/invoices/[id]/edit/page.tsx +++ b/app/dashboard/invoices/[id]/edit/page.tsx @@ -1,11 +1,11 @@ -import Form from '@/app/ui/invoices/edit-form'; -import Breadcrumbs from '@/app/ui/invoices/breadcrumbs'; -import { fetchInvoiceById, fetchCustomers } from '@/app/lib/data'; -import { notFound } from 'next/navigation'; -import { Metadata } from 'next'; - +import type { Metadata } from "next"; +import { notFound } from "next/navigation"; +import { fetchCustomers, fetchInvoiceById } from "@/app/lib/data"; +import Breadcrumbs from "@/app/ui/invoices/breadcrumbs"; +import Form from "@/app/ui/invoices/edit-form"; + export const metadata: Metadata = { - title: 'Edit invoices', + title: "Edit invoices", }; export default async function Page(props: { params: Promise<{ id: string }> }) { @@ -16,17 +16,17 @@ export default async function Page(props: { params: Promise<{ id: string }> }) { fetchCustomers(), ]); -if (!invoice) { + if (!invoice) { notFound(); -} + } return (
); -} \ No newline at end of file +} diff --git a/app/dashboard/invoices/create/page.tsx b/app/dashboard/invoices/create/page.tsx index 03bc879e..160ddff3 100644 --- a/app/dashboard/invoices/create/page.tsx +++ b/app/dashboard/invoices/create/page.tsx @@ -1,23 +1,23 @@ -import Form from '@/app/ui/invoices/create-form'; -import Breadcrumbs from '@/app/ui/invoices/breadcrumbs'; -import { fetchCustomers } from '@/app/lib/data'; -import { Metadata } from 'next'; - +import type { Metadata } from "next"; +import { fetchCustomers } from "@/app/lib/data"; +import Breadcrumbs from "@/app/ui/invoices/breadcrumbs"; +import Form from "@/app/ui/invoices/create-form"; + export const metadata: Metadata = { - title: 'Create invoices', + title: "Create invoices", }; export default async function Page() { const customers = await fetchCustomers(); - + return (
); -} \ No newline at end of file +} diff --git a/app/dashboard/invoices/error.tsx b/app/dashboard/invoices/error.tsx index e285214f..dcf11027 100644 --- a/app/dashboard/invoices/error.tsx +++ b/app/dashboard/invoices/error.tsx @@ -1,7 +1,7 @@ -'use client'; - -import { useEffect } from 'react'; - +"use client"; + +import { useEffect } from "react"; + export default function Error({ error, reset, @@ -13,7 +13,7 @@ export default function Error({ // Optionally log the error to an error reporting service console.error(error); }, [error]); - + return (

Something went wrong!

@@ -28,4 +28,4 @@ export default function Error({
); -} \ No newline at end of file +} diff --git a/app/dashboard/invoices/page.tsx b/app/dashboard/invoices/page.tsx index 8c363469..5516268a 100644 --- a/app/dashboard/invoices/page.tsx +++ b/app/dashboard/invoices/page.tsx @@ -1,43 +1,43 @@ -import Pagination from '@/app/ui/invoices/pagination'; -import Search from '@/app/ui/search'; -import Table from '@/app/ui/invoices/table'; -import { CreateInvoice } from '@/app/ui/invoices/buttons'; -import { lusitana } from '@/app/ui/fonts'; -import { Suspense } from 'react'; -import { InvoicesTableSkeleton } from '@/app/ui/skeletons'; -import { fetchInvoicesPages } from '@/app/lib/data'; -import { Metadata } from 'next'; - -export const metadata: Metadata = { - title: 'Invoices', -}; - -export default async function Page(props: { - searchParams?: Promise<{ - query?: string; - page?: string; - }>; -}) { - const searchParams = await props.searchParams; - const query = searchParams?.query || ''; - const currentPage = Number(searchParams?.page) || 1; - const totalPages = await fetchInvoicesPages(query); - - return ( -
-
-

Invoices

-
-
- - -
- }> - - -
- -
- - ); -} \ No newline at end of file +import type { Metadata } from "next"; +import { Suspense } from "react"; +import { fetchInvoicesPages } from "@/app/lib/data"; +import { lusitana } from "@/app/ui/fonts"; +import { CreateInvoice } from "@/app/ui/invoices/buttons"; +import Pagination from "@/app/ui/invoices/pagination"; +import Table from "@/app/ui/invoices/table"; +import Search from "@/app/ui/search"; +import { InvoicesTableSkeleton } from "@/app/ui/skeletons"; + +export const metadata: Metadata = { + title: "Invoices", +}; + +export default async function Page(props: { + searchParams?: Promise<{ + query?: string; + page?: string; + }>; +}) { + const searchParams = await props.searchParams; + const query = searchParams?.query || ""; + const currentPage = Number(searchParams?.page) || 1; + const totalPages = await fetchInvoicesPages(query); + + return ( +
+
+

Invoices

+
+
+ + +
+ }> +
+ +
+ +
+ + ); +} diff --git a/app/dashboard/layout.tsx b/app/dashboard/layout.tsx index cea40fe9..95c72f66 100644 --- a/app/dashboard/layout.tsx +++ b/app/dashboard/layout.tsx @@ -1,12 +1,12 @@ -import SideNav from '@/app/ui/dashboard/sidenav'; - -export default function Layout({ children }: { children: React.ReactNode }) { - return ( -
-
- -
-
{children}
-
- ); -} \ No newline at end of file +import SideNav from "@/app/ui/dashboard/sidenav"; + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( +
+
+ +
+
{children}
+
+ ); +} diff --git a/app/layout.tsx b/app/layout.tsx index 6a96e628..de5a5ad1 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,14 +1,14 @@ -import '@/app/ui/global.css'; -import { inter } from '@/app/ui/fonts'; -import { Metadata } from 'next'; - +import "@/app/ui/global.css"; +import type { Metadata } from "next"; +import { inter } from "@/app/ui/fonts"; + export const metadata: Metadata = { title: { - template: '%s | Acme Dashboard', - default: 'Acme Dashboard', + template: "%s | Acme Dashboard", + default: "Acme Dashboard", }, - description: 'The official Next.js Learn Dashboard built with App Router.', - metadataBase: new URL('https://next-learn-dashboard.vercel.sh'), + description: "The official Next.js Learn Dashboard built with App Router.", + metadataBase: new URL("https://next-learn-dashboard.vercel.sh"), }; export default function RootLayout({ @@ -21,4 +21,4 @@ export default function RootLayout({ {children} ); -} \ No newline at end of file +} diff --git a/app/lib/actions.ts b/app/lib/actions.ts index febfa35b..f08093f1 100644 --- a/app/lib/actions.ts +++ b/app/lib/actions.ts @@ -1,24 +1,24 @@ -'use server'; +"use server"; -import { z } from 'zod'; -import { revalidatePath } from 'next/cache'; -import { redirect } from 'next/navigation'; -import postgres from 'postgres'; -import { signIn } from '@/auth'; -import { AuthError } from 'next-auth'; +import { revalidatePath } from "next/cache"; +import { redirect } from "next/navigation"; +import { AuthError } from "next-auth"; +import postgres from "postgres"; +import { z } from "zod"; +import { signIn } from "@/auth"; -const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' }); +const sql = postgres(process.env.POSTGRES_URL!, { ssl: "require" }); const FormSchema = z.object({ id: z.string(), customerId: z.string({ - invalid_type_error: 'Please select a customer', + invalid_type_error: "Please select a customer", }), amount: z.coerce .number() - .gt(0, { message: 'Please enter an amount greater than $0.' }), - status: z.enum(['pending', 'paid'], { - invalid_type_error: 'Please select an invoice status. ', + .gt(0, { message: "Please enter an amount greater than $0." }), + status: z.enum(["pending", "paid"], { + invalid_type_error: "Please select an invoice status. ", }), date: z.string(), }); @@ -35,53 +35,53 @@ export type State = { message?: string | null; }; -export async function createInvoice(prevState: State, formData: FormData) { +export async function createInvoice(_prevState: State, formData: FormData) { const validateFields = CreateInvoice.safeParse({ - customerId: formData.get('customerId'), - amount: formData.get('amount'), - status: formData.get('status'), + customerId: formData.get("customerId"), + amount: formData.get("amount"), + status: formData.get("status"), }); if (!validateFields.success) { return { errors: validateFields.error.flatten().fieldErrors, - message: 'Missing Fields. Failed to Create Invoice. ', + message: "Missing Fields. Failed to Create Invoice. ", }; } const { customerId, amount, status } = validateFields.data; const amountInCents = amount * 100; - const date = new Date().toISOString().split('T')[0]; + const date = new Date().toISOString().split("T")[0]; try { await sql` INSERT INTO invoices (customer_id, amount, status, date) VALUES (${customerId}, ${amountInCents}, ${status}, ${date}) `; - } catch (error) { + } catch (_error) { return { - message: 'Database Error: Failed to Create Invoice.', + message: "Database Error: Failed to Create Invoice.", }; } - revalidatePath('/dashboard/invoices'); - redirect('/dashboard/invoices'); + revalidatePath("/dashboard/invoices"); + redirect("/dashboard/invoices"); } export async function updateInvoice( id: string, - prevState: State, + _prevState: State, formData: FormData, ) { const validateFields = UpdateInvoice.safeParse({ - customerId: formData.get('customerId'), - amount: formData.get('amount'), - status: formData.get('status'), + customerId: formData.get("customerId"), + amount: formData.get("amount"), + status: formData.get("status"), }); if (!validateFields.success) { return { errors: validateFields.error.flatten().fieldErrors, - message: 'Missing Fields. Failed to update Invoice', + message: "Missing Fields. Failed to update Invoice", }; } @@ -93,14 +93,14 @@ export async function updateInvoice( SET customer_id = ${customerId}, amount = ${amountInCents}, status = ${status} WHERE id = ${id} `; - } catch (error) { + } catch (_error) { return { - message: 'Database Failed: Error while updating the Invoice', + message: "Database Failed: Error while updating the Invoice", }; } - revalidatePath('/dashboard/invoices'); - redirect('/dashboard/invoices'); + revalidatePath("/dashboard/invoices"); + redirect("/dashboard/invoices"); } export async function deleteInvoice(id: string) { @@ -110,30 +110,30 @@ export async function deleteInvoice(id: string) { DELETE FROM invoices WHERE id = ${id} `; - revalidatePath('/dashboard/invoices'); - return { message: 'Deleted Invoice. ' }; - } catch (error) { + revalidatePath("/dashboard/invoices"); + return { message: "Deleted Invoice. " }; + } catch (_error) { return { - message: 'Database Error: Failed while deleting record from Invoice. ', + message: "Database Error: Failed while deleting record from Invoice. ", }; } } export async function authenticate( - prevState: string | undefined, + _prevState: string | undefined, formData: FormData, ) { try { - await signIn('credentials', formData); + await signIn("credentials", formData); } catch (error) { if (error instanceof AuthError) { switch (error.type) { - case 'CredentialsSignin': - return 'Invalid credentials'; + case "CredentialsSignin": + return "Invalid credentials"; default: - return 'Something went wrong.'; + return "Something went wrong."; } } throw error; } -} \ No newline at end of file +} diff --git a/app/lib/data.ts b/app/lib/data.ts index 31e1a0ac..f3be146c 100644 --- a/app/lib/data.ts +++ b/app/lib/data.ts @@ -1,15 +1,16 @@ -import postgres from 'postgres'; -import { +import postgres from "postgres"; +import type { CustomerField, CustomersTable, InvoiceForm, InvoicesTable, LatestInvoiceRaw, - User, Revenue, -} from './definitions'; -import { formatCurrency } from './utils'; -const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' }); + User, +} from "./definitions"; +import { formatCurrency } from "./utils"; + +const sql = postgres(process.env.POSTGRES_URL!, { ssl: "require" }); export async function fetchRevenue() { // Add noStore() here prevent the response from being cached. // This is equivalent to in fetch(..., {cache: 'no-store'}). @@ -18,17 +19,17 @@ export async function fetchRevenue() { // Artificially delay a reponse for demo purposes. // Don't do this in real life :) - console.log('Fetching revenue data...'); - await new Promise((resolve) => setTimeout(resolve, 3000)); + console.log("Fetching revenue data..."); + await new Promise((resolve) => setTimeout(resolve, 3000)); - const data = sql`SELECT * FROM revenue`; + const data = sql`SELECT * FROM revenue`; - console.log('Data fetch complete after 3 seconds.'); + console.log("Data fetch complete after 3 seconds."); return data; } catch (error) { - console.error('Database Error:', error); - throw new Error('Failed to fetch revenue data.'); + console.error("Database Error:", error); + throw new Error("Failed to fetch revenue data."); } } @@ -47,8 +48,8 @@ export async function fetchLatestInvoices() { })); return latestInvoices; } catch (error) { - console.error('Database Error:', error); - throw new Error('Failed to fetch the latest invoices.'); + console.error("Database Error:", error); + throw new Error("Failed to fetch the latest invoices."); } } @@ -70,10 +71,10 @@ export async function fetchCardData() { invoiceStatusPromise, ]); - const numberOfInvoices = Number(data[0][0].count ?? '0'); - const numberOfCustomers = Number(data[1][0].count ?? '0'); - const totalPaidInvoices = formatCurrency(data[2][0].paid ?? '0'); - const totalPendingInvoices = formatCurrency(data[2][0].pending ?? '0'); + const numberOfInvoices = Number(data[0][0].count ?? "0"); + const numberOfCustomers = Number(data[1][0].count ?? "0"); + const totalPaidInvoices = formatCurrency(data[2][0].paid ?? "0"); + const totalPendingInvoices = formatCurrency(data[2][0].pending ?? "0"); return { numberOfCustomers, @@ -82,8 +83,8 @@ export async function fetchCardData() { totalPendingInvoices, }; } catch (error) { - console.error('Database Error:', error); - throw new Error('Failed to card data.'); + console.error("Database Error:", error); + throw new Error("Failed to card data."); } } @@ -118,8 +119,8 @@ export async function fetchFilteredInvoices( return invoices; } catch (error) { - console.error('Database Error:', error); - throw new Error('Failed to fetch invoices.'); + console.error("Database Error:", error); + throw new Error("Failed to fetch invoices."); } } @@ -139,8 +140,8 @@ export async function fetchInvoicesPages(query: string) { const totalPages = Math.ceil(Number(count[0].count) / ITEMS_PER_PAGE); return totalPages; } catch (error) { - console.error('Database Error:', error); - throw new Error('Failed to fetch total number of invoices.'); + console.error("Database Error:", error); + throw new Error("Failed to fetch total number of invoices."); } } @@ -165,7 +166,7 @@ export async function fetchInvoiceById(id: string) { console.log(invoice); // Invoice is an empty array [] return invoice[0]; } catch (error) { - console.error('Database Error:', error); + console.error("Database Error:", error); } } @@ -182,8 +183,8 @@ export async function fetchCustomers() { const customers = data; return customers; } catch (err) { - console.error('Database Error:', err); - throw new Error('Failed to fetch all customers.'); + console.error("Database Error:", err); + throw new Error("Failed to fetch all customers."); } } @@ -215,8 +216,8 @@ export async function fetchFilteredCustomers(query: string) { return customers; } catch (err) { - console.error('Database Error:', err); - throw new Error('Failed to fetch customer table.'); + console.error("Database Error:", err); + throw new Error("Failed to fetch customer table."); } } @@ -225,7 +226,7 @@ export async function getUser(email: string) { const user = await sql`SELECT * from USERS where email=${email}`; return user[0] as User; } catch (error) { - console.error('Failed to fetch user:', error); - throw new Error('Failed to fetch user.'); + console.error("Failed to fetch user:", error); + throw new Error("Failed to fetch user."); } -} \ No newline at end of file +} diff --git a/app/lib/definitions.ts b/app/lib/definitions.ts index 610eb546..941886c4 100644 --- a/app/lib/definitions.ts +++ b/app/lib/definitions.ts @@ -23,7 +23,7 @@ export type Invoice = { date: string; // In TypeScript, this is called a string union type. // It means that the "status" property can only be one of the two strings: 'pending' or 'paid'. - status: 'pending' | 'paid'; + status: "pending" | "paid"; }; export type Revenue = { @@ -40,7 +40,7 @@ export type LatestInvoice = { }; // The database returns a number for amount, but we later format it to a string with the formatCurrency function -export type LatestInvoiceRaw = Omit & { +export type LatestInvoiceRaw = Omit & { amount: number; }; @@ -52,7 +52,7 @@ export type InvoicesTable = { image_url: string; date: string; amount: number; - status: 'pending' | 'paid'; + status: "pending" | "paid"; }; export type CustomersTable = { @@ -84,5 +84,5 @@ export type InvoiceForm = { id: string; customer_id: string; amount: number; - status: 'pending' | 'paid'; + status: "pending" | "paid"; }; diff --git a/app/lib/placeholder-data.js b/app/lib/placeholder-data.js index 15a41565..be007eea 100644 --- a/app/lib/placeholder-data.js +++ b/app/lib/placeholder-data.js @@ -2,73 +2,73 @@ // https://nextjs.org/learn/dashboard-app/fetching-data const users = [ { - id: '410544b2-4001-4271-9855-fec4b6a6442a', - name: 'User', - email: 'user@nextmail.com', - password: '123456', + id: "410544b2-4001-4271-9855-fec4b6a6442a", + name: "User", + email: "user@nextmail.com", + password: "123456", }, ]; const customers = [ { - id: '3958dc9e-712f-4377-85e9-fec4b6a6442a', - name: 'Delba de Oliveira', - email: 'delba@oliveira.com', - image_url: '/customers/delba-de-oliveira.png', + id: "3958dc9e-712f-4377-85e9-fec4b6a6442a", + name: "Delba de Oliveira", + email: "delba@oliveira.com", + image_url: "/customers/delba-de-oliveira.png", }, { - id: '3958dc9e-742f-4377-85e9-fec4b6a6442a', - name: 'Lee Robinson', - email: 'lee@robinson.com', - image_url: '/customers/lee-robinson.png', + id: "3958dc9e-742f-4377-85e9-fec4b6a6442a", + name: "Lee Robinson", + email: "lee@robinson.com", + image_url: "/customers/lee-robinson.png", }, { - id: '3958dc9e-737f-4377-85e9-fec4b6a6442a', - name: 'Hector Simpson', - email: 'hector@simpson.com', - image_url: '/customers/hector-simpson.png', + id: "3958dc9e-737f-4377-85e9-fec4b6a6442a", + name: "Hector Simpson", + email: "hector@simpson.com", + image_url: "/customers/hector-simpson.png", }, { - id: '50ca3e18-62cd-11ee-8c99-0242ac120002', - name: 'Steven Tey', - email: 'steven@tey.com', - image_url: '/customers/steven-tey.png', + id: "50ca3e18-62cd-11ee-8c99-0242ac120002", + name: "Steven Tey", + email: "steven@tey.com", + image_url: "/customers/steven-tey.png", }, { - id: '3958dc9e-787f-4377-85e9-fec4b6a6442a', - name: 'Steph Dietz', - email: 'steph@dietz.com', - image_url: '/customers/steph-dietz.png', + id: "3958dc9e-787f-4377-85e9-fec4b6a6442a", + name: "Steph Dietz", + email: "steph@dietz.com", + image_url: "/customers/steph-dietz.png", }, { - id: '76d65c26-f784-44a2-ac19-586678f7c2f2', - name: 'Michael Novotny', - email: 'michael@novotny.com', - image_url: '/customers/michael-novotny.png', + id: "76d65c26-f784-44a2-ac19-586678f7c2f2", + name: "Michael Novotny", + email: "michael@novotny.com", + image_url: "/customers/michael-novotny.png", }, { - id: 'd6e15727-9fe1-4961-8c5b-ea44a9bd81aa', - name: 'Evil Rabbit', - email: 'evil@rabbit.com', - image_url: '/customers/evil-rabbit.png', + id: "d6e15727-9fe1-4961-8c5b-ea44a9bd81aa", + name: "Evil Rabbit", + email: "evil@rabbit.com", + image_url: "/customers/evil-rabbit.png", }, { - id: '126eed9c-c90c-4ef6-a4a8-fcf7408d3c66', - name: 'Emil Kowalski', - email: 'emil@kowalski.com', - image_url: '/customers/emil-kowalski.png', + id: "126eed9c-c90c-4ef6-a4a8-fcf7408d3c66", + name: "Emil Kowalski", + email: "emil@kowalski.com", + image_url: "/customers/emil-kowalski.png", }, { - id: 'CC27C14A-0ACF-4F4A-A6C9-D45682C144B9', - name: 'Amy Burns', - email: 'amy@burns.com', - image_url: '/customers/amy-burns.png', + id: "CC27C14A-0ACF-4F4A-A6C9-D45682C144B9", + name: "Amy Burns", + email: "amy@burns.com", + image_url: "/customers/amy-burns.png", }, { - id: '13D07535-C59E-4157-A011-F8D2EF4E0CBB', - name: 'Balazs Orban', - email: 'balazs@orban.com', - image_url: '/customers/balazs-orban.png', + id: "13D07535-C59E-4157-A011-F8D2EF4E0CBB", + name: "Balazs Orban", + email: "balazs@orban.com", + image_url: "/customers/balazs-orban.png", }, ]; @@ -76,108 +76,108 @@ const invoices = [ { customer_id: customers[0].id, amount: 15795, - status: 'pending', - date: '2022-12-06', + status: "pending", + date: "2022-12-06", }, { customer_id: customers[1].id, amount: 20348, - status: 'pending', - date: '2022-11-14', + status: "pending", + date: "2022-11-14", }, { customer_id: customers[4].id, amount: 3040, - status: 'paid', - date: '2022-10-29', + status: "paid", + date: "2022-10-29", }, { customer_id: customers[3].id, amount: 44800, - status: 'paid', - date: '2023-09-10', + status: "paid", + date: "2023-09-10", }, { customer_id: customers[5].id, amount: 34577, - status: 'pending', - date: '2023-08-05', + status: "pending", + date: "2023-08-05", }, { customer_id: customers[7].id, amount: 54246, - status: 'pending', - date: '2023-07-16', + status: "pending", + date: "2023-07-16", }, { customer_id: customers[6].id, amount: 666, - status: 'pending', - date: '2023-06-27', + status: "pending", + date: "2023-06-27", }, { customer_id: customers[3].id, amount: 32545, - status: 'paid', - date: '2023-06-09', + status: "paid", + date: "2023-06-09", }, { customer_id: customers[4].id, amount: 1250, - status: 'paid', - date: '2023-06-17', + status: "paid", + date: "2023-06-17", }, { customer_id: customers[5].id, amount: 8546, - status: 'paid', - date: '2023-06-07', + status: "paid", + date: "2023-06-07", }, { customer_id: customers[1].id, amount: 500, - status: 'paid', - date: '2023-08-19', + status: "paid", + date: "2023-08-19", }, { customer_id: customers[5].id, amount: 8945, - status: 'paid', - date: '2023-06-03', + status: "paid", + date: "2023-06-03", }, { customer_id: customers[2].id, amount: 8945, - status: 'paid', - date: '2023-06-18', + status: "paid", + date: "2023-06-18", }, { customer_id: customers[0].id, amount: 8945, - status: 'paid', - date: '2023-10-04', + status: "paid", + date: "2023-10-04", }, { customer_id: customers[2].id, amount: 1000, - status: 'paid', - date: '2022-06-05', + status: "paid", + date: "2022-06-05", }, ]; const revenue = [ - { month: 'Jan', revenue: 2000 }, - { month: 'Feb', revenue: 1800 }, - { month: 'Mar', revenue: 2200 }, - { month: 'Apr', revenue: 2500 }, - { month: 'May', revenue: 2300 }, - { month: 'Jun', revenue: 3200 }, - { month: 'Jul', revenue: 3500 }, - { month: 'Aug', revenue: 3700 }, - { month: 'Sep', revenue: 2500 }, - { month: 'Oct', revenue: 2800 }, - { month: 'Nov', revenue: 3000 }, - { month: 'Dec', revenue: 4800 }, + { month: "Jan", revenue: 2000 }, + { month: "Feb", revenue: 1800 }, + { month: "Mar", revenue: 2200 }, + { month: "Apr", revenue: 2500 }, + { month: "May", revenue: 2300 }, + { month: "Jun", revenue: 3200 }, + { month: "Jul", revenue: 3500 }, + { month: "Aug", revenue: 3700 }, + { month: "Sep", revenue: 2500 }, + { month: "Oct", revenue: 2800 }, + { month: "Nov", revenue: 3000 }, + { month: "Dec", revenue: 4800 }, ]; module.exports = { diff --git a/app/lib/placeholder-data.ts b/app/lib/placeholder-data.ts index 257fb14a..a5e379ca 100644 --- a/app/lib/placeholder-data.ts +++ b/app/lib/placeholder-data.ts @@ -2,49 +2,49 @@ // https://nextjs.org/learn/dashboard-app/fetching-data const users = [ { - id: '410544b2-4001-4271-9855-fec4b6a6442a', - name: 'User', - email: 'user@nextmail.com', - password: '123456', + id: "410544b2-4001-4271-9855-fec4b6a6442a", + name: "User", + email: "user@nextmail.com", + password: "123456", }, ]; const customers = [ { - id: 'd6e15727-9fe1-4961-8c5b-ea44a9bd81aa', - name: 'Evil Rabbit', - email: 'evil@rabbit.com', - image_url: '/customers/evil-rabbit.png', + id: "d6e15727-9fe1-4961-8c5b-ea44a9bd81aa", + name: "Evil Rabbit", + email: "evil@rabbit.com", + image_url: "/customers/evil-rabbit.png", }, { - id: '3958dc9e-712f-4377-85e9-fec4b6a6442a', - name: 'Delba de Oliveira', - email: 'delba@oliveira.com', - image_url: '/customers/delba-de-oliveira.png', + id: "3958dc9e-712f-4377-85e9-fec4b6a6442a", + name: "Delba de Oliveira", + email: "delba@oliveira.com", + image_url: "/customers/delba-de-oliveira.png", }, { - id: '3958dc9e-742f-4377-85e9-fec4b6a6442a', - name: 'Lee Robinson', - email: 'lee@robinson.com', - image_url: '/customers/lee-robinson.png', + id: "3958dc9e-742f-4377-85e9-fec4b6a6442a", + name: "Lee Robinson", + email: "lee@robinson.com", + image_url: "/customers/lee-robinson.png", }, { - id: '76d65c26-f784-44a2-ac19-586678f7c2f2', - name: 'Michael Novotny', - email: 'michael@novotny.com', - image_url: '/customers/michael-novotny.png', + id: "76d65c26-f784-44a2-ac19-586678f7c2f2", + name: "Michael Novotny", + email: "michael@novotny.com", + image_url: "/customers/michael-novotny.png", }, { - id: 'CC27C14A-0ACF-4F4A-A6C9-D45682C144B9', - name: 'Amy Burns', - email: 'amy@burns.com', - image_url: '/customers/amy-burns.png', + id: "CC27C14A-0ACF-4F4A-A6C9-D45682C144B9", + name: "Amy Burns", + email: "amy@burns.com", + image_url: "/customers/amy-burns.png", }, { - id: '13D07535-C59E-4157-A011-F8D2EF4E0CBB', - name: 'Balazs Orban', - email: 'balazs@orban.com', - image_url: '/customers/balazs-orban.png', + id: "13D07535-C59E-4157-A011-F8D2EF4E0CBB", + name: "Balazs Orban", + email: "balazs@orban.com", + image_url: "/customers/balazs-orban.png", }, ]; @@ -52,96 +52,96 @@ const invoices = [ { customer_id: customers[0].id, amount: 15795, - status: 'pending', - date: '2022-12-06', + status: "pending", + date: "2022-12-06", }, { customer_id: customers[1].id, amount: 20348, - status: 'pending', - date: '2022-11-14', + status: "pending", + date: "2022-11-14", }, { customer_id: customers[4].id, amount: 3040, - status: 'paid', - date: '2022-10-29', + status: "paid", + date: "2022-10-29", }, { customer_id: customers[3].id, amount: 44800, - status: 'paid', - date: '2023-09-10', + status: "paid", + date: "2023-09-10", }, { customer_id: customers[5].id, amount: 34577, - status: 'pending', - date: '2023-08-05', + status: "pending", + date: "2023-08-05", }, { customer_id: customers[2].id, amount: 54246, - status: 'pending', - date: '2023-07-16', + status: "pending", + date: "2023-07-16", }, { customer_id: customers[0].id, amount: 666, - status: 'pending', - date: '2023-06-27', + status: "pending", + date: "2023-06-27", }, { customer_id: customers[3].id, amount: 32545, - status: 'paid', - date: '2023-06-09', + status: "paid", + date: "2023-06-09", }, { customer_id: customers[4].id, amount: 1250, - status: 'paid', - date: '2023-06-17', + status: "paid", + date: "2023-06-17", }, { customer_id: customers[5].id, amount: 8546, - status: 'paid', - date: '2023-06-07', + status: "paid", + date: "2023-06-07", }, { customer_id: customers[1].id, amount: 500, - status: 'paid', - date: '2023-08-19', + status: "paid", + date: "2023-08-19", }, { customer_id: customers[5].id, amount: 8945, - status: 'paid', - date: '2023-06-03', + status: "paid", + date: "2023-06-03", }, { customer_id: customers[2].id, amount: 1000, - status: 'paid', - date: '2022-06-05', + status: "paid", + date: "2022-06-05", }, ]; const revenue = [ - { month: 'Jan', revenue: 2000 }, - { month: 'Feb', revenue: 1800 }, - { month: 'Mar', revenue: 2200 }, - { month: 'Apr', revenue: 2500 }, - { month: 'May', revenue: 2300 }, - { month: 'Jun', revenue: 3200 }, - { month: 'Jul', revenue: 3500 }, - { month: 'Aug', revenue: 3700 }, - { month: 'Sep', revenue: 2500 }, - { month: 'Oct', revenue: 2800 }, - { month: 'Nov', revenue: 3000 }, - { month: 'Dec', revenue: 4800 }, + { month: "Jan", revenue: 2000 }, + { month: "Feb", revenue: 1800 }, + { month: "Mar", revenue: 2200 }, + { month: "Apr", revenue: 2500 }, + { month: "May", revenue: 2300 }, + { month: "Jun", revenue: 3200 }, + { month: "Jul", revenue: 3500 }, + { month: "Aug", revenue: 3700 }, + { month: "Sep", revenue: 2500 }, + { month: "Oct", revenue: 2800 }, + { month: "Nov", revenue: 3000 }, + { month: "Dec", revenue: 4800 }, ]; -export { users, customers, invoices, revenue }; +export { customers, invoices, revenue, users }; diff --git a/app/lib/utils.ts b/app/lib/utils.ts index b7f7cffe..c2e0e21e 100644 --- a/app/lib/utils.ts +++ b/app/lib/utils.ts @@ -1,21 +1,21 @@ -import { Revenue } from './definitions'; +import type { Revenue } from "./definitions"; export const formatCurrency = (amount: number) => { - return (amount / 100).toLocaleString('en-US', { - style: 'currency', - currency: 'USD', + return (amount / 100).toLocaleString("en-US", { + style: "currency", + currency: "USD", }); }; export const formatDateToLocal = ( dateStr: string, - locale: string = 'en-US', + locale: string = "en-US", ) => { const date = new Date(dateStr); const options: Intl.DateTimeFormatOptions = { - day: 'numeric', - month: 'short', - year: 'numeric', + day: "numeric", + month: "short", + year: "numeric", }; const formatter = new Intl.DateTimeFormat(locale, options); return formatter.format(date); @@ -45,13 +45,13 @@ export const generatePagination = (currentPage: number, totalPages: number) => { // If the current page is among the first 3 pages, // show the first 3, an ellipsis, and the last 2 pages. if (currentPage <= 3) { - return [1, 2, 3, '...', totalPages - 1, totalPages]; + return [1, 2, 3, "...", totalPages - 1, totalPages]; } // If the current page is among the last 3 pages, // show the first 2, an ellipsis, and the last 3 pages. if (currentPage >= totalPages - 2) { - return [1, 2, '...', totalPages - 2, totalPages - 1, totalPages]; + return [1, 2, "...", totalPages - 2, totalPages - 1, totalPages]; } // If the current page is somewhere in the middle, @@ -59,11 +59,11 @@ export const generatePagination = (currentPage: number, totalPages: number) => { // another ellipsis, and the last page. return [ 1, - '...', + "...", currentPage - 1, currentPage, currentPage + 1, - '...', + "...", totalPages, ]; }; diff --git a/app/login/page.tsx b/app/login/page.tsx index 3a46e86d..2246b133 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -1,10 +1,10 @@ -import AcmeLogo from '@/app/ui/acme-logo'; -import LoginForm from '@/app/ui/login-form'; -import { Suspense } from 'react'; -import { Metadata } from 'next'; - +import type { Metadata } from "next"; +import { Suspense } from "react"; +import AcmeLogo from "@/app/ui/acme-logo"; +import LoginForm from "@/app/ui/login-form"; + export const metadata: Metadata = { - title: 'Login', + title: "Login", }; export default function LoginPage() { @@ -22,4 +22,4 @@ export default function LoginPage() { ); -} \ No newline at end of file +} diff --git a/app/page.tsx b/app/page.tsx index de9ce760..c36883f0 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,22 +1,22 @@ -import AcmeLogo from '@/app/ui/acme-logo'; -import styles from '@/app/ui/home.module.css'; -import Link from 'next/link'; -import { lusitana } from '@/app/ui/fonts'; -import Image from 'next/image'; +import Image from "next/image"; +import Link from "next/link"; +import AcmeLogo from "@/app/ui/acme-logo"; +import { lusitana } from "@/app/ui/fonts"; +import styles from "@/app/ui/home.module.css"; export default function Page() { return (
- {/* */} + {/* */}
-
-

- Welcome to Acme. This is the example for the{' '} +

+

+ Welcome to Acme. This is the example for the{" "} Next.js Learn Course @@ -30,20 +30,20 @@ export default function Page() {

- - + +
diff --git a/app/query/route.ts b/app/query/route.ts index 58221efb..e82de90d 100644 --- a/app/query/route.ts +++ b/app/query/route.ts @@ -1,20 +1,20 @@ -import postgres from 'postgres'; +import postgres from "postgres"; -const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' }); +const sql = postgres(process.env.POSTGRES_URL!, { ssl: "require" }); async function listInvoices() { - const data = await sql` + const data = await sql` SELECT invoices.amount, customers.name FROM invoices JOIN customers ON invoices.customer_id = customers.id WHERE invoices.amount = 666; `; - return data; + return data; } - export async function GET() { +export async function GET() { try { - return Response.json(await listInvoices()); + return Response.json(await listInvoices()); } catch (error) { - return Response.json({ error }, { status: 500 }); + return Response.json({ error }, { status: 500 }); } } diff --git a/app/seed/route.ts b/app/seed/route.ts index c6428b27..fa008830 100644 --- a/app/seed/route.ts +++ b/app/seed/route.ts @@ -1,8 +1,8 @@ -import bcrypt from 'bcrypt'; -import postgres from 'postgres'; -import { invoices, customers, revenue, users } from '../lib/placeholder-data'; +import bcrypt from "bcrypt"; +import postgres from "postgres"; +import { customers, invoices, revenue, users } from "../lib/placeholder-data"; -const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' }); +const sql = postgres(process.env.POSTGRES_URL!, { ssl: "require" }); async function seedUsers() { await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`; @@ -103,14 +103,14 @@ async function seedRevenue() { export async function GET() { try { - const result = await sql.begin((sql) => [ + const _result = await sql.begin((_sql) => [ seedUsers(), seedCustomers(), seedInvoices(), seedRevenue(), ]); - return Response.json({ message: 'Database seeded successfully' }); + return Response.json({ message: "Database seeded successfully" }); } catch (error) { return Response.json({ error }, { status: 500 }); } diff --git a/app/ui/acme-logo.tsx b/app/ui/acme-logo.tsx index 18d8f60b..13428799 100644 --- a/app/ui/acme-logo.tsx +++ b/app/ui/acme-logo.tsx @@ -1,5 +1,5 @@ -import { GlobeAltIcon } from '@heroicons/react/24/outline'; -import { lusitana } from '@/app/ui/fonts'; +import { GlobeAltIcon } from "@heroicons/react/24/outline"; +import { lusitana } from "@/app/ui/fonts"; export default function AcmeLogo() { return ( diff --git a/app/ui/button.tsx b/app/ui/button.tsx index af8f627b..62414455 100644 --- a/app/ui/button.tsx +++ b/app/ui/button.tsx @@ -1,4 +1,4 @@ -import clsx from 'clsx'; +import clsx from "clsx"; interface ButtonProps extends React.ButtonHTMLAttributes { children: React.ReactNode; @@ -9,7 +9,7 @@ export function Button({ children, className, ...rest }: ButtonProps) { - + ); -} \ No newline at end of file +} diff --git a/app/ui/invoices/create-form.tsx b/app/ui/invoices/create-form.tsx index 6d745ea2..9c2c4548 100644 --- a/app/ui/invoices/create-form.tsx +++ b/app/ui/invoices/create-form.tsx @@ -1,25 +1,23 @@ -'use client'; +"use client"; -import { useFormState } from 'react-dom'; -import { CustomerField } from '@/app/lib/definitions'; -import Link from 'next/link'; import { CheckIcon, ClockIcon, CurrencyDollarIcon, UserCircleIcon, -} from '@heroicons/react/24/outline'; -import { Button } from '../button'; -import { createInvoice, State } from '@/app/lib/actions'; -import { useActionState } from 'react'; - +} from "@heroicons/react/24/outline"; +import Link from "next/link"; +import { useFormState } from "react-dom"; +import { createInvoice } from "@/app/lib/actions"; +import type { CustomerField } from "@/app/lib/definitions"; +import { Button } from "../button"; export default function Form({ customers }: { customers: CustomerField[] }) { - const initialState = { message: '', error: {} }; -const [state, dispatch] = useFormState(createInvoice, initialState); - + const initialState = { message: "", error: {} }; + const [state, dispatch] = useFormState(createInvoice, initialState); + return ( -
+
{/* Customer Name */}
@@ -46,11 +44,10 @@ const [state, dispatch] = useFormState(createInvoice, initialState);
- {state.errors?.customerId && - state.errors.customerId.map((error: string) => ( -

+ {state.errors?.customerId?.map((error: string) => ( +

{error} -

+

))}
diff --git a/app/ui/invoices/edit-form.tsx b/app/ui/invoices/edit-form.tsx index e10f9c05..e0a2d519 100644 --- a/app/ui/invoices/edit-form.tsx +++ b/app/ui/invoices/edit-form.tsx @@ -1,14 +1,14 @@ -'use client'; +"use client"; -import { CustomerField, InvoiceForm } from '@/app/lib/definitions'; import { CheckIcon, ClockIcon, CurrencyDollarIcon, UserCircleIcon, -} from '@heroicons/react/24/outline'; -import Link from 'next/link'; -import { Button } from '@/app/ui/button'; +} from "@heroicons/react/24/outline"; +import Link from "next/link"; +import type { CustomerField, InvoiceForm } from "@/app/lib/definitions"; +import { Button } from "@/app/ui/button"; export default function EditInvoiceForm({ invoice, @@ -80,7 +80,7 @@ export default function EditInvoiceForm({ name="status" type="radio" value="pending" - defaultChecked={invoice.status === 'pending'} + defaultChecked={invoice.status === "pending"} 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" />