@burdenoff/microfe-eventfullymanaged 2026.704.7 → 2026.704.8

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":"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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"}
@@ -1,17 +1,17 @@
1
1
  import { useEventfullyManaged as e } from "../../context/EventfullyManagedContext.js";
2
2
  import { gql as t } from "../../../node_modules/graphql-tag/lib/index.js";
3
3
  import { useQuery as n } from "../../../node_modules/@apollo/client/react/hooks/useQuery.js";
4
- import { useEventsQuery as r, useVenuesQuery as i } from "../../api/eventApi.js";
5
- import { formatDateTime as a } from "../events/eventFormUtils.js";
6
- import { ActionMenu as o } from "../../components/ActionMenu.js";
7
- import { Pagination as s, usePagination as c } from "../../components/Pagination.js";
8
- import { useMemo as l, useState as u } from "react";
9
- import { jsx as d, jsxs as f } from "react/jsx-runtime";
10
- import { CalendarClock as p, CalendarPlus as m, MapPin as h, Search as g, Ticket as _, UserCheck as v, Users as y } from "lucide-react";
11
- import { useI18n as b } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
12
- import { Badge as x, Button as S, EmphasisPanel as C, GlassCard as w, GradientText as T, IllustratedEmptyState as E, Input as D, NextSteps as O, PagePurpose as k, Skeleton as A } from "@burdenoff/fe-libs/ui";
4
+ import { useEventsQuery as r, useSponsorsQuery as i, useVenuesQuery as a } from "../../api/eventApi.js";
5
+ import { formatDateTime as o } from "../events/eventFormUtils.js";
6
+ import { ActionMenu as s } from "../../components/ActionMenu.js";
7
+ import { Pagination as c, usePagination as l } from "../../components/Pagination.js";
8
+ import { useMemo as u, useState as d } from "react";
9
+ import { jsx as f, jsxs as p } from "react/jsx-runtime";
10
+ import { Award as m, CalendarClock as h, CalendarDays as g, CalendarPlus as _, MapPin as v, Search as ee, Ticket as y, UserCheck as b, Users as x } from "lucide-react";
11
+ import { useI18n as S } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
12
+ import { Badge as C, Button as w, EmphasisPanel as T, GlassCard as E, GradientText as D, IllustratedEmptyState as O, Input as k, NextSteps as te, PagePurpose as ne, Skeleton as A, Tabs as re, TabsList as ie, TabsTrigger as j } from "@burdenoff/fe-libs/ui";
13
13
  //#region src/eventfullymanaged/pages/modules/EventsPage.tsx
