@burdenoff/microfe-eventfullymanaged 2026.704.7 → 2026.710.1
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.
- package/dist/dev/main.d.ts +2 -0
- package/dist/dev/main.d.ts.map +1 -0
- package/dist/eventfullymanaged/EventfullyManagedRoot.d.ts +4 -0
- package/dist/eventfullymanaged/EventfullyManagedRoot.d.ts.map +1 -0
- package/dist/eventfullymanaged/EventfullyManagedRoutes.d.ts +2 -0
- package/dist/eventfullymanaged/EventfullyManagedRoutes.d.ts.map +1 -0
- package/dist/eventfullymanaged/api/eventApi.d.ts +1185 -0
- package/dist/eventfullymanaged/api/eventApi.d.ts.map +1 -0
- package/dist/eventfullymanaged/components/ActionMenu.d.ts +45 -0
- package/dist/eventfullymanaged/components/ActionMenu.d.ts.map +1 -0
- package/dist/eventfullymanaged/components/EntityFormShell.d.ts +27 -0
- package/dist/eventfullymanaged/components/EntityFormShell.d.ts.map +1 -0
- package/dist/eventfullymanaged/components/EntityTagsField.d.ts +19 -0
- package/dist/eventfullymanaged/components/EntityTagsField.d.ts.map +1 -0
- package/dist/eventfullymanaged/components/EventCharts.d.ts +44 -0
- package/dist/eventfullymanaged/components/EventCharts.d.ts.map +1 -0
- package/dist/eventfullymanaged/components/EventCharts.js.map +1 -1
- package/dist/eventfullymanaged/components/ImageUploadField.d.ts +24 -0
- package/dist/eventfullymanaged/components/ImageUploadField.d.ts.map +1 -0
- package/dist/eventfullymanaged/components/PageLayout.d.ts +14 -0
- package/dist/eventfullymanaged/components/PageLayout.d.ts.map +1 -0
- package/dist/eventfullymanaged/components/Pagination.d.ts +41 -0
- package/dist/eventfullymanaged/components/Pagination.d.ts.map +1 -0
- package/dist/eventfullymanaged/components/SafeLink.d.ts +30 -0
- package/dist/eventfullymanaged/components/SafeLink.d.ts.map +1 -0
- package/dist/eventfullymanaged/components/index.d.ts +13 -0
- package/dist/eventfullymanaged/components/index.d.ts.map +1 -0
- package/dist/eventfullymanaged/context/EventfullyManagedContext.d.ts +25 -0
- package/dist/eventfullymanaged/context/EventfullyManagedContext.d.ts.map +1 -0
- package/dist/eventfullymanaged/hooks/index.d.ts +3 -0
- package/dist/eventfullymanaged/hooks/index.d.ts.map +1 -0
- package/dist/eventfullymanaged/index.d.ts +27 -0
- package/dist/eventfullymanaged/index.d.ts.map +1 -0
- package/dist/eventfullymanaged/lib/format.d.ts +18 -0
- package/dist/eventfullymanaged/lib/format.d.ts.map +1 -0
- package/dist/eventfullymanaged/lib/mutationResult.d.ts +23 -0
- package/dist/eventfullymanaged/lib/mutationResult.d.ts.map +1 -0
- package/dist/eventfullymanaged/lib/toast.d.ts +22 -0
- package/dist/eventfullymanaged/lib/toast.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/dashboard/DashboardPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/dashboard/DashboardPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/dashboard/DashboardPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/events/EventDetailPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/events/EventDetailPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/events/EventDetailPage.js +1 -2
- package/dist/eventfullymanaged/pages/events/EventDetailPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/events/EventFormPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/events/EventFormPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/events/SessionFormPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/events/SessionFormPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/events/TicketTypeFormPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/events/TicketTypeFormPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/events/eventFormUtils.d.ts +40 -0
- package/dist/eventfullymanaged/pages/events/eventFormUtils.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/exhibitors/ExhibitorFormPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/exhibitors/ExhibitorFormPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/exhibitors/ExhibitorsPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/exhibitors/ExhibitorsPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/leads/LeadCaptureDialog.d.ts +23 -0
- package/dist/eventfullymanaged/pages/leads/LeadCaptureDialog.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/AnalyticsPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/modules/AnalyticsPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/AnalyticsPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/modules/AttendeeProfilePage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/modules/AttendeeProfilePage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/CommunicationPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/modules/CommunicationPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/CommunicationPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/modules/EntityListSection.d.ts +16 -0
- package/dist/eventfullymanaged/pages/modules/EntityListSection.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/EventsPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/modules/EventsPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/EventsPage.js +223 -118
- package/dist/eventfullymanaged/pages/modules/EventsPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/modules/KioskPage.d.ts +19 -0
- package/dist/eventfullymanaged/pages/modules/KioskPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/NetworkingPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/modules/NetworkingPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/OperationsHubPage.d.ts +9 -0
- package/dist/eventfullymanaged/pages/modules/OperationsHubPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/RegistrationPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/modules/RegistrationPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/RegistrationPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/modules/RoadmapPanel.d.ts +24 -0
- package/dist/eventfullymanaged/pages/modules/RoadmapPanel.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/SponsorDetailsPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/modules/SponsorDetailsPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/SponsorDetailsPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/modules/SponsorLeadsPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/modules/SponsorLeadsPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/VenueResourcesPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/modules/VenueResourcesPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/WorkflowsPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/modules/WorkflowsPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/networking/MeetingFormPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/networking/MeetingFormPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/networking/NetworkingProfileFormPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/networking/NetworkingProfileFormPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/registration/AttendeeFormPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/registration/AttendeeFormPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/registration/AttendeeFormPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/registration/PromoCodeFormPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/registration/PromoCodeFormPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/registration/RegisterAttendeePage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/registration/RegisterAttendeePage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/registration/RegisterAttendeePage.js.map +1 -1
- package/dist/eventfullymanaged/pages/speakers/SpeakerFormPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/speakers/SpeakerFormPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/speakers/SpeakersPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/speakers/SpeakersPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/speakers/SpeakersPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/sponsors/SponsorFormPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/sponsors/SponsorFormPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/sponsors/SponsorsListPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/sponsors/SponsorsListPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/sponsors/SponsorsListPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/venues/FloorPlansPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/venues/FloorPlansPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/venues/FloorPlansPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/venues/VenueDetailPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/venues/VenueDetailPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/venues/VenueDetailPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/venues/VenueFormPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/venues/VenueFormPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/venues/VenueListPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/venues/VenueListPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/store/eventfullyManagedStore.d.ts +47 -0
- package/dist/eventfullymanaged/store/eventfullyManagedStore.d.ts.map +1 -0
- package/dist/eventfullymanaged/types/index.d.ts +3649 -0
- package/dist/eventfullymanaged/types/index.d.ts.map +1 -0
- package/dist/eventfullymanaged/utils/cn.d.ts +2 -0
- package/dist/eventfullymanaged/utils/cn.d.ts.map +1 -0
- package/dist/eventfullymanaged/utils/constants.d.ts +3 -0
- package/dist/eventfullymanaged/utils/constants.d.ts.map +1 -0
- package/dist/eventfullymanaged/utils/navigation.d.ts +2 -0
- package/dist/eventfullymanaged/utils/navigation.d.ts.map +1 -0
- package/dist/generated/global-operations.d.ts +2 -0
- package/dist/generated/global-operations.d.ts.map +1 -0
- package/dist/generated/global-types.d.ts +23527 -0
- package/dist/generated/global-types.d.ts.map +1 -0
- package/dist/generated/wspace-operations.d.ts +2 -0
- package/dist/generated/wspace-operations.d.ts.map +1 -0
- package/dist/generated/wspace-types.d.ts +57851 -0
- package/dist/generated/wspace-types.d.ts.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -1
- package/dist/services/createSandboxAssistantTransport.d.ts +33 -0
- package/dist/services/createSandboxAssistantTransport.d.ts.map +1 -0
- package/dist/services/createSandboxAssistantTransport.js +19 -0
- package/dist/services/createSandboxAssistantTransport.js.map +1 -0
- package/package.json +7 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/AnalyticsPage.tsx"],"sourcesContent":["import {\n BarChart3,\n CalendarDays,\n ClipboardList,\n MapPin,\n QrCode,\n Sparkles,\n Ticket,\n Trophy,\n Users,\n} from 'lucide-react';\nimport type { LucideIcon } from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n Badge,\n Button,\n EmphasisPanel,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\n\nimport {\n CHART_SERIES_COLORS,\n ChartLegend,\n EventAreaChart,\n EventBarChart,\n EventDonutChart,\n type AreaPoint,\n type BarDatum,\n type DonutSlice,\n} from '../../components/EventCharts';\nimport { Pagination, usePagination } from '../../components/Pagination';\nimport { useDashboardSummaryQuery, useRegistrationsQuery, useUpsertAnalyticsSnapshotMutation } from '../../api/eventApi';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\n/**\n * Snapshot metrics are computed server-side at generation time and returned on\n * the `metrics` JSON field of an analytics snapshot. The shared\n * `useAnalyticsSnapshotsQuery` hook does not select `metrics`, so this page owns\n * a dedicated query (distinct operation name) that pulls the metric payload in\n * addition to the identity fields.\n */\nconst ANALYTICS_SNAPSHOTS_WITH_METRICS_QUERY = gql`\n query EventfullymanagedAnalyticsSnapshotsWithMetrics($limit: Int, $offset: Int) {\n analyticsSnapshots: eventfullyManagedAnalyticsSnapshots(limit: $limit, offset: $offset) {\n total\n items {\n id\n name\n title\n status\n metrics\n createdAt\n }\n }\n }\n`;\n\ntype SnapshotMetrics = {\n totalEvents?: number | null;\n activeEvents?: number | null;\n totalRegistrations?: number | null;\n totalAttendees?: number | null;\n totalVenues?: number | null;\n totalSponsors?: number | null;\n upcomingSessions?: number | null;\n generatedAt?: string | null;\n};\n\ntype SnapshotRow = {\n id: string;\n name?: string | null;\n title?: string | null;\n status?: string | null;\n createdAt?: string | null;\n metrics?: SnapshotMetrics | null;\n};\n\ntype SnapshotsResponse = {\n analyticsSnapshots: { total: number; items: SnapshotRow[] };\n};\n\nconst METRIC_FIELDS: { key: keyof SnapshotMetrics; label: string }[] = [\n { key: 'totalEvents', label: 'Events' },\n { key: 'activeEvents', label: 'Active' },\n { key: 'totalRegistrations', label: 'Registrations' },\n { key: 'totalAttendees', label: 'Attendees' },\n { key: 'totalVenues', label: 'Venues' },\n { key: 'totalSponsors', label: 'Sponsors' },\n { key: 'upcomingSessions', label: 'Upcoming Sessions' },\n];\n\nfunction statusVariant(status: string): 'success' | 'warning' | 'secondary' | 'info' {\n const value = status.toLowerCase();\n if (['active', 'published', 'completed'].includes(value)) return 'success';\n if (['inactive', 'cancelled', 'archived', 'expired'].includes(value)) return 'warning';\n if (['draft', 'created', 'pending', 'scheduled'].includes(value)) return 'info';\n return 'secondary';\n}\n\nfunction formatGeneratedAt(value?: string | null): string | null {\n if (!value) return null;\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? null : parsed.toLocaleString();\n}\n\nfunction shortDate(value?: string | null): string {\n if (!value) return '—';\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) return '—';\n return parsed.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n}\n\n/** Humanise a registration status token (e.g. `checked_in` → `Checked In`). */\nfunction humanizeStatus(status: string): string {\n return status\n .replace(/[_-]+/g, ' ')\n .split(' ')\n .filter(Boolean)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ');\n}\n\nfunction MetricTile({ icon: Icon, label, value }: { icon: LucideIcon; label: string; value: number }) {\n return (\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Icon className=\"h-4 w-4 text-accent-primary\" />\n <p className=\"text-xs font-medium uppercase tracking-wide\">{label}</p>\n </div>\n <p className=\"mt-2 text-2xl font-bold tabular-nums text-text-primary\">{value.toLocaleString()}</p>\n </GlassCard>\n );\n}\n\nexport default function AnalyticsPage() {\n const pagination = usePagination({ pageSize: 8 });\n const { data, loading, refetch } = useQuery<SnapshotsResponse, { limit: number; offset: number }>(\n ANALYTICS_SNAPSHOTS_WITH_METRICS_QUERY,\n { variables: { limit: pagination.limit, offset: pagination.offset } }\n );\n const summaryQuery = useDashboardSummaryQuery();\n const registrationsQuery = useRegistrationsQuery({ limit: 200, offset: 0 });\n const [upsertSnapshot, { loading: creating }] = useUpsertAnalyticsSnapshotMutation();\n const [snapshotName, setSnapshotName] = useState('overview');\n\n const snapshots = useMemo(() => data?.analyticsSnapshots.items ?? [], [data]);\n const total = data?.analyticsSnapshots.total ?? 0;\n\n const summary = summaryQuery.data?.dashboardSummary;\n const registrations = useMemo(\n () => registrationsQuery.data?.registrations.items ?? [],\n [registrationsQuery.data]\n );\n\n // Check-ins are derived from the loaded registration sample (registrations\n // carrying a `checkedInAt`) — the dashboard summary does not expose them.\n const checkInCount = useMemo(\n () => registrations.filter((row) => Boolean(row.checkedInAt)).length,\n [registrations]\n );\n\n // Registrations over time: the totalRegistrations captured on each snapshot,\n // ordered oldest → newest. Purely real, snapshot-derived — never synthesised.\n const registrationsSeries = useMemo<AreaPoint[]>(() => {\n return snapshots\n .map((snapshot) => {\n const at = snapshot.metrics?.generatedAt ?? snapshot.createdAt ?? null;\n const parsed = at ? new Date(at) : null;\n const value = typeof snapshot.metrics?.totalRegistrations === 'number' ? snapshot.metrics.totalRegistrations : null;\n return parsed && !Number.isNaN(parsed.getTime()) && value !== null\n ? { time: parsed.getTime(), label: shortDate(at), value }\n : null;\n })\n .filter((point): point is { time: number; label: string; value: number } => point !== null)\n .sort((a, b) => a.time - b.time)\n .map(({ label, value }) => ({ label, value }));\n }, [snapshots]);\n\n // Registrations by status donut, computed from the real registration list.\n const statusBreakdown = useMemo<DonutSlice[]>(() => {\n const counts = new Map<string, number>();\n for (const row of registrations) {\n const key = (row.status || 'unknown').toLowerCase();\n counts.set(key, (counts.get(key) ?? 0) + 1);\n }\n return Array.from(counts.entries())\n .sort((a, b) => b[1] - a[1])\n .map(([key, count], index) => ({\n name: humanizeStatus(key),\n value: count,\n color: CHART_SERIES_COLORS[index % CHART_SERIES_COLORS.length],\n }));\n }, [registrations]);\n\n const registrationTotal = registrations.length;\n\n // Entity-count bar chart from the real dashboard summary + derived check-ins.\n const entityCounts = useMemo<BarDatum[]>(() => {\n if (!summary) return [];\n return [\n { name: 'Events', value: summary.totalEvents },\n { name: 'Regs', value: summary.totalRegistrations },\n { name: 'Attendees', value: summary.totalAttendees },\n { name: 'Check-ins', value: checkInCount },\n { name: 'Venues', value: summary.totalVenues },\n { name: 'Sponsors', value: summary.totalSponsors },\n { name: 'Sessions', value: summary.upcomingSessions },\n ];\n }, [summary, checkInCount]);\n\n const onCreate = async () => {\n if (!snapshotName.trim()) return;\n try {\n const response = await upsertSnapshot({\n variables: {\n input: {\n name: snapshotName,\n title: snapshotName,\n status: 'created',\n metadata: {},\n },\n },\n });\n if (!mutationSucceeded(response, response.data?.upsertAnalyticsSnapshot?.id, 'Could not generate snapshot')) {\n return;\n }\n notify.success('Snapshot generated');\n await refetch();\n } catch (error) {\n notify.error('Could not generate snapshot', describeError(error));\n }\n };\n\n const summaryLoading = summaryQuery.loading && !summary;\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n Analytics gives you the live pulse of your event operations — registrations over time, how those registrations\n break down by status, and totals across every entity you manage. Generate a snapshot to freeze today's\n numbers so you can compare trends over time and report on outcomes.\n </PagePurpose>\n\n <EmphasisPanel>\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n <GradientText>Analytics</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Real-time metrics from live event operations, plus point-in-time snapshots you control.\n </p>\n </div>\n <div className=\"flex w-full flex-col gap-2 sm:w-auto sm:flex-row\">\n <Input\n value={snapshotName}\n onChange={(e) => setSnapshotName(e.target.value)}\n placeholder=\"daily-summary\"\n aria-label=\"Snapshot name\"\n className=\"sm:w-48\"\n />\n <Button type=\"button\" disabled={creating} onClick={onCreate}>\n <Sparkles className=\"h-4 w-4\" />\n {creating ? 'Generating...' : 'Generate snapshot'}\n </Button>\n </div>\n </div>\n </EmphasisPanel>\n\n <div className=\"grid grid-cols-1 gap-6 xl:grid-cols-[minmax(0,1fr)_320px]\">\n <div className=\"space-y-6\">\n {/* Live metric tiles */}\n {summaryLoading ? (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 xl:grid-cols-4\">\n {Array.from({ length: 7 }).map((_, index) => (\n <Skeleton key={index} className=\"h-[92px] rounded-card\" />\n ))}\n </div>\n ) : summary ? (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 xl:grid-cols-4\">\n <MetricTile icon={CalendarDays} label=\"Events\" value={summary.totalEvents} />\n <MetricTile icon={ClipboardList} label=\"Registrations\" value={summary.totalRegistrations} />\n <MetricTile icon={Users} label=\"Attendees\" value={summary.totalAttendees} />\n <MetricTile icon={QrCode} label=\"Check-ins\" value={checkInCount} />\n <MetricTile icon={MapPin} label=\"Venues\" value={summary.totalVenues} />\n <MetricTile icon={Trophy} label=\"Sponsors\" value={summary.totalSponsors} />\n <MetricTile icon={Ticket} label=\"Upcoming Sessions\" value={summary.upcomingSessions} />\n </div>\n ) : null}\n\n {/* Registrations over time + by status */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <GlassCard treatment=\"insight\" className=\"p-5\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-base font-semibold text-text-primary\">Registrations over time</h2>\n <span className=\"text-xs text-text-muted\">By snapshot</span>\n </div>\n <div className=\"mt-4\">\n <EventAreaChart\n data={registrationsSeries}\n emptyLabel=\"Generate a few snapshots to chart the trend\"\n />\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"insight\" className=\"p-5\">\n <h2 className=\"text-base font-semibold text-text-primary\">Registrations by status</h2>\n <div className=\"mt-4 flex flex-col items-center gap-4 sm:flex-row\">\n <div className=\"w-full sm:w-1/2\">\n <EventDonutChart\n data={statusBreakdown}\n centerValue={registrationTotal.toLocaleString()}\n centerLabel=\"Registrations\"\n emptyLabel=\"No registrations yet\"\n />\n </div>\n <div className=\"w-full sm:w-1/2\">\n <ChartLegend\n items={statusBreakdown.map((slice) => ({\n label: slice.name,\n color: slice.color ?? 'var(--color-chart-1)',\n value: slice.value.toLocaleString(),\n }))}\n />\n </div>\n </div>\n </GlassCard>\n </div>\n\n {/* Entity totals */}\n <GlassCard treatment=\"insight\" className=\"p-5\">\n <h2 className=\"text-base font-semibold text-text-primary\">Portfolio totals</h2>\n <p className=\"mt-0.5 text-xs text-text-muted\">Live counts across every entity you manage.</p>\n <div className=\"mt-4\">\n <EventBarChart data={entityCounts} emptyLabel=\"No operational data yet\" />\n </div>\n </GlassCard>\n </div>\n\n {/* Saved snapshots rail */}\n <aside className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n Saved snapshots {total > 0 ? <span className=\"text-text-muted\">({total})</span> : null}\n </h2>\n <BarChart3 className=\"h-4 w-4 text-text-muted\" />\n </div>\n\n {loading && snapshots.length === 0 ? (\n <div className=\"space-y-3\">\n {Array.from({ length: 3 }).map((_, index) => (\n <Skeleton key={index} className=\"h-24 rounded-card\" />\n ))}\n </div>\n ) : snapshots.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No snapshots yet\"\n description=\"Generate your first snapshot to freeze the current metrics.\"\n className=\"py-8\"\n />\n </GlassCard>\n ) : (\n <div className=\"space-y-3\">\n {snapshots.map((snapshot) => {\n const generatedAt =\n formatGeneratedAt(snapshot.metrics?.generatedAt) ?? formatGeneratedAt(snapshot.createdAt);\n const captured = METRIC_FIELDS.map((field) => {\n const raw = snapshot.metrics?.[field.key];\n return { label: field.label, value: typeof raw === 'number' ? raw : null };\n }).filter((entry) => entry.value !== null);\n return (\n <GlassCard key={snapshot.id} treatment=\"plain\" className=\"border-border-seam p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">\n {snapshot.title ?? snapshot.name ?? `Snapshot ${snapshot.id}`}\n </h3>\n {generatedAt ? <p className=\"mt-0.5 text-xs text-text-muted\">{generatedAt}</p> : null}\n </div>\n {snapshot.status ? (\n <Badge variant={statusVariant(snapshot.status)} className=\"shrink-0 uppercase\">\n {snapshot.status}\n </Badge>\n ) : null}\n </div>\n {captured.length > 0 ? (\n <dl className=\"mt-3 space-y-1.5\">\n {captured.map((entry) => (\n <div key={entry.label} className=\"flex items-center justify-between text-xs\">\n <dt className=\"text-text-secondary\">{entry.label}</dt>\n <dd className=\"font-semibold tabular-nums text-text-primary\">\n {(entry.value ?? 0).toLocaleString()}\n </dd>\n </div>\n ))}\n </dl>\n ) : (\n <p className=\"mt-3 text-xs text-text-muted\">No metrics captured.</p>\n )}\n </GlassCard>\n );\n })}\n\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={snapshots.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"snapshots\"\n loading={loading}\n />\n </div>\n )}\n </aside>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/AnalyticsPage.tsx"],"sourcesContent":["import { BarChart3, CalendarDays, ClipboardList, MapPin, QrCode, Sparkles, Ticket, Trophy, Users } from 'lucide-react';\nimport type { LucideIcon } from 'lucide-react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n Badge,\n Button,\n EmphasisPanel,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\n\nimport {\n CHART_SERIES_COLORS,\n ChartLegend,\n EventAreaChart,\n EventBarChart,\n EventDonutChart,\n type AreaPoint,\n type BarDatum,\n type DonutSlice,\n} from '../../components/EventCharts';\nimport { Pagination, usePagination } from '../../components/Pagination';\nimport {\n useDashboardSummaryQuery,\n useRegistrationsQuery,\n useUpsertAnalyticsSnapshotMutation,\n} from '../../api/eventApi';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\n/**\n * Snapshot metrics are computed server-side at generation time and returned on\n * the `metrics` JSON field of an analytics snapshot. The shared\n * `useAnalyticsSnapshotsQuery` hook does not select `metrics`, so this page owns\n * a dedicated query (distinct operation name) that pulls the metric payload in\n * addition to the identity fields.\n */\nconst ANALYTICS_SNAPSHOTS_WITH_METRICS_QUERY = gql`\n query EventfullymanagedAnalyticsSnapshotsWithMetrics($limit: Int, $offset: Int) {\n analyticsSnapshots: eventfullyManagedAnalyticsSnapshots(limit: $limit, offset: $offset) {\n total\n items {\n id\n name\n title\n status\n metrics\n createdAt\n }\n }\n }\n`;\n\ntype SnapshotMetrics = {\n totalEvents?: number | null;\n activeEvents?: number | null;\n totalRegistrations?: number | null;\n totalAttendees?: number | null;\n totalVenues?: number | null;\n totalSponsors?: number | null;\n upcomingSessions?: number | null;\n generatedAt?: string | null;\n};\n\ntype SnapshotRow = {\n id: string;\n name?: string | null;\n title?: string | null;\n status?: string | null;\n createdAt?: string | null;\n metrics?: SnapshotMetrics | null;\n};\n\ntype SnapshotsResponse = {\n analyticsSnapshots: { total: number; items: SnapshotRow[] };\n};\n\nconst METRIC_FIELDS: { key: keyof SnapshotMetrics; label: string }[] = [\n { key: 'totalEvents', label: 'Events' },\n { key: 'activeEvents', label: 'Active' },\n { key: 'totalRegistrations', label: 'Registrations' },\n { key: 'totalAttendees', label: 'Attendees' },\n { key: 'totalVenues', label: 'Venues' },\n { key: 'totalSponsors', label: 'Sponsors' },\n { key: 'upcomingSessions', label: 'Upcoming Sessions' },\n];\n\nfunction statusVariant(status: string): 'success' | 'warning' | 'secondary' | 'info' {\n const value = status.toLowerCase();\n if (['active', 'published', 'completed'].includes(value)) return 'success';\n if (['inactive', 'cancelled', 'archived', 'expired'].includes(value)) return 'warning';\n if (['draft', 'created', 'pending', 'scheduled'].includes(value)) return 'info';\n return 'secondary';\n}\n\nfunction formatGeneratedAt(value?: string | null): string | null {\n if (!value) return null;\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? null : parsed.toLocaleString();\n}\n\nfunction shortDate(value?: string | null): string {\n if (!value) return '—';\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) return '—';\n return parsed.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n}\n\n/** Humanise a registration status token (e.g. `checked_in` → `Checked In`). */\nfunction humanizeStatus(status: string): string {\n return status\n .replace(/[_-]+/g, ' ')\n .split(' ')\n .filter(Boolean)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ');\n}\n\nfunction MetricTile({ icon: Icon, label, value }: { icon: LucideIcon; label: string; value: number }) {\n return (\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-2 text-text-secondary\">\n <Icon className=\"h-4 w-4 text-accent-primary\" />\n <p className=\"text-xs font-medium uppercase tracking-wide\">{label}</p>\n </div>\n <p className=\"mt-2 text-2xl font-bold tabular-nums text-text-primary\">{value.toLocaleString()}</p>\n </GlassCard>\n );\n}\n\nexport default function AnalyticsPage() {\n const pagination = usePagination({ pageSize: 8 });\n const { data, loading, refetch } = useQuery<SnapshotsResponse, { limit: number; offset: number }>(\n ANALYTICS_SNAPSHOTS_WITH_METRICS_QUERY,\n { variables: { limit: pagination.limit, offset: pagination.offset } }\n );\n const summaryQuery = useDashboardSummaryQuery();\n const registrationsQuery = useRegistrationsQuery({ limit: 200, offset: 0 });\n const [upsertSnapshot, { loading: creating }] = useUpsertAnalyticsSnapshotMutation();\n const [snapshotName, setSnapshotName] = useState('overview');\n\n const snapshots = useMemo(() => data?.analyticsSnapshots.items ?? [], [data]);\n const total = data?.analyticsSnapshots.total ?? 0;\n\n const summary = summaryQuery.data?.dashboardSummary;\n const registrations = useMemo(() => registrationsQuery.data?.registrations.items ?? [], [registrationsQuery.data]);\n\n // Check-ins are derived from the loaded registration sample (registrations\n // carrying a `checkedInAt`) — the dashboard summary does not expose them.\n const checkInCount = useMemo(() => registrations.filter((row) => Boolean(row.checkedInAt)).length, [registrations]);\n\n // Registrations over time: the totalRegistrations captured on each snapshot,\n // ordered oldest → newest. Purely real, snapshot-derived — never synthesised.\n const registrationsSeries = useMemo<AreaPoint[]>(() => {\n return snapshots\n .map((snapshot) => {\n const at = snapshot.metrics?.generatedAt ?? snapshot.createdAt ?? null;\n const parsed = at ? new Date(at) : null;\n const value =\n typeof snapshot.metrics?.totalRegistrations === 'number' ? snapshot.metrics.totalRegistrations : null;\n return parsed && !Number.isNaN(parsed.getTime()) && value !== null\n ? { time: parsed.getTime(), label: shortDate(at), value }\n : null;\n })\n .filter((point): point is { time: number; label: string; value: number } => point !== null)\n .sort((a, b) => a.time - b.time)\n .map(({ label, value }) => ({ label, value }));\n }, [snapshots]);\n\n // Registrations by status donut, computed from the real registration list.\n const statusBreakdown = useMemo<DonutSlice[]>(() => {\n const counts = new Map<string, number>();\n for (const row of registrations) {\n const key = (row.status || 'unknown').toLowerCase();\n counts.set(key, (counts.get(key) ?? 0) + 1);\n }\n return Array.from(counts.entries())\n .sort((a, b) => b[1] - a[1])\n .map(([key, count], index) => ({\n name: humanizeStatus(key),\n value: count,\n color: CHART_SERIES_COLORS[index % CHART_SERIES_COLORS.length],\n }));\n }, [registrations]);\n\n const registrationTotal = registrations.length;\n\n // Entity-count bar chart from the real dashboard summary + derived check-ins.\n const entityCounts = useMemo<BarDatum[]>(() => {\n if (!summary) return [];\n return [\n { name: 'Events', value: summary.totalEvents },\n { name: 'Regs', value: summary.totalRegistrations },\n { name: 'Attendees', value: summary.totalAttendees },\n { name: 'Check-ins', value: checkInCount },\n { name: 'Venues', value: summary.totalVenues },\n { name: 'Sponsors', value: summary.totalSponsors },\n { name: 'Sessions', value: summary.upcomingSessions },\n ];\n }, [summary, checkInCount]);\n\n const onCreate = async () => {\n if (!snapshotName.trim()) return;\n try {\n const response = await upsertSnapshot({\n variables: {\n input: {\n name: snapshotName,\n title: snapshotName,\n status: 'created',\n metadata: {},\n },\n },\n });\n if (!mutationSucceeded(response, response.data?.upsertAnalyticsSnapshot?.id, 'Could not generate snapshot')) {\n return;\n }\n notify.success('Snapshot generated');\n await refetch();\n } catch (error) {\n notify.error('Could not generate snapshot', describeError(error));\n }\n };\n\n const summaryLoading = summaryQuery.loading && !summary;\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n Analytics gives you the live pulse of your event operations — registrations over time, how those registrations\n break down by status, and totals across every entity you manage. Generate a snapshot to freeze today's\n numbers so you can compare trends over time and report on outcomes.\n </PagePurpose>\n\n <EmphasisPanel>\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n <GradientText>Analytics</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Real-time metrics from live event operations, plus point-in-time snapshots you control.\n </p>\n </div>\n <div className=\"flex w-full flex-col gap-2 sm:w-auto sm:flex-row\">\n <Input\n value={snapshotName}\n onChange={(e) => setSnapshotName(e.target.value)}\n placeholder=\"daily-summary\"\n aria-label=\"Snapshot name\"\n className=\"sm:w-48\"\n />\n <Button type=\"button\" disabled={creating} onClick={onCreate}>\n <Sparkles className=\"h-4 w-4\" />\n {creating ? 'Generating...' : 'Generate snapshot'}\n </Button>\n </div>\n </div>\n </EmphasisPanel>\n\n <div className=\"grid grid-cols-1 gap-6 xl:grid-cols-[minmax(0,1fr)_320px]\">\n <div className=\"space-y-6\">\n {/* Live metric tiles */}\n {summaryLoading ? (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 xl:grid-cols-4\">\n {Array.from({ length: 7 }).map((_, index) => (\n <Skeleton key={index} className=\"h-[92px] rounded-card\" />\n ))}\n </div>\n ) : summary ? (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 xl:grid-cols-4\">\n <MetricTile icon={CalendarDays} label=\"Events\" value={summary.totalEvents} />\n <MetricTile icon={ClipboardList} label=\"Registrations\" value={summary.totalRegistrations} />\n <MetricTile icon={Users} label=\"Attendees\" value={summary.totalAttendees} />\n <MetricTile icon={QrCode} label=\"Check-ins\" value={checkInCount} />\n <MetricTile icon={MapPin} label=\"Venues\" value={summary.totalVenues} />\n <MetricTile icon={Trophy} label=\"Sponsors\" value={summary.totalSponsors} />\n <MetricTile icon={Ticket} label=\"Upcoming Sessions\" value={summary.upcomingSessions} />\n </div>\n ) : null}\n\n {/* Registrations over time + by status */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <GlassCard treatment=\"insight\" className=\"p-5\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-base font-semibold text-text-primary\">Registrations over time</h2>\n <span className=\"text-xs text-text-muted\">By snapshot</span>\n </div>\n <div className=\"mt-4\">\n <EventAreaChart data={registrationsSeries} emptyLabel=\"Generate a few snapshots to chart the trend\" />\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"insight\" className=\"p-5\">\n <h2 className=\"text-base font-semibold text-text-primary\">Registrations by status</h2>\n <div className=\"mt-4 flex flex-col items-center gap-4 sm:flex-row\">\n <div className=\"w-full sm:w-1/2\">\n <EventDonutChart\n data={statusBreakdown}\n centerValue={registrationTotal.toLocaleString()}\n centerLabel=\"Registrations\"\n emptyLabel=\"No registrations yet\"\n />\n </div>\n <div className=\"w-full sm:w-1/2\">\n <ChartLegend\n items={statusBreakdown.map((slice) => ({\n label: slice.name,\n color: slice.color ?? 'var(--color-chart-1)',\n value: slice.value.toLocaleString(),\n }))}\n />\n </div>\n </div>\n </GlassCard>\n </div>\n\n {/* Entity totals */}\n <GlassCard treatment=\"insight\" className=\"p-5\">\n <h2 className=\"text-base font-semibold text-text-primary\">Portfolio totals</h2>\n <p className=\"mt-0.5 text-xs text-text-muted\">Live counts across every entity you manage.</p>\n <div className=\"mt-4\">\n <EventBarChart data={entityCounts} emptyLabel=\"No operational data yet\" />\n </div>\n </GlassCard>\n </div>\n\n {/* Saved snapshots rail */}\n <aside className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-base font-semibold text-text-primary\">\n Saved snapshots {total > 0 ? <span className=\"text-text-muted\">({total})</span> : null}\n </h2>\n <BarChart3 className=\"h-4 w-4 text-text-muted\" />\n </div>\n\n {loading && snapshots.length === 0 ? (\n <div className=\"space-y-3\">\n {Array.from({ length: 3 }).map((_, index) => (\n <Skeleton key={index} className=\"h-24 rounded-card\" />\n ))}\n </div>\n ) : snapshots.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No snapshots yet\"\n description=\"Generate your first snapshot to freeze the current metrics.\"\n className=\"py-8\"\n />\n </GlassCard>\n ) : (\n <div className=\"space-y-3\">\n {snapshots.map((snapshot) => {\n const generatedAt =\n formatGeneratedAt(snapshot.metrics?.generatedAt) ?? formatGeneratedAt(snapshot.createdAt);\n const captured = METRIC_FIELDS.map((field) => {\n const raw = snapshot.metrics?.[field.key];\n return { label: field.label, value: typeof raw === 'number' ? raw : null };\n }).filter((entry) => entry.value !== null);\n return (\n <GlassCard key={snapshot.id} treatment=\"plain\" className=\"border-border-seam p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">\n {snapshot.title ?? snapshot.name ?? `Snapshot ${snapshot.id}`}\n </h3>\n {generatedAt ? <p className=\"mt-0.5 text-xs text-text-muted\">{generatedAt}</p> : null}\n </div>\n {snapshot.status ? (\n <Badge variant={statusVariant(snapshot.status)} className=\"shrink-0 uppercase\">\n {snapshot.status}\n </Badge>\n ) : null}\n </div>\n {captured.length > 0 ? (\n <dl className=\"mt-3 space-y-1.5\">\n {captured.map((entry) => (\n <div key={entry.label} className=\"flex items-center justify-between text-xs\">\n <dt className=\"text-text-secondary\">{entry.label}</dt>\n <dd className=\"font-semibold tabular-nums text-text-primary\">\n {(entry.value ?? 0).toLocaleString()}\n </dd>\n </div>\n ))}\n </dl>\n ) : (\n <p className=\"mt-3 text-xs text-text-muted\">No metrics captured.</p>\n )}\n </GlassCard>\n );\n })}\n\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={snapshots.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"snapshots\"\n loading={loading}\n />\n </div>\n )}\n </aside>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AttendeeProfilePage.d.ts","sourceRoot":"","sources":["../../../../src/eventfullymanaged/pages/modules/AttendeeProfilePage.tsx"],"names":[],"mappings":"AAiDA,MAAM,CAAC,OAAO,UAAU,mBAAmB,4CAwH1C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CommunicationPage.d.ts","sourceRoot":"","sources":["../../../../src/eventfullymanaged/pages/modules/CommunicationPage.tsx"],"names":[],"mappings":"AAkDA,MAAM,CAAC,OAAO,UAAU,iBAAiB,4CAiXxC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommunicationPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/CommunicationPage.tsx"],"sourcesContent":["import { Mail, Megaphone, Plus, Send, Upload } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Badge,\n Button,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\n\nimport {\n useAnnouncementsQuery,\n useCommunicationCampaignsQuery,\n usePublishAnnouncementMutation,\n useSendCommunicationCampaignMutation,\n useUpsertAnnouncementMutation,\n useUpsertCommunicationCampaignMutation,\n} from '../../api/eventApi';\nimport { Pagination, usePagination } from '../../components/Pagination';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\ntype SimpleRow = {\n id: string;\n name?: string | null;\n title?: string | null;\n status?: string | null;\n};\n\ntype ComposeKind = 'announcement' | 'campaign';\n\nfunction statusVariant(status: string): 'success' | 'warning' | 'secondary' | 'info' {\n const value = status.toLowerCase();\n if (['published', 'sent', 'active', 'completed'].includes(value)) return 'success';\n if (['cancelled', 'archived', 'failed', 'expired'].includes(value)) return 'warning';\n if (['draft', 'created', 'pending', 'scheduled'].includes(value)) return 'info';\n return 'secondary';\n}\n\nconst isDraft = (status?: string | null): boolean => {\n const value = (status ?? '').toLowerCase();\n return value === '' || value === 'draft' || value === 'created';\n};\n\nconst rowLabel = (row: SimpleRow, fallback: string): string => row.title ?? row.name ?? `${fallback} ${row.id}`;\n\nexport default function CommunicationPage() {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string) => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n\n const announcementPagination = usePagination({ pageSize: 8 });\n const campaignPagination = usePagination({ pageSize: 8 });\n\n const announcementsQuery = useAnnouncementsQuery({\n limit: announcementPagination.limit,\n offset: announcementPagination.offset,\n });\n const campaignsQuery = useCommunicationCampaignsQuery({\n limit: campaignPagination.limit,\n offset: campaignPagination.offset,\n });\n\n const [createAnnouncement, { loading: creatingAnnouncement }] = useUpsertAnnouncementMutation();\n const [publishAnnouncement, { loading: publishing }] = usePublishAnnouncementMutation();\n const [createCampaign, { loading: creatingCampaign }] = useUpsertCommunicationCampaignMutation();\n const [sendCampaign, { loading: sending }] = useSendCommunicationCampaignMutation();\n\n const [kind, setKind] = useState<ComposeKind>('announcement');\n const [subject, setSubject] = useState('');\n const [audience, setAudience] = useState('');\n const [body, setBody] = useState('');\n\n const announcements = useMemo(\n () => (announcementsQuery.data?.announcements.items ?? []) as SimpleRow[],\n [announcementsQuery.data]\n );\n const announcementTotal = announcementsQuery.data?.announcements.total ?? 0;\n const campaigns = useMemo(\n () => (campaignsQuery.data?.communicationCampaigns.items ?? []) as SimpleRow[],\n [campaignsQuery.data]\n );\n const campaignTotal = campaignsQuery.data?.communicationCampaigns.total ?? 0;\n\n const composing = kind === 'announcement' ? creatingAnnouncement : creatingCampaign;\n const resetCompose = () => {\n setSubject('');\n setAudience('');\n setBody('');\n };\n\n const onCompose = async () => {\n if (!subject.trim()) {\n notify.error(t('eventfullymanaged.communication.subjectRequired', 'Subject required'), '');\n return;\n }\n const metadata: Record<string, unknown> = {};\n if (body.trim()) metadata.body = body.trim();\n if (audience.trim()) metadata.audience = audience.trim();\n\n if (kind === 'announcement') {\n const failureMessage = t('eventfullymanaged.communication.errors.create', 'Could not create announcement');\n try {\n const response = await createAnnouncement({\n variables: { input: { title: subject, name: subject, status: 'draft', metadata } },\n });\n if (!mutationSucceeded(response, response.data?.upsertAnnouncement?.id, failureMessage)) return;\n resetCompose();\n notify.success(t('eventfullymanaged.communication.created', 'Announcement created'));\n await announcementsQuery.refetch();\n } catch (error) {\n notify.error(failureMessage, describeError(error));\n }\n return;\n }\n\n const failureMessage = t('eventfullymanaged.communication.errors.createCampaign', 'Could not create campaign');\n try {\n const response = await createCampaign({\n variables: { input: { title: subject, name: subject, status: 'draft', metadata } },\n });\n if (!mutationSucceeded(response, response.data?.upsertCommunicationCampaign?.id, failureMessage)) return;\n resetCompose();\n notify.success(t('eventfullymanaged.communication.campaignCreated', 'Campaign created'));\n await campaignsQuery.refetch();\n } catch (error) {\n notify.error(failureMessage, describeError(error));\n }\n };\n\n const onPublishAnnouncement = async (row: SimpleRow) => {\n const failureMessage = t('eventfullymanaged.communication.errors.publish', 'Could not publish announcement');\n try {\n const response = await publishAnnouncement({ variables: { id: row.id } });\n if (!mutationSucceeded(response, response.data?.publishAnnouncement?.id, failureMessage)) return;\n notify.success(t('eventfullymanaged.communication.published', 'Announcement published'));\n await announcementsQuery.refetch();\n } catch (error) {\n notify.error(failureMessage, describeError(error));\n }\n };\n\n const onSendCampaign = async (row: SimpleRow) => {\n const failureMessage = t('eventfullymanaged.communication.errors.send', 'Could not send campaign');\n try {\n const response = await sendCampaign({ variables: { id: row.id } });\n if (!mutationSucceeded(response, response.data?.sendCommunicationCampaign?.id, failureMessage)) return;\n notify.success(t('eventfullymanaged.communication.sent', 'Campaign delivery started'));\n await campaignsQuery.refetch();\n } catch (error) {\n notify.error(failureMessage, describeError(error));\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n {t(\n 'eventfullymanaged.communication.purpose',\n 'Keep attendees informed and engaged. Announcements are one-off broadcasts (a schedule change, a welcome message); campaigns are scheduled, multi-step outreach (reminder emails, follow-ups). Compose each one here, then publish an announcement or send a campaign — delivery runs server-side through the notification service.'\n )}\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\">\n <GradientText>{t('eventfullymanaged.communication.title', 'Communications')}</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {t('eventfullymanaged.communication.description', 'Draft, publish and send from one place.')}\n </p>\n </div>\n <div className=\"flex flex-col gap-2 sm:flex-row\">\n <Button\n type=\"button\"\n variant={kind === 'announcement' ? 'default' : 'outline'}\n onClick={() => setKind('announcement')}\n >\n <Plus className=\"h-4 w-4\" />\n {t('eventfullymanaged.communication.actions.newAnnouncement', 'New announcement')}\n </Button>\n <Button\n type=\"button\"\n variant={kind === 'campaign' ? 'default' : 'outline'}\n onClick={() => setKind('campaign')}\n >\n <Plus className=\"h-4 w-4\" />\n {t('eventfullymanaged.communication.actions.newCampaign', 'New campaign')}\n </Button>\n </div>\n </div>\n\n {/* Compose + live preview */}\n <GlassCard treatment=\"glass\" glow className=\"p-5\">\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n {kind === 'announcement' ? (\n <Megaphone className=\"h-4 w-4 text-accent-primary\" />\n ) : (\n <Mail className=\"h-4 w-4 text-accent-primary\" />\n )}\n <h2 className=\"text-base font-semibold text-text-primary\">\n {kind === 'announcement'\n ? t('eventfullymanaged.communication.compose.announcement', 'Compose announcement')\n : t('eventfullymanaged.communication.compose.campaign', 'Compose campaign')}\n </h2>\n </div>\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">\n {t('eventfullymanaged.communication.fields.subject', 'Subject')}\n </span>\n <Input\n value={subject}\n onChange={(e) => setSubject(e.target.value)}\n placeholder={t('eventfullymanaged.communication.fields.subjectPlaceholder', 'Q4 Product Roadmap Update')}\n aria-label={t('eventfullymanaged.communication.fields.subject', 'Subject')}\n />\n </label>\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">\n {t('eventfullymanaged.communication.fields.audience', 'Audience')}\n </span>\n <Input\n value={audience}\n onChange={(e) => setAudience(e.target.value)}\n placeholder={t('eventfullymanaged.communication.fields.audiencePlaceholder', 'All attendees')}\n aria-label={t('eventfullymanaged.communication.fields.audience', 'Audience')}\n />\n </label>\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">\n {t('eventfullymanaged.communication.fields.body', 'Message')}\n </span>\n <Textarea\n value={body}\n onChange={(e) => setBody(e.target.value)}\n rows={5}\n placeholder={t(\n 'eventfullymanaged.communication.fields.bodyPlaceholder',\n 'Hi there, here are the latest updates…'\n )}\n aria-label={t('eventfullymanaged.communication.fields.body', 'Message')}\n />\n </label>\n <div className=\"flex justify-end\">\n <Button type=\"button\" disabled={composing} onClick={onCompose}>\n {composing\n ? t('eventfullymanaged.communication.actions.saving', 'Saving…')\n : kind === 'announcement'\n ? t('eventfullymanaged.communication.actions.createAnnouncement', 'Create draft')\n : t('eventfullymanaged.communication.actions.createCampaign', 'Create draft')}\n </Button>\n </div>\n </div>\n\n {/* Live preview */}\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4\">\n <div className=\"flex items-center justify-between border-b border-border-seam pb-2\">\n <span className=\"flex items-center gap-2 text-xs font-medium uppercase tracking-wide text-text-muted\">\n <Mail className=\"h-3.5 w-3.5\" />\n {t('eventfullymanaged.communication.preview.label', 'Preview')}\n </span>\n <Badge variant=\"info\" className=\"uppercase\">\n {t('eventfullymanaged.communication.preview.draft', 'Draft')}\n </Badge>\n </div>\n <div className=\"mt-3 space-y-2\">\n <p className=\"text-base font-semibold text-text-primary\">\n {subject.trim() || t('eventfullymanaged.communication.preview.noSubject', 'Your subject line')}\n </p>\n {audience.trim() ? (\n <p className=\"text-xs text-text-secondary\">\n {t('eventfullymanaged.communication.preview.to', 'To')}: {audience.trim()}\n </p>\n ) : null}\n <p className=\"whitespace-pre-wrap text-sm text-text-secondary\">\n {body.trim() ||\n t(\n 'eventfullymanaged.communication.preview.noBody',\n 'Start typing to see your message preview here.'\n )}\n </p>\n </div>\n </div>\n </div>\n </GlassCard>\n\n {/* Two-pane lists */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('eventfullymanaged.communication.announcements.heading', 'Announcements')}\n </h2>\n {announcementTotal > 0 ? <span className=\"text-sm text-text-muted\">{announcementTotal}</span> : null}\n </div>\n <div className=\"mt-4\">\n {announcementsQuery.loading && announcements.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n {t('eventfullymanaged.communication.announcements.loading', 'Loading announcements…')}\n </p>\n ) : announcements.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('eventfullymanaged.communication.announcements.empty', 'No announcements yet.')}\n className=\"py-8\"\n />\n ) : (\n <ul className=\"space-y-2.5\">\n {announcements.map((row) => (\n <li\n key={row.id}\n className=\"flex items-center justify-between gap-3 rounded-card border border-border-seam bg-bg-surface p-3 transition-[box-shadow,border-color] duration-200 hover:border-border-strong hover:shadow-elevation-1\"\n >\n <p className=\"min-w-0 truncate font-medium text-text-primary\">\n {rowLabel(row, t('eventfullymanaged.communication.announcements.itemPrefix', 'Announcement'))}\n </p>\n <div className=\"flex shrink-0 items-center gap-2\">\n {row.status ? (\n <Badge variant={statusVariant(row.status)} className=\"uppercase\">\n {row.status}\n </Badge>\n ) : null}\n {isDraft(row.status) ? (\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={publishing}\n onClick={() => void onPublishAnnouncement(row)}\n >\n <Upload className=\"h-3.5 w-3.5\" />\n {t('eventfullymanaged.communication.actions.publish', 'Publish')}\n </Button>\n ) : null}\n </div>\n </li>\n ))}\n </ul>\n )}\n <Pagination\n offset={announcementPagination.offset}\n limit={announcementPagination.limit}\n loadedCount={announcements.length}\n total={announcementTotal}\n onPrev={announcementPagination.prevPage}\n onNext={announcementPagination.nextPage}\n itemLabel={t('eventfullymanaged.communication.announcements.heading', 'Announcements').toLowerCase()}\n loading={announcementsQuery.loading}\n />\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('eventfullymanaged.communication.campaigns.heading', 'Campaigns')}\n </h2>\n {campaignTotal > 0 ? <span className=\"text-sm text-text-muted\">{campaignTotal}</span> : null}\n </div>\n <div className=\"mt-4\">\n {campaignsQuery.loading && campaigns.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n {t('eventfullymanaged.communication.campaigns.loading', 'Loading campaigns…')}\n </p>\n ) : campaigns.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('eventfullymanaged.communication.campaigns.empty', 'No campaigns yet.')}\n className=\"py-8\"\n />\n ) : (\n <ul className=\"space-y-2.5\">\n {campaigns.map((row) => (\n <li\n key={row.id}\n className=\"flex items-center justify-between gap-3 rounded-card border border-border-seam bg-bg-surface p-3 transition-[box-shadow,border-color] duration-200 hover:border-border-strong hover:shadow-elevation-1\"\n >\n <p className=\"min-w-0 truncate font-medium text-text-primary\">\n {rowLabel(row, t('eventfullymanaged.communication.campaigns.itemPrefix', 'Campaign'))}\n </p>\n <div className=\"flex shrink-0 items-center gap-2\">\n {row.status ? (\n <Badge variant={statusVariant(row.status)} className=\"uppercase\">\n {row.status}\n </Badge>\n ) : null}\n {isDraft(row.status) ? (\n <Button type=\"button\" size=\"sm\" disabled={sending} onClick={() => void onSendCampaign(row)}>\n <Send className=\"h-3.5 w-3.5\" />\n {t('eventfullymanaged.communication.actions.send', 'Send campaign')}\n </Button>\n ) : null}\n </div>\n </li>\n ))}\n </ul>\n )}\n <Pagination\n offset={campaignPagination.offset}\n limit={campaignPagination.limit}\n loadedCount={campaigns.length}\n total={campaignTotal}\n onPrev={campaignPagination.prevPage}\n onNext={campaignPagination.nextPage}\n itemLabel={t('eventfullymanaged.communication.campaigns.heading', 'Campaigns').toLowerCase()}\n loading={campaignsQuery.loading}\n />\n </div>\n </GlassCard>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CommunicationPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/CommunicationPage.tsx"],"sourcesContent":["import { Mail, Megaphone, Plus, Send, Upload } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport {\n Badge,\n Button,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\n\nimport {\n useAnnouncementsQuery,\n useCommunicationCampaignsQuery,\n usePublishAnnouncementMutation,\n useSendCommunicationCampaignMutation,\n useUpsertAnnouncementMutation,\n useUpsertCommunicationCampaignMutation,\n} from '../../api/eventApi';\nimport { Pagination, usePagination } from '../../components/Pagination';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\ntype SimpleRow = {\n id: string;\n name?: string | null;\n title?: string | null;\n status?: string | null;\n};\n\ntype ComposeKind = 'announcement' | 'campaign';\n\nfunction statusVariant(status: string): 'success' | 'warning' | 'secondary' | 'info' {\n const value = status.toLowerCase();\n if (['published', 'sent', 'active', 'completed'].includes(value)) return 'success';\n if (['cancelled', 'archived', 'failed', 'expired'].includes(value)) return 'warning';\n if (['draft', 'created', 'pending', 'scheduled'].includes(value)) return 'info';\n return 'secondary';\n}\n\nconst isDraft = (status?: string | null): boolean => {\n const value = (status ?? '').toLowerCase();\n return value === '' || value === 'draft' || value === 'created';\n};\n\nconst rowLabel = (row: SimpleRow, fallback: string): string => row.title ?? row.name ?? `${fallback} ${row.id}`;\n\nexport default function CommunicationPage() {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string) => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n\n const announcementPagination = usePagination({ pageSize: 8 });\n const campaignPagination = usePagination({ pageSize: 8 });\n\n const announcementsQuery = useAnnouncementsQuery({\n limit: announcementPagination.limit,\n offset: announcementPagination.offset,\n });\n const campaignsQuery = useCommunicationCampaignsQuery({\n limit: campaignPagination.limit,\n offset: campaignPagination.offset,\n });\n\n const [createAnnouncement, { loading: creatingAnnouncement }] = useUpsertAnnouncementMutation();\n const [publishAnnouncement, { loading: publishing }] = usePublishAnnouncementMutation();\n const [createCampaign, { loading: creatingCampaign }] = useUpsertCommunicationCampaignMutation();\n const [sendCampaign, { loading: sending }] = useSendCommunicationCampaignMutation();\n\n const [kind, setKind] = useState<ComposeKind>('announcement');\n const [subject, setSubject] = useState('');\n const [audience, setAudience] = useState('');\n const [body, setBody] = useState('');\n\n const announcements = useMemo(\n () => (announcementsQuery.data?.announcements.items ?? []) as SimpleRow[],\n [announcementsQuery.data]\n );\n const announcementTotal = announcementsQuery.data?.announcements.total ?? 0;\n const campaigns = useMemo(\n () => (campaignsQuery.data?.communicationCampaigns.items ?? []) as SimpleRow[],\n [campaignsQuery.data]\n );\n const campaignTotal = campaignsQuery.data?.communicationCampaigns.total ?? 0;\n\n const composing = kind === 'announcement' ? creatingAnnouncement : creatingCampaign;\n const resetCompose = () => {\n setSubject('');\n setAudience('');\n setBody('');\n };\n\n const onCompose = async () => {\n if (!subject.trim()) {\n notify.error(t('eventfullymanaged.communication.subjectRequired', 'Subject required'), '');\n return;\n }\n const metadata: Record<string, unknown> = {};\n if (body.trim()) metadata.body = body.trim();\n if (audience.trim()) metadata.audience = audience.trim();\n\n if (kind === 'announcement') {\n const failureMessage = t('eventfullymanaged.communication.errors.create', 'Could not create announcement');\n try {\n const response = await createAnnouncement({\n variables: { input: { title: subject, name: subject, status: 'draft', metadata } },\n });\n if (!mutationSucceeded(response, response.data?.upsertAnnouncement?.id, failureMessage)) return;\n resetCompose();\n notify.success(t('eventfullymanaged.communication.created', 'Announcement created'));\n await announcementsQuery.refetch();\n } catch (error) {\n notify.error(failureMessage, describeError(error));\n }\n return;\n }\n\n const failureMessage = t('eventfullymanaged.communication.errors.createCampaign', 'Could not create campaign');\n try {\n const response = await createCampaign({\n variables: { input: { title: subject, name: subject, status: 'draft', metadata } },\n });\n if (!mutationSucceeded(response, response.data?.upsertCommunicationCampaign?.id, failureMessage)) return;\n resetCompose();\n notify.success(t('eventfullymanaged.communication.campaignCreated', 'Campaign created'));\n await campaignsQuery.refetch();\n } catch (error) {\n notify.error(failureMessage, describeError(error));\n }\n };\n\n const onPublishAnnouncement = async (row: SimpleRow) => {\n const failureMessage = t('eventfullymanaged.communication.errors.publish', 'Could not publish announcement');\n try {\n const response = await publishAnnouncement({ variables: { id: row.id } });\n if (!mutationSucceeded(response, response.data?.publishAnnouncement?.id, failureMessage)) return;\n notify.success(t('eventfullymanaged.communication.published', 'Announcement published'));\n await announcementsQuery.refetch();\n } catch (error) {\n notify.error(failureMessage, describeError(error));\n }\n };\n\n const onSendCampaign = async (row: SimpleRow) => {\n const failureMessage = t('eventfullymanaged.communication.errors.send', 'Could not send campaign');\n try {\n const response = await sendCampaign({ variables: { id: row.id } });\n if (!mutationSucceeded(response, response.data?.sendCommunicationCampaign?.id, failureMessage)) return;\n notify.success(t('eventfullymanaged.communication.sent', 'Campaign delivery started'));\n await campaignsQuery.refetch();\n } catch (error) {\n notify.error(failureMessage, describeError(error));\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n {t(\n 'eventfullymanaged.communication.purpose',\n 'Keep attendees informed and engaged. Announcements are one-off broadcasts (a schedule change, a welcome message); campaigns are scheduled, multi-step outreach (reminder emails, follow-ups). Compose each one here, then publish an announcement or send a campaign — delivery runs server-side through the notification service.'\n )}\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\">\n <GradientText>{t('eventfullymanaged.communication.title', 'Communications')}</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {t('eventfullymanaged.communication.description', 'Draft, publish and send from one place.')}\n </p>\n </div>\n <div className=\"flex flex-col gap-2 sm:flex-row\">\n <Button\n type=\"button\"\n variant={kind === 'announcement' ? 'default' : 'outline'}\n onClick={() => setKind('announcement')}\n >\n <Plus className=\"h-4 w-4\" />\n {t('eventfullymanaged.communication.actions.newAnnouncement', 'New announcement')}\n </Button>\n <Button\n type=\"button\"\n variant={kind === 'campaign' ? 'default' : 'outline'}\n onClick={() => setKind('campaign')}\n >\n <Plus className=\"h-4 w-4\" />\n {t('eventfullymanaged.communication.actions.newCampaign', 'New campaign')}\n </Button>\n </div>\n </div>\n\n {/* Compose + live preview */}\n <GlassCard treatment=\"glass\" glow className=\"p-5\">\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n {kind === 'announcement' ? (\n <Megaphone className=\"h-4 w-4 text-accent-primary\" />\n ) : (\n <Mail className=\"h-4 w-4 text-accent-primary\" />\n )}\n <h2 className=\"text-base font-semibold text-text-primary\">\n {kind === 'announcement'\n ? t('eventfullymanaged.communication.compose.announcement', 'Compose announcement')\n : t('eventfullymanaged.communication.compose.campaign', 'Compose campaign')}\n </h2>\n </div>\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">\n {t('eventfullymanaged.communication.fields.subject', 'Subject')}\n </span>\n <Input\n value={subject}\n onChange={(e) => setSubject(e.target.value)}\n placeholder={t(\n 'eventfullymanaged.communication.fields.subjectPlaceholder',\n 'Q4 Product Roadmap Update'\n )}\n aria-label={t('eventfullymanaged.communication.fields.subject', 'Subject')}\n />\n </label>\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">\n {t('eventfullymanaged.communication.fields.audience', 'Audience')}\n </span>\n <Input\n value={audience}\n onChange={(e) => setAudience(e.target.value)}\n placeholder={t('eventfullymanaged.communication.fields.audiencePlaceholder', 'All attendees')}\n aria-label={t('eventfullymanaged.communication.fields.audience', 'Audience')}\n />\n </label>\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">\n {t('eventfullymanaged.communication.fields.body', 'Message')}\n </span>\n <Textarea\n value={body}\n onChange={(e) => setBody(e.target.value)}\n rows={5}\n placeholder={t(\n 'eventfullymanaged.communication.fields.bodyPlaceholder',\n 'Hi there, here are the latest updates…'\n )}\n aria-label={t('eventfullymanaged.communication.fields.body', 'Message')}\n />\n </label>\n <div className=\"flex justify-end\">\n <Button type=\"button\" disabled={composing} onClick={onCompose}>\n {composing\n ? t('eventfullymanaged.communication.actions.saving', 'Saving…')\n : kind === 'announcement'\n ? t('eventfullymanaged.communication.actions.createAnnouncement', 'Create draft')\n : t('eventfullymanaged.communication.actions.createCampaign', 'Create draft')}\n </Button>\n </div>\n </div>\n\n {/* Live preview */}\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4\">\n <div className=\"flex items-center justify-between border-b border-border-seam pb-2\">\n <span className=\"flex items-center gap-2 text-xs font-medium uppercase tracking-wide text-text-muted\">\n <Mail className=\"h-3.5 w-3.5\" />\n {t('eventfullymanaged.communication.preview.label', 'Preview')}\n </span>\n <Badge variant=\"info\" className=\"uppercase\">\n {t('eventfullymanaged.communication.preview.draft', 'Draft')}\n </Badge>\n </div>\n <div className=\"mt-3 space-y-2\">\n <p className=\"text-base font-semibold text-text-primary\">\n {subject.trim() || t('eventfullymanaged.communication.preview.noSubject', 'Your subject line')}\n </p>\n {audience.trim() ? (\n <p className=\"text-xs text-text-secondary\">\n {t('eventfullymanaged.communication.preview.to', 'To')}: {audience.trim()}\n </p>\n ) : null}\n <p className=\"whitespace-pre-wrap text-sm text-text-secondary\">\n {body.trim() ||\n t('eventfullymanaged.communication.preview.noBody', 'Start typing to see your message preview here.')}\n </p>\n </div>\n </div>\n </div>\n </GlassCard>\n\n {/* Two-pane lists */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('eventfullymanaged.communication.announcements.heading', 'Announcements')}\n </h2>\n {announcementTotal > 0 ? <span className=\"text-sm text-text-muted\">{announcementTotal}</span> : null}\n </div>\n <div className=\"mt-4\">\n {announcementsQuery.loading && announcements.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n {t('eventfullymanaged.communication.announcements.loading', 'Loading announcements…')}\n </p>\n ) : announcements.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('eventfullymanaged.communication.announcements.empty', 'No announcements yet.')}\n className=\"py-8\"\n />\n ) : (\n <ul className=\"space-y-2.5\">\n {announcements.map((row) => (\n <li\n key={row.id}\n className=\"flex items-center justify-between gap-3 rounded-card border border-border-seam bg-bg-surface p-3 transition-[box-shadow,border-color] duration-200 hover:border-border-strong hover:shadow-elevation-1\"\n >\n <p className=\"min-w-0 truncate font-medium text-text-primary\">\n {rowLabel(row, t('eventfullymanaged.communication.announcements.itemPrefix', 'Announcement'))}\n </p>\n <div className=\"flex shrink-0 items-center gap-2\">\n {row.status ? (\n <Badge variant={statusVariant(row.status)} className=\"uppercase\">\n {row.status}\n </Badge>\n ) : null}\n {isDraft(row.status) ? (\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={publishing}\n onClick={() => void onPublishAnnouncement(row)}\n >\n <Upload className=\"h-3.5 w-3.5\" />\n {t('eventfullymanaged.communication.actions.publish', 'Publish')}\n </Button>\n ) : null}\n </div>\n </li>\n ))}\n </ul>\n )}\n <Pagination\n offset={announcementPagination.offset}\n limit={announcementPagination.limit}\n loadedCount={announcements.length}\n total={announcementTotal}\n onPrev={announcementPagination.prevPage}\n onNext={announcementPagination.nextPage}\n itemLabel={t('eventfullymanaged.communication.announcements.heading', 'Announcements').toLowerCase()}\n loading={announcementsQuery.loading}\n />\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {t('eventfullymanaged.communication.campaigns.heading', 'Campaigns')}\n </h2>\n {campaignTotal > 0 ? <span className=\"text-sm text-text-muted\">{campaignTotal}</span> : null}\n </div>\n <div className=\"mt-4\">\n {campaignsQuery.loading && campaigns.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-text-secondary\">\n {t('eventfullymanaged.communication.campaigns.loading', 'Loading campaigns…')}\n </p>\n ) : campaigns.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('eventfullymanaged.communication.campaigns.empty', 'No campaigns yet.')}\n className=\"py-8\"\n />\n ) : (\n <ul className=\"space-y-2.5\">\n {campaigns.map((row) => (\n <li\n key={row.id}\n className=\"flex items-center justify-between gap-3 rounded-card border border-border-seam bg-bg-surface p-3 transition-[box-shadow,border-color] duration-200 hover:border-border-strong hover:shadow-elevation-1\"\n >\n <p className=\"min-w-0 truncate font-medium text-text-primary\">\n {rowLabel(row, t('eventfullymanaged.communication.campaigns.itemPrefix', 'Campaign'))}\n </p>\n <div className=\"flex shrink-0 items-center gap-2\">\n {row.status ? (\n <Badge variant={statusVariant(row.status)} className=\"uppercase\">\n {row.status}\n </Badge>\n ) : null}\n {isDraft(row.status) ? (\n <Button type=\"button\" size=\"sm\" disabled={sending} onClick={() => void onSendCampaign(row)}>\n <Send className=\"h-3.5 w-3.5\" />\n {t('eventfullymanaged.communication.actions.send', 'Send campaign')}\n </Button>\n ) : null}\n </div>\n </li>\n ))}\n </ul>\n )}\n <Pagination\n offset={campaignPagination.offset}\n limit={campaignPagination.limit}\n loadedCount={campaigns.length}\n total={campaignTotal}\n onPrev={campaignPagination.prevPage}\n onNext={campaignPagination.nextPage}\n itemLabel={t('eventfullymanaged.communication.campaigns.heading', 'Campaigns').toLowerCase()}\n loading={campaignsQuery.loading}\n />\n </div>\n </GlassCard>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type EntityRow = {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
subtitle?: string;
|
|
5
|
+
status?: string;
|
|
6
|
+
};
|
|
7
|
+
interface EntityListSectionProps {
|
|
8
|
+
heading: string;
|
|
9
|
+
rows: EntityRow[];
|
|
10
|
+
emptyLabel: string;
|
|
11
|
+
/** Illustration used for the zero-state. Defaults to a generic data illustration. */
|
|
12
|
+
emptyIllustration?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function EntityListSection({ heading, rows, emptyLabel, emptyIllustration, }: EntityListSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=EntityListSection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntityListSection.d.ts","sourceRoot":"","sources":["../../../../src/eventfullymanaged/pages/modules/EntityListSection.tsx"],"names":[],"mappings":"AAEA,KAAK,SAAS,GAAG;IACf,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,UAAU,sBAAsB;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,qFAAqF;IACrF,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAgBD,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,IAAI,EACJ,UAAU,EACV,iBAAgC,GACjC,EAAE,sBAAsB,2CAkCxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventsPage.d.ts","sourceRoot":"","sources":["../../../../src/eventfullymanaged/pages/modules/EventsPage.tsx"],"names":[],"mappings":"AAqKA,MAAM,CAAC,OAAO,UAAU,UAAU,4CAqUjC"}
|