├── .gitignore ├── README.md ├── app ├── dashboard │ ├── (overview) │ │ ├── loading.tsx │ │ └── page.tsx │ ├── customers │ │ └── page.tsx │ ├── invoices │ │ └── page.tsx │ └── layout.tsx ├── layout.tsx ├── lib │ ├── data.ts │ ├── definitions.ts │ ├── placeholder-data.ts │ └── utils.ts ├── page.tsx ├── query │ └── route.ts ├── seed │ └── route.ts └── ui │ ├── acme-logo.tsx │ ├── button.tsx │ ├── customers │ └── table.tsx │ ├── dashboard │ ├── cards.tsx │ ├── latest-invoices.tsx │ ├── nav-links.tsx │ ├── revenue-chart.tsx │ └── sidenav.tsx │ ├── fonts.ts │ ├── global.css │ ├── invoices │ ├── breadcrumbs.tsx │ ├── buttons.tsx │ ├── create-form.tsx │ ├── edit-form.tsx │ ├── pagination.tsx │ ├── status.tsx │ └── table.tsx │ ├── login-form.tsx │ ├── search.tsx │ └── skeletons.tsx ├── next.config.ts ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── public ├── customers │ ├── amy-burns.png │ ├── balazs-orban.png │ ├── delba-de-oliveira.png │ ├── evil-rabbit.png │ ├── lee-robinson.png │ └── michael-novotny.png ├── favicon.ico ├── hero-desktop.png ├── hero-mobile.png └── opengraph-image.png ├── tailwind.config.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | .env 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | # idea 39 | .idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Next.js App Router Course - Starter 2 | 3 | This is the starter template for the Next.js App Router Course. It contains the starting code for the dashboard application. 4 | 5 | For more information, see the [course curriculum](https://nextjs.org/learn) on the Next.js Website. 6 | -------------------------------------------------------------------------------- /app/dashboard/(overview)/loading.tsx: -------------------------------------------------------------------------------- 1 | import DashboardSkeleton from "@/app/ui/skeletons"; 2 | 3 | const Loading = () => { 4 | return ; 5 | } 6 | export default Loading 7 | -------------------------------------------------------------------------------- /app/dashboard/(overview)/page.tsx: -------------------------------------------------------------------------------- 1 | import CardWrapper from '@/app/ui/dashboard/cards'; 2 | import RevenueChart from '@/app/ui/dashboard/revenue-chart'; 3 | import LatestInvoices from '@/app/ui/dashboard/latest-invoices'; 4 | import { lusitana } from '@/app/ui/fonts'; 5 | import { Suspense } from 'react'; 6 | import {CardSkeleton, CardsSkeleton, LatestInvoicesSkeleton, RevenueChartSkeleton} from "@/app/ui/skeletons"; 7 | 8 | export default async function Page() { 9 | 10 | return ( 11 |
12 |

13 | Dashboard 14 |

