@open-mercato/core 0.5.1-develop.2953.6647bb2c43 → 0.5.1-develop.2964.d5ac4a6ebb
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/helpers/integration/salesUi.js +25 -23
- package/dist/helpers/integration/salesUi.js.map +2 -2
- package/dist/modules/api_docs/frontend/docs/api/Explorer.js +24 -24
- package/dist/modules/api_docs/frontend/docs/api/Explorer.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js +15 -7
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
- package/dist/modules/attachments/fields/attachment.js +4 -6
- package/dist/modules/attachments/fields/attachment.js.map +2 -2
- package/dist/modules/auth/backend/users/create/page.js +26 -26
- package/dist/modules/auth/backend/users/create/page.js.map +2 -2
- package/dist/modules/business_rules/components/ActionRow.js +36 -25
- package/dist/modules/business_rules/components/ActionRow.js.map +2 -2
- package/dist/modules/business_rules/components/ConditionGroup.js +14 -5
- package/dist/modules/business_rules/components/ConditionGroup.js.map +2 -2
- package/dist/modules/business_rules/components/ConditionRow.js +19 -10
- package/dist/modules/business_rules/components/ConditionRow.js.map +2 -2
- package/dist/modules/business_rules/components/RuleSetMembers.js +16 -10
- package/dist/modules/business_rules/components/RuleSetMembers.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +30 -34
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/create/page.js +220 -223
- package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
- package/dist/modules/catalog/components/PriceKindSettings.js +20 -19
- package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductUomSection.js +42 -37
- package/dist/modules/catalog/components/products/ProductUomSection.js.map +2 -2
- package/dist/modules/catalog/components/products/VariantBuilder.js +22 -18
- package/dist/modules/catalog/components/products/VariantBuilder.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +18 -26
- package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js +4 -6
- package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js +5 -4
- package/dist/modules/customer_accounts/widgets/injection/account-status/widget.client.js.map +2 -2
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +19 -7
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js +24 -21
- package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
- package/dist/modules/customers/components/AddressEditor.js +24 -7
- package/dist/modules/customers/components/AddressEditor.js.map +2 -2
- package/dist/modules/customers/components/AddressFormatSettings.js +35 -25
- package/dist/modules/customers/components/AddressFormatSettings.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivityForm.js +20 -12
- package/dist/modules/customers/components/detail/ActivityForm.js.map +2 -2
- package/dist/modules/customers/components/detail/AnnualRevenueField.js +2 -2
- package/dist/modules/customers/components/detail/AnnualRevenueField.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +19 -14
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/formConfig.js +16 -12
- package/dist/modules/customers/components/formConfig.js.map +2 -2
- package/dist/modules/customers/widgets/dashboard/customer-todos/widget.client.js +3 -2
- package/dist/modules/customers/widgets/dashboard/customer-todos/widget.client.js.map +2 -2
- package/dist/modules/customers/widgets/dashboard/new-customers/widget.client.js +18 -10
- package/dist/modules/customers/widgets/dashboard/new-customers/widget.client.js.map +2 -2
- package/dist/modules/customers/widgets/dashboard/new-deals/widget.client.js +3 -2
- package/dist/modules/customers/widgets/dashboard/new-deals/widget.client.js.map +2 -2
- package/dist/modules/customers/widgets/dashboard/next-interactions/widget.client.js +3 -2
- package/dist/modules/customers/widgets/dashboard/next-interactions/widget.client.js.map +2 -2
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +27 -28
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js +14 -6
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js +14 -6
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js +3 -2
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js +3 -2
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js +17 -8
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +40 -23
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js +15 -6
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
- package/dist/modules/dictionaries/components/AppearanceSelector.js +4 -4
- package/dist/modules/dictionaries/components/AppearanceSelector.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js +4 -5
- package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionaryEntrySelect.js +22 -14
- package/dist/modules/dictionaries/components/DictionaryEntrySelect.js.map +2 -2
- package/dist/modules/dictionaries/fields/dictionary.js +18 -13
- package/dist/modules/dictionaries/fields/dictionary.js.map +2 -2
- package/dist/modules/entities/components/EncryptionManager.js +23 -19
- package/dist/modules/entities/components/EncryptionManager.js.map +2 -2
- package/dist/modules/feature_toggles/components/formConfig.js +17 -9
- package/dist/modules/feature_toggles/components/formConfig.js.map +2 -2
- package/dist/modules/feature_toggles/components/overrideFormConfig.js +17 -9
- package/dist/modules/feature_toggles/components/overrideFormConfig.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js +15 -8
- package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js.map +2 -2
- package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js +37 -22
- package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +22 -17
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +12 -6
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
- package/dist/modules/planner/components/AvailabilityRulesEditor.js +19 -12
- package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
- package/dist/modules/resources/components/ResourceCrudForm.js +15 -10
- package/dist/modules/resources/components/ResourceCrudForm.js.map +3 -3
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +15 -18
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/components/ProviderFieldInput.js +23 -20
- package/dist/modules/sales/components/ProviderFieldInput.js.map +2 -2
- package/dist/modules/sales/components/ShippingMethodsSettings.js +25 -17
- package/dist/modules/sales/components/ShippingMethodsSettings.js.map +3 -3
- package/dist/modules/sales/components/channels/ChannelOfferForm.js +35 -42
- package/dist/modules/sales/components/channels/ChannelOfferForm.js.map +2 -2
- package/dist/modules/sales/components/documents/AddressesSection.js +87 -90
- package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
- package/dist/modules/sales/components/documents/AdjustmentDialog.js +17 -6
- package/dist/modules/sales/components/documents/AdjustmentDialog.js.map +3 -3
- package/dist/modules/sales/components/documents/LineItemDialog.js +42 -25
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentForm.js +96 -87
- package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
- package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js +20 -11
- package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js.map +2 -2
- package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js +20 -11
- package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.js +36 -22
- package/dist/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.js.map +2 -2
- package/dist/modules/staff/components/TeamMemberForm.js +14 -9
- package/dist/modules/staff/components/TeamMemberForm.js.map +3 -3
- package/dist/modules/workflows/backend/tasks/[id]/page.js +42 -21
- package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
- package/dist/modules/workflows/components/ActivitiesEditor.js +14 -6
- package/dist/modules/workflows/components/ActivitiesEditor.js.map +3 -3
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +25 -17
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +3 -3
- package/dist/modules/workflows/components/EdgeEditDialog.js +48 -45
- package/dist/modules/workflows/components/EdgeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialog.js +90 -90
- package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/StepsEditor.js +14 -6
- package/dist/modules/workflows/components/StepsEditor.js.map +3 -3
- package/dist/modules/workflows/components/TransitionsEditor.js +31 -26
- package/dist/modules/workflows/components/TransitionsEditor.js.map +3 -3
- package/dist/modules/workflows/components/fields/ActivityArrayEditor.js +19 -11
- package/dist/modules/workflows/components/fields/ActivityArrayEditor.js.map +3 -3
- package/dist/modules/workflows/components/fields/BusinessRuleConditionsEditor.js +12 -14
- package/dist/modules/workflows/components/fields/BusinessRuleConditionsEditor.js.map +2 -2
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +24 -16
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +3 -3
- package/dist/modules/workflows/components/fields/StartPreConditionsEditor.js +12 -13
- package/dist/modules/workflows/components/fields/StartPreConditionsEditor.js.map +2 -2
- package/dist/modules/workflows/components/mobile/MobileTaskForm.js +12 -8
- package/dist/modules/workflows/components/mobile/MobileTaskForm.js.map +2 -2
- package/dist/modules/workflows/frontend/checkout-demo/page.js +43 -46
- package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
- package/package.json +3 -3
- package/src/helpers/integration/salesUi.ts +40 -30
- package/src/modules/api_docs/frontend/docs/api/Explorer.tsx +25 -19
- package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +21 -11
- package/src/modules/attachments/fields/attachment.tsx +4 -6
- package/src/modules/auth/backend/users/create/page.tsx +16 -20
- package/src/modules/business_rules/components/ActionRow.tsx +51 -32
- package/src/modules/business_rules/components/ConditionGroup.tsx +20 -9
- package/src/modules/business_rules/components/ConditionRow.tsx +24 -15
- package/src/modules/business_rules/components/RuleSetMembers.tsx +23 -13
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +47 -53
- package/src/modules/catalog/backend/catalog/products/create/page.tsx +84 -87
- package/src/modules/catalog/components/PriceKindSettings.tsx +9 -9
- package/src/modules/catalog/components/products/ProductUomSection.tsx +85 -83
- package/src/modules/catalog/components/products/VariantBuilder.tsx +49 -33
- package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +12 -27
- package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +4 -6
- package/src/modules/customer_accounts/widgets/injection/account-status/widget.client.tsx +5 -4
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +28 -15
- package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +37 -26
- package/src/modules/customers/components/AddressEditor.tsx +30 -16
- package/src/modules/customers/components/AddressFormatSettings.tsx +25 -19
- package/src/modules/customers/components/detail/ActivityForm.tsx +35 -23
- package/src/modules/customers/components/detail/AnnualRevenueField.tsx +2 -2
- package/src/modules/customers/components/detail/DealForm.tsx +33 -20
- package/src/modules/customers/components/formConfig.tsx +25 -17
- package/src/modules/customers/widgets/dashboard/customer-todos/widget.client.tsx +3 -2
- package/src/modules/customers/widgets/dashboard/new-customers/widget.client.tsx +21 -11
- package/src/modules/customers/widgets/dashboard/new-deals/widget.client.tsx +3 -2
- package/src/modules/customers/widgets/dashboard/next-interactions/widget.client.tsx +3 -2
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +17 -22
- package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.tsx +17 -7
- package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.tsx +20 -10
- package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.tsx +3 -2
- package/src/modules/dashboards/widgets/dashboard/top-customers/widget.client.tsx +3 -2
- package/src/modules/dashboards/widgets/dashboard/top-products/widget.client.tsx +20 -9
- package/src/modules/data_sync/backend/data-sync/page.tsx +64 -38
- package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +18 -7
- package/src/modules/dictionaries/components/AppearanceSelector.tsx +4 -4
- package/src/modules/dictionaries/components/DictionaryEntriesEditor.tsx +3 -4
- package/src/modules/dictionaries/components/DictionaryEntrySelect.tsx +27 -21
- package/src/modules/dictionaries/fields/dictionary.tsx +36 -23
- package/src/modules/entities/components/EncryptionManager.tsx +49 -33
- package/src/modules/feature_toggles/components/formConfig.tsx +20 -10
- package/src/modules/feature_toggles/components/overrideFormConfig.tsx +20 -10
- package/src/modules/inbox_ops/backend/inbox-ops/settings/page.tsx +19 -10
- package/src/modules/inbox_ops/components/proposals/EditActionDialog.tsx +49 -26
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +20 -11
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +19 -9
- package/src/modules/planner/components/AvailabilityRulesEditor.tsx +34 -21
- package/src/modules/resources/components/ResourceCrudForm.tsx +24 -15
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +12 -15
- package/src/modules/sales/components/ProviderFieldInput.tsx +26 -17
- package/src/modules/sales/components/ShippingMethodsSettings.tsx +28 -20
- package/src/modules/sales/components/channels/ChannelOfferForm.tsx +51 -46
- package/src/modules/sales/components/documents/AddressesSection.tsx +78 -76
- package/src/modules/sales/components/documents/AdjustmentDialog.tsx +27 -15
- package/src/modules/sales/components/documents/LineItemDialog.tsx +69 -51
- package/src/modules/sales/components/documents/SalesDocumentForm.tsx +98 -87
- package/src/modules/sales/widgets/dashboard/new-orders/widget.client.tsx +23 -12
- package/src/modules/sales/widgets/dashboard/new-quotes/widget.client.tsx +23 -12
- package/src/modules/shipping_carriers/lib/shipment-wizard/components/ConfigureStep.tsx +35 -19
- package/src/modules/staff/components/TeamMemberForm.tsx +23 -14
- package/src/modules/workflows/backend/tasks/[id]/page.tsx +51 -23
- package/src/modules/workflows/components/ActivitiesEditor.tsx +20 -10
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +28 -18
- package/src/modules/workflows/components/EdgeEditDialog.tsx +51 -40
- package/src/modules/workflows/components/NodeEditDialog.tsx +81 -77
- package/src/modules/workflows/components/StepsEditor.tsx +20 -10
- package/src/modules/workflows/components/TransitionsEditor.tsx +61 -44
- package/src/modules/workflows/components/fields/ActivityArrayEditor.tsx +22 -12
- package/src/modules/workflows/components/fields/BusinessRuleConditionsEditor.tsx +9 -13
- package/src/modules/workflows/components/fields/FormFieldArrayEditor.tsx +27 -17
- package/src/modules/workflows/components/fields/StartPreConditionsEditor.tsx +9 -12
- package/src/modules/workflows/components/mobile/MobileTaskForm.tsx +19 -11
- package/src/modules/workflows/frontend/checkout-demo/page.tsx +71 -60
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/widgets/dashboard/customer-todos/widget.client.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport type { DashboardWidgetComponentProps } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { DEFAULT_SETTINGS, hydrateCustomerTodoSettings, type CustomerTodoWidgetSettings } from './config'\nimport { resolveExampleIntegrationHref } from '../../../lib/interactionCompatibility'\nimport { resolveTodoHref } from '../../../components/detail/utils'\n\ntype TodoLinkSummary = {\n id: string\n todoId: string\n todoSource: string\n todoTitle: string | null\n createdAt: string\n _integrations?: {\n example?: {\n href?: string | null\n }\n [key: string]: unknown\n }\n entity: {\n id: string | null\n displayName: string | null\n kind: string | null\n }\n}\n\nasync function loadTodos(settings: CustomerTodoWidgetSettings): Promise<TodoLinkSummary[]> {\n const params = new URLSearchParams({\n limit: String(settings.pageSize),\n })\n const call = await apiCall<{ items?: unknown[]; error?: string }>(\n `/api/customers/dashboard/widgets/customer-todos?${params.toString()}`,\n )\n if (!call.ok) {\n const message =\n typeof (call.result as Record<string, unknown> | null)?.error === 'string'\n ? ((call.result as Record<string, unknown>).error as string)\n : `Request failed with status ${call.status}`\n throw new Error(message)\n }\n const payload = call.result ?? {}\n const rawItems = Array.isArray((payload as { items?: unknown }).items)\n ? ((payload as { items: unknown[] }).items)\n : []\n return rawItems\n .map((item): TodoLinkSummary | null => {\n if (!item || typeof item !== 'object') return null\n const data = item as any\n const entity = data.entity ?? {}\n return {\n id: typeof data.id === 'string' ? data.id : null,\n todoId: typeof data.todoId === 'string' ? data.todoId : '',\n todoSource: typeof data.todoSource === 'string' ? data.todoSource : '',\n todoTitle: typeof data.todoTitle === 'string' ? data.todoTitle : null,\n createdAt: typeof data.createdAt === 'string' ? data.createdAt : '',\n _integrations: data._integrations && typeof data._integrations === 'object'\n ? (data._integrations as TodoLinkSummary['_integrations'])\n : undefined,\n entity: {\n id: typeof entity.id === 'string' ? entity.id : null,\n displayName: typeof entity.displayName === 'string' ? entity.displayName : null,\n kind: typeof entity.kind === 'string' ? entity.kind : null,\n },\n }\n })\n .filter((item): item is TodoLinkSummary => !!item && !!item.id)\n}\n\nfunction formatDate(value: string | null, locale?: string): string {\n if (!value) return ''\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n return date.toLocaleString(locale ?? undefined)\n}\n\nfunction resolveDetailHref(entity: { id: string | null; kind: string | null } | null | undefined): string | null {\n if (!entity?.id) return null\n if (entity.kind === 'company') return `/backend/customers/companies-v2/${encodeURIComponent(entity.id)}`\n if (entity.kind === 'person') return `/backend/customers/people-v2/${encodeURIComponent(entity.id)}`\n return `/backend/customers/people-v2/${encodeURIComponent(entity.id)}`\n}\n\nconst CustomerTodosWidget: React.FC<DashboardWidgetComponentProps<CustomerTodoWidgetSettings>> = ({\n mode,\n settings = DEFAULT_SETTINGS,\n onSettingsChange,\n refreshToken,\n onRefreshStateChange,\n}) => {\n const t = useT()\n const hydrated = React.useMemo(() => hydrateCustomerTodoSettings(settings), [settings])\n const [items, setItems] = React.useState<TodoLinkSummary[]>([])\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [locale, setLocale] = React.useState<string | undefined>(undefined)\n\n React.useEffect(() => {\n if (typeof navigator !== 'undefined') {\n setLocale(navigator.language)\n }\n }, [])\n\n const refresh = React.useCallback(async () => {\n onRefreshStateChange?.(true)\n setLoading(true)\n setError(null)\n try {\n const data = await loadTodos(hydrated)\n setItems(data)\n } catch (err) {\n console.error('Failed to load customer todos widget data', err)\n setError(t('customers.widgets.todos.error'))\n } finally {\n setLoading(false)\n onRefreshStateChange?.(false)\n }\n }, [hydrated, onRefreshStateChange, t])\n\n React.useEffect(() => {\n refresh().catch(() => {})\n }, [refresh, refreshToken])\n\n if (mode === 'settings') {\n return (\n <div className=\"space-y-4 text-sm\">\n <div className=\"space-y-1.5\">\n <label htmlFor=\"customer-todos-page-size\" className=\"text-xs font-semibold uppercase text-muted-foreground\">\n {t('customers.widgets.todos.settings.pageSize')}\n </label>\n <
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport type { DashboardWidgetComponentProps } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { DEFAULT_SETTINGS, hydrateCustomerTodoSettings, type CustomerTodoWidgetSettings } from './config'\nimport { resolveExampleIntegrationHref } from '../../../lib/interactionCompatibility'\nimport { resolveTodoHref } from '../../../components/detail/utils'\n\ntype TodoLinkSummary = {\n id: string\n todoId: string\n todoSource: string\n todoTitle: string | null\n createdAt: string\n _integrations?: {\n example?: {\n href?: string | null\n }\n [key: string]: unknown\n }\n entity: {\n id: string | null\n displayName: string | null\n kind: string | null\n }\n}\n\nasync function loadTodos(settings: CustomerTodoWidgetSettings): Promise<TodoLinkSummary[]> {\n const params = new URLSearchParams({\n limit: String(settings.pageSize),\n })\n const call = await apiCall<{ items?: unknown[]; error?: string }>(\n `/api/customers/dashboard/widgets/customer-todos?${params.toString()}`,\n )\n if (!call.ok) {\n const message =\n typeof (call.result as Record<string, unknown> | null)?.error === 'string'\n ? ((call.result as Record<string, unknown>).error as string)\n : `Request failed with status ${call.status}`\n throw new Error(message)\n }\n const payload = call.result ?? {}\n const rawItems = Array.isArray((payload as { items?: unknown }).items)\n ? ((payload as { items: unknown[] }).items)\n : []\n return rawItems\n .map((item): TodoLinkSummary | null => {\n if (!item || typeof item !== 'object') return null\n const data = item as any\n const entity = data.entity ?? {}\n return {\n id: typeof data.id === 'string' ? data.id : null,\n todoId: typeof data.todoId === 'string' ? data.todoId : '',\n todoSource: typeof data.todoSource === 'string' ? data.todoSource : '',\n todoTitle: typeof data.todoTitle === 'string' ? data.todoTitle : null,\n createdAt: typeof data.createdAt === 'string' ? data.createdAt : '',\n _integrations: data._integrations && typeof data._integrations === 'object'\n ? (data._integrations as TodoLinkSummary['_integrations'])\n : undefined,\n entity: {\n id: typeof entity.id === 'string' ? entity.id : null,\n displayName: typeof entity.displayName === 'string' ? entity.displayName : null,\n kind: typeof entity.kind === 'string' ? entity.kind : null,\n },\n }\n })\n .filter((item): item is TodoLinkSummary => !!item && !!item.id)\n}\n\nfunction formatDate(value: string | null, locale?: string): string {\n if (!value) return ''\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n return date.toLocaleString(locale ?? undefined)\n}\n\nfunction resolveDetailHref(entity: { id: string | null; kind: string | null } | null | undefined): string | null {\n if (!entity?.id) return null\n if (entity.kind === 'company') return `/backend/customers/companies-v2/${encodeURIComponent(entity.id)}`\n if (entity.kind === 'person') return `/backend/customers/people-v2/${encodeURIComponent(entity.id)}`\n return `/backend/customers/people-v2/${encodeURIComponent(entity.id)}`\n}\n\nconst CustomerTodosWidget: React.FC<DashboardWidgetComponentProps<CustomerTodoWidgetSettings>> = ({\n mode,\n settings = DEFAULT_SETTINGS,\n onSettingsChange,\n refreshToken,\n onRefreshStateChange,\n}) => {\n const t = useT()\n const hydrated = React.useMemo(() => hydrateCustomerTodoSettings(settings), [settings])\n const [items, setItems] = React.useState<TodoLinkSummary[]>([])\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [locale, setLocale] = React.useState<string | undefined>(undefined)\n\n React.useEffect(() => {\n if (typeof navigator !== 'undefined') {\n setLocale(navigator.language)\n }\n }, [])\n\n const refresh = React.useCallback(async () => {\n onRefreshStateChange?.(true)\n setLoading(true)\n setError(null)\n try {\n const data = await loadTodos(hydrated)\n setItems(data)\n } catch (err) {\n console.error('Failed to load customer todos widget data', err)\n setError(t('customers.widgets.todos.error'))\n } finally {\n setLoading(false)\n onRefreshStateChange?.(false)\n }\n }, [hydrated, onRefreshStateChange, t])\n\n React.useEffect(() => {\n refresh().catch(() => {})\n }, [refresh, refreshToken])\n\n if (mode === 'settings') {\n return (\n <div className=\"space-y-4 text-sm\">\n <div className=\"space-y-1.5\">\n <label htmlFor=\"customer-todos-page-size\" className=\"text-xs font-semibold uppercase text-muted-foreground\">\n {t('customers.widgets.todos.settings.pageSize')}\n </label>\n <Input\n id=\"customer-todos-page-size\"\n type=\"number\"\n min={1}\n max={20}\n className=\"w-24\"\n value={hydrated.pageSize}\n onChange={(event) => {\n const next = Number(event.target.value)\n onSettingsChange({ ...hydrated, pageSize: Number.isFinite(next) ? next : hydrated.pageSize })\n }}\n />\n </div>\n <p className=\"text-xs text-muted-foreground\">{t('customers.widgets.todos.settings.help')}</p>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-4\">\n {error ? (\n <p className=\"text-sm text-destructive\">{error}</p>\n ) : loading ? (\n <div className=\"flex h-32 items-center justify-center\">\n <Spinner className=\"h-6 w-6 text-muted-foreground\" />\n </div>\n ) : items.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">{t('customers.widgets.todos.empty')}</p>\n ) : (\n <ul className=\"space-y-3\">\n {items.map((item) => {\n const createdLabel = formatDate(item.createdAt, locale)\n const href = resolveDetailHref(item.entity)\n const exampleHref = resolveExampleIntegrationHref(item)\n const taskHref = exampleHref ?? resolveTodoHref(item.todoSource, item.todoId)\n return (\n <li key={item.id} className=\"rounded-md border p-3\">\n <div className=\"flex items-start justify-between gap-3 text-sm font-medium\">\n <span>{item.entity.displayName ?? t('customers.widgets.common.unknown')}</span>\n <span className=\"text-xs text-muted-foreground\">{createdLabel || t('customers.widgets.common.unknownDate')}</span>\n </div>\n <div className=\"mt-1 space-y-0.5\">\n <p className=\"text-sm font-medium text-foreground\">\n {item.todoTitle ?? t('customers.widgets.todos.untitled')}\n </p>\n {item.todoSource ? (\n <p className=\"text-xs text-muted-foreground\">{item.todoSource}</p>\n ) : null}\n </div>\n <div className=\"mt-2 flex flex-wrap gap-3 text-xs\">\n {href ? (\n <Link className=\"text-primary hover:underline\" href={href}>\n {t('customers.widgets.common.viewRecord')}\n </Link>\n ) : null}\n {taskHref ? (\n <Link className=\"text-primary hover:underline\" href={taskHref}>\n {t('customers.workPlan.customerTodos.table.actions.openTask')}\n </Link>\n ) : null}\n </div>\n </li>\n )\n })}\n </ul>\n )}\n </div>\n )\n}\n\nexport default CustomerTodosWidget\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,6 +3,14 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import Link from "next/link";
|
|
5
5
|
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
6
|
+
import { Input } from "@open-mercato/ui/primitives/input";
|
|
7
|
+
import {
|
|
8
|
+
Select,
|
|
9
|
+
SelectContent,
|
|
10
|
+
SelectItem,
|
|
11
|
+
SelectTrigger,
|
|
12
|
+
SelectValue
|
|
13
|
+
} from "@open-mercato/ui/primitives/select";
|
|
6
14
|
import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
7
15
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
8
16
|
import {
|
|
@@ -95,13 +103,13 @@ const CustomerNewCustomersWidget = ({
|
|
|
95
103
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
96
104
|
/* @__PURE__ */ jsx("label", { htmlFor: "customer-new-customers-page-size", className: "text-xs font-semibold uppercase text-muted-foreground", children: t("customers.widgets.newCustomers.settings.pageSize") }),
|
|
97
105
|
/* @__PURE__ */ jsx(
|
|
98
|
-
|
|
106
|
+
Input,
|
|
99
107
|
{
|
|
100
108
|
id: "customer-new-customers-page-size",
|
|
101
109
|
type: "number",
|
|
102
110
|
min: 1,
|
|
103
111
|
max: 20,
|
|
104
|
-
className: "w-24
|
|
112
|
+
className: "w-24",
|
|
105
113
|
value: hydrated.pageSize,
|
|
106
114
|
onChange: (event) => {
|
|
107
115
|
const next = Number(event.target.value);
|
|
@@ -113,21 +121,21 @@ const CustomerNewCustomersWidget = ({
|
|
|
113
121
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
114
122
|
/* @__PURE__ */ jsx("label", { htmlFor: "customer-new-customers-kind", className: "text-xs font-semibold uppercase text-muted-foreground", children: t("customers.widgets.newCustomers.settings.kind") }),
|
|
115
123
|
/* @__PURE__ */ jsxs(
|
|
116
|
-
|
|
124
|
+
Select,
|
|
117
125
|
{
|
|
118
|
-
id: "customer-new-customers-kind",
|
|
119
|
-
className: "w-full rounded-md border px-2 py-1 text-sm focus-visible:border-ring focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
120
126
|
value: hydrated.kind,
|
|
121
|
-
|
|
122
|
-
const value = event.target.value;
|
|
127
|
+
onValueChange: (value) => {
|
|
123
128
|
if (value === "person" || value === "company" || value === "all") {
|
|
124
129
|
onSettingsChange({ ...hydrated, kind: value });
|
|
125
130
|
}
|
|
126
131
|
},
|
|
127
132
|
children: [
|
|
128
|
-
/* @__PURE__ */ jsx(
|
|
129
|
-
/* @__PURE__ */
|
|
130
|
-
|
|
133
|
+
/* @__PURE__ */ jsx(SelectTrigger, { id: "customer-new-customers-kind", size: "sm", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
134
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
135
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "all", children: t("customers.widgets.newCustomers.filters.all") }),
|
|
136
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "person", children: t("customers.widgets.newCustomers.filters.person") }),
|
|
137
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "company", children: t("customers.widgets.newCustomers.filters.company") })
|
|
138
|
+
] })
|
|
131
139
|
]
|
|
132
140
|
}
|
|
133
141
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/widgets/dashboard/new-customers/widget.client.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport type { DashboardWidgetComponentProps } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n DEFAULT_SETTINGS,\n hydrateNewCustomersSettings,\n type CustomerNewCustomersSettings,\n} from './config'\n\ntype NewCustomerItem = {\n id: string\n displayName: string | null\n kind: string | null\n createdAt: string\n}\n\nasync function loadNewCustomers(settings: CustomerNewCustomersSettings): Promise<NewCustomerItem[]> {\n const params = new URLSearchParams({\n limit: String(settings.pageSize),\n })\n if (settings.kind !== 'all') {\n params.set('kind', settings.kind)\n }\n const call = await apiCall<{ items?: unknown[]; error?: string }>(\n `/api/customers/dashboard/widgets/new-customers?${params.toString()}`,\n )\n if (!call.ok) {\n const message =\n typeof (call.result as Record<string, unknown> | null)?.error === 'string'\n ? ((call.result as Record<string, unknown>).error as string)\n : `Request failed with status ${call.status}`\n throw new Error(message)\n }\n const payload = call.result ?? {}\n const rawItems = Array.isArray((payload as { items?: unknown[] }).items)\n ? (payload as { items: unknown[] }).items\n : []\n return rawItems\n .map((item: unknown): NewCustomerItem | null => {\n if (!item || typeof item !== 'object') return null\n const data = item as any\n return {\n id: typeof data.id === 'string' ? data.id : null,\n displayName: typeof data.displayName === 'string' ? data.displayName : null,\n kind: typeof data.kind === 'string' ? data.kind : null,\n createdAt: typeof data.createdAt === 'string' ? data.createdAt : '',\n }\n })\n .filter((item: NewCustomerItem | null): item is NewCustomerItem => !!item && !!item.id && !!item.createdAt)\n}\n\nfunction resolveDetailHref(item: NewCustomerItem): string | null {\n if (!item.id || !item.kind) return null\n if (item.kind === 'company') return `/backend/customers/companies-v2/${encodeURIComponent(item.id)}`\n if (item.kind === 'person') return `/backend/customers/people-v2/${encodeURIComponent(item.id)}`\n return null\n}\n\nfunction formatDate(value: string | null, locale?: string): string {\n if (!value) return ''\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n return date.toLocaleString(locale ?? undefined, { dateStyle: 'medium', timeStyle: 'short' })\n}\n\nfunction formatKind(kind: string | null, t: (key: string) => string): string {\n if (kind === 'person') return t('customers.widgets.newCustomers.kind.person')\n if (kind === 'company') return t('customers.widgets.newCustomers.kind.company')\n return t('customers.widgets.newCustomers.kind.unknown')\n}\n\nconst CustomerNewCustomersWidget: React.FC<DashboardWidgetComponentProps<CustomerNewCustomersSettings>> = ({\n mode,\n settings = DEFAULT_SETTINGS,\n onSettingsChange,\n refreshToken,\n onRefreshStateChange,\n}) => {\n const t = useT()\n const hydrated = React.useMemo(() => hydrateNewCustomersSettings(settings), [settings])\n const [items, setItems] = React.useState<NewCustomerItem[]>([])\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [locale, setLocale] = React.useState<string | undefined>(undefined)\n\n React.useEffect(() => {\n if (typeof navigator !== 'undefined') {\n setLocale(navigator.language)\n }\n }, [])\n\n const refresh = React.useCallback(async () => {\n onRefreshStateChange?.(true)\n setLoading(true)\n setError(null)\n try {\n const data = await loadNewCustomers(hydrated)\n setItems(data)\n } catch (err) {\n console.error('Failed to load new customers widget data', err)\n setError(t('customers.widgets.newCustomers.error'))\n } finally {\n setLoading(false)\n onRefreshStateChange?.(false)\n }\n }, [hydrated, onRefreshStateChange, t])\n\n React.useEffect(() => {\n refresh().catch(() => {})\n }, [refresh, refreshToken])\n\n if (mode === 'settings') {\n return (\n <div className=\"space-y-4 text-sm\">\n <div className=\"space-y-1.5\">\n <label htmlFor=\"customer-new-customers-page-size\" className=\"text-xs font-semibold uppercase text-muted-foreground\">\n {t('customers.widgets.newCustomers.settings.pageSize')}\n </label>\n <
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport type { DashboardWidgetComponentProps } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Input } from '@open-mercato/ui/primitives/input'\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 { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n DEFAULT_SETTINGS,\n hydrateNewCustomersSettings,\n type CustomerNewCustomersSettings,\n} from './config'\n\ntype NewCustomerItem = {\n id: string\n displayName: string | null\n kind: string | null\n createdAt: string\n}\n\nasync function loadNewCustomers(settings: CustomerNewCustomersSettings): Promise<NewCustomerItem[]> {\n const params = new URLSearchParams({\n limit: String(settings.pageSize),\n })\n if (settings.kind !== 'all') {\n params.set('kind', settings.kind)\n }\n const call = await apiCall<{ items?: unknown[]; error?: string }>(\n `/api/customers/dashboard/widgets/new-customers?${params.toString()}`,\n )\n if (!call.ok) {\n const message =\n typeof (call.result as Record<string, unknown> | null)?.error === 'string'\n ? ((call.result as Record<string, unknown>).error as string)\n : `Request failed with status ${call.status}`\n throw new Error(message)\n }\n const payload = call.result ?? {}\n const rawItems = Array.isArray((payload as { items?: unknown[] }).items)\n ? (payload as { items: unknown[] }).items\n : []\n return rawItems\n .map((item: unknown): NewCustomerItem | null => {\n if (!item || typeof item !== 'object') return null\n const data = item as any\n return {\n id: typeof data.id === 'string' ? data.id : null,\n displayName: typeof data.displayName === 'string' ? data.displayName : null,\n kind: typeof data.kind === 'string' ? data.kind : null,\n createdAt: typeof data.createdAt === 'string' ? data.createdAt : '',\n }\n })\n .filter((item: NewCustomerItem | null): item is NewCustomerItem => !!item && !!item.id && !!item.createdAt)\n}\n\nfunction resolveDetailHref(item: NewCustomerItem): string | null {\n if (!item.id || !item.kind) return null\n if (item.kind === 'company') return `/backend/customers/companies-v2/${encodeURIComponent(item.id)}`\n if (item.kind === 'person') return `/backend/customers/people-v2/${encodeURIComponent(item.id)}`\n return null\n}\n\nfunction formatDate(value: string | null, locale?: string): string {\n if (!value) return ''\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n return date.toLocaleString(locale ?? undefined, { dateStyle: 'medium', timeStyle: 'short' })\n}\n\nfunction formatKind(kind: string | null, t: (key: string) => string): string {\n if (kind === 'person') return t('customers.widgets.newCustomers.kind.person')\n if (kind === 'company') return t('customers.widgets.newCustomers.kind.company')\n return t('customers.widgets.newCustomers.kind.unknown')\n}\n\nconst CustomerNewCustomersWidget: React.FC<DashboardWidgetComponentProps<CustomerNewCustomersSettings>> = ({\n mode,\n settings = DEFAULT_SETTINGS,\n onSettingsChange,\n refreshToken,\n onRefreshStateChange,\n}) => {\n const t = useT()\n const hydrated = React.useMemo(() => hydrateNewCustomersSettings(settings), [settings])\n const [items, setItems] = React.useState<NewCustomerItem[]>([])\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [locale, setLocale] = React.useState<string | undefined>(undefined)\n\n React.useEffect(() => {\n if (typeof navigator !== 'undefined') {\n setLocale(navigator.language)\n }\n }, [])\n\n const refresh = React.useCallback(async () => {\n onRefreshStateChange?.(true)\n setLoading(true)\n setError(null)\n try {\n const data = await loadNewCustomers(hydrated)\n setItems(data)\n } catch (err) {\n console.error('Failed to load new customers widget data', err)\n setError(t('customers.widgets.newCustomers.error'))\n } finally {\n setLoading(false)\n onRefreshStateChange?.(false)\n }\n }, [hydrated, onRefreshStateChange, t])\n\n React.useEffect(() => {\n refresh().catch(() => {})\n }, [refresh, refreshToken])\n\n if (mode === 'settings') {\n return (\n <div className=\"space-y-4 text-sm\">\n <div className=\"space-y-1.5\">\n <label htmlFor=\"customer-new-customers-page-size\" className=\"text-xs font-semibold uppercase text-muted-foreground\">\n {t('customers.widgets.newCustomers.settings.pageSize')}\n </label>\n <Input\n id=\"customer-new-customers-page-size\"\n type=\"number\"\n min={1}\n max={20}\n className=\"w-24\"\n value={hydrated.pageSize}\n onChange={(event) => {\n const next = Number(event.target.value)\n onSettingsChange({ ...hydrated, pageSize: Number.isFinite(next) ? next : hydrated.pageSize })\n }}\n />\n </div>\n <div className=\"space-y-1.5\">\n <label htmlFor=\"customer-new-customers-kind\" className=\"text-xs font-semibold uppercase text-muted-foreground\">\n {t('customers.widgets.newCustomers.settings.kind')}\n </label>\n <Select\n value={hydrated.kind}\n onValueChange={(value) => {\n if (value === 'person' || value === 'company' || value === 'all') {\n onSettingsChange({ ...hydrated, kind: value })\n }\n }}\n >\n <SelectTrigger id=\"customer-new-customers-kind\" size=\"sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">{t('customers.widgets.newCustomers.filters.all')}</SelectItem>\n <SelectItem value=\"person\">{t('customers.widgets.newCustomers.filters.person')}</SelectItem>\n <SelectItem value=\"company\">{t('customers.widgets.newCustomers.filters.company')}</SelectItem>\n </SelectContent>\n </Select>\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-4\">\n {error ? (\n <p className=\"text-sm text-destructive\">{error}</p>\n ) : loading ? (\n <div className=\"flex h-32 items-center justify-center\">\n <Spinner className=\"h-6 w-6 text-muted-foreground\" />\n </div>\n ) : items.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">{t('customers.widgets.newCustomers.empty')}</p>\n ) : (\n <ul className=\"space-y-3\">\n {items.map((item) => {\n const href = resolveDetailHref(item)\n const createdLabel = formatDate(item.createdAt, locale)\n return (\n <li key={item.id} className=\"rounded-md border p-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <p className=\"text-sm font-medium\">{item.displayName ?? t('customers.widgets.common.unknown')}</p>\n <p className=\"text-xs text-muted-foreground\">{formatKind(item.kind, t)}</p>\n </div>\n <p className=\"text-xs text-muted-foreground\">{createdLabel || t('customers.widgets.common.unknownDate')}</p>\n </div>\n {href ? (\n <div className=\"mt-2 text-xs\">\n <Link className=\"text-primary hover:underline\" href={href}>\n {t('customers.widgets.common.viewRecord')}\n </Link>\n </div>\n ) : null}\n </li>\n )\n })}\n </ul>\n )}\n </div>\n )\n}\n\nexport default CustomerNewCustomersWidget\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,6 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import Link from "next/link";
|
|
5
5
|
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
6
|
+
import { Input } from "@open-mercato/ui/primitives/input";
|
|
6
7
|
import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
7
8
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
8
9
|
import { DEFAULT_SETTINGS, hydrateNewDealsSettings } from "./config.js";
|
|
@@ -79,13 +80,13 @@ const CustomerNewDealsWidget = ({
|
|
|
79
80
|
return /* @__PURE__ */ jsx("div", { className: "space-y-4 text-sm", children: /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
80
81
|
/* @__PURE__ */ jsx("label", { htmlFor: "customer-new-deals-page-size", className: "text-xs font-semibold uppercase text-muted-foreground", children: t("customers.widgets.newDeals.settings.pageSize") }),
|
|
81
82
|
/* @__PURE__ */ jsx(
|
|
82
|
-
|
|
83
|
+
Input,
|
|
83
84
|
{
|
|
84
85
|
id: "customer-new-deals-page-size",
|
|
85
86
|
type: "number",
|
|
86
87
|
min: 1,
|
|
87
88
|
max: 20,
|
|
88
|
-
className: "w-24
|
|
89
|
+
className: "w-24",
|
|
89
90
|
value: hydrated.pageSize,
|
|
90
91
|
onChange: (event) => {
|
|
91
92
|
const next = Number(event.target.value);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/widgets/dashboard/new-deals/widget.client.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport type { DashboardWidgetComponentProps } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { DEFAULT_SETTINGS, hydrateNewDealsSettings, type CustomerNewDealsSettings } from './config'\n\ntype NewDealItem = {\n id: string\n title: string | null\n status: string | null\n createdAt: string\n}\n\nasync function loadNewDeals(settings: CustomerNewDealsSettings): Promise<NewDealItem[]> {\n const params = new URLSearchParams({ limit: String(settings.pageSize) })\n const call = await apiCall<{ items?: unknown[]; error?: string }>(\n `/api/customers/dashboard/widgets/new-deals?${params.toString()}`,\n )\n if (!call.ok) {\n const message =\n typeof (call.result as Record<string, unknown> | null)?.error === 'string'\n ? ((call.result as Record<string, unknown>).error as string)\n : `Request failed with status ${call.status}`\n throw new Error(message)\n }\n const payload = call.result ?? {}\n const rawItems = Array.isArray((payload as { items?: unknown[] }).items)\n ? (payload as { items: unknown[] }).items\n : []\n return rawItems\n .map((item: unknown): NewDealItem | null => {\n if (!item || typeof item !== 'object') return null\n const data = item as any\n return {\n id: typeof data.id === 'string' ? data.id : null,\n title: typeof data.title === 'string' ? data.title : null,\n status: typeof data.status === 'string' ? data.status : null,\n createdAt: typeof data.createdAt === 'string' ? data.createdAt : '',\n }\n })\n .filter((item: NewDealItem | null): item is NewDealItem => !!item && !!item.id && !!item.createdAt)\n}\n\nfunction resolveDetailHref(item: NewDealItem): string | null {\n if (!item.id) return null\n return `/backend/customers/deals/${encodeURIComponent(item.id)}`\n}\n\nfunction formatDate(value: string | null, locale?: string): string {\n if (!value) return ''\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n return date.toLocaleString(locale ?? undefined, { dateStyle: 'medium', timeStyle: 'short' })\n}\n\nconst CustomerNewDealsWidget: React.FC<DashboardWidgetComponentProps<CustomerNewDealsSettings>> = ({\n mode,\n settings = DEFAULT_SETTINGS,\n onSettingsChange,\n refreshToken,\n onRefreshStateChange,\n}) => {\n const t = useT()\n const hydrated = React.useMemo(() => hydrateNewDealsSettings(settings), [settings])\n const [items, setItems] = React.useState<NewDealItem[]>([])\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [locale, setLocale] = React.useState<string | undefined>(undefined)\n\n React.useEffect(() => {\n if (typeof navigator !== 'undefined') {\n setLocale(navigator.language)\n }\n }, [])\n\n const refresh = React.useCallback(async () => {\n onRefreshStateChange?.(true)\n setLoading(true)\n setError(null)\n try {\n const data = await loadNewDeals(hydrated)\n setItems(data)\n } catch (err) {\n console.error('Failed to load new deals widget data', err)\n setError(t('customers.widgets.newDeals.error'))\n } finally {\n setLoading(false)\n onRefreshStateChange?.(false)\n }\n }, [hydrated, onRefreshStateChange, t])\n\n React.useEffect(() => {\n refresh().catch(() => {})\n }, [refresh, refreshToken])\n\n if (mode === 'settings') {\n return (\n <div className=\"space-y-4 text-sm\">\n <div className=\"space-y-1.5\">\n <label htmlFor=\"customer-new-deals-page-size\" className=\"text-xs font-semibold uppercase text-muted-foreground\">\n {t('customers.widgets.newDeals.settings.pageSize')}\n </label>\n <
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport type { DashboardWidgetComponentProps } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { DEFAULT_SETTINGS, hydrateNewDealsSettings, type CustomerNewDealsSettings } from './config'\n\ntype NewDealItem = {\n id: string\n title: string | null\n status: string | null\n createdAt: string\n}\n\nasync function loadNewDeals(settings: CustomerNewDealsSettings): Promise<NewDealItem[]> {\n const params = new URLSearchParams({ limit: String(settings.pageSize) })\n const call = await apiCall<{ items?: unknown[]; error?: string }>(\n `/api/customers/dashboard/widgets/new-deals?${params.toString()}`,\n )\n if (!call.ok) {\n const message =\n typeof (call.result as Record<string, unknown> | null)?.error === 'string'\n ? ((call.result as Record<string, unknown>).error as string)\n : `Request failed with status ${call.status}`\n throw new Error(message)\n }\n const payload = call.result ?? {}\n const rawItems = Array.isArray((payload as { items?: unknown[] }).items)\n ? (payload as { items: unknown[] }).items\n : []\n return rawItems\n .map((item: unknown): NewDealItem | null => {\n if (!item || typeof item !== 'object') return null\n const data = item as any\n return {\n id: typeof data.id === 'string' ? data.id : null,\n title: typeof data.title === 'string' ? data.title : null,\n status: typeof data.status === 'string' ? data.status : null,\n createdAt: typeof data.createdAt === 'string' ? data.createdAt : '',\n }\n })\n .filter((item: NewDealItem | null): item is NewDealItem => !!item && !!item.id && !!item.createdAt)\n}\n\nfunction resolveDetailHref(item: NewDealItem): string | null {\n if (!item.id) return null\n return `/backend/customers/deals/${encodeURIComponent(item.id)}`\n}\n\nfunction formatDate(value: string | null, locale?: string): string {\n if (!value) return ''\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n return date.toLocaleString(locale ?? undefined, { dateStyle: 'medium', timeStyle: 'short' })\n}\n\nconst CustomerNewDealsWidget: React.FC<DashboardWidgetComponentProps<CustomerNewDealsSettings>> = ({\n mode,\n settings = DEFAULT_SETTINGS,\n onSettingsChange,\n refreshToken,\n onRefreshStateChange,\n}) => {\n const t = useT()\n const hydrated = React.useMemo(() => hydrateNewDealsSettings(settings), [settings])\n const [items, setItems] = React.useState<NewDealItem[]>([])\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [locale, setLocale] = React.useState<string | undefined>(undefined)\n\n React.useEffect(() => {\n if (typeof navigator !== 'undefined') {\n setLocale(navigator.language)\n }\n }, [])\n\n const refresh = React.useCallback(async () => {\n onRefreshStateChange?.(true)\n setLoading(true)\n setError(null)\n try {\n const data = await loadNewDeals(hydrated)\n setItems(data)\n } catch (err) {\n console.error('Failed to load new deals widget data', err)\n setError(t('customers.widgets.newDeals.error'))\n } finally {\n setLoading(false)\n onRefreshStateChange?.(false)\n }\n }, [hydrated, onRefreshStateChange, t])\n\n React.useEffect(() => {\n refresh().catch(() => {})\n }, [refresh, refreshToken])\n\n if (mode === 'settings') {\n return (\n <div className=\"space-y-4 text-sm\">\n <div className=\"space-y-1.5\">\n <label htmlFor=\"customer-new-deals-page-size\" className=\"text-xs font-semibold uppercase text-muted-foreground\">\n {t('customers.widgets.newDeals.settings.pageSize')}\n </label>\n <Input\n id=\"customer-new-deals-page-size\"\n type=\"number\"\n min={1}\n max={20}\n className=\"w-24\"\n value={hydrated.pageSize}\n onChange={(event) => {\n const next = Number(event.target.value)\n onSettingsChange({ ...hydrated, pageSize: Number.isFinite(next) ? next : hydrated.pageSize })\n }}\n />\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-4\">\n {error ? (\n <p className=\"text-sm text-destructive\">{error}</p>\n ) : loading ? (\n <div className=\"flex h-32 items-center justify-center\">\n <Spinner className=\"h-6 w-6 text-muted-foreground\" />\n </div>\n ) : items.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">{t('customers.widgets.newDeals.empty')}</p>\n ) : (\n <ul className=\"space-y-3\">\n {items.map((item) => {\n const href = resolveDetailHref(item)\n const createdLabel = formatDate(item.createdAt, locale)\n return (\n <li key={item.id} className=\"rounded-md border p-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <p className=\"text-sm font-medium\">{item.title ?? t('customers.widgets.newDeals.untitled')}</p>\n <p className=\"text-xs text-muted-foreground\">{item.status ?? t('customers.widgets.common.unknown')}</p>\n </div>\n <p className=\"text-xs text-muted-foreground\">{createdLabel || t('customers.widgets.common.unknownDate')}</p>\n </div>\n {href ? (\n <div className=\"mt-2 text-xs\">\n <Link className=\"text-primary hover:underline\" href={href}>\n {t('customers.widgets.common.viewRecord')}\n </Link>\n </div>\n ) : null}\n </li>\n )\n })}\n </ul>\n )}\n </div>\n )\n}\n\nexport default CustomerNewDealsWidget\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,6 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import Link from "next/link";
|
|
5
5
|
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
6
6
|
import { formatRelativeTime } from "@open-mercato/shared/lib/time";
|
|
7
|
+
import { Input } from "@open-mercato/ui/primitives/input";
|
|
7
8
|
import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
8
9
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
9
10
|
import {
|
|
@@ -100,13 +101,13 @@ const CustomerNextInteractionsWidget = ({
|
|
|
100
101
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
101
102
|
/* @__PURE__ */ jsx("label", { htmlFor: "customer-next-interactions-page-size", className: "text-xs font-semibold uppercase text-muted-foreground", children: t("customers.widgets.nextInteractions.settings.pageSize") }),
|
|
102
103
|
/* @__PURE__ */ jsx(
|
|
103
|
-
|
|
104
|
+
Input,
|
|
104
105
|
{
|
|
105
106
|
id: "customer-next-interactions-page-size",
|
|
106
107
|
type: "number",
|
|
107
108
|
min: 1,
|
|
108
109
|
max: 20,
|
|
109
|
-
className: "w-24
|
|
110
|
+
className: "w-24",
|
|
110
111
|
value: hydrated.pageSize,
|
|
111
112
|
onChange: (event) => {
|
|
112
113
|
const next = Number(event.target.value);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/customers/widgets/dashboard/next-interactions/widget.client.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport type { DashboardWidgetComponentProps } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { formatRelativeTime } from '@open-mercato/shared/lib/time'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n DEFAULT_SETTINGS,\n hydrateNextInteractionsSettings,\n type CustomerNextInteractionsSettings,\n} from './config'\nimport { renderDictionaryColor, renderDictionaryIcon } from '../../../lib/dictionaries'\n\ntype NextInteractionItem = {\n id: string\n displayName: string | null\n kind: string | null\n nextInteractionAt: string | null\n nextInteractionName: string | null\n nextInteractionIcon: string | null\n nextInteractionColor: string | null\n organizationId: string | null\n}\n\ntype ApiResponse = {\n items: NextInteractionItem[]\n now?: string\n}\n\n// NOTE(SPEC-046b): This widget reads from next_interaction_* projection fields\n// on CustomerEntity. These fields are now maintained by the interaction\n// projection recompute service (lib/interactionProjection.ts).\nasync function loadNextInteractions(settings: CustomerNextInteractionsSettings): Promise<ApiResponse> {\n const params = new URLSearchParams({\n limit: String(settings.pageSize),\n includePast: settings.includePast ? 'true' : 'false',\n })\n const call = await apiCall<ApiResponse>(`/api/customers/dashboard/widgets/next-interactions?${params.toString()}`)\n if (!call.ok) {\n const rawError = (call.result as Record<string, unknown> | null)?.error\n const message =\n typeof rawError === 'string'\n ? rawError\n : `Request failed with status ${call.status}`\n throw new Error(message)\n }\n const payloadData = (call.result ?? {}) as Record<string, unknown>\n const now = typeof payloadData.now === 'string' ? payloadData.now : undefined\n const rawItems = Array.isArray(payloadData.items) ? payloadData.items : []\n const items = rawItems\n .map((item): NextInteractionItem | null => {\n if (!item || typeof item !== 'object') return null\n const data = item as Record<string, unknown>\n const id = typeof data.id === 'string' ? data.id : null\n if (!id) return null\n return {\n id,\n displayName: typeof data.displayName === 'string' ? data.displayName : null,\n kind: typeof data.kind === 'string' ? data.kind : null,\n nextInteractionAt: typeof data.nextInteractionAt === 'string' ? data.nextInteractionAt : null,\n nextInteractionName: typeof data.nextInteractionName === 'string' ? data.nextInteractionName : null,\n nextInteractionIcon: typeof data.nextInteractionIcon === 'string' ? data.nextInteractionIcon : null,\n nextInteractionColor: typeof data.nextInteractionColor === 'string' ? data.nextInteractionColor : null,\n organizationId: typeof data.organizationId === 'string' ? data.organizationId : null,\n }\n })\n .filter((item): item is NextInteractionItem => !!item && !!item.id)\n\n return { items, now }\n}\n\nfunction resolveDetailHref(item: NextInteractionItem): string | null {\n if (!item.id || !item.kind) return null\n if (item.kind === 'company') return `/backend/customers/companies-v2/${encodeURIComponent(item.id)}`\n if (item.kind === 'person') return `/backend/customers/people-v2/${encodeURIComponent(item.id)}`\n return null\n}\n\nfunction formatAbsolute(value: string | null, locale?: string): string {\n if (!value) return ''\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n return date.toLocaleString(locale ?? undefined, {\n dateStyle: 'medium',\n timeStyle: 'short',\n })\n}\n\nconst CustomerNextInteractionsWidget: React.FC<DashboardWidgetComponentProps<CustomerNextInteractionsSettings>> = ({\n mode,\n settings = DEFAULT_SETTINGS,\n onSettingsChange,\n refreshToken,\n onRefreshStateChange,\n}) => {\n const t = useT()\n const hydrated = React.useMemo(() => hydrateNextInteractionsSettings(settings), [settings])\n const [data, setData] = React.useState<NextInteractionItem[]>([])\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [locale, setLocale] = React.useState<string | undefined>(undefined)\n\n React.useEffect(() => {\n if (typeof navigator !== 'undefined') {\n setLocale(navigator.language)\n }\n }, [])\n\n const refresh = React.useCallback(async () => {\n onRefreshStateChange?.(true)\n setLoading(true)\n setError(null)\n try {\n const response = await loadNextInteractions(hydrated)\n setData(response.items)\n } catch (err) {\n console.error('Failed to load next interactions widget data', err)\n setError(t('customers.widgets.nextInteractions.error'))\n } finally {\n setLoading(false)\n onRefreshStateChange?.(false)\n }\n }, [hydrated, onRefreshStateChange, t])\n\n React.useEffect(() => {\n refresh().catch(() => {})\n }, [refresh, refreshToken])\n\n if (mode === 'settings') {\n return (\n <div className=\"space-y-4 text-sm\">\n <div className=\"space-y-1.5\">\n <label htmlFor=\"customer-next-interactions-page-size\" className=\"text-xs font-semibold uppercase text-muted-foreground\">\n {t('customers.widgets.nextInteractions.settings.pageSize')}\n </label>\n <
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport type { DashboardWidgetComponentProps } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { formatRelativeTime } from '@open-mercato/shared/lib/time'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport {\n DEFAULT_SETTINGS,\n hydrateNextInteractionsSettings,\n type CustomerNextInteractionsSettings,\n} from './config'\nimport { renderDictionaryColor, renderDictionaryIcon } from '../../../lib/dictionaries'\n\ntype NextInteractionItem = {\n id: string\n displayName: string | null\n kind: string | null\n nextInteractionAt: string | null\n nextInteractionName: string | null\n nextInteractionIcon: string | null\n nextInteractionColor: string | null\n organizationId: string | null\n}\n\ntype ApiResponse = {\n items: NextInteractionItem[]\n now?: string\n}\n\n// NOTE(SPEC-046b): This widget reads from next_interaction_* projection fields\n// on CustomerEntity. These fields are now maintained by the interaction\n// projection recompute service (lib/interactionProjection.ts).\nasync function loadNextInteractions(settings: CustomerNextInteractionsSettings): Promise<ApiResponse> {\n const params = new URLSearchParams({\n limit: String(settings.pageSize),\n includePast: settings.includePast ? 'true' : 'false',\n })\n const call = await apiCall<ApiResponse>(`/api/customers/dashboard/widgets/next-interactions?${params.toString()}`)\n if (!call.ok) {\n const rawError = (call.result as Record<string, unknown> | null)?.error\n const message =\n typeof rawError === 'string'\n ? rawError\n : `Request failed with status ${call.status}`\n throw new Error(message)\n }\n const payloadData = (call.result ?? {}) as Record<string, unknown>\n const now = typeof payloadData.now === 'string' ? payloadData.now : undefined\n const rawItems = Array.isArray(payloadData.items) ? payloadData.items : []\n const items = rawItems\n .map((item): NextInteractionItem | null => {\n if (!item || typeof item !== 'object') return null\n const data = item as Record<string, unknown>\n const id = typeof data.id === 'string' ? data.id : null\n if (!id) return null\n return {\n id,\n displayName: typeof data.displayName === 'string' ? data.displayName : null,\n kind: typeof data.kind === 'string' ? data.kind : null,\n nextInteractionAt: typeof data.nextInteractionAt === 'string' ? data.nextInteractionAt : null,\n nextInteractionName: typeof data.nextInteractionName === 'string' ? data.nextInteractionName : null,\n nextInteractionIcon: typeof data.nextInteractionIcon === 'string' ? data.nextInteractionIcon : null,\n nextInteractionColor: typeof data.nextInteractionColor === 'string' ? data.nextInteractionColor : null,\n organizationId: typeof data.organizationId === 'string' ? data.organizationId : null,\n }\n })\n .filter((item): item is NextInteractionItem => !!item && !!item.id)\n\n return { items, now }\n}\n\nfunction resolveDetailHref(item: NextInteractionItem): string | null {\n if (!item.id || !item.kind) return null\n if (item.kind === 'company') return `/backend/customers/companies-v2/${encodeURIComponent(item.id)}`\n if (item.kind === 'person') return `/backend/customers/people-v2/${encodeURIComponent(item.id)}`\n return null\n}\n\nfunction formatAbsolute(value: string | null, locale?: string): string {\n if (!value) return ''\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return ''\n return date.toLocaleString(locale ?? undefined, {\n dateStyle: 'medium',\n timeStyle: 'short',\n })\n}\n\nconst CustomerNextInteractionsWidget: React.FC<DashboardWidgetComponentProps<CustomerNextInteractionsSettings>> = ({\n mode,\n settings = DEFAULT_SETTINGS,\n onSettingsChange,\n refreshToken,\n onRefreshStateChange,\n}) => {\n const t = useT()\n const hydrated = React.useMemo(() => hydrateNextInteractionsSettings(settings), [settings])\n const [data, setData] = React.useState<NextInteractionItem[]>([])\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [locale, setLocale] = React.useState<string | undefined>(undefined)\n\n React.useEffect(() => {\n if (typeof navigator !== 'undefined') {\n setLocale(navigator.language)\n }\n }, [])\n\n const refresh = React.useCallback(async () => {\n onRefreshStateChange?.(true)\n setLoading(true)\n setError(null)\n try {\n const response = await loadNextInteractions(hydrated)\n setData(response.items)\n } catch (err) {\n console.error('Failed to load next interactions widget data', err)\n setError(t('customers.widgets.nextInteractions.error'))\n } finally {\n setLoading(false)\n onRefreshStateChange?.(false)\n }\n }, [hydrated, onRefreshStateChange, t])\n\n React.useEffect(() => {\n refresh().catch(() => {})\n }, [refresh, refreshToken])\n\n if (mode === 'settings') {\n return (\n <div className=\"space-y-4 text-sm\">\n <div className=\"space-y-1.5\">\n <label htmlFor=\"customer-next-interactions-page-size\" className=\"text-xs font-semibold uppercase text-muted-foreground\">\n {t('customers.widgets.nextInteractions.settings.pageSize')}\n </label>\n <Input\n id=\"customer-next-interactions-page-size\"\n type=\"number\"\n min={1}\n max={20}\n className=\"w-24\"\n value={hydrated.pageSize}\n onChange={(event) => {\n const next = Number(event.target.value)\n onSettingsChange({ ...hydrated, pageSize: Number.isFinite(next) ? next : hydrated.pageSize })\n }}\n />\n </div>\n <label className=\"flex items-center gap-2 text-xs text-muted-foreground\">\n <input\n type=\"checkbox\"\n checked={hydrated.includePast}\n onChange={(event) => onSettingsChange({ ...hydrated, includePast: event.target.checked })}\n />\n {t('customers.widgets.nextInteractions.settings.includePast')}\n </label>\n </div>\n )\n }\n\n return (\n <div className=\"space-y-4\">\n {error ? (\n <p className=\"text-sm text-destructive\">{error}</p>\n ) : loading ? (\n <div className=\"flex h-32 items-center justify-center\">\n <Spinner className=\"h-6 w-6 text-muted-foreground\" />\n </div>\n ) : data.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">{t('customers.widgets.nextInteractions.empty')}</p>\n ) : (\n <ul className=\"space-y-3\">\n {data.map((item) => {\n const href = resolveDetailHref(item)\n const absolute = formatAbsolute(item.nextInteractionAt, locale)\n const relative = formatRelativeTime(item.nextInteractionAt, { locale }) ?? ''\n return (\n <li key={item.id} className=\"rounded-md border p-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-start gap-3\">\n {item.nextInteractionIcon ? (\n <span className=\"mt-0.5 inline-flex h-7 w-7 items-center justify-center rounded border border-border bg-card\">\n {renderDictionaryIcon(item.nextInteractionIcon, 'h-4 w-4')}\n </span>\n ) : null}\n <div>\n <p className=\"text-sm font-medium\">{item.displayName ?? t('customers.widgets.common.unknown')}</p>\n {item.nextInteractionName ? (\n <p className=\"text-xs text-muted-foreground\">{item.nextInteractionName}</p>\n ) : null}\n </div>\n </div>\n <div className=\"flex items-center gap-2 text-right text-xs text-muted-foreground\">\n <div>\n <p>{absolute || t('customers.widgets.common.unknownDate')}</p>\n {relative ? <p>{relative}</p> : null}\n </div>\n {item.nextInteractionColor\n ? renderDictionaryColor(item.nextInteractionColor, 'h-3 w-3 rounded-full border border-border')\n : null}\n </div>\n </div>\n {href ? (\n <div className=\"mt-2 text-xs\">\n <Link className=\"text-primary hover:underline\" href={href}>\n {t('customers.widgets.common.viewRecord')}\n </Link>\n </div>\n ) : null}\n </li>\n )\n })}\n </ul>\n )}\n </div>\n )\n}\n\nexport default CustomerNextInteractionsWidget\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,6 +3,8 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
5
5
|
import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
6
|
+
import { RadioGroup } from "@open-mercato/ui/primitives/radio";
|
|
7
|
+
import { RadioField } from "@open-mercato/ui/primitives/radio-field";
|
|
6
8
|
import { apiCallOrThrow, readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
7
9
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
8
10
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
@@ -208,34 +210,31 @@ const WidgetVisibilityEditor = React.forwardRef(function WidgetVisibilityEditor2
|
|
|
208
210
|
}
|
|
209
211
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
210
212
|
error && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive", children: error }),
|
|
211
|
-
kind === "user" && /* @__PURE__ */ jsxs(
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
t("dashboards.widgets.mode.override", "Override for this user")
|
|
237
|
-
] })
|
|
238
|
-
] }),
|
|
213
|
+
kind === "user" && /* @__PURE__ */ jsxs(
|
|
214
|
+
RadioGroup,
|
|
215
|
+
{
|
|
216
|
+
className: "flex flex-row items-center gap-3 rounded-md border bg-muted/30 px-3 py-2",
|
|
217
|
+
name: "widgetOverride",
|
|
218
|
+
value: mode,
|
|
219
|
+
onValueChange: (next) => setMode(next),
|
|
220
|
+
children: [
|
|
221
|
+
/* @__PURE__ */ jsx(
|
|
222
|
+
RadioField,
|
|
223
|
+
{
|
|
224
|
+
value: "inherit",
|
|
225
|
+
label: t("dashboards.widgets.mode.inherit", "Inherit from roles")
|
|
226
|
+
}
|
|
227
|
+
),
|
|
228
|
+
/* @__PURE__ */ jsx(
|
|
229
|
+
RadioField,
|
|
230
|
+
{
|
|
231
|
+
value: "override",
|
|
232
|
+
label: t("dashboards.widgets.mode.override", "Override for this user")
|
|
233
|
+
}
|
|
234
|
+
)
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
),
|
|
239
238
|
kind === "user" && mode === "inherit" && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-muted bg-muted/30 px-3 py-2 text-xs text-muted-foreground", children: t("dashboards.widgets.mode.hint", "This user currently inherits widgets from their assigned roles. Switch to override to customize.") }),
|
|
240
239
|
(kind === "role" || mode === "override") && /* @__PURE__ */ jsx("div", { className: "space-y-3", children: catalog.map((widget) => /* @__PURE__ */ jsxs("label", { className: "flex items-start gap-3 rounded-md border px-3 py-2 hover:border-primary/40", children: [
|
|
241
240
|
/* @__PURE__ */ jsx(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/dashboards/components/WidgetVisibilityEditor.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype WidgetCatalogItem = {\n id: string\n title: string\n description: string | null\n}\n\ntype RoleResponse = {\n widgetIds: string[]\n hasCustom: boolean\n scope: { tenantId: string | null; organizationId: string | null }\n}\n\ntype UserResponse = {\n mode: 'inherit' | 'override'\n widgetIds: string[]\n hasCustom: boolean\n effectiveWidgetIds: string[]\n scope: { tenantId: string | null; organizationId: string | null }\n}\n\ntype BaseProps = {\n tenantId?: string | null\n organizationId?: string | null\n}\n\ntype RoleProps = BaseProps & {\n kind: 'role'\n targetId: string\n}\n\ntype UserProps = BaseProps & {\n kind: 'user'\n targetId: string\n}\n\ntype WidgetVisibilityEditorProps = RoleProps | UserProps\n\nexport type WidgetVisibilityEditorHandle = {\n save: () => Promise<void>\n}\n\nconst EMPTY: string[] = []\n\nfunction resolveWidgetText(\n t: (key: string, fallback: string) => string,\n id: string,\n field: 'title' | 'description',\n fallback: string,\n): string {\n const key1 = `${id}.${field}`\n const result1 = t(key1, '')\n if (result1 && result1 !== key1) return result1\n\n const key2 = `dashboard.widgets.${id}.${field}`\n const result2 = t(key2, '')\n if (result2 && result2 !== key2) return result2\n\n const dotIndex = id.lastIndexOf('.')\n if (dotIndex > 0) {\n const prefix = id.slice(0, dotIndex)\n const lastPart = id.slice(dotIndex + 1)\n const key3 = `${prefix}.widgets.${lastPart}.${field}`\n const result3 = t(key3, '')\n if (result3 && result3 !== key3) return result3\n }\n\n return fallback\n}\n\nexport const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHandle, WidgetVisibilityEditorProps>(function WidgetVisibilityEditor(props, ref) {\n const t = useT()\n const tRef = React.useRef(t)\n tRef.current = t\n\n const resolveTitle = React.useCallback(\n (widget: WidgetCatalogItem) => resolveWidgetText(t, widget.id, 'title', widget.title),\n [t],\n )\n\n const resolveDescription = React.useCallback(\n (widget: WidgetCatalogItem) => resolveWidgetText(t, widget.id, 'description', widget.description || ''),\n [t],\n )\n const { kind, targetId, tenantId, organizationId } = props\n const [catalog, setCatalog] = React.useState<WidgetCatalogItem[]>([])\n const [loading, setLoading] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const [selected, setSelected] = React.useState<string[]>(EMPTY)\n const [original, setOriginal] = React.useState<string[]>(EMPTY)\n const [mode, setMode] = React.useState<'inherit' | 'override'>('inherit')\n const [originalMode, setOriginalMode] = React.useState<'inherit' | 'override'>('inherit')\n const [effective, setEffective] = React.useState<string[]>(EMPTY)\n\n const dirty = React.useMemo(() => {\n if (kind === 'user') {\n if (mode !== originalMode) return true\n if (mode === 'override') return selected.join('|') !== original.join('|')\n return false\n }\n return selected.join('|') !== original.join('|')\n }, [kind, mode, original, originalMode, selected])\n\n const loadCatalog = React.useCallback(async () => {\n const data = await readApiResultOrThrow<{ items?: unknown[] }>(\n '/api/dashboards/widgets/catalog',\n undefined,\n { errorMessage: tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const items = Array.isArray(data?.items) ? data.items : []\n const mapped = items\n .map((item: unknown): WidgetCatalogItem | null => {\n if (!item || typeof item !== 'object') return null\n const entry = item as Record<string, unknown>\n const id = typeof entry.id === 'string' ? entry.id : null\n if (!id || !id.length) return null\n const title =\n typeof entry.title === 'string' && entry.title.length ? entry.title : id\n const description =\n typeof entry.description === 'string' && entry.description.length ? entry.description : null\n return { id, title, description }\n })\n .filter((item: WidgetCatalogItem | null): item is WidgetCatalogItem => item !== null)\n setCatalog(mapped)\n }, [])\n\n const loadRoleData = React.useCallback(async () => {\n const params = new URLSearchParams({ roleId: targetId })\n if (tenantId) params.set('tenantId', tenantId)\n if (organizationId) params.set('organizationId', organizationId)\n const data = await readApiResultOrThrow<RoleResponse>(\n `/api/dashboards/roles/widgets?${params.toString()}`,\n undefined,\n { errorMessage: tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const ids = Array.isArray(data.widgetIds) ? data.widgetIds : []\n setSelected(ids)\n setOriginal(ids)\n setMode('override')\n setOriginalMode('override')\n setEffective(ids)\n }, [organizationId, targetId, tenantId])\n\n const loadUserData = React.useCallback(async () => {\n const params = new URLSearchParams({ userId: targetId })\n if (tenantId) params.set('tenantId', tenantId)\n if (organizationId) params.set('organizationId', organizationId)\n const data = await readApiResultOrThrow<UserResponse>(\n `/api/dashboards/users/widgets?${params.toString()}`,\n undefined,\n { errorMessage: tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const ids = Array.isArray(data.widgetIds) ? data.widgetIds : []\n setSelected(ids)\n setOriginal(ids)\n setMode(data.mode || 'inherit')\n setOriginalMode(data.mode || 'inherit')\n setEffective(Array.isArray(data.effectiveWidgetIds) ? data.effectiveWidgetIds : [])\n }, [organizationId, targetId, tenantId])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setLoading(true)\n setError(null)\n try {\n await loadCatalog()\n if (kind === 'role') await loadRoleData()\n else await loadUserData()\n } catch (err) {\n console.error('Failed to load widget visibility data', err)\n if (!cancelled) {\n setError(tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.'))\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [kind, loadCatalog, loadRoleData, loadUserData])\n\n const toggle = React.useCallback((id: string) => {\n setSelected((prev) => (prev.includes(id) ? prev.filter((value) => value !== id) : [...prev, id]))\n }, [])\n\n const resetSelections = React.useCallback(() => {\n setSelected(original)\n setMode(originalMode)\n }, [original, originalMode])\n\n const save = React.useCallback(async () => {\n if (loading) return\n if (error && catalog.length === 0) return\n if (!dirty) return\n setSaving(true)\n setError(null)\n try {\n const saveError = t('dashboards.widgets.error.save', 'Unable to save dashboard widget preferences.')\n if (kind === 'role') {\n const payload = {\n roleId: targetId,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n widgetIds: selected,\n }\n await apiCallOrThrow('/api/dashboards/roles/widgets', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }, { errorMessage: saveError })\n setOriginal(selected)\n setOriginalMode('override')\n setEffective(selected)\n } else {\n const payload = {\n userId: targetId,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n mode,\n widgetIds: selected,\n }\n await apiCallOrThrow('/api/dashboards/users/widgets', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }, { errorMessage: saveError })\n setOriginal(selected)\n if (mode === 'inherit') {\n const refreshed = await readApiResultOrThrow<UserResponse>(\n `/api/dashboards/users/widgets?userId=${encodeURIComponent(targetId)}`,\n undefined,\n { errorMessage: saveError },\n )\n setEffective(Array.isArray(refreshed.effectiveWidgetIds) ? refreshed.effectiveWidgetIds : [])\n } else {\n setEffective(selected)\n }\n setOriginal(selected)\n setOriginalMode(mode)\n }\n try { flash(t('dashboards.widgets.flash.saved', 'Dashboard widgets updated'), 'success') } catch {}\n } catch (err) {\n console.error('Failed to save widget visibility', err)\n setError(t('dashboards.widgets.error.save', 'Unable to save dashboard widget preferences.'))\n } finally {\n setSaving(false)\n }\n }, [catalog.length, dirty, error, kind, loading, mode, organizationId, selected, t, targetId, tenantId])\n\n React.useImperativeHandle(ref, () => ({ save }), [save])\n\n if (loading) {\n return (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner size=\"sm\" /> {t('dashboards.widgets.loading', 'Loading widget options\u2026')}\n </div>\n )\n }\n\n if (error && catalog.length === 0) {\n return <div className=\"rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">{error}</div>\n }\n\n return (\n <div className=\"space-y-4\">\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">{error}</div>\n )}\n\n {kind === 'user' && (\n <div className=\"flex items-center gap-3 rounded-md border bg-muted/30 px-3 py-2\">\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n name=\"widgetOverride\"\n value=\"inherit\"\n checked={mode === 'inherit'}\n onChange={() => setMode('inherit')}\n />\n {t('dashboards.widgets.mode.inherit', 'Inherit from roles')}\n </label>\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n name=\"widgetOverride\"\n value=\"override\"\n checked={mode === 'override'}\n onChange={() => setMode('override')}\n />\n {t('dashboards.widgets.mode.override', 'Override for this user')}\n </label>\n </div>\n )}\n\n {kind === 'user' && mode === 'inherit' && (\n <div className=\"rounded-md border border-muted bg-muted/30 px-3 py-2 text-xs text-muted-foreground\">\n {t('dashboards.widgets.mode.hint', 'This user currently inherits widgets from their assigned roles. Switch to override to customize.')}\n </div>\n )}\n\n {(kind === 'role' || mode === 'override') && (\n <div className=\"space-y-3\">\n {catalog.map((widget) => (\n <label key={widget.id} className=\"flex items-start gap-3 rounded-md border px-3 py-2 hover:border-primary/40\">\n <input\n type=\"checkbox\"\n className=\"mt-1 size-4\"\n checked={selected.includes(widget.id)}\n onChange={() => toggle(widget.id)}\n />\n <div>\n <div className=\"text-sm font-medium leading-none\">{resolveTitle(widget)}</div>\n {widget.description ? <div className=\"mt-1 text-xs text-muted-foreground\">{resolveDescription(widget)}</div> : null}\n </div>\n </label>\n ))}\n </div>\n )}\n\n {kind === 'user' && effective.length > 0 && (\n <div className=\"rounded-md border bg-muted/30 px-3 py-2 text-xs text-muted-foreground\">\n {t('dashboards.widgets.effective', 'Effective widgets:')} {effective.map((id) => { const meta = catalog.find((m) => m.id === id); return meta ? resolveTitle(meta) : id }).join(', ')}\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" onClick={save} disabled={saving || !dirty}>\n {saving ? t('dashboards.widgets.saving', 'Saving\u2026') : t('dashboards.widgets.save', 'Save widgets')}\n </Button>\n <Button type=\"button\" variant=\"ghost\" onClick={resetSelections} disabled={!dirty}>\n {t('dashboards.widgets.reset', 'Reset')}\n </Button>\n </div>\n </div>\n )\n})\n\nWidgetVisibilityEditor.displayName = 'WidgetVisibilityEditor'\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { RadioGroup } from '@open-mercato/ui/primitives/radio'\nimport { RadioField } from '@open-mercato/ui/primitives/radio-field'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\n\ntype WidgetCatalogItem = {\n id: string\n title: string\n description: string | null\n}\n\ntype RoleResponse = {\n widgetIds: string[]\n hasCustom: boolean\n scope: { tenantId: string | null; organizationId: string | null }\n}\n\ntype UserResponse = {\n mode: 'inherit' | 'override'\n widgetIds: string[]\n hasCustom: boolean\n effectiveWidgetIds: string[]\n scope: { tenantId: string | null; organizationId: string | null }\n}\n\ntype BaseProps = {\n tenantId?: string | null\n organizationId?: string | null\n}\n\ntype RoleProps = BaseProps & {\n kind: 'role'\n targetId: string\n}\n\ntype UserProps = BaseProps & {\n kind: 'user'\n targetId: string\n}\n\ntype WidgetVisibilityEditorProps = RoleProps | UserProps\n\nexport type WidgetVisibilityEditorHandle = {\n save: () => Promise<void>\n}\n\nconst EMPTY: string[] = []\n\nfunction resolveWidgetText(\n t: (key: string, fallback: string) => string,\n id: string,\n field: 'title' | 'description',\n fallback: string,\n): string {\n const key1 = `${id}.${field}`\n const result1 = t(key1, '')\n if (result1 && result1 !== key1) return result1\n\n const key2 = `dashboard.widgets.${id}.${field}`\n const result2 = t(key2, '')\n if (result2 && result2 !== key2) return result2\n\n const dotIndex = id.lastIndexOf('.')\n if (dotIndex > 0) {\n const prefix = id.slice(0, dotIndex)\n const lastPart = id.slice(dotIndex + 1)\n const key3 = `${prefix}.widgets.${lastPart}.${field}`\n const result3 = t(key3, '')\n if (result3 && result3 !== key3) return result3\n }\n\n return fallback\n}\n\nexport const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHandle, WidgetVisibilityEditorProps>(function WidgetVisibilityEditor(props, ref) {\n const t = useT()\n const tRef = React.useRef(t)\n tRef.current = t\n\n const resolveTitle = React.useCallback(\n (widget: WidgetCatalogItem) => resolveWidgetText(t, widget.id, 'title', widget.title),\n [t],\n )\n\n const resolveDescription = React.useCallback(\n (widget: WidgetCatalogItem) => resolveWidgetText(t, widget.id, 'description', widget.description || ''),\n [t],\n )\n const { kind, targetId, tenantId, organizationId } = props\n const [catalog, setCatalog] = React.useState<WidgetCatalogItem[]>([])\n const [loading, setLoading] = React.useState(true)\n const [saving, setSaving] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const [selected, setSelected] = React.useState<string[]>(EMPTY)\n const [original, setOriginal] = React.useState<string[]>(EMPTY)\n const [mode, setMode] = React.useState<'inherit' | 'override'>('inherit')\n const [originalMode, setOriginalMode] = React.useState<'inherit' | 'override'>('inherit')\n const [effective, setEffective] = React.useState<string[]>(EMPTY)\n\n const dirty = React.useMemo(() => {\n if (kind === 'user') {\n if (mode !== originalMode) return true\n if (mode === 'override') return selected.join('|') !== original.join('|')\n return false\n }\n return selected.join('|') !== original.join('|')\n }, [kind, mode, original, originalMode, selected])\n\n const loadCatalog = React.useCallback(async () => {\n const data = await readApiResultOrThrow<{ items?: unknown[] }>(\n '/api/dashboards/widgets/catalog',\n undefined,\n { errorMessage: tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const items = Array.isArray(data?.items) ? data.items : []\n const mapped = items\n .map((item: unknown): WidgetCatalogItem | null => {\n if (!item || typeof item !== 'object') return null\n const entry = item as Record<string, unknown>\n const id = typeof entry.id === 'string' ? entry.id : null\n if (!id || !id.length) return null\n const title =\n typeof entry.title === 'string' && entry.title.length ? entry.title : id\n const description =\n typeof entry.description === 'string' && entry.description.length ? entry.description : null\n return { id, title, description }\n })\n .filter((item: WidgetCatalogItem | null): item is WidgetCatalogItem => item !== null)\n setCatalog(mapped)\n }, [])\n\n const loadRoleData = React.useCallback(async () => {\n const params = new URLSearchParams({ roleId: targetId })\n if (tenantId) params.set('tenantId', tenantId)\n if (organizationId) params.set('organizationId', organizationId)\n const data = await readApiResultOrThrow<RoleResponse>(\n `/api/dashboards/roles/widgets?${params.toString()}`,\n undefined,\n { errorMessage: tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const ids = Array.isArray(data.widgetIds) ? data.widgetIds : []\n setSelected(ids)\n setOriginal(ids)\n setMode('override')\n setOriginalMode('override')\n setEffective(ids)\n }, [organizationId, targetId, tenantId])\n\n const loadUserData = React.useCallback(async () => {\n const params = new URLSearchParams({ userId: targetId })\n if (tenantId) params.set('tenantId', tenantId)\n if (organizationId) params.set('organizationId', organizationId)\n const data = await readApiResultOrThrow<UserResponse>(\n `/api/dashboards/users/widgets?${params.toString()}`,\n undefined,\n { errorMessage: tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.') },\n )\n const ids = Array.isArray(data.widgetIds) ? data.widgetIds : []\n setSelected(ids)\n setOriginal(ids)\n setMode(data.mode || 'inherit')\n setOriginalMode(data.mode || 'inherit')\n setEffective(Array.isArray(data.effectiveWidgetIds) ? data.effectiveWidgetIds : [])\n }, [organizationId, targetId, tenantId])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setLoading(true)\n setError(null)\n try {\n await loadCatalog()\n if (kind === 'role') await loadRoleData()\n else await loadUserData()\n } catch (err) {\n console.error('Failed to load widget visibility data', err)\n if (!cancelled) {\n setError(tRef.current('dashboards.widgets.error.load', 'Unable to load widget configuration.'))\n }\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [kind, loadCatalog, loadRoleData, loadUserData])\n\n const toggle = React.useCallback((id: string) => {\n setSelected((prev) => (prev.includes(id) ? prev.filter((value) => value !== id) : [...prev, id]))\n }, [])\n\n const resetSelections = React.useCallback(() => {\n setSelected(original)\n setMode(originalMode)\n }, [original, originalMode])\n\n const save = React.useCallback(async () => {\n if (loading) return\n if (error && catalog.length === 0) return\n if (!dirty) return\n setSaving(true)\n setError(null)\n try {\n const saveError = t('dashboards.widgets.error.save', 'Unable to save dashboard widget preferences.')\n if (kind === 'role') {\n const payload = {\n roleId: targetId,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n widgetIds: selected,\n }\n await apiCallOrThrow('/api/dashboards/roles/widgets', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }, { errorMessage: saveError })\n setOriginal(selected)\n setOriginalMode('override')\n setEffective(selected)\n } else {\n const payload = {\n userId: targetId,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n mode,\n widgetIds: selected,\n }\n await apiCallOrThrow('/api/dashboards/users/widgets', {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }, { errorMessage: saveError })\n setOriginal(selected)\n if (mode === 'inherit') {\n const refreshed = await readApiResultOrThrow<UserResponse>(\n `/api/dashboards/users/widgets?userId=${encodeURIComponent(targetId)}`,\n undefined,\n { errorMessage: saveError },\n )\n setEffective(Array.isArray(refreshed.effectiveWidgetIds) ? refreshed.effectiveWidgetIds : [])\n } else {\n setEffective(selected)\n }\n setOriginal(selected)\n setOriginalMode(mode)\n }\n try { flash(t('dashboards.widgets.flash.saved', 'Dashboard widgets updated'), 'success') } catch {}\n } catch (err) {\n console.error('Failed to save widget visibility', err)\n setError(t('dashboards.widgets.error.save', 'Unable to save dashboard widget preferences.'))\n } finally {\n setSaving(false)\n }\n }, [catalog.length, dirty, error, kind, loading, mode, organizationId, selected, t, targetId, tenantId])\n\n React.useImperativeHandle(ref, () => ({ save }), [save])\n\n if (loading) {\n return (\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner size=\"sm\" /> {t('dashboards.widgets.loading', 'Loading widget options\u2026')}\n </div>\n )\n }\n\n if (error && catalog.length === 0) {\n return <div className=\"rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">{error}</div>\n }\n\n return (\n <div className=\"space-y-4\">\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive\">{error}</div>\n )}\n\n {kind === 'user' && (\n <RadioGroup\n className=\"flex flex-row items-center gap-3 rounded-md border bg-muted/30 px-3 py-2\"\n name=\"widgetOverride\"\n value={mode}\n onValueChange={(next) => setMode(next as 'inherit' | 'override')}\n >\n <RadioField\n value=\"inherit\"\n label={t('dashboards.widgets.mode.inherit', 'Inherit from roles')}\n />\n <RadioField\n value=\"override\"\n label={t('dashboards.widgets.mode.override', 'Override for this user')}\n />\n </RadioGroup>\n )}\n\n {kind === 'user' && mode === 'inherit' && (\n <div className=\"rounded-md border border-muted bg-muted/30 px-3 py-2 text-xs text-muted-foreground\">\n {t('dashboards.widgets.mode.hint', 'This user currently inherits widgets from their assigned roles. Switch to override to customize.')}\n </div>\n )}\n\n {(kind === 'role' || mode === 'override') && (\n <div className=\"space-y-3\">\n {catalog.map((widget) => (\n <label key={widget.id} className=\"flex items-start gap-3 rounded-md border px-3 py-2 hover:border-primary/40\">\n <input\n type=\"checkbox\"\n className=\"mt-1 size-4\"\n checked={selected.includes(widget.id)}\n onChange={() => toggle(widget.id)}\n />\n <div>\n <div className=\"text-sm font-medium leading-none\">{resolveTitle(widget)}</div>\n {widget.description ? <div className=\"mt-1 text-xs text-muted-foreground\">{resolveDescription(widget)}</div> : null}\n </div>\n </label>\n ))}\n </div>\n )}\n\n {kind === 'user' && effective.length > 0 && (\n <div className=\"rounded-md border bg-muted/30 px-3 py-2 text-xs text-muted-foreground\">\n {t('dashboards.widgets.effective', 'Effective widgets:')} {effective.map((id) => { const meta = catalog.find((m) => m.id === id); return meta ? resolveTitle(meta) : id }).join(', ')}\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" onClick={save} disabled={saving || !dirty}>\n {saving ? t('dashboards.widgets.saving', 'Saving\u2026') : t('dashboards.widgets.save', 'Save widgets')}\n </Button>\n <Button type=\"button\" variant=\"ghost\" onClick={resetSelections} disabled={!dirty}>\n {t('dashboards.widgets.reset', 'Reset')}\n </Button>\n </div>\n </div>\n )\n})\n\nWidgetVisibilityEditor.displayName = 'WidgetVisibilityEditor'\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["WidgetVisibilityEditor"]
|
|
7
7
|
}
|