@burdenoff/microfe-eventfullymanaged 2026.704.5 → 2026.704.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"RegistrationPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/RegistrationPage.tsx"],"sourcesContent":["import { Megaphone, Tag, TicketPlus, UserPlus, Users } from 'lucide-react';\nimport {\n Badge,\n Button,\n EmphasisPanel,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useMemo } from 'react';\n\nimport {\n useAttendeesQuery,\n useDeletePromoCodeMutation,\n usePromoCodesQuery,\n useRegistrationsQuery,\n useUpdateRegistrationMutation,\n type AttendeeGql,\n type PromoCodeGql,\n type RegistrationGql,\n} from '../../api/eventApi';\nimport { ActionMenu, type ActionMenuItem } from '../../components/ActionMenu';\nimport { Pagination, usePagination } from '../../components/Pagination';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\nconst REGISTRATION_STATUSES = ['PENDING', 'CONFIRMED', 'WAITLISTED', 'CANCELLED', 'CHECKED_IN'] as const;\n\nfunction statusVariant(status?: string | null): 'default' | 'secondary' | 'destructive' | 'outline' {\n switch ((status ?? '').toUpperCase()) {\n case 'CONFIRMED':\n case 'CHECKED_IN':\n return 'default';\n case 'CANCELLED':\n return 'destructive';\n case 'WAITLISTED':\n return 'outline';\n default:\n return 'secondary';\n }\n}\n\nfunction RegistrationsSection() {\n const pagination = usePagination({ pageSize: 10 });\n const { data, loading } = useRegistrationsQuery({ limit: pagination.limit, offset: pagination.offset });\n const [updateRegistration] = useUpdateRegistrationMutation();\n\n // Resolve attendee display names (the registrations query returns only\n // attendeeId). Pull a page of attendees to build an id -> name lookup so rows\n // read \"Ada Lovelace\" instead of a raw UUID; fall back to a short id.\n const { data: attendeesData } = useAttendeesQuery({ limit: 200, offset: 0 });\n const nameById = useMemo(() => {\n const map = new Map<string, string>();\n for (const attendee of attendeesData?.attendees.items ?? []) {\n if (attendee.id && attendee.fullName) map.set(attendee.id, attendee.fullName);\n }\n return map;\n }, [attendeesData]);\n const attendeeLabel = (attendeeId?: string | null): string | null => {\n if (!attendeeId) return null;\n return nameById.get(attendeeId) ?? `Attendee ${attendeeId.slice(0, 8)}…`;\n };\n\n const items = useMemo<RegistrationGql[]>(() => data?.registrations.items ?? [], [data]);\n const total = data?.registrations.total ?? 0;\n\n const changeStatus = async (row: RegistrationGql, status: string) => {\n const response = await updateRegistration({\n variables: {\n id: row.id,\n input: {\n eventId: row.eventId,\n attendeeId: row.attendeeId ?? null,\n ticketTypeId: row.ticketTypeId ?? null,\n quantity: row.quantity,\n currency: row.currency,\n totalAmount: row.totalAmount ?? null,\n status,\n },\n },\n });\n if (!mutationSucceeded(response, response.data?.updateRegistration?.id, 'Could not update registration')) {\n return;\n }\n notify.success('Registration updated', `Status set to ${status}`);\n };\n\n return (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {loading ? 'Loading registrations…' : `Registrations (${total})`}\n </h2>\n {items.length === 0 && !loading ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No registrations yet.\"\n description=\"Register your first attendee to start tracking sign-ups.\"\n className=\"mt-4\"\n />\n ) : (\n <ul className=\"mt-4 space-y-2\">\n {items.map((row) => {\n const statusItems: ActionMenuItem[] = REGISTRATION_STATUSES.filter(\n (candidate) => candidate !== (row.status ?? '').toUpperCase()\n ).map((candidate, index) => ({\n key: candidate,\n label: `Set ${candidate.replace('_', ' ').toLowerCase()}`,\n onSelect: () => void changeStatus(row, candidate),\n separatorBefore: index === 0,\n }));\n return (\n <li\n key={row.id}\n className=\"flex items-center justify-between gap-3 rounded-input border border-border-seam bg-bg-surface px-3 py-2\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {attendeeLabel(row.attendeeId) ?? `Registration ${row.id}`}\n </p>\n <p className=\"truncate text-xs text-text-secondary\">\n {[`Qty ${row.quantity}`, row.totalAmount ? `${row.totalAmount} ${row.currency}` : undefined]\n .filter(Boolean)\n .join(' • ')}\n </p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <Badge variant={statusVariant(row.status)}>{row.status ?? 'UNKNOWN'}</Badge>\n <ActionMenu ariaLabel=\"Registration actions\" extraItems={statusItems} />\n </div>\n </li>\n );\n })}\n </ul>\n )}\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={items.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"registrations\"\n loading={loading}\n />\n </GlassCard>\n );\n}\n\nfunction AttendeesSection() {\n const { navigate } = useEventfullyManaged();\n const pagination = usePagination({ pageSize: 10 });\n const { data, loading } = useAttendeesQuery({ limit: pagination.limit, offset: pagination.offset });\n\n const items = useMemo<AttendeeGql[]>(() => data?.attendees.items ?? [], [data]);\n const total = data?.attendees.total ?? 0;\n\n return (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <div className=\"flex items-center justify-between gap-3\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {loading ? 'Loading attendees…' : `Attendees (${total})`}\n </h2>\n <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={() => navigate('/registration/attendees/create')}>\n <UserPlus className=\"h-4 w-4\" />\n Add attendee\n </Button>\n </div>\n {items.length === 0 && !loading ? (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No attendees yet.\"\n description=\"Add attendees or register one to build your guest list.\"\n className=\"mt-4\"\n />\n ) : (\n <ul className=\"mt-4 space-y-2\">\n {items.map((row) => (\n <li\n key={row.id}\n className=\"flex items-center justify-between gap-3 rounded-input border border-border-seam bg-bg-surface px-3 py-2\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{row.fullName}</p>\n <p className=\"truncate text-xs text-text-secondary\">\n {[row.email, row.company, row.role].filter(Boolean).join(' • ')}\n </p>\n </div>\n <ActionMenu\n ariaLabel={`Actions for ${row.fullName}`}\n onView={() => navigate(`/attendees/${row.id}/profile`)}\n onEdit={() => navigate(`/registration/attendees/${row.id}/edit`)}\n />\n </li>\n ))}\n </ul>\n )}\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={items.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"attendees\"\n loading={loading}\n />\n </GlassCard>\n );\n}\n\nfunction PromoCodesSection() {\n const { navigate } = useEventfullyManaged();\n const pagination = usePagination({ pageSize: 10 });\n const { data, loading } = usePromoCodesQuery({ limit: pagination.limit, offset: pagination.offset });\n const [deletePromoCode] = useDeletePromoCodeMutation();\n\n const items = useMemo<PromoCodeGql[]>(() => data?.promoCodes.items ?? [], [data]);\n const total = data?.promoCodes.total ?? 0;\n\n const onDelete = async (row: PromoCodeGql) => {\n const response = await deletePromoCode({ variables: { id: row.id } });\n if (response.error) {\n notify.error('Could not delete promo code', response.error.message);\n throw response.error;\n }\n notify.success('Promo code deleted', row.code);\n };\n\n return (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <div className=\"flex items-center justify-between gap-3\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {loading ? 'Loading promo codes…' : `Promo Codes (${total})`}\n </h2>\n <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={() => navigate('/registration/promo-codes/create')}>\n <TicketPlus className=\"h-4 w-4\" />\n New promo code\n </Button>\n </div>\n {items.length === 0 && !loading ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No promo codes yet.\"\n description=\"Create a code to offer discounts and drive registrations.\"\n className=\"mt-4\"\n />\n ) : (\n <ul className=\"mt-4 space-y-2\">\n {items.map((row) => (\n <li\n key={row.id}\n className=\"flex items-center justify-between gap-3 rounded-input border border-border-seam bg-bg-surface px-3 py-2\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{row.code}</p>\n <p className=\"truncate text-xs text-text-secondary\">\n {row.discountType === 'PERCENT' ? `${row.discountValue}% off` : `${row.discountValue} off`}\n {row.maxUses != null ? ` • ${row.usedCount}/${row.maxUses} used` : ` • ${row.usedCount} used`}\n </p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <Badge variant={row.isActive ? 'default' : 'secondary'}>{row.isActive ? 'Active' : 'Inactive'}</Badge>\n <ActionMenu\n ariaLabel={`Actions for ${row.code}`}\n onEdit={() => navigate(`/registration/promo-codes/${row.id}/edit`)}\n onDelete={() => onDelete(row)}\n entityName={row.code}\n />\n </div>\n </li>\n ))}\n </ul>\n )}\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={items.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"promo codes\"\n loading={loading}\n />\n </GlassCard>\n );\n}\n\nexport default function RegistrationPage() {\n const { navigate } = useEventfullyManaged();\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n This is where you see who is coming. Registrations are the paid or confirmed sign-ups, attendees are the people\n behind them, and promo codes let you offer discounts. Register attendees, edit their details, change a\n registration&apos;s status, and manage discount codes — all backed by live GraphQL APIs.\n </PagePurpose>\n\n <EmphasisPanel>\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n <GradientText>Registration</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Track registrations, attendees, and promo codes with backend persistence.\n </p>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Button type=\"button\" onClick={() => navigate('/registration/register')}>\n <TicketPlus className=\"h-4 w-4\" />\n Register attendee\n </Button>\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('/registration/attendees/create')}>\n <UserPlus className=\"h-4 w-4\" />\n Add attendee\n </Button>\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('/registration/promo-codes/create')}>\n <Tag className=\"h-4 w-4\" />\n New promo code\n </Button>\n </div>\n </div>\n </EmphasisPanel>\n\n <RegistrationsSection />\n <AttendeesSection />\n <PromoCodesSection />\n\n <NextSteps\n storageKey=\"eventfullymanaged-registration\"\n title=\"Grow your registrations\"\n steps={[\n {\n id: 'view-networking',\n label: 'Set up attendee networking',\n description: 'Turn registered attendees into connections with matchmaking.',\n icon: <Users className=\"h-4 w-4\" />,\n onClick: () => navigate('/networking'),\n },\n {\n id: 'promote',\n label: 'Promote with a campaign',\n description: 'Send reminders and offers to drive more sign-ups.',\n icon: <Megaphone className=\"h-4 w-4\" />,\n onClick: () => navigate('/communication'),\n },\n {\n id: 'analyze',\n label: 'Snapshot your numbers',\n description: 'Capture an analytics snapshot to track registration trends.',\n icon: <Tag className=\"h-4 w-4\" />,\n onClick: () => navigate('/analytics'),\n },\n ]}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;AA6BA,IAAM,IAAwB;CAAC;CAAW;CAAa;CAAc;CAAa;CAAa;AAE/F,SAAS,EAAc,GAA6E;AAClG,UAAS,KAAU,IAAI,aAAa,EAApC;EACE,KAAK;EACL,KAAK,aACH,QAAO;EACT,KAAK,YACH,QAAO;EACT,KAAK,aACH,QAAO;EACT,QACE,QAAO;;;AAIb,SAAS,IAAuB;CAC9B,IAAM,IAAa,EAAc,EAAE,UAAU,IAAI,CAAC,EAC5C,EAAE,SAAM,eAAY,EAAsB;EAAE,OAAO,EAAW;EAAO,QAAQ,EAAW;EAAQ,CAAC,EACjG,CAAC,KAAsB,GAA+B,EAKtD,EAAE,MAAM,MAAkB,EAAkB;EAAE,OAAO;EAAK,QAAQ;EAAG,CAAC,EACtE,IAAW,QAAc;EAC7B,IAAM,oBAAM,IAAI,KAAqB;AACrC,OAAK,IAAM,KAAY,GAAe,UAAU,SAAS,EAAE,CACzD,CAAI,EAAS,MAAM,EAAS,YAAU,EAAI,IAAI,EAAS,IAAI,EAAS,SAAS;AAE/E,SAAO;IACN,CAAC,EAAc,CAAC,EACb,KAAiB,MAChB,IACE,EAAS,IAAI,EAAW,IAAI,YAAY,EAAW,MAAM,GAAG,EAAE,CAAC,KAD9C,MAIpB,IAAQ,QAAiC,GAAM,cAAc,SAAS,EAAE,EAAE,CAAC,EAAK,CAAC,EACjF,IAAQ,GAAM,cAAc,SAAS,GAErC,IAAe,OAAO,GAAsB,MAAmB;EACnE,IAAM,IAAW,MAAM,EAAmB,EACxC,WAAW;GACT,IAAI,EAAI;GACR,OAAO;IACL,SAAS,EAAI;IACb,YAAY,EAAI,cAAc;IAC9B,cAAc,EAAI,gBAAgB;IAClC,UAAU,EAAI;IACd,UAAU,EAAI;IACd,aAAa,EAAI,eAAe;IAChC;IACD;GACF,EACF,CAAC;AACG,IAAkB,GAAU,EAAS,MAAM,oBAAoB,IAAI,gCAAgC,IAGxG,EAAO,QAAQ,wBAAwB,iBAAiB,IAAS;;AAGnE,QACE,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAU;YAAvC;GACE,kBAAC,MAAD;IAAI,WAAU;cACX,IAAU,2BAA2B,kBAAkB,EAAM;IAC3D,CAAA;GACJ,EAAM,WAAW,KAAK,CAAC,IACtB,kBAAC,GAAD;IACE,cAAa;IACb,OAAM;IACN,aAAY;IACZ,WAAU;IACV,CAAA,GAEF,kBAAC,MAAD;IAAI,WAAU;cACX,EAAM,KAAK,MAAQ;KAClB,IAAM,IAAgC,EAAsB,QACzD,MAAc,OAAe,EAAI,UAAU,IAAI,aAAa,CAC9D,CAAC,KAAK,GAAW,OAAW;MAC3B,KAAK;MACL,OAAO,OAAO,EAAU,QAAQ,KAAK,IAAI,CAAC,aAAa;MACvD,gBAAgB,KAAK,EAAa,GAAK,EAAU;MACjD,iBAAiB,MAAU;MAC5B,EAAE;AACH,YACE,kBAAC,MAAD;MAEE,WAAU;gBAFZ,CAIE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAc,EAAI,WAAW,IAAI,gBAAgB,EAAI;QACpD,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBACV,CAAC,OAAO,EAAI,YAAY,EAAI,cAAc,GAAG,EAAI,YAAY,GAAG,EAAI,aAAa,KAAA,EAAU,CACzF,OAAO,QAAQ,CACf,KAAK,MAAM;QACZ,CAAA,CACA;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAS,EAAc,EAAI,OAAO;kBAAG,EAAI,UAAU;QAAkB,CAAA,EAC5E,kBAAC,GAAD;QAAY,WAAU;QAAuB,YAAY;QAAe,CAAA,CACpE;SACH;QAjBE,EAAI,GAiBN;MAEP;IACC,CAAA;GAEP,kBAAC,GAAD;IACE,QAAQ,EAAW;IACnB,OAAO,EAAW;IAClB,aAAa,EAAM;IACZ;IACP,QAAQ,EAAW;IACnB,QAAQ,EAAW;IACnB,WAAU;IACD;IACT,CAAA;GACQ;;;AAIhB,SAAS,IAAmB;CAC1B,IAAM,EAAE,gBAAa,GAAsB,EACrC,IAAa,EAAc,EAAE,UAAU,IAAI,CAAC,EAC5C,EAAE,SAAM,eAAY,EAAkB;EAAE,OAAO,EAAW;EAAO,QAAQ,EAAW;EAAQ,CAAC,EAE7F,IAAQ,QAA6B,GAAM,UAAU,SAAS,EAAE,EAAE,CAAC,EAAK,CAAC,EACzE,IAAQ,GAAM,UAAU,SAAS;AAEvC,QACE,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAU;YAAvC;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,IAAU,uBAAuB,cAAc,EAAM;KACnD,CAAA,EACL,kBAAC,GAAD;KAAQ,MAAK;KAAS,MAAK;KAAK,SAAQ;KAAU,eAAe,EAAS,iCAAiC;eAA3G,CACE,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA,EAAA,eAEzB;OACL;;GACL,EAAM,WAAW,KAAK,CAAC,IACtB,kBAAC,GAAD;IACE,cAAa;IACb,OAAM;IACN,aAAY;IACZ,WAAU;IACV,CAAA,GAEF,kBAAC,MAAD;IAAI,WAAU;cACX,EAAM,KAAK,MACV,kBAAC,MAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAkD,EAAI;OAAa,CAAA,EAChF,kBAAC,KAAD;OAAG,WAAU;iBACV;QAAC,EAAI;QAAO,EAAI;QAAS,EAAI;QAAK,CAAC,OAAO,QAAQ,CAAC,KAAK,MAAM;OAC7D,CAAA,CACA;SACN,kBAAC,GAAD;MACE,WAAW,eAAe,EAAI;MAC9B,cAAc,EAAS,cAAc,EAAI,GAAG,UAAU;MACtD,cAAc,EAAS,2BAA2B,EAAI,GAAG,OAAO;MAChE,CAAA,CACC;OAdE,EAAI,GAcN,CACL;IACC,CAAA;GAEP,kBAAC,GAAD;IACE,QAAQ,EAAW;IACnB,OAAO,EAAW;IAClB,aAAa,EAAM;IACZ;IACP,QAAQ,EAAW;IACnB,QAAQ,EAAW;IACnB,WAAU;IACD;IACT,CAAA;GACQ;;;AAIhB,SAAS,IAAoB;CAC3B,IAAM,EAAE,gBAAa,GAAsB,EACrC,IAAa,EAAc,EAAE,UAAU,IAAI,CAAC,EAC5C,EAAE,SAAM,eAAY,EAAmB;EAAE,OAAO,EAAW;EAAO,QAAQ,EAAW;EAAQ,CAAC,EAC9F,CAAC,KAAmB,GAA4B,EAEhD,IAAQ,QAA8B,GAAM,WAAW,SAAS,EAAE,EAAE,CAAC,EAAK,CAAC,EAC3E,IAAQ,GAAM,WAAW,SAAS,GAElC,IAAW,OAAO,MAAsB;EAC5C,IAAM,IAAW,MAAM,EAAgB,EAAE,WAAW,EAAE,IAAI,EAAI,IAAI,EAAE,CAAC;AACrE,MAAI,EAAS,MAEX,OADA,EAAO,MAAM,+BAA+B,EAAS,MAAM,QAAQ,EAC7D,EAAS;AAEjB,IAAO,QAAQ,sBAAsB,EAAI,KAAK;;AAGhD,QACE,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAU;YAAvC;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,IAAU,yBAAyB,gBAAgB,EAAM;KACvD,CAAA,EACL,kBAAC,GAAD;KAAQ,MAAK;KAAS,MAAK;KAAK,SAAQ;KAAU,eAAe,EAAS,mCAAmC;eAA7G,CACE,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA,EAAA,iBAE3B;OACL;;GACL,EAAM,WAAW,KAAK,CAAC,IACtB,kBAAC,GAAD;IACE,cAAa;IACb,OAAM;IACN,aAAY;IACZ,WAAU;IACV,CAAA,GAEF,kBAAC,MAAD;IAAI,WAAU;cACX,EAAM,KAAK,MACV,kBAAC,MAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAkD,EAAI;OAAS,CAAA,EAC5E,kBAAC,KAAD;OAAG,WAAU;iBAAb,CACG,EAAI,iBAAiB,YAAY,GAAG,EAAI,cAAc,SAAS,GAAG,EAAI,cAAc,OACpF,EAAI,WAAW,OAAmD,MAAM,EAAI,UAAU,SAAhE,MAAM,EAAI,UAAU,GAAG,EAAI,QAAQ,OACxD;SACA;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OAAO,SAAS,EAAI,WAAW,YAAY;iBAAc,EAAI,WAAW,WAAW;OAAmB,CAAA,EACtG,kBAAC,GAAD;OACE,WAAW,eAAe,EAAI;OAC9B,cAAc,EAAS,6BAA6B,EAAI,GAAG,OAAO;OAClE,gBAAgB,EAAS,EAAI;OAC7B,YAAY,EAAI;OAChB,CAAA,CACE;QACH;OAnBE,EAAI,GAmBN,CACL;IACC,CAAA;GAEP,kBAAC,GAAD;IACE,QAAQ,EAAW;IACnB,OAAO,EAAW;IAClB,aAAa,EAAM;IACZ;IACP,QAAQ,EAAW;IACnB,QAAQ,EAAW;IACnB,WAAU;IACD;IACT,CAAA;GACQ;;;AAIhB,SAAwB,IAAmB;CACzC,IAAM,EAAE,gBAAa,GAAsB;AAE3C,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD,EAAA,UAAa,8SAIC,CAAA;GAEd,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eACZ,kBAAC,GAAD,EAAA,UAAc,gBAA2B,CAAA;KACtC,CAAA,EACL,kBAAC,KAAD;KAAG,WAAU;eAAmC;KAE5C,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAQ,MAAK;OAAS,eAAe,EAAS,yBAAyB;iBAAvE,CACE,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA,EAAA,oBAE3B;;MACT,kBAAC,GAAD;OAAQ,MAAK;OAAS,SAAQ;OAAU,eAAe,EAAS,iCAAiC;iBAAjG,CACE,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA,EAAA,eAEzB;;MACT,kBAAC,GAAD;OAAQ,MAAK;OAAS,SAAQ;OAAU,eAAe,EAAS,mCAAmC;iBAAnG,CACE,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA,EAAA,iBAEpB;;MACL;OACF;OACQ,CAAA;GAEhB,kBAAC,GAAD,EAAwB,CAAA;GACxB,kBAAC,GAAD,EAAoB,CAAA;GACpB,kBAAC,GAAD,EAAqB,CAAA;GAErB,kBAAC,GAAD;IACE,YAAW;IACX,OAAM;IACN,OAAO;KACL;MACE,IAAI;MACJ,OAAO;MACP,aAAa;MACb,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;MACnC,eAAe,EAAS,cAAc;MACvC;KACD;MACE,IAAI;MACJ,OAAO;MACP,aAAa;MACb,MAAM,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;MACvC,eAAe,EAAS,iBAAiB;MAC1C;KACD;MACE,IAAI;MACJ,OAAO;MACP,aAAa;MACb,MAAM,kBAAC,GAAD,EAAK,WAAU,WAAY,CAAA;MACjC,eAAe,EAAS,aAAa;MACtC;KACF;IACD,CAAA;GACE"}
1
+ {"version":3,"file":"RegistrationPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/RegistrationPage.tsx"],"sourcesContent":["import { Megaphone, Tag, TicketPlus, UserPlus, Users } from 'lucide-react';\nimport {\n Badge,\n Button,\n EmphasisPanel,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useMemo } from 'react';\n\nimport {\n useAttendeesQuery,\n useDeleteAttendeeMutation,\n useDeletePromoCodeMutation,\n useDeleteRegistrationMutation,\n usePromoCodesQuery,\n useRegistrationsQuery,\n useUpdateRegistrationMutation,\n type AttendeeGql,\n type PromoCodeGql,\n type RegistrationGql,\n} from '../../api/eventApi';\nimport { ActionMenu, type ActionMenuItem } from '../../components/ActionMenu';\nimport { Pagination, usePagination } from '../../components/Pagination';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\nconst REGISTRATION_STATUSES = ['PENDING', 'CONFIRMED', 'WAITLISTED', 'CANCELLED', 'CHECKED_IN'] as const;\n\nfunction statusVariant(status?: string | null): 'default' | 'secondary' | 'destructive' | 'outline' {\n switch ((status ?? '').toUpperCase()) {\n case 'CONFIRMED':\n case 'CHECKED_IN':\n return 'default';\n case 'CANCELLED':\n return 'destructive';\n case 'WAITLISTED':\n return 'outline';\n default:\n return 'secondary';\n }\n}\n\nfunction RegistrationsSection() {\n const pagination = usePagination({ pageSize: 10 });\n const { data, loading } = useRegistrationsQuery({ limit: pagination.limit, offset: pagination.offset });\n const [updateRegistration] = useUpdateRegistrationMutation();\n const [deleteRegistration] = useDeleteRegistrationMutation();\n\n // Resolve attendee display names (the registrations query returns only\n // attendeeId). Pull a page of attendees to build an id -> name lookup so rows\n // read \"Ada Lovelace\" instead of a raw UUID; fall back to a short id.\n const { data: attendeesData } = useAttendeesQuery({ limit: 200, offset: 0 });\n const nameById = useMemo(() => {\n const map = new Map<string, string>();\n for (const attendee of attendeesData?.attendees.items ?? []) {\n if (attendee.id && attendee.fullName) map.set(attendee.id, attendee.fullName);\n }\n return map;\n }, [attendeesData]);\n const attendeeLabel = (attendeeId?: string | null): string | null => {\n if (!attendeeId) return null;\n return nameById.get(attendeeId) ?? `Attendee ${attendeeId.slice(0, 8)}…`;\n };\n\n const items = useMemo<RegistrationGql[]>(() => data?.registrations.items ?? [], [data]);\n const total = data?.registrations.total ?? 0;\n\n const changeStatus = async (row: RegistrationGql, status: string) => {\n const response = await updateRegistration({\n variables: {\n id: row.id,\n input: {\n eventId: row.eventId,\n attendeeId: row.attendeeId ?? null,\n ticketTypeId: row.ticketTypeId ?? null,\n quantity: row.quantity,\n currency: row.currency,\n totalAmount: row.totalAmount ?? null,\n status,\n },\n },\n });\n if (!mutationSucceeded(response, response.data?.updateRegistration?.id, 'Could not update registration')) {\n return;\n }\n notify.success('Registration updated', `Status set to ${status}`);\n };\n\n const onDelete = async (row: RegistrationGql, label: string) => {\n const response = await deleteRegistration({ variables: { id: row.id } });\n if (response.error) {\n notify.error('Could not delete registration', response.error.message);\n throw response.error;\n }\n if (!response.data?.deleteRegistration) {\n notify.error('Could not delete registration', 'The server did not confirm the change.');\n throw new Error('delete-unconfirmed');\n }\n notify.success('Registration deleted', label);\n };\n\n return (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {loading ? 'Loading registrations…' : `Registrations (${total})`}\n </h2>\n {items.length === 0 && !loading ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No registrations yet.\"\n description=\"Register your first attendee to start tracking sign-ups.\"\n className=\"mt-4\"\n />\n ) : (\n <ul className=\"mt-4 space-y-2\">\n {items.map((row) => {\n const label = attendeeLabel(row.attendeeId) ?? `Registration ${row.id}`;\n const statusItems: ActionMenuItem[] = REGISTRATION_STATUSES.filter(\n (candidate) => candidate !== (row.status ?? '').toUpperCase()\n ).map((candidate, index) => ({\n key: candidate,\n label: `Set ${candidate.replace('_', ' ').toLowerCase()}`,\n onSelect: () => void changeStatus(row, candidate),\n separatorBefore: index === 0,\n }));\n return (\n <li\n key={row.id}\n className=\"flex items-center justify-between gap-3 rounded-input border border-border-seam bg-bg-surface px-3 py-2\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{label}</p>\n <p className=\"truncate text-xs text-text-secondary\">\n {[`Qty ${row.quantity}`, row.totalAmount ? `${row.totalAmount} ${row.currency}` : undefined]\n .filter(Boolean)\n .join(' • ')}\n </p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <Badge variant={statusVariant(row.status)}>{row.status ?? 'UNKNOWN'}</Badge>\n <ActionMenu\n ariaLabel=\"Registration actions\"\n entityName={label}\n deleteLabel=\"Delete registration\"\n extraItems={statusItems}\n onDelete={() => onDelete(row, label)}\n />\n </div>\n </li>\n );\n })}\n </ul>\n )}\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={items.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"registrations\"\n loading={loading}\n />\n </GlassCard>\n );\n}\n\nfunction AttendeesSection() {\n const { navigate } = useEventfullyManaged();\n const pagination = usePagination({ pageSize: 10 });\n const { data, loading } = useAttendeesQuery({ limit: pagination.limit, offset: pagination.offset });\n const [deleteAttendee] = useDeleteAttendeeMutation();\n\n const items = useMemo<AttendeeGql[]>(() => data?.attendees.items ?? [], [data]);\n const total = data?.attendees.total ?? 0;\n\n const onDelete = async (row: AttendeeGql) => {\n const response = await deleteAttendee({ variables: { id: row.id } });\n if (response.error) {\n notify.error('Could not delete attendee', response.error.message);\n throw response.error;\n }\n if (!response.data?.deleteAttendee) {\n notify.error('Could not delete attendee', 'The server did not confirm the change.');\n throw new Error('delete-unconfirmed');\n }\n notify.success('Attendee deleted', row.fullName);\n };\n\n return (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <div className=\"flex items-center justify-between gap-3\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {loading ? 'Loading attendees…' : `Attendees (${total})`}\n </h2>\n <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={() => navigate('/registration/attendees/create')}>\n <UserPlus className=\"h-4 w-4\" />\n Add attendee\n </Button>\n </div>\n {items.length === 0 && !loading ? (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No attendees yet.\"\n description=\"Add attendees or register one to build your guest list.\"\n className=\"mt-4\"\n />\n ) : (\n <ul className=\"mt-4 space-y-2\">\n {items.map((row) => (\n <li\n key={row.id}\n className=\"flex items-center justify-between gap-3 rounded-input border border-border-seam bg-bg-surface px-3 py-2\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{row.fullName}</p>\n <p className=\"truncate text-xs text-text-secondary\">\n {[row.email, row.company, row.role].filter(Boolean).join(' • ')}\n </p>\n </div>\n <ActionMenu\n ariaLabel={`Actions for ${row.fullName}`}\n entityName={row.fullName}\n deleteLabel=\"Delete attendee\"\n onView={() => navigate(`/attendees/${row.id}/profile`)}\n onEdit={() => navigate(`/registration/attendees/${row.id}/edit`)}\n onDelete={() => onDelete(row)}\n />\n </li>\n ))}\n </ul>\n )}\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={items.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"attendees\"\n loading={loading}\n />\n </GlassCard>\n );\n}\n\nfunction PromoCodesSection() {\n const { navigate } = useEventfullyManaged();\n const pagination = usePagination({ pageSize: 10 });\n const { data, loading } = usePromoCodesQuery({ limit: pagination.limit, offset: pagination.offset });\n const [deletePromoCode] = useDeletePromoCodeMutation();\n\n const items = useMemo<PromoCodeGql[]>(() => data?.promoCodes.items ?? [], [data]);\n const total = data?.promoCodes.total ?? 0;\n\n const onDelete = async (row: PromoCodeGql) => {\n const response = await deletePromoCode({ variables: { id: row.id } });\n if (response.error) {\n notify.error('Could not delete promo code', response.error.message);\n throw response.error;\n }\n notify.success('Promo code deleted', row.code);\n };\n\n return (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <div className=\"flex items-center justify-between gap-3\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {loading ? 'Loading promo codes…' : `Promo Codes (${total})`}\n </h2>\n <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={() => navigate('/registration/promo-codes/create')}>\n <TicketPlus className=\"h-4 w-4\" />\n New promo code\n </Button>\n </div>\n {items.length === 0 && !loading ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No promo codes yet.\"\n description=\"Create a code to offer discounts and drive registrations.\"\n className=\"mt-4\"\n />\n ) : (\n <ul className=\"mt-4 space-y-2\">\n {items.map((row) => (\n <li\n key={row.id}\n className=\"flex items-center justify-between gap-3 rounded-input border border-border-seam bg-bg-surface px-3 py-2\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{row.code}</p>\n <p className=\"truncate text-xs text-text-secondary\">\n {row.discountType === 'PERCENT' ? `${row.discountValue}% off` : `${row.discountValue} off`}\n {row.maxUses != null ? ` • ${row.usedCount}/${row.maxUses} used` : ` • ${row.usedCount} used`}\n </p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <Badge variant={row.isActive ? 'default' : 'secondary'}>{row.isActive ? 'Active' : 'Inactive'}</Badge>\n <ActionMenu\n ariaLabel={`Actions for ${row.code}`}\n onEdit={() => navigate(`/registration/promo-codes/${row.id}/edit`)}\n onDelete={() => onDelete(row)}\n entityName={row.code}\n />\n </div>\n </li>\n ))}\n </ul>\n )}\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={items.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"promo codes\"\n loading={loading}\n />\n </GlassCard>\n );\n}\n\nexport default function RegistrationPage() {\n const { navigate } = useEventfullyManaged();\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n This is where you see who is coming. Registrations are the paid or confirmed sign-ups, attendees are the people\n behind them, and promo codes let you offer discounts. Register attendees, edit their details, change a\n registration&apos;s status, and manage discount codes — all backed by live GraphQL APIs.\n </PagePurpose>\n\n <EmphasisPanel>\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n <GradientText>Registration</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Track registrations, attendees, and promo codes with backend persistence.\n </p>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Button type=\"button\" onClick={() => navigate('/registration/register')}>\n <TicketPlus className=\"h-4 w-4\" />\n Register attendee\n </Button>\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('/registration/attendees/create')}>\n <UserPlus className=\"h-4 w-4\" />\n Add attendee\n </Button>\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('/registration/promo-codes/create')}>\n <Tag className=\"h-4 w-4\" />\n New promo code\n </Button>\n </div>\n </div>\n </EmphasisPanel>\n\n <RegistrationsSection />\n <AttendeesSection />\n <PromoCodesSection />\n\n <NextSteps\n storageKey=\"eventfullymanaged-registration\"\n title=\"Grow your registrations\"\n steps={[\n {\n id: 'view-networking',\n label: 'Set up attendee networking',\n description: 'Turn registered attendees into connections with matchmaking.',\n icon: <Users className=\"h-4 w-4\" />,\n onClick: () => navigate('/networking'),\n },\n {\n id: 'promote',\n label: 'Promote with a campaign',\n description: 'Send reminders and offers to drive more sign-ups.',\n icon: <Megaphone className=\"h-4 w-4\" />,\n onClick: () => navigate('/communication'),\n },\n {\n id: 'analyze',\n label: 'Snapshot your numbers',\n description: 'Capture an analytics snapshot to track registration trends.',\n icon: <Tag className=\"h-4 w-4\" />,\n onClick: () => navigate('/analytics'),\n },\n ]}\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,96 +1,105 @@
1
1
  import { useEventfullyManaged as e } from "../../context/EventfullyManagedContext.js";
2
- import { useSpeakersQuery as t } from "../../api/eventApi.js";
3
- import { ActionMenu as n } from "../../components/ActionMenu.js";
4
- import { Pagination as r, usePagination as i } from "../../components/Pagination.js";
5
- import { useMemo as a } from "react";
6
- import { jsx as o, jsxs as s } from "react/jsx-runtime";
7
- import { Mic2 as c, Plus as l } from "lucide-react";
8
- import { Button as u, GlassCard as d, IllustratedEmptyState as f, PagePurpose as p } from "@burdenoff/fe-libs/ui";
2
+ import { notify as t } from "../../lib/toast.js";
3
+ import { useDeleteSpeakerMutation as n, useSpeakersQuery as r } from "../../api/eventApi.js";
4
+ import { ActionMenu as i } from "../../components/ActionMenu.js";
5
+ import { Pagination as a, usePagination as o } from "../../components/Pagination.js";
6
+ import { useMemo as s } from "react";
7
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
8
+ import { Mic2 as u, Plus as d } from "lucide-react";
9
+ import { Button as f, GlassCard as p, IllustratedEmptyState as m, PagePurpose as h } from "@burdenoff/fe-libs/ui";
9
10
  //#region src/eventfullymanaged/pages/speakers/SpeakersPage.tsx
10
- function m() {
11
- let { navigate: m } = e(), h = i(), { data: g, loading: _ } = t({
12
- limit: h.limit,
13
- offset: h.offset
14
- }), v = g?.speakers?.total ?? 0, y = a(() => g?.speakers?.items ?? [], [g]);
15
- return /* @__PURE__ */ s("div", {
11
+ function g() {
12
+ let { navigate: g } = e(), _ = o(), { data: v, loading: y, refetch: b } = r({
13
+ limit: _.limit,
14
+ offset: _.offset
15
+ }), [x] = n(), S = v?.speakers?.total ?? 0, C = s(() => v?.speakers?.items ?? [], [v]), w = async (e) => {
16
+ let n = await x({ variables: { id: e.id } });
17
+ if (n.error) throw t.error("Could not delete speaker", n.error.message), n.error;
18
+ if (!n.data?.deleteSpeaker) throw t.error("Could not delete speaker", "The server did not confirm the change."), Error("delete-unconfirmed");
19
+ t.success("Speaker deleted", e.fullName), await b();
20
+ };
21
+ return /* @__PURE__ */ l("div", {
16
22
  className: "space-y-6 p-6",
17
23
  children: [
18
- /* @__PURE__ */ o(p, { children: "Speakers are the people who present your sessions. Keep a roster here — their name, role, company and bio — so that when you build the agenda you can assign a speaker to each session in a couple of clicks. A good speaker profile also gives attendees a reason to attend." }),
19
- /* @__PURE__ */ s("div", {
24
+ /* @__PURE__ */ c(h, { children: "Speakers are the people who present your sessions. Keep a roster here — their name, role, company and bio — so that when you build the agenda you can assign a speaker to each session in a couple of clicks. A good speaker profile also gives attendees a reason to attend." }),
25
+ /* @__PURE__ */ l("div", {
20
26
  className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
21
- children: [/* @__PURE__ */ s("div", { children: [/* @__PURE__ */ o("h1", {
27
+ children: [/* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("h1", {
22
28
  className: "text-2xl font-bold text-text-primary",
23
29
  children: "Speakers"
24
- }), /* @__PURE__ */ o("p", {
30
+ }), /* @__PURE__ */ c("p", {
25
31
  className: "mt-1 text-sm text-text-secondary",
26
32
  children: "Manage your speaker roster with live API data."
27
- })] }), /* @__PURE__ */ s(u, {
33
+ })] }), /* @__PURE__ */ l(f, {
28
34
  type: "button",
29
- onClick: () => m("/speakers/create"),
30
- children: [/* @__PURE__ */ o(l, { className: "h-4 w-4" }), "Add Speaker"]
35
+ onClick: () => g("/speakers/create"),
36
+ children: [/* @__PURE__ */ c(d, { className: "h-4 w-4" }), "Add Speaker"]
31
37
  })]
32
38
  }),
33
- _ ? /* @__PURE__ */ o(d, {
39
+ y ? /* @__PURE__ */ c(p, {
34
40
  treatment: "plain",
35
41
  className: "border-border-seam p-8 text-center text-sm text-text-secondary",
36
42
  children: "Loading speakers…"
37
- }) : y.length === 0 ? /* @__PURE__ */ o(d, {
43
+ }) : C.length === 0 ? /* @__PURE__ */ c(p, {
38
44
  treatment: "plain",
39
45
  className: "border-border-seam",
40
- children: /* @__PURE__ */ o(f, {
46
+ children: /* @__PURE__ */ c(m, {
41
47
  illustration: "empty-people",
42
48
  title: "No speakers yet.",
43
49
  description: "Add your first speaker to start building the agenda.",
44
- action: /* @__PURE__ */ s(u, {
50
+ action: /* @__PURE__ */ l(f, {
45
51
  type: "button",
46
- onClick: () => m("/speakers/create"),
47
- children: [/* @__PURE__ */ o(l, { className: "h-4 w-4" }), "Add Speaker"]
52
+ onClick: () => g("/speakers/create"),
53
+ children: [/* @__PURE__ */ c(d, { className: "h-4 w-4" }), "Add Speaker"]
48
54
  })
49
55
  })
50
- }) : /* @__PURE__ */ s("div", {
56
+ }) : /* @__PURE__ */ l("div", {
51
57
  className: "space-y-3",
52
- children: [y.map((e) => /* @__PURE__ */ o(d, {
58
+ children: [C.map((e) => /* @__PURE__ */ c(p, {
53
59
  treatment: "plain",
54
60
  className: "border-border-seam p-4",
55
- children: /* @__PURE__ */ s("div", {
61
+ children: /* @__PURE__ */ l("div", {
56
62
  className: "flex items-center justify-between gap-3",
57
- children: [/* @__PURE__ */ s("div", {
63
+ children: [/* @__PURE__ */ l("div", {
58
64
  className: "flex min-w-0 items-center gap-3",
59
- children: [/* @__PURE__ */ o("span", {
65
+ children: [/* @__PURE__ */ c("span", {
60
66
  className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-text-secondary",
61
- children: /* @__PURE__ */ o(c, { className: "h-5 w-5" })
62
- }), /* @__PURE__ */ s("div", {
67
+ children: /* @__PURE__ */ c(u, { className: "h-5 w-5" })
68
+ }), /* @__PURE__ */ l("div", {
63
69
  className: "min-w-0",
64
- children: [/* @__PURE__ */ o("button", {
70
+ children: [/* @__PURE__ */ c("button", {
65
71
  type: "button",
66
- onClick: () => m(`/speakers/${e.id}/edit`),
72
+ onClick: () => g(`/speakers/${e.id}/edit`),
67
73
  className: "rounded-button text-left font-semibold text-text-primary transition-colors hover:text-text-link focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-focus",
68
74
  children: e.fullName
69
- }), /* @__PURE__ */ o("p", {
75
+ }), /* @__PURE__ */ c("p", {
70
76
  className: "mt-0.5 truncate text-sm text-text-secondary",
71
77
  children: [e.title, e.organization].filter(Boolean).join(" • ") || "Speaker"
72
78
  })]
73
79
  })]
74
- }), /* @__PURE__ */ o(n, {
80
+ }), /* @__PURE__ */ c(i, {
75
81
  ariaLabel: `Speaker actions for ${e.fullName}`,
76
- onEdit: () => m(`/speakers/${e.id}/edit`)
82
+ entityName: e.fullName,
83
+ deleteLabel: "Delete speaker",
84
+ onEdit: () => g(`/speakers/${e.id}/edit`),
85
+ onDelete: () => w(e)
77
86
  })]
78
87
  })
79
- }, e.id)), /* @__PURE__ */ o(r, {
80
- offset: h.offset,
81
- limit: h.limit,
82
- loadedCount: y.length,
83
- total: v,
84
- onPrev: h.prevPage,
85
- onNext: h.nextPage,
88
+ }, e.id)), /* @__PURE__ */ c(a, {
89
+ offset: _.offset,
90
+ limit: _.limit,
91
+ loadedCount: C.length,
92
+ total: S,
93
+ onPrev: _.prevPage,
94
+ onNext: _.nextPage,
86
95
  itemLabel: "speakers",
87
- loading: _
96
+ loading: y
88
97
  })]
89
98
  })
90
99
  ]
91
100
  });
92
101
  }
93
102
  //#endregion
94
- export { m as default };
103
+ export { g as default };
95
104
 
96
105
  //# sourceMappingURL=SpeakersPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SpeakersPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/speakers/SpeakersPage.tsx"],"sourcesContent":["import { Mic2, Plus } from 'lucide-react';\nimport { Button, GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useMemo } from 'react';\n\nimport { useSpeakersQuery } from '../../api/eventApi';\nimport { ActionMenu } from '../../components/ActionMenu';\nimport { Pagination, usePagination } from '../../components/Pagination';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\n\ntype SpeakerRow = {\n id: string;\n fullName: string;\n title?: string | null;\n organization?: string | null;\n email?: string | null;\n};\n\nexport default function SpeakersPage() {\n const { navigate } = useEventfullyManaged();\n const pagination = usePagination();\n const { data, loading } = useSpeakersQuery({ limit: pagination.limit, offset: pagination.offset });\n\n const total = data?.speakers?.total ?? 0;\n const speakers = useMemo(() => (data?.speakers?.items ?? []) as unknown as SpeakerRow[], [data]);\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n Speakers are the people who present your sessions. Keep a roster here — their name, role, company and bio — so\n that when you build the agenda you can assign a speaker to each session in a couple of clicks. A good speaker\n profile also gives attendees a reason to attend.\n </PagePurpose>\n\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">Speakers</h1>\n <p className=\"mt-1 text-sm text-text-secondary\">Manage your speaker roster with live API data.</p>\n </div>\n <Button type=\"button\" onClick={() => navigate('/speakers/create')}>\n <Plus className=\"h-4 w-4\" />\n Add Speaker\n </Button>\n </div>\n\n {loading ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-8 text-center text-sm text-text-secondary\">\n Loading speakers…\n </GlassCard>\n ) : speakers.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No speakers yet.\"\n description=\"Add your first speaker to start building the agenda.\"\n action={\n <Button type=\"button\" onClick={() => navigate('/speakers/create')}>\n <Plus className=\"h-4 w-4\" />\n Add Speaker\n </Button>\n }\n />\n </GlassCard>\n ) : (\n <div className=\"space-y-3\">\n {speakers.map((speaker) => (\n <GlassCard key={speaker.id} treatment=\"plain\" className=\"border-border-seam p-4\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <span className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-text-secondary\">\n <Mic2 className=\"h-5 w-5\" />\n </span>\n <div className=\"min-w-0\">\n <button\n type=\"button\"\n onClick={() => navigate(`/speakers/${speaker.id}/edit`)}\n className=\"rounded-button text-left font-semibold text-text-primary transition-colors hover:text-text-link focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-focus\"\n >\n {speaker.fullName}\n </button>\n <p className=\"mt-0.5 truncate text-sm text-text-secondary\">\n {[speaker.title, speaker.organization].filter(Boolean).join(' • ') || 'Speaker'}\n </p>\n </div>\n </div>\n <ActionMenu\n ariaLabel={`Speaker actions for ${speaker.fullName}`}\n onEdit={() => navigate(`/speakers/${speaker.id}/edit`)}\n />\n </div>\n </GlassCard>\n ))}\n\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={speakers.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"speakers\"\n loading={loading}\n />\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAiBA,SAAwB,IAAe;CACrC,IAAM,EAAE,gBAAa,GAAsB,EACrC,IAAa,GAAe,EAC5B,EAAE,SAAM,eAAY,EAAiB;EAAE,OAAO,EAAW;EAAO,QAAQ,EAAW;EAAQ,CAAC,EAE5F,IAAQ,GAAM,UAAU,SAAS,GACjC,IAAW,QAAe,GAAM,UAAU,SAAS,EAAE,EAA8B,CAAC,EAAK,CAAC;AAEhG,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD,EAAA,UAAa,iRAIC,CAAA;GAEd,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAAuC;KAAa,CAAA,EAClE,kBAAC,KAAD;KAAG,WAAU;eAAmC;KAAkD,CAAA,CAC9F,EAAA,CAAA,EACN,kBAAC,GAAD;KAAQ,MAAK;KAAS,eAAe,EAAS,mBAAmB;eAAjE,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,cAErB;OACL;;GAEL,IACC,kBAAC,GAAD;IAAW,WAAU;IAAQ,WAAU;cAAiE;IAE5F,CAAA,GACV,EAAS,WAAW,IACtB,kBAAC,GAAD;IAAW,WAAU;IAAQ,WAAU;cACrC,kBAAC,GAAD;KACE,cAAa;KACb,OAAM;KACN,aAAY;KACZ,QACE,kBAAC,GAAD;MAAQ,MAAK;MAAS,eAAe,EAAS,mBAAmB;gBAAjE,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,cAErB;;KAEX,CAAA;IACQ,CAAA,GAEZ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,EAAS,KAAK,MACb,kBAAC,GAAD;KAA4B,WAAU;KAAQ,WAAU;eACtD,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBACd,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;QACvB,CAAA,EACP,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAS,aAAa,EAAQ,GAAG,OAAO;SACvD,WAAU;mBAET,EAAQ;SACF,CAAA,EACT,kBAAC,KAAD;SAAG,WAAU;mBACV,CAAC,EAAQ,OAAO,EAAQ,aAAa,CAAC,OAAO,QAAQ,CAAC,KAAK,MAAM,IAAI;SACpE,CAAA,CACA;UACF;UACN,kBAAC,GAAD;OACE,WAAW,uBAAuB,EAAQ;OAC1C,cAAc,EAAS,aAAa,EAAQ,GAAG,OAAO;OACtD,CAAA,CACE;;KACI,EAxBI,EAAQ,GAwBZ,CACZ,EAEF,kBAAC,GAAD;KACE,QAAQ,EAAW;KACnB,OAAO,EAAW;KAClB,aAAa,EAAS;KACf;KACP,QAAQ,EAAW;KACnB,QAAQ,EAAW;KACnB,WAAU;KACD;KACT,CAAA,CACE;;GAEJ"}
1
+ {"version":3,"file":"SpeakersPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/speakers/SpeakersPage.tsx"],"sourcesContent":["import { Mic2, Plus } from 'lucide-react';\nimport { Button, GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useMemo } from 'react';\n\nimport { useDeleteSpeakerMutation, useSpeakersQuery } from '../../api/eventApi';\nimport { ActionMenu } from '../../components/ActionMenu';\nimport { Pagination, usePagination } from '../../components/Pagination';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { notify } from '../../lib/toast';\n\ntype SpeakerRow = {\n id: string;\n fullName: string;\n title?: string | null;\n organization?: string | null;\n email?: string | null;\n};\n\nexport default function SpeakersPage() {\n const { navigate } = useEventfullyManaged();\n const pagination = usePagination();\n const { data, loading, refetch } = useSpeakersQuery({ limit: pagination.limit, offset: pagination.offset });\n const [deleteSpeaker] = useDeleteSpeakerMutation();\n\n const total = data?.speakers?.total ?? 0;\n const speakers = useMemo(() => (data?.speakers?.items ?? []) as unknown as SpeakerRow[], [data]);\n\n const onDelete = async (speaker: SpeakerRow) => {\n const response = await deleteSpeaker({ variables: { id: speaker.id } });\n if (response.error) {\n notify.error('Could not delete speaker', response.error.message);\n throw response.error;\n }\n if (!response.data?.deleteSpeaker) {\n notify.error('Could not delete speaker', 'The server did not confirm the change.');\n throw new Error('delete-unconfirmed');\n }\n notify.success('Speaker deleted', speaker.fullName);\n await refetch();\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n Speakers are the people who present your sessions. Keep a roster here — their name, role, company and bio — so\n that when you build the agenda you can assign a speaker to each session in a couple of clicks. A good speaker\n profile also gives attendees a reason to attend.\n </PagePurpose>\n\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">Speakers</h1>\n <p className=\"mt-1 text-sm text-text-secondary\">Manage your speaker roster with live API data.</p>\n </div>\n <Button type=\"button\" onClick={() => navigate('/speakers/create')}>\n <Plus className=\"h-4 w-4\" />\n Add Speaker\n </Button>\n </div>\n\n {loading ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-8 text-center text-sm text-text-secondary\">\n Loading speakers…\n </GlassCard>\n ) : speakers.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No speakers yet.\"\n description=\"Add your first speaker to start building the agenda.\"\n action={\n <Button type=\"button\" onClick={() => navigate('/speakers/create')}>\n <Plus className=\"h-4 w-4\" />\n Add Speaker\n </Button>\n }\n />\n </GlassCard>\n ) : (\n <div className=\"space-y-3\">\n {speakers.map((speaker) => (\n <GlassCard key={speaker.id} treatment=\"plain\" className=\"border-border-seam p-4\">\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <span className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-text-secondary\">\n <Mic2 className=\"h-5 w-5\" />\n </span>\n <div className=\"min-w-0\">\n <button\n type=\"button\"\n onClick={() => navigate(`/speakers/${speaker.id}/edit`)}\n className=\"rounded-button text-left font-semibold text-text-primary transition-colors hover:text-text-link focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-focus\"\n >\n {speaker.fullName}\n </button>\n <p className=\"mt-0.5 truncate text-sm text-text-secondary\">\n {[speaker.title, speaker.organization].filter(Boolean).join(' • ') || 'Speaker'}\n </p>\n </div>\n </div>\n <ActionMenu\n ariaLabel={`Speaker actions for ${speaker.fullName}`}\n entityName={speaker.fullName}\n deleteLabel=\"Delete speaker\"\n onEdit={() => navigate(`/speakers/${speaker.id}/edit`)}\n onDelete={() => onDelete(speaker)}\n />\n </div>\n </GlassCard>\n ))}\n\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={speakers.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"speakers\"\n loading={loading}\n />\n </div>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;AAkBA,SAAwB,IAAe;CACrC,IAAM,EAAE,gBAAa,GAAsB,EACrC,IAAa,GAAe,EAC5B,EAAE,SAAM,YAAS,eAAY,EAAiB;EAAE,OAAO,EAAW;EAAO,QAAQ,EAAW;EAAQ,CAAC,EACrG,CAAC,KAAiB,GAA0B,EAE5C,IAAQ,GAAM,UAAU,SAAS,GACjC,IAAW,QAAe,GAAM,UAAU,SAAS,EAAE,EAA8B,CAAC,EAAK,CAAC,EAE1F,IAAW,OAAO,MAAwB;EAC9C,IAAM,IAAW,MAAM,EAAc,EAAE,WAAW,EAAE,IAAI,EAAQ,IAAI,EAAE,CAAC;AACvE,MAAI,EAAS,MAEX,OADA,EAAO,MAAM,4BAA4B,EAAS,MAAM,QAAQ,EAC1D,EAAS;AAEjB,MAAI,CAAC,EAAS,MAAM,cAElB,OADA,EAAO,MAAM,4BAA4B,yCAAyC,EACxE,MAAM,qBAAqB;AAGvC,EADA,EAAO,QAAQ,mBAAmB,EAAQ,SAAS,EACnD,MAAM,GAAS;;AAGjB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD,EAAA,UAAa,iRAIC,CAAA;GAEd,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAAuC;KAAa,CAAA,EAClE,kBAAC,KAAD;KAAG,WAAU;eAAmC;KAAkD,CAAA,CAC9F,EAAA,CAAA,EACN,kBAAC,GAAD;KAAQ,MAAK;KAAS,eAAe,EAAS,mBAAmB;eAAjE,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,cAErB;OACL;;GAEL,IACC,kBAAC,GAAD;IAAW,WAAU;IAAQ,WAAU;cAAiE;IAE5F,CAAA,GACV,EAAS,WAAW,IACtB,kBAAC,GAAD;IAAW,WAAU;IAAQ,WAAU;cACrC,kBAAC,GAAD;KACE,cAAa;KACb,OAAM;KACN,aAAY;KACZ,QACE,kBAAC,GAAD;MAAQ,MAAK;MAAS,eAAe,EAAS,mBAAmB;gBAAjE,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,cAErB;;KAEX,CAAA;IACQ,CAAA,GAEZ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,EAAS,KAAK,MACb,kBAAC,GAAD;KAA4B,WAAU;KAAQ,WAAU;eACtD,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBACd,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;QACvB,CAAA,EACP,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAS,aAAa,EAAQ,GAAG,OAAO;SACvD,WAAU;mBAET,EAAQ;SACF,CAAA,EACT,kBAAC,KAAD;SAAG,WAAU;mBACV,CAAC,EAAQ,OAAO,EAAQ,aAAa,CAAC,OAAO,QAAQ,CAAC,KAAK,MAAM,IAAI;SACpE,CAAA,CACA;UACF;UACN,kBAAC,GAAD;OACE,WAAW,uBAAuB,EAAQ;OAC1C,YAAY,EAAQ;OACpB,aAAY;OACZ,cAAc,EAAS,aAAa,EAAQ,GAAG,OAAO;OACtD,gBAAgB,EAAS,EAAQ;OACjC,CAAA,CACE;;KACI,EA3BI,EAAQ,GA2BZ,CACZ,EAEF,kBAAC,GAAD;KACE,QAAQ,EAAW;KACnB,OAAO,EAAW;KAClB,aAAa,EAAS;KACf;KACP,QAAQ,EAAW;KACnB,QAAQ,EAAW;KACnB,WAAU;KACD;KACT,CAAA,CACE;;GAEJ"}
@@ -1,33 +1,34 @@
1
1
  import { useEventfullyManaged as e } from "../../context/EventfullyManagedContext.js";
2
2
  import { notify as t } from "../../lib/toast.js";
3
- import { useCreateFloorPlanMutation as n, useUpdateFloorPlanMutation as r, useVenueQuery as i } from "../../api/eventApi.js";
4
- import { describeError as a, mutationSucceeded as o } from "../../lib/mutationResult.js";
5
- import { ImageUploadField as s } from "../../components/ImageUploadField.js";
6
- import { useState as c } from "react";
7
- import { useParams as l } from "react-router-dom";
8
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
9
- import { ArrowLeft as f, Plus as p, SquareStack as m } from "lucide-react";
10
- import { Button as h, EmphasisPanel as g, GlassCard as _, IllustratedEmptyState as v, Input as y, PagePurpose as b } from "@burdenoff/fe-libs/ui";
3
+ import { useCreateFloorPlanMutation as n, useDeleteFloorPlanMutation as r, useUpdateFloorPlanMutation as i, useVenueQuery as a } from "../../api/eventApi.js";
4
+ import { ActionMenu as o } from "../../components/ActionMenu.js";
5
+ import { describeError as s, mutationSucceeded as c } from "../../lib/mutationResult.js";
6
+ import { ImageUploadField as l } from "../../components/ImageUploadField.js";
7
+ import { useState as u } from "react";
8
+ import { useParams as d } from "react-router-dom";
9
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
10
+ import { ArrowLeft as m, Plus as h, SquareStack as g } from "lucide-react";
11
+ import { Button as _, EmphasisPanel as v, GlassCard as y, IllustratedEmptyState as b, Input as x, PagePurpose as S } from "@burdenoff/fe-libs/ui";
11
12
  //#region src/eventfullymanaged/pages/venues/FloorPlansPage.tsx
12
- var x = "/EventfullyManaged/FloorPlans";
13
- function S() {
14
- let { id: S = "" } = l(), { navigate: C } = e(), { data: w, loading: T, refetch: E } = i(S), [D, { loading: O }] = n(), [k] = r(), [A, j] = c(""), [M, N] = c(""), P = w?.venue, F = w?.floorPlans?.items ?? [], I = async () => {
15
- if (!(!A.trim() || !S)) try {
16
- let e = await D({ variables: { input: {
17
- venueId: S,
18
- name: A.trim(),
19
- imageUrl: M,
13
+ var C = "/EventfullyManaged/FloorPlans";
14
+ function w() {
15
+ let { id: w = "" } = d(), { navigate: T } = e(), { data: E, loading: D, refetch: O } = a(w), [k, { loading: A }] = n(), [j] = i(), [M] = r(), [N, P] = u(""), [F, I] = u(""), L = E?.venue, R = E?.floorPlans?.items ?? [], z = async () => {
16
+ if (!(!N.trim() || !w)) try {
17
+ let e = await k({ variables: { input: {
18
+ venueId: w,
19
+ name: N.trim(),
20
+ imageUrl: F,
20
21
  dimensions: {},
21
22
  interactiveAreas: {}
22
23
  } } });
23
- if (!o(e, e.data?.createFloorPlan?.id, "Could not create floor plan")) return;
24
- j(""), N(""), t.success("Floor plan created"), await E();
24
+ if (!c(e, e.data?.createFloorPlan?.id, "Could not create floor plan")) return;
25
+ P(""), I(""), t.success("Floor plan created"), await O();
25
26
  } catch (e) {
26
- t.error("Could not create floor plan", a(e));
27
+ t.error("Could not create floor plan", s(e));
27
28
  }
28
- }, L = async (e, n) => {
29
+ }, B = async (e, n) => {
29
30
  try {
30
- let t = await k({ variables: {
31
+ let t = await j({ variables: {
31
32
  id: e.id,
32
33
  input: {
33
34
  venueId: e.venueId,
@@ -37,97 +38,110 @@ function S() {
37
38
  interactiveAreas: e.interactiveAreas ?? {}
38
39
  }
39
40
  } });
40
- if (!o(t, t.data?.updateFloorPlan?.id, "Could not attach image")) return;
41
- await E();
41
+ if (!c(t, t.data?.updateFloorPlan?.id, "Could not attach image")) return;
42
+ await O();
42
43
  } catch (e) {
43
- t.error("Could not attach image", a(e));
44
+ t.error("Could not attach image", s(e));
44
45
  }
46
+ }, V = async (e) => {
47
+ let n = await M({ variables: { id: e.id } });
48
+ if (n.error) throw t.error("Could not delete floor plan", n.error.message), n.error;
49
+ if (!n.data?.deleteFloorPlan) throw t.error("Could not delete floor plan", "The server did not confirm the change."), Error("delete-unconfirmed");
50
+ t.success("Floor plan deleted", e.name), await O();
45
51
  };
46
- return /* @__PURE__ */ d("div", {
52
+ return /* @__PURE__ */ p("div", {
47
53
  className: "space-y-6 p-6",
48
54
  children: [
49
- /* @__PURE__ */ d("div", {
55
+ /* @__PURE__ */ p("div", {
50
56
  className: "space-y-3",
51
- children: [/* @__PURE__ */ d(h, {
57
+ children: [/* @__PURE__ */ p(_, {
52
58
  type: "button",
53
59
  variant: "ghost",
54
60
  size: "sm",
55
- onClick: () => C(`/venues/${S}`),
56
- children: [/* @__PURE__ */ u(f, { className: "h-4 w-4" }), "Back to Venue"]
57
- }), /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("h1", {
61
+ onClick: () => T(`/venues/${w}`),
62
+ children: [/* @__PURE__ */ f(m, { className: "h-4 w-4" }), "Back to Venue"]
63
+ }), /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h1", {
58
64
  className: "text-2xl font-bold text-text-primary",
59
65
  children: "Floor Plans"
60
- }), /* @__PURE__ */ u("p", {
66
+ }), /* @__PURE__ */ f("p", {
61
67
  className: "mt-1 text-sm text-text-secondary",
62
- children: P?.name ?? "Venue"
68
+ children: L?.name ?? "Venue"
63
69
  })] })]
64
70
  }),
65
- /* @__PURE__ */ u(b, { children: "Floor plans are the maps of your venue — the hall layout, booth positions, stage and entrances. Add one per space so you can lay out exhibitors, plan attendee flow, and give everyone a clear picture of the room on the day." }),
66
- /* @__PURE__ */ d(g, { children: [/* @__PURE__ */ u("h2", {
71
+ /* @__PURE__ */ f(S, { children: "Floor plans are the maps of your venue — the hall layout, booth positions, stage and entrances. Add one per space so you can lay out exhibitors, plan attendee flow, and give everyone a clear picture of the room on the day." }),
72
+ /* @__PURE__ */ p(v, { children: [/* @__PURE__ */ f("h2", {
67
73
  className: "mb-3 text-sm font-semibold text-text-primary",
68
74
  children: "Create floor plan"
69
- }), /* @__PURE__ */ d("div", {
75
+ }), /* @__PURE__ */ p("div", {
70
76
  className: "space-y-4",
71
- children: [/* @__PURE__ */ d("div", {
77
+ children: [/* @__PURE__ */ p("div", {
72
78
  className: "flex flex-col gap-3 sm:flex-row",
73
- children: [/* @__PURE__ */ u(y, {
79
+ children: [/* @__PURE__ */ f(x, {
74
80
  type: "text",
75
- value: A,
76
- onChange: (e) => j(e.target.value),
81
+ value: N,
82
+ onChange: (e) => P(e.target.value),
77
83
  placeholder: "Main Hall Layout",
78
84
  "aria-label": "Floor plan name",
79
85
  className: "flex-1"
80
- }), /* @__PURE__ */ d(h, {
86
+ }), /* @__PURE__ */ p(_, {
81
87
  type: "button",
82
- onClick: I,
83
- disabled: O,
84
- children: [/* @__PURE__ */ u(p, { className: "h-4 w-4" }), O ? "Creating..." : "Create"]
88
+ onClick: z,
89
+ disabled: A,
90
+ children: [/* @__PURE__ */ f(h, { className: "h-4 w-4" }), A ? "Creating..." : "Create"]
85
91
  })]
86
- }), /* @__PURE__ */ u(s, {
92
+ }), /* @__PURE__ */ f(l, {
87
93
  label: "Upload floor plan image",
88
- folderPath: x,
89
- value: M || null,
90
- onUploaded: N,
94
+ folderPath: C,
95
+ value: F || null,
96
+ onUploaded: I,
91
97
  hint: "Optional. Attach the hall map now, or add it later from the list below."
92
98
  })]
93
99
  })] }),
94
- T ? /* @__PURE__ */ u(_, {
100
+ D ? /* @__PURE__ */ f(y, {
95
101
  treatment: "plain",
96
102
  className: "border-border-seam p-8 text-center text-sm text-text-secondary",
97
103
  children: "Loading floor plans..."
98
- }) : F.length === 0 ? /* @__PURE__ */ u(_, {
104
+ }) : R.length === 0 ? /* @__PURE__ */ f(y, {
99
105
  treatment: "plain",
100
106
  className: "border-border-seam",
101
- children: /* @__PURE__ */ u(v, {
107
+ children: /* @__PURE__ */ f(b, {
102
108
  illustration: "empty-files",
103
109
  title: "No floor plans found.",
104
110
  description: "Create one to start mapping your venue."
105
111
  })
106
- }) : /* @__PURE__ */ u("div", {
112
+ }) : /* @__PURE__ */ f("div", {
107
113
  className: "grid grid-cols-1 gap-4 md:grid-cols-2",
108
- children: F.map((e) => /* @__PURE__ */ d(_, {
114
+ children: R.map((e) => /* @__PURE__ */ p(y, {
109
115
  treatment: "metric",
110
116
  className: "space-y-3 p-4",
111
117
  children: [
112
- /* @__PURE__ */ d("div", {
113
- className: "flex items-center gap-2 text-text-primary",
114
- children: [/* @__PURE__ */ u(m, { className: "h-4 w-4 text-action-primary-bg" }), /* @__PURE__ */ u("h3", {
115
- className: "font-semibold",
116
- children: e.name
118
+ /* @__PURE__ */ p("div", {
119
+ className: "flex items-center justify-between gap-2 text-text-primary",
120
+ children: [/* @__PURE__ */ p("div", {
121
+ className: "flex min-w-0 items-center gap-2",
122
+ children: [/* @__PURE__ */ f(g, { className: "h-4 w-4 shrink-0 text-action-primary-bg" }), /* @__PURE__ */ f("h3", {
123
+ className: "truncate font-semibold",
124
+ children: e.name
125
+ })]
126
+ }), /* @__PURE__ */ f(o, {
127
+ ariaLabel: `Floor plan actions for ${e.name}`,
128
+ entityName: e.name,
129
+ deleteLabel: "Delete floor plan",
130
+ onDelete: () => V(e)
117
131
  })]
118
132
  }),
119
- e.imageUrl ? /* @__PURE__ */ u("img", {
133
+ e.imageUrl ? /* @__PURE__ */ f("img", {
120
134
  src: e.imageUrl,
121
135
  alt: `${e.name} floor plan`,
122
136
  className: "w-full rounded-card border border-border-seam object-cover"
123
- }) : /* @__PURE__ */ u(s, {
137
+ }) : /* @__PURE__ */ f(l, {
124
138
  label: "Upload image",
125
- folderPath: x,
139
+ folderPath: C,
126
140
  value: null,
127
- onUploaded: (t) => L(e, t),
141
+ onUploaded: (t) => B(e, t),
128
142
  hint: "Add the hall map for this floor plan."
129
143
  }),
130
- /* @__PURE__ */ u("p", {
144
+ /* @__PURE__ */ f("p", {
131
145
  className: "text-sm text-text-secondary",
132
146
  children: e.dimensions ? "Dimensions configured" : "No dimensions configured"
133
147
  })
@@ -138,6 +152,6 @@ function S() {
138
152
  });
139
153
  }
140
154
  //#endregion
141
- export { S as default };
155
+ export { w as default };
142
156
 
143
157
  //# sourceMappingURL=FloorPlansPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FloorPlansPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/venues/FloorPlansPage.tsx"],"sourcesContent":["import { ArrowLeft, Plus, SquareStack } from 'lucide-react';\nimport { Button, EmphasisPanel, GlassCard, IllustratedEmptyState, Input, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useState } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport { useCreateFloorPlanMutation, useUpdateFloorPlanMutation, useVenueQuery } from '../../api/eventApi';\nimport { ImageUploadField } from '../../components/ImageUploadField';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\ntype FloorPlan = {\n id: string;\n venueId: string;\n name: string;\n imageUrl?: string | null;\n dimensions?: Record<string, unknown> | null;\n interactiveAreas?: Record<string, unknown> | null;\n};\n\nconst FLOOR_PLAN_FOLDER = '/EventfullyManaged/FloorPlans';\n\nexport default function FloorPlansPage() {\n const { id = '' } = useParams<{ id: string }>();\n const { navigate } = useEventfullyManaged();\n const { data, loading, refetch } = useVenueQuery(id);\n const [createFloorPlan, { loading: creating }] = useCreateFloorPlanMutation();\n const [updateFloorPlan] = useUpdateFloorPlanMutation();\n const [name, setName] = useState('');\n const [imageUrl, setImageUrl] = useState('');\n\n const venue = data?.venue;\n const floorPlans = (data?.floorPlans?.items ?? []) as FloorPlan[];\n\n const handleCreate = async () => {\n if (!name.trim() || !id) {\n return;\n }\n try {\n const response = await createFloorPlan({\n variables: {\n input: {\n venueId: id,\n name: name.trim(),\n imageUrl,\n dimensions: {},\n interactiveAreas: {},\n },\n },\n });\n if (!mutationSucceeded(response, response.data?.createFloorPlan?.id, 'Could not create floor plan')) {\n return;\n }\n setName('');\n setImageUrl('');\n notify.success('Floor plan created');\n await refetch();\n } catch (error) {\n notify.error('Could not create floor plan', describeError(error));\n }\n };\n\n const handleAttachImage = async (plan: FloorPlan, url: string) => {\n try {\n const response = await updateFloorPlan({\n variables: {\n id: plan.id,\n input: {\n venueId: plan.venueId,\n name: plan.name,\n imageUrl: url,\n dimensions: plan.dimensions ?? {},\n interactiveAreas: plan.interactiveAreas ?? {},\n },\n },\n });\n if (!mutationSucceeded(response, response.data?.updateFloorPlan?.id, 'Could not attach image')) {\n return;\n }\n await refetch();\n } catch (error) {\n notify.error('Could not attach image', describeError(error));\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"space-y-3\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => navigate(`/venues/${id}`)}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Venue\n </Button>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">Floor Plans</h1>\n <p className=\"mt-1 text-sm text-text-secondary\">{venue?.name ?? 'Venue'}</p>\n </div>\n </div>\n\n <PagePurpose>\n Floor plans are the maps of your venue — the hall layout, booth positions, stage and entrances. Add one per\n space so you can lay out exhibitors, plan attendee flow, and give everyone a clear picture of the room on the\n day.\n </PagePurpose>\n\n <EmphasisPanel>\n <h2 className=\"mb-3 text-sm font-semibold text-text-primary\">Create floor plan</h2>\n <div className=\"space-y-4\">\n <div className=\"flex flex-col gap-3 sm:flex-row\">\n <Input\n type=\"text\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n placeholder=\"Main Hall Layout\"\n aria-label=\"Floor plan name\"\n className=\"flex-1\"\n />\n <Button type=\"button\" onClick={handleCreate} disabled={creating}>\n <Plus className=\"h-4 w-4\" />\n {creating ? 'Creating...' : 'Create'}\n </Button>\n </div>\n <ImageUploadField\n label=\"Upload floor plan image\"\n folderPath={FLOOR_PLAN_FOLDER}\n value={imageUrl || null}\n onUploaded={setImageUrl}\n hint=\"Optional. Attach the hall map now, or add it later from the list below.\"\n />\n </div>\n </EmphasisPanel>\n\n {loading ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-8 text-center text-sm text-text-secondary\">\n Loading floor plans...\n </GlassCard>\n ) : floorPlans.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-files\"\n title=\"No floor plans found.\"\n description=\"Create one to start mapping your venue.\"\n />\n </GlassCard>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n {floorPlans.map((plan) => (\n <GlassCard key={plan.id} treatment=\"metric\" className=\"space-y-3 p-4\">\n <div className=\"flex items-center gap-2 text-text-primary\">\n <SquareStack className=\"h-4 w-4 text-action-primary-bg\" />\n <h3 className=\"font-semibold\">{plan.name}</h3>\n </div>\n {plan.imageUrl ? (\n <img\n src={plan.imageUrl}\n alt={`${plan.name} floor plan`}\n className=\"w-full rounded-card border border-border-seam object-cover\"\n />\n ) : (\n <ImageUploadField\n label=\"Upload image\"\n folderPath={FLOOR_PLAN_FOLDER}\n value={null}\n onUploaded={(url) => handleAttachImage(plan, url)}\n hint=\"Add the hall map for this floor plan.\"\n />\n )}\n <p className=\"text-sm text-text-secondary\">\n {plan.dimensions ? 'Dimensions configured' : 'No dimensions configured'}\n </p>\n </GlassCard>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"FloorPlansPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/venues/FloorPlansPage.tsx"],"sourcesContent":["import { ArrowLeft, Plus, SquareStack } from 'lucide-react';\nimport { Button, EmphasisPanel, GlassCard, IllustratedEmptyState, Input, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useState } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport {\n useCreateFloorPlanMutation,\n useDeleteFloorPlanMutation,\n useUpdateFloorPlanMutation,\n useVenueQuery,\n} from '../../api/eventApi';\nimport { ActionMenu } from '../../components/ActionMenu';\nimport { ImageUploadField } from '../../components/ImageUploadField';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\ntype FloorPlan = {\n id: string;\n venueId: string;\n name: string;\n imageUrl?: string | null;\n dimensions?: Record<string, unknown> | null;\n interactiveAreas?: Record<string, unknown> | null;\n};\n\nconst FLOOR_PLAN_FOLDER = '/EventfullyManaged/FloorPlans';\n\nexport default function FloorPlansPage() {\n const { id = '' } = useParams<{ id: string }>();\n const { navigate } = useEventfullyManaged();\n const { data, loading, refetch } = useVenueQuery(id);\n const [createFloorPlan, { loading: creating }] = useCreateFloorPlanMutation();\n const [updateFloorPlan] = useUpdateFloorPlanMutation();\n const [deleteFloorPlan] = useDeleteFloorPlanMutation();\n const [name, setName] = useState('');\n const [imageUrl, setImageUrl] = useState('');\n\n const venue = data?.venue;\n const floorPlans = (data?.floorPlans?.items ?? []) as FloorPlan[];\n\n const handleCreate = async () => {\n if (!name.trim() || !id) {\n return;\n }\n try {\n const response = await createFloorPlan({\n variables: {\n input: {\n venueId: id,\n name: name.trim(),\n imageUrl,\n dimensions: {},\n interactiveAreas: {},\n },\n },\n });\n if (!mutationSucceeded(response, response.data?.createFloorPlan?.id, 'Could not create floor plan')) {\n return;\n }\n setName('');\n setImageUrl('');\n notify.success('Floor plan created');\n await refetch();\n } catch (error) {\n notify.error('Could not create floor plan', describeError(error));\n }\n };\n\n const handleAttachImage = async (plan: FloorPlan, url: string) => {\n try {\n const response = await updateFloorPlan({\n variables: {\n id: plan.id,\n input: {\n venueId: plan.venueId,\n name: plan.name,\n imageUrl: url,\n dimensions: plan.dimensions ?? {},\n interactiveAreas: plan.interactiveAreas ?? {},\n },\n },\n });\n if (!mutationSucceeded(response, response.data?.updateFloorPlan?.id, 'Could not attach image')) {\n return;\n }\n await refetch();\n } catch (error) {\n notify.error('Could not attach image', describeError(error));\n }\n };\n\n const handleDelete = async (plan: FloorPlan) => {\n const response = await deleteFloorPlan({ variables: { id: plan.id } });\n if (response.error) {\n notify.error('Could not delete floor plan', response.error.message);\n throw response.error;\n }\n if (!response.data?.deleteFloorPlan) {\n notify.error('Could not delete floor plan', 'The server did not confirm the change.');\n throw new Error('delete-unconfirmed');\n }\n notify.success('Floor plan deleted', plan.name);\n await refetch();\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"space-y-3\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => navigate(`/venues/${id}`)}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Venue\n </Button>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">Floor Plans</h1>\n <p className=\"mt-1 text-sm text-text-secondary\">{venue?.name ?? 'Venue'}</p>\n </div>\n </div>\n\n <PagePurpose>\n Floor plans are the maps of your venue — the hall layout, booth positions, stage and entrances. Add one per\n space so you can lay out exhibitors, plan attendee flow, and give everyone a clear picture of the room on the\n day.\n </PagePurpose>\n\n <EmphasisPanel>\n <h2 className=\"mb-3 text-sm font-semibold text-text-primary\">Create floor plan</h2>\n <div className=\"space-y-4\">\n <div className=\"flex flex-col gap-3 sm:flex-row\">\n <Input\n type=\"text\"\n value={name}\n onChange={(event) => setName(event.target.value)}\n placeholder=\"Main Hall Layout\"\n aria-label=\"Floor plan name\"\n className=\"flex-1\"\n />\n <Button type=\"button\" onClick={handleCreate} disabled={creating}>\n <Plus className=\"h-4 w-4\" />\n {creating ? 'Creating...' : 'Create'}\n </Button>\n </div>\n <ImageUploadField\n label=\"Upload floor plan image\"\n folderPath={FLOOR_PLAN_FOLDER}\n value={imageUrl || null}\n onUploaded={setImageUrl}\n hint=\"Optional. Attach the hall map now, or add it later from the list below.\"\n />\n </div>\n </EmphasisPanel>\n\n {loading ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-8 text-center text-sm text-text-secondary\">\n Loading floor plans...\n </GlassCard>\n ) : floorPlans.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-files\"\n title=\"No floor plans found.\"\n description=\"Create one to start mapping your venue.\"\n />\n </GlassCard>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n {floorPlans.map((plan) => (\n <GlassCard key={plan.id} treatment=\"metric\" className=\"space-y-3 p-4\">\n <div className=\"flex items-center justify-between gap-2 text-text-primary\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <SquareStack className=\"h-4 w-4 shrink-0 text-action-primary-bg\" />\n <h3 className=\"truncate font-semibold\">{plan.name}</h3>\n </div>\n <ActionMenu\n ariaLabel={`Floor plan actions for ${plan.name}`}\n entityName={plan.name}\n deleteLabel=\"Delete floor plan\"\n onDelete={() => handleDelete(plan)}\n />\n </div>\n {plan.imageUrl ? (\n <img\n src={plan.imageUrl}\n alt={`${plan.name} floor plan`}\n className=\"w-full rounded-card border border-border-seam object-cover\"\n />\n ) : (\n <ImageUploadField\n label=\"Upload image\"\n folderPath={FLOOR_PLAN_FOLDER}\n value={null}\n onUploaded={(url) => handleAttachImage(plan, url)}\n hint=\"Add the hall map for this floor plan.\"\n />\n )}\n <p className=\"text-sm text-text-secondary\">\n {plan.dimensions ? 'Dimensions configured' : 'No dimensions configured'}\n </p>\n </GlassCard>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-eventfullymanaged",
3
- "version": "2026.704.5",
3
+ "version": "2026.704.6",
4
4
  "description": "EventfullyManaged - End-to-end event management platform",
5
5
  "type": "module",
6
6
  "files": [