@open-mercato/core 0.6.3-develop.3491.1.4700ef658a → 0.6.3-develop.3502.1.234f53f93c
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/modules/data_sync/api/options.js +2 -0
- package/dist/modules/data_sync/api/options.js.map +2 -2
- package/dist/modules/data_sync/api/run.js +9 -0
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +3 -2
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js +8 -3
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
- package/dist/modules/messages/components/MessagesInboxPageClient.js +24 -1
- package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
- package/dist/modules/sync_excel/lib/adapters/customers.js +1 -0
- package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/data_sync/api/options.ts +2 -0
- package/src/modules/data_sync/api/run.ts +9 -0
- package/src/modules/data_sync/backend/data-sync/page.tsx +10 -0
- package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +17 -3
- package/src/modules/data_sync/lib/adapter.ts +9 -0
- package/src/modules/messages/components/MessagesInboxPageClient.tsx +29 -1
- package/src/modules/sync_excel/lib/adapters/customers.ts +1 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/data_sync/components/IntegrationScheduleTab.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n CalendarClock,\n Play,\n RefreshCw,\n Save,\n ShieldAlert,\n ShieldCheck,\n Trash2,\n} from 'lucide-react'\n\ntype SyncOption = {\n integrationId: string\n title: string\n direction: 'import' | 'export' | 'bidirectional'\n supportedEntities: string[]\n hasCredentials: boolean\n isEnabled: boolean\n}\n\ntype SyncOptionsResponse = {\n items?: SyncOption[]\n}\n\ntype SyncScheduleRecord = {\n id: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n lastRunAt: string | null\n}\n\ntype SyncSchedulesResponse = {\n items?: SyncScheduleRecord[]\n}\n\ntype SyncScheduleEditorState = {\n id?: string\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n lastRunAt: string | null\n}\n\ntype IntegrationScheduleTabProps = {\n integrationId: string\n hasCredentials: boolean\n isEnabled: boolean\n}\n\nconst DEFAULT_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'\n\nfunction formatEntityTypeLabel(entityType: string): string {\n return entityType\n .replace(/[_-]+/g, ' ')\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase())\n}\n\nfunction buildDefaultScheduleState(entityType: string): SyncScheduleEditorState {\n const normalized = entityType.trim().toLowerCase()\n const longerInterval = normalized === 'categories' || normalized === 'attributes'\n return {\n scheduleType: 'interval',\n scheduleValue: longerInterval ? '6h' : '1h',\n timezone: DEFAULT_TIMEZONE,\n fullSync: normalized !== 'products',\n isEnabled: true,\n lastRunAt: null,\n }\n}\n\nfunction getSupportedDirections(direction: SyncOption['direction'] | null | undefined): Array<'import' | 'export'> {\n if (direction === 'export') return ['export']\n if (direction === 'bidirectional') return ['import', 'export']\n return ['import']\n}\n\nfunction buildScheduleKey(entityType: string, direction: 'import' | 'export'): string {\n return `${entityType}:${direction}`\n}\n\nfunction buildScheduleEditors(\n entityTypes: string[],\n directions: Array<'import' | 'export'>,\n records: SyncScheduleRecord[],\n): Record<string, SyncScheduleEditorState> {\n const nextEntries: Array<[string, SyncScheduleEditorState]> = []\n for (const entityType of entityTypes) {\n for (const direction of directions) {\n const record = records.find((item) => item.entityType === entityType && item.direction === direction)\n nextEntries.push([\n buildScheduleKey(entityType, direction),\n record\n ? {\n id: record.id,\n scheduleType: record.scheduleType,\n scheduleValue: record.scheduleValue,\n timezone: record.timezone,\n fullSync: record.fullSync,\n isEnabled: record.isEnabled,\n lastRunAt: record.lastRunAt,\n }\n : buildDefaultScheduleState(entityType),\n ])\n }\n }\n return Object.fromEntries(nextEntries)\n}\n\nexport function IntegrationScheduleTab(props: IntegrationScheduleTabProps) {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const [option, setOption] = React.useState<SyncOption | null>(null)\n const [schedules, setSchedules] = React.useState<Record<string, SyncScheduleEditorState>>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [runningKey, setRunningKey] = React.useState<string | null>(null)\n const [savingKey, setSavingKey] = React.useState<string | null>(null)\n const [deletingKey, setDeletingKey] = React.useState<string | null>(null)\n\n const load = React.useCallback(async () => {\n setIsLoading(true)\n try {\n const [optionsCall, schedulesCall] = await Promise.all([\n apiCall<SyncOptionsResponse>('/api/data_sync/options', undefined, { fallback: { items: [] } }),\n apiCall<SyncSchedulesResponse>(`/api/data_sync/schedules?integrationId=${encodeURIComponent(props.integrationId)}&page=1&pageSize=100`, undefined, { fallback: { items: [] } }),\n ])\n\n const resolvedOption = (optionsCall.result?.items ?? []).find((item) => item.integrationId === props.integrationId) ?? null\n setOption(resolvedOption)\n\n const supportedEntities = resolvedOption?.supportedEntities ?? []\n const supportedDirections = getSupportedDirections(resolvedOption?.direction)\n setSchedules(buildScheduleEditors(supportedEntities, supportedDirections, schedulesCall.result?.items ?? []))\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.integrationTab.loadError', 'Failed to load sync schedules.')\n flash(message, 'error')\n } finally {\n setIsLoading(false)\n }\n }, [props.integrationId, t])\n\n React.useEffect(() => {\n void load()\n }, [load, scopeVersion])\n\n const supportedDirections = React.useMemo(\n () => getSupportedDirections(option?.direction),\n [option?.direction],\n )\n\n const rows = React.useMemo(\n () => (option?.supportedEntities ?? []).flatMap((entityType) => (\n supportedDirections.map((direction) => ({\n entityType,\n direction,\n key: buildScheduleKey(entityType, direction),\n }))\n )),\n [option?.supportedEntities, supportedDirections],\n )\n\n const updateScheduleEditor = React.useCallback((key: string, patch: Partial<SyncScheduleEditorState>, entityType: string) => {\n setSchedules((current) => ({\n ...current,\n [key]: {\n ...(current[key] ?? buildDefaultScheduleState(entityType)),\n ...patch,\n },\n }))\n }, [])\n\n const handleStartSync = React.useCallback(async (entityType: string, direction: 'import' | 'export', scheduleKey: string) => {\n if (!props.isEnabled) {\n flash(t('data_sync.integrationTab.integrationDisabled', 'Enable the integration before starting a sync.'), 'error')\n return\n }\n if (!props.hasCredentials) {\n flash(t('data_sync.integrationTab.credentialsMissing', 'Configure credentials before starting a sync.'), 'error')\n return\n }\n\n setRunningKey(scheduleKey)\n try {\n const scheduleState = schedules[scheduleKey] ?? buildDefaultScheduleState(entityType)\n const call = await apiCall<{ id: string }>('/api/data_sync/run', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n integrationId: props.integrationId,\n entityType,\n direction,\n fullSync: scheduleState.fullSync,\n batchSize: 100,\n }),\n }, { fallback: null })\n\n if (!call.ok) {\n throw new Error((call.result as { error?: string } | null)?.error ?? 'Failed to start sync')\n }\n\n flash(t('data_sync.integrationTab.runStarted', 'Sync run started.'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.integrationTab.runError', 'Failed to start sync.')\n flash(message, 'error')\n } finally {\n setRunningKey(null)\n }\n }, [props.hasCredentials, props.integrationId, props.isEnabled, schedules, t])\n\n const handleSaveSchedule = React.useCallback(async (entityType: string, direction: 'import' | 'export', scheduleKey: string) => {\n const scheduleState = schedules[scheduleKey] ?? buildDefaultScheduleState(entityType)\n setSavingKey(scheduleKey)\n try {\n const call = await apiCall<SyncScheduleRecord>('/api/data_sync/schedules', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n integrationId: props.integrationId,\n entityType,\n direction,\n scheduleType: scheduleState.scheduleType,\n scheduleValue: scheduleState.scheduleValue,\n timezone: scheduleState.timezone,\n fullSync: scheduleState.fullSync,\n isEnabled: scheduleState.isEnabled,\n }),\n }, { fallback: null })\n\n if (!call.ok || !call.result) {\n throw new Error((call.result as { error?: string } | null)?.error ?? 'Failed to save schedule')\n }\n\n updateScheduleEditor(scheduleKey, {\n id: call.result.id,\n scheduleType: call.result.scheduleType,\n scheduleValue: call.result.scheduleValue,\n timezone: call.result.timezone,\n fullSync: call.result.fullSync,\n isEnabled: call.result.isEnabled,\n lastRunAt: call.result.lastRunAt,\n }, entityType)\n flash(t('data_sync.dashboard.schedule.success', 'Recurring schedule saved'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule')\n flash(message, 'error')\n } finally {\n setSavingKey(null)\n }\n }, [props.integrationId, schedules, t, updateScheduleEditor])\n\n const handleDeleteSchedule = React.useCallback(async (entityType: string, scheduleKey: string) => {\n const scheduleState = schedules[scheduleKey]\n if (!scheduleState?.id) {\n updateScheduleEditor(scheduleKey, buildDefaultScheduleState(entityType), entityType)\n return\n }\n\n setDeletingKey(scheduleKey)\n try {\n const call = await apiCall(`/api/data_sync/schedules/${encodeURIComponent(scheduleState.id)}`, {\n method: 'DELETE',\n }, { fallback: null })\n\n if (!call.ok) {\n throw new Error((call.result as { error?: string } | null)?.error ?? 'Failed to delete schedule')\n }\n\n setSchedules((current) => ({\n ...current,\n [scheduleKey]: buildDefaultScheduleState(entityType),\n }))\n flash(t('data_sync.dashboard.schedule.deleteSuccess', 'Recurring schedule removed'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.schedule.deleteError', 'Failed to remove recurring schedule')\n flash(message, 'error')\n } finally {\n setDeletingKey(null)\n }\n }, [schedules, t, updateScheduleEditor])\n\n if (isLoading) {\n return (\n <div className=\"flex min-h-40 items-center justify-center rounded-lg border bg-card\">\n <Spinner />\n </div>\n )\n }\n\n if (!option) {\n return (\n <Alert variant=\"warning\">\n <AlertDescription>\n {t('data_sync.integrationTab.notAvailable', 'This integration is not registered as a data sync provider.')}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <section className=\"space-y-4 rounded-lg border bg-card p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\" className={props.isEnabled ? 'border-emerald-300 text-emerald-700' : 'border-amber-300 text-amber-700'}>\n {props.isEnabled ? <ShieldCheck className=\"mr-2 h-3.5 w-3.5\" /> : <ShieldAlert className=\"mr-2 h-3.5 w-3.5\" />}\n {props.isEnabled\n ? t('data_sync.dashboard.start.status.enabled', 'Integration enabled')\n : t('data_sync.dashboard.start.status.disabled', 'Integration disabled')}\n </Badge>\n <Badge variant=\"outline\" className={props.hasCredentials ? 'border-sky-300 text-sky-700' : 'border-amber-300 text-amber-700'}>\n <CalendarClock className=\"mr-2 h-3.5 w-3.5\" />\n {props.hasCredentials\n ? t('data_sync.dashboard.start.status.credentialsReady', 'Credentials ready')\n : t('data_sync.dashboard.start.status.credentialsMissing', 'Credentials missing')}\n </Badge>\n </div>\n <div>\n <h3 className=\"text-base font-semibold\">{t('data_sync.integrationTab.title', 'Sync schedules')}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {t('data_sync.integrationTab.description', 'Run one-off syncs or save recurring schedules for every supported entity directly from the integration detail page.')}\n </p>\n </div>\n </div>\n <Button type=\"button\" variant=\"outline\" onClick={() => void load()} disabled={isLoading}>\n <RefreshCw className=\"mr-2 h-4 w-4\" />\n {t('data_sync.integrationTab.refresh', 'Refresh')}\n </Button>\n </div>\n\n {!props.isEnabled ? (\n <Alert variant=\"warning\">\n <AlertDescription>\n {t('data_sync.integrationTab.integrationDisabledNotice', 'The integration is disabled. You can save schedules now, but runs will stay blocked until the integration is enabled.')}\n </AlertDescription>\n </Alert>\n ) : null}\n\n {!props.hasCredentials ? (\n <Alert variant=\"warning\">\n <AlertDescription>\n {t('data_sync.integrationTab.credentialsMissingNotice', 'Credentials are still missing. Save schedules first if you want, but manual and scheduled runs will fail until credentials are configured.')}\n </AlertDescription>\n </Alert>\n ) : null}\n\n {rows.length === 0 ? (\n <Alert variant=\"info\">\n <AlertDescription>\n {t('data_sync.integrationTab.empty', 'This provider does not expose any schedulable sync entities yet.')}\n </AlertDescription>\n </Alert>\n ) : (\n <div className=\"overflow-x-auto rounded-lg border\">\n <table className=\"w-full min-w-[1080px] text-sm\">\n <thead className=\"bg-muted/50 text-left\">\n <tr>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.columns.entityType', 'Entity Type')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.columns.direction', 'Direction')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.schedule.type', 'Schedule type')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.integrationTab.columns.value', 'Value')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.schedule.timezone', 'Timezone')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.start.fullSync', 'Run as full sync')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.schedule.enabled', 'Schedule enabled')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.integrationTab.columns.lastRun', 'Last run')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.integrationTab.columns.actions', 'Actions')}</th>\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => {\n const scheduleState = schedules[row.key] ?? buildDefaultScheduleState(row.entityType)\n const isRunning = runningKey === row.key\n const isSaving = savingKey === row.key\n const isDeleting = deletingKey === row.key\n const controlsDisabled = isRunning || isSaving || isDeleting\n return (\n <tr key={row.key} className=\"border-t align-top\">\n <td className=\"px-3 py-3 font-medium\">{formatEntityTypeLabel(row.entityType)}</td>\n <td className=\"px-3 py-3\">{t(`data_sync.dashboard.direction.${row.direction}`, row.direction === 'import' ? 'Import' : 'Export')}</td>\n <td className=\"px-3 py-3\">\n <Select\n value={scheduleState.scheduleType}\n onValueChange={(value) => updateScheduleEditor(row.key, {\n scheduleType: value === 'cron' ? 'cron' : 'interval',\n }, row.entityType)}\n disabled={controlsDisabled}\n >\n <SelectTrigger size=\"lg\" className=\"min-w-32\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"interval\">{t('data_sync.dashboard.schedule.interval', 'Interval')}</SelectItem>\n <SelectItem value=\"cron\">{t('data_sync.dashboard.schedule.cron', 'Cron')}</SelectItem>\n </SelectContent>\n </Select>\n </td>\n <td className=\"px-3 py-3\">\n <Input\n value={scheduleState.scheduleValue}\n onChange={(event) => updateScheduleEditor(row.key, { scheduleValue: event.target.value }, row.entityType)}\n disabled={controlsDisabled}\n placeholder={scheduleState.scheduleType === 'cron' ? '0 * * * *' : '1h'}\n />\n </td>\n <td className=\"px-3 py-3\">\n <Input\n value={scheduleState.timezone}\n onChange={(event) => updateScheduleEditor(row.key, { timezone: event.target.value }, row.entityType)}\n disabled={controlsDisabled}\n />\n </td>\n <td className=\"px-3 py-3\">\n <label className=\"flex min-h-10 items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={scheduleState.fullSync}\n onChange={(event) => updateScheduleEditor(row.key, { fullSync: event.target.checked }, row.entityType)}\n disabled={controlsDisabled}\n />\n <span>{t('data_sync.integrationTab.fullSyncShort', 'Full')}</span>\n </label>\n </td>\n <td className=\"px-3 py-3\">\n <label className=\"flex min-h-10 items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={scheduleState.isEnabled}\n onChange={(event) => updateScheduleEditor(row.key, { isEnabled: event.target.checked }, row.entityType)}\n disabled={controlsDisabled}\n />\n <span>{scheduleState.isEnabled ? t('data_sync.dashboard.schedule.status.shortEnabled', 'Scheduled') : t('data_sync.dashboard.schedule.status.shortDisabled', 'Paused')}</span>\n </label>\n </td>\n <td className=\"px-3 py-3 text-muted-foreground\">\n {scheduleState.lastRunAt\n ? new Date(scheduleState.lastRunAt).toLocaleString()\n : scheduleState.id\n ? t('data_sync.dashboard.schedule.neverRun', 'Saved, but no scheduled execution has completed yet.')\n : t('data_sync.dashboard.schedule.none', 'No recurring schedule saved for this target yet.')}\n </td>\n <td className=\"px-3 py-3\">\n <div className=\"flex flex-wrap gap-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => void handleStartSync(row.entityType, row.direction, row.key)}\n disabled={controlsDisabled || !props.isEnabled || !props.hasCredentials}\n >\n {isRunning ? <Spinner className=\"mr-2 h-4 w-4\" /> : <Play className=\"mr-2 h-4 w-4\" />}\n {isRunning\n ? t('data_sync.integrationTab.starting', 'Starting...')\n : t('data_sync.integrationTab.start', 'Run now')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => void handleSaveSchedule(row.entityType, row.direction, row.key)}\n disabled={controlsDisabled}\n >\n {isSaving ? <Spinner className=\"mr-2 h-4 w-4\" /> : <Save className=\"mr-2 h-4 w-4\" />}\n {isSaving\n ? t('data_sync.dashboard.schedule.saving', 'Saving...')\n : t('data_sync.dashboard.schedule.save', 'Save recurring schedule')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => void handleDeleteSchedule(row.entityType, row.key)}\n disabled={controlsDisabled || !scheduleState.id}\n >\n {isDeleting ? <Spinner className=\"mr-2 h-4 w-4\" /> : <Trash2 className=\"mr-2 h-4 w-4\" />}\n {isDeleting\n ? t('data_sync.dashboard.schedule.deleting', 'Removing...')\n : t('data_sync.dashboard.schedule.delete', 'Remove schedule')}\n </Button>\n </div>\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n )}\n\n <div className=\"grid gap-3 rounded-lg border border-dashed p-4 text-sm text-muted-foreground md:grid-cols-3\">\n <div className=\"space-y-1\">\n <Label>{t('data_sync.dashboard.schedule.interval', 'Interval')}</Label>\n <p>{t('data_sync.dashboard.schedule.intervalHelp', 'Example: `1h`, `6h`, or `24h` for repeating intervals.')}</p>\n </div>\n <div className=\"space-y-1\">\n <Label>{t('data_sync.dashboard.schedule.cron', 'Cron')}</Label>\n <p>{t('data_sync.dashboard.schedule.cronHelp', 'Example: `0 * * * *` runs at the start of every hour.')}</p>\n </div>\n <div className=\"space-y-1\">\n <Label>{t('data_sync.integrationTab.runNowTitle', 'Manual runs')}</Label>\n <p>{t('data_sync.integrationTab.runNowHelp', 'Run now uses the full-sync checkbox from the same row and starts progress tracking immediately in Data Sync.')}</p>\n </div>\n </div>\n </section>\n )\n}\n\nexport default IntegrationScheduleTab\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Label } from '@open-mercato/ui/primitives/label'\nimport { Alert, AlertDescription } from '@open-mercato/ui/primitives/alert'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n CalendarClock,\n Play,\n RefreshCw,\n Save,\n ShieldAlert,\n ShieldCheck,\n Trash2,\n} from 'lucide-react'\n\ntype SyncOption = {\n integrationId: string\n title: string\n direction: 'import' | 'export' | 'bidirectional'\n runMode?: 'generic' | 'provider'\n canStartRun?: boolean\n supportedEntities: string[]\n hasCredentials: boolean\n isEnabled: boolean\n}\n\ntype SyncOptionsResponse = {\n items?: SyncOption[]\n}\n\ntype SyncScheduleRecord = {\n id: string\n integrationId: string\n entityType: string\n direction: 'import' | 'export'\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n lastRunAt: string | null\n}\n\ntype SyncSchedulesResponse = {\n items?: SyncScheduleRecord[]\n}\n\ntype SyncScheduleEditorState = {\n id?: string\n scheduleType: 'cron' | 'interval'\n scheduleValue: string\n timezone: string\n fullSync: boolean\n isEnabled: boolean\n lastRunAt: string | null\n}\n\ntype IntegrationScheduleTabProps = {\n integrationId: string\n hasCredentials: boolean\n isEnabled: boolean\n}\n\nconst DEFAULT_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'\n\nfunction formatEntityTypeLabel(entityType: string): string {\n return entityType\n .replace(/[_-]+/g, ' ')\n .replace(/\\b\\w/g, (letter) => letter.toUpperCase())\n}\n\nfunction buildDefaultScheduleState(entityType: string): SyncScheduleEditorState {\n const normalized = entityType.trim().toLowerCase()\n const longerInterval = normalized === 'categories' || normalized === 'attributes'\n return {\n scheduleType: 'interval',\n scheduleValue: longerInterval ? '6h' : '1h',\n timezone: DEFAULT_TIMEZONE,\n fullSync: normalized !== 'products',\n isEnabled: true,\n lastRunAt: null,\n }\n}\n\nfunction getSupportedDirections(direction: SyncOption['direction'] | null | undefined): Array<'import' | 'export'> {\n if (direction === 'export') return ['export']\n if (direction === 'bidirectional') return ['import', 'export']\n return ['import']\n}\n\nfunction buildScheduleKey(entityType: string, direction: 'import' | 'export'): string {\n return `${entityType}:${direction}`\n}\n\nfunction buildScheduleEditors(\n entityTypes: string[],\n directions: Array<'import' | 'export'>,\n records: SyncScheduleRecord[],\n): Record<string, SyncScheduleEditorState> {\n const nextEntries: Array<[string, SyncScheduleEditorState]> = []\n for (const entityType of entityTypes) {\n for (const direction of directions) {\n const record = records.find((item) => item.entityType === entityType && item.direction === direction)\n nextEntries.push([\n buildScheduleKey(entityType, direction),\n record\n ? {\n id: record.id,\n scheduleType: record.scheduleType,\n scheduleValue: record.scheduleValue,\n timezone: record.timezone,\n fullSync: record.fullSync,\n isEnabled: record.isEnabled,\n lastRunAt: record.lastRunAt,\n }\n : buildDefaultScheduleState(entityType),\n ])\n }\n }\n return Object.fromEntries(nextEntries)\n}\n\nexport function IntegrationScheduleTab(props: IntegrationScheduleTabProps) {\n const t = useT()\n const scopeVersion = useOrganizationScopeVersion()\n const [option, setOption] = React.useState<SyncOption | null>(null)\n const [schedules, setSchedules] = React.useState<Record<string, SyncScheduleEditorState>>({})\n const [isLoading, setIsLoading] = React.useState(true)\n const [runningKey, setRunningKey] = React.useState<string | null>(null)\n const [savingKey, setSavingKey] = React.useState<string | null>(null)\n const [deletingKey, setDeletingKey] = React.useState<string | null>(null)\n\n const load = React.useCallback(async () => {\n setIsLoading(true)\n try {\n const [optionsCall, schedulesCall] = await Promise.all([\n apiCall<SyncOptionsResponse>('/api/data_sync/options', undefined, { fallback: { items: [] } }),\n apiCall<SyncSchedulesResponse>(`/api/data_sync/schedules?integrationId=${encodeURIComponent(props.integrationId)}&page=1&pageSize=100`, undefined, { fallback: { items: [] } }),\n ])\n\n const resolvedOption = (optionsCall.result?.items ?? []).find((item) => item.integrationId === props.integrationId) ?? null\n setOption(resolvedOption)\n\n const supportedEntities = resolvedOption?.supportedEntities ?? []\n const supportedDirections = getSupportedDirections(resolvedOption?.direction)\n setSchedules(buildScheduleEditors(supportedEntities, supportedDirections, schedulesCall.result?.items ?? []))\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.integrationTab.loadError', 'Failed to load sync schedules.')\n flash(message, 'error')\n } finally {\n setIsLoading(false)\n }\n }, [props.integrationId, t])\n\n React.useEffect(() => {\n void load()\n }, [load, scopeVersion])\n\n const supportedDirections = React.useMemo(\n () => getSupportedDirections(option?.direction),\n [option?.direction],\n )\n\n const rows = React.useMemo(\n () => (option?.supportedEntities ?? []).flatMap((entityType) => (\n supportedDirections.map((direction) => ({\n entityType,\n direction,\n key: buildScheduleKey(entityType, direction),\n }))\n )),\n [option?.supportedEntities, supportedDirections],\n )\n\n const updateScheduleEditor = React.useCallback((key: string, patch: Partial<SyncScheduleEditorState>, entityType: string) => {\n setSchedules((current) => ({\n ...current,\n [key]: {\n ...(current[key] ?? buildDefaultScheduleState(entityType)),\n ...patch,\n },\n }))\n }, [])\n\n const handleStartSync = React.useCallback(async (entityType: string, direction: 'import' | 'export', scheduleKey: string) => {\n if (!props.isEnabled) {\n flash(t('data_sync.integrationTab.integrationDisabled', 'Enable the integration before starting a sync.'), 'error')\n return\n }\n if (!props.hasCredentials) {\n flash(t('data_sync.integrationTab.credentialsMissing', 'Configure credentials before starting a sync.'), 'error')\n return\n }\n if (option?.canStartRun === false) {\n flash(t('data_sync.integrationTab.providerManaged', 'Start this integration from its provider-specific setup flow.'), 'error')\n return\n }\n\n setRunningKey(scheduleKey)\n try {\n const scheduleState = schedules[scheduleKey] ?? buildDefaultScheduleState(entityType)\n const call = await apiCall<{ id: string }>('/api/data_sync/run', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n integrationId: props.integrationId,\n entityType,\n direction,\n fullSync: scheduleState.fullSync,\n batchSize: 100,\n }),\n }, { fallback: null })\n\n if (!call.ok) {\n throw new Error((call.result as { error?: string } | null)?.error ?? 'Failed to start sync')\n }\n\n flash(t('data_sync.integrationTab.runStarted', 'Sync run started.'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.integrationTab.runError', 'Failed to start sync.')\n flash(message, 'error')\n } finally {\n setRunningKey(null)\n }\n }, [option?.canStartRun, props.hasCredentials, props.integrationId, props.isEnabled, schedules, t])\n\n const handleSaveSchedule = React.useCallback(async (entityType: string, direction: 'import' | 'export', scheduleKey: string) => {\n const scheduleState = schedules[scheduleKey] ?? buildDefaultScheduleState(entityType)\n setSavingKey(scheduleKey)\n try {\n const call = await apiCall<SyncScheduleRecord>('/api/data_sync/schedules', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n integrationId: props.integrationId,\n entityType,\n direction,\n scheduleType: scheduleState.scheduleType,\n scheduleValue: scheduleState.scheduleValue,\n timezone: scheduleState.timezone,\n fullSync: scheduleState.fullSync,\n isEnabled: scheduleState.isEnabled,\n }),\n }, { fallback: null })\n\n if (!call.ok || !call.result) {\n throw new Error((call.result as { error?: string } | null)?.error ?? 'Failed to save schedule')\n }\n\n updateScheduleEditor(scheduleKey, {\n id: call.result.id,\n scheduleType: call.result.scheduleType,\n scheduleValue: call.result.scheduleValue,\n timezone: call.result.timezone,\n fullSync: call.result.fullSync,\n isEnabled: call.result.isEnabled,\n lastRunAt: call.result.lastRunAt,\n }, entityType)\n flash(t('data_sync.dashboard.schedule.success', 'Recurring schedule saved'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.schedule.error', 'Failed to save recurring schedule')\n flash(message, 'error')\n } finally {\n setSavingKey(null)\n }\n }, [props.integrationId, schedules, t, updateScheduleEditor])\n\n const handleDeleteSchedule = React.useCallback(async (entityType: string, scheduleKey: string) => {\n const scheduleState = schedules[scheduleKey]\n if (!scheduleState?.id) {\n updateScheduleEditor(scheduleKey, buildDefaultScheduleState(entityType), entityType)\n return\n }\n\n setDeletingKey(scheduleKey)\n try {\n const call = await apiCall(`/api/data_sync/schedules/${encodeURIComponent(scheduleState.id)}`, {\n method: 'DELETE',\n }, { fallback: null })\n\n if (!call.ok) {\n throw new Error((call.result as { error?: string } | null)?.error ?? 'Failed to delete schedule')\n }\n\n setSchedules((current) => ({\n ...current,\n [scheduleKey]: buildDefaultScheduleState(entityType),\n }))\n flash(t('data_sync.dashboard.schedule.deleteSuccess', 'Recurring schedule removed'), 'success')\n } catch (error) {\n const message = error instanceof Error ? error.message : t('data_sync.dashboard.schedule.deleteError', 'Failed to remove recurring schedule')\n flash(message, 'error')\n } finally {\n setDeletingKey(null)\n }\n }, [schedules, t, updateScheduleEditor])\n\n if (isLoading) {\n return (\n <div className=\"flex min-h-40 items-center justify-center rounded-lg border bg-card\">\n <Spinner />\n </div>\n )\n }\n\n if (!option) {\n return (\n <Alert variant=\"warning\">\n <AlertDescription>\n {t('data_sync.integrationTab.notAvailable', 'This integration is not registered as a data sync provider.')}\n </AlertDescription>\n </Alert>\n )\n }\n\n return (\n <section className=\"space-y-4 rounded-lg border bg-card p-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <Badge variant=\"outline\" className={props.isEnabled ? 'border-emerald-300 text-emerald-700' : 'border-amber-300 text-amber-700'}>\n {props.isEnabled ? <ShieldCheck className=\"mr-2 h-3.5 w-3.5\" /> : <ShieldAlert className=\"mr-2 h-3.5 w-3.5\" />}\n {props.isEnabled\n ? t('data_sync.dashboard.start.status.enabled', 'Integration enabled')\n : t('data_sync.dashboard.start.status.disabled', 'Integration disabled')}\n </Badge>\n <Badge variant=\"outline\" className={props.hasCredentials ? 'border-sky-300 text-sky-700' : 'border-amber-300 text-amber-700'}>\n <CalendarClock className=\"mr-2 h-3.5 w-3.5\" />\n {props.hasCredentials\n ? t('data_sync.dashboard.start.status.credentialsReady', 'Credentials ready')\n : t('data_sync.dashboard.start.status.credentialsMissing', 'Credentials missing')}\n </Badge>\n </div>\n <div>\n <h3 className=\"text-base font-semibold\">{t('data_sync.integrationTab.title', 'Sync schedules')}</h3>\n <p className=\"text-sm text-muted-foreground\">\n {t('data_sync.integrationTab.description', 'Run one-off syncs or save recurring schedules for every supported entity directly from the integration detail page.')}\n </p>\n </div>\n </div>\n <Button type=\"button\" variant=\"outline\" onClick={() => void load()} disabled={isLoading}>\n <RefreshCw className=\"mr-2 h-4 w-4\" />\n {t('data_sync.integrationTab.refresh', 'Refresh')}\n </Button>\n </div>\n\n {!props.isEnabled ? (\n <Alert variant=\"warning\">\n <AlertDescription>\n {t('data_sync.integrationTab.integrationDisabledNotice', 'The integration is disabled. You can save schedules now, but runs will stay blocked until the integration is enabled.')}\n </AlertDescription>\n </Alert>\n ) : null}\n\n {!props.hasCredentials ? (\n <Alert variant=\"warning\">\n <AlertDescription>\n {t('data_sync.integrationTab.credentialsMissingNotice', 'Credentials are still missing. Save schedules first if you want, but manual and scheduled runs will fail until credentials are configured.')}\n </AlertDescription>\n </Alert>\n ) : null}\n\n {option.canStartRun === false ? (\n <Alert variant=\"info\">\n <AlertDescription>\n {t('data_sync.integrationTab.providerManagedNotice', 'This provider needs its own setup flow before a run can start. Use the provider tab on this page instead of generic schedules.')}\n </AlertDescription>\n </Alert>\n ) : null}\n\n {rows.length === 0 ? (\n <Alert variant=\"info\">\n <AlertDescription>\n {t('data_sync.integrationTab.empty', 'This provider does not expose any schedulable sync entities yet.')}\n </AlertDescription>\n </Alert>\n ) : (\n <div className=\"overflow-x-auto rounded-lg border\">\n <table className=\"w-full min-w-[1080px] text-sm\">\n <thead className=\"bg-muted/50 text-left\">\n <tr>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.columns.entityType', 'Entity Type')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.columns.direction', 'Direction')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.schedule.type', 'Schedule type')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.integrationTab.columns.value', 'Value')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.schedule.timezone', 'Timezone')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.start.fullSync', 'Run as full sync')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.dashboard.schedule.enabled', 'Schedule enabled')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.integrationTab.columns.lastRun', 'Last run')}</th>\n <th className=\"px-3 py-2 font-medium\">{t('data_sync.integrationTab.columns.actions', 'Actions')}</th>\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => {\n const scheduleState = schedules[row.key] ?? buildDefaultScheduleState(row.entityType)\n const isRunning = runningKey === row.key\n const isSaving = savingKey === row.key\n const isDeleting = deletingKey === row.key\n const controlsDisabled = isRunning || isSaving || isDeleting\n return (\n <tr key={row.key} className=\"border-t align-top\">\n <td className=\"px-3 py-3 font-medium\">{formatEntityTypeLabel(row.entityType)}</td>\n <td className=\"px-3 py-3\">{t(`data_sync.dashboard.direction.${row.direction}`, row.direction === 'import' ? 'Import' : 'Export')}</td>\n <td className=\"px-3 py-3\">\n <Select\n value={scheduleState.scheduleType}\n onValueChange={(value) => updateScheduleEditor(row.key, {\n scheduleType: value === 'cron' ? 'cron' : 'interval',\n }, row.entityType)}\n disabled={controlsDisabled}\n >\n <SelectTrigger size=\"lg\" className=\"min-w-32\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"interval\">{t('data_sync.dashboard.schedule.interval', 'Interval')}</SelectItem>\n <SelectItem value=\"cron\">{t('data_sync.dashboard.schedule.cron', 'Cron')}</SelectItem>\n </SelectContent>\n </Select>\n </td>\n <td className=\"px-3 py-3\">\n <Input\n value={scheduleState.scheduleValue}\n onChange={(event) => updateScheduleEditor(row.key, { scheduleValue: event.target.value }, row.entityType)}\n disabled={controlsDisabled}\n placeholder={scheduleState.scheduleType === 'cron' ? '0 * * * *' : '1h'}\n />\n </td>\n <td className=\"px-3 py-3\">\n <Input\n value={scheduleState.timezone}\n onChange={(event) => updateScheduleEditor(row.key, { timezone: event.target.value }, row.entityType)}\n disabled={controlsDisabled}\n />\n </td>\n <td className=\"px-3 py-3\">\n <label className=\"flex min-h-10 items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={scheduleState.fullSync}\n onChange={(event) => updateScheduleEditor(row.key, { fullSync: event.target.checked }, row.entityType)}\n disabled={controlsDisabled}\n />\n <span>{t('data_sync.integrationTab.fullSyncShort', 'Full')}</span>\n </label>\n </td>\n <td className=\"px-3 py-3\">\n <label className=\"flex min-h-10 items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={scheduleState.isEnabled}\n onChange={(event) => updateScheduleEditor(row.key, { isEnabled: event.target.checked }, row.entityType)}\n disabled={controlsDisabled}\n />\n <span>{scheduleState.isEnabled ? t('data_sync.dashboard.schedule.status.shortEnabled', 'Scheduled') : t('data_sync.dashboard.schedule.status.shortDisabled', 'Paused')}</span>\n </label>\n </td>\n <td className=\"px-3 py-3 text-muted-foreground\">\n {scheduleState.lastRunAt\n ? new Date(scheduleState.lastRunAt).toLocaleString()\n : scheduleState.id\n ? t('data_sync.dashboard.schedule.neverRun', 'Saved, but no scheduled execution has completed yet.')\n : t('data_sync.dashboard.schedule.none', 'No recurring schedule saved for this target yet.')}\n </td>\n <td className=\"px-3 py-3\">\n <div className=\"flex flex-wrap gap-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n onClick={() => void handleStartSync(row.entityType, row.direction, row.key)}\n disabled={controlsDisabled || !props.isEnabled || !props.hasCredentials || option.canStartRun === false}\n >\n {isRunning ? <Spinner className=\"mr-2 h-4 w-4\" /> : <Play className=\"mr-2 h-4 w-4\" />}\n {isRunning\n ? t('data_sync.integrationTab.starting', 'Starting...')\n : t('data_sync.integrationTab.start', 'Run now')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => void handleSaveSchedule(row.entityType, row.direction, row.key)}\n disabled={controlsDisabled || option.canStartRun === false}\n >\n {isSaving ? <Spinner className=\"mr-2 h-4 w-4\" /> : <Save className=\"mr-2 h-4 w-4\" />}\n {isSaving\n ? t('data_sync.dashboard.schedule.saving', 'Saving...')\n : t('data_sync.dashboard.schedule.save', 'Save recurring schedule')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => void handleDeleteSchedule(row.entityType, row.key)}\n disabled={controlsDisabled || !scheduleState.id}\n >\n {isDeleting ? <Spinner className=\"mr-2 h-4 w-4\" /> : <Trash2 className=\"mr-2 h-4 w-4\" />}\n {isDeleting\n ? t('data_sync.dashboard.schedule.deleting', 'Removing...')\n : t('data_sync.dashboard.schedule.delete', 'Remove schedule')}\n </Button>\n </div>\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n )}\n\n <div className=\"grid gap-3 rounded-lg border border-dashed p-4 text-sm text-muted-foreground md:grid-cols-3\">\n <div className=\"space-y-1\">\n <Label>{t('data_sync.dashboard.schedule.interval', 'Interval')}</Label>\n <p>{t('data_sync.dashboard.schedule.intervalHelp', 'Example: `1h`, `6h`, or `24h` for repeating intervals.')}</p>\n </div>\n <div className=\"space-y-1\">\n <Label>{t('data_sync.dashboard.schedule.cron', 'Cron')}</Label>\n <p>{t('data_sync.dashboard.schedule.cronHelp', 'Example: `0 * * * *` runs at the start of every hour.')}</p>\n </div>\n <div className=\"space-y-1\">\n <Label>{t('data_sync.integrationTab.runNowTitle', 'Manual runs')}</Label>\n <p>{t('data_sync.integrationTab.runNowHelp', 'Run now uses the full-sync checkbox from the same row and starts progress tracking immediately in Data Sync.')}</p>\n </div>\n </div>\n </section>\n )\n}\n\nexport default IntegrationScheduleTab\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["supportedDirections"]
|
|
7
7
|
}
|
|
@@ -130,7 +130,15 @@ function MessagesInboxPageClient() {
|
|
|
130
130
|
if (query && query.trim().length > 0) {
|
|
131
131
|
params.set("search", query.trim());
|
|
132
132
|
}
|
|
133
|
-
const call = await apiCall(
|
|
133
|
+
const call = await apiCall(
|
|
134
|
+
`/api/auth/users?${params.toString()}`,
|
|
135
|
+
{
|
|
136
|
+
headers: {
|
|
137
|
+
"x-om-forbidden-redirect": "0"
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
).catch(() => null);
|
|
141
|
+
if (!call) return [];
|
|
134
142
|
if (!call.ok) return [];
|
|
135
143
|
const items = Array.isArray(call.result?.items) ? call.result?.items ?? [] : [];
|
|
136
144
|
const next = items.flatMap((item) => {
|
|
@@ -149,6 +157,21 @@ function MessagesInboxPageClient() {
|
|
|
149
157
|
}
|
|
150
158
|
return next;
|
|
151
159
|
}, [mergeSenderOptions, scopeVersion]);
|
|
160
|
+
React.useEffect(() => {
|
|
161
|
+
const items = listQuery.data?.items ?? [];
|
|
162
|
+
const next = items.flatMap((item) => {
|
|
163
|
+
if (typeof item.senderUserId !== "string" || item.senderUserId.trim().length === 0) return [];
|
|
164
|
+
const name = typeof item.senderName === "string" && item.senderName.trim().length > 0 ? item.senderName.trim() : null;
|
|
165
|
+
const email = typeof item.senderEmail === "string" && item.senderEmail.trim().length > 0 ? item.senderEmail.trim() : null;
|
|
166
|
+
const label = name ?? email ?? item.senderUserId;
|
|
167
|
+
return [{
|
|
168
|
+
value: item.senderUserId,
|
|
169
|
+
label,
|
|
170
|
+
description: email && email !== label ? email : null
|
|
171
|
+
}];
|
|
172
|
+
});
|
|
173
|
+
mergeSenderOptions(next);
|
|
174
|
+
}, [listQuery.data?.items, mergeSenderOptions]);
|
|
152
175
|
React.useEffect(() => {
|
|
153
176
|
senderOptionsScopeRef.current = scopeVersion;
|
|
154
177
|
setSenderOptions([]);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/messages/components/MessagesInboxPageClient.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { Archive, ChevronDown, FilePenLine, Inbox, Layers, Send } from 'lucide-react'\nimport { getMessageUiComponentRegistry } from './utils/typeUiRegistry'\nimport { DefaultMessageListItem } from './defaults/DefaultMessageListItem'\nimport { getMessageListParticipantLabel } from './messageListLabels'\nimport { toErrorMessage } from './message-detail/utils'\nimport { useMessagesInboxBulkActions, type MessageFolder } from './useMessagesInboxBulkActions'\nimport {\n buildMessagesInboxFilters,\n buildMessagesListParams,\n type SenderOption,\n} from './inboxFilters'\n\ntype MessageListItem = {\n id: string\n type: string\n subject: string\n bodyPreview: string\n senderUserId: string\n senderName?: string | null\n senderEmail?: string | null\n priority: string\n status: string\n hasObjects: boolean\n objectCount: number\n hasAttachments: boolean\n attachmentCount: number\n recipientCount?: number\n hasActions: boolean\n actionTaken?: string | null\n sentAt?: string | null\n readAt?: string | null\n threadId?: string | null\n}\n\ntype MessageListResponse = {\n items?: MessageListItem[]\n total?: number\n page?: number\n pageSize?: number\n totalPages?: number\n}\n\ntype MessageTypeItem = {\n type: string\n module: string\n labelKey: string\n ui?: {\n listItemComponent?: string | null\n } | null\n}\n\ntype UserListItem = {\n id: string\n email?: string | null\n name?: string | null\n}\n\nexport function MessagesInboxPageClient() {\n const router = useRouter()\n const t = useT()\n const queryClient = useQueryClient()\n const scopeVersion = useOrganizationScopeVersion()\n\n const invalidateMessageListQueries = React.useCallback(() => {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'list'] })\n }, [queryClient])\n\n useAppEvent('messages.message.*', invalidateMessageListQueries, [invalidateMessageListQueries])\n\n useAppEvent('om:bridge:reconnected', invalidateMessageListQueries, [invalidateMessageListQueries])\n\n const [folder, setFolder] = React.useState<MessageFolder>('inbox')\n const [folderMenuOpen, setFolderMenuOpen] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [page, setPage] = React.useState(1)\n const pageSize = 20\n const folderMenuRef = React.useRef<HTMLDivElement | null>(null)\n const messageUiRegistry = React.useMemo(() => getMessageUiComponentRegistry(), [])\n const { bulkActions, selectionScopeKey, injectionContext, ConfirmDialogElement } = useMessagesInboxBulkActions<MessageListItem>({\n folder,\n page,\n search,\n filterValues,\n })\n\n const listQuery = useQuery({\n queryKey: [\n 'messages',\n 'list',\n folder,\n search,\n page,\n pageSize,\n JSON.stringify(filterValues),\n scopeVersion,\n ],\n queryFn: async () => {\n const params = buildMessagesListParams({\n folder,\n page,\n pageSize,\n search,\n filterValues,\n })\n\n const call = await apiCall<MessageListResponse>(`/api/messages?${params.toString()}`)\n if (!call.ok) {\n throw new Error(\n toErrorMessage(call.result)\n ?? t('messages.errors.loadListFailed', 'Failed to load messages.'),\n )\n }\n\n return {\n items: Array.isArray(call.result?.items) ? call.result?.items ?? [] : [],\n total: Number(call.result?.total ?? 0),\n page: Number(call.result?.page ?? page),\n pageSize: Number(call.result?.pageSize ?? pageSize),\n totalPages: Number(call.result?.totalPages ?? 0),\n }\n },\n })\n\n const messageTypesQuery = useQuery({\n queryKey: ['messages', 'types', scopeVersion],\n queryFn: async () => {\n const call = await apiCall<{ items?: MessageTypeItem[] }>('/api/messages/types')\n if (!call.ok) {\n throw new Error(\n toErrorMessage(call.result)\n ?? t('messages.errors.loadTypesFailed', 'Failed to load message types.'),\n )\n }\n return Array.isArray(call.result?.items) ? call.result?.items ?? [] : []\n },\n })\n\n React.useEffect(() => {\n if (!listQuery.error) return\n flash(\n listQuery.error instanceof Error\n ? listQuery.error.message\n : t('messages.errors.loadListFailed', 'Failed to load messages.'),\n 'error',\n )\n }, [listQuery.error, t])\n\n React.useEffect(() => {\n if (!messageTypesQuery.error) return\n flash(\n messageTypesQuery.error instanceof Error\n ? messageTypesQuery.error.message\n : t('messages.errors.loadTypesFailed', 'Failed to load message types.'),\n 'error',\n )\n }, [messageTypesQuery.error, t])\n\n const messageTypeLabelMap = React.useMemo(() => {\n const map: Record<string, string> = {}\n for (const item of messageTypesQuery.data ?? []) {\n map[item.type] = t(item.labelKey, item.type)\n }\n return map\n }, [messageTypesQuery.data, t])\n\n const [senderOptions, setSenderOptions] = React.useState<SenderOption[]>([])\n const senderOptionsScopeRef = React.useRef(scopeVersion)\n\n const mergeSenderOptions = React.useCallback((incoming: SenderOption[]) => {\n if (incoming.length === 0) return\n setSenderOptions((prev) => {\n const map = new Map<string, SenderOption>()\n for (const opt of prev) map.set(opt.value, opt)\n for (const opt of incoming) map.set(opt.value, opt)\n return Array.from(map.values())\n })\n }, [])\n\n const loadSenderOptions = React.useCallback(async (query?: string) => {\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', '20')\n if (query && query.trim().length > 0) {\n params.set('search', query.trim())\n }\n\n const call = await apiCall<{ items?: UserListItem[] }>(`/api/auth/users?${params.toString()}`)\n if (!call.ok) return []\n\n const items = Array.isArray(call.result?.items) ? call.result?.items ?? [] : []\n const next: SenderOption[] = items.flatMap((item) => {\n if (!item || typeof item.id !== 'string' || item.id.trim().length === 0) return []\n const name = typeof item.name === 'string' && item.name.trim().length > 0 ? item.name.trim() : null\n const email = typeof item.email === 'string' && item.email.trim().length > 0 ? item.email.trim() : null\n const label = name ?? email ?? item.id\n return [{\n value: item.id,\n label,\n description: email && email !== label ? email : null,\n }]\n })\n if (senderOptionsScopeRef.current === scopeVersion) {\n mergeSenderOptions(next)\n }\n return next\n }, [mergeSenderOptions, scopeVersion])\n\n React.useEffect(() => {\n senderOptionsScopeRef.current = scopeVersion\n setSenderOptions([])\n loadSenderOptions().catch((error: unknown) => {\n console.warn('[messages] Failed to load sender filter options', error)\n })\n }, [loadSenderOptions, scopeVersion])\n\n const listItemComponentKeyByType = React.useMemo(() => {\n const map: Record<string, string | null> = {}\n for (const item of messageTypesQuery.data ?? []) {\n map[item.type] = item.ui?.listItemComponent ?? null\n }\n return map\n }, [messageTypesQuery.data])\n\n const filters = React.useMemo<FilterDef[]>(() => {\n const typeOptions = (messageTypesQuery.data ?? []).map((item) => ({\n value: item.type,\n label: t(item.labelKey, item.type),\n }))\n\n return buildMessagesInboxFilters({\n t,\n typeOptions,\n senderOptions,\n loadSenderOptions,\n })\n }, [loadSenderOptions, messageTypesQuery.data, senderOptions, t])\n\n const columns = React.useMemo<ColumnDef<MessageListItem>[]>(() => [\n {\n accessorKey: 'message',\n header: t('messages.title', 'Messages'),\n meta: {\n truncate: false,\n maxWidth: '100%',\n },\n cell: ({ row }) => {\n const item = row.original\n const listItemComponentKey = listItemComponentKeyByType[item.type]\n const ListItemComponent = listItemComponentKey\n ? messageUiRegistry.listItemComponents[listItemComponentKey] ?? null\n : null\n const ComponentToUse = ListItemComponent || DefaultMessageListItem\n\n return (\n <ComponentToUse\n message={{\n id: item.id,\n type: item.type,\n typeLabel: messageTypeLabelMap[item.type] ?? item.type,\n subject: item.subject,\n body: item.bodyPreview,\n bodyFormat: 'text' as const,\n priority: (item.priority as 'low' | 'normal' | 'high' | 'urgent') ?? 'normal',\n sentAt: item.sentAt ? new Date(item.sentAt) : null,\n senderName: getMessageListParticipantLabel(item, folder, t),\n hasObjects: item.hasObjects,\n objectCount: item.objectCount,\n hasAttachments: item.hasAttachments,\n attachmentCount: item.attachmentCount,\n recipientCount: item.recipientCount ?? 0,\n hasActions: item.hasActions,\n actionTaken: item.actionTaken ?? null,\n unread: item.status === 'unread',\n }}\n onClick={() => router.push(`/backend/messages/${item.id}`)}\n />\n )\n },\n },\n ], [folder, listItemComponentKeyByType, messageTypeLabelMap, messageUiRegistry, router, t])\n\n const folderOptions = React.useMemo(() => [\n { id: 'inbox' as const, label: t('messages.folder.inbox', 'Inbox'), icon: Inbox },\n { id: 'sent' as const, label: t('messages.folder.sent', 'Sent'), icon: Send },\n { id: 'drafts' as const, label: t('messages.folder.drafts', 'Drafts'), icon: FilePenLine },\n { id: 'archived' as const, label: t('messages.folder.archived', 'Archived'), icon: Archive },\n { id: 'all' as const, label: t('messages.folder.all', 'All'), icon: Layers },\n ], [t])\n\n const activeFolderOption = folderOptions.find((option) => option.id === folder) ?? folderOptions[0]\n const ActiveFolderIcon = activeFolderOption.icon\n\n React.useEffect(() => {\n if (!folderMenuOpen) return\n const handleClickOutside = (event: MouseEvent) => {\n if (!folderMenuRef.current) return\n const target = event.target\n if (target instanceof Node && !folderMenuRef.current.contains(target)) {\n setFolderMenuOpen(false)\n }\n }\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') setFolderMenuOpen(false)\n }\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n return () => {\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n }\n }, [folderMenuOpen])\n\n const rows = listQuery.data?.items ?? []\n const total = listQuery.data?.total ?? 0\n const totalPages = listQuery.data?.totalPages ?? 0\n\n return (\n <div className=\"space-y-4\">\n <DataTable\n title={t('messages.title', 'Messages')}\n columns={columns}\n data={rows}\n bulkActions={bulkActions}\n selectionScopeKey={selectionScopeKey}\n searchValue={search}\n onSearchChange={(value) => {\n setSearch(value)\n setPage(1)\n }}\n searchPlaceholder={t('messages.searchPlaceholder', 'Search messages')}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={(value) => {\n setFilterValues(value)\n setPage(1)\n }}\n onFiltersClear={() => {\n setFilterValues({})\n setPage(1)\n }}\n injectionContext={injectionContext}\n isLoading={listQuery.isLoading || listQuery.isFetching}\n pagination={{\n page,\n pageSize,\n total,\n totalPages,\n onPageChange: setPage,\n }}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <div className=\"relative\" ref={folderMenuRef}>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"gap-2\"\n aria-expanded={folderMenuOpen}\n aria-haspopup=\"menu\"\n onClick={() => setFolderMenuOpen((value) => !value)}\n >\n <ActiveFolderIcon className=\"h-4 w-4\" aria-hidden />\n <span>{t('messages.folder.selector', 'Folder')}:</span>\n <span>{activeFolderOption.label}</span>\n <ChevronDown className=\"h-4 w-4 opacity-70\" aria-hidden />\n </Button>\n {folderMenuOpen ? (\n <div\n className=\"absolute right-0 z-dropdown mt-1 min-w-52 rounded-md border bg-background p-1 shadow\"\n role=\"menu\"\n >\n {folderOptions.map((option) => {\n const Icon = option.icon\n const isActive = option.id === folder\n return (\n <Button\n key={option.id}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n role=\"menuitemradio\"\n aria-checked={isActive}\n className={`w-full justify-start h-auto px-2 py-1.5 text-sm font-normal ${isActive ? 'bg-accent/60' : ''}`}\n onClick={() => {\n setFolder(option.id)\n setPage(1)\n setFolderMenuOpen(false)\n }}\n >\n <Icon className=\"h-4 w-4\" aria-hidden />\n <span>{option.label}</span>\n </Button>\n )\n })}\n </div>\n ) : null}\n </div>\n <Button asChild>\n <Link href=\"/backend/messages/compose\">{t('messages.compose', 'Compose message')}</Link>\n </Button>\n </div>\n }\n onRowClick={(row) => {\n router.push(`/backend/messages/${row.id}`)\n }}\n embedded\n />\n {ConfirmDialogElement}\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { useQuery, useQueryClient } from '@tanstack/react-query'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { Archive, ChevronDown, FilePenLine, Inbox, Layers, Send } from 'lucide-react'\nimport { getMessageUiComponentRegistry } from './utils/typeUiRegistry'\nimport { DefaultMessageListItem } from './defaults/DefaultMessageListItem'\nimport { getMessageListParticipantLabel } from './messageListLabels'\nimport { toErrorMessage } from './message-detail/utils'\nimport { useMessagesInboxBulkActions, type MessageFolder } from './useMessagesInboxBulkActions'\nimport {\n buildMessagesInboxFilters,\n buildMessagesListParams,\n type SenderOption,\n} from './inboxFilters'\n\ntype MessageListItem = {\n id: string\n type: string\n subject: string\n bodyPreview: string\n senderUserId: string\n senderName?: string | null\n senderEmail?: string | null\n priority: string\n status: string\n hasObjects: boolean\n objectCount: number\n hasAttachments: boolean\n attachmentCount: number\n recipientCount?: number\n hasActions: boolean\n actionTaken?: string | null\n sentAt?: string | null\n readAt?: string | null\n threadId?: string | null\n}\n\ntype MessageListResponse = {\n items?: MessageListItem[]\n total?: number\n page?: number\n pageSize?: number\n totalPages?: number\n}\n\ntype MessageTypeItem = {\n type: string\n module: string\n labelKey: string\n ui?: {\n listItemComponent?: string | null\n } | null\n}\n\ntype UserListItem = {\n id: string\n email?: string | null\n name?: string | null\n}\n\nexport function MessagesInboxPageClient() {\n const router = useRouter()\n const t = useT()\n const queryClient = useQueryClient()\n const scopeVersion = useOrganizationScopeVersion()\n\n const invalidateMessageListQueries = React.useCallback(() => {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'list'] })\n }, [queryClient])\n\n useAppEvent('messages.message.*', invalidateMessageListQueries, [invalidateMessageListQueries])\n\n useAppEvent('om:bridge:reconnected', invalidateMessageListQueries, [invalidateMessageListQueries])\n\n const [folder, setFolder] = React.useState<MessageFolder>('inbox')\n const [folderMenuOpen, setFolderMenuOpen] = React.useState(false)\n const [search, setSearch] = React.useState('')\n const [filterValues, setFilterValues] = React.useState<FilterValues>({})\n const [page, setPage] = React.useState(1)\n const pageSize = 20\n const folderMenuRef = React.useRef<HTMLDivElement | null>(null)\n const messageUiRegistry = React.useMemo(() => getMessageUiComponentRegistry(), [])\n const { bulkActions, selectionScopeKey, injectionContext, ConfirmDialogElement } = useMessagesInboxBulkActions<MessageListItem>({\n folder,\n page,\n search,\n filterValues,\n })\n\n const listQuery = useQuery({\n queryKey: [\n 'messages',\n 'list',\n folder,\n search,\n page,\n pageSize,\n JSON.stringify(filterValues),\n scopeVersion,\n ],\n queryFn: async () => {\n const params = buildMessagesListParams({\n folder,\n page,\n pageSize,\n search,\n filterValues,\n })\n\n const call = await apiCall<MessageListResponse>(`/api/messages?${params.toString()}`)\n if (!call.ok) {\n throw new Error(\n toErrorMessage(call.result)\n ?? t('messages.errors.loadListFailed', 'Failed to load messages.'),\n )\n }\n\n return {\n items: Array.isArray(call.result?.items) ? call.result?.items ?? [] : [],\n total: Number(call.result?.total ?? 0),\n page: Number(call.result?.page ?? page),\n pageSize: Number(call.result?.pageSize ?? pageSize),\n totalPages: Number(call.result?.totalPages ?? 0),\n }\n },\n })\n\n const messageTypesQuery = useQuery({\n queryKey: ['messages', 'types', scopeVersion],\n queryFn: async () => {\n const call = await apiCall<{ items?: MessageTypeItem[] }>('/api/messages/types')\n if (!call.ok) {\n throw new Error(\n toErrorMessage(call.result)\n ?? t('messages.errors.loadTypesFailed', 'Failed to load message types.'),\n )\n }\n return Array.isArray(call.result?.items) ? call.result?.items ?? [] : []\n },\n })\n\n React.useEffect(() => {\n if (!listQuery.error) return\n flash(\n listQuery.error instanceof Error\n ? listQuery.error.message\n : t('messages.errors.loadListFailed', 'Failed to load messages.'),\n 'error',\n )\n }, [listQuery.error, t])\n\n React.useEffect(() => {\n if (!messageTypesQuery.error) return\n flash(\n messageTypesQuery.error instanceof Error\n ? messageTypesQuery.error.message\n : t('messages.errors.loadTypesFailed', 'Failed to load message types.'),\n 'error',\n )\n }, [messageTypesQuery.error, t])\n\n const messageTypeLabelMap = React.useMemo(() => {\n const map: Record<string, string> = {}\n for (const item of messageTypesQuery.data ?? []) {\n map[item.type] = t(item.labelKey, item.type)\n }\n return map\n }, [messageTypesQuery.data, t])\n\n const [senderOptions, setSenderOptions] = React.useState<SenderOption[]>([])\n const senderOptionsScopeRef = React.useRef(scopeVersion)\n\n const mergeSenderOptions = React.useCallback((incoming: SenderOption[]) => {\n if (incoming.length === 0) return\n setSenderOptions((prev) => {\n const map = new Map<string, SenderOption>()\n for (const opt of prev) map.set(opt.value, opt)\n for (const opt of incoming) map.set(opt.value, opt)\n return Array.from(map.values())\n })\n }, [])\n\n const loadSenderOptions = React.useCallback(async (query?: string) => {\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', '20')\n if (query && query.trim().length > 0) {\n params.set('search', query.trim())\n }\n\n const call = await apiCall<{ items?: UserListItem[] }>(\n `/api/auth/users?${params.toString()}`,\n {\n headers: {\n 'x-om-forbidden-redirect': '0',\n },\n },\n ).catch(() => null)\n if (!call) return []\n if (!call.ok) return []\n\n const items = Array.isArray(call.result?.items) ? call.result?.items ?? [] : []\n const next: SenderOption[] = items.flatMap((item) => {\n if (!item || typeof item.id !== 'string' || item.id.trim().length === 0) return []\n const name = typeof item.name === 'string' && item.name.trim().length > 0 ? item.name.trim() : null\n const email = typeof item.email === 'string' && item.email.trim().length > 0 ? item.email.trim() : null\n const label = name ?? email ?? item.id\n return [{\n value: item.id,\n label,\n description: email && email !== label ? email : null,\n }]\n })\n if (senderOptionsScopeRef.current === scopeVersion) {\n mergeSenderOptions(next)\n }\n return next\n }, [mergeSenderOptions, scopeVersion])\n\n React.useEffect(() => {\n const items = listQuery.data?.items ?? []\n const next = items.flatMap((item): SenderOption[] => {\n if (typeof item.senderUserId !== 'string' || item.senderUserId.trim().length === 0) return []\n const name = typeof item.senderName === 'string' && item.senderName.trim().length > 0\n ? item.senderName.trim()\n : null\n const email = typeof item.senderEmail === 'string' && item.senderEmail.trim().length > 0\n ? item.senderEmail.trim()\n : null\n const label = name ?? email ?? item.senderUserId\n return [{\n value: item.senderUserId,\n label,\n description: email && email !== label ? email : null,\n }]\n })\n mergeSenderOptions(next)\n }, [listQuery.data?.items, mergeSenderOptions])\n\n React.useEffect(() => {\n senderOptionsScopeRef.current = scopeVersion\n setSenderOptions([])\n loadSenderOptions().catch((error: unknown) => {\n console.warn('[messages] Failed to load sender filter options', error)\n })\n }, [loadSenderOptions, scopeVersion])\n\n const listItemComponentKeyByType = React.useMemo(() => {\n const map: Record<string, string | null> = {}\n for (const item of messageTypesQuery.data ?? []) {\n map[item.type] = item.ui?.listItemComponent ?? null\n }\n return map\n }, [messageTypesQuery.data])\n\n const filters = React.useMemo<FilterDef[]>(() => {\n const typeOptions = (messageTypesQuery.data ?? []).map((item) => ({\n value: item.type,\n label: t(item.labelKey, item.type),\n }))\n\n return buildMessagesInboxFilters({\n t,\n typeOptions,\n senderOptions,\n loadSenderOptions,\n })\n }, [loadSenderOptions, messageTypesQuery.data, senderOptions, t])\n\n const columns = React.useMemo<ColumnDef<MessageListItem>[]>(() => [\n {\n accessorKey: 'message',\n header: t('messages.title', 'Messages'),\n meta: {\n truncate: false,\n maxWidth: '100%',\n },\n cell: ({ row }) => {\n const item = row.original\n const listItemComponentKey = listItemComponentKeyByType[item.type]\n const ListItemComponent = listItemComponentKey\n ? messageUiRegistry.listItemComponents[listItemComponentKey] ?? null\n : null\n const ComponentToUse = ListItemComponent || DefaultMessageListItem\n\n return (\n <ComponentToUse\n message={{\n id: item.id,\n type: item.type,\n typeLabel: messageTypeLabelMap[item.type] ?? item.type,\n subject: item.subject,\n body: item.bodyPreview,\n bodyFormat: 'text' as const,\n priority: (item.priority as 'low' | 'normal' | 'high' | 'urgent') ?? 'normal',\n sentAt: item.sentAt ? new Date(item.sentAt) : null,\n senderName: getMessageListParticipantLabel(item, folder, t),\n hasObjects: item.hasObjects,\n objectCount: item.objectCount,\n hasAttachments: item.hasAttachments,\n attachmentCount: item.attachmentCount,\n recipientCount: item.recipientCount ?? 0,\n hasActions: item.hasActions,\n actionTaken: item.actionTaken ?? null,\n unread: item.status === 'unread',\n }}\n onClick={() => router.push(`/backend/messages/${item.id}`)}\n />\n )\n },\n },\n ], [folder, listItemComponentKeyByType, messageTypeLabelMap, messageUiRegistry, router, t])\n\n const folderOptions = React.useMemo(() => [\n { id: 'inbox' as const, label: t('messages.folder.inbox', 'Inbox'), icon: Inbox },\n { id: 'sent' as const, label: t('messages.folder.sent', 'Sent'), icon: Send },\n { id: 'drafts' as const, label: t('messages.folder.drafts', 'Drafts'), icon: FilePenLine },\n { id: 'archived' as const, label: t('messages.folder.archived', 'Archived'), icon: Archive },\n { id: 'all' as const, label: t('messages.folder.all', 'All'), icon: Layers },\n ], [t])\n\n const activeFolderOption = folderOptions.find((option) => option.id === folder) ?? folderOptions[0]\n const ActiveFolderIcon = activeFolderOption.icon\n\n React.useEffect(() => {\n if (!folderMenuOpen) return\n const handleClickOutside = (event: MouseEvent) => {\n if (!folderMenuRef.current) return\n const target = event.target\n if (target instanceof Node && !folderMenuRef.current.contains(target)) {\n setFolderMenuOpen(false)\n }\n }\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === 'Escape') setFolderMenuOpen(false)\n }\n document.addEventListener('mousedown', handleClickOutside)\n document.addEventListener('keydown', handleEscape)\n return () => {\n document.removeEventListener('mousedown', handleClickOutside)\n document.removeEventListener('keydown', handleEscape)\n }\n }, [folderMenuOpen])\n\n const rows = listQuery.data?.items ?? []\n const total = listQuery.data?.total ?? 0\n const totalPages = listQuery.data?.totalPages ?? 0\n\n return (\n <div className=\"space-y-4\">\n <DataTable\n title={t('messages.title', 'Messages')}\n columns={columns}\n data={rows}\n bulkActions={bulkActions}\n selectionScopeKey={selectionScopeKey}\n searchValue={search}\n onSearchChange={(value) => {\n setSearch(value)\n setPage(1)\n }}\n searchPlaceholder={t('messages.searchPlaceholder', 'Search messages')}\n filters={filters}\n filterValues={filterValues}\n onFiltersApply={(value) => {\n setFilterValues(value)\n setPage(1)\n }}\n onFiltersClear={() => {\n setFilterValues({})\n setPage(1)\n }}\n injectionContext={injectionContext}\n isLoading={listQuery.isLoading || listQuery.isFetching}\n pagination={{\n page,\n pageSize,\n total,\n totalPages,\n onPageChange: setPage,\n }}\n actions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <div className=\"relative\" ref={folderMenuRef}>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"gap-2\"\n aria-expanded={folderMenuOpen}\n aria-haspopup=\"menu\"\n onClick={() => setFolderMenuOpen((value) => !value)}\n >\n <ActiveFolderIcon className=\"h-4 w-4\" aria-hidden />\n <span>{t('messages.folder.selector', 'Folder')}:</span>\n <span>{activeFolderOption.label}</span>\n <ChevronDown className=\"h-4 w-4 opacity-70\" aria-hidden />\n </Button>\n {folderMenuOpen ? (\n <div\n className=\"absolute right-0 z-dropdown mt-1 min-w-52 rounded-md border bg-background p-1 shadow\"\n role=\"menu\"\n >\n {folderOptions.map((option) => {\n const Icon = option.icon\n const isActive = option.id === folder\n return (\n <Button\n key={option.id}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n role=\"menuitemradio\"\n aria-checked={isActive}\n className={`w-full justify-start h-auto px-2 py-1.5 text-sm font-normal ${isActive ? 'bg-accent/60' : ''}`}\n onClick={() => {\n setFolder(option.id)\n setPage(1)\n setFolderMenuOpen(false)\n }}\n >\n <Icon className=\"h-4 w-4\" aria-hidden />\n <span>{option.label}</span>\n </Button>\n )\n })}\n </div>\n ) : null}\n </div>\n <Button asChild>\n <Link href=\"/backend/messages/compose\">{t('messages.compose', 'Compose message')}</Link>\n </Button>\n </div>\n }\n onRowClick={(row) => {\n router.push(`/backend/messages/${row.id}`)\n }}\n embedded\n />\n {ConfirmDialogElement}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|