diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index bdf273e..522514a 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -9,6 +9,7 @@ concurrency: cancel-in-progress: true jobs: test: + if: false timeout-minutes: 60 runs-on: ubuntu-latest container: diff --git a/.nvmrc b/.nvmrc index 3bc99c2..19e7d57 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -23.2.0 \ No newline at end of file +23.3.0 \ No newline at end of file diff --git a/app/dashboard/expenses/page.tsx b/app/[slug]/dashboard/expenses/page.tsx similarity index 100% rename from app/dashboard/expenses/page.tsx rename to app/[slug]/dashboard/expenses/page.tsx diff --git a/app/dashboard/guests/page.tsx b/app/[slug]/dashboard/guests/page.tsx similarity index 96% rename from app/dashboard/guests/page.tsx rename to app/[slug]/dashboard/guests/page.tsx index 85e249a..09db838 100644 --- a/app/dashboard/guests/page.tsx +++ b/app/[slug]/dashboard/guests/page.tsx @@ -8,7 +8,6 @@ import { Group, Guest } from '@/app/lib/definitions'; import { classNames } from '@/app/ui/components/button'; import GuestFormDialog from '@/app/ui/components/guest-form-dialog'; import GroupsTable from '@/app/ui/groups/table'; -import AffinityGroupsTree from '@/app/ui/guests/affinity-groups-tree'; import SkeletonTable from '@/app/ui/guests/skeleton-row'; import GuestsTable from '@/app/ui/guests/table'; import { TabPanel, TabView } from 'primereact/tabview'; @@ -42,8 +41,6 @@ export default function Page() { return (
- -
diff --git a/app/dashboard/layout.tsx b/app/[slug]/dashboard/layout.tsx similarity index 100% rename from app/dashboard/layout.tsx rename to app/[slug]/dashboard/layout.tsx diff --git a/app/dashboard/page.tsx b/app/[slug]/dashboard/page.tsx similarity index 100% rename from app/dashboard/page.tsx rename to app/[slug]/dashboard/page.tsx diff --git a/app/dashboard/tables/page.tsx b/app/[slug]/dashboard/tables/page.tsx similarity index 100% rename from app/dashboard/tables/page.tsx rename to app/[slug]/dashboard/tables/page.tsx diff --git a/app/[slug]/page.tsx b/app/[slug]/page.tsx new file mode 100644 index 0000000..34e8adf --- /dev/null +++ b/app/[slug]/page.tsx @@ -0,0 +1,30 @@ +/* Copyright (C) 2024 Manuel Bustillo*/ + +'use client'; +import Link from 'next/link'; +import styles from '@/app/ui/home.module.css'; +import LoginForm from '@/app/ui/components/login-form'; + + + +export default async function Page({ params }: { params: Promise<{ slug: string }> }) { + + localStorage.setItem('slug', (await params).slug) + return ( +
+
+
+ + Already have an account? Sign in + + +
+ +
+ Don't have an account? Register now! +
+
+ +
+ ); +} diff --git a/app/api/authentication.tsx b/app/api/authentication.tsx new file mode 100644 index 0000000..6b57d06 --- /dev/null +++ b/app/api/authentication.tsx @@ -0,0 +1,36 @@ +/* Copyright (C) 2024 Manuel Bustillo*/ + +import { getCsrfToken, getSlug } from '@/app/lib/utils'; +import { User } from '@/app/lib/definitions'; + +export function login({ email, password, onLogin }: { email: string, password: string, onLogin: (user: User) => void }) { + console.log(email, password); + return fetch(`/api/${getSlug()}/users/sign_in`, { + method: 'POST', + body: JSON.stringify({ user: { email, password } }), + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': getCsrfToken(), + } + }) + .then((response) => response.json()) + .then((data: any) => { + console.log(data); + onLogin({ + id: data.id || '', + email: data.email || '', + }); + }) + .catch((error) => console.error(error)); +} + +export function logout({ onLogout }: { onLogout: () => void }) { + fetch(`/api/${getSlug()}/users/sign_out`, { + method: 'DELETE', + headers: { + 'X-CSRF-TOKEN': getCsrfToken(), + } + }).then(onLogout) + .catch((error) => console.error(error)); +} diff --git a/app/api/expenses.tsx b/app/api/expenses.tsx index f73f6b7..a23cd16 100644 --- a/app/api/expenses.tsx +++ b/app/api/expenses.tsx @@ -1,10 +1,10 @@ /* Copyright (C) 2024 Manuel Bustillo*/ import { Expense } from '@/app/lib/definitions'; -import { getCsrfToken } from '@/app/lib/utils'; +import { getCsrfToken, getSlug } from '@/app/lib/utils'; export function loadExpenses(onLoad?: (expenses: Expense[]) => void) { - fetch("/api/expenses") + fetch(`/api/${getSlug()}/expenses`) .then((response) => response.json()) .then((data) => { onLoad && onLoad(data.map((record: any) => { @@ -21,7 +21,7 @@ export function loadExpenses(onLoad?: (expenses: Expense[]) => void) { } export function updateExpense(expense: Expense) { - fetch(`/api/expenses/${expense.id}`, + fetch(`/api/${getSlug()}/expenses/${expense.id}`, { method: 'PUT', body: JSON.stringify({ diff --git a/app/api/groups.tsx b/app/api/groups.tsx index aa0e4db..807c19f 100644 --- a/app/api/groups.tsx +++ b/app/api/groups.tsx @@ -1,9 +1,10 @@ /* Copyright (C) 2024 Manuel Bustillo*/ import { Group } from '@/app/lib/definitions'; +import { getSlug } from '../lib/utils'; export function loadGroups(onLoad?: (groups: Group[]) => void) { - fetch("/api/groups") + fetch(`/api/${getSlug()}/groups`) .then((response) => response.json()) .then((data) => { onLoad && onLoad(data.map((record: any) => { diff --git a/app/api/guests.tsx b/app/api/guests.tsx index ffd7cf6..ce4f232 100644 --- a/app/api/guests.tsx +++ b/app/api/guests.tsx @@ -1,10 +1,10 @@ /* Copyright (C) 2024 Manuel Bustillo*/ import { Guest } from '@/app/lib/definitions'; -import { getCsrfToken } from '@/app/lib/utils'; +import { getCsrfToken, getSlug } from '@/app/lib/utils'; export function loadGuests(onLoad?: (guests: Guest[]) => void) { - fetch("/api/guests") + fetch(`/api/${getSlug()}/guests`) .then((response) => response.json()) .then((data) => { onLoad && onLoad(data.map((record: any) => { @@ -22,7 +22,7 @@ export function loadGuests(onLoad?: (guests: Guest[]) => void) { }; export function updateGuest(guest: Guest) { - return fetch(`/api/guests/${guest.id}`, + return fetch(`/api/${getSlug()}/guests/${guest.id}`, { method: 'PUT', body: JSON.stringify({ guest: { name: guest.name, status: guest.status, group_id: guest.groupId } }), @@ -35,7 +35,7 @@ export function updateGuest(guest: Guest) { } export function createGuest(guest: Guest, onCreate?: () => void) { - fetch("/api/guests", { + fetch(`/api/${getSlug()}/guests`, { method: 'POST', body: JSON.stringify({ name: guest.name, group_id: guest.groupId, status: guest.status }), headers: { @@ -51,7 +51,7 @@ export function createGuest(guest: Guest, onCreate?: () => void) { } export function destroyGuest(guest: Guest, onDestroy?: () => void) { - fetch(`/api/guests/${guest.id}`, { + fetch(`/api/${getSlug()}/guests/${guest.id}`, { method: 'DELETE', headers: { 'X-CSRF-TOKEN': getCsrfToken(), diff --git a/app/api/tableSimulations.tsx b/app/api/tableSimulations.tsx index 4e28688..5f6d2d9 100644 --- a/app/api/tableSimulations.tsx +++ b/app/api/tableSimulations.tsx @@ -1,9 +1,10 @@ /* Copyright (C) 2024 Manuel Bustillo*/ import { TableArrangement } from '@/app/lib/definitions'; +import { getSlug } from '../lib/utils'; export function loadTableSimulations(onLoad?: (tableSimulations: TableArrangement[]) => void) { - fetch('/api/tables_arrangements') + fetch(`/api/${getSlug()}/tables_arrangements`) .then((response) => response.json()) .then((data) => { onLoad && onLoad(data.map((record: any) => { diff --git a/app/lib/definitions.ts b/app/lib/definitions.ts index c897c4b..45b5536 100644 --- a/app/lib/definitions.ts +++ b/app/lib/definitions.ts @@ -4,19 +4,6 @@ // It describes the shape of the data, and what data type each property should accept. // For simplicity of teaching, we're manually defining these types. // However, these types are generated automatically if you're using an ORM such as Prisma. -export type User = { - id: string; - name: string; - email: string; - password: string; -}; - -export type Customer = { - id: string; - name: string; - email: string; - image_url: string; -}; export const guestStatuses = ['considered', 'invited', 'confirmed', 'declined', 'tentative'] as const; export type GuestStatus = typeof guestStatuses[number]; @@ -63,34 +50,6 @@ export type AttendanceSummary = { total: number; } -export type Invoice = { - id: string; - customer_id: string; - amount: number; - date: string; - // In TypeScript, this is called a string union type. - // It means that the "status" property can only be one of the two strings: 'pending' or 'paid'. - status: 'pending' | 'paid'; -}; - -export type Revenue = { - month: string; - revenue: number; -}; - -export type LatestInvoice = { - id: string; - name: string; - image_url: string; - email: string; - amount: string; -}; - -// The database returns a number for amount, but we later format it to a string with the formatCurrency function -export type LatestInvoiceRaw = Omit & { - amount: number; -}; - export type guestsTable = { id: string; customer_id: string; @@ -102,34 +61,7 @@ export type guestsTable = { status: 'pending' | 'paid'; }; -export type CustomersTableType = { +export type User = { id: string; - name: string; email: string; - image_url: string; - total_guests: number; - total_pending: number; - total_paid: number; -}; - -export type FormattedCustomersTable = { - id: string; - name: string; - email: string; - image_url: string; - total_guests: number; - total_pending: string; - total_paid: string; -}; - -export type CustomerField = { - id: string; - name: string; -}; - -export type InvoiceForm = { - id: string; - customer_id: string; - amount: number; - status: 'pending' | 'paid'; -}; +} \ No newline at end of file diff --git a/app/lib/placeholder-data.ts b/app/lib/placeholder-data.ts deleted file mode 100644 index 2d78faa..0000000 --- a/app/lib/placeholder-data.ts +++ /dev/null @@ -1,149 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -// This file contains placeholder data that you'll be replacing with real data in the Data Fetching chapter: -// https://nextjs.org/learn/dashboard-app/fetching-data -const users = [ - { - id: '410544b2-4001-4271-9855-fec4b6a6442a', - name: 'User', - email: 'user@nextmail.com', - password: '123456', - }, -]; - -const customers = [ - { - id: 'd6e15727-9fe1-4961-8c5b-ea44a9bd81aa', - name: 'Evil Rabbit', - email: 'evil@rabbit.com', - image_url: '/customers/evil-rabbit.png', - }, - { - id: '3958dc9e-712f-4377-85e9-fec4b6a6442a', - name: 'Delba de Oliveira', - email: 'delba@oliveira.com', - image_url: '/customers/delba-de-oliveira.png', - }, - { - id: '3958dc9e-742f-4377-85e9-fec4b6a6442a', - name: 'Lee Robinson', - email: 'lee@robinson.com', - image_url: '/customers/lee-robinson.png', - }, - { - id: '76d65c26-f784-44a2-ac19-586678f7c2f2', - name: 'Michael Novotny', - email: 'michael@novotny.com', - image_url: '/customers/michael-novotny.png', - }, - { - id: 'CC27C14A-0ACF-4F4A-A6C9-D45682C144B9', - name: 'Amy Burns', - email: 'amy@burns.com', - image_url: '/customers/amy-burns.png', - }, - { - id: '13D07535-C59E-4157-A011-F8D2EF4E0CBB', - name: 'Balazs Orban', - email: 'balazs@orban.com', - image_url: '/customers/balazs-orban.png', - }, -]; - -const guests = [ - { - customer_id: customers[0].id, - amount: 15795, - status: 'pending', - date: '2022-12-06', - }, - { - customer_id: customers[1].id, - amount: 20348, - status: 'pending', - date: '2022-11-14', - }, - { - customer_id: customers[4].id, - amount: 3040, - status: 'paid', - date: '2022-10-29', - }, - { - customer_id: customers[3].id, - amount: 44800, - status: 'paid', - date: '2023-09-10', - }, - { - customer_id: customers[5].id, - amount: 34577, - status: 'pending', - date: '2023-08-05', - }, - { - customer_id: customers[2].id, - amount: 54246, - status: 'pending', - date: '2023-07-16', - }, - { - customer_id: customers[0].id, - amount: 666, - status: 'pending', - date: '2023-06-27', - }, - { - customer_id: customers[3].id, - amount: 32545, - status: 'paid', - date: '2023-06-09', - }, - { - customer_id: customers[4].id, - amount: 1250, - status: 'paid', - date: '2023-06-17', - }, - { - customer_id: customers[5].id, - amount: 8546, - status: 'paid', - date: '2023-06-07', - }, - { - customer_id: customers[1].id, - amount: 500, - status: 'paid', - date: '2023-08-19', - }, - { - customer_id: customers[5].id, - amount: 8945, - status: 'paid', - date: '2023-06-03', - }, - { - customer_id: customers[2].id, - amount: 1000, - status: 'paid', - date: '2022-06-05', - }, -]; - -const revenue = [ - { month: 'Jan', revenue: 2000 }, - { month: 'Feb', revenue: 1800 }, - { month: 'Mar', revenue: 2200 }, - { month: 'Apr', revenue: 2500 }, - { month: 'May', revenue: 2300 }, - { month: 'Jun', revenue: 3200 }, - { month: 'Jul', revenue: 3500 }, - { month: 'Aug', revenue: 3700 }, - { month: 'Sep', revenue: 2500 }, - { month: 'Oct', revenue: 2800 }, - { month: 'Nov', revenue: 3000 }, - { month: 'Dec', revenue: 4800 }, -]; - -export { users, customers, guests, revenue }; diff --git a/app/lib/utils.ts b/app/lib/utils.ts index 2200ed3..09359e4 100644 --- a/app/lib/utils.ts +++ b/app/lib/utils.ts @@ -1,14 +1,5 @@ /* Copyright (C) 2024 Manuel Bustillo*/ -import { Revenue } from './definitions'; - -export const formatCurrency = (amount: number) => { - return (amount / 100).toLocaleString('en-US', { - style: 'currency', - currency: 'USD', - }); -}; - export const getCsrfToken = () => { return document.cookie .split("; ") @@ -16,68 +7,4 @@ export const getCsrfToken = () => { ?.split("=")[1] || 'unknown'; } -// From https://stackoverflow.com/a/1026087/3607039 -export const capitalize = (val:string) => { - return String(val).charAt(0).toUpperCase() + String(val).slice(1); -} - -export const formatDateToLocal = ( - dateStr: string, - locale: string = 'en-US', -) => { - const date = new Date(dateStr); - const options: Intl.DateTimeFormatOptions = { - day: 'numeric', - month: 'short', - year: 'numeric', - }; - const formatter = new Intl.DateTimeFormat(locale, options); - return formatter.format(date); -}; - -export const generateYAxis = (revenue: Revenue[]) => { - // Calculate what labels we need to display on the y-axis - // based on highest record and in 1000s - const yAxisLabels = []; - const highestRecord = Math.max(...revenue.map((month) => month.revenue)); - const topLabel = Math.ceil(highestRecord / 1000) * 1000; - - for (let i = topLabel; i >= 0; i -= 1000) { - yAxisLabels.push(`$${i / 1000}K`); - } - - return { yAxisLabels, topLabel }; -}; - -export const generatePagination = (currentPage: number, totalPages: number) => { - // If the total number of pages is 7 or less, - // display all pages without any ellipsis. - if (totalPages <= 7) { - return Array.from({ length: totalPages }, (_, i) => i + 1); - } - - // If the current page is among the first 3 pages, - // show the first 3, an ellipsis, and the last 2 pages. - if (currentPage <= 3) { - return [1, 2, 3, '...', totalPages - 1, totalPages]; - } - - // If the current page is among the last 3 pages, - // show the first 2, an ellipsis, and the last 3 pages. - if (currentPage >= totalPages - 2) { - return [1, 2, '...', totalPages - 2, totalPages - 1, totalPages]; - } - - // If the current page is somewhere in the middle, - // show the first page, an ellipsis, the current page and its neighbors, - // another ellipsis, and the last page. - return [ - 1, - '...', - currentPage - 1, - currentPage, - currentPage + 1, - '...', - totalPages, - ]; -}; +export const getSlug = () => localStorage.getItem('slug') || 'default'; diff --git a/app/page.tsx b/app/page.tsx deleted file mode 100644 index 95ca011..0000000 --- a/app/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -import Link from 'next/link'; -import styles from '@/app/ui/home.module.css'; - -export default function Page() { - return ( -
- -
- ); -} diff --git a/app/seed/route.ts b/app/seed/route.ts deleted file mode 100644 index db632cd..0000000 --- a/app/seed/route.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -// import bcrypt from 'bcrypt'; -// import { db } from '@vercel/postgres'; -// import { guests, customers, revenue, users } from '../lib/placeholder-data'; - -// const client = await db.connect(); - -// async function seedUsers() { -// await client.sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`; -// await client.sql` -// CREATE TABLE IF NOT EXISTS users ( -// id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, -// name VARCHAR(255) NOT NULL, -// email TEXT NOT NULL UNIQUE, -// password TEXT NOT NULL -// ); -// `; - -// const insertedUsers = await Promise.all( -// users.map(async (user) => { -// const hashedPassword = await bcrypt.hash(user.password, 10); -// return client.sql` -// INSERT INTO users (id, name, email, password) -// VALUES (${user.id}, ${user.name}, ${user.email}, ${hashedPassword}) -// ON CONFLICT (id) DO NOTHING; -// `; -// }), -// ); - -// return insertedUsers; -// } - -// async function seedguests() { -// await client.sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`; - -// await client.sql` -// CREATE TABLE IF NOT EXISTS guests ( -// id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, -// customer_id UUID NOT NULL, -// amount INT NOT NULL, -// status VARCHAR(255) NOT NULL, -// date DATE NOT NULL -// ); -// `; - -// const insertedguests = await Promise.all( -// guests.map( -// (invoice) => client.sql` -// INSERT INTO guests (customer_id, amount, status, date) -// VALUES (${invoice.customer_id}, ${invoice.amount}, ${invoice.status}, ${invoice.date}) -// ON CONFLICT (id) DO NOTHING; -// `, -// ), -// ); - -// return insertedguests; -// } - -// async function seedCustomers() { -// await client.sql`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`; - -// await client.sql` -// CREATE TABLE IF NOT EXISTS customers ( -// id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, -// name VARCHAR(255) NOT NULL, -// email VARCHAR(255) NOT NULL, -// image_url VARCHAR(255) NOT NULL -// ); -// `; - -// const insertedCustomers = await Promise.all( -// customers.map( -// (customer) => client.sql` -// INSERT INTO customers (id, name, email, image_url) -// VALUES (${customer.id}, ${customer.name}, ${customer.email}, ${customer.image_url}) -// ON CONFLICT (id) DO NOTHING; -// `, -// ), -// ); - -// return insertedCustomers; -// } - -// async function seedRevenue() { -// await client.sql` -// CREATE TABLE IF NOT EXISTS revenue ( -// month VARCHAR(4) NOT NULL UNIQUE, -// revenue INT NOT NULL -// ); -// `; - -// const insertedRevenue = await Promise.all( -// revenue.map( -// (rev) => client.sql` -// INSERT INTO revenue (month, revenue) -// VALUES (${rev.month}, ${rev.revenue}) -// ON CONFLICT (month) DO NOTHING; -// `, -// ), -// ); - -// return insertedRevenue; -// } - -export async function GET() { - return Response.json({ - message: - 'Uncomment this file and remove this line. You can delete this file when you are finished.', - }); - // try { - // await client.sql`BEGIN`; - // await seedUsers(); - // await seedCustomers(); - // await seedguests(); - // await seedRevenue(); - // await client.sql`COMMIT`; - - // return Response.json({ message: 'Database seeded successfully' }); - // } catch (error) { - // await client.sql`ROLLBACK`; - // return Response.json({ error }, { status: 500 }); - // } -} diff --git a/app/ui/components/login-form.tsx b/app/ui/components/login-form.tsx new file mode 100644 index 0000000..63ad65d --- /dev/null +++ b/app/ui/components/login-form.tsx @@ -0,0 +1,51 @@ +/* Copyright (C) 2024 Manuel Bustillo*/ + +'use client'; + +import { FloatLabel } from 'primereact/floatlabel'; +import { InputText } from 'primereact/inputtext'; +import { login } from '../../api/authentication'; +import { useState, useEffect } from 'react'; +import { classNames } from './button'; +import { useRouter } from 'next/navigation' +import { User } from '../../lib/definitions'; +import { getSlug } from '@/app/lib/utils'; + +export default function LoginForm() { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + + const router = useRouter(); + + const [currentUser, setCurrentUser] = useState(null); + + useEffect(() => { + localStorage.setItem('currentUser', JSON.stringify(currentUser)); + }, [currentUser]); + + return ( +
+ + setEmail(e.target.value)} /> + + + + setPassword(e.target.value)} /> + + + +
+ ) +} \ No newline at end of file diff --git a/app/ui/customers/table.tsx b/app/ui/customers/table.tsx deleted file mode 100644 index 6a41e3f..0000000 --- a/app/ui/customers/table.tsx +++ /dev/null @@ -1,125 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -import Image from 'next/image'; -import { lusitana } from '@/app/ui/fonts'; -import Search from '@/app/ui/search'; -import { - CustomersTableType, - FormattedCustomersTable, -} from '@/app/lib/definitions'; - -export default async function CustomersTable({ - customers, -}: { - customers: FormattedCustomersTable[]; -}) { - return ( -
-

- Customers -

- -
-
-
-
-
- {customers?.map((customer) => ( -
-
-
-
-
- {`${customer.name}'s -

{customer.name}

-
-
-

- {customer.email} -

-
-
-
-
-

Pending

-

{customer.total_pending}

-
-
-

Paid

-

{customer.total_paid}

-
-
-
-

{customer.total_guests} guests

-
-
- ))} -
- - - - - - - - - - - - - {customers.map((customer) => ( - - - - - - - - ))} - -
- Name - - Email - - Total guests - - Total Pending - - Total Paid -
-
- {`${customer.name}'s -

{customer.name}

-
-
- {customer.email} - - {customer.total_guests} - - {customer.total_pending} - - {customer.total_paid} -
-
-
-
-
-
- ); -} diff --git a/app/ui/dashboard/cards.tsx b/app/ui/dashboard/cards.tsx deleted file mode 100644 index 687043c..0000000 --- a/app/ui/dashboard/cards.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -import { - BanknotesIcon, - ClockIcon, - UserGroupIcon, - InboxIcon, -} from '@heroicons/react/24/outline'; -import { lusitana } from '@/app/ui/fonts'; - -const iconMap = { - collected: BanknotesIcon, - customers: UserGroupIcon, - pending: ClockIcon, - guests: InboxIcon, -}; - -export default async function CardWrapper() { - return ( - <> - {/* NOTE: Uncomment this code in Chapter 9 */} - - {/* - - - */} - - ); -} - -export function Card({ - title, - value, - type, -}: { - title: string; - value: number | string; - type: 'guests' | 'customers' | 'pending' | 'collected'; -}) { - const Icon = iconMap[type]; - - return ( -
-
- {Icon ? : null} -

{title}

-
-

- {value} -

-
- ); -} diff --git a/app/ui/dashboard/latest-invoices.tsx b/app/ui/dashboard/latest-invoices.tsx deleted file mode 100644 index 7c668e9..0000000 --- a/app/ui/dashboard/latest-invoices.tsx +++ /dev/null @@ -1,66 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -import { ArrowPathIcon } from '@heroicons/react/24/outline'; -import clsx from 'clsx'; -import Image from 'next/image'; -import { lusitana } from '@/app/ui/fonts'; -import { LatestInvoice } from '@/app/lib/definitions'; -export default async function Latestguests({ - latestguests, -}: { - latestguests: LatestInvoice[]; -}) { - return ( -
-

- Latest guests -

-
- {/* NOTE: Uncomment this code in Chapter 7 */} - - {/*
- {latestguests.map((invoice, i) => { - return ( -
-
- {`${invoice.name}'s -
-

- {invoice.name} -

-

- {invoice.email} -

-
-
-

- {invoice.amount} -

-
- ); - })} -
*/} -
- -

Updated just now

-
-
-
- ); -} diff --git a/app/ui/dashboard/nav-links.tsx b/app/ui/dashboard/nav-links.tsx index 7d88618..0b881dc 100644 --- a/app/ui/dashboard/nav-links.tsx +++ b/app/ui/dashboard/nav-links.tsx @@ -10,13 +10,14 @@ import { import Link from 'next/link'; import { usePathname } from 'next/navigation'; import clsx from 'clsx'; +import { getSlug } from '@/app/lib/utils'; // Map of links to display in the side navigation. // Depending on the size of the application, this would be stored in a database. const links = [ - { name: 'Guests', href: '/dashboard/guests', icon: UserGroupIcon }, - { name: 'Expenses', href: '/dashboard/expenses', icon: BanknotesIcon }, - { name: 'Table distributions', href: '/dashboard/tables', icon: RectangleGroupIcon }, + { name: 'Guests', href: `/${getSlug()}/dashboard/guests`, icon: UserGroupIcon }, + { name: 'Expenses', href: `/${getSlug()}/dashboard/expenses`, icon: BanknotesIcon }, + { name: 'Table distributions', href: `/${getSlug()}/dashboard/tables`, icon: RectangleGroupIcon }, ]; diff --git a/app/ui/dashboard/revenue-chart.tsx b/app/ui/dashboard/revenue-chart.tsx deleted file mode 100644 index 023e61d..0000000 --- a/app/ui/dashboard/revenue-chart.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -import { generateYAxis } from '@/app/lib/utils'; -import { CalendarIcon } from '@heroicons/react/24/outline'; -import { lusitana } from '@/app/ui/fonts'; -import { Revenue } from '@/app/lib/definitions'; - -// This component is representational only. -// For data visualization UI, check out: -// https://www.tremor.so/ -// https://www.chartjs.org/ -// https://airbnb.io/visx/ - -export default async function RevenueChart({ - revenue, -}: { - revenue: Revenue[]; -}) { - const chartHeight = 350; - // NOTE: Uncomment this code in Chapter 7 - - // const { yAxisLabels, topLabel } = generateYAxis(revenue); - - // if (!revenue || revenue.length === 0) { - // return

No data available.

; - // } - - return ( -
-

- Recent Revenue -

- {/* NOTE: Uncomment this code in Chapter 7 */} - - {/*
-
-
- {yAxisLabels.map((label) => ( -

{label}

- ))} -
- - {revenue.map((month) => ( -
-
-

- {month.month} -

-
- ))} -
-
- -

Last 12 months

-
-
*/} -
- ); -} diff --git a/app/ui/dashboard/sidenav.tsx b/app/ui/dashboard/sidenav.tsx index a13a2e6..ec4e61c 100644 --- a/app/ui/dashboard/sidenav.tsx +++ b/app/ui/dashboard/sidenav.tsx @@ -1,30 +1,46 @@ /* Copyright (C) 2024 Manuel Bustillo*/ +'use client'; + import Link from 'next/link'; import NavLinks from '@/app/ui/dashboard/nav-links'; import { PowerIcon } from '@heroicons/react/24/outline'; import { gloriaHallelujah } from '@/app/ui/fonts'; +import { logout } from '@/app/api/authentication'; +import { useRouter } from 'next/navigation'; +import { getSlug } from '@/app/lib/utils'; export default function SideNav() { + const router = useRouter(); + return (
-
+

Wedding Planner

-
- -
+ Logged in as {JSON.parse(localStorage.getItem('currentUser') || '{}').email} +
); diff --git a/app/ui/guests/affinity-groups-tree.tsx b/app/ui/guests/affinity-groups-tree.tsx deleted file mode 100644 index 0082070..0000000 --- a/app/ui/guests/affinity-groups-tree.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -'use client' - -import React, { useState, useEffect, Suspense } from 'react'; -import { Tree } from 'primereact/tree'; -import { PrimeIcons } from 'primereact/api'; -import { debug } from 'console'; -import { Group } from '@/app/lib/definitions'; - -export default function AffinityGroupsTree() { - const [nodes, setNodes] = useState([]); - - const groupToNode = (group: Group): any => { - return({ - key: group.id, - label: `${group.name} (${group.guest_count})`, - icon: group.icon, - children: group.children.map((child) => groupToNode(child)), - className: "px-4", - }) - } - - const parseNode = (record: any, included: any[]): Group => { - if (!record.attributes) { - record = included.find((a) => a.id === record.id); - } - - const children: Group[] = (record?.relationships?.children?.data || []).map((child: any) => { - return (parseNode(child, included)); - }); - - const children_guest_count: number = children.reduce((acc: number, child: Group) => acc + child.guest_count, 0); - - return ({ - id: record.id, - name: record.attributes.name, - guest_count: record.attributes.guest_count + children_guest_count, - icon: record.attributes.icon, - children: children, - }) - } - - - useEffect(() => { - if (nodes.length > 0) { - return; - } - fetch("/api/groups.json") - .then((response) => response.json()) - .then((data) => { - setNodes(data.data.map((record: any) => { - return (groupToNode(parseNode(record, data.included))); - })) - }); - }); - - return ( -
- - setNodes(e.value as any)} className="w-full md:w-30rem" /> - -
- ) -} \ No newline at end of file diff --git a/app/ui/guests/breadcrumbs.tsx b/app/ui/guests/breadcrumbs.tsx deleted file mode 100644 index 828a614..0000000 --- a/app/ui/guests/breadcrumbs.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -import { clsx } from 'clsx'; -import Link from 'next/link'; -import { lusitana } from '@/app/ui/fonts'; - -interface Breadcrumb { - label: string; - href: string; - active?: boolean; -} - -export default function Breadcrumbs({ - breadcrumbs, -}: { - breadcrumbs: Breadcrumb[]; -}) { - return ( - - ); -} diff --git a/app/ui/guests/buttons.tsx b/app/ui/guests/buttons.tsx deleted file mode 100644 index d4e2183..0000000 --- a/app/ui/guests/buttons.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -import { PencilIcon, PlusIcon, TrashIcon } from '@heroicons/react/24/outline'; -import Link from 'next/link'; - -export function CreateInvoice() { - return ( - - Create Invoice{' '} - - - ); -} - -export function UpdateInvoice({ id }: { id: string }) { - return ( - - - - ); -} - -export function DeleteInvoice({ id }: { id: string }) { - return ( - <> - - - ); -} diff --git a/app/ui/guests/create-form.tsx b/app/ui/guests/create-form.tsx deleted file mode 100644 index d66a259..0000000 --- a/app/ui/guests/create-form.tsx +++ /dev/null @@ -1,114 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -import { CustomerField } from '@/app/lib/definitions'; -import Link from 'next/link'; -import { - CheckIcon, - ClockIcon, - CurrencyDollarIcon, - UserCircleIcon, -} from '@heroicons/react/24/outline'; -import { Button } from '@/app/ui/button'; - -export default function Form({ customers }: { customers: CustomerField[] }) { - return ( -
-
- {/* Customer Name */} -
- -
- - -
-
- - {/* Invoice Amount */} -
- -
-
- - -
-
-
- - {/* Invoice Status */} -
- - Set the invoice status - -
-
-
- - -
-
- - -
-
-
-
-
-
- - Cancel - - -
-
- ); -} diff --git a/app/ui/guests/edit-form.tsx b/app/ui/guests/edit-form.tsx deleted file mode 100644 index d71a275..0000000 --- a/app/ui/guests/edit-form.tsx +++ /dev/null @@ -1,125 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -'use client'; - -import { CustomerField, InvoiceForm } from '@/app/lib/definitions'; -import { - CheckIcon, - ClockIcon, - CurrencyDollarIcon, - UserCircleIcon, -} from '@heroicons/react/24/outline'; -import Link from 'next/link'; -import { Button } from '@/app/ui/button'; - -export default function EditInvoiceForm({ - invoice, - customers, -}: { - invoice: InvoiceForm; - customers: CustomerField[]; -}) { - return ( -
-
- {/* Customer Name */} -
- -
- - -
-
- - {/* Invoice Amount */} -
- -
-
- - -
-
-
- - {/* Invoice Status */} -
- - Set the invoice status - -
-
-
- - -
-
- - -
-
-
-
-
-
- - Cancel - - -
-
- ); -} diff --git a/app/ui/guests/pagination.tsx b/app/ui/guests/pagination.tsx deleted file mode 100644 index 42488ae..0000000 --- a/app/ui/guests/pagination.tsx +++ /dev/null @@ -1,121 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -'use client'; - -import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline'; -import clsx from 'clsx'; -import Link from 'next/link'; -import { generatePagination } from '@/app/lib/utils'; - -export default function Pagination({ totalPages }: { totalPages: number }) { - // NOTE: Uncomment this code in Chapter 11 - - // const allPages = generatePagination(currentPage, totalPages); - - return ( - <> - {/* NOTE: Uncomment this code in Chapter 11 */} - - {/*
- - -
- {allPages.map((page, index) => { - let position: 'first' | 'last' | 'single' | 'middle' | undefined; - - if (index === 0) position = 'first'; - if (index === allPages.length - 1) position = 'last'; - if (allPages.length === 1) position = 'single'; - if (page === '...') position = 'middle'; - - return ( - - ); - })} -
- - = totalPages} - /> -
*/} - - ); -} - -function PaginationNumber({ - page, - href, - isActive, - position, -}: { - page: number | string; - href: string; - position?: 'first' | 'last' | 'middle' | 'single'; - isActive: boolean; -}) { - const className = clsx( - 'flex h-10 w-10 items-center justify-center text-sm border', - { - 'rounded-l-md': position === 'first' || position === 'single', - 'rounded-r-md': position === 'last' || position === 'single', - 'z-10 bg-blue-600 border-blue-600 text-white': isActive, - 'hover:bg-gray-100': !isActive && position !== 'middle', - 'text-gray-300': position === 'middle', - }, - ); - - return isActive || position === 'middle' ? ( -
{page}
- ) : ( - - {page} - - ); -} - -function PaginationArrow({ - href, - direction, - isDisabled, -}: { - href: string; - direction: 'left' | 'right'; - isDisabled?: boolean; -}) { - const className = clsx( - 'flex h-10 w-10 items-center justify-center rounded-md border', - { - 'pointer-events-none text-gray-300': isDisabled, - 'hover:bg-gray-100': !isDisabled, - 'mr-2 md:mr-4': direction === 'left', - 'ml-2 md:ml-4': direction === 'right', - }, - ); - - const icon = - direction === 'left' ? ( - - ) : ( - - ); - - return isDisabled ? ( -
{icon}
- ) : ( - - {icon} - - ); -} diff --git a/app/ui/guests/status.tsx b/app/ui/guests/status.tsx deleted file mode 100644 index f8a3a78..0000000 --- a/app/ui/guests/status.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -import { CheckIcon, ClockIcon } from '@heroicons/react/24/outline'; -import clsx from 'clsx'; - -export default function gueststatus({ status }: { status: string }) { - return ( - - {status === 'pending' ? ( - <> - Pending - - - ) : null} - {status === 'paid' ? ( - <> - Paid - - - ) : null} - - ); -} diff --git a/app/ui/login-form.tsx b/app/ui/login-form.tsx deleted file mode 100644 index 6e6241e..0000000 --- a/app/ui/login-form.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright (C) 2024 Manuel Bustillo*/ - -import { lusitana } from '@/app/ui/fonts'; -import { - AtSymbolIcon, - KeyIcon, - ExclamationCircleIcon, -} from '@heroicons/react/24/outline'; -import { ArrowRightIcon } from '@heroicons/react/20/solid'; -import { Button } from './button'; - -export default function LoginForm() { - return ( -
-
-

- Please log in to continue. -

-
-
- -
- - -
-
-
- -
- - -
-
-
- -
- {/* Add form errors here */} -
-
-
- ); -} diff --git a/package.json b/package.json index cd0557c..41ea854 100644 --- a/package.json +++ b/package.json @@ -19,19 +19,19 @@ "react": "19.0.0-rc-f38c22b244-20240704", "react-dom": "19.0.0-rc-f38c22b244-20240704", "tailwindcss": "3.4.15", - "typescript": "5.6.3", + "typescript": "5.7.2", "use-debounce": "^10.0.1", "zod": "^3.23.8" }, "devDependencies": { "@playwright/test": "^1.46.0", "@types/bcrypt": "^5.0.2", - "@types/node": "22.9.0", + "@types/node": "22.10.1", "@types/react": "18.3.12", "@types/react-dom": "18.3.1" }, "engines": { "node": ">=23.0.0" }, - "packageManager": "pnpm@9.13.2+sha512.88c9c3864450350e65a33587ab801acf946d7c814ed1134da4a924f6df5a2120fd36b46aab68f7cd1d413149112d53c7db3a4136624cfd00ff1846a0c6cef48a" + "packageManager": "pnpm@9.14.4+sha512.c8180b3fbe4e4bca02c94234717896b5529740a6cbadf19fa78254270403ea2f27d4e1d46a08a0f56c89b63dc8ebfd3ee53326da720273794e6200fcf0d184ab" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c367c00..f249b78 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@heroicons/react': specifier: ^2.1.4 - version: 2.1.5(react@19.0.0-rc-f38c22b244-20240704) + version: 2.2.0(react@19.0.0-rc-f38c22b244-20240704) '@tailwindcss/forms': specifier: ^0.5.7 version: 0.5.9(tailwindcss@3.4.15) @@ -25,10 +25,10 @@ importers: version: 2.1.1 next: specifier: 15.0.3 - version: 15.0.3(@playwright/test@1.48.2)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704) + version: 15.0.3(@playwright/test@1.49.0)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704) next-auth: specifier: 5.0.0-beta.25 - version: 5.0.0-beta.25(next@15.0.3(@playwright/test@1.48.2)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704) + version: 5.0.0-beta.25(next@15.0.3(@playwright/test@1.49.0)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704) postcss: specifier: 8.4.49 version: 8.4.49 @@ -37,7 +37,7 @@ importers: version: 7.0.0 primereact: specifier: ^10.8.2 - version: 10.8.4(@types/react@18.3.12)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704) + version: 10.8.5(@types/react@18.3.12)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704) react: specifier: 19.0.0-rc-f38c22b244-20240704 version: 19.0.0-rc-f38c22b244-20240704 @@ -48,8 +48,8 @@ importers: specifier: 3.4.15 version: 3.4.15 typescript: - specifier: 5.6.3 - version: 5.6.3 + specifier: 5.7.2 + version: 5.7.2 use-debounce: specifier: ^10.0.1 version: 10.0.4(react@19.0.0-rc-f38c22b244-20240704) @@ -59,13 +59,13 @@ importers: devDependencies: '@playwright/test': specifier: ^1.46.0 - version: 1.48.2 + version: 1.49.0 '@types/bcrypt': specifier: ^5.0.2 version: 5.0.2 '@types/node': - specifier: 22.9.0 - version: 22.9.0 + specifier: 22.10.1 + version: 22.10.1 '@types/react': specifier: 18.3.12 version: 18.3.12 @@ -93,17 +93,17 @@ packages: nodemailer: optional: true - '@babel/runtime@7.25.7': - resolution: {integrity: sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==} + '@babel/runtime@7.26.0': + resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} engines: {node: '>=6.9.0'} '@emnapi/runtime@1.2.0': resolution: {integrity: sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==} - '@heroicons/react@2.1.5': - resolution: {integrity: sha512-FuzFN+BsHa+7OxbvAERtgBTNeZpUjgM/MIizfVkSCL2/edriN0Hx/DWRCR//aPYwO5QX/YlgLGXk+E3PcfZwjA==} + '@heroicons/react@2.2.0': + resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==} peerDependencies: - react: '>= 16' + react: '>= 16 || ^19.0.0-rc' '@img/sharp-darwin-arm64@0.33.5': resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} @@ -306,8 +306,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@playwright/test@1.48.2': - resolution: {integrity: sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==} + '@playwright/test@1.49.0': + resolution: {integrity: sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==} engines: {node: '>=18'} hasBin: true @@ -328,8 +328,8 @@ packages: '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} - '@types/node@22.9.0': - resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==} + '@types/node@22.10.1': + resolution: {integrity: sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==} '@types/prop-types@15.7.12': resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} @@ -839,13 +839,13 @@ packages: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} - playwright-core@1.48.2: - resolution: {integrity: sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==} + playwright-core@1.49.0: + resolution: {integrity: sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==} engines: {node: '>=18'} hasBin: true - playwright@1.48.2: - resolution: {integrity: sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==} + playwright@1.49.0: + resolution: {integrity: sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==} engines: {node: '>=18'} hasBin: true @@ -908,8 +908,8 @@ packages: primeicons@7.0.0: resolution: {integrity: sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==} - primereact@10.8.4: - resolution: {integrity: sha512-jwkSzq6pOHayzEh+9dgk2M71gEZtoQakwPKVo3FUJO3eEX3SoAOchON+Xm1tGCNqtd66ca8RgOWQcpv3AQoMvg==} + primereact@10.8.5: + resolution: {integrity: sha512-B1LeJdNGGAB8P1VRJE0TDYz6rZSDwxE7Ft8PLFjRYLO44+mIJNDsLYSB56LRJOoqUNRhQrRIe/5ctS5eyQAzwQ==} engines: {node: '>=14.0.0'} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -1096,13 +1096,13 @@ packages: tslib@2.6.3: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} - typescript@5.6.3: - resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} + typescript@5.7.2: + resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} engines: {node: '>=14.17'} hasBin: true - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} update-browserslist-db@1.1.0: resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} @@ -1169,7 +1169,7 @@ snapshots: preact: 10.11.3 preact-render-to-string: 5.2.3(preact@10.11.3) - '@babel/runtime@7.25.7': + '@babel/runtime@7.26.0': dependencies: regenerator-runtime: 0.14.1 @@ -1178,7 +1178,7 @@ snapshots: tslib: 2.6.3 optional: true - '@heroicons/react@2.1.5(react@19.0.0-rc-f38c22b244-20240704)': + '@heroicons/react@2.2.0(react@19.0.0-rc-f38c22b244-20240704)': dependencies: react: 19.0.0-rc-f38c22b244-20240704 @@ -1341,9 +1341,9 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/test@1.48.2': + '@playwright/test@1.49.0': dependencies: - playwright: 1.48.2 + playwright: 1.49.0 '@swc/counter@0.1.3': {} @@ -1358,13 +1358,13 @@ snapshots: '@types/bcrypt@5.0.2': dependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.1 '@types/cookie@0.6.0': {} - '@types/node@22.9.0': + '@types/node@22.10.1': dependencies: - undici-types: 6.19.8 + undici-types: 6.20.0 '@types/prop-types@15.7.12': {} @@ -1535,7 +1535,7 @@ snapshots: dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.25.7 + '@babel/runtime': 7.26.0 csstype: 3.1.3 eastasianwidth@0.2.0: {} @@ -1735,13 +1735,13 @@ snapshots: nanoid@3.3.7: {} - next-auth@5.0.0-beta.25(next@15.0.3(@playwright/test@1.48.2)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704): + next-auth@5.0.0-beta.25(next@15.0.3(@playwright/test@1.49.0)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704): dependencies: '@auth/core': 0.37.2 - next: 15.0.3(@playwright/test@1.48.2)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704) + next: 15.0.3(@playwright/test@1.49.0)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704) react: 19.0.0-rc-f38c22b244-20240704 - next@15.0.3(@playwright/test@1.48.2)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704): + next@15.0.3(@playwright/test@1.49.0)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704): dependencies: '@next/env': 15.0.3 '@swc/counter': 0.1.3 @@ -1761,7 +1761,7 @@ snapshots: '@next/swc-linux-x64-musl': 15.0.3 '@next/swc-win32-arm64-msvc': 15.0.3 '@next/swc-win32-x64-msvc': 15.0.3 - '@playwright/test': 1.48.2 + '@playwright/test': 1.49.0 sharp: 0.33.5 transitivePeerDependencies: - '@babel/core' @@ -1821,11 +1821,11 @@ snapshots: pirates@4.0.6: {} - playwright-core@1.48.2: {} + playwright-core@1.49.0: {} - playwright@1.48.2: + playwright@1.49.0: dependencies: - playwright-core: 1.48.2 + playwright-core: 1.49.0 optionalDependencies: fsevents: 2.3.2 @@ -1883,7 +1883,7 @@ snapshots: primeicons@7.0.0: {} - primereact@10.8.4(@types/react@18.3.12)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704): + primereact@10.8.5(@types/react@18.3.12)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704): dependencies: '@types/react-transition-group': 4.4.11 react: 19.0.0-rc-f38c22b244-20240704 @@ -1909,7 +1909,7 @@ snapshots: react-transition-group@4.4.5(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704): dependencies: - '@babel/runtime': 7.25.7 + '@babel/runtime': 7.26.0 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -2104,9 +2104,9 @@ snapshots: tslib@2.6.3: {} - typescript@5.6.3: {} + typescript@5.7.2: {} - undici-types@6.19.8: {} + undici-types@6.20.0: {} update-browserslist-db@1.1.0(browserslist@4.23.3): dependencies: diff --git a/public/customers/amy-burns.png b/public/customers/amy-burns.png deleted file mode 100644 index 7b29d72..0000000 Binary files a/public/customers/amy-burns.png and /dev/null differ diff --git a/public/customers/balazs-orban.png b/public/customers/balazs-orban.png deleted file mode 100644 index 7fbc009..0000000 Binary files a/public/customers/balazs-orban.png and /dev/null differ diff --git a/public/customers/delba-de-oliveira.png b/public/customers/delba-de-oliveira.png deleted file mode 100644 index 08db1b8..0000000 Binary files a/public/customers/delba-de-oliveira.png and /dev/null differ diff --git a/public/customers/evil-rabbit.png b/public/customers/evil-rabbit.png deleted file mode 100644 index fe7990f..0000000 Binary files a/public/customers/evil-rabbit.png and /dev/null differ diff --git a/public/customers/lee-robinson.png b/public/customers/lee-robinson.png deleted file mode 100644 index 633ae98..0000000 Binary files a/public/customers/lee-robinson.png and /dev/null differ diff --git a/public/customers/michael-novotny.png b/public/customers/michael-novotny.png deleted file mode 100644 index 96a13a6..0000000 Binary files a/public/customers/michael-novotny.png and /dev/null differ diff --git a/tests/guests.spec.ts b/tests/guests.spec.ts index 87abe3a..8bf139c 100644 --- a/tests/guests.spec.ts +++ b/tests/guests.spec.ts @@ -1,7 +1,7 @@ import { test, expect, Page } from '@playwright/test' const mockGuestsAPI = ({ page }: { page: Page }) => { - page.route('*/**/api/guests', async route => { + page.route('*/**/api/default/guests', async route => { const json = [ { "id": "f4a09c28-40ea-4553-90a5-96935a59cac6", @@ -28,7 +28,7 @@ const mockGuestsAPI = ({ page }: { page: Page }) => { } const mockGroupsAPI = ({ page }: { page: Page }) => { - page.route('*/**/api/groups', async route => { + page.route('*/**/api/default/groups', async route => { const json = [ { "id": "ee44ffb9-1147-4842-a378-9eaeb0f0871a", @@ -65,7 +65,7 @@ const mockGroupsAPI = ({ page }: { page: Page }) => { test('should display the list of guests', async ({ page }) => { await mockGuestsAPI({ page }); - await page.goto('/dashboard/guests'); + await page.goto('/default/dashboard/guests'); await expect(page.getByRole('tab', { name: 'Groups' })).toBeVisible(); await expect(page.getByRole('tab', { name: 'Guests' })).toBeVisible(); @@ -90,7 +90,7 @@ test('should display the list of guests', async ({ page }) => { test('should display the list of groups', async ({ page }) => { await mockGroupsAPI({ page }); - await page.goto('/dashboard/guests'); + await page.goto('/default/dashboard/guests'); await page.getByRole('tab', { name: 'Groups' }).click(); await expect(page.getByText('There are 2 elements in the list')).toBeVisible();