Compare commits
No commits in common. "main" and "website-editor" have entirely different histories.
main
...
website-ed
2
.github/workflows/playwright.yml
vendored
2
.github/workflows/playwright.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
node-version: lts/*
|
||||
- name: Install dependencies
|
||||
run: npm install -g pnpm && pnpm install
|
||||
- name: Build the service that will be tested
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Based on https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
|
||||
|
||||
FROM node:24-alpine AS base
|
||||
FROM node:23-alpine AS base
|
||||
|
||||
# Install dependencies only when needed
|
||||
FROM base AS deps
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Based on https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile
|
||||
|
||||
FROM node:24-alpine AS base
|
||||
FROM node:23-alpine AS base
|
||||
|
||||
# Install dependencies only when needed
|
||||
FROM base AS deps
|
||||
|
@ -18,7 +18,6 @@ import { Toast } from 'primereact/toast';
|
||||
import { Suspense, useEffect, useRef, useState } from 'react';
|
||||
import InvitationsBoard from '@/app/ui/invitations/board';
|
||||
import { Invitation, InvitationSerializer } from '@/app/lib/invitation';
|
||||
import { Entity } from '@/app/lib/definitions';
|
||||
|
||||
|
||||
export default function Page() {
|
||||
@ -30,9 +29,9 @@ export default function Page() {
|
||||
refreshGuests();
|
||||
refreshInvitations();
|
||||
}, []);
|
||||
|
||||
|
||||
const toast = useRef<Toast>(null);
|
||||
|
||||
|
||||
function refreshGuests() {
|
||||
new AbstractApi<Guest>().getAll(new GuestSerializer(), (objects: Guest[]) => {
|
||||
setGuests(objects);
|
||||
@ -55,20 +54,20 @@ export default function Page() {
|
||||
fetch(`/api/${slug}/groups/affinities/reset`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'X-CSRF-TOKEN': getCsrfToken(),
|
||||
'Accept': 'application/json',
|
||||
'X-CSRF-TOKEN': getCsrfToken(),
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
showAffinitiesResetSuccess();
|
||||
} else {
|
||||
console.error('Failed to reset affinities');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error resetting affinities:', error);
|
||||
});
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
showAffinitiesResetSuccess();
|
||||
} else {
|
||||
console.error('Failed to reset affinities');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error resetting affinities:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function showAffinitiesResetSuccess() {
|
||||
@ -89,22 +88,7 @@ export default function Page() {
|
||||
|
||||
const [invitations, setInvitations] = useState<Array<Invitation>>([]);
|
||||
|
||||
function updateList<T extends Entity>(originalList: T[], element: T): T[] {
|
||||
{
|
||||
const index = originalList.findIndex(g => g.id === element?.id);
|
||||
if (index !== -1) {
|
||||
// Replace existing element
|
||||
return [
|
||||
element!,
|
||||
...originalList.slice(0, index),
|
||||
...originalList.slice(index + 1)
|
||||
];
|
||||
} else {
|
||||
// Add new element at the start
|
||||
return [element!, ...originalList];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
@ -115,9 +99,9 @@ export default function Page() {
|
||||
<GuestFormDialog
|
||||
key={guestBeingEdited?.id}
|
||||
groups={groups}
|
||||
onCreate={(newGuest) => {
|
||||
setGuests(guests => updateList(guests, newGuest));
|
||||
setGuestBeingEdited(undefined);
|
||||
onCreate={(newGuest) => {
|
||||
setGuests([newGuest!, ...guests]);
|
||||
setGuestBeingEdited(undefined) ;
|
||||
}}
|
||||
guest={guestBeingEdited}
|
||||
visible={guestBeingEdited !== undefined}
|
||||
@ -144,10 +128,7 @@ export default function Page() {
|
||||
<GroupFormDialog
|
||||
key={groupBeingEdited?.id}
|
||||
groups={groups}
|
||||
onCreate={(newGroup) => {
|
||||
setGroups(groups => updateList(groups, newGroup));
|
||||
setGroupBeingEdited(undefined)
|
||||
}}
|
||||
onCreate={() => { refreshGroups(); setGroupBeingEdited(undefined) }}
|
||||
group={groupBeingEdited}
|
||||
visible={groupBeingEdited !== undefined}
|
||||
onHide={() => { setGroupBeingEdited(undefined) }}
|
||||
@ -171,7 +152,7 @@ export default function Page() {
|
||||
</div>
|
||||
</ TabPanel>
|
||||
<TabPanel header="Invitations" leftIcon="pi pi-envelope mx-2">
|
||||
<InvitationsBoard guests={guests} invitations={invitations} />
|
||||
<InvitationsBoard guests={guests} invitations={invitations}/>
|
||||
</TabPanel>
|
||||
</ TabView>
|
||||
</div>
|
||||
|
@ -3,23 +3,16 @@
|
||||
'use client'
|
||||
|
||||
import { AbstractApi } from '@/app/api/abstract-api';
|
||||
import { getSlug } from '@/app/lib/utils';
|
||||
import { Website, WebsiteSerializer } from '@/app/lib/website';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Tiptap from '../../../components/Tiptap';
|
||||
|
||||
|
||||
export default function Page() {
|
||||
|
||||
const [website, setWebsite] = useState<Website>()
|
||||
const api = new AbstractApi<Website>();
|
||||
const serializer = new WebsiteSerializer();
|
||||
|
||||
const [slug, setSlug] = useState<string>("default");
|
||||
useEffect(() => { setSlug(getSlug()) }, []);
|
||||
|
||||
const [iframeKey, setIframeKey] = useState<number>(Math.random());
|
||||
|
||||
const [timeoutId, setTimeoutId] = useState<NodeJS.Timeout | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@ -36,30 +29,18 @@ export default function Page() {
|
||||
|
||||
setTimeoutId(
|
||||
setTimeout(() => {
|
||||
api.update(serializer, new Website('', newContent), () => {
|
||||
setIframeKey(Math.random()); // Change key to force iframe reload
|
||||
});
|
||||
api.update(serializer, new Website('', newContent), () => { });
|
||||
}, 500)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex">
|
||||
<div className="w-1/2 border rounded-lg p-4">
|
||||
<Tiptap
|
||||
key={website?.content ?? 'empty'}
|
||||
content={website?.content || ''}
|
||||
onUpdate={updateWebsite}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-1/2 border rounded-lg p-4 ml-4">
|
||||
<iframe
|
||||
key={iframeKey}
|
||||
src={`/${slug}/site`}
|
||||
title="Website Preview"
|
||||
className="w-full h-[80vh] rounded"
|
||||
/>
|
||||
</div>
|
||||
<div className="border rounded-lg p-4">
|
||||
<Tiptap
|
||||
key={website?.content ?? 'empty'}
|
||||
content={website?.content || ''}
|
||||
onUpdate={updateWebsite}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
/* Copyright (C) 2024-2025 LibreWeddingPlanner contributors*/
|
||||
|
||||
'use client'
|
||||
|
||||
import { AbstractApi } from "@/app/api/abstract-api";
|
||||
import { Invitation, InvitationSerializer } from "@/app/lib/invitation";
|
||||
import { useParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FloatLabel } from "primereact/floatlabel";
|
||||
import { Dropdown } from "primereact/dropdown";
|
||||
import { Guest, GuestSerializer, GuestStatus } from "@/app/lib/guest";
|
||||
import { Button } from "primereact/button";
|
||||
|
||||
type FormResponse = {
|
||||
attendance: GuestStatus;
|
||||
};
|
||||
|
||||
function GuestForm({ guest, idx }: { guest: Guest, idx: number }) {
|
||||
const [response, setResponse] = useState<FormResponse>({
|
||||
attendance: guest.status!
|
||||
});
|
||||
|
||||
const [pendingChanges, setPendingChanges] = useState(false);
|
||||
const [sending, setSending] = useState(false);
|
||||
|
||||
console.log('GuestForm response', response.attendance);
|
||||
|
||||
const attendanceOptions: { name: string, code: GuestStatus }[] = [
|
||||
{
|
||||
name: 'Attending',
|
||||
code: 'confirmed'
|
||||
},
|
||||
{
|
||||
name: 'Declined',
|
||||
code: 'declined'
|
||||
},
|
||||
{
|
||||
name: 'Tentative',
|
||||
code: 'tentative'
|
||||
}
|
||||
];
|
||||
|
||||
const api = new AbstractApi<Guest>();
|
||||
const serializer = new GuestSerializer();
|
||||
|
||||
const submitForm = () => {
|
||||
setSending(true);
|
||||
setPendingChanges(false);
|
||||
api.update(serializer, {
|
||||
id: guest.id!,
|
||||
status: response.attendance,
|
||||
}, () => setSending(false));
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={guest.id}
|
||||
className={`px-2 py-6 flex flex-col items-center ${idx !== 0 ? 'border-t border-gray-300' : ''}`}
|
||||
>
|
||||
<h2 className="m-2 text-xl font-semibold">{guest.name}</h2>
|
||||
|
||||
<Dropdown
|
||||
value={response.attendance}
|
||||
options={attendanceOptions}
|
||||
optionLabel="name"
|
||||
optionValue="code"
|
||||
className="rounded-md w-full max-w-xs border border-gray-300"
|
||||
checkmark={true}
|
||||
highlightOnSelect={false}
|
||||
onChange={(e) => {
|
||||
setPendingChanges(true);
|
||||
setResponse({ ...response, attendance: e.value })
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
label="Save"
|
||||
icon="pi pi-save"
|
||||
loading={sending}
|
||||
onClick={submitForm}
|
||||
disabled={!pendingChanges || sending}
|
||||
className="mt-4 max-w-xs"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
const params = useParams<{ slug: string, id: string }>()
|
||||
|
||||
const [invitation, setInvitation] = useState<Invitation>();
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem('slug', params.slug);
|
||||
|
||||
const api = new AbstractApi<Invitation>();
|
||||
const serializer = new InvitationSerializer();
|
||||
|
||||
api.get(serializer, params.id, (invitation: Invitation) => {
|
||||
setInvitation(invitation);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center">
|
||||
<h1 className="text-2xl font-bold mb-4">Invitation</h1>
|
||||
{invitation ? (
|
||||
<div>
|
||||
<p>We have reserved {invitation.guests.length} seats in your honor. Please, confirm attendance submitting the following form:</p>
|
||||
{invitation.guests.map((guest, idx) => (
|
||||
<GuestForm key={guest.id} guest={guest} idx={idx} />
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p>Loading...</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -29,7 +29,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
|
||||
<div className="max-w-4xl w-full h-full bg-[#f9f9f7] shadow-lg">
|
||||
<div
|
||||
className="max-w-4xl lg:m-6 m-3 lg:px-6 px-3 lg:py-24 py-2 border-2 border-[#d3d3d1] rounded-xl text-[#958971] flex justify-center"
|
||||
className="max-w-4xl lg:m-6 m-3 lg:px-6 px-3 py-24 border-2 border-[#d3d3d1] rounded-xl text-[#958971] flex justify-center"
|
||||
style={{ height: 'calc(100% - 3rem)' }}
|
||||
>
|
||||
{children}
|
||||
|
@ -6,16 +6,8 @@ import { AbstractApi } from '@/app/api/abstract-api';
|
||||
import { Website, WebsiteSerializer } from '@/app/lib/website';
|
||||
import { useState, useEffect } from 'react';
|
||||
import DOMPurify from "dompurify";
|
||||
import { useParams } from 'next/navigation';
|
||||
|
||||
export default function Page() {
|
||||
const params = useParams<{ slug: string }>()
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem('slug', params.slug);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const [websiteContent, setWebsiteContent] = useState<string>("");
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
import { Entity } from '@/app/lib/definitions';
|
||||
import { getCsrfToken, getSlug } from '@/app/lib/utils';
|
||||
|
||||
|
||||
export interface Api<T extends Entity> {
|
||||
getAll(serializable: Serializable<T>, callback: (objets: T[]) => void): void;
|
||||
get(serializable: Serializable<T>, id: string, callback: (object: T) => void): void;
|
||||
@ -31,18 +30,6 @@ export class AbstractApi<T extends Entity> implements Api<T> {
|
||||
});
|
||||
}
|
||||
|
||||
getAllPdf(serializable: Serializable<T>, callback: () => void): void {
|
||||
fetch(`/api/${getSlug()}/${serializable.apiPath()}`, {
|
||||
headers: {
|
||||
'Accept': 'application/pdf',
|
||||
}
|
||||
}).then(res => res.blob())
|
||||
.then(blob => {
|
||||
var file = window.URL.createObjectURL(blob);
|
||||
window.location.assign(file);
|
||||
});
|
||||
}
|
||||
|
||||
get(serializable: Serializable<T>, id: (string | undefined), callback: (object: T) => void): void {
|
||||
const endpoint = id ? `/api/${getSlug()}/${serializable.apiPath()}/${id}` : `/api/${getSlug()}/${serializable.apiPath()}`;
|
||||
fetch(endpoint)
|
||||
@ -54,7 +41,7 @@ export class AbstractApi<T extends Entity> implements Api<T> {
|
||||
});
|
||||
}
|
||||
|
||||
update(serializable: Serializable<T>, object: T, callback: (updatedObject: T) => void): void {
|
||||
update(serializable: Serializable<T>, object: T, callback: () => void): void {
|
||||
const endpoint = object.id ? `/api/${getSlug()}/${serializable.apiPath()}/${object.id}` : `/api/${getSlug()}/${serializable.apiPath()}`;
|
||||
|
||||
fetch(endpoint, {
|
||||
@ -62,13 +49,9 @@ export class AbstractApi<T extends Entity> implements Api<T> {
|
||||
body: serializable.toJson(object),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-CSRF-TOKEN': getCsrfToken(),
|
||||
}
|
||||
}).then((response) => response.json())
|
||||
.then((data) => {
|
||||
callback(serializable.fromJson(data));
|
||||
})
|
||||
}).then(callback)
|
||||
.catch((error) => console.error(error));
|
||||
}
|
||||
|
||||
@ -78,7 +61,6 @@ export class AbstractApi<T extends Entity> implements Api<T> {
|
||||
body: serializable.toJson(object),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-CSRF-TOKEN': getCsrfToken(),
|
||||
}
|
||||
})
|
||||
|
@ -34,7 +34,7 @@ export class TableSimulationSerializer implements Serializable<TableSimulation>
|
||||
return new TableSimulation(data.id, data.tables.map((table: any) => {
|
||||
return {
|
||||
number: table.number,
|
||||
guests: table.guests.map((guest: any) => new Guest(guest.id, guest.name, guest.color, guest.status, [], guest.group)),
|
||||
guests: table.guests.map((guest: any) => new Guest(guest.id, guest.name, guest.group?.name, guest.group?.id, guest.color)),
|
||||
discomfort: {
|
||||
discomfort: table.discomfort.discomfort,
|
||||
breakdown: {
|
||||
|
@ -5,7 +5,7 @@ import clsx from "clsx";
|
||||
type ButtonColor = 'primary' | 'blue' | 'green' | 'red' | 'yellow' | 'gray';
|
||||
|
||||
export function classNames(type: ButtonColor) {
|
||||
return (clsx("text-white py-1 px-2 m-2 rounded disabled:opacity-50 disabled:cursor-not-allowed", {
|
||||
return (clsx("text-white py-1 px-2 mx-1 rounded disabled:opacity-50 disabled:cursor-not-allowed", {
|
||||
'bg-blue-400 hover:bg-blue-600': type === 'primary' || type === 'blue',
|
||||
'bg-green-500 hover:bg-green-600': type === 'green',
|
||||
'bg-red-500 hover:bg-red-600': type === 'red',
|
||||
|
@ -14,7 +14,7 @@ import { useState } from 'react';
|
||||
|
||||
export default function GroupFormDialog({ groups, onCreate, onHide, group, visible }: {
|
||||
groups: Group[],
|
||||
onCreate?: (newGroup: Group) => void,
|
||||
onCreate?: () => void,
|
||||
onHide: () => void,
|
||||
group?: Group,
|
||||
visible: boolean,
|
||||
@ -46,15 +46,15 @@ export default function GroupFormDialog({ groups, onCreate, onHide, group, visib
|
||||
group.color = color;
|
||||
group.parentId = parentId;
|
||||
|
||||
api.update(serializer, group, (newGroup) => {
|
||||
api.update(serializer, group, () => {
|
||||
resetForm();
|
||||
onCreate && onCreate(newGroup);
|
||||
onCreate && onCreate();
|
||||
});
|
||||
} else {
|
||||
|
||||
api.create(serializer, new Group(undefined, name, undefined, icon, undefined, parentId, color), (newGroup) => {
|
||||
api.create(serializer, new Group(undefined, name, undefined, icon, undefined, parentId, color), () => {
|
||||
resetForm();
|
||||
onCreate && onCreate(newGroup);
|
||||
onCreate && onCreate();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -44,9 +44,9 @@ export default function GuestFormDialog({ groups, onCreate, onHide, guest, visib
|
||||
guest.group!.id = group;
|
||||
guest.status = status;
|
||||
|
||||
api.update(serializer, guest, (updatedGuest) => {
|
||||
api.update(serializer, guest, () => {
|
||||
resetForm();
|
||||
onCreate && onCreate(updatedGuest);
|
||||
onCreate && onCreate(guest);
|
||||
});
|
||||
} else {
|
||||
api.create(serializer, new Guest(undefined, name, undefined, status, [], groups.find((g) => g.id === group)), (newGuest)=> {
|
||||
@ -62,8 +62,8 @@ export default function GuestFormDialog({ groups, onCreate, onHide, guest, visib
|
||||
<Dialog header="Add guest" visible={visible} style={{ width: '60vw' }} onHide={onHide}>
|
||||
<div className="card flex justify-evenly py-5">
|
||||
<FloatLabel>
|
||||
<InputText id="name" className='rounded-sm' value={name} onChange={(e) => setName(e.target.value)} />
|
||||
<label htmlFor="name">Name</label>
|
||||
<InputText id="username" className='rounded-sm' value={name} onChange={(e) => setName(e.target.value)} />
|
||||
<label htmlFor="username">Username</label>
|
||||
</FloatLabel>
|
||||
<FloatLabel>
|
||||
<Dropdown id="group" className='rounded-sm min-w-32' value={group} onChange={(e) => setGroup(e.target.value)} options={
|
||||
|
@ -21,12 +21,7 @@ export default function GroupsTable({ groups, onUpdate, onEdit, onEditAffinities
|
||||
|
||||
const actions = (group: Group) => (
|
||||
<div className="flex flex-row items-center">
|
||||
<TrashIcon className='size-6 cursor-pointer' onClick={() => {
|
||||
if (window.confirm(`Are you sure you want to delete guest "${group.name}"?`)) {
|
||||
api.destroy(serializer, group, onUpdate)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<TrashIcon className='size-6 cursor-pointer' onClick={() => { api.destroy(serializer, group, onUpdate) }} />
|
||||
<PencilIcon className='size-6 cursor-pointer' onClick={() => onEdit(group)} />
|
||||
<AdjustmentsHorizontalIcon className='size-6 cursor-pointer' onClick={() => onEditAffinities(group)} />
|
||||
</div>
|
||||
|
@ -3,7 +3,7 @@
|
||||
'use client';
|
||||
|
||||
import { AbstractApi } from '@/app/api/abstract-api';
|
||||
import { Guest, GuestSerializer } from '@/app/lib/guest';
|
||||
import { Guest , GuestSerializer} from '@/app/lib/guest';
|
||||
import { PencilIcon, TrashIcon } from '@heroicons/react/24/outline';
|
||||
import clsx from 'clsx';
|
||||
import TableOfContents from '../components/table-of-contents';
|
||||
@ -48,12 +48,7 @@ export default function guestsTable({ guests, onUpdate, onEdit }: {
|
||||
</td>
|
||||
<td>
|
||||
<div className="flex flex-row items-center">
|
||||
<TrashIcon className='size-6 cursor-pointer' onClick={() => {
|
||||
if (window.confirm(`Are you sure you want to delete guest "${guest.name}"?`)) {
|
||||
api.destroy(serializer, guest, onUpdate)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<TrashIcon className='size-6 cursor-pointer' onClick={() => { api.destroy(serializer, guest, onUpdate)}} />
|
||||
<PencilIcon className='size-6 cursor-pointer' onClick={() => onEdit(guest)} />
|
||||
</div>
|
||||
</td>
|
||||
|
@ -5,12 +5,10 @@
|
||||
import { AbstractApi } from "@/app/api/abstract-api";
|
||||
import { Guest } from "@/app/lib/guest";
|
||||
import { Invitation, InvitationSerializer } from "@/app/lib/invitation";
|
||||
import { getSlug } from "@/app/lib/utils";
|
||||
import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
|
||||
import { LinkIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||
import { TrashIcon } from "@heroicons/react/24/outline";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useState } from "react";
|
||||
import { classNames } from "../components/button";
|
||||
|
||||
function InvitationCard({ invitation, allGuests, onGuestAdded, onDestroy }: {
|
||||
invitation: Invitation,
|
||||
@ -26,8 +24,6 @@ function InvitationCard({ invitation, allGuests, onGuestAdded, onDestroy }: {
|
||||
const api = new AbstractApi<Invitation>();
|
||||
const serializer = new InvitationSerializer();
|
||||
|
||||
const iconClassName = "w-5 h-5 text-white absolute top-2 opacity-0 group-hover:opacity-100 cursor-pointer";
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current) {
|
||||
return dropTargetForElements({
|
||||
@ -57,14 +53,8 @@ function InvitationCard({ invitation, allGuests, onGuestAdded, onDestroy }: {
|
||||
className="relative flex items-center justify-center w-full bg-green-800 border border-green-900 group"
|
||||
style={{ aspectRatio: "1.618 / 1" }}
|
||||
>
|
||||
<LinkIcon
|
||||
className={`${iconClassName} right-8`}
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(`https://${window.location.host}/${getSlug()}/site/invitation/${invitation.id}`);
|
||||
}}
|
||||
/>
|
||||
<TrashIcon
|
||||
className={`${iconClassName} right-2`}
|
||||
className="w-5 h-5 text-white absolute top-2 right-2 opacity-0 group-hover:opacity-100 cursor-pointer"
|
||||
onClick={() => {
|
||||
if (window.confirm("Are you sure you want to delete this invitation?")) {
|
||||
api.destroy(serializer, invitation, () => {
|
||||
@ -74,7 +64,6 @@ function InvitationCard({ invitation, allGuests, onGuestAdded, onDestroy }: {
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
{guests.length === 0 ? (
|
||||
<p className="text-center text-yellow-500 text-lg italic">
|
||||
(empty invitation)
|
||||
@ -145,12 +134,6 @@ export default function InvitationsBoard({ guests, invitations: originalInvitati
|
||||
});
|
||||
}
|
||||
|
||||
function handleDownloadQrCodes() {
|
||||
api.getAllPdf(serializer, () => {
|
||||
console.log("QR codes downloaded");
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-screen">
|
||||
{/* Left Column: Guests */}
|
||||
@ -171,18 +154,11 @@ export default function InvitationsBoard({ guests, invitations: originalInvitati
|
||||
|
||||
<button
|
||||
onClick={handleCreateInvitation}
|
||||
className={classNames('primary')}
|
||||
className="mb-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
||||
>
|
||||
Create New Invitation
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={handleDownloadQrCodes}
|
||||
className={classNames('primary')}
|
||||
>
|
||||
Download QR codes
|
||||
</button>
|
||||
|
||||
|
||||
<div className="grid grid-cols-4 gap-6">
|
||||
|
||||
|
12
package.json
12
package.json
@ -13,12 +13,12 @@
|
||||
"@tiptap/react": "^2.14.0",
|
||||
"@tiptap/starter-kit": "^2.14.0",
|
||||
"autoprefixer": "10.4.21",
|
||||
"bcrypt": "^6.0.0",
|
||||
"bcrypt": "^5.1.1",
|
||||
"clsx": "^2.1.1",
|
||||
"dompurify": "^3.2.6",
|
||||
"next": "15.3.4",
|
||||
"next-auth": "5.0.0-beta.29",
|
||||
"postcss": "8.5.6",
|
||||
"next": "15.3.3",
|
||||
"next-auth": "5.0.0-beta.28",
|
||||
"postcss": "8.5.4",
|
||||
"primeicons": "^7.0.0",
|
||||
"primereact": "^10.8.2",
|
||||
"react": "19.0.0-rc-f38c22b244-20240704",
|
||||
@ -32,7 +32,7 @@
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.52.0",
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"@types/node": "24.0.7",
|
||||
"@types/node": "22.15.29",
|
||||
"@types/react": "18.3.23",
|
||||
"@types/react-dom": "18.3.7",
|
||||
"wait-on": "^8.0.3"
|
||||
@ -40,5 +40,5 @@
|
||||
"engines": {
|
||||
"node": ">=23.0.0"
|
||||
},
|
||||
"packageManager": "pnpm@10.12.3+sha512.467df2c586056165580ad6dfb54ceaad94c5a30f80893ebdec5a44c5aa73c205ae4a5bb9d5ed6bb84ea7c249ece786642bbb49d06a307df218d03da41c317417"
|
||||
"packageManager": "pnpm@10.11.1+sha512.e519b9f7639869dc8d5c3c5dfef73b3f091094b0a006d7317353c72b124e80e1afd429732e28705ad6bfa1ee879c1fce46c128ccebd3192101f43dd67c667912"
|
||||
}
|
||||
|
1410
pnpm-lock.yaml
generated
1410
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,82 +0,0 @@
|
||||
import { test, expect, Page } from '@playwright/test'
|
||||
import mockGroupsAPI from './mocks/groups';
|
||||
import mockGuestsAPI from './mocks/guests';
|
||||
|
||||
test('should allow CRUD on groups', async ({ page }) => {
|
||||
await mockGuestsAPI({ page });
|
||||
await mockGroupsAPI({ page });
|
||||
|
||||
await page.goto('/default/dashboard/guests');
|
||||
await page.getByRole('tab', { name: 'Groups' }).click();
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Add new' })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Reset affinities' })).toBeVisible();
|
||||
|
||||
|
||||
// List all groups
|
||||
await expect(page.getByRole('row')).toHaveCount(3); // 1 header row + 2 data rows
|
||||
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(0)).toHaveText('Name');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(1)).toHaveText('Color');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(2)).toHaveText('Confirmed');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(3)).toHaveText('Tentative');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(4)).toHaveText('Pending');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(5)).toHaveText('Declined');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(6)).toHaveText('Considered');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(7)).toHaveText('Total');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(8)).toHaveText('Actions');
|
||||
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toContainText('Pam\'s family');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(3)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(4)).toHaveText('1');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(5)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(6)).toHaveText('2');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(7)).toHaveText('3');
|
||||
await expect(page.getByRole('row').nth(1).locator('svg:visible')).toHaveCount(3);
|
||||
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(0)).toContainText('Pam\'s work');
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(2)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(3)).toHaveText('2');
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(4)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(5)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(6)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(7)).toHaveText('2');
|
||||
await expect(page.getByRole('row').nth(2).locator('svg:visible')).toHaveCount(3);
|
||||
|
||||
// Add a new group
|
||||
await page.getByRole('button', { name: 'Add new' }).click();
|
||||
const dialog = page.getByRole('dialog');
|
||||
await expect(dialog).toBeVisible();
|
||||
|
||||
await dialog.getByLabel('Name').fill("Pam's friends");
|
||||
await dialog.getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
await expect(page.getByRole('row')).toHaveCount(4); // 1 header row + 3 data rows
|
||||
|
||||
await expect(dialog).not.toBeVisible();
|
||||
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toContainText('Pam\'s friends');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(3)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(4)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(5)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(6)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(7)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(1).locator('svg:visible')).toHaveCount(3);
|
||||
|
||||
// Modify the newly added group
|
||||
await page.getByRole('row').nth(1).locator('svg').nth(2).click(); // Click edit icon
|
||||
await expect(dialog).toBeVisible();
|
||||
await expect(dialog.getByLabel('Name')).toHaveValue("Pam's friends");
|
||||
await dialog.getByLabel('Name').fill('Pam\'s best friends');
|
||||
await dialog.getByRole('button', { name: 'Update' }).click();
|
||||
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toContainText('Pam\'s best friends');
|
||||
|
||||
// Delete the newly added group
|
||||
page.on('dialog', dialog => dialog.accept());
|
||||
|
||||
await page.getByRole('row').nth(1).locator('svg').nth(1).click(); // Click delete icon
|
||||
await expect(page.getByRole('row')).toHaveCount(3); // 1 header row + 2 data rows
|
||||
});
|
@ -1,10 +1,88 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import mockGroupsAPI from './mocks/groups';
|
||||
import mockGuestsAPI from './mocks/guests';
|
||||
import { test, expect, Page } from '@playwright/test'
|
||||
import { mock } from 'node:test';
|
||||
|
||||
test('should allow CRUD on guests', async ({ page }) => {
|
||||
const mockGuestsAPI = ({ page }: { page: Page }) => {
|
||||
page.route('*/**/api/default/guests', async route => {
|
||||
if (route.request().method() === 'GET') {
|
||||
const json = [
|
||||
{
|
||||
"id": "f4a09c28-40ea-4553-90a5-96935a59cac6",
|
||||
"status": "tentative",
|
||||
"name": "Kristofer Rohan DVM",
|
||||
"group": {
|
||||
"id": "2fcb8b22-6b07-4c34-92e3-a2535dbc5b14",
|
||||
"name": "Childhood friends",
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bd585c40-0937-4cde-960a-bb23acfd6f18",
|
||||
"status": "invited",
|
||||
"name": "Olevia Quigley Jr.",
|
||||
"group": {
|
||||
"id": "da8edf26-3e1e-4cbb-b985-450c49fffe01",
|
||||
"name": "Work",
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
await route.fulfill({ json })
|
||||
} else if (route.request().method() === 'POST') {
|
||||
const json = {
|
||||
"id":"ff58aa2d-643d-4c29-be9c-50e10ae6853c",
|
||||
"name":"John Snow",
|
||||
"status":"invited",
|
||||
"group": {
|
||||
"id": "da8edf26-3e1e-4cbb-b985-450c49fffe01",
|
||||
"name": "Work",
|
||||
}
|
||||
};
|
||||
|
||||
await route.fulfill({ json });
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const mockGroupsAPI = ({ page }: { page: Page }) => {
|
||||
page.route('*/**/api/default/groups', async route => {
|
||||
const json = [
|
||||
{
|
||||
"id": "ee44ffb9-1147-4842-a378-9eaeb0f0871a",
|
||||
"name": "Pam's family",
|
||||
"icon": "pi pi-users",
|
||||
"parent_id": null,
|
||||
"color": "#ff0000",
|
||||
"attendance": {
|
||||
"total": 3,
|
||||
"considered": 2,
|
||||
"invited": 1,
|
||||
"confirmed": 0,
|
||||
"declined": 0,
|
||||
"tentative": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c8bda6ca-d8af-4bb8-b2bf-e6ec1c21b1e6",
|
||||
"name": "Pam's work",
|
||||
"icon": "pi pi-desktop",
|
||||
"parent_id": null,
|
||||
"color": "#00ff00",
|
||||
"attendance": {
|
||||
"total": 2,
|
||||
"considered": 0,
|
||||
"invited": 0,
|
||||
"confirmed": 0,
|
||||
"declined": 0,
|
||||
"tentative": 2
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
await route.fulfill({ json })
|
||||
})
|
||||
}
|
||||
|
||||
test('should display the list of guests', async ({ page }) => {
|
||||
await mockGuestsAPI({ page });
|
||||
await mockGroupsAPI({ page });
|
||||
|
||||
await page.goto('/default/dashboard/guests');
|
||||
|
||||
@ -14,63 +92,89 @@ test('should allow CRUD on guests', async ({ page }) => {
|
||||
await expect(page.getByRole('tab', { name: 'Groups' })).toBeVisible();
|
||||
await expect(page.getByRole('tab', { name: 'Invitations' })).toBeVisible();
|
||||
|
||||
// List all guests
|
||||
await expect(page.getByText('There are 2 elements in the list')).toBeVisible();
|
||||
await expect(page.getByRole('row')).toHaveCount(3); // 1 header row + 2 data rows
|
||||
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell', { name: 'Kristofer Rohan DVM' })).toBeVisible();
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell', { name: "Pam's family" })).toBeVisible();
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell', { name: 'Childhood friends' })).toBeVisible();
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell', { name: 'Tentative' })).toBeVisible();
|
||||
await expect(page.getByRole('row').nth(1).locator('svg')).toHaveCount(2);
|
||||
|
||||
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell', { name: 'Olevia Quigley Jr.' })).toBeVisible();
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell', { name: "Pam's work" })).toBeVisible();
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell', { name: 'Work' })).toBeVisible();
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell', { name: 'Invited' })).toBeVisible();
|
||||
await expect(page.getByRole('row').nth(2).locator('svg')).toHaveCount(2);
|
||||
});
|
||||
|
||||
// Add a new guest
|
||||
test('should allow creating a new guest', async ({ page }) => {
|
||||
await mockGuestsAPI({ page });
|
||||
await mockGroupsAPI({ page });
|
||||
|
||||
await page.goto('/default/dashboard/guests');
|
||||
await expect(page.getByText('There are 2 elements in the list')).toBeVisible();
|
||||
await page.getByRole('button', { name: 'Add new' }).click();
|
||||
|
||||
await page.getByRole('dialog').getByLabel('Name').fill('John Snow');
|
||||
|
||||
await page.locator('#group').click();
|
||||
await page.getByRole('option', { name: "Pam's work" }).click();
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
await page.locator('#status').click();
|
||||
await page.getByRole('option', { name: 'Invited' }).click();
|
||||
await page.keyboard.press('Tab');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Create' }).click();
|
||||
|
||||
await expect(page.getByText('There are 3 elements in the list')).toBeVisible();
|
||||
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell', { name: 'John Snow' })).toBeVisible();
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell', { name: "Pam\'s work" })).toBeVisible();
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell', { name: 'Work' })).toBeVisible();
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell', { name: 'Invited' })).toBeVisible();
|
||||
await expect(page.getByRole('row').nth(1).locator('svg')).toHaveCount(2);
|
||||
|
||||
// Edit the just-added John Snow
|
||||
await page.getByRole('row').nth(1).locator('svg').nth(1).click(); // Click edit icon
|
||||
|
||||
const dialog = page.getByRole('dialog');
|
||||
await expect(dialog.getByLabel('Name')).toHaveValue('John Snow');
|
||||
await dialog.getByLabel('Name').fill('John Fire');
|
||||
|
||||
await dialog.locator('#group').click();
|
||||
await page.getByRole('option', { name: "Pam's family" }).click();
|
||||
|
||||
await dialog.locator('#status').click();
|
||||
await page.getByRole('option', { name: 'Declined' }).click();
|
||||
|
||||
await dialog.getByRole('button', { name: 'Update' }).click();
|
||||
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell', { name: 'John Fire' })).toBeVisible();
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell', { name: 'Pam\'s Family' })).toBeVisible();
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell', { name: 'Declined' })).toBeVisible();
|
||||
|
||||
await expect(page.getByText('There are 3 elements in the list')).toBeVisible();
|
||||
|
||||
// Delete John Fire
|
||||
page.on('dialog', dialog => dialog.accept());
|
||||
await page.getByRole('row').nth(1).locator('svg').nth(0).click(); // Click delete icon
|
||||
await expect(page.getByText('There are 2 elements in the list')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should display the list of groups', async ({ page }) => {
|
||||
await mockGroupsAPI({ page });
|
||||
|
||||
await page.goto('/default/dashboard/guests');
|
||||
await page.getByRole('tab', { name: 'Groups' }).click();
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Add new' })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: 'Reset affinities' })).toBeVisible();
|
||||
|
||||
await expect(page.getByRole('row')).toHaveCount(3); // 1 header row + 2 data rows
|
||||
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(0)).toHaveText('Name');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(1)).toHaveText('Color');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(2)).toHaveText('Confirmed');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(3)).toHaveText('Tentative');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(4)).toHaveText('Pending');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(5)).toHaveText('Declined');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(6)).toHaveText('Considered');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(7)).toHaveText('Total');
|
||||
await expect(page.getByRole('row').nth(0).getByRole('columnheader').nth(8)).toHaveText('Actions');
|
||||
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(0)).toContainText('Pam\'s family');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(2)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(3)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(4)).toHaveText('1');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(5)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(6)).toHaveText('2');
|
||||
await expect(page.getByRole('row').nth(1).getByRole('cell').nth(7)).toHaveText('3');
|
||||
await expect(page.getByRole('row').nth(1).locator('svg:visible')).toHaveCount(3);
|
||||
|
||||
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(0)).toContainText('Pam\'s work');
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(2)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(3)).toHaveText('2');
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(4)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(5)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(6)).toHaveText('0');
|
||||
await expect(page.getByRole('row').nth(2).getByRole('cell').nth(7)).toHaveText('2');
|
||||
await expect(page.getByRole('row').nth(2).locator('svg:visible')).toHaveCount(3);
|
||||
|
||||
});
|
@ -1,86 +0,0 @@
|
||||
import { Page } from "@playwright/test";
|
||||
|
||||
export default async function mockGroupsAPI({ page }: { page: Page }): Promise<void> {
|
||||
page.route('*/**/api/default/groups', async route => {
|
||||
if (route.request().method() === 'GET') {
|
||||
const json = [
|
||||
{
|
||||
"id": "ee44ffb9-1147-4842-a378-9eaeb0f0871a",
|
||||
"name": "Pam's family",
|
||||
"icon": "pi pi-users",
|
||||
"parent_id": null,
|
||||
"color": "#ff0000",
|
||||
"attendance": {
|
||||
"total": 3,
|
||||
"considered": 2,
|
||||
"invited": 1,
|
||||
"confirmed": 0,
|
||||
"declined": 0,
|
||||
"tentative": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c8bda6ca-d8af-4bb8-b2bf-e6ec1c21b1e6",
|
||||
"name": "Pam's work",
|
||||
"icon": "pi pi-desktop",
|
||||
"parent_id": null,
|
||||
"color": "#00ff00",
|
||||
"attendance": {
|
||||
"total": 2,
|
||||
"considered": 0,
|
||||
"invited": 0,
|
||||
"confirmed": 0,
|
||||
"declined": 0,
|
||||
"tentative": 2
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
await route.fulfill({ json })
|
||||
} else if (route.request().method() === 'POST') {
|
||||
const json = {
|
||||
"id": "4d55bc34-6f42-4e2e-82a1-71ae32da2466",
|
||||
"name": "Pam's friends",
|
||||
"icon": "pi pi-desktop",
|
||||
"parent_id": null,
|
||||
"color": "#0000ff",
|
||||
"attendance": {
|
||||
"total": 0,
|
||||
"considered": 0,
|
||||
"invited": 0,
|
||||
"confirmed": 0,
|
||||
"declined": 0,
|
||||
"tentative": 0
|
||||
}
|
||||
}
|
||||
|
||||
await route.fulfill({ json })
|
||||
}
|
||||
})
|
||||
|
||||
page.route("*/**/api/default/groups/*", async route => {
|
||||
if (route.request().method() === 'PUT') {
|
||||
const json = {
|
||||
"id": "4d55bc34-6f42-4e2e-82a1-71ae32da2466",
|
||||
"name": "Pam's best friends",
|
||||
"icon": "pi pi-desktop",
|
||||
"parent_id": null,
|
||||
"color": "#0000ff",
|
||||
"attendance": {
|
||||
"total": 0,
|
||||
"considered": 0,
|
||||
"invited": 0,
|
||||
"confirmed": 0,
|
||||
"declined": 0,
|
||||
"tentative": 0
|
||||
}
|
||||
}
|
||||
|
||||
await route.fulfill({ json });
|
||||
} else if (route.request().method() === 'DELETE') {
|
||||
const json = {}
|
||||
|
||||
await route.fulfill({ json });
|
||||
}
|
||||
});
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
import { Page } from "@playwright/test";
|
||||
|
||||
export default async function mockGuestsAPI({ page }: { page: Page }): Promise<void> {
|
||||
page.route('*/**/api/default/guests', async route => {
|
||||
if (route.request().method() === 'GET') {
|
||||
const json = [
|
||||
{
|
||||
"id": "f4a09c28-40ea-4553-90a5-96935a59cac6",
|
||||
"status": "tentative",
|
||||
"name": "Kristofer Rohan DVM",
|
||||
"group": {
|
||||
"id": "ee44ffb9-1147-4842-a378-9eaeb0f0871a",
|
||||
"name": "Pam's family",
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bd585c40-0937-4cde-960a-bb23acfd6f18",
|
||||
"status": "invited",
|
||||
"name": "Olevia Quigley Jr.",
|
||||
"group": {
|
||||
"id": "c8bda6ca-d8af-4bb8-b2bf-e6ec1c21b1e6",
|
||||
"name": "Pam's work",
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
await route.fulfill({ json })
|
||||
} else if (route.request().method() === 'POST') {
|
||||
const json = {
|
||||
"id": "ff58aa2d-643d-4c29-be9c-50e10ae6853c",
|
||||
"name": "John Snow",
|
||||
"status": "invited",
|
||||
"group": {
|
||||
"id": "c8bda6ca-d8af-4bb8-b2bf-e6ec1c21b1e6",
|
||||
"name": "Pam's work",
|
||||
}
|
||||
};
|
||||
|
||||
await route.fulfill({ json });
|
||||
}
|
||||
})
|
||||
|
||||
page.route("*/**/api/default/guests/*", async route => {
|
||||
if (route.request().method() === 'PUT') {
|
||||
const json = {
|
||||
"id": "ff58aa2d-643d-4c29-be9c-50e10ae6853c",
|
||||
"name": "John Fire",
|
||||
"status": "declined",
|
||||
"group": {
|
||||
"id": "ee44ffb9-1147-4842-a378-9eaeb0f0871a",
|
||||
"name": "Pam's family",
|
||||
}
|
||||
}
|
||||
await route.fulfill({ json });
|
||||
|
||||
} else if (route.request().method() === 'DELETE') {
|
||||
const json = {}
|
||||
await route.fulfill({ json });
|
||||
}
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user