15 |
16 | }> 17 | 18 | 19 |
20 |
21 | }> 22 | 23 | 24 | }> 25 | 26 | 27 |
28 |
29 | ); 30 | } -------------------------------------------------------------------------------- /app/dashboard/customers/page.tsx: -------------------------------------------------------------------------------- 1 | const Page = () => { 2 | return
Customers Page
3 | } 4 | export default Page 5 | -------------------------------------------------------------------------------- /app/dashboard/invoices/page.tsx: -------------------------------------------------------------------------------- 1 | const Page = () => { 2 | return
Invoices Page
3 | } 4 | export default Page 5 | -------------------------------------------------------------------------------- /app/dashboard/layout.tsx: -------------------------------------------------------------------------------- 1 | import SideNav from '@/app/ui/dashboard/sidenav'; 2 | 3 | export default function Layout({ children }: { children: React.ReactNode }) { 4 | return ( 5 |
6 |
7 | 8 |
9 |
{children}
10 |
11 | ); 12 | } -------------------------------------------------------------------------------- /app/layout.tsx: -------------------------------------------------------------------------------- 1 | import '@/app/ui/global.css' 2 | import {inter} from "@/app/ui/fonts"; 3 | 4 | export default function RootLayout({ 5 | children, 6 | }: { 7 | children: React.ReactNode; 8 | }) { 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /app/lib/data.ts: -------------------------------------------------------------------------------- 1 | import postgres from 'postgres'; 2 | import { 3 | CustomerField, 4 | CustomersTableType, 5 | InvoiceForm, 6 | InvoicesTable, 7 | LatestInvoiceRaw, 8 | Revenue, 9 | } from './definitions'; 10 | import { formatCurrency } from './utils'; 11 | 12 | const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' }); 13 | 14 | export async function fetchRevenue() { 15 | try { 16 | // Artificially delay a response for demo purposes. 17 | // Don't do this in production :) 18 | 19 | console.log('Fetching revenue data...'); 20 | await new Promise((resolve) => setTimeout(resolve, 3000)); 21 | 22 | const data = await sql`SELECT * FROM revenue`; 23 | 24 | console.log('Data fetch completed after 3 seconds.'); 25 | 26 | return data; 27 | } catch (error) { 28 | console.error('Database Error:', error); 29 | throw new Error('Failed to fetch revenue data.'); 30 | } 31 | } 32 | 33 | export async function fetchLatestInvoices() { 34 | try { 35 | const data = await sql` 36 | SELECT invoices.amount, customers.name, customers.image_url, customers.email, invoices.id 37 | FROM invoices 38 | JOIN customers ON invoices.customer_id = customers.id 39 | ORDER BY invoices.date DESC 40 | LIMIT 5`; 41 | 42 | const latestInvoices = data.map((invoice) => ({ 43 | ...invoice, 44 | amount: formatCurrency(invoice.amount), 45 | })); 46 | return latestInvoices; 47 | } catch (error) { 48 | console.error('Database Error:', error); 49 | throw new Error('Failed to fetch the latest invoices.'); 50 | } 51 | } 52 | 53 | export async function fetchCardData() { 54 | try { 55 | // You can probably combine these into a single SQL query 56 | // However, we are intentionally splitting them to demonstrate 57 | // how to initialize multiple queries in parallel with JS. 58 | const invoiceCountPromise = sql`SELECT COUNT(*) FROM invoices`; 59 | const customerCountPromise = sql`SELECT COUNT(*) FROM customers`; 60 | const invoiceStatusPromise = sql`SELECT 61 | SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid", 62 | SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS "pending" 63 | FROM invoices`; 64 | 65 | const data = await Promise.all([ 66 | invoiceCountPromise, 67 | customerCountPromise, 68 | invoiceStatusPromise, 69 | ]); 70 | 71 | const numberOfInvoices = Number(data[0].count ?? '0'); 72 | const numberOfCustomers = Number(data[1].count ?? '0'); 73 | const totalPaidInvoices = formatCurrency(data[2][0].paid ?? '0'); 74 | const totalPendingInvoices = formatCurrency(data[2][0].pending ?? '0'); 75 | 76 | return { 77 | numberOfCustomers, 78 | numberOfInvoices, 79 | totalPaidInvoices, 80 | totalPendingInvoices, 81 | }; 82 | } catch (error) { 83 | console.error('Database Error:', error); 84 | throw new Error('Failed to fetch card data.'); 85 | } 86 | } 87 | 88 | const ITEMS_PER_PAGE = 6; 89 | export async function fetchFilteredInvoices( 90 | query: string, 91 | currentPage: number, 92 | ) { 93 | const offset = (currentPage - 1) * ITEMS_PER_PAGE; 94 | 95 | try { 96 | const invoices = await sql` 97 | SELECT 98 | invoices.id, 99 | invoices.amount, 100 | invoices.date, 101 | invoices.status, 102 | customers.name, 103 | customers.email, 104 | customers.image_url 105 | FROM invoices 106 | JOIN customers ON invoices.customer_id = customers.id 107 | WHERE 108 | customers.name ILIKE ${`%${query}%`} OR 109 | customers.email ILIKE ${`%${query}%`} OR 110 | invoices.amount::text ILIKE ${`%${query}%`} OR 111 | invoices.date::text ILIKE ${`%${query}%`} OR 112 | invoices.status ILIKE ${`%${query}%`} 113 | ORDER BY invoices.date DESC 114 | LIMIT ${ITEMS_PER_PAGE} OFFSET ${offset} 115 | `; 116 | 117 | return invoices; 118 | } catch (error) { 119 | console.error('Database Error:', error); 120 | throw new Error('Failed to fetch invoices.'); 121 | } 122 | } 123 | 124 | export async function fetchInvoicesPages(query: string) { 125 | try { 126 | const data = await sql`SELECT COUNT(*) 127 | FROM invoices 128 | JOIN customers ON invoices.customer_id = customers.id 129 | WHERE 130 | customers.name ILIKE ${`%${query}%`} OR 131 | customers.email ILIKE ${`%${query}%`} OR 132 | invoices.amount::text ILIKE ${`%${query}%`} OR 133 | invoices.date::text ILIKE ${`%${query}%`} OR 134 | invoices.status ILIKE ${`%${query}%`} 135 | `; 136 | 137 | const totalPages = Math.ceil(Number(data[0].count) / ITEMS_PER_PAGE); 138 | return totalPages; 139 | } catch (error) { 140 | console.error('Database Error:', error); 141 | throw new Error('Failed to fetch total number of invoices.'); 142 | } 143 | } 144 | 145 | export async function fetchInvoiceById(id: string) { 146 | try { 147 | const data = await sql` 148 | SELECT 149 | invoices.id, 150 | invoices.customer_id, 151 | invoices.amount, 152 | invoices.status 153 | FROM invoices 154 | WHERE invoices.id = ${id}; 155 | `; 156 | 157 | const invoice = data.map((invoice) => ({ 158 | ...invoice, 159 | // Convert amount from cents to dollars 160 | amount: invoice.amount / 100, 161 | })); 162 | 163 | return invoice[0]; 164 | } catch (error) { 165 | console.error('Database Error:', error); 166 | throw new Error('Failed to fetch invoice.'); 167 | } 168 | } 169 | 170 | export async function fetchCustomers() { 171 | try { 172 | const customers = await sql` 173 | SELECT 174 | id, 175 | name 176 | FROM customers 177 | ORDER BY name ASC 178 | `; 179 | 180 | return customers; 181 | } catch (err) { 182 | console.error('Database Error:', err); 183 | throw new Error('Failed to fetch all customers.'); 184 | } 185 | } 186 | 187 | export async function fetchFilteredCustomers(query: string) { 188 | try { 189 | const data = await sql` 190 | SELECT 191 | customers.id, 192 | customers.name, 193 | customers.email, 194 | customers.image_url, 195 | COUNT(invoices.id) AS total_invoices, 196 | SUM(CASE WHEN invoices.status = 'pending' THEN invoices.amount ELSE 0 END) AS total_pending, 197 | SUM(CASE WHEN invoices.status = 'paid' THEN invoices.amount ELSE 0 END) AS total_paid 198 | FROM customers 199 | LEFT JOIN invoices ON customers.id = invoices.customer_id 200 | WHERE 201 | customers.name ILIKE ${`%${query}%`} OR 202 | customers.email ILIKE ${`%${query}%`} 203 | GROUP BY customers.id, customers.name, customers.email, customers.image_url 204 | ORDER BY customers.name ASC 205 | `; 206 | 207 | const customers = data.map((customer) => ({ 208 | ...customer, 209 | total_pending: formatCurrency(customer.total_pending), 210 | total_paid: formatCurrency(customer.total_paid), 211 | })); 212 | 213 | return customers; 214 | } catch (err) { 215 | console.error('Database Error:', err); 216 | throw new Error('Failed to fetch customer table.'); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /app/lib/definitions.ts: -------------------------------------------------------------------------------- 1 | // This file contains type definitions for your data. 2 | // It describes the shape of the data, and what data type each property should accept. 3 | // For simplicity of teaching, we're manually defining these types. 4 | // However, these types are generated automatically if you're using an ORM such as Prisma. 5 | export type User = { 6 | id: string; 7 | name: string; 8 | email: string; 9 | password: string; 10 | }; 11 | 12 | export type Customer = { 13 | id: string; 14 | name: string; 15 | email: string; 16 | image_url: string; 17 | }; 18 | 19 | export type Invoice = { 20 | id: string; 21 | customer_id: string; 22 | amount: number; 23 | date: string; 24 | // In TypeScript, this is called a string union type. 25 | // It means that the "status" property can only be one of the two strings: 'pending' or 'paid'. 26 | status: 'pending' | 'paid'; 27 | }; 28 | 29 | export type Revenue = { 30 | month: string; 31 | revenue: number; 32 | }; 33 | 34 | export type LatestInvoice = { 35 | id: string; 36 | name: string; 37 | image_url: string; 38 | email: string; 39 | amount: string; 40 | }; 41 | 42 | // The database returns a number for amount, but we later format it to a string with the formatCurrency function 43 | export type LatestInvoiceRaw = Omit & { 44 | amount: number; 45 | }; 46 | 47 | export type InvoicesTable = { 48 | id: string; 49 | customer_id: string; 50 | name: string; 51 | email: string; 52 | image_url: string; 53 | date: string; 54 | amount: number; 55 | status: 'pending' | 'paid'; 56 | }; 57 | 58 | export type CustomersTableType = { 59 | id: string; 60 | name: string; 61 | email: string; 62 | image_url: string; 63 | total_invoices: number; 64 | total_pending: number; 65 | total_paid: number; 66 | }; 67 | 68 | export type FormattedCustomersTable = { 69 | id: string; 70 | name: string; 71 | email: string; 72 | image_url: string; 73 | total_invoices: number; 74 | total_pending: string; 75 | total_paid: string; 76 | }; 77 | 78 | export type CustomerField = { 79 | id: string; 80 | name: string; 81 | }; 82 | 83 | export type InvoiceForm = { 84 | id: string; 85 | customer_id: string; 86 | amount: number; 87 | status: 'pending' | 'paid'; 88 | }; 89 | -------------------------------------------------------------------------------- /app/lib/placeholder-data.ts: -------------------------------------------------------------------------------- 1 | // This file contains placeholder data that you'll be replacing with real data in the Data Fetching chapter: 2 | // https://nextjs.org/learn/dashboard-app/fetching-data 3 | const users = [ 4 | { 5 | id: '410544b2-4001-4271-9855-fec4b6a6442a', 6 | name: 'User', 7 | email: 'user@nextmail.com', 8 | password: '123456', 9 | }, 10 | ]; 11 | 12 | const customers = [ 13 | { 14 | id: 'd6e15727-9fe1-4961-8c5b-ea44a9bd81aa', 15 | name: 'Evil Rabbit', 16 | email: 'evil@rabbit.com', 17 | image_url: '/customers/evil-rabbit.png', 18 | }, 19 | { 20 | id: '3958dc9e-712f-4377-85e9-fec4b6a6442a', 21 | name: 'Delba de Oliveira', 22 | email: 'delba@oliveira.com', 23 | image_url: '/customers/delba-de-oliveira.png', 24 | }, 25 | { 26 | id: '3958dc9e-742f-4377-85e9-fec4b6a6442a', 27 | name: 'Lee Robinson', 28 | email: 'lee@robinson.com', 29 | image_url: '/customers/lee-robinson.png', 30 | }, 31 | { 32 | id: '76d65c26-f784-44a2-ac19-586678f7c2f2', 33 | name: 'Michael Novotny', 34 | email: 'michael@novotny.com', 35 | image_url: '/customers/michael-novotny.png', 36 | }, 37 | { 38 | id: 'CC27C14A-0ACF-4F4A-A6C9-D45682C144B9', 39 | name: 'Amy Burns', 40 | email: 'amy@burns.com', 41 | image_url: '/customers/amy-burns.png', 42 | }, 43 | { 44 | id: '13D07535-C59E-4157-A011-F8D2EF4E0CBB', 45 | name: 'Balazs Orban', 46 | email: 'balazs@orban.com', 47 | image_url: '/customers/balazs-orban.png', 48 | }, 49 | ]; 50 | 51 | const invoices = [ 52 | { 53 | customer_id: customers[0].id, 54 | amount: 15795, 55 | status: 'pending', 56 | date: '2022-12-06', 57 | }, 58 | { 59 | customer_id: customers[1].id, 60 | amount: 20348, 61 | status: 'pending', 62 | date: '2022-11-14', 63 | }, 64 | { 65 | customer_id: customers[4].id, 66 | amount: 3040, 67 | status: 'paid', 68 | date: '2022-10-29', 69 | }, 70 | { 71 | customer_id: customers[3].id, 72 | amount: 44800, 73 | status: 'paid', 74 | date: '2023-09-10', 75 | }, 76 | { 77 | customer_id: customers[5].id, 78 | amount: 34577, 79 | status: 'pending', 80 | date: '2023-08-05', 81 | }, 82 | { 83 | customer_id: customers[2].id, 84 | amount: 54246, 85 | status: 'pending', 86 | date: '2023-07-16', 87 | }, 88 | { 89 | customer_id: customers[0].id, 90 | amount: 666, 91 | status: 'pending', 92 | date: '2023-06-27', 93 | }, 94 | { 95 | customer_id: customers[3].id, 96 | amount: 32545, 97 | status: 'paid', 98 | date: '2023-06-09', 99 | }, 100 | { 101 | customer_id: customers[4].id, 102 | amount: 1250, 103 | status: 'paid', 104 | date: '2023-06-17', 105 | }, 106 | { 107 | customer_id: customers[5].id, 108 | amount: 8546, 109 | status: 'paid', 110 | date: '2023-06-07', 111 | }, 112 | { 113 | customer_id: customers[1].id, 114 | amount: 500, 115 | status: 'paid', 116 | date: '2023-08-19', 117 | }, 118 | { 119 | customer_id: customers[5].id, 120 | amount: 8945, 121 | status: 'paid', 122 | date: '2023-06-03', 123 | }, 124 | { 125 | customer_id: customers[2].id, 126 | amount: 1000, 127 | status: 'paid', 128 | date: '2022-06-05', 129 | }, 130 | ]; 131 | 132 | const revenue = [ 133 | { month: 'Jan', revenue: 2000 }, 134 | { month: 'Feb', revenue: 1800 }, 135 | { month: 'Mar', revenue: 2200 }, 136 | { month: 'Apr', revenue: 2500 }, 137 | { month: 'May', revenue: 2300 }, 138 | { month: 'Jun', revenue: 3200 }, 139 | { month: 'Jul', revenue: 3500 }, 140 | { month: 'Aug', revenue: 3700 }, 141 | { month: 'Sep', revenue: 2500 }, 142 | { month: 'Oct', revenue: 2800 }, 143 | { month: 'Nov', revenue: 3000 }, 144 | { month: 'Dec', revenue: 4800 }, 145 | ]; 146 | 147 | export { users, customers, invoices, revenue }; 148 | -------------------------------------------------------------------------------- /app/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { Revenue } from './definitions'; 2 | 3 | export const formatCurrency = (amount: number) => { 4 | return (amount / 100).toLocaleString('en-US', { 5 | style: 'currency', 6 | currency: 'USD', 7 | }); 8 | }; 9 | 10 | export const formatDateToLocal = ( 11 | dateStr: string, 12 | locale: string = 'en-US', 13 | ) => { 14 | const date = new Date(dateStr); 15 | const options: Intl.DateTimeFormatOptions = { 16 | day: 'numeric', 17 | month: 'short', 18 | year: 'numeric', 19 | }; 20 | const formatter = new Intl.DateTimeFormat(locale, options); 21 | return formatter.format(date); 22 | }; 23 | 24 | export const generateYAxis = (revenue: Revenue[]) => { 25 | // Calculate what labels we need to display on the y-axis 26 | // based on highest record and in 1000s 27 | const yAxisLabels = []; 28 | const highestRecord = Math.max(...revenue.map((month) => month.revenue)); 29 | const topLabel = Math.ceil(highestRecord / 1000) * 1000; 30 | 31 | for (let i = topLabel; i >= 0; i -= 1000) { 32 | yAxisLabels.push(`$${i / 1000}K`); 33 | } 34 | 35 | return { yAxisLabels, topLabel }; 36 | }; 37 | 38 | export const generatePagination = (currentPage: number, totalPages: number) => { 39 | // If the total number of pages is 7 or less, 40 | // display all pages without any ellipsis. 41 | if (totalPages <= 7) { 42 | return Array.from({ length: totalPages }, (_, i) => i + 1); 43 | } 44 | 45 | // If the current page is among the first 3 pages, 46 | // show the first 3, an ellipsis, and the last 2 pages. 47 | if (currentPage <= 3) { 48 | return [1, 2, 3, '...', totalPages - 1, totalPages]; 49 | } 50 | 51 | // If the current page is among the last 3 pages, 52 | // show the first 2, an ellipsis, and the last 3 pages. 53 | if (currentPage >= totalPages - 2) { 54 | return [1, 2, '...', totalPages - 2, totalPages - 1, totalPages]; 55 | } 56 | 57 | // If the current page is somewhere in the middle, 58 | // show the first page, an ellipsis, the current page and its neighbors, 59 | // another ellipsis, and the last page. 60 | return [ 61 | 1, 62 | '...', 63 | currentPage - 1, 64 | currentPage, 65 | currentPage + 1, 66 | '...', 67 | totalPages, 68 | ]; 69 | }; 70 | -------------------------------------------------------------------------------- /app/page.tsx: -------------------------------------------------------------------------------- 1 | import AcmeLogo from '@/app/ui/acme-logo'; 2 | import { ArrowRightIcon } from '@heroicons/react/24/outline'; 3 | import Link from 'next/link'; 4 | import Image from "next/image"; 5 | 6 | export default function Page() { 7 | return ( 8 |
9 |
10 | 11 |
12 |
13 |
14 |
17 |

18 | Welcome to Acme. This is the example for the{' '} 19 | 20 | Next.js Learn Course 21 | 22 | , brought to you by Vercel. 23 |

24 | 28 | Log in 29 | 30 |
31 |
32 | {/* Add Hero Images Here */} 33 | Screenshots of the dashboard project showing desktop version 39 | Screents of the dashboard project showing mobile version 45 |
46 |
47 |
48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /app/query/route.ts: -------------------------------------------------------------------------------- 1 | import postgres from 'postgres'; 2 | 3 | const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' }); 4 | 5 | async function listInvoices() { 6 | const data = await sql` 7 | SELECT invoices.amount, customers.name 8 | FROM invoices 9 | JOIN customers ON invoices.customer_id = customers.id 10 | WHERE invoices.amount = 666; 11 | `; 12 | 13 | return data; 14 | } 15 | 16 | export async function GET() { 17 | try { 18 | return Response.json(await listInvoices()); 19 | } catch (error) { 20 | return Response.json({ error }, { status: 500 }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/seed/route.ts: -------------------------------------------------------------------------------- 1 | import bcrypt from 'bcrypt'; 2 | import postgres from 'postgres'; 3 | import { invoices, customers, revenue, users } from '../lib/placeholder-data'; 4 | 5 | const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' }); 6 | 7 | async function seedUsers() { 8 | await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`; 9 | await sql` 10 | CREATE TABLE IF NOT EXISTS users ( 11 | id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, 12 | name VARCHAR(255) NOT NULL, 13 | email TEXT NOT NULL UNIQUE, 14 | password TEXT NOT NULL 15 | ); 16 | `; 17 | 18 | const insertedUsers = await Promise.all( 19 | users.map(async (user) => { 20 | const hashedPassword = await bcrypt.hash(user.password, 10); 21 | return sql` 22 | INSERT INTO users (id, name, email, password) 23 | VALUES (${user.id}, ${user.name}, ${user.email}, ${hashedPassword}) 24 | ON CONFLICT (id) DO NOTHING; 25 | `; 26 | }), 27 | ); 28 | 29 | return insertedUsers; 30 | } 31 | 32 | async function seedInvoices() { 33 | await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`; 34 | 35 | await sql` 36 | CREATE TABLE IF NOT EXISTS invoices ( 37 | id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, 38 | customer_id UUID NOT NULL, 39 | amount INT NOT NULL, 40 | status VARCHAR(255) NOT NULL, 41 | date DATE NOT NULL 42 | ); 43 | `; 44 | 45 | const insertedInvoices = await Promise.all( 46 | invoices.map( 47 | (invoice) => sql` 48 | INSERT INTO invoices (customer_id, amount, status, date) 49 | VALUES (${invoice.customer_id}, ${invoice.amount}, ${invoice.status}, ${invoice.date}) 50 | ON CONFLICT (id) DO NOTHING; 51 | `, 52 | ), 53 | ); 54 | 55 | return insertedInvoices; 56 | } 57 | 58 | async function seedCustomers() { 59 | await sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`; 60 | 61 | await sql` 62 | CREATE TABLE IF NOT EXISTS customers ( 63 | id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, 64 | name VARCHAR(255) NOT NULL, 65 | email VARCHAR(255) NOT NULL, 66 | image_url VARCHAR(255) NOT NULL 67 | ); 68 | `; 69 | 70 | const insertedCustomers = await Promise.all( 71 | customers.map( 72 | (customer) => sql` 73 | INSERT INTO customers (id, name, email, image_url) 74 | VALUES (${customer.id}, ${customer.name}, ${customer.email}, ${customer.image_url}) 75 | ON CONFLICT (id) DO NOTHING; 76 | `, 77 | ), 78 | ); 79 | 80 | return insertedCustomers; 81 | } 82 | 83 | async function seedRevenue() { 84 | await sql` 85 | CREATE TABLE IF NOT EXISTS revenue ( 86 | month VARCHAR(4) NOT NULL UNIQUE, 87 | revenue INT NOT NULL 88 | ); 89 | `; 90 | 91 | const insertedRevenue = await Promise.all( 92 | revenue.map( 93 | (rev) => sql` 94 | INSERT INTO revenue (month, revenue) 95 | VALUES (${rev.month}, ${rev.revenue}) 96 | ON CONFLICT (month) DO NOTHING; 97 | `, 98 | ), 99 | ); 100 | 101 | return insertedRevenue; 102 | } 103 | 104 | export async function GET() { 105 | try { 106 | const result = await sql.begin((sql) => [ 107 | seedUsers(), 108 | seedCustomers(), 109 | seedInvoices(), 110 | seedRevenue(), 111 | ]); 112 | 113 | return Response.json({ message: 'Database seeded successfully' }); 114 | } catch (error) { 115 | return Response.json({ error }, { status: 500 }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/ui/acme-logo.tsx: -------------------------------------------------------------------------------- 1 | import { GlobeAltIcon } from '@heroicons/react/24/outline'; 2 | import { lusitana } from '@/app/ui/fonts'; 3 | 4 | export default function AcmeLogo() { 5 | return ( 6 |
9 | 10 |

Acme

11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /app/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import clsx from 'clsx'; 2 | 3 | interface ButtonProps extends React.ButtonHTMLAttributes { 4 | children: React.ReactNode; 5 | } 6 | 7 | export function Button({ children, className, ...rest }: ButtonProps) { 8 | return ( 9 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /app/ui/customers/table.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | import { lusitana } from '@/app/ui/fonts'; 3 | import Search from '@/app/ui/search'; 4 | import { 5 | CustomersTableType, 6 | FormattedCustomersTable, 7 | } from '@/app/lib/definitions'; 8 | 9 | export default async function CustomersTable({ 10 | customers, 11 | }: { 12 | customers: FormattedCustomersTable[]; 13 | }) { 14 | return ( 15 |
16 |

17 | Customers 18 |

19 | 20 |
21 |
22 |
23 |
24 |
25 | {customers?.map((customer) => ( 26 |
30 |
31 |
32 |
33 |
34 | {`${customer.name}'s 41 |

{customer.name}

42 |
43 |
44 |

45 | {customer.email} 46 |

47 |
48 |
49 |
50 |
51 |

Pending

52 |

{customer.total_pending}

53 |
54 |
55 |

Paid

56 |

{customer.total_paid}

57 |
58 |
59 |
60 |

{customer.total_invoices} invoices

61 |
62 |
63 | ))} 64 |
65 | 66 | 67 | 68 | 71 | 74 | 77 | 80 | 83 | 84 | 85 | 86 | 87 | {customers.map((customer) => ( 88 | 89 | 101 | 104 | 107 | 110 | 113 | 114 | ))} 115 | 116 |
69 | Name 70 | 72 | Email 73 | 75 | Total Invoices 76 | 78 | Total Pending 79 | 81 | Total Paid 82 |
90 |
91 | {`${customer.name}'s 98 |

{customer.name}

99 |
100 |
102 | {customer.email} 103 | 105 | {customer.total_invoices} 106 | 108 | {customer.total_pending} 109 | 111 | {customer.total_paid} 112 |
117 |
118 |
119 |
120 |
121 |
122 | ); 123 | } 124 | -------------------------------------------------------------------------------- /app/ui/dashboard/cards.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | BanknotesIcon, 3 | ClockIcon, 4 | UserGroupIcon, 5 | InboxIcon, 6 | } from '@heroicons/react/24/outline'; 7 | import { lusitana } from '@/app/ui/fonts'; 8 | import {fetchCardData} from "@/app/lib/data"; 9 | 10 | const iconMap = { 11 | collected: BanknotesIcon, 12 | customers: UserGroupIcon, 13 | pending: ClockIcon, 14 | invoices: InboxIcon, 15 | }; 16 | 17 | export default async function CardWrapper() { 18 | const { numberOfCustomers, numberOfInvoices, totalPaidInvoices, totalPendingInvoices } = await fetchCardData(); 19 | return ( 20 | <> 21 | {/* NOTE: Uncomment this code in Chapter 9 */} 22 | 23 | 24 | 25 | 26 | 31 | 32 | ); 33 | } 34 | 35 | export function Card({ 36 | title, 37 | value, 38 | type, 39 | }: { 40 | title: string; 41 | value: number | string; 42 | type: 'invoices' | 'customers' | 'pending' | 'collected'; 43 | }) { 44 | const Icon = iconMap[type]; 45 | 46 | return ( 47 |
48 |
49 | {Icon ? : null} 50 |

{title}

51 |
52 |

56 | {value} 57 |

58 |
59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /app/ui/dashboard/latest-invoices.tsx: -------------------------------------------------------------------------------- 1 | import { ArrowPathIcon } from '@heroicons/react/24/outline'; 2 | import clsx from 'clsx'; 3 | import Image from 'next/image'; 4 | import { lusitana } from '@/app/ui/fonts'; 5 | import { LatestInvoice } from '@/app/lib/definitions'; 6 | import { fetchLatestInvoices } from '@/app/lib/data'; 7 | 8 | export default async function LatestInvoices() { 9 | const latestInvoices = await fetchLatestInvoices(); 10 | 11 | return ( 12 |
13 |

14 | Latest Invoices 15 |

16 |
17 | {/* NOTE: Uncomment this code in Chapter 7 */} 18 | 19 |
20 | {latestInvoices.map((invoice, i) => { 21 | return ( 22 |
31 |
32 | {`${invoice.name}'s 39 |
40 |

41 | {invoice.name} 42 |

43 |

44 | {invoice.email} 45 |

46 |
47 |
48 |

51 | {invoice.amount} 52 |

53 |
54 | ); 55 | })} 56 |
57 |
58 | 59 |

