All checks were successful
		
		
	
	Check usage of free licenses / build-static-assets (pull_request) Successful in 22s
				
			Add copyright notice / copyright_notice (pull_request) Successful in 26s
				
			Build Nginx-based docker image / build-static-assets (push) Successful in 4m22s
				
			Playwright Tests / test (pull_request) Successful in 9m39s
				
			
		
			
				
	
	
		
			119 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			119 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /* 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>
 | |
|   );
 | |
| } |