From 3cca58cfb09a1477b0b52fc0766d98addd2b74ac Mon Sep 17 00:00:00 2001 From: Manuel Bustillo Date: Sun, 17 Nov 2024 16:10:01 +0100 Subject: [PATCH] Define a dialog to create a new guest --- app/dashboard/guests/page.tsx | 88 ++++++++++++++++++++++++--- app/ui/components/button.tsx | 2 +- app/ui/components/creation-dialog.tsx | 60 ++++++++++++++++++ app/ui/groups/table.tsx | 34 +---------- app/ui/guests/table.tsx | 59 +++++------------- 5 files changed, 158 insertions(+), 85 deletions(-) create mode 100644 app/ui/components/creation-dialog.tsx diff --git a/app/dashboard/guests/page.tsx b/app/dashboard/guests/page.tsx index 2ce7335..67bb05f 100644 --- a/app/dashboard/guests/page.tsx +++ b/app/dashboard/guests/page.tsx @@ -1,29 +1,101 @@ /* Copyright (C) 2024 Manuel Bustillo*/ -import AffinityGroupsTree from '@/app/ui/guests/affinity-groups-tree'; -import GuestsTable from '@/app/ui/guests/table'; -import React, { Suspense } from 'react'; -import SkeletonTable from '@/app/ui/guests/skeleton-row'; -import { TabView, TabPanel } from 'primereact/tabview'; +'use client'; + +import { Group, Guest } from '@/app/lib/definitions'; +import { getCsrfToken } from '@/app/lib/utils'; +import CreationDialog from '@/app/ui/components/creation-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'; +import { Suspense, useState } from 'react'; export default function Page() { + function loadGroups() { + fetch("/api/groups") + .then((response) => response.json()) + .then((data) => { + setGroups(data.map((record: any) => { + return ({ + id: record.id, + name: record.name, + color: record.color, + attendance: { + considered: record.considered, + invited: record.invited, + confirmed: record.confirmed, + tentative: record.tentative, + declined: record.declined, + total: record.total, + } + }); + })); + setGroupsLoaded(true); + }, (error) => { + return []; + }); + } + + function loadGuests() { + fetch("/api/guests.json") + .then((response) => response.json()) + .then((data) => { + setGuests(data.map((record: any) => { + return ({ + id: record.id, + name: record.name, + status: record.status, + group_name: record.group.name, + }); + })); + setGuestsLoaded(true); + }, (error) => { + return []; + }); + }; + + const updateGuestStatus = (id: string, status: string) => { + fetch("/api/guests/bulk_update.json", + { + method: 'POST', + body: JSON.stringify({ properties: { status: status }, guest_ids: [id] }), + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': getCsrfToken(), + } + }) + .then(() => loadGuests()) + .catch((error) => console.error(error)); + } + + const [groupsLoaded, setGroupsLoaded] = useState(false); + const [groups, setGroups] = useState>([]); + + const [guestsLoaded, setGuestsLoaded] = useState(false); + const [guests, setGuests] = useState>([]); + + !groupsLoaded && loadGroups(); + !guestsLoaded && loadGuests(); + return (
-
+
+ ; }> - +
}> - +
diff --git a/app/ui/components/button.tsx b/app/ui/components/button.tsx index 76906a2..6b65ba0 100644 --- a/app/ui/components/button.tsx +++ b/app/ui/components/button.tsx @@ -5,7 +5,7 @@ import clsx from "clsx"; type ButtonColor = 'primary' | 'blue' | 'green' | 'red' | 'yellow'; export function classNames(type: ButtonColor) { - return(clsx("text-white py-1 px-2 mx-1 rounded", { + 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', diff --git a/app/ui/components/creation-dialog.tsx b/app/ui/components/creation-dialog.tsx new file mode 100644 index 0000000..ed69937 --- /dev/null +++ b/app/ui/components/creation-dialog.tsx @@ -0,0 +1,60 @@ +/* Copyright (C) 2024 Manuel Bustillo*/ + +'use client'; + +import { Group } from '@/app/lib/definitions'; +import { getCsrfToken } from '@/app/lib/utils'; +import { classNames } from '@/app/ui/components/button'; +import { Dialog } from 'primereact/dialog'; +import { Dropdown } from 'primereact/dropdown'; +import { FloatLabel } from 'primereact/floatlabel'; +import { InputText } from 'primereact/inputtext'; +import { useState } from 'react'; + +export default function CreationDialog({ groups, onCreate }: { groups: Group[], onCreate?: () => void }) { + const [visible, setVisible] = useState(false); + + const [name, setName] = useState(''); + const [group, setGroup] = useState(null); + function createGuest() { + fetch("/api/guests", { + method: 'POST', + body: JSON.stringify({ name: name, group_id: group }), + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': getCsrfToken(), + } + }) + .then((response) => response.json()) + .then((data) => { + console.log(data); + setVisible(false); + onCreate && onCreate(); + }) + .catch((error) => console.error(error)); + } + + return ( + <> + + + { if (!visible) return; setVisible(false); }}> +
+ + setName(e.target.value)} /> + + + + setGroup(e.target.value)} options={ + groups.map((group) => { + return { label: group.name, value: group.id }; + }) + } /> + + + +
+
+ + ); +} \ No newline at end of file diff --git a/app/ui/groups/table.tsx b/app/ui/groups/table.tsx index 9b4e76b..354c229 100644 --- a/app/ui/groups/table.tsx +++ b/app/ui/groups/table.tsx @@ -2,40 +2,10 @@ 'use client'; -import TableOfContents from '../components/table-of-contents'; -import React, { useState } from 'react'; import { Group } from '@/app/lib/definitions'; +import TableOfContents from '../components/table-of-contents'; -export default function GroupsTable() { - const [groups, setGroups] = useState>([]); - const [groupsLoaded, setGroupsLoaded] = useState(false); - - function loadGroups() { - fetch("/api/groups") - .then((response) => response.json()) - .then((data) => { - setGroups(data.map((record: any) => { - return ({ - id: record.id, - name: record.name, - color: record.color, - attendance: { - considered: record.considered, - invited: record.invited, - confirmed: record.confirmed, - tentative: record.tentative, - declined: record.declined, - total: record.total, - } - }); - })); - setGroupsLoaded(true); - }, (error) => { - return []; - }); - } - - !groupsLoaded && loadGroups(); +export default function GroupsTable({ groups }: { groups: Group[] }) { return ( >([]); - - function loadGuests() { - fetch("/api/guests.json") - .then((response) => response.json()) - .then((data) => { - setGuests(data.map((record: any) => { - return ({ - id: record.id, - name: record.name, - status: record.status, - group_name: record.group.name, - }); - })); - }, (error) => { - return []; - }); - }; - +export default function guestsTable({ guests, updateGuestStatus }: { guests: Guest[], updateGuestStatus: (id: string, status: string) => void }) { const handleInviteGuest = (e: React.MouseEvent) => handleGuestChange(e, 'invited'); const handleConfirmGuest = (e: React.MouseEvent) => handleGuestChange(e, 'confirmed'); const handleDeclineGuest = (e: React.MouseEvent) => handleGuestChange(e, 'declined'); const handleTentativeGuest = (e: React.MouseEvent) => handleGuestChange(e, 'tentative'); - const handleGuestUpdate = (guest:Guest) => { + const handleGuestUpdate = (guest: Guest) => { fetch(`/api/guests/${guest.id}`, - { - method: 'PUT', - body: JSON.stringify({ guest: { name: guest.name } }), - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-TOKEN': getCsrfToken(), - } - }) - .catch((error) => console.error(error)); - } - - const handleGuestChange = (e: React.MouseEvent, status:string) => { - fetch("/api/guests/bulk_update.json", { - method: 'POST', - body: JSON.stringify({ properties: { status: status }, guest_ids: [e.currentTarget.getAttribute('data-guest-id')] }), + method: 'PUT', + body: JSON.stringify({ guest: { name: guest.name } }), headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': getCsrfToken(), } }) - .then(() => loadGuests()) .catch((error) => console.error(error)); } - guests.length === 0 && loadGuests(); + + const handleGuestChange = (e: React.MouseEvent, status: string) => { + updateGuestStatus(e.currentTarget.getAttribute('data-guest-id') || '', status); + } + return ( ( - { guest.name = newName ; handleGuestUpdate(guest) }} /> + { guest.name = newName; handleGuestUpdate(guest) }} /> {guest.group_name} @@ -108,5 +79,5 @@ export default function guestsTable() { )} /> - ); + ); } -- 2.47.1