Updated just now

60 |
61 |
62 |
63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /app/ui/dashboard/nav-links.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { 4 | UserGroupIcon, 5 | HomeIcon, 6 | DocumentDuplicateIcon, 7 | } from '@heroicons/react/24/outline'; 8 | import Link from "next/link"; 9 | import { usePathname } from "next/navigation"; 10 | import clsx from "clsx"; 11 | 12 | // Map of links to display in the side navigation. 13 | // Depending on the size of the application, this would be stored in a database. 14 | const links = [ 15 | { name: 'Home', href: '/dashboard', icon: HomeIcon }, 16 | { 17 | name: 'Invoices', 18 | href: '/dashboard/invoices', 19 | icon: DocumentDuplicateIcon, 20 | }, 21 | { name: 'Customers', href: '/dashboard/customers', icon: UserGroupIcon }, 22 | ]; 23 | 24 | export default function NavLinks() { 25 | const pathname = usePathname(); 26 | 27 | return ( 28 | <> 29 | {links.map((link) => { 30 | const LinkIcon = link.icon; 31 | return ( 32 | 41 | 42 |

{link.name}

43 | 44 | ); 45 | })} 46 | 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /app/ui/dashboard/revenue-chart.tsx: -------------------------------------------------------------------------------- 1 | import { generateYAxis } from '@/app/lib/utils'; 2 | import { CalendarIcon } from '@heroicons/react/24/outline'; 3 | import { lusitana } from '@/app/ui/fonts'; 4 | import { Revenue } from '@/app/lib/definitions'; 5 | import { fetchRevenue } from "@/app/lib/data"; 6 | 7 | // This component is representational only. 8 | // For data visualization UI, check out: 9 | // https://www.tremor.so/ 10 | // https://www.chartjs.org/ 11 | // https://airbnb.io/visx/ 12 | 13 | export default async function RevenueChart() { 14 | const revenue = await fetchRevenue(); 15 | 16 | const chartHeight = 350; 17 | // NOTE: Uncomment this code in Chapter 7 18 | 19 | const { yAxisLabels, topLabel } = generateYAxis(revenue); 20 | 21 | if (!revenue || revenue.length === 0) { 22 | return

No data available.

