/* 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 { 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 { useEffect, useRef } from "react"; import { useState } from "react"; function InvitationCard({ invitation, allGuests, onGuestAdded, onDestroy }: { invitation: Invitation, allGuests: Guest[], onGuestAdded: (guest: Guest) => void, onDestroy: (invitation: Invitation) => void } ) { const [guests, setGuests] = useState(invitation.guests); const ref = useRef(null); const api = new AbstractApi(); 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({ element: ref.current, onDrop: (data) => { const guestId = data.source.element.dataset.guestId; if (guestId) { const guestToAdd = allGuests.find((guest) => guest.id === guestId); if (guestToAdd) { setGuests((prevGuests) => [...prevGuests, guestToAdd]); invitation.guests.push(guestToAdd); api.update(serializer, invitation, () => { onGuestAdded(guestToAdd); }); } } }, }); } }, []); return (
{ navigator.clipboard.writeText(`https://${window.location.host}/${getSlug()}/invitations/${invitation.id}`); }} /> { if (window.confirm("Are you sure you want to delete this invitation?")) { api.destroy(serializer, invitation, () => { onDestroy(invitation); }); } }} /> {guests.length === 0 ? (

(empty invitation)

) : (
    {guests.map((guest) => (
  • {guest.name}
  • ))}
)}
) } function GuestCard(guest: Guest) { const ref = useRef(null); useEffect(() => { if (ref.current) { return draggable({ element: ref.current, }); } }, [guest.id]); return (

{guest.name}

) } export default function InvitationsBoard({ guests, invitations: originalInvitations }: { guests: Array, invitations: Array }) { const api = new AbstractApi(); const serializer = new InvitationSerializer(); const [invitations, setInvitations] = useState(originalInvitations); const [unassignedGuests, setUnassignedGuests] = useState( guests.filter( (guest) => guest.status !== 'considered' && !invitations.some((invitation) => invitation.guests.some((invitedGuest) => invitedGuest.id === guest.id) ) ) ); // Sort invitations to display those without guests at the top const sortedInvitations = [...invitations].sort((a, b) => { if (a.guests.length === 0 && b.guests.length > 0) return -1; if (a.guests.length > 0 && b.guests.length === 0) return 1; return 0; }); function handleCreateInvitation() { api.create(serializer, new Invitation(), (createdInvitation) => { setInvitations((prevInvitations) => [createdInvitation, ...prevInvitations]); }); } return (
{/* Left Column: Guests */}

{unassignedGuests.length} guests without invitation

{unassignedGuests.map((guest) => ( ))}
{/* Right Column: Invitations */}

{invitations.length} invitations

{sortedInvitations.map((invitation) => ( { setUnassignedGuests((prevUnassignedGuests) => prevUnassignedGuests.filter(g => g.id !== guestAdded.id)); }} onDestroy={(invitationToDestroy: Invitation) => { setInvitations((prevInvitations) => prevInvitations.filter(i => i.id !== invitationToDestroy.id)); setUnassignedGuests((prevUnassignedGuests) => [ ...prevUnassignedGuests, ...invitationToDestroy.guests ]); }} /> ))}
); }