14
- var j = t`
14
+ var M = t`
15
15
  query EventfullymanagedEventsRegistrations($limit: Int) {
16
16
  registrations: eventfullyManagedRegistrations(limit: $limit, offset: 0) {
17
17
  total
@@ -23,7 +23,7 @@ var j = t`
23
23
  }
24
24
  }
25
25
  `;
26
- function M(e) {
26
+ function N(e) {
27
27
  let t = e.toLowerCase();
28
28
  return [
29
29
  "active",
@@ -45,44 +45,73 @@ function M(e) {
45
45
  "scheduled"
46
46
  ].includes(t) ? "info" : "secondary";
47
47
  }
48
- function N(e, t) {
49
- let n = e ? a(e) : "", r = t ? a(t) : "";
48
+ function P(e, t) {
49
+ let n = e ? o(e) : "", r = t ? o(t) : "";
50
50
  return n && r ? `${n} — ${r}` : n || r || "No schedule set";
51
51
  }
52
- function P({ icon: e, label: t, value: n }) {
53
- return /* @__PURE__ */ f("div", {
52
+ function F(e, t) {
53
+ let n = Date.now(), r = e ? new Date(e).getTime() : NaN, i = t ? new Date(t).getTime() : NaN, a = !Number.isNaN(r), o = !Number.isNaN(i);
54
+ return !a && !o ? null : a && n < r ? "upcoming" : o && n > i ? "past" : a ? "live" : "upcoming";
55
+ }
56
+ function I({ icon: e, label: t, value: n, loading: r }) {
57
+ return /* @__PURE__ */ f(E, {
58
+ treatment: "plain",
59
+ className: "border-border-seam p-4",
60
+ children: /* @__PURE__ */ p("div", {
61
+ className: "flex items-center gap-3",
62
+ children: [/* @__PURE__ */ f("span", {
63
+ className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-button bg-bg-sunken text-text-secondary",
64
+ children: /* @__PURE__ */ f(e, { className: "h-4 w-4" })
65
+ }), /* @__PURE__ */ p("div", {
66
+ className: "min-w-0",
67
+ children: [r ? /* @__PURE__ */ f(A, { className: "h-6 w-12" }) : /* @__PURE__ */ f("p", {
68
+ className: "truncate text-xl font-bold tabular-nums text-text-primary",
69
+ children: n
70
+ }), /* @__PURE__ */ f("p", {
71
+ className: "truncate text-xs text-text-muted",
72
+ children: t
73
+ })]
74
+ })]
75
+ })
76
+ });
77
+ }
78
+ function L({ icon: e, label: t, value: n }) {
79
+ return /* @__PURE__ */ p("div", {
54
80
  className: "flex items-center gap-1.5",
55
81
  children: [
56
- /* @__PURE__ */ d(e, { className: "h-3.5 w-3.5 text-text-muted" }),
57
- /* @__PURE__ */ d("span", {
82
+ /* @__PURE__ */ f(e, { className: "h-3.5 w-3.5 text-text-muted" }),
83
+ /* @__PURE__ */ f("span", {
58
84
  className: "tabular-nums font-medium text-text-primary",
59
85
  children: n
60
86
  }),
61
- /* @__PURE__ */ d("span", {
87
+ /* @__PURE__ */ f("span", {
62
88
  className: "text-text-muted",
63
89
  children: t
64
90
  })
65
91
  ]
66
92
  });
67
93
  }
68
- function F() {
69
- let { t } = b(), a = (e, n) => {
94
+ function R() {
95
+ let { t } = S(), o = (e, n) => {
70
96
  let r = t(e);
71
97
  return r === e ? n : r;
72
- }, { navigate: F } = e(), [I, L] = u(""), R = c({ resetKey: I }), { data: z, loading: B } = r({
73
- search: I,
74
- limit: R.limit,
75
- offset: R.offset
76
- }), { data: V } = i({
98
+ }, { navigate: R } = e(), [z, B] = d(""), [V, H] = d("all"), U = l({ resetKey: z }), { data: W, loading: G } = r({
99
+ search: z,
100
+ limit: U.limit,
101
+ offset: U.offset
102
+ }), { data: K, loading: q } = a({
77
103
  limit: 100,
78
104
  offset: 0
79
- }), { data: H } = n(j, { variables: { limit: 500 } }), U = z?.events?.total ?? 0, W = l(() => z?.events?.items ?? [], [z]), G = l(() => {
105
+ }), { data: J, loading: ae } = i({
106
+ limit: 1,
107
+ offset: 0
108
+ }), { data: Y, loading: oe } = n(M, { variables: { limit: 500 } }), X = W?.events?.total ?? 0, Z = u(() => W?.events?.items ?? [], [W]), se = Y?.registrations?.total ?? 0, Q = K?.venues?.total ?? 0, ce = J?.sponsors?.total ?? 0, $ = u(() => V === "all" ? Z : Z.filter((e) => F(e.startAt, e.endAt) === V), [Z, V]), le = u(() => {
80
109
  let e = /* @__PURE__ */ new Map();
81
- for (let t of V?.venues?.items ?? []) e.set(t.id, t.name);
110
+ for (let t of K?.venues?.items ?? []) e.set(t.id, t.name);
82
111
  return e;
83
- }, [V]), K = l(() => {
112
+ }, [K]), ue = u(() => {
84
113
  let e = /* @__PURE__ */ new Map();
85
- for (let t of H?.registrations?.items ?? []) {
114
+ for (let t of Y?.registrations?.items ?? []) {
86
115
  let n = e.get(t.eventId) ?? {
87
116
  registrations: 0,
88
117
  checkedIn: 0
@@ -90,176 +119,252 @@ function F() {
90
119
  n.registrations += 1, t.checkedInAt && (n.checkedIn += 1), e.set(t.eventId, n);
91
120
  }
92
121
  return e;
93
- }, [H]);
94
- return /* @__PURE__ */ f("div", {
122
+ }, [Y]);
123
+ return /* @__PURE__ */ p("div", {
95
124
  className: "space-y-6 p-6",
96
125
  children: [
97
- /* @__PURE__ */ d(k, { children: a("eventfullymanaged.events.purpose", "Events are the heart of the platform — each one is a happening you sell tickets for, register attendees to, and run on the day. Create an event, open it to reveal its agenda, speakers and ticket types, then publish it when it is ready.") }),
98
- /* @__PURE__ */ d(C, { children: /* @__PURE__ */ f("div", {
126
+ /* @__PURE__ */ f(ne, { children: o("eventfullymanaged.events.purpose", "Events are the heart of the platform — each one is a happening you sell tickets for, register attendees to, and run on the day. Create an event, open it to reveal its agenda, speakers and ticket types, then publish it when it is ready.") }),
127
+ /* @__PURE__ */ f(T, { children: /* @__PURE__ */ p("div", {
99
128
  className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
100
- children: [/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("h1", {
129
+ children: [/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h1", {
101
130
  className: "text-2xl font-bold text-text-primary",
102
- children: /* @__PURE__ */ d(T, { children: a("eventfullymanaged.events.title", "Events") })
103
- }), /* @__PURE__ */ d("p", {
131
+ children: /* @__PURE__ */ f(D, { children: o("eventfullymanaged.events.title", "Events") })
132
+ }), /* @__PURE__ */ f("p", {
104
133
  className: "mt-1 text-sm text-text-secondary",
105
- children: a("eventfullymanaged.events.description", "Create and manage live events from the workspace graph.")
106
- })] }), /* @__PURE__ */ f(S, {
134
+ children: o("eventfullymanaged.events.description", "Create and manage live events from the workspace graph.")
135
+ })] }), /* @__PURE__ */ p(w, {
107
136
  type: "button",
108
- onClick: () => F("/events/create"),
109
- children: [/* @__PURE__ */ d(m, { className: "h-4 w-4" }), a("eventfullymanaged.events.actions.create", "Create Event")]
137
+ onClick: () => R("/events/create"),
138
+ children: [/* @__PURE__ */ f(_, { className: "h-4 w-4" }), o("eventfullymanaged.events.actions.create", "Create Event")]
110
139
  })]
111
140
  }) }),
112
- /* @__PURE__ */ f("div", {
113
- className: "relative",
114
- children: [/* @__PURE__ */ d(g, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-muted" }), /* @__PURE__ */ d(D, {
115
- type: "text",
116
- value: I,
117
- onChange: (e) => L(e.target.value),
118
- placeholder: a("eventfullymanaged.events.searchPlaceholder", "Search events"),
119
- "aria-label": a("eventfullymanaged.events.searchPlaceholder", "Search events"),
120
- className: "pl-10"
141
+ /* @__PURE__ */ p("div", {
142
+ className: "grid grid-cols-2 gap-3 sm:grid-cols-4",
143
+ children: [
144
+ /* @__PURE__ */ f(I, {
145
+ icon: g,
146
+ label: o("eventfullymanaged.events.portfolio.events", "Events"),
147
+ value: X.toLocaleString(),
148
+ loading: G && X === 0
149
+ }),
150
+ /* @__PURE__ */ f(I, {
151
+ icon: x,
152
+ label: o("eventfullymanaged.events.portfolio.registrations", "Registrations"),
153
+ value: se.toLocaleString(),
154
+ loading: oe && !Y
155
+ }),
156
+ /* @__PURE__ */ f(I, {
157
+ icon: v,
158
+ label: o("eventfullymanaged.events.portfolio.venues", "Venues"),
159
+ value: Q.toLocaleString(),
160
+ loading: q && !K
161
+ }),
162
+ /* @__PURE__ */ f(I, {
163
+ icon: m,
164
+ label: o("eventfullymanaged.events.portfolio.sponsors", "Sponsors"),
165
+ value: ce.toLocaleString(),
166
+ loading: ae && !J
167
+ })
168
+ ]
169
+ }),
170
+ /* @__PURE__ */ p("div", {
171
+ className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
172
+ children: [/* @__PURE__ */ p("div", {
173
+ className: "relative flex-1",
174
+ children: [/* @__PURE__ */ f(ee, { className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-muted" }), /* @__PURE__ */ f(k, {
175
+ type: "text",
176
+ value: z,
177
+ onChange: (e) => B(e.target.value),
178
+ placeholder: o("eventfullymanaged.events.searchPlaceholder", "Search events"),
179
+ "aria-label": o("eventfullymanaged.events.searchPlaceholder", "Search events"),
180
+ className: "pl-10"
181
+ })]
182
+ }), /* @__PURE__ */ f(re, {
183
+ value: V,
184
+ onValueChange: (e) => H(e),
185
+ className: "w-full sm:w-auto",
186
+ children: /* @__PURE__ */ p(ie, {
187
+ className: "w-full overflow-x-auto sm:w-auto",
188
+ children: [
189
+ /* @__PURE__ */ f(j, {
190
+ value: "all",
191
+ children: o("eventfullymanaged.events.filters.all", "All")
192
+ }),
193
+ /* @__PURE__ */ f(j, {
194
+ value: "upcoming",
195
+ children: o("eventfullymanaged.events.filters.upcoming", "Upcoming")
196
+ }),
197
+ /* @__PURE__ */ f(j, {
198
+ value: "live",
199
+ children: o("eventfullymanaged.events.filters.live", "Live")
200
+ }),
201
+ /* @__PURE__ */ f(j, {
202
+ value: "past",
203
+ children: o("eventfullymanaged.events.filters.past", "Past")
204
+ })
205
+ ]
206
+ })
121
207
  })]
122
208
  }),
123
- B ? /* @__PURE__ */ f("div", {
209
+ G ? /* @__PURE__ */ p("div", {
124
210
  className: "space-y-3",
125
211
  children: [
126
- /* @__PURE__ */ d(A, { className: "h-28 w-full rounded-card" }),
127
- /* @__PURE__ */ d(A, { className: "h-28 w-full rounded-card" }),
128
- /* @__PURE__ */ d(A, { className: "h-28 w-full rounded-card" })
212
+ /* @__PURE__ */ f(A, { className: "h-28 w-full rounded-card" }),
213
+ /* @__PURE__ */ f(A, { className: "h-28 w-full rounded-card" }),
214
+ /* @__PURE__ */ f(A, { className: "h-28 w-full rounded-card" })
129
215
  ]
130
- }) : W.length === 0 ? /* @__PURE__ */ d(w, {
216
+ }) : Z.length === 0 ? /* @__PURE__ */ f(E, {
131
217
  treatment: "plain",
132
218
  className: "border-border-seam",
133
- children: /* @__PURE__ */ d(E, {
134
- illustration: I ? "empty-search" : "empty-data",
135
- title: I ? a("eventfullymanaged.events.list.emptySearch", "No events match your search.") : a("eventfullymanaged.events.list.empty", "No events yet."),
136
- description: I ? a("eventfullymanaged.events.list.emptySearchHint", "Try a different name.") : a("eventfullymanaged.events.list.emptyHint", "Create your first event to get started."),
137
- action: I ? void 0 : /* @__PURE__ */ f(S, {
219
+ children: /* @__PURE__ */ f(O, {
220
+ illustration: z ? "empty-search" : "empty-data",
221
+ title: z ? o("eventfullymanaged.events.list.emptySearch", "No events match your search.") : o("eventfullymanaged.events.list.empty", "No events yet."),
222
+ description: z ? o("eventfullymanaged.events.list.emptySearchHint", "Try a different name.") : o("eventfullymanaged.events.list.emptyHint", "Create your first event to get started."),
223
+ action: z ? void 0 : /* @__PURE__ */ p(w, {
138
224
  type: "button",
139
- onClick: () => F("/events/create"),
140
- children: [/* @__PURE__ */ d(m, { className: "h-4 w-4" }), a("eventfullymanaged.events.actions.create", "Create Event")]
225
+ onClick: () => R("/events/create"),
226
+ children: [/* @__PURE__ */ f(_, { className: "h-4 w-4" }), o("eventfullymanaged.events.actions.create", "Create Event")]
141
227
  })
142
228
  })
143
- }) : /* @__PURE__ */ f("div", {
229
+ }) : $.length === 0 ? /* @__PURE__ */ p("div", {
230
+ className: "space-y-3",
231
+ children: [/* @__PURE__ */ f(E, {
232
+ treatment: "plain",
233
+ className: "border-border-seam",
234
+ children: /* @__PURE__ */ f("p", {
235
+ className: "px-2 py-8 text-center text-sm text-text-secondary",
236
+ children: o("eventfullymanaged.events.filters.empty", `No ${V} events on this page. Switch tabs or move to another page.`)
237
+ })
238
+ }), /* @__PURE__ */ f(c, {
239
+ offset: U.offset,
240
+ limit: U.limit,
241
+ loadedCount: Z.length,
242
+ total: X,
243
+ onPrev: U.prevPage,
244
+ onNext: U.nextPage,
245
+ itemLabel: "events",
246
+ loading: G
247
+ })]
248
+ }) : /* @__PURE__ */ p("div", {
144
249
  className: "space-y-3",
145
- children: [W.map((e) => {
146
- let t = e.metadata ?? {}, n = typeof t.venueId == "string" ? t.venueId : "", r = n ? G.get(n) : void 0, i = K.get(e.id), s = i?.registrations ?? 0, c = s > 0 ? Math.round((i?.checkedIn ?? 0) / s * 100) : 0;
147
- return /* @__PURE__ */ f(w, {
250
+ children: [$.map((e) => {
251
+ let t = e.metadata ?? {}, n = typeof t.venueId == "string" ? t.venueId : "", r = n ? le.get(n) : void 0, i = ue.get(e.id), a = i?.registrations ?? 0, c = a > 0 ? Math.round((i?.checkedIn ?? 0) / a * 100) : 0;
252
+ return /* @__PURE__ */ p(E, {
148
253
  treatment: "plain",
149
254
  role: "button",
150
255
  tabIndex: 0,
151
256
  "aria-label": e.name,
152
257
  className: "group relative cursor-pointer overflow-hidden border-border-seam p-0 outline-none transition-[box-shadow,border-color] duration-200 hover:border-border-strong hover:shadow-elevation-1 focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]",
153
- onClick: () => F(`/events/${e.id}`),
258
+ onClick: () => R(`/events/${e.id}`),
154
259
  onKeyDown: (t) => {
155
- (t.key === "Enter" || t.key === " ") && (t.preventDefault(), F(`/events/${e.id}`));
260
+ (t.key === "Enter" || t.key === " ") && (t.preventDefault(), R(`/events/${e.id}`));
156
261
  },
157
- children: [/* @__PURE__ */ d("span", {
262
+ children: [/* @__PURE__ */ f("span", {
158
263
  "aria-hidden": "true",
159
264
  className: "absolute inset-y-0 left-0 w-1.5 bg-gradient-to-b from-[var(--color-chart-1)] to-[var(--color-chart-2)]"
160
- }), /* @__PURE__ */ f("div", {
265
+ }), /* @__PURE__ */ p("div", {
161
266
  className: "flex flex-col gap-3 py-4 pl-5 pr-4 sm:flex-row sm:items-center sm:justify-between",
162
- children: [/* @__PURE__ */ f("div", {
267
+ children: [/* @__PURE__ */ p("div", {
163
268
  className: "min-w-0 space-y-2",
164
269
  children: [
165
- /* @__PURE__ */ f("div", {
270
+ /* @__PURE__ */ p("div", {
166
271
  className: "flex flex-wrap items-center gap-2",
167
272
  children: [
168
- /* @__PURE__ */ d("p", {
273
+ /* @__PURE__ */ f("p", {
169
274
  className: "truncate text-lg font-semibold text-text-primary",
170
275
  children: e.name
171
276
  }),
172
- e.status ? /* @__PURE__ */ d(x, {
173
- variant: M(e.status),
277
+ e.status ? /* @__PURE__ */ f(C, {
278
+ variant: N(e.status),
174
279
  className: "uppercase",
175
280
  children: e.status
176
281
  }) : null,
177
- e.type ? /* @__PURE__ */ d(x, {
282
+ e.type ? /* @__PURE__ */ f(C, {
178
283
  variant: "secondary",
179
284
  className: "capitalize",
180
285
  children: e.type.toLowerCase().replace(/_/g, " ")
181
286
  }) : null
182
287
  ]
183
288
  }),
184
- /* @__PURE__ */ f("div", {
289
+ /* @__PURE__ */ p("div", {
185
290
  className: "flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-text-secondary",
186
- children: [/* @__PURE__ */ f("span", {
291
+ children: [/* @__PURE__ */ p("span", {
187
292
  className: "flex items-center gap-1.5",
188
- children: [/* @__PURE__ */ d(p, { className: "h-4 w-4 shrink-0 text-text-muted" }), N(e.startAt, e.endAt)]
189
- }), /* @__PURE__ */ f("span", {
293
+ children: [/* @__PURE__ */ f(h, { className: "h-4 w-4 shrink-0 text-text-muted" }), P(e.startAt, e.endAt)]
294
+ }), /* @__PURE__ */ p("span", {
190
295
  className: "flex items-center gap-1.5",
191
- children: [/* @__PURE__ */ d(h, { className: "h-4 w-4 shrink-0 text-text-muted" }), r ?? a("eventfullymanaged.events.card.noVenue", "No venue")]
296
+ children: [/* @__PURE__ */ f(v, { className: "h-4 w-4 shrink-0 text-text-muted" }), r ?? o("eventfullymanaged.events.card.noVenue", "No venue")]
192
297
  })]
193
298
  }),
194
- /* @__PURE__ */ f("div", {
299
+ /* @__PURE__ */ p("div", {
195
300
  className: "flex flex-wrap items-center gap-x-4 gap-y-1 text-xs",
196
301
  children: [
197
- /* @__PURE__ */ d(P, {
198
- icon: v,
199
- label: a("eventfullymanaged.events.card.registered", "registered"),
200
- value: s.toLocaleString()
302
+ /* @__PURE__ */ f(L, {
303
+ icon: b,
304
+ label: o("eventfullymanaged.events.card.registered", "registered"),
305
+ value: a.toLocaleString()
201
306
  }),
202
- /* @__PURE__ */ d(P, {
203
- icon: v,
204
- label: a("eventfullymanaged.events.card.checkedIn", "checked in"),
307
+ /* @__PURE__ */ f(L, {
308
+ icon: b,
309
+ label: o("eventfullymanaged.events.card.checkedIn", "checked in"),
205
310
  value: `${c}%`
206
311
  }),
207
- /* @__PURE__ */ d(P, {
208
- icon: y,
209
- label: a("eventfullymanaged.events.card.capacity", "capacity"),
312
+ /* @__PURE__ */ f(L, {
313
+ icon: x,
314
+ label: o("eventfullymanaged.events.card.capacity", "capacity"),
210
315
  value: e.capacity == null ? "—" : e.capacity.toLocaleString()
211
316
  })
212
317
  ]
213
318
  })
214
319
  ]
215
- }), /* @__PURE__ */ d("div", {
320
+ }), /* @__PURE__ */ f("div", {
216
321
  className: "flex shrink-0 items-center gap-2 self-start sm:self-center",
217
322
  onClick: (e) => e.stopPropagation(),
218
323
  onKeyDown: (e) => e.stopPropagation(),
219
324
  role: "presentation",
220
- children: /* @__PURE__ */ d(o, {
325
+ children: /* @__PURE__ */ f(s, {
221
326
  ariaLabel: `Event actions for ${e.name}`,
222
- onView: () => F(`/events/${e.id}`),
223
- onEdit: () => F(`/events/${e.id}/edit`)
327
+ onView: () => R(`/events/${e.id}`),
328
+ onEdit: () => R(`/events/${e.id}/edit`)
224
329
  })
225
330
  })]
226
331
  })]
227
332
  }, e.id);
228
- }), /* @__PURE__ */ d(s, {
229
- offset: R.offset,
230
- limit: R.limit,
231
- loadedCount: W.length,
232
- total: U,
233
- onPrev: R.prevPage,
234
- onNext: R.nextPage,
333
+ }), /* @__PURE__ */ f(c, {
334
+ offset: U.offset,
335
+ limit: U.limit,
336
+ loadedCount: Z.length,
337
+ total: X,
338
+ onPrev: U.prevPage,
339
+ onNext: U.nextPage,
235
340
  itemLabel: "events",
236
- loading: B
341
+ loading: G
237
342
  })]
238
343
  }),
239
- /* @__PURE__ */ d(O, {
344
+ /* @__PURE__ */ f(te, {
240
345
  storageKey: "eventfullymanaged-events",
241
- title: a("eventfullymanaged.events.nextSteps.title", "Finish setting up your event"),
346
+ title: o("eventfullymanaged.events.nextSteps.title", "Finish setting up your event"),
242
347
  steps: [
243
348
  {
244
349
  id: "configure-registration",
245
- label: a("eventfullymanaged.events.nextSteps.registration", "Configure registration & tickets"),
246
- description: a("eventfullymanaged.events.nextSteps.registrationDesc", "Open an event and define ticket types so people can register."),
247
- icon: /* @__PURE__ */ d(_, { className: "h-4 w-4" }),
248
- onClick: () => F("/registration")
350
+ label: o("eventfullymanaged.events.nextSteps.registration", "Configure registration & tickets"),
351
+ description: o("eventfullymanaged.events.nextSteps.registrationDesc", "Open an event and define ticket types so people can register."),
352
+ icon: /* @__PURE__ */ f(y, { className: "h-4 w-4" }),
353
+ onClick: () => R("/registration")
249
354
  },
250
355
  {
251
356
  id: "plan-networking",
252
- label: a("eventfullymanaged.events.nextSteps.networking", "Plan networking & sessions"),
253
- description: a("eventfullymanaged.events.nextSteps.networkingDesc", "Set up attendee matchmaking and meeting schedules."),
254
- icon: /* @__PURE__ */ d(y, { className: "h-4 w-4" }),
255
- onClick: () => F("/networking")
357
+ label: o("eventfullymanaged.events.nextSteps.networking", "Plan networking & sessions"),
358
+ description: o("eventfullymanaged.events.nextSteps.networkingDesc", "Set up attendee matchmaking and meeting schedules."),
359
+ icon: /* @__PURE__ */ f(x, { className: "h-4 w-4" }),
360
+ onClick: () => R("/networking")
256
361
  },
257
362
  {
258
363
  id: "announce",
259
- label: a("eventfullymanaged.events.nextSteps.announce", "Announce to attendees"),
260
- description: a("eventfullymanaged.events.nextSteps.announceDesc", "Send announcements and run communication campaigns."),
261
- icon: /* @__PURE__ */ d(m, { className: "h-4 w-4" }),
262
- onClick: () => F("/communication")
364
+ label: o("eventfullymanaged.events.nextSteps.announce", "Announce to attendees"),
365
+ description: o("eventfullymanaged.events.nextSteps.announceDesc", "Send announcements and run communication campaigns."),
366
+ icon: /* @__PURE__ */ f(_, { className: "h-4 w-4" }),
367
+ onClick: () => R("/communication")
263
368
  }
264
369
  ]
265
370
  })
@@ -267,6 +372,6 @@ function F() {
267
372
  });
268
373
  }
269
374
  //#endregion
270
- export { F as default };
375
+ export { R as default };
271
376
 
272
377
  //# sourceMappingURL=EventsPage.js.map