; 23 | } 24 | 25 | return ( 26 |
27 |

28 | Recent Revenue 29 |

30 | {/* NOTE: Uncomment this code in Chapter 7 */} 31 | 32 |
33 |
34 |
38 | {yAxisLabels.map((label) => ( 39 |

{label}

40 | ))} 41 |
42 | 43 | {revenue.map((month) => ( 44 |
45 |
51 |

52 | {month.month} 53 |

54 |
55 | ))} 56 |
57 |
58 | 59 |

Last 12 months

60 |
61 |
62 |
63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /app/ui/dashboard/sidenav.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link'; 2 | import NavLinks from '@/app/ui/dashboard/nav-links'; 3 | import AcmeLogo from '@/app/ui/acme-logo'; 4 | import { PowerIcon } from '@heroicons/react/24/outline'; 5 | 6 | export default function SideNav() { 7 | return ( 8 |
9 | 13 |
14 | 15 |
16 | 17 |
18 | 19 |
20 |
21 | 25 |
26 |
27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /app/ui/fonts.ts: -------------------------------------------------------------------------------- 1 | import { Inter, Lusitana } from 'next/font/google'; 2 | 3 | export const inter = Inter({ subsets: ['latin'] }); 4 | export const lusitana = Lusitana({ 5 | weight: ['400', '700'], 6 | subsets: ['latin'], 7 | }) -------------------------------------------------------------------------------- /app/ui/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | input[type='number'] { 6 | -moz-appearance: textfield; 7 | appearance: textfield; 8 | } 9 | 10 | input[type='number']::-webkit-inner-spin-button { 11 | -webkit-appearance: none; 12 | margin: 0; 13 | } 14 | 15 | input[type='number']::-webkit-outer-spin-button { 16 | -webkit-appearance: none; 17 | margin: 0; 18 | } 19 | -------------------------------------------------------------------------------- /app/ui/invoices/breadcrumbs.tsx: -------------------------------------------------------------------------------- 1 | import { clsx } from 'clsx'; 2 | import Link from 'next/link'; 3 | import { lusitana } from '@/app/ui/fonts'; 4 | 5 | interface Breadcrumb { 6 | label: string; 7 | href: string; 8 | active?: boolean; 9 | } 10 | 11 | export default function Breadcrumbs({ 12 | breadcrumbs, 13 | }: { 14 | breadcrumbs: Breadcrumb[]; 15 | }) { 16 | return ( 17 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /app/ui/invoices/buttons.tsx: -------------------------------------------------------------------------------- 1 | import { PencilIcon, PlusIcon, TrashIcon } from '@heroicons/react/24/outline'; 2 | import Link from 'next/link'; 3 | 4 | export function CreateInvoice() { 5 | return ( 6 | 10 | Create Invoice{' '} 11 | 12 | 13 | ); 14 | } 15 | 16 | export function UpdateInvoice({ id }: { id: string }) { 17 | return ( 18 | 22 | 23 | 24 | ); 25 | } 26 | 27 | export function DeleteInvoice({ id }: { id: string }) { 28 | return ( 29 | <> 30 | 34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /app/ui/invoices/create-form.tsx: -------------------------------------------------------------------------------- 1 | import { CustomerField } from '@/app/lib/definitions'; 2 | import Link from 'next/link'; 3 | import { 4 | CheckIcon, 5 | ClockIcon, 6 | CurrencyDollarIcon, 7 | UserCircleIcon, 8 | } from '@heroicons/react/24/outline'; 9 | import { Button } from '@/app/ui/button'; 10 | 11 | export default function Form({ customers }: { customers: CustomerField[] }) { 12 | return ( 13 |
14 |
15 | {/* Customer Name */} 16 |
17 | 20 |
21 | 36 | 37 |
38 |
39 | 40 | {/* Invoice Amount */} 41 |
42 | 45 |
46 |
47 | 55 | 56 |
57 |
58 |
59 | 60 | {/* Invoice Status */} 61 |
62 | 63 | Set the invoice status 64 | 65 |
66 |
67 |
68 | 75 | 81 |
82 |
83 | 90 | 96 |
97 |
98 |
99 |
100 |
101 |
102 | 106 | Cancel 107 | 108 | 109 |
110 |
111 | ); 112 | } 113 | -------------------------------------------------------------------------------- /app/ui/invoices/edit-form.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { CustomerField, InvoiceForm } from '@/app/lib/definitions'; 4 | import { 5 | CheckIcon, 6 | ClockIcon, 7 | CurrencyDollarIcon, 8 | UserCircleIcon, 9 | } from '@heroicons/react/24/outline'; 10 | import Link from 'next/link'; 11 | import { Button } from '@/app/ui/button'; 12 | 13 | export default function EditInvoiceForm({ 14 | invoice, 15 | customers, 16 | }: { 17 | invoice: InvoiceForm; 18 | customers: CustomerField[]; 19 | }) { 20 | return ( 21 |
22 |
23 | {/* Customer Name */} 24 |
25 | 28 |
29 | 44 | 45 |
46 |
47 | 48 | {/* Invoice Amount */} 49 |
50 | 53 |
54 |
55 | 64 | 65 |
66 |
67 |
68 | 69 | {/* Invoice Status */} 70 |
71 | 72 | Set the invoice status 73 | 74 |
75 |
76 |
77 | 85 | 91 |
92 |
93 | 101 | 107 |
108 |
109 |
110 |
111 |
112 |
113 | 117 | Cancel 118 | 119 | 120 |
121 |
122 | ); 123 | } 124 | -------------------------------------------------------------------------------- /app/ui/invoices/pagination.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline'; 4 | import clsx from 'clsx'; 5 | import Link from 'next/link'; 6 | import { generatePagination } from '@/app/lib/utils'; 7 | 8 | export default function Pagination({ totalPages }: { totalPages: number }) { 9 | // NOTE: Uncomment this code in Chapter 11 10 | 11 | // const allPages = generatePagination(currentPage, totalPages); 12 | 13 | return ( 14 | <> 15 | {/* NOTE: Uncomment this code in Chapter 11 */} 16 | 17 | {/*
18 | 23 | 24 |
25 | {allPages.map((page, index) => { 26 | let position: 'first' | 'last' | 'single' | 'middle' | undefined; 27 | 28 | if (index === 0) position = 'first'; 29 | if (index === allPages.length - 1) position = 'last'; 30 | if (allPages.length === 1) position = 'single'; 31 | if (page === '...') position = 'middle'; 32 | 33 | return ( 34 | 41 | ); 42 | })} 43 |
44 | 45 | = totalPages} 49 | /> 50 |
*/} 51 | 52 | ); 53 | } 54 | 55 | function PaginationNumber({ 56 | page, 57 | href, 58 | isActive, 59 | position, 60 | }: { 61 | page: number | string; 62 | href: string; 63 | position?: 'first' | 'last' | 'middle' | 'single'; 64 | isActive: boolean; 65 | }) { 66 | const className = clsx( 67 | 'flex h-10 w-10 items-center justify-center text-sm border', 68 | { 69 | 'rounded-l-md': position === 'first' || position === 'single', 70 | 'rounded-r-md': position === 'last' || position === 'single', 71 | 'z-10 bg-blue-600 border-blue-600 text-white': isActive, 72 | 'hover:bg-gray-100': !isActive && position !== 'middle', 73 | 'text-gray-300': position === 'middle', 74 | }, 75 | ); 76 | 77 | return isActive || position === 'middle' ? ( 78 |
{page}
79 | ) : ( 80 | 81 | {page} 82 | 83 | ); 84 | } 85 | 86 | function PaginationArrow({ 87 | href, 88 | direction, 89 | isDisabled, 90 | }: { 91 | href: string; 92 | direction: 'left' | 'right'; 93 | isDisabled?: boolean; 94 | }) { 95 | const className = clsx( 96 | 'flex h-10 w-10 items-center justify-center rounded-md border', 97 | { 98 | 'pointer-events-none text-gray-300': isDisabled, 99 | 'hover:bg-gray-100': !isDisabled, 100 | 'mr-2 md:mr-4': direction === 'left', 101 | 'ml-2 md:ml-4': direction === 'right', 102 | }, 103 | ); 104 | 105 | const icon = 106 | direction === 'left' ? ( 107 | 108 | ) : ( 109 | 110 | ); 111 | 112 | return isDisabled ? ( 113 |
{icon}
114 | ) : ( 115 | 116 | {icon} 117 | 118 | ); 119 | } 120 | -------------------------------------------------------------------------------- /app/ui/invoices/status.tsx: -------------------------------------------------------------------------------- 1 | import { CheckIcon, ClockIcon } from '@heroicons/react/24/outline'; 2 | import clsx from 'clsx'; 3 | 4 | export default function InvoiceStatus({ status }: { status: string }) { 5 | return ( 6 | 15 | {status === 'pending' ? ( 16 | <> 17 | Pending 18 | 19 | 20 | ) : null} 21 | {status === 'paid' ? ( 22 | <> 23 | Paid 24 | 25 | 26 | ) : null} 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /app/ui/invoices/table.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | import { UpdateInvoice, DeleteInvoice } from '@/app/ui/invoices/buttons'; 3 | import InvoiceStatus from '@/app/ui/invoices/status'; 4 | import { formatDateToLocal, formatCurrency } from '@/app/lib/utils'; 5 | import { fetchFilteredInvoices } from '@/app/lib/data'; 6 | 7 | export default async function InvoicesTable({ 8 | query, 9 | currentPage, 10 | }: { 11 | query: string; 12 | currentPage: number; 13 | }) { 14 | const invoices = await fetchFilteredInvoices(query, currentPage); 15 | 16 | return ( 17 |
18 |
19 |
20 |
21 | {invoices?.map((invoice) => ( 22 |
26 |
27 |
28 |
29 | {`${invoice.name}'s 36 |

{invoice.name}

37 |
38 |

{invoice.email}

39 |
40 | 41 |
42 |
43 |
44 |

45 | {formatCurrency(invoice.amount)} 46 |

47 |

{formatDateToLocal(invoice.date)}

48 |
49 |
50 | 51 | 52 |
53 |
54 |
55 | ))} 56 |
57 | 58 | 59 | 60 | 63 | 66 | 69 | 72 | 75 | 78 | 79 | 80 | 81 | {invoices?.map((invoice) => ( 82 | 86 | 98 | 101 | 104 | 107 | 110 | 116 | 117 | ))} 118 | 119 |
61 | Customer 62 | 64 | Email 65 | 67 | Amount 68 | 70 | Date 71 | 73 | Status 74 | 76 | Edit 77 |
87 |
88 | {`${invoice.name}'s 95 |

{invoice.name}

96 |
97 |
99 | {invoice.email} 100 | 102 | {formatCurrency(invoice.amount)} 103 | 105 | {formatDateToLocal(invoice.date)} 106 | 108 | 109 | 111 |
112 | 113 | 114 |
115 |
120 |
121 |
122 |
123 | ); 124 | } 125 | -------------------------------------------------------------------------------- /app/ui/login-form.tsx: -------------------------------------------------------------------------------- 1 | import { lusitana } from '@/app/ui/fonts'; 2 | import { 3 | AtSymbolIcon, 4 | KeyIcon, 5 | ExclamationCircleIcon, 6 | } from '@heroicons/react/24/outline'; 7 | import { ArrowRightIcon } from '@heroicons/react/20/solid'; 8 | import { Button } from './button'; 9 | 10 | export default function LoginForm() { 11 | return ( 12 |
13 |
14 |

15 | Please log in to continue. 16 |

17 |
18 |
19 | 25 |
26 | 34 | 35 |
36 |
37 |
38 | 44 |
45 | 54 | 55 |
56 |
57 |
58 | 61 |
62 | {/* Add form errors here */} 63 |
64 |
65 |
66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /app/ui/search.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { MagnifyingGlassIcon } from '@heroicons/react/24/outline'; 4 | 5 | export default function Search({ placeholder }: { placeholder: string }) { 6 | return ( 7 |
8 | 11 | 15 | 16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /app/ui/skeletons.tsx: -------------------------------------------------------------------------------- 1 | // Loading animation 2 | const shimmer = 3 | 'before:absolute before:inset-0 before:-translate-x-full before:animate-[shimmer_2s_infinite] before:bg-gradient-to-r before:from-transparent before:via-white/60 before:to-transparent'; 4 | 5 | export function CardSkeleton() { 6 | return ( 7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | ); 19 | } 20 | 21 | export function CardsSkeleton() { 22 | return ( 23 | <> 24 | 25 | 26 | 27 | 28 | 29 | ); 30 | } 31 | 32 | export function RevenueChartSkeleton() { 33 | return ( 34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | ); 45 | } 46 | 47 | export function InvoiceSkeleton() { 48 | return ( 49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | ); 60 | } 61 | 62 | export function LatestInvoicesSkeleton() { 63 | return ( 64 |
67 |
68 |
69 |
70 | 71 | 72 | 73 | 74 | 75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | ); 83 | } 84 | 85 | export default function DashboardSkeleton() { 86 | return ( 87 | <> 88 |
91 |
92 | 93 | 94 | 95 | 96 |
97 |
98 | 99 | 100 |
101 | 102 | ); 103 | } 104 | 105 | export function TableRowSkeleton() { 106 | return ( 107 | 108 | {/* Customer Name and Image */} 109 | 110 |
111 |
112 |
113 |
114 | 115 | {/* Email */} 116 | 117 |
118 | 119 | {/* Amount */} 120 | 121 |
122 | 123 | {/* Date */} 124 | 125 |
126 | 127 | {/* Status */} 128 | 129 |
130 | 131 | {/* Actions */} 132 | 133 |
134 |
135 |
136 |
137 | 138 | 139 | ); 140 | } 141 | 142 | export function InvoicesMobileSkeleton() { 143 | return ( 144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 | ); 164 | } 165 | 166 | export function InvoicesTableSkeleton() { 167 | return ( 168 |
169 |
170 |
171 |
172 | 173 | 174 | 175 | 176 | 177 | 178 |
179 | 180 | 181 | 182 | 185 | 188 | 191 | 194 | 197 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 |
183 | Customer 184 | 186 | Email 187 | 189 | Amount 190 | 192 | Date 193 | 195 | Status 196 | 201 | Edit 202 |
214 |
215 |
216 |
217 | ); 218 | } 219 | -------------------------------------------------------------------------------- /next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from 'next'; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "next build", 5 | "dev": "next dev --turbopack", 6 | "start": "next start" 7 | }, 8 | "dependencies": { 9 | "@heroicons/react": "^2.2.0", 10 | "@tailwindcss/forms": "^0.5.10", 11 | "autoprefixer": "10.4.20", 12 | "bcrypt": "^5.1.1", 13 | "clsx": "^2.1.1", 14 | "next": "latest", 15 | "next-auth": "5.0.0-beta.25", 16 | "postcss": "8.5.1", 17 | "postgres": "^3.4.5", 18 | "react": "latest", 19 | "react-dom": "latest", 20 | "tailwindcss": "3.4.17", 21 | "typescript": "5.7.3", 22 | "use-debounce": "^10.0.4", 23 | "zod": "^3.24.1" 24 | }, 25 | "devDependencies": { 26 | "@types/bcrypt": "^5.0.2", 27 | "@types/node": "22.10.7", 28 | "@types/react": "19.0.7", 29 | "@types/react-dom": "19.0.3" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@heroicons/react': 12 | specifier: ^2.2.0 13 | version: 2.2.0(react@19.0.0) 14 | '@tailwindcss/forms': 15 | specifier: ^0.5.10 16 | version: 0.5.10(tailwindcss@3.4.17) 17 | autoprefixer: 18 | specifier: 10.4.20 19 | version: 10.4.20(postcss@8.5.1) 20 | bcrypt: 21 | specifier: ^5.1.1 22 | version: 5.1.1 23 | clsx: 24 | specifier: ^2.1.1 25 | version: 2.1.1 26 | next: 27 | specifier: latest 28 | version: 15.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 29 | next-auth: 30 | specifier: 5.0.0-beta.25 31 | version: 5.0.0-beta.25(next@15.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) 32 | postcss: 33 | specifier: 8.5.1 34 | version: 8.5.1 35 | postgres: 36 | specifier: ^3.4.5 37 | version: 3.4.5 38 | react: 39 | specifier: latest 40 | version: 19.0.0 41 | react-dom: 42 | specifier: latest 43 | version: 19.0.0(react@19.0.0) 44 | tailwindcss: 45 | specifier: 3.4.17 46 | version: 3.4.17 47 | typescript: 48 | specifier: 5.7.3 49 | version: 5.7.3 50 | use-debounce: 51 | specifier: ^10.0.4 52 | version: 10.0.4(react@19.0.0) 53 | zod: 54 | specifier: ^3.24.1 55 | version: 3.24.1 56 | devDependencies: 57 | '@types/bcrypt': 58 | specifier: ^5.0.2 59 | version: 5.0.2 60 | '@types/node': 61 | specifier: 22.10.7 62 | version: 22.10.7 63 | '@types/react': 64 | specifier: 19.0.7 65 | version: 19.0.7 66 | '@types/react-dom': 67 | specifier: 19.0.3 68 | version: 19.0.3(@types/react@19.0.7) 69 | 70 | packages: 71 | 72 | '@alloc/quick-lru@5.2.0': 73 | resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} 74 | engines: {node: '>=10'} 75 | 76 | '@auth/core@0.37.2': 77 | resolution: {integrity: sha512-kUvzyvkcd6h1vpeMAojK2y7+PAV5H+0Cc9+ZlKYDFhDY31AlvsB+GW5vNO4qE3Y07KeQgvNO9U0QUx/fN62kBw==} 78 | peerDependencies: 79 | '@simplewebauthn/browser': ^9.0.1 80 | '@simplewebauthn/server': ^9.0.2 81 | nodemailer: ^6.8.0 82 | peerDependenciesMeta: 83 | '@simplewebauthn/browser': 84 | optional: true 85 | '@simplewebauthn/server': 86 | optional: true 87 | nodemailer: 88 | optional: true 89 | 90 | '@emnapi/runtime@1.3.1': 91 | resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} 92 | 93 | '@heroicons/react@2.2.0': 94 | resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==} 95 | peerDependencies: 96 | react: '>= 16 || ^19.0.0-rc' 97 | 98 | '@img/sharp-darwin-arm64@0.33.5': 99 | resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} 100 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 101 | cpu: [arm64] 102 | os: [darwin] 103 | 104 | '@img/sharp-darwin-x64@0.33.5': 105 | resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} 106 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 107 | cpu: [x64] 108 | os: [darwin] 109 | 110 | '@img/sharp-libvips-darwin-arm64@1.0.4': 111 | resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} 112 | cpu: [arm64] 113 | os: [darwin] 114 | 115 | '@img/sharp-libvips-darwin-x64@1.0.4': 116 | resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} 117 | cpu: [x64] 118 | os: [darwin] 119 | 120 | '@img/sharp-libvips-linux-arm64@1.0.4': 121 | resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} 122 | cpu: [arm64] 123 | os: [linux] 124 | 125 | '@img/sharp-libvips-linux-arm@1.0.5': 126 | resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} 127 | cpu: [arm] 128 | os: [linux] 129 | 130 | '@img/sharp-libvips-linux-s390x@1.0.4': 131 | resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} 132 | cpu: [s390x] 133 | os: [linux] 134 | 135 | '@img/sharp-libvips-linux-x64@1.0.4': 136 | resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} 137 | cpu: [x64] 138 | os: [linux] 139 | 140 | '@img/sharp-libvips-linuxmusl-arm64@1.0.4': 141 | resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} 142 | cpu: [arm64] 143 | os: [linux] 144 | 145 | '@img/sharp-libvips-linuxmusl-x64@1.0.4': 146 | resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} 147 | cpu: [x64] 148 | os: [linux] 149 | 150 | '@img/sharp-linux-arm64@0.33.5': 151 | resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} 152 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 153 | cpu: [arm64] 154 | os: [linux] 155 | 156 | '@img/sharp-linux-arm@0.33.5': 157 | resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} 158 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 159 | cpu: [arm] 160 | os: [linux] 161 | 162 | '@img/sharp-linux-s390x@0.33.5': 163 | resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} 164 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 165 | cpu: [s390x] 166 | os: [linux] 167 | 168 | '@img/sharp-linux-x64@0.33.5': 169 | resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} 170 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 171 | cpu: [x64] 172 | os: [linux] 173 | 174 | '@img/sharp-linuxmusl-arm64@0.33.5': 175 | resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} 176 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 177 | cpu: [arm64] 178 | os: [linux] 179 | 180 | '@img/sharp-linuxmusl-x64@0.33.5': 181 | resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} 182 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 183 | cpu: [x64] 184 | os: [linux] 185 | 186 | '@img/sharp-wasm32@0.33.5': 187 | resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} 188 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 189 | cpu: [wasm32] 190 | 191 | '@img/sharp-win32-ia32@0.33.5': 192 | resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} 193 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 194 | cpu: [ia32] 195 | os: [win32] 196 | 197 | '@img/sharp-win32-x64@0.33.5': 198 | resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} 199 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 200 | cpu: [x64] 201 | os: [win32] 202 | 203 | '@isaacs/cliui@8.0.2': 204 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 205 | engines: {node: '>=12'} 206 | 207 | '@jridgewell/gen-mapping@0.3.8': 208 | resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} 209 | engines: {node: '>=6.0.0'} 210 | 211 | '@jridgewell/resolve-uri@3.1.2': 212 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 213 | engines: {node: '>=6.0.0'} 214 | 215 | '@jridgewell/set-array@1.2.1': 216 | resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 217 | engines: {node: '>=6.0.0'} 218 | 219 | '@jridgewell/sourcemap-codec@1.5.0': 220 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 221 | 222 | '@jridgewell/trace-mapping@0.3.25': 223 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 224 | 225 | '@mapbox/node-pre-gyp@1.0.11': 226 | resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==} 227 | hasBin: true 228 | 229 | '@next/env@15.1.6': 230 | resolution: {integrity: sha512-d9AFQVPEYNr+aqokIiPLNK/MTyt3DWa/dpKveiAaVccUadFbhFEvY6FXYX2LJO2Hv7PHnLBu2oWwB4uBuHjr/w==} 231 | 232 | '@next/swc-darwin-arm64@15.1.6': 233 | resolution: {integrity: sha512-u7lg4Mpl9qWpKgy6NzEkz/w0/keEHtOybmIl0ykgItBxEM5mYotS5PmqTpo+Rhg8FiOiWgwr8USxmKQkqLBCrw==} 234 | engines: {node: '>= 10'} 235 | cpu: [arm64] 236 | os: [darwin] 237 | 238 | '@next/swc-darwin-x64@15.1.6': 239 | resolution: {integrity: sha512-x1jGpbHbZoZ69nRuogGL2MYPLqohlhnT9OCU6E6QFewwup+z+M6r8oU47BTeJcWsF2sdBahp5cKiAcDbwwK/lg==} 240 | engines: {node: '>= 10'} 241 | cpu: [x64] 242 | os: [darwin] 243 | 244 | '@next/swc-linux-arm64-gnu@15.1.6': 245 | resolution: {integrity: sha512-jar9sFw0XewXsBzPf9runGzoivajeWJUc/JkfbLTC4it9EhU8v7tCRLH7l5Y1ReTMN6zKJO0kKAGqDk8YSO2bg==} 246 | engines: {node: '>= 10'} 247 | cpu: [arm64] 248 | os: [linux] 249 | 250 | '@next/swc-linux-arm64-musl@15.1.6': 251 | resolution: {integrity: sha512-+n3u//bfsrIaZch4cgOJ3tXCTbSxz0s6brJtU3SzLOvkJlPQMJ+eHVRi6qM2kKKKLuMY+tcau8XD9CJ1OjeSQQ==} 252 | engines: {node: '>= 10'} 253 | cpu: [arm64] 254 | os: [linux] 255 | 256 | '@next/swc-linux-x64-gnu@15.1.6': 257 | resolution: {integrity: sha512-SpuDEXixM3PycniL4iVCLyUyvcl6Lt0mtv3am08sucskpG0tYkW1KlRhTgj4LI5ehyxriVVcfdoxuuP8csi3kQ==} 258 | engines: {node: '>= 10'} 259 | cpu: [x64] 260 | os: [linux] 261 | 262 | '@next/swc-linux-x64-musl@15.1.6': 263 | resolution: {integrity: sha512-L4druWmdFSZIIRhF+G60API5sFB7suTbDRhYWSjiw0RbE+15igQvE2g2+S973pMGvwN3guw7cJUjA/TmbPWTHQ==} 264 | engines: {node: '>= 10'} 265 | cpu: [x64] 266 | os: [linux] 267 | 268 | '@next/swc-win32-arm64-msvc@15.1.6': 269 | resolution: {integrity: sha512-s8w6EeqNmi6gdvM19tqKKWbCyOBvXFbndkGHl+c9YrzsLARRdCHsD9S1fMj8gsXm9v8vhC8s3N8rjuC/XrtkEg==} 270 | engines: {node: '>= 10'} 271 | cpu: [arm64] 272 | os: [win32] 273 | 274 | '@next/swc-win32-x64-msvc@15.1.6': 275 | resolution: {integrity: sha512-6xomMuu54FAFxttYr5PJbEfu96godcxBTRk1OhAvJq0/EnmFU/Ybiax30Snis4vdWZ9LGpf7Roy5fSs7v/5ROQ==} 276 | engines: {node: '>= 10'} 277 | cpu: [x64] 278 | os: [win32] 279 | 280 | '@nodelib/fs.scandir@2.1.5': 281 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 282 | engines: {node: '>= 8'} 283 | 284 | '@nodelib/fs.stat@2.0.5': 285 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 286 | engines: {node: '>= 8'} 287 | 288 | '@nodelib/fs.walk@1.2.8': 289 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 290 | engines: {node: '>= 8'} 291 | 292 | '@panva/hkdf@1.2.1': 293 | resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} 294 | 295 | '@pkgjs/parseargs@0.11.0': 296 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 297 | engines: {node: '>=14'} 298 | 299 | '@swc/counter@0.1.3': 300 | resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} 301 | 302 | '@swc/helpers@0.5.15': 303 | resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} 304 | 305 | '@tailwindcss/forms@0.5.10': 306 | resolution: {integrity: sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==} 307 | peerDependencies: 308 | tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1' 309 | 310 | '@types/bcrypt@5.0.2': 311 | resolution: {integrity: sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==} 312 | 313 | '@types/cookie@0.6.0': 314 | resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} 315 | 316 | '@types/node@22.10.7': 317 | resolution: {integrity: sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==} 318 | 319 | '@types/react-dom@19.0.3': 320 | resolution: {integrity: sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==} 321 | peerDependencies: 322 | '@types/react': ^19.0.0 323 | 324 | '@types/react@19.0.7': 325 | resolution: {integrity: sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==} 326 | 327 | abbrev@1.1.1: 328 | resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} 329 | 330 | agent-base@6.0.2: 331 | resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} 332 | engines: {node: '>= 6.0.0'} 333 | 334 | ansi-regex@5.0.1: 335 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 336 | engines: {node: '>=8'} 337 | 338 | ansi-regex@6.1.0: 339 | resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} 340 | engines: {node: '>=12'} 341 | 342 | ansi-styles@4.3.0: 343 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 344 | engines: {node: '>=8'} 345 | 346 | ansi-styles@6.2.1: 347 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 348 | engines: {node: '>=12'} 349 | 350 | any-promise@1.3.0: 351 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} 352 | 353 | anymatch@3.1.3: 354 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 355 | engines: {node: '>= 8'} 356 | 357 | aproba@2.0.0: 358 | resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} 359 | 360 | are-we-there-yet@2.0.0: 361 | resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==} 362 | engines: {node: '>=10'} 363 | deprecated: This package is no longer supported. 364 | 365 | arg@5.0.2: 366 | resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} 367 | 368 | autoprefixer@10.4.20: 369 | resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} 370 | engines: {node: ^10 || ^12 || >=14} 371 | hasBin: true 372 | peerDependencies: 373 | postcss: ^8.1.0 374 | 375 | balanced-match@1.0.2: 376 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 377 | 378 | bcrypt@5.1.1: 379 | resolution: {integrity: sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==} 380 | engines: {node: '>= 10.0.0'} 381 | 382 | binary-extensions@2.3.0: 383 | resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} 384 | engines: {node: '>=8'} 385 | 386 | brace-expansion@1.1.11: 387 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 388 | 389 | brace-expansion@2.0.1: 390 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 391 | 392 | braces@3.0.3: 393 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 394 | engines: {node: '>=8'} 395 | 396 | browserslist@4.24.4: 397 | resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} 398 | engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 399 | hasBin: true 400 | 401 | busboy@1.6.0: 402 | resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} 403 | engines: {node: '>=10.16.0'} 404 | 405 | camelcase-css@2.0.1: 406 | resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} 407 | engines: {node: '>= 6'} 408 | 409 | caniuse-lite@1.0.30001695: 410 | resolution: {integrity: sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==} 411 | 412 | chokidar@3.6.0: 413 | resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} 414 | engines: {node: '>= 8.10.0'} 415 | 416 | chownr@2.0.0: 417 | resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} 418 | engines: {node: '>=10'} 419 | 420 | client-only@0.0.1: 421 | resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} 422 | 423 | clsx@2.1.1: 424 | resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} 425 | engines: {node: '>=6'} 426 | 427 | color-convert@2.0.1: 428 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 429 | engines: {node: '>=7.0.0'} 430 | 431 | color-name@1.1.4: 432 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 433 | 434 | color-string@1.9.1: 435 | resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} 436 | 437 | color-support@1.1.3: 438 | resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} 439 | hasBin: true 440 | 441 | color@4.2.3: 442 | resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} 443 | engines: {node: '>=12.5.0'} 444 | 445 | commander@4.1.1: 446 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} 447 | engines: {node: '>= 6'} 448 | 449 | concat-map@0.0.1: 450 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 451 | 452 | console-control-strings@1.1.0: 453 | resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} 454 | 455 | cookie@0.7.1: 456 | resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} 457 | engines: {node: '>= 0.6'} 458 | 459 | cross-spawn@7.0.6: 460 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 461 | engines: {node: '>= 8'} 462 | 463 | cssesc@3.0.0: 464 | resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} 465 | engines: {node: '>=4'} 466 | hasBin: true 467 | 468 | csstype@3.1.3: 469 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} 470 | 471 | debug@4.4.0: 472 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 473 | engines: {node: '>=6.0'} 474 | peerDependencies: 475 | supports-color: '*' 476 | peerDependenciesMeta: 477 | supports-color: 478 | optional: true 479 | 480 | delegates@1.0.0: 481 | resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} 482 | 483 | detect-libc@2.0.3: 484 | resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} 485 | engines: {node: '>=8'} 486 | 487 | didyoumean@1.2.2: 488 | resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} 489 | 490 | dlv@1.1.3: 491 | resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} 492 | 493 | eastasianwidth@0.2.0: 494 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 495 | 496 | electron-to-chromium@1.5.83: 497 | resolution: {integrity: sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==} 498 | 499 | emoji-regex@8.0.0: 500 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 501 | 502 | emoji-regex@9.2.2: 503 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 504 | 505 | escalade@3.2.0: 506 | resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} 507 | engines: {node: '>=6'} 508 | 509 | fast-glob@3.3.3: 510 | resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} 511 | engines: {node: '>=8.6.0'} 512 | 513 | fastq@1.18.0: 514 | resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==} 515 | 516 | fill-range@7.1.1: 517 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 518 | engines: {node: '>=8'} 519 | 520 | foreground-child@3.3.0: 521 | resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} 522 | engines: {node: '>=14'} 523 | 524 | fraction.js@4.3.7: 525 | resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} 526 | 527 | fs-minipass@2.1.0: 528 | resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} 529 | engines: {node: '>= 8'} 530 | 531 | fs.realpath@1.0.0: 532 | resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} 533 | 534 | fsevents@2.3.3: 535 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 536 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 537 | os: [darwin] 538 | 539 | function-bind@1.1.2: 540 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 541 | 542 | gauge@3.0.2: 543 | resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==} 544 | engines: {node: '>=10'} 545 | deprecated: This package is no longer supported. 546 | 547 | glob-parent@5.1.2: 548 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 549 | engines: {node: '>= 6'} 550 | 551 | glob-parent@6.0.2: 552 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 553 | engines: {node: '>=10.13.0'} 554 | 555 | glob@10.4.5: 556 | resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} 557 | hasBin: true 558 | 559 | glob@7.2.3: 560 | resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} 561 | deprecated: Glob versions prior to v9 are no longer supported 562 | 563 | has-unicode@2.0.1: 564 | resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} 565 | 566 | hasown@2.0.2: 567 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 568 | engines: {node: '>= 0.4'} 569 | 570 | https-proxy-agent@5.0.1: 571 | resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} 572 | engines: {node: '>= 6'} 573 | 574 | inflight@1.0.6: 575 | resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} 576 | deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. 577 | 578 | inherits@2.0.4: 579 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 580 | 581 | is-arrayish@0.3.2: 582 | resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} 583 | 584 | is-binary-path@2.1.0: 585 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 586 | engines: {node: '>=8'} 587 | 588 | is-core-module@2.16.1: 589 | resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} 590 | engines: {node: '>= 0.4'} 591 | 592 | is-extglob@2.1.1: 593 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 594 | engines: {node: '>=0.10.0'} 595 | 596 | is-fullwidth-code-point@3.0.0: 597 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 598 | engines: {node: '>=8'} 599 | 600 | is-glob@4.0.3: 601 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 602 | engines: {node: '>=0.10.0'} 603 | 604 | is-number@7.0.0: 605 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 606 | engines: {node: '>=0.12.0'} 607 | 608 | isexe@2.0.0: 609 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 610 | 611 | jackspeak@3.4.3: 612 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} 613 | 614 | jiti@1.21.7: 615 | resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} 616 | hasBin: true 617 | 618 | jose@5.9.6: 619 | resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==} 620 | 621 | lilconfig@3.1.3: 622 | resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} 623 | engines: {node: '>=14'} 624 | 625 | lines-and-columns@1.2.4: 626 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 627 | 628 | lru-cache@10.4.3: 629 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 630 | 631 | make-dir@3.1.0: 632 | resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} 633 | engines: {node: '>=8'} 634 | 635 | merge2@1.4.1: 636 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 637 | engines: {node: '>= 8'} 638 | 639 | micromatch@4.0.8: 640 | resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 641 | engines: {node: '>=8.6'} 642 | 643 | mini-svg-data-uri@1.4.4: 644 | resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} 645 | hasBin: true 646 | 647 | minimatch@3.1.2: 648 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 649 | 650 | minimatch@9.0.5: 651 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 652 | engines: {node: '>=16 || 14 >=14.17'} 653 | 654 | minipass@3.3.6: 655 | resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} 656 | engines: {node: '>=8'} 657 | 658 | minipass@5.0.0: 659 | resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} 660 | engines: {node: '>=8'} 661 | 662 | minipass@7.1.2: 663 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 664 | engines: {node: '>=16 || 14 >=14.17'} 665 | 666 | minizlib@2.1.2: 667 | resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} 668 | engines: {node: '>= 8'} 669 | 670 | mkdirp@1.0.4: 671 | resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} 672 | engines: {node: '>=10'} 673 | hasBin: true 674 | 675 | ms@2.1.3: 676 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 677 | 678 | mz@2.7.0: 679 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} 680 | 681 | nanoid@3.3.8: 682 | resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} 683 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 684 | hasBin: true 685 | 686 | next-auth@5.0.0-beta.25: 687 | resolution: {integrity: sha512-2dJJw1sHQl2qxCrRk+KTQbeH+izFbGFPuJj5eGgBZFYyiYYtvlrBeUw1E/OJJxTRjuxbSYGnCTkUIRsIIW0bog==} 688 | peerDependencies: 689 | '@simplewebauthn/browser': ^9.0.1 690 | '@simplewebauthn/server': ^9.0.2 691 | next: ^14.0.0-0 || ^15.0.0-0 692 | nodemailer: ^6.6.5 693 | react: ^18.2.0 || ^19.0.0-0 694 | peerDependenciesMeta: 695 | '@simplewebauthn/browser': 696 | optional: true 697 | '@simplewebauthn/server': 698 | optional: true 699 | nodemailer: 700 | optional: true 701 | 702 | next@15.1.6: 703 | resolution: {integrity: sha512-Hch4wzbaX0vKQtalpXvUiw5sYivBy4cm5rzUKrBnUB/y436LGrvOUqYvlSeNVCWFO/770gDlltR9gqZH62ct4Q==} 704 | engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} 705 | hasBin: true 706 | peerDependencies: 707 | '@opentelemetry/api': ^1.1.0 708 | '@playwright/test': ^1.41.2 709 | babel-plugin-react-compiler: '*' 710 | react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 711 | react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 712 | sass: ^1.3.0 713 | peerDependenciesMeta: 714 | '@opentelemetry/api': 715 | optional: true 716 | '@playwright/test': 717 | optional: true 718 | babel-plugin-react-compiler: 719 | optional: true 720 | sass: 721 | optional: true 722 | 723 | node-addon-api@5.1.0: 724 | resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} 725 | 726 | node-fetch@2.7.0: 727 | resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} 728 | engines: {node: 4.x || >=6.0.0} 729 | peerDependencies: 730 | encoding: ^0.1.0 731 | peerDependenciesMeta: 732 | encoding: 733 | optional: true 734 | 735 | node-releases@2.0.19: 736 | resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} 737 | 738 | nopt@5.0.0: 739 | resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} 740 | engines: {node: '>=6'} 741 | hasBin: true 742 | 743 | normalize-path@3.0.0: 744 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 745 | engines: {node: '>=0.10.0'} 746 | 747 | normalize-range@0.1.2: 748 | resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} 749 | engines: {node: '>=0.10.0'} 750 | 751 | npmlog@5.0.1: 752 | resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} 753 | deprecated: This package is no longer supported. 754 | 755 | oauth4webapi@3.1.4: 756 | resolution: {integrity: sha512-eVfN3nZNbok2s/ROifO0UAc5G8nRoLSbrcKJ09OqmucgnhXEfdIQOR4gq1eJH1rN3gV7rNw62bDEgftsgFtBEg==} 757 | 758 | object-assign@4.1.1: 759 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 760 | engines: {node: '>=0.10.0'} 761 | 762 | object-hash@3.0.0: 763 | resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} 764 | engines: {node: '>= 6'} 765 | 766 | once@1.4.0: 767 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 768 | 769 | package-json-from-dist@1.0.1: 770 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 771 | 772 | path-is-absolute@1.0.1: 773 | resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} 774 | engines: {node: '>=0.10.0'} 775 | 776 | path-key@3.1.1: 777 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 778 | engines: {node: '>=8'} 779 | 780 | path-parse@1.0.7: 781 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 782 | 783 | path-scurry@1.11.1: 784 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 785 | engines: {node: '>=16 || 14 >=14.18'} 786 | 787 | picocolors@1.1.1: 788 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 789 | 790 | picomatch@2.3.1: 791 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 792 | engines: {node: '>=8.6'} 793 | 794 | pify@2.3.0: 795 | resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} 796 | engines: {node: '>=0.10.0'} 797 | 798 | pirates@4.0.6: 799 | resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} 800 | engines: {node: '>= 6'} 801 | 802 | postcss-import@15.1.0: 803 | resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} 804 | engines: {node: '>=14.0.0'} 805 | peerDependencies: 806 | postcss: ^8.0.0 807 | 808 | postcss-js@4.0.1: 809 | resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} 810 | engines: {node: ^12 || ^14 || >= 16} 811 | peerDependencies: 812 | postcss: ^8.4.21 813 | 814 | postcss-load-config@4.0.2: 815 | resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} 816 | engines: {node: '>= 14'} 817 | peerDependencies: 818 | postcss: '>=8.0.9' 819 | ts-node: '>=9.0.0' 820 | peerDependenciesMeta: 821 | postcss: 822 | optional: true 823 | ts-node: 824 | optional: true 825 | 826 | postcss-nested@6.2.0: 827 | resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} 828 | engines: {node: '>=12.0'} 829 | peerDependencies: 830 | postcss: ^8.2.14 831 | 832 | postcss-selector-parser@6.1.2: 833 | resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} 834 | engines: {node: '>=4'} 835 | 836 | postcss-value-parser@4.2.0: 837 | resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} 838 | 839 | postcss@8.4.31: 840 | resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} 841 | engines: {node: ^10 || ^12 || >=14} 842 | 843 | postcss@8.5.1: 844 | resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} 845 | engines: {node: ^10 || ^12 || >=14} 846 | 847 | postgres@3.4.5: 848 | resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==} 849 | engines: {node: '>=12'} 850 | 851 | preact-render-to-string@5.2.3: 852 | resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==} 853 | peerDependencies: 854 | preact: '>=10' 855 | 856 | preact@10.11.3: 857 | resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==} 858 | 859 | pretty-format@3.8.0: 860 | resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} 861 | 862 | queue-microtask@1.2.3: 863 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 864 | 865 | react-dom@19.0.0: 866 | resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} 867 | peerDependencies: 868 | react: ^19.0.0 869 | 870 | react@19.0.0: 871 | resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} 872 | engines: {node: '>=0.10.0'} 873 | 874 | read-cache@1.0.0: 875 | resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} 876 | 877 | readable-stream@3.6.2: 878 | resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} 879 | engines: {node: '>= 6'} 880 | 881 | readdirp@3.6.0: 882 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 883 | engines: {node: '>=8.10.0'} 884 | 885 | resolve@1.22.10: 886 | resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} 887 | engines: {node: '>= 0.4'} 888 | hasBin: true 889 | 890 | reusify@1.0.4: 891 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 892 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 893 | 894 | rimraf@3.0.2: 895 | resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} 896 | deprecated: Rimraf versions prior to v4 are no longer supported 897 | hasBin: true 898 | 899 | run-parallel@1.2.0: 900 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 901 | 902 | safe-buffer@5.2.1: 903 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 904 | 905 | scheduler@0.25.0: 906 | resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} 907 | 908 | semver@6.3.1: 909 | resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} 910 | hasBin: true 911 | 912 | semver@7.6.3: 913 | resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} 914 | engines: {node: '>=10'} 915 | hasBin: true 916 | 917 | set-blocking@2.0.0: 918 | resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} 919 | 920 | sharp@0.33.5: 921 | resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} 922 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} 923 | 924 | shebang-command@2.0.0: 925 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 926 | engines: {node: '>=8'} 927 | 928 | shebang-regex@3.0.0: 929 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 930 | engines: {node: '>=8'} 931 | 932 | signal-exit@3.0.7: 933 | resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} 934 | 935 | signal-exit@4.1.0: 936 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 937 | engines: {node: '>=14'} 938 | 939 | simple-swizzle@0.2.2: 940 | resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} 941 | 942 | source-map-js@1.2.1: 943 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 944 | engines: {node: '>=0.10.0'} 945 | 946 | streamsearch@1.1.0: 947 | resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} 948 | engines: {node: '>=10.0.0'} 949 | 950 | string-width@4.2.3: 951 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 952 | engines: {node: '>=8'} 953 | 954 | string-width@5.1.2: 955 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 956 | engines: {node: '>=12'} 957 | 958 | string_decoder@1.3.0: 959 | resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} 960 | 961 | strip-ansi@6.0.1: 962 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 963 | engines: {node: '>=8'} 964 | 965 | strip-ansi@7.1.0: 966 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 967 | engines: {node: '>=12'} 968 | 969 | styled-jsx@5.1.6: 970 | resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} 971 | engines: {node: '>= 12.0.0'} 972 | peerDependencies: 973 | '@babel/core': '*' 974 | babel-plugin-macros: '*' 975 | react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' 976 | peerDependenciesMeta: 977 | '@babel/core': 978 | optional: true 979 | babel-plugin-macros: 980 | optional: true 981 | 982 | sucrase@3.35.0: 983 | resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} 984 | engines: {node: '>=16 || 14 >=14.17'} 985 | hasBin: true 986 | 987 | supports-preserve-symlinks-flag@1.0.0: 988 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 989 | engines: {node: '>= 0.4'} 990 | 991 | tailwindcss@3.4.17: 992 | resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} 993 | engines: {node: '>=14.0.0'} 994 | hasBin: true 995 | 996 | tar@6.2.1: 997 | resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} 998 | engines: {node: '>=10'} 999 | 1000 | thenify-all@1.6.0: 1001 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} 1002 | engines: {node: '>=0.8'} 1003 | 1004 | thenify@3.3.1: 1005 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} 1006 | 1007 | to-regex-range@5.0.1: 1008 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 1009 | engines: {node: '>=8.0'} 1010 | 1011 | tr46@0.0.3: 1012 | resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} 1013 | 1014 | ts-interface-checker@0.1.13: 1015 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 1016 | 1017 | tslib@2.8.1: 1018 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 1019 | 1020 | typescript@5.7.3: 1021 | resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} 1022 | engines: {node: '>=14.17'} 1023 | hasBin: true 1024 | 1025 | undici-types@6.20.0: 1026 | resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} 1027 | 1028 | update-browserslist-db@1.1.2: 1029 | resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} 1030 | hasBin: true 1031 | peerDependencies: 1032 | browserslist: '>= 4.21.0' 1033 | 1034 | use-debounce@10.0.4: 1035 | resolution: {integrity: sha512-6Cf7Yr7Wk7Kdv77nnJMf6de4HuDE4dTxKij+RqE9rufDsI6zsbjyAxcH5y2ueJCQAnfgKbzXbZHYlkFwmBlWkw==} 1036 | engines: {node: '>= 16.0.0'} 1037 | peerDependencies: 1038 | react: '*' 1039 | 1040 | util-deprecate@1.0.2: 1041 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 1042 | 1043 | webidl-conversions@3.0.1: 1044 | resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} 1045 | 1046 | whatwg-url@5.0.0: 1047 | resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} 1048 | 1049 | which@2.0.2: 1050 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1051 | engines: {node: '>= 8'} 1052 | hasBin: true 1053 | 1054 | wide-align@1.1.5: 1055 | resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} 1056 | 1057 | wrap-ansi@7.0.0: 1058 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 1059 | engines: {node: '>=10'} 1060 | 1061 | wrap-ansi@8.1.0: 1062 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 1063 | engines: {node: '>=12'} 1064 | 1065 | wrappy@1.0.2: 1066 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 1067 | 1068 | yallist@4.0.0: 1069 | resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} 1070 | 1071 | yaml@2.7.0: 1072 | resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} 1073 | engines: {node: '>= 14'} 1074 | hasBin: true 1075 | 1076 | zod@3.24.1: 1077 | resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} 1078 | 1079 | snapshots: 1080 | 1081 | '@alloc/quick-lru@5.2.0': {} 1082 | 1083 | '@auth/core@0.37.2': 1084 | dependencies: 1085 | '@panva/hkdf': 1.2.1 1086 | '@types/cookie': 0.6.0 1087 | cookie: 0.7.1 1088 | jose: 5.9.6 1089 | oauth4webapi: 3.1.4 1090 | preact: 10.11.3 1091 | preact-render-to-string: 5.2.3(preact@10.11.3) 1092 | 1093 | '@emnapi/runtime@1.3.1': 1094 | dependencies: 1095 | tslib: 2.8.1 1096 | optional: true 1097 | 1098 | '@heroicons/react@2.2.0(react@19.0.0)': 1099 | dependencies: 1100 | react: 19.0.0 1101 | 1102 | '@img/sharp-darwin-arm64@0.33.5': 1103 | optionalDependencies: 1104 | '@img/sharp-libvips-darwin-arm64': 1.0.4 1105 | optional: true 1106 | 1107 | '@img/sharp-darwin-x64@0.33.5': 1108 | optionalDependencies: 1109 | '@img/sharp-libvips-darwin-x64': 1.0.4 1110 | optional: true 1111 | 1112 | '@img/sharp-libvips-darwin-arm64@1.0.4': 1113 | optional: true 1114 | 1115 | '@img/sharp-libvips-darwin-x64@1.0.4': 1116 | optional: true 1117 | 1118 | '@img/sharp-libvips-linux-arm64@1.0.4': 1119 | optional: true 1120 | 1121 | '@img/sharp-libvips-linux-arm@1.0.5': 1122 | optional: true 1123 | 1124 | '@img/sharp-libvips-linux-s390x@1.0.4': 1125 | optional: true 1126 | 1127 | '@img/sharp-libvips-linux-x64@1.0.4': 1128 | optional: true 1129 | 1130 | '@img/sharp-libvips-linuxmusl-arm64@1.0.4': 1131 | optional: true 1132 | 1133 | '@img/sharp-libvips-linuxmusl-x64@1.0.4': 1134 | optional: true 1135 | 1136 | '@img/sharp-linux-arm64@0.33.5': 1137 | optionalDependencies: 1138 | '@img/sharp-libvips-linux-arm64': 1.0.4 1139 | optional: true 1140 | 1141 | '@img/sharp-linux-arm@0.33.5': 1142 | optionalDependencies: 1143 | '@img/sharp-libvips-linux-arm': 1.0.5 1144 | optional: true 1145 | 1146 | '@img/sharp-linux-s390x@0.33.5': 1147 | optionalDependencies: 1148 | '@img/sharp-libvips-linux-s390x': 1.0.4 1149 | optional: true 1150 | 1151 | '@img/sharp-linux-x64@0.33.5': 1152 | optionalDependencies: 1153 | '@img/sharp-libvips-linux-x64': 1.0.4 1154 | optional: true 1155 | 1156 | '@img/sharp-linuxmusl-arm64@0.33.5': 1157 | optionalDependencies: 1158 | '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 1159 | optional: true 1160 | 1161 | '@img/sharp-linuxmusl-x64@0.33.5': 1162 | optionalDependencies: 1163 | '@img/sharp-libvips-linuxmusl-x64': 1.0.4 1164 | optional: true 1165 | 1166 | '@img/sharp-wasm32@0.33.5': 1167 | dependencies: 1168 | '@emnapi/runtime': 1.3.1 1169 | optional: true 1170 | 1171 | '@img/sharp-win32-ia32@0.33.5': 1172 | optional: true 1173 | 1174 | '@img/sharp-win32-x64@0.33.5': 1175 | optional: true 1176 | 1177 | '@isaacs/cliui@8.0.2': 1178 | dependencies: 1179 | string-width: 5.1.2 1180 | string-width-cjs: string-width@4.2.3 1181 | strip-ansi: 7.1.0 1182 | strip-ansi-cjs: strip-ansi@6.0.1 1183 | wrap-ansi: 8.1.0 1184 | wrap-ansi-cjs: wrap-ansi@7.0.0 1185 | 1186 | '@jridgewell/gen-mapping@0.3.8': 1187 | dependencies: 1188 | '@jridgewell/set-array': 1.2.1 1189 | '@jridgewell/sourcemap-codec': 1.5.0 1190 | '@jridgewell/trace-mapping': 0.3.25 1191 | 1192 | '@jridgewell/resolve-uri@3.1.2': {} 1193 | 1194 | '@jridgewell/set-array@1.2.1': {} 1195 | 1196 | '@jridgewell/sourcemap-codec@1.5.0': {} 1197 | 1198 | '@jridgewell/trace-mapping@0.3.25': 1199 | dependencies: 1200 | '@jridgewell/resolve-uri': 3.1.2 1201 | '@jridgewell/sourcemap-codec': 1.5.0 1202 | 1203 | '@mapbox/node-pre-gyp@1.0.11': 1204 | dependencies: 1205 | detect-libc: 2.0.3 1206 | https-proxy-agent: 5.0.1 1207 | make-dir: 3.1.0 1208 | node-fetch: 2.7.0 1209 | nopt: 5.0.0 1210 | npmlog: 5.0.1 1211 | rimraf: 3.0.2 1212 | semver: 7.6.3 1213 | tar: 6.2.1 1214 | transitivePeerDependencies: 1215 | - encoding 1216 | - supports-color 1217 | 1218 | '@next/env@15.1.6': {} 1219 | 1220 | '@next/swc-darwin-arm64@15.1.6': 1221 | optional: true 1222 | 1223 | '@next/swc-darwin-x64@15.1.6': 1224 | optional: true 1225 | 1226 | '@next/swc-linux-arm64-gnu@15.1.6': 1227 | optional: true 1228 | 1229 | '@next/swc-linux-arm64-musl@15.1.6': 1230 | optional: true 1231 | 1232 | '@next/swc-linux-x64-gnu@15.1.6': 1233 | optional: true 1234 | 1235 | '@next/swc-linux-x64-musl@15.1.6': 1236 | optional: true 1237 | 1238 | '@next/swc-win32-arm64-msvc@15.1.6': 1239 | optional: true 1240 | 1241 | '@next/swc-win32-x64-msvc@15.1.6': 1242 | optional: true 1243 | 1244 | '@nodelib/fs.scandir@2.1.5': 1245 | dependencies: 1246 | '@nodelib/fs.stat': 2.0.5 1247 | run-parallel: 1.2.0 1248 | 1249 | '@nodelib/fs.stat@2.0.5': {} 1250 | 1251 | '@nodelib/fs.walk@1.2.8': 1252 | dependencies: 1253 | '@nodelib/fs.scandir': 2.1.5 1254 | fastq: 1.18.0 1255 | 1256 | '@panva/hkdf@1.2.1': {} 1257 | 1258 | '@pkgjs/parseargs@0.11.0': 1259 | optional: true 1260 | 1261 | '@swc/counter@0.1.3': {} 1262 | 1263 | '@swc/helpers@0.5.15': 1264 | dependencies: 1265 | tslib: 2.8.1 1266 | 1267 | '@tailwindcss/forms@0.5.10(tailwindcss@3.4.17)': 1268 | dependencies: 1269 | mini-svg-data-uri: 1.4.4 1270 | tailwindcss: 3.4.17 1271 | 1272 | '@types/bcrypt@5.0.2': 1273 | dependencies: 1274 | '@types/node': 22.10.7 1275 | 1276 | '@types/cookie@0.6.0': {} 1277 | 1278 | '@types/node@22.10.7': 1279 | dependencies: 1280 | undici-types: 6.20.0 1281 | 1282 | '@types/react-dom@19.0.3(@types/react@19.0.7)': 1283 | dependencies: 1284 | '@types/react': 19.0.7 1285 | 1286 | '@types/react@19.0.7': 1287 | dependencies: 1288 | csstype: 3.1.3 1289 | 1290 | abbrev@1.1.1: {} 1291 | 1292 | agent-base@6.0.2: 1293 | dependencies: 1294 | debug: 4.4.0 1295 | transitivePeerDependencies: 1296 | - supports-color 1297 | 1298 | ansi-regex@5.0.1: {} 1299 | 1300 | ansi-regex@6.1.0: {} 1301 | 1302 | ansi-styles@4.3.0: 1303 | dependencies: 1304 | color-convert: 2.0.1 1305 | 1306 | ansi-styles@6.2.1: {} 1307 | 1308 | any-promise@1.3.0: {} 1309 | 1310 | anymatch@3.1.3: 1311 | dependencies: 1312 | normalize-path: 3.0.0 1313 | picomatch: 2.3.1 1314 | 1315 | aproba@2.0.0: {} 1316 | 1317 | are-we-there-yet@2.0.0: 1318 | dependencies: 1319 | delegates: 1.0.0 1320 | readable-stream: 3.6.2 1321 | 1322 | arg@5.0.2: {} 1323 | 1324 | autoprefixer@10.4.20(postcss@8.5.1): 1325 | dependencies: 1326 | browserslist: 4.24.4 1327 | caniuse-lite: 1.0.30001695 1328 | fraction.js: 4.3.7 1329 | normalize-range: 0.1.2 1330 | picocolors: 1.1.1 1331 | postcss: 8.5.1 1332 | postcss-value-parser: 4.2.0 1333 | 1334 | balanced-match@1.0.2: {} 1335 | 1336 | bcrypt@5.1.1: 1337 | dependencies: 1338 | '@mapbox/node-pre-gyp': 1.0.11 1339 | node-addon-api: 5.1.0 1340 | transitivePeerDependencies: 1341 | - encoding 1342 | - supports-color 1343 | 1344 | binary-extensions@2.3.0: {} 1345 | 1346 | brace-expansion@1.1.11: 1347 | dependencies: 1348 | balanced-match: 1.0.2 1349 | concat-map: 0.0.1 1350 | 1351 | brace-expansion@2.0.1: 1352 | dependencies: 1353 | balanced-match: 1.0.2 1354 | 1355 | braces@3.0.3: 1356 | dependencies: 1357 | fill-range: 7.1.1 1358 | 1359 | browserslist@4.24.4: 1360 | dependencies: 1361 | caniuse-lite: 1.0.30001695 1362 | electron-to-chromium: 1.5.83 1363 | node-releases: 2.0.19 1364 | update-browserslist-db: 1.1.2(browserslist@4.24.4) 1365 | 1366 | busboy@1.6.0: 1367 | dependencies: 1368 | streamsearch: 1.1.0 1369 | 1370 | camelcase-css@2.0.1: {} 1371 | 1372 | caniuse-lite@1.0.30001695: {} 1373 | 1374 | chokidar@3.6.0: 1375 | dependencies: 1376 | anymatch: 3.1.3 1377 | braces: 3.0.3 1378 | glob-parent: 5.1.2 1379 | is-binary-path: 2.1.0 1380 | is-glob: 4.0.3 1381 | normalize-path: 3.0.0 1382 | readdirp: 3.6.0 1383 | optionalDependencies: 1384 | fsevents: 2.3.3 1385 | 1386 | chownr@2.0.0: {} 1387 | 1388 | client-only@0.0.1: {} 1389 | 1390 | clsx@2.1.1: {} 1391 | 1392 | color-convert@2.0.1: 1393 | dependencies: 1394 | color-name: 1.1.4 1395 | 1396 | color-name@1.1.4: {} 1397 | 1398 | color-string@1.9.1: 1399 | dependencies: 1400 | color-name: 1.1.4 1401 | simple-swizzle: 0.2.2 1402 | optional: true 1403 | 1404 | color-support@1.1.3: {} 1405 | 1406 | color@4.2.3: 1407 | dependencies: 1408 | color-convert: 2.0.1 1409 | color-string: 1.9.1 1410 | optional: true 1411 | 1412 | commander@4.1.1: {} 1413 | 1414 | concat-map@0.0.1: {} 1415 | 1416 | console-control-strings@1.1.0: {} 1417 | 1418 | cookie@0.7.1: {} 1419 | 1420 | cross-spawn@7.0.6: 1421 | dependencies: 1422 | path-key: 3.1.1 1423 | shebang-command: 2.0.0 1424 | which: 2.0.2 1425 | 1426 | cssesc@3.0.0: {} 1427 | 1428 | csstype@3.1.3: {} 1429 | 1430 | debug@4.4.0: 1431 | dependencies: 1432 | ms: 2.1.3 1433 | 1434 | delegates@1.0.0: {} 1435 | 1436 | detect-libc@2.0.3: {} 1437 | 1438 | didyoumean@1.2.2: {} 1439 | 1440 | dlv@1.1.3: {} 1441 | 1442 | eastasianwidth@0.2.0: {} 1443 | 1444 | electron-to-chromium@1.5.83: {} 1445 | 1446 | emoji-regex@8.0.0: {} 1447 | 1448 | emoji-regex@9.2.2: {} 1449 | 1450 | escalade@3.2.0: {} 1451 | 1452 | fast-glob@3.3.3: 1453 | dependencies: 1454 | '@nodelib/fs.stat': 2.0.5 1455 | '@nodelib/fs.walk': 1.2.8 1456 | glob-parent: 5.1.2 1457 | merge2: 1.4.1 1458 | micromatch: 4.0.8 1459 | 1460 | fastq@1.18.0: 1461 | dependencies: 1462 | reusify: 1.0.4 1463 | 1464 | fill-range@7.1.1: 1465 | dependencies: 1466 | to-regex-range: 5.0.1 1467 | 1468 | foreground-child@3.3.0: 1469 | dependencies: 1470 | cross-spawn: 7.0.6 1471 | signal-exit: 4.1.0 1472 | 1473 | fraction.js@4.3.7: {} 1474 | 1475 | fs-minipass@2.1.0: 1476 | dependencies: 1477 | minipass: 3.3.6 1478 | 1479 | fs.realpath@1.0.0: {} 1480 | 1481 | fsevents@2.3.3: 1482 | optional: true 1483 | 1484 | function-bind@1.1.2: {} 1485 | 1486 | gauge@3.0.2: 1487 | dependencies: 1488 | aproba: 2.0.0 1489 | color-support: 1.1.3 1490 | console-control-strings: 1.1.0 1491 | has-unicode: 2.0.1 1492 | object-assign: 4.1.1 1493 | signal-exit: 3.0.7 1494 | string-width: 4.2.3 1495 | strip-ansi: 6.0.1 1496 | wide-align: 1.1.5 1497 | 1498 | glob-parent@5.1.2: 1499 | dependencies: 1500 | is-glob: 4.0.3 1501 | 1502 | glob-parent@6.0.2: 1503 | dependencies: 1504 | is-glob: 4.0.3 1505 | 1506 | glob@10.4.5: 1507 | dependencies: 1508 | foreground-child: 3.3.0 1509 | jackspeak: 3.4.3 1510 | minimatch: 9.0.5 1511 | minipass: 7.1.2 1512 | package-json-from-dist: 1.0.1 1513 | path-scurry: 1.11.1 1514 | 1515 | glob@7.2.3: 1516 | dependencies: 1517 | fs.realpath: 1.0.0 1518 | inflight: 1.0.6 1519 | inherits: 2.0.4 1520 | minimatch: 3.1.2 1521 | once: 1.4.0 1522 | path-is-absolute: 1.0.1 1523 | 1524 | has-unicode@2.0.1: {} 1525 | 1526 | hasown@2.0.2: 1527 | dependencies: 1528 | function-bind: 1.1.2 1529 | 1530 | https-proxy-agent@5.0.1: 1531 | dependencies: 1532 | agent-base: 6.0.2 1533 | debug: 4.4.0 1534 | transitivePeerDependencies: 1535 | - supports-color 1536 | 1537 | inflight@1.0.6: 1538 | dependencies: 1539 | once: 1.4.0 1540 | wrappy: 1.0.2 1541 | 1542 | inherits@2.0.4: {} 1543 | 1544 | is-arrayish@0.3.2: 1545 | optional: true 1546 | 1547 | is-binary-path@2.1.0: 1548 | dependencies: 1549 | binary-extensions: 2.3.0 1550 | 1551 | is-core-module@2.16.1: 1552 | dependencies: 1553 | hasown: 2.0.2 1554 | 1555 | is-extglob@2.1.1: {} 1556 | 1557 | is-fullwidth-code-point@3.0.0: {} 1558 | 1559 | is-glob@4.0.3: 1560 | dependencies: 1561 | is-extglob: 2.1.1 1562 | 1563 | is-number@7.0.0: {} 1564 | 1565 | isexe@2.0.0: {} 1566 | 1567 | jackspeak@3.4.3: 1568 | dependencies: 1569 | '@isaacs/cliui': 8.0.2 1570 | optionalDependencies: 1571 | '@pkgjs/parseargs': 0.11.0 1572 | 1573 | jiti@1.21.7: {} 1574 | 1575 | jose@5.9.6: {} 1576 | 1577 | lilconfig@3.1.3: {} 1578 | 1579 | lines-and-columns@1.2.4: {} 1580 | 1581 | lru-cache@10.4.3: {} 1582 | 1583 | make-dir@3.1.0: 1584 | dependencies: 1585 | semver: 6.3.1 1586 | 1587 | merge2@1.4.1: {} 1588 | 1589 | micromatch@4.0.8: 1590 | dependencies: 1591 | braces: 3.0.3 1592 | picomatch: 2.3.1 1593 | 1594 | mini-svg-data-uri@1.4.4: {} 1595 | 1596 | minimatch@3.1.2: 1597 | dependencies: 1598 | brace-expansion: 1.1.11 1599 | 1600 | minimatch@9.0.5: 1601 | dependencies: 1602 | brace-expansion: 2.0.1 1603 | 1604 | minipass@3.3.6: 1605 | dependencies: 1606 | yallist: 4.0.0 1607 | 1608 | minipass@5.0.0: {} 1609 | 1610 | minipass@7.1.2: {} 1611 | 1612 | minizlib@2.1.2: 1613 | dependencies: 1614 | minipass: 3.3.6 1615 | yallist: 4.0.0 1616 | 1617 | mkdirp@1.0.4: {} 1618 | 1619 | ms@2.1.3: {} 1620 | 1621 | mz@2.7.0: 1622 | dependencies: 1623 | any-promise: 1.3.0 1624 | object-assign: 4.1.1 1625 | thenify-all: 1.6.0 1626 | 1627 | nanoid@3.3.8: {} 1628 | 1629 | next-auth@5.0.0-beta.25(next@15.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0): 1630 | dependencies: 1631 | '@auth/core': 0.37.2 1632 | next: 15.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0) 1633 | react: 19.0.0 1634 | 1635 | next@15.1.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0): 1636 | dependencies: 1637 | '@next/env': 15.1.6 1638 | '@swc/counter': 0.1.3 1639 | '@swc/helpers': 0.5.15 1640 | busboy: 1.6.0 1641 | caniuse-lite: 1.0.30001695 1642 | postcss: 8.4.31 1643 | react: 19.0.0 1644 | react-dom: 19.0.0(react@19.0.0) 1645 | styled-jsx: 5.1.6(react@19.0.0) 1646 | optionalDependencies: 1647 | '@next/swc-darwin-arm64': 15.1.6 1648 | '@next/swc-darwin-x64': 15.1.6 1649 | '@next/swc-linux-arm64-gnu': 15.1.6 1650 | '@next/swc-linux-arm64-musl': 15.1.6 1651 | '@next/swc-linux-x64-gnu': 15.1.6 1652 | '@next/swc-linux-x64-musl': 15.1.6 1653 | '@next/swc-win32-arm64-msvc': 15.1.6 1654 | '@next/swc-win32-x64-msvc': 15.1.6 1655 | sharp: 0.33.5 1656 | transitivePeerDependencies: 1657 | - '@babel/core' 1658 | - babel-plugin-macros 1659 | 1660 | node-addon-api@5.1.0: {} 1661 | 1662 | node-fetch@2.7.0: 1663 | dependencies: 1664 | whatwg-url: 5.0.0 1665 | 1666 | node-releases@2.0.19: {} 1667 | 1668 | nopt@5.0.0: 1669 | dependencies: 1670 | abbrev: 1.1.1 1671 | 1672 | normalize-path@3.0.0: {} 1673 | 1674 | normalize-range@0.1.2: {} 1675 | 1676 | npmlog@5.0.1: 1677 | dependencies: 1678 | are-we-there-yet: 2.0.0 1679 | console-control-strings: 1.1.0 1680 | gauge: 3.0.2 1681 | set-blocking: 2.0.0 1682 | 1683 | oauth4webapi@3.1.4: {} 1684 | 1685 | object-assign@4.1.1: {} 1686 | 1687 | object-hash@3.0.0: {} 1688 | 1689 | once@1.4.0: 1690 | dependencies: 1691 | wrappy: 1.0.2 1692 | 1693 | package-json-from-dist@1.0.1: {} 1694 | 1695 | path-is-absolute@1.0.1: {} 1696 | 1697 | path-key@3.1.1: {} 1698 | 1699 | path-parse@1.0.7: {} 1700 | 1701 | path-scurry@1.11.1: 1702 | dependencies: 1703 | lru-cache: 10.4.3 1704 | minipass: 7.1.2 1705 | 1706 | picocolors@1.1.1: {} 1707 | 1708 | picomatch@2.3.1: {} 1709 | 1710 | pify@2.3.0: {} 1711 | 1712 | pirates@4.0.6: {} 1713 | 1714 | postcss-import@15.1.0(postcss@8.5.1): 1715 | dependencies: 1716 | postcss: 8.5.1 1717 | postcss-value-parser: 4.2.0 1718 | read-cache: 1.0.0 1719 | resolve: 1.22.10 1720 | 1721 | postcss-js@4.0.1(postcss@8.5.1): 1722 | dependencies: 1723 | camelcase-css: 2.0.1 1724 | postcss: 8.5.1 1725 | 1726 | postcss-load-config@4.0.2(postcss@8.5.1): 1727 | dependencies: 1728 | lilconfig: 3.1.3 1729 | yaml: 2.7.0 1730 | optionalDependencies: 1731 | postcss: 8.5.1 1732 | 1733 | postcss-nested@6.2.0(postcss@8.5.1): 1734 | dependencies: 1735 | postcss: 8.5.1 1736 | postcss-selector-parser: 6.1.2 1737 | 1738 | postcss-selector-parser@6.1.2: 1739 | dependencies: 1740 | cssesc: 3.0.0 1741 | util-deprecate: 1.0.2 1742 | 1743 | postcss-value-parser@4.2.0: {} 1744 | 1745 | postcss@8.4.31: 1746 | dependencies: 1747 | nanoid: 3.3.8 1748 | picocolors: 1.1.1 1749 | source-map-js: 1.2.1 1750 | 1751 | postcss@8.5.1: 1752 | dependencies: 1753 | nanoid: 3.3.8 1754 | picocolors: 1.1.1 1755 | source-map-js: 1.2.1 1756 | 1757 | postgres@3.4.5: {} 1758 | 1759 | preact-render-to-string@5.2.3(preact@10.11.3): 1760 | dependencies: 1761 | preact: 10.11.3 1762 | pretty-format: 3.8.0 1763 | 1764 | preact@10.11.3: {} 1765 | 1766 | pretty-format@3.8.0: {} 1767 | 1768 | queue-microtask@1.2.3: {} 1769 | 1770 | react-dom@19.0.0(react@19.0.0): 1771 | dependencies: 1772 | react: 19.0.0 1773 | scheduler: 0.25.0 1774 | 1775 | react@19.0.0: {} 1776 | 1777 | read-cache@1.0.0: 1778 | dependencies: 1779 | pify: 2.3.0 1780 | 1781 | readable-stream@3.6.2: 1782 | dependencies: 1783 | inherits: 2.0.4 1784 | string_decoder: 1.3.0 1785 | util-deprecate: 1.0.2 1786 | 1787 | readdirp@3.6.0: 1788 | dependencies: 1789 | picomatch: 2.3.1 1790 | 1791 | resolve@1.22.10: 1792 | dependencies: 1793 | is-core-module: 2.16.1 1794 | path-parse: 1.0.7 1795 | supports-preserve-symlinks-flag: 1.0.0 1796 | 1797 | reusify@1.0.4: {} 1798 | 1799 | rimraf@3.0.2: 1800 | dependencies: 1801 | glob: 7.2.3 1802 | 1803 | run-parallel@1.2.0: 1804 | dependencies: 1805 | queue-microtask: 1.2.3 1806 | 1807 | safe-buffer@5.2.1: {} 1808 | 1809 | scheduler@0.25.0: {} 1810 | 1811 | semver@6.3.1: {} 1812 | 1813 | semver@7.6.3: {} 1814 | 1815 | set-blocking@2.0.0: {} 1816 | 1817 | sharp@0.33.5: 1818 | dependencies: 1819 | color: 4.2.3 1820 | detect-libc: 2.0.3 1821 | semver: 7.6.3 1822 | optionalDependencies: 1823 | '@img/sharp-darwin-arm64': 0.33.5 1824 | '@img/sharp-darwin-x64': 0.33.5 1825 | '@img/sharp-libvips-darwin-arm64': 1.0.4 1826 | '@img/sharp-libvips-darwin-x64': 1.0.4 1827 | '@img/sharp-libvips-linux-arm': 1.0.5 1828 | '@img/sharp-libvips-linux-arm64': 1.0.4 1829 | '@img/sharp-libvips-linux-s390x': 1.0.4 1830 | '@img/sharp-libvips-linux-x64': 1.0.4 1831 | '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 1832 | '@img/sharp-libvips-linuxmusl-x64': 1.0.4 1833 | '@img/sharp-linux-arm': 0.33.5 1834 | '@img/sharp-linux-arm64': 0.33.5 1835 | '@img/sharp-linux-s390x': 0.33.5 1836 | '@img/sharp-linux-x64': 0.33.5 1837 | '@img/sharp-linuxmusl-arm64': 0.33.5 1838 | '@img/sharp-linuxmusl-x64': 0.33.5 1839 | '@img/sharp-wasm32': 0.33.5 1840 | '@img/sharp-win32-ia32': 0.33.5 1841 | '@img/sharp-win32-x64': 0.33.5 1842 | optional: true 1843 | 1844 | shebang-command@2.0.0: 1845 | dependencies: 1846 | shebang-regex: 3.0.0 1847 | 1848 | shebang-regex@3.0.0: {} 1849 | 1850 | signal-exit@3.0.7: {} 1851 | 1852 | signal-exit@4.1.0: {} 1853 | 1854 | simple-swizzle@0.2.2: 1855 | dependencies: 1856 | is-arrayish: 0.3.2 1857 | optional: true 1858 | 1859 | source-map-js@1.2.1: {} 1860 | 1861 | streamsearch@1.1.0: {} 1862 | 1863 | string-width@4.2.3: 1864 | dependencies: 1865 | emoji-regex: 8.0.0 1866 | is-fullwidth-code-point: 3.0.0 1867 | strip-ansi: 6.0.1 1868 | 1869 | string-width@5.1.2: 1870 | dependencies: 1871 | eastasianwidth: 0.2.0 1872 | emoji-regex: 9.2.2 1873 | strip-ansi: 7.1.0 1874 | 1875 | string_decoder@1.3.0: 1876 | dependencies: 1877 | safe-buffer: 5.2.1 1878 | 1879 | strip-ansi@6.0.1: 1880 | dependencies: 1881 | ansi-regex: 5.0.1 1882 | 1883 | strip-ansi@7.1.0: 1884 | dependencies: 1885 | ansi-regex: 6.1.0 1886 | 1887 | styled-jsx@5.1.6(react@19.0.0): 1888 | dependencies: 1889 | client-only: 0.0.1 1890 | react: 19.0.0 1891 | 1892 | sucrase@3.35.0: 1893 | dependencies: 1894 | '@jridgewell/gen-mapping': 0.3.8 1895 | commander: 4.1.1 1896 | glob: 10.4.5 1897 | lines-and-columns: 1.2.4 1898 | mz: 2.7.0 1899 | pirates: 4.0.6 1900 | ts-interface-checker: 0.1.13 1901 | 1902 | supports-preserve-symlinks-flag@1.0.0: {} 1903 | 1904 | tailwindcss@3.4.17: 1905 | dependencies: 1906 | '@alloc/quick-lru': 5.2.0 1907 | arg: 5.0.2 1908 | chokidar: 3.6.0 1909 | didyoumean: 1.2.2 1910 | dlv: 1.1.3 1911 | fast-glob: 3.3.3 1912 | glob-parent: 6.0.2 1913 | is-glob: 4.0.3 1914 | jiti: 1.21.7 1915 | lilconfig: 3.1.3 1916 | micromatch: 4.0.8 1917 | normalize-path: 3.0.0 1918 | object-hash: 3.0.0 1919 | picocolors: 1.1.1 1920 | postcss: 8.5.1 1921 | postcss-import: 15.1.0(postcss@8.5.1) 1922 | postcss-js: 4.0.1(postcss@8.5.1) 1923 | postcss-load-config: 4.0.2(postcss@8.5.1) 1924 | postcss-nested: 6.2.0(postcss@8.5.1) 1925 | postcss-selector-parser: 6.1.2 1926 | resolve: 1.22.10 1927 | sucrase: 3.35.0 1928 | transitivePeerDependencies: 1929 | - ts-node 1930 | 1931 | tar@6.2.1: 1932 | dependencies: 1933 | chownr: 2.0.0 1934 | fs-minipass: 2.1.0 1935 | minipass: 5.0.0 1936 | minizlib: 2.1.2 1937 | mkdirp: 1.0.4 1938 | yallist: 4.0.0 1939 | 1940 | thenify-all@1.6.0: 1941 | dependencies: 1942 | thenify: 3.3.1 1943 | 1944 | thenify@3.3.1: 1945 | dependencies: 1946 | any-promise: 1.3.0 1947 | 1948 | to-regex-range@5.0.1: 1949 | dependencies: 1950 | is-number: 7.0.0 1951 | 1952 | tr46@0.0.3: {} 1953 | 1954 | ts-interface-checker@0.1.13: {} 1955 | 1956 | tslib@2.8.1: {} 1957 | 1958 | typescript@5.7.3: {} 1959 | 1960 | undici-types@6.20.0: {} 1961 | 1962 | update-browserslist-db@1.1.2(browserslist@4.24.4): 1963 | dependencies: 1964 | browserslist: 4.24.4 1965 | escalade: 3.2.0 1966 | picocolors: 1.1.1 1967 | 1968 | use-debounce@10.0.4(react@19.0.0): 1969 | dependencies: 1970 | react: 19.0.0 1971 | 1972 | util-deprecate@1.0.2: {} 1973 | 1974 | webidl-conversions@3.0.1: {} 1975 | 1976 | whatwg-url@5.0.0: 1977 | dependencies: 1978 | tr46: 0.0.3 1979 | webidl-conversions: 3.0.1 1980 | 1981 | which@2.0.2: 1982 | dependencies: 1983 | isexe: 2.0.0 1984 | 1985 | wide-align@1.1.5: 1986 | dependencies: 1987 | string-width: 4.2.3 1988 | 1989 | wrap-ansi@7.0.0: 1990 | dependencies: 1991 | ansi-styles: 4.3.0 1992 | string-width: 4.2.3 1993 | strip-ansi: 6.0.1 1994 | 1995 | wrap-ansi@8.1.0: 1996 | dependencies: 1997 | ansi-styles: 6.2.1 1998 | string-width: 5.1.2 1999 | strip-ansi: 7.1.0 2000 | 2001 | wrappy@1.0.2: {} 2002 | 2003 | yallist@4.0.0: {} 2004 | 2005 | yaml@2.7.0: {} 2006 | 2007 | zod@3.24.1: {} 2008 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/customers/amy-burns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dmytrozaiets81/nextjs-learn/f371f123ce3bc566ec6bde86c03334a93dfc7242/public/customers/amy-burns.png -------------------------------------------------------------------------------- /public/customers/balazs-orban.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dmytrozaiets81/nextjs-learn/f371f123ce3bc566ec6bde86c03334a93dfc7242/public/customers/balazs-orban.png -------------------------------------------------------------------------------- /public/customers/delba-de-oliveira.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dmytrozaiets81/nextjs-learn/f371f123ce3bc566ec6bde86c03334a93dfc7242/public/customers/delba-de-oliveira.png -------------------------------------------------------------------------------- /public/customers/evil-rabbit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dmytrozaiets81/nextjs-learn/f371f123ce3bc566ec6bde86c03334a93dfc7242/public/customers/evil-rabbit.png -------------------------------------------------------------------------------- /public/customers/lee-robinson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dmytrozaiets81/nextjs-learn/f371f123ce3bc566ec6bde86c03334a93dfc7242/public/customers/lee-robinson.png -------------------------------------------------------------------------------- /public/customers/michael-novotny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dmytrozaiets81/nextjs-learn/f371f123ce3bc566ec6bde86c03334a93dfc7242/public/customers/michael-novotny.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dmytrozaiets81/nextjs-learn/f371f123ce3bc566ec6bde86c03334a93dfc7242/public/favicon.ico -------------------------------------------------------------------------------- /public/hero-desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dmytrozaiets81/nextjs-learn/f371f123ce3bc566ec6bde86c03334a93dfc7242/public/hero-desktop.png -------------------------------------------------------------------------------- /public/hero-mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dmytrozaiets81/nextjs-learn/f371f123ce3bc566ec6bde86c03334a93dfc7242/public/hero-mobile.png -------------------------------------------------------------------------------- /public/opengraph-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dmytrozaiets81/nextjs-learn/f371f123ce3bc566ec6bde86c03334a93dfc7242/public/opengraph-image.png -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss'; 2 | 3 | const config: Config = { 4 | content: [ 5 | './pages/**/*.{js,ts,jsx,tsx,mdx}', 6 | './components/**/*.{js,ts,jsx,tsx,mdx}', 7 | './app/**/*.{js,ts,jsx,tsx,mdx}', 8 | ], 9 | theme: { 10 | extend: { 11 | gridTemplateColumns: { 12 | '13': 'repeat(13, minmax(0, 1fr))', 13 | }, 14 | colors: { 15 | blue: { 16 | 400: '#2589FE', 17 | 500: '#0070F3', 18 | 600: '#2F6FEB', 19 | }, 20 | }, 21 | }, 22 | keyframes: { 23 | shimmer: { 24 | '100%': { 25 | transform: 'translateX(100%)', 26 | }, 27 | }, 28 | }, 29 | }, 30 | plugins: [require('@tailwindcss/forms')], 31 | }; 32 | export default config; 33 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "baseUrl": ".", 22 | "paths": { 23 | "@/*": ["./*"] 24 | } 25 | }, 26 | "include": [ 27 | "next-env.d.ts", 28 | "**/*.ts", 29 | "**/*.tsx", 30 | ".next/types/**/*.ts", 31 | "app/lib/placeholder-data.ts", 32 | "scripts/seed.js" 33 | ], 34 | "exclude": ["node_modules"] 35 | } 36 | --------------------------------------------------------------------------------