Compare commits
2 Commits
main
...
invitation
Author | SHA1 | Date | |
---|---|---|---|
42d1de933f | |||
e23997e39a |
109
app/[slug]/site/invitation/[id]/page.tsx
Normal file
109
app/[slug]/site/invitation/[id]/page.tsx
Normal file
@ -0,0 +1,109 @@
|
||||
/* Copyright (C) 2024-2025 LibreWeddingPlanner contributors*/
|
||||
|
||||
'use client';
|
||||
|
||||
import { AbstractApi } from '@/app/api/abstract-api';
|
||||
import { Guest } from '@/app/lib/guest';
|
||||
import { Invitation, InvitationSerializer } from '@/app/lib/invitation';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { Dropdown } from 'primereact/dropdown';
|
||||
import { TabPanel, TabView } from 'primereact/tabview';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
|
||||
function GuestForm({ guest }: { guest: Guest }) {
|
||||
const [formResponses, setFormResponses] = useState<Guest['formResponses']>(guest.formResponses || {});
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div>
|
||||
<label className="block font-medium">Will you use the bus to get to the venue?</label>
|
||||
<Dropdown
|
||||
value={formResponses?.busNeeded ? 'Yes' : formResponses?.busNeeded === false ? 'No' : ''}
|
||||
options={[
|
||||
{ label: '', value: '' }, // Blank option as default
|
||||
{ label: 'Yes', value: 'Yes' },
|
||||
{ label: 'No', value: 'No' },
|
||||
]}
|
||||
onChange={(e) =>
|
||||
setFormResponses((prev) => ({
|
||||
...prev,
|
||||
busNeeded: e.value === 'Yes',
|
||||
}))
|
||||
}
|
||||
placeholder="Select an option"
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block font-medium">Dietary Restrictions</label>
|
||||
<textarea
|
||||
value={formResponses?.dietaryRestrictions}
|
||||
onChange={(e) =>
|
||||
setFormResponses((prev) => ({
|
||||
...prev,
|
||||
dietaryRestrictions: e.target.value,
|
||||
}))
|
||||
}
|
||||
className="w-full p-2 border border-gray-300 rounded-md"
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block font-medium">Meal Preference</label>
|
||||
<Dropdown
|
||||
value={formResponses?.mealPreference}
|
||||
options={[
|
||||
{ label: '', value: '' }, // Blank option as default
|
||||
{ label: 'Meat', value: 'Meat' },
|
||||
{ label: 'Fish', value: 'Fish' },
|
||||
]}
|
||||
onChange={(e) =>
|
||||
setFormResponses((prev) => ({
|
||||
...prev,
|
||||
mealPreference: e.value,
|
||||
}))
|
||||
}
|
||||
placeholder="Select an option"
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default function Page() {
|
||||
const params = useParams<{ slug: string, id: string }>()
|
||||
|
||||
const serializer = new InvitationSerializer();
|
||||
const api = new AbstractApi<Invitation>();
|
||||
|
||||
const [invitation, setInvitation] = useState<Invitation | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
api.get(serializer, params.id, (invitation) => {
|
||||
setInvitation(invitation);
|
||||
});
|
||||
}, [params.slug, params.id]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{invitation && (
|
||||
<div>
|
||||
<p className="mb-4 font-medium">
|
||||
{invitation.guests.length} seats have been reserved for this invitation.
|
||||
</p>
|
||||
<TabView className="border border-gray-300 rounded-md">
|
||||
{invitation.guests.map((guest) => (
|
||||
<TabPanel header={guest.name} >
|
||||
<GuestForm guest={guest} />
|
||||
</TabPanel>
|
||||
))}
|
||||
</TabView>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div >
|
||||
);
|
||||
}
|
20
app/[slug]/site/layout.tsx
Normal file
20
app/[slug]/site/layout.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen">
|
||||
{/* Full-width image */}
|
||||
<div className="w-full">
|
||||
<img
|
||||
src="/path-to-your-image.jpg"
|
||||
alt="Wedding Banner"
|
||||
className="w-full h-64 object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* Body */}
|
||||
<main className="flex-grow p-6 mx-auto" style={{ maxWidth: '60%' }}>
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -14,25 +14,72 @@ export class Guest implements Entity {
|
||||
color?: string;
|
||||
status?: GuestStatus;
|
||||
children?: Guest[];
|
||||
formResponses?: {
|
||||
busNeeded?: boolean;
|
||||
dietaryRestrictions?: string;
|
||||
mealPreference?: 'meat' | 'fish' | undefined;
|
||||
};
|
||||
|
||||
constructor(id?: string, name?: string, group_name?: string, groupId?: string, color?: string, status?: GuestStatus, children?: Guest[]) {
|
||||
constructor(
|
||||
id?: string,
|
||||
name?: string,
|
||||
groupName?: string,
|
||||
groupId?: string,
|
||||
color?: string,
|
||||
status?: GuestStatus,
|
||||
children?: Guest[],
|
||||
formResponses?: {
|
||||
busNeeded?: boolean;
|
||||
dietaryRestrictions?: string;
|
||||
mealPreference?: 'meat' | 'fish' | undefined;
|
||||
}
|
||||
) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.group_name = group_name;
|
||||
this.group_name = groupName;
|
||||
this.groupId = groupId;
|
||||
this.color = color;
|
||||
this.status = status;
|
||||
this.children = children;
|
||||
this.formResponses = formResponses;
|
||||
}
|
||||
}
|
||||
|
||||
export class GuestSerializer implements Serializable<Guest> {
|
||||
fromJson(data: any): Guest {
|
||||
return new Guest(data.id, data.name, data.group?.name, data.group?.id, data.color, data.status, data.children);
|
||||
return new Guest(
|
||||
data.id,
|
||||
data.name,
|
||||
data.group?.name,
|
||||
data.group?.id,
|
||||
data.color,
|
||||
data.status,
|
||||
data.children,
|
||||
data.form_responses
|
||||
? {
|
||||
busNeeded: data.form_responses.bus_needed,
|
||||
dietaryRestrictions: data.form_responses.dietary_restrictions,
|
||||
mealPreference: data.form_responses.meal_preference,
|
||||
}
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
toJson(guest: Guest): string {
|
||||
return JSON.stringify({ guest: { name: guest.name, status: guest.status, group_id: guest.groupId } });
|
||||
return JSON.stringify({
|
||||
guest: {
|
||||
name: guest.name,
|
||||
status: guest.status,
|
||||
group_id: guest.groupId,
|
||||
form_responses: guest.formResponses
|
||||
? {
|
||||
bus_needed: guest.formResponses.busNeeded,
|
||||
dietary_restrictions: guest.formResponses.dietaryRestrictions,
|
||||
meal_preference: guest.formResponses.mealPreference,
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
apiPath(): string {
|
||||
|
Loading…
x
Reference in New Issue
Block a user