navigate(`/groups/${group.id}`)}
+ className="bg-white p-4 rounded-lg shadow cursor-pointer hover:shadow-lg transition-shadow"
+ >
{group.name}
{group.contactPersonName && (
{group.contactPersonName}
diff --git a/src/hospitality-web/src/pages/admin/GroupDetailPage.tsx b/src/hospitality-web/src/pages/admin/GroupDetailPage.tsx
new file mode 100644
index 0000000..c597ca8
--- /dev/null
+++ b/src/hospitality-web/src/pages/admin/GroupDetailPage.tsx
@@ -0,0 +1,201 @@
+import { useState } from 'react';
+import { useParams } from 'react-router-dom';
+import { useGroup, useCreatePerson, useAssignQuota } from '../../hooks/useGroups';
+import { useProducts } from '../../hooks/useProducts';
+import type { CreatePersonRequest, Person } from '../../lib/types';
+
+export default function GroupDetailPage() {
+ const { id } = useParams<{ id: string }>();
+ const { data: group, refetch } = useGroup(id!);
+ const createPerson = useCreatePerson(id!);
+ const assignQuota = useAssignQuota();
+
+ const [showPersonForm, setShowPersonForm] = useState(false);
+ const [personForm, setPersonForm] = useState
({ name: '' });
+ const [selectedPerson, setSelectedPerson] = useState(null);
+ const [quotaProductId, setQuotaProductId] = useState('');
+ const [quotaAmount, setQuotaAmount] = useState(1);
+
+ // Get event ID from group data
+ const eventId = group?.eventId || '';
+ const { data: products } = useProducts(eventId);
+
+ const handleCreatePerson = async (e: React.FormEvent) => {
+ e.preventDefault();
+ await createPerson.mutateAsync(personForm);
+ setPersonForm({ name: '' });
+ setShowPersonForm(false);
+ refetch();
+ };
+
+ const handleAssignQuota = async (personId: string) => {
+ if (!quotaProductId) return;
+
+ try {
+ await assignQuota.mutateAsync({
+ personId,
+ data: {
+ productId: quotaProductId,
+ initialAmount: quotaAmount,
+ },
+ });
+ alert('Quota assigned successfully!');
+ setQuotaProductId('');
+ setQuotaAmount(1);
+ refetch();
+ } catch (error) {
+ alert('Failed to assign quota');
+ }
+ };
+
+ if (!group) return Loading...
;
+
+ return (
+
+
+
{group.name}
+ {group.contactPersonName && (
+
Contact: {group.contactPersonName}
+ )}
+ {group.contactEmail && (
+
{group.contactEmail}
+ )}
+
+
+
+
People ({group.people?.length || 0})
+
+
+
+ {showPersonForm && (
+
+ )}
+
+
+ {group.people?.map((person) => (
+
+
+
+
{person.name}
+ {person.email &&
{person.email}
}
+ {person.phoneNumber &&
{person.phoneNumber}
}
+
QR: {person.qrCode}
+
+
+
+
+ {person.quotas && person.quotas.length > 0 && (
+
+
Current Quotas
+
+ {person.quotas.map((quota) => (
+
+ {quota.productName}
+ {quota.remainingAmount}/{quota.initialAmount}
+
+ ))}
+
+
+ )}
+
+ {selectedPerson?.id === person.id && (
+
+
Assign New Quota
+
+
+ setQuotaAmount(parseInt(e.target.value))}
+ placeholder="Amount"
+ className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm"
+ />
+
+
+
+ )}
+
+ ))}
+
+
+ {(!group.people || group.people.length === 0) && !showPersonForm && (
+
+
No people in this group yet.
+
+
+ )}
+
+ );
+}