@open-mercato/core 0.4.2-canary-10c7a8bf2a → 0.4.2-canary-e6bf6a353e

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.
Files changed (136) hide show
  1. package/dist/modules/auth/lib/setup-app.js +2 -0
  2. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  3. package/dist/modules/catalog/analytics.js +27 -0
  4. package/dist/modules/catalog/analytics.js.map +7 -0
  5. package/dist/modules/customers/analytics.js +50 -0
  6. package/dist/modules/customers/analytics.js.map +7 -0
  7. package/dist/modules/dashboards/acl.js +2 -1
  8. package/dist/modules/dashboards/acl.js.map +2 -2
  9. package/dist/modules/dashboards/api/widgets/data/route.js +187 -0
  10. package/dist/modules/dashboards/api/widgets/data/route.js.map +7 -0
  11. package/dist/modules/dashboards/cli.js +142 -1
  12. package/dist/modules/dashboards/cli.js.map +2 -2
  13. package/dist/modules/dashboards/di.js +11 -0
  14. package/dist/modules/dashboards/di.js.map +7 -0
  15. package/dist/modules/dashboards/lib/aggregations.js +162 -0
  16. package/dist/modules/dashboards/lib/aggregations.js.map +7 -0
  17. package/dist/modules/dashboards/lib/formatters.js +34 -0
  18. package/dist/modules/dashboards/lib/formatters.js.map +7 -0
  19. package/dist/modules/dashboards/seed/analytics.js +383 -0
  20. package/dist/modules/dashboards/seed/analytics.js.map +7 -0
  21. package/dist/modules/dashboards/services/analyticsRegistry.js +52 -0
  22. package/dist/modules/dashboards/services/analyticsRegistry.js.map +7 -0
  23. package/dist/modules/dashboards/services/widgetDataService.js +207 -0
  24. package/dist/modules/dashboards/services/widgetDataService.js.map +7 -0
  25. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/config.js +18 -0
  26. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/config.js.map +7 -0
  27. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.js +128 -0
  28. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.js.map +7 -0
  29. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.js +25 -0
  30. package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.js.map +7 -0
  31. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/config.js +18 -0
  32. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/config.js.map +7 -0
  33. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.js +126 -0
  34. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.js.map +7 -0
  35. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.js +25 -0
  36. package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.js.map +7 -0
  37. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/config.js +18 -0
  38. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/config.js.map +7 -0
  39. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js +151 -0
  40. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js.map +7 -0
  41. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.js +25 -0
  42. package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.js.map +7 -0
  43. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/config.js +18 -0
  44. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/config.js.map +7 -0
  45. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.js +126 -0
  46. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.js.map +7 -0
  47. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.js +25 -0
  48. package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.js.map +7 -0
  49. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/config.js +16 -0
  50. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/config.js.map +7 -0
  51. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js +123 -0
  52. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js.map +7 -0
  53. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js +25 -0
  54. package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.js.map +7 -0
  55. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/config.js +18 -0
  56. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/config.js.map +7 -0
  57. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.js +128 -0
  58. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.js.map +7 -0
  59. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.js +25 -0
  60. package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.js.map +7 -0
  61. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/config.js +21 -0
  62. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/config.js.map +7 -0
  63. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js +211 -0
  64. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js.map +7 -0
  65. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.js +25 -0
  66. package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.js.map +7 -0
  67. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/config.js +19 -0
  68. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/config.js.map +7 -0
  69. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js +131 -0
  70. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js.map +7 -0
  71. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.js +25 -0
  72. package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.js.map +7 -0
  73. package/dist/modules/dashboards/widgets/dashboard/top-customers/config.js +19 -0
  74. package/dist/modules/dashboards/widgets/dashboard/top-customers/config.js.map +7 -0
  75. package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js +153 -0
  76. package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js.map +7 -0
  77. package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.js +25 -0
  78. package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.js.map +7 -0
  79. package/dist/modules/dashboards/widgets/dashboard/top-products/config.js +22 -0
  80. package/dist/modules/dashboards/widgets/dashboard/top-products/config.js.map +7 -0
  81. package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js +180 -0
  82. package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js.map +7 -0
  83. package/dist/modules/dashboards/widgets/dashboard/top-products/widget.js +25 -0
  84. package/dist/modules/dashboards/widgets/dashboard/top-products/widget.js.map +7 -0
  85. package/dist/modules/sales/analytics.js +67 -0
  86. package/dist/modules/sales/analytics.js.map +7 -0
  87. package/package.json +2 -2
  88. package/src/modules/auth/lib/setup-app.ts +2 -0
  89. package/src/modules/catalog/analytics.ts +24 -0
  90. package/src/modules/customers/analytics.ts +47 -0
  91. package/src/modules/dashboards/acl.ts +1 -0
  92. package/src/modules/dashboards/api/widgets/data/route.ts +221 -0
  93. package/src/modules/dashboards/cli.ts +164 -1
  94. package/src/modules/dashboards/di.ts +9 -0
  95. package/src/modules/dashboards/i18n/de.json +115 -1
  96. package/src/modules/dashboards/i18n/en.json +115 -1
  97. package/src/modules/dashboards/i18n/es.json +115 -1
  98. package/src/modules/dashboards/i18n/pl.json +115 -1
  99. package/src/modules/dashboards/lib/__tests__/aggregations.test.ts +327 -0
  100. package/src/modules/dashboards/lib/__tests__/formatters.test.ts +128 -0
  101. package/src/modules/dashboards/lib/aggregations.ts +225 -0
  102. package/src/modules/dashboards/lib/formatters.ts +36 -0
  103. package/src/modules/dashboards/seed/analytics.ts +405 -0
  104. package/src/modules/dashboards/services/analyticsRegistry.ts +79 -0
  105. package/src/modules/dashboards/services/widgetDataService.ts +329 -0
  106. package/src/modules/dashboards/widgets/dashboard/aov-kpi/config.ts +20 -0
  107. package/src/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.tsx +135 -0
  108. package/src/modules/dashboards/widgets/dashboard/aov-kpi/widget.ts +24 -0
  109. package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/config.ts +20 -0
  110. package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.tsx +133 -0
  111. package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.ts +24 -0
  112. package/src/modules/dashboards/widgets/dashboard/orders-by-status/config.ts +20 -0
  113. package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.tsx +154 -0
  114. package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.ts +24 -0
  115. package/src/modules/dashboards/widgets/dashboard/orders-kpi/config.ts +20 -0
  116. package/src/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.tsx +133 -0
  117. package/src/modules/dashboards/widgets/dashboard/orders-kpi/widget.ts +24 -0
  118. package/src/modules/dashboards/widgets/dashboard/pipeline-summary/config.ts +17 -0
  119. package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.tsx +137 -0
  120. package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.ts +24 -0
  121. package/src/modules/dashboards/widgets/dashboard/revenue-kpi/config.ts +20 -0
  122. package/src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.tsx +135 -0
  123. package/src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.ts +24 -0
  124. package/src/modules/dashboards/widgets/dashboard/revenue-trend/config.ts +24 -0
  125. package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.tsx +220 -0
  126. package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.ts +24 -0
  127. package/src/modules/dashboards/widgets/dashboard/sales-by-region/config.ts +21 -0
  128. package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.tsx +131 -0
  129. package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.ts +24 -0
  130. package/src/modules/dashboards/widgets/dashboard/top-customers/config.ts +21 -0
  131. package/src/modules/dashboards/widgets/dashboard/top-customers/widget.client.tsx +161 -0
  132. package/src/modules/dashboards/widgets/dashboard/top-customers/widget.ts +24 -0
  133. package/src/modules/dashboards/widgets/dashboard/top-products/config.ts +27 -0
  134. package/src/modules/dashboards/widgets/dashboard/top-products/widget.client.tsx +181 -0
  135. package/src/modules/dashboards/widgets/dashboard/top-products/widget.ts +24 -0
  136. package/src/modules/sales/analytics.ts +64 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { DashboardWidgetComponentProps } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { BarChart, type BarChartDataItem } from '@open-mercato/ui/backend/charts'\nimport {\n DateRangeSelect,\n InlineDateRangeSelect,\n type DateRangePreset,\n} from '@open-mercato/ui/backend/date-range'\nimport { DEFAULT_SETTINGS, hydrateSettings, type PipelineSummarySettings } from './config'\nimport type { WidgetDataResponse } from '../../../services/widgetDataService'\nimport { formatCurrencyCompact } from '../../../lib/formatters'\n\nasync function fetchPipelineData(settings: PipelineSummarySettings): Promise<WidgetDataResponse> {\n const body = {\n entityType: 'customers:deals',\n metric: {\n field: 'valueAmount',\n aggregate: 'sum',\n },\n groupBy: {\n field: 'pipelineStage',\n resolveLabels: true,\n },\n dateRange: {\n field: 'createdAt',\n preset: settings.dateRange,\n },\n }\n\n const call = await apiCall<WidgetDataResponse>('/api/dashboards/widgets/data', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n\n if (!call.ok) {\n const errorMsg = (call.result as Record<string, unknown>)?.error\n throw new Error(typeof errorMsg === 'string' ? errorMsg : 'Failed to fetch pipeline data')\n }\n\n return call.result as WidgetDataResponse\n}\n\nfunction formatStageLabel(stage: unknown, t: (key: string, fallback: string) => string): string {\n if (stage == null || stage === '') return t('dashboards.analytics.labels.unknown', 'Unknown')\n const stageStr = String(stage)\n if (stageStr === '0' || stageStr === 'null' || stageStr === 'undefined') {\n return t('dashboards.analytics.labels.unknown', 'Unknown')\n }\n return stageStr\n .replace(/_/g, ' ')\n .replace(/\\b\\w/g, (l) => l.toUpperCase())\n}\n\nconst PipelineSummaryWidget: React.FC<DashboardWidgetComponentProps<PipelineSummarySettings>> = ({\n mode,\n settings = DEFAULT_SETTINGS,\n onSettingsChange,\n refreshToken,\n onRefreshStateChange,\n}) => {\n const t = useT()\n const hydrated = React.useMemo(() => hydrateSettings(settings), [settings])\n const [data, setData] = React.useState<BarChartDataItem[]>([])\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n\n const refresh = React.useCallback(async () => {\n onRefreshStateChange?.(true)\n setLoading(true)\n setError(null)\n try {\n const result = await fetchPipelineData(hydrated)\n const chartData = result.data\n .filter((item) => item.groupKey != null && item.groupKey !== '' && String(item.groupKey) !== '0')\n .map((item) => ({\n stage: formatStageLabel(item.groupLabel ?? item.groupKey, t),\n Value: item.value ?? 0,\n }))\n setData(chartData)\n } catch (err) {\n console.error('Failed to load pipeline data', err)\n setError(t('dashboards.analytics.widgets.pipelineSummary.error', 'Failed to load data'))\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 <DateRangeSelect\n id=\"pipeline-summary-date-range\"\n label={t('dashboards.analytics.settings.dateRange', 'Date Range')}\n value={hydrated.dateRange}\n onChange={(dateRange: DateRangePreset) => onSettingsChange({ ...hydrated, dateRange })}\n />\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-col h-full\">\n <div className=\"flex justify-end mb-2\">\n <InlineDateRangeSelect\n value={hydrated.dateRange}\n onChange={(dateRange) => onSettingsChange({ ...hydrated, dateRange })}\n />\n </div>\n <div className=\"flex-1 min-h-0\">\n <BarChart\n data={data}\n index=\"stage\"\n categories={['Value']}\n categoryLabels={{ Value: t('dashboards.analytics.labels.value', 'Value') }}\n loading={loading}\n error={error}\n valueFormatter={formatCurrencyCompact}\n colors={['violet']}\n showLegend={false}\n emptyMessage={t('dashboards.analytics.widgets.pipelineSummary.empty', 'No deal data for this period')}\n />\n </div>\n </div>\n )\n}\n\nexport default PipelineSummaryWidget\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,25 @@
1
+ import PipelineSummaryWidget from "./widget.client.js";
2
+ import { DEFAULT_SETTINGS, hydrateSettings } from "./config.js";
3
+ const widget = {
4
+ metadata: {
5
+ id: "dashboards.analytics.pipelineSummary",
6
+ title: "Pipeline Summary",
7
+ description: "Deal value by pipeline stage",
8
+ features: ["analytics.view", "customers.deals.view"],
9
+ defaultSize: "md",
10
+ defaultEnabled: false,
11
+ defaultSettings: DEFAULT_SETTINGS,
12
+ tags: ["analytics", "customers", "deals", "chart"],
13
+ category: "analytics",
14
+ icon: "git-branch",
15
+ supportsRefresh: true
16
+ },
17
+ Widget: PipelineSummaryWidget,
18
+ hydrateSettings,
19
+ dehydrateSettings: (s) => ({ dateRange: s.dateRange })
20
+ };
21
+ var widget_default = widget;
22
+ export {
23
+ widget_default as default
24
+ };
25
+ //# sourceMappingURL=widget.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.ts"],
4
+ "sourcesContent": ["import type { DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'\nimport PipelineSummaryWidget from './widget.client'\nimport { DEFAULT_SETTINGS, hydrateSettings, type PipelineSummarySettings } from './config'\n\nconst widget: DashboardWidgetModule<PipelineSummarySettings> = {\n metadata: {\n id: 'dashboards.analytics.pipelineSummary',\n title: 'Pipeline Summary',\n description: 'Deal value by pipeline stage',\n features: ['analytics.view', 'customers.deals.view'],\n defaultSize: 'md',\n defaultEnabled: false,\n defaultSettings: DEFAULT_SETTINGS,\n tags: ['analytics', 'customers', 'deals', 'chart'],\n category: 'analytics',\n icon: 'git-branch',\n supportsRefresh: true,\n },\n Widget: PipelineSummaryWidget,\n hydrateSettings,\n dehydrateSettings: (s) => ({ dateRange: s.dateRange }),\n}\n\nexport default widget\n"],
5
+ "mappings": "AACA,OAAO,2BAA2B;AAClC,SAAS,kBAAkB,uBAAqD;AAEhF,MAAM,SAAyD;AAAA,EAC7D,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU,CAAC,kBAAkB,sBAAsB;AAAA,IACnD,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,MAAM,CAAC,aAAa,aAAa,SAAS,OAAO;AAAA,IACjD,UAAU;AAAA,IACV,MAAM;AAAA,IACN,iBAAiB;AAAA,EACnB;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA,mBAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,UAAU;AACtD;AAEA,IAAO,iBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,18 @@
1
+ import { isValidDateRangePreset } from "@open-mercato/ui/backend/date-range";
2
+ const DEFAULT_SETTINGS = {
3
+ dateRange: "this_month",
4
+ showComparison: true
5
+ };
6
+ function hydrateSettings(raw) {
7
+ if (!raw || typeof raw !== "object") return { ...DEFAULT_SETTINGS };
8
+ const obj = raw;
9
+ return {
10
+ dateRange: isValidDateRangePreset(obj.dateRange) ? obj.dateRange : DEFAULT_SETTINGS.dateRange,
11
+ showComparison: typeof obj.showComparison === "boolean" ? obj.showComparison : DEFAULT_SETTINGS.showComparison
12
+ };
13
+ }
14
+ export {
15
+ DEFAULT_SETTINGS,
16
+ hydrateSettings
17
+ };
18
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/dashboards/widgets/dashboard/revenue-kpi/config.ts"],
4
+ "sourcesContent": ["import { type DateRangePreset, isValidDateRangePreset } from '@open-mercato/ui/backend/date-range'\n\nexport type RevenueKpiSettings = {\n dateRange: DateRangePreset\n showComparison: boolean\n}\n\nexport const DEFAULT_SETTINGS: RevenueKpiSettings = {\n dateRange: 'this_month',\n showComparison: true,\n}\n\nexport function hydrateSettings(raw: unknown): RevenueKpiSettings {\n if (!raw || typeof raw !== 'object') return { ...DEFAULT_SETTINGS }\n const obj = raw as Record<string, unknown>\n return {\n dateRange: isValidDateRangePreset(obj.dateRange) ? obj.dateRange : DEFAULT_SETTINGS.dateRange,\n showComparison: typeof obj.showComparison === 'boolean' ? obj.showComparison : DEFAULT_SETTINGS.showComparison,\n }\n}\n"],
5
+ "mappings": "AAAA,SAA+B,8BAA8B;AAOtD,MAAM,mBAAuC;AAAA,EAClD,WAAW;AAAA,EACX,gBAAgB;AAClB;AAEO,SAAS,gBAAgB,KAAkC;AAChE,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO,EAAE,GAAG,iBAAiB;AAClE,QAAM,MAAM;AACZ,SAAO;AAAA,IACL,WAAW,uBAAuB,IAAI,SAAS,IAAI,IAAI,YAAY,iBAAiB;AAAA,IACpF,gBAAgB,OAAO,IAAI,mBAAmB,YAAY,IAAI,iBAAiB,iBAAiB;AAAA,EAClG;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,128 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
5
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
6
+ import { KpiCard } from "@open-mercato/ui/backend/charts";
7
+ import {
8
+ DateRangeSelect,
9
+ InlineDateRangeSelect,
10
+ getComparisonLabelKey
11
+ } from "@open-mercato/ui/backend/date-range";
12
+ import { DEFAULT_SETTINGS, hydrateSettings } from "./config.js";
13
+ import { formatCurrency } from "../../../lib/formatters.js";
14
+ async function fetchRevenueData(settings) {
15
+ const body = {
16
+ entityType: "sales:orders",
17
+ metric: {
18
+ field: "grandTotalGrossAmount",
19
+ aggregate: "sum"
20
+ },
21
+ dateRange: {
22
+ field: "placedAt",
23
+ preset: settings.dateRange
24
+ },
25
+ comparison: settings.showComparison ? { type: "previous_period" } : void 0
26
+ };
27
+ const call = await apiCall("/api/dashboards/widgets/data", {
28
+ method: "POST",
29
+ headers: { "Content-Type": "application/json" },
30
+ body: JSON.stringify(body)
31
+ });
32
+ if (!call.ok) {
33
+ const errorMsg = call.result?.error;
34
+ throw new Error(typeof errorMsg === "string" ? errorMsg : "Failed to fetch revenue data");
35
+ }
36
+ return call.result;
37
+ }
38
+ const RevenueKpiWidget = ({
39
+ mode,
40
+ settings = DEFAULT_SETTINGS,
41
+ onSettingsChange,
42
+ refreshToken,
43
+ onRefreshStateChange
44
+ }) => {
45
+ const t = useT();
46
+ const hydrated = React.useMemo(() => hydrateSettings(settings), [settings]);
47
+ const [value, setValue] = React.useState(null);
48
+ const [trend, setTrend] = React.useState(void 0);
49
+ const [loading, setLoading] = React.useState(true);
50
+ const [error, setError] = React.useState(null);
51
+ const refresh = React.useCallback(async () => {
52
+ onRefreshStateChange?.(true);
53
+ setLoading(true);
54
+ setError(null);
55
+ try {
56
+ const data = await fetchRevenueData(hydrated);
57
+ setValue(data.value);
58
+ if (data.comparison) {
59
+ setTrend({
60
+ value: data.comparison.change,
61
+ direction: data.comparison.direction
62
+ });
63
+ } else {
64
+ setTrend(void 0);
65
+ }
66
+ } catch (err) {
67
+ console.error("Failed to load revenue KPI data", err);
68
+ setError(t("dashboards.analytics.widgets.revenueKpi.error", "Failed to load data"));
69
+ } finally {
70
+ setLoading(false);
71
+ onRefreshStateChange?.(false);
72
+ }
73
+ }, [hydrated, onRefreshStateChange, t]);
74
+ React.useEffect(() => {
75
+ refresh().catch(() => {
76
+ });
77
+ }, [refresh, refreshToken]);
78
+ if (mode === "settings") {
79
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4 text-sm", children: [
80
+ /* @__PURE__ */ jsx(
81
+ DateRangeSelect,
82
+ {
83
+ id: "revenue-kpi-date-range",
84
+ label: t("dashboards.analytics.settings.dateRange", "Date Range"),
85
+ value: hydrated.dateRange,
86
+ onChange: (dateRange) => onSettingsChange({ ...hydrated, dateRange })
87
+ }
88
+ ),
89
+ /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
90
+ /* @__PURE__ */ jsx(
91
+ "input",
92
+ {
93
+ type: "checkbox",
94
+ checked: hydrated.showComparison,
95
+ onChange: (e) => onSettingsChange({ ...hydrated, showComparison: e.target.checked }),
96
+ className: "h-4 w-4 rounded border focus:ring-primary"
97
+ }
98
+ ),
99
+ t("dashboards.analytics.settings.showComparison", "Show comparison")
100
+ ] }) })
101
+ ] });
102
+ }
103
+ const comparisonLabelInfo = getComparisonLabelKey(hydrated.dateRange);
104
+ const comparisonLabel = hydrated.showComparison ? t(comparisonLabelInfo.key, comparisonLabelInfo.fallback) : void 0;
105
+ return /* @__PURE__ */ jsx(
106
+ KpiCard,
107
+ {
108
+ value,
109
+ trend,
110
+ comparisonLabel,
111
+ loading,
112
+ error,
113
+ formatValue: formatCurrency,
114
+ headerAction: /* @__PURE__ */ jsx(
115
+ InlineDateRangeSelect,
116
+ {
117
+ value: hydrated.dateRange,
118
+ onChange: (dateRange) => onSettingsChange({ ...hydrated, dateRange })
119
+ }
120
+ )
121
+ }
122
+ );
123
+ };
124
+ var widget_client_default = RevenueKpiWidget;
125
+ export {
126
+ widget_client_default as default
127
+ };
128
+ //# sourceMappingURL=widget.client.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { DashboardWidgetComponentProps } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { KpiCard, type KpiTrend } from '@open-mercato/ui/backend/charts'\nimport {\n DateRangeSelect,\n InlineDateRangeSelect,\n type DateRangePreset,\n getComparisonLabelKey,\n} from '@open-mercato/ui/backend/date-range'\nimport { DEFAULT_SETTINGS, hydrateSettings, type RevenueKpiSettings } from './config'\nimport type { WidgetDataResponse } from '../../../services/widgetDataService'\nimport { formatCurrency } from '../../../lib/formatters'\n\nasync function fetchRevenueData(settings: RevenueKpiSettings): Promise<WidgetDataResponse> {\n const body = {\n entityType: 'sales:orders',\n metric: {\n field: 'grandTotalGrossAmount',\n aggregate: 'sum',\n },\n dateRange: {\n field: 'placedAt',\n preset: settings.dateRange,\n },\n comparison: settings.showComparison ? { type: 'previous_period' } : undefined,\n }\n\n const call = await apiCall<WidgetDataResponse>('/api/dashboards/widgets/data', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n\n if (!call.ok) {\n const errorMsg = (call.result as Record<string, unknown>)?.error\n throw new Error(typeof errorMsg === 'string' ? errorMsg : 'Failed to fetch revenue data')\n }\n\n return call.result as WidgetDataResponse\n}\n\nconst RevenueKpiWidget: React.FC<DashboardWidgetComponentProps<RevenueKpiSettings>> = ({\n mode,\n settings = DEFAULT_SETTINGS,\n onSettingsChange,\n refreshToken,\n onRefreshStateChange,\n}) => {\n const t = useT()\n const hydrated = React.useMemo(() => hydrateSettings(settings), [settings])\n const [value, setValue] = React.useState<number | null>(null)\n const [trend, setTrend] = React.useState<KpiTrend | undefined>(undefined)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n\n const refresh = React.useCallback(async () => {\n onRefreshStateChange?.(true)\n setLoading(true)\n setError(null)\n try {\n const data = await fetchRevenueData(hydrated)\n setValue(data.value)\n if (data.comparison) {\n setTrend({\n value: data.comparison.change,\n direction: data.comparison.direction,\n })\n } else {\n setTrend(undefined)\n }\n } catch (err) {\n console.error('Failed to load revenue KPI data', err)\n setError(t('dashboards.analytics.widgets.revenueKpi.error', 'Failed to load data'))\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 <DateRangeSelect\n id=\"revenue-kpi-date-range\"\n label={t('dashboards.analytics.settings.dateRange', 'Date Range')}\n value={hydrated.dateRange}\n onChange={(dateRange: DateRangePreset) => onSettingsChange({ ...hydrated, dateRange })}\n />\n <div className=\"space-y-1.5\">\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={hydrated.showComparison}\n onChange={(e) => onSettingsChange({ ...hydrated, showComparison: e.target.checked })}\n className=\"h-4 w-4 rounded border focus:ring-primary\"\n />\n {t('dashboards.analytics.settings.showComparison', 'Show comparison')}\n </label>\n </div>\n </div>\n )\n }\n\n const comparisonLabelInfo = getComparisonLabelKey(hydrated.dateRange)\n const comparisonLabel = hydrated.showComparison\n ? t(comparisonLabelInfo.key, comparisonLabelInfo.fallback)\n : undefined\n\n return (\n <KpiCard\n value={value}\n trend={trend}\n comparisonLabel={comparisonLabel}\n loading={loading}\n error={error}\n formatValue={formatCurrency}\n headerAction={\n <InlineDateRangeSelect\n value={hydrated.dateRange}\n onChange={(dateRange) => onSettingsChange({ ...hydrated, dateRange })}\n />\n }\n />\n )\n}\n\nexport default RevenueKpiWidget\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,25 @@
1
+ import RevenueKpiWidget from "./widget.client.js";
2
+ import { DEFAULT_SETTINGS, hydrateSettings } from "./config.js";
3
+ const widget = {
4
+ metadata: {
5
+ id: "dashboards.analytics.revenueKpi",
6
+ title: "Revenue",
7
+ description: "Total revenue with period comparison",
8
+ features: ["analytics.view", "sales.orders.view"],
9
+ defaultSize: "sm",
10
+ defaultEnabled: false,
11
+ defaultSettings: DEFAULT_SETTINGS,
12
+ tags: ["analytics", "sales", "kpi"],
13
+ category: "analytics",
14
+ icon: "dollar-sign",
15
+ supportsRefresh: true
16
+ },
17
+ Widget: RevenueKpiWidget,
18
+ hydrateSettings,
19
+ dehydrateSettings: (s) => ({ dateRange: s.dateRange, showComparison: s.showComparison })
20
+ };
21
+ var widget_default = widget;
22
+ export {
23
+ widget_default as default
24
+ };
25
+ //# sourceMappingURL=widget.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.ts"],
4
+ "sourcesContent": ["import type { DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'\nimport RevenueKpiWidget from './widget.client'\nimport { DEFAULT_SETTINGS, hydrateSettings, type RevenueKpiSettings } from './config'\n\nconst widget: DashboardWidgetModule<RevenueKpiSettings> = {\n metadata: {\n id: 'dashboards.analytics.revenueKpi',\n title: 'Revenue',\n description: 'Total revenue with period comparison',\n features: ['analytics.view', 'sales.orders.view'],\n defaultSize: 'sm',\n defaultEnabled: false,\n defaultSettings: DEFAULT_SETTINGS,\n tags: ['analytics', 'sales', 'kpi'],\n category: 'analytics',\n icon: 'dollar-sign',\n supportsRefresh: true,\n },\n Widget: RevenueKpiWidget,\n hydrateSettings,\n dehydrateSettings: (s) => ({ dateRange: s.dateRange, showComparison: s.showComparison }),\n}\n\nexport default widget\n"],
5
+ "mappings": "AACA,OAAO,sBAAsB;AAC7B,SAAS,kBAAkB,uBAAgD;AAE3E,MAAM,SAAoD;AAAA,EACxD,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU,CAAC,kBAAkB,mBAAmB;AAAA,IAChD,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,MAAM,CAAC,aAAa,SAAS,KAAK;AAAA,IAClC,UAAU;AAAA,IACV,MAAM;AAAA,IACN,iBAAiB;AAAA,EACnB;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA,mBAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,WAAW,gBAAgB,EAAE,eAAe;AACxF;AAEA,IAAO,iBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,21 @@
1
+ import { isValidDateRangePreset } from "@open-mercato/ui/backend/date-range";
2
+ import { isValidGranularity } from "../../../lib/aggregations.js";
3
+ const DEFAULT_SETTINGS = {
4
+ dateRange: "last_30_days",
5
+ granularity: "day",
6
+ showArea: true
7
+ };
8
+ function hydrateSettings(raw) {
9
+ if (!raw || typeof raw !== "object") return { ...DEFAULT_SETTINGS };
10
+ const obj = raw;
11
+ return {
12
+ dateRange: isValidDateRangePreset(obj.dateRange) ? obj.dateRange : DEFAULT_SETTINGS.dateRange,
13
+ granularity: isValidGranularity(obj.granularity) ? obj.granularity : DEFAULT_SETTINGS.granularity,
14
+ showArea: typeof obj.showArea === "boolean" ? obj.showArea : DEFAULT_SETTINGS.showArea
15
+ };
16
+ }
17
+ export {
18
+ DEFAULT_SETTINGS,
19
+ hydrateSettings
20
+ };
21
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/dashboards/widgets/dashboard/revenue-trend/config.ts"],
4
+ "sourcesContent": ["import { type DateRangePreset, isValidDateRangePreset } from '@open-mercato/ui/backend/date-range'\nimport { type DateGranularity, isValidGranularity } from '../../../lib/aggregations'\n\nexport type RevenueTrendSettings = {\n dateRange: DateRangePreset\n granularity: DateGranularity\n showArea: boolean\n}\n\nexport const DEFAULT_SETTINGS: RevenueTrendSettings = {\n dateRange: 'last_30_days',\n granularity: 'day',\n showArea: true,\n}\n\nexport function hydrateSettings(raw: unknown): RevenueTrendSettings {\n if (!raw || typeof raw !== 'object') return { ...DEFAULT_SETTINGS }\n const obj = raw as Record<string, unknown>\n return {\n dateRange: isValidDateRangePreset(obj.dateRange) ? obj.dateRange : DEFAULT_SETTINGS.dateRange,\n granularity: isValidGranularity(obj.granularity) ? obj.granularity : DEFAULT_SETTINGS.granularity,\n showArea: typeof obj.showArea === 'boolean' ? obj.showArea : DEFAULT_SETTINGS.showArea,\n }\n}\n"],
5
+ "mappings": "AAAA,SAA+B,8BAA8B;AAC7D,SAA+B,0BAA0B;AAQlD,MAAM,mBAAyC;AAAA,EACpD,WAAW;AAAA,EACX,aAAa;AAAA,EACb,UAAU;AACZ;AAEO,SAAS,gBAAgB,KAAoC;AAClE,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO,EAAE,GAAG,iBAAiB;AAClE,QAAM,MAAM;AACZ,SAAO;AAAA,IACL,WAAW,uBAAuB,IAAI,SAAS,IAAI,IAAI,YAAY,iBAAiB;AAAA,IACpF,aAAa,mBAAmB,IAAI,WAAW,IAAI,IAAI,cAAc,iBAAiB;AAAA,IACtF,UAAU,OAAO,IAAI,aAAa,YAAY,IAAI,WAAW,iBAAiB;AAAA,EAChF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,211 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
5
+ import { useT, useLocale } from "@open-mercato/shared/lib/i18n/context";
6
+ import { LineChart } from "@open-mercato/ui/backend/charts";
7
+ import {
8
+ DateRangeSelect,
9
+ InlineDateRangeSelect
10
+ } from "@open-mercato/ui/backend/date-range";
11
+ import { DEFAULT_SETTINGS, hydrateSettings } from "./config.js";
12
+ import { formatCurrencyCompact } from "../../../lib/formatters.js";
13
+ async function fetchRevenueTrendData(settings) {
14
+ const body = {
15
+ entityType: "sales:orders",
16
+ metric: {
17
+ field: "grandTotalGrossAmount",
18
+ aggregate: "sum"
19
+ },
20
+ groupBy: {
21
+ field: "placedAt",
22
+ granularity: settings.granularity
23
+ },
24
+ dateRange: {
25
+ field: "placedAt",
26
+ preset: settings.dateRange
27
+ }
28
+ };
29
+ const call = await apiCall("/api/dashboards/widgets/data", {
30
+ method: "POST",
31
+ headers: { "Content-Type": "application/json" },
32
+ body: JSON.stringify(body)
33
+ });
34
+ if (!call.ok) {
35
+ const errorMsg = call.result?.error;
36
+ throw new Error(typeof errorMsg === "string" ? errorMsg : "Failed to fetch revenue trend data");
37
+ }
38
+ return call.result;
39
+ }
40
+ function formatDate(dateStr, granularity, locale) {
41
+ if (!dateStr) return "--";
42
+ try {
43
+ const date = new Date(dateStr);
44
+ const localeStr = locale ?? void 0;
45
+ switch (granularity) {
46
+ case "day":
47
+ case "week":
48
+ return date.toLocaleDateString(localeStr, { month: "short", day: "numeric" });
49
+ case "month":
50
+ return date.toLocaleDateString(localeStr, { month: "short", year: "numeric" });
51
+ case "quarter": {
52
+ const quarter = Math.floor(date.getMonth() / 3) + 1;
53
+ return `Q${quarter} ${date.getFullYear()}`;
54
+ }
55
+ case "year":
56
+ return date.toLocaleDateString(localeStr, { year: "numeric" });
57
+ default:
58
+ return date.toLocaleDateString(localeStr, { month: "short", day: "numeric" });
59
+ }
60
+ } catch {
61
+ return String(dateStr);
62
+ }
63
+ }
64
+ const GRANULARITY_OPTIONS = [
65
+ { value: "day", labelKey: "dashboards.analytics.granularity.day" },
66
+ { value: "week", labelKey: "dashboards.analytics.granularity.week" },
67
+ { value: "month", labelKey: "dashboards.analytics.granularity.month" },
68
+ { value: "quarter", labelKey: "dashboards.analytics.granularity.quarter" },
69
+ { value: "year", labelKey: "dashboards.analytics.granularity.year" }
70
+ ];
71
+ function getAutoGranularity(dateRange) {
72
+ switch (dateRange) {
73
+ case "today":
74
+ case "yesterday":
75
+ case "last_7_days":
76
+ return "day";
77
+ case "this_week":
78
+ case "last_week":
79
+ case "last_30_days":
80
+ return "day";
81
+ case "this_month":
82
+ case "last_month":
83
+ case "last_90_days":
84
+ return "week";
85
+ case "this_quarter":
86
+ case "last_quarter":
87
+ return "week";
88
+ case "this_year":
89
+ case "last_year":
90
+ return "month";
91
+ default:
92
+ return "day";
93
+ }
94
+ }
95
+ const RevenueTrendWidget = ({
96
+ mode,
97
+ settings = DEFAULT_SETTINGS,
98
+ onSettingsChange,
99
+ refreshToken,
100
+ onRefreshStateChange
101
+ }) => {
102
+ const t = useT();
103
+ const locale = useLocale();
104
+ const hydrated = React.useMemo(() => hydrateSettings(settings), [settings]);
105
+ const [data, setData] = React.useState([]);
106
+ const [loading, setLoading] = React.useState(true);
107
+ const [error, setError] = React.useState(null);
108
+ const refresh = React.useCallback(async () => {
109
+ onRefreshStateChange?.(true);
110
+ setLoading(true);
111
+ setError(null);
112
+ try {
113
+ const result = await fetchRevenueTrendData(hydrated);
114
+ const sortedData = [...result.data].sort((a, b) => {
115
+ const aTime = new Date(a.groupKey || 0).getTime();
116
+ const bTime = new Date(b.groupKey || 0).getTime();
117
+ return aTime - bTime;
118
+ });
119
+ const chartData = sortedData.map((item) => ({
120
+ date: formatDate(item.groupKey, hydrated.granularity, locale),
121
+ Revenue: item.value ?? 0
122
+ }));
123
+ setData(chartData);
124
+ } catch (err) {
125
+ console.error("Failed to load revenue trend data", err);
126
+ setError(t("dashboards.analytics.widgets.revenueTrend.error", "Failed to load data"));
127
+ } finally {
128
+ setLoading(false);
129
+ onRefreshStateChange?.(false);
130
+ }
131
+ }, [hydrated, locale, onRefreshStateChange, t]);
132
+ React.useEffect(() => {
133
+ refresh().catch(() => {
134
+ });
135
+ }, [refresh, refreshToken]);
136
+ if (mode === "settings") {
137
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4 text-sm", children: [
138
+ /* @__PURE__ */ jsx(
139
+ DateRangeSelect,
140
+ {
141
+ id: "revenue-trend-date-range",
142
+ label: t("dashboards.analytics.settings.dateRange", "Date Range"),
143
+ value: hydrated.dateRange,
144
+ onChange: (dateRange) => onSettingsChange({ ...hydrated, dateRange })
145
+ }
146
+ ),
147
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
148
+ /* @__PURE__ */ jsx(
149
+ "label",
150
+ {
151
+ htmlFor: "revenue-trend-granularity",
152
+ className: "text-xs font-semibold uppercase text-muted-foreground",
153
+ children: t("dashboards.analytics.settings.granularity", "Granularity")
154
+ }
155
+ ),
156
+ /* @__PURE__ */ jsx(
157
+ "select",
158
+ {
159
+ id: "revenue-trend-granularity",
160
+ className: "w-full rounded-md border bg-background px-2 py-1 text-sm text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary",
161
+ value: hydrated.granularity,
162
+ onChange: (e) => onSettingsChange({ ...hydrated, granularity: e.target.value }),
163
+ children: GRANULARITY_OPTIONS.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: t(opt.labelKey, opt.value) }, opt.value))
164
+ }
165
+ )
166
+ ] }),
167
+ /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
168
+ /* @__PURE__ */ jsx(
169
+ "input",
170
+ {
171
+ type: "checkbox",
172
+ checked: hydrated.showArea,
173
+ onChange: (e) => onSettingsChange({ ...hydrated, showArea: e.target.checked }),
174
+ className: "h-4 w-4 rounded border focus:ring-primary"
175
+ }
176
+ ),
177
+ t("dashboards.analytics.settings.showArea", "Show area fill")
178
+ ] }) })
179
+ ] });
180
+ }
181
+ const effectiveGranularity = hydrated.granularity === "day" ? getAutoGranularity(hydrated.dateRange) : hydrated.granularity;
182
+ return /* @__PURE__ */ jsxs("div", { children: [
183
+ /* @__PURE__ */ jsx("div", { className: "mb-2 flex justify-end", children: /* @__PURE__ */ jsx(
184
+ InlineDateRangeSelect,
185
+ {
186
+ value: hydrated.dateRange,
187
+ onChange: (dateRange) => onSettingsChange({ ...hydrated, dateRange, granularity: getAutoGranularity(dateRange) })
188
+ }
189
+ ) }),
190
+ /* @__PURE__ */ jsx(
191
+ LineChart,
192
+ {
193
+ data,
194
+ index: "date",
195
+ categories: ["Revenue"],
196
+ categoryLabels: { Revenue: t("dashboards.analytics.widgets.topCustomers.column.revenue", "Revenue") },
197
+ loading,
198
+ error,
199
+ showArea: hydrated.showArea,
200
+ valueFormatter: formatCurrencyCompact,
201
+ colors: ["blue"],
202
+ emptyMessage: t("dashboards.analytics.widgets.revenueTrend.empty", "No revenue data for this period")
203
+ }
204
+ )
205
+ ] });
206
+ };
207
+ var widget_client_default = RevenueTrendWidget;
208
+ export {
209
+ widget_client_default as default
210
+ };
211
+ //# sourceMappingURL=widget.client.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport type { DashboardWidgetComponentProps } from '@open-mercato/shared/modules/dashboard/widgets'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT, useLocale } from '@open-mercato/shared/lib/i18n/context'\nimport { LineChart, type LineChartDataItem } from '@open-mercato/ui/backend/charts'\nimport {\n DateRangeSelect,\n InlineDateRangeSelect,\n type DateRangePreset,\n} from '@open-mercato/ui/backend/date-range'\nimport type { DateGranularity } from '@open-mercato/shared/modules/analytics'\nimport { DEFAULT_SETTINGS, hydrateSettings, type RevenueTrendSettings } from './config'\nimport type { WidgetDataResponse } from '../../../services/widgetDataService'\nimport { formatCurrencyCompact } from '../../../lib/formatters'\n\nasync function fetchRevenueTrendData(settings: RevenueTrendSettings): Promise<WidgetDataResponse> {\n const body = {\n entityType: 'sales:orders',\n metric: {\n field: 'grandTotalGrossAmount',\n aggregate: 'sum',\n },\n groupBy: {\n field: 'placedAt',\n granularity: settings.granularity,\n },\n dateRange: {\n field: 'placedAt',\n preset: settings.dateRange,\n },\n }\n\n const call = await apiCall<WidgetDataResponse>('/api/dashboards/widgets/data', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n\n if (!call.ok) {\n const errorMsg = (call.result as Record<string, unknown>)?.error\n throw new Error(typeof errorMsg === 'string' ? errorMsg : 'Failed to fetch revenue trend data')\n }\n\n return call.result as WidgetDataResponse\n}\n\nfunction formatDate(dateStr: string | null, granularity: DateGranularity, locale?: string): string {\n if (!dateStr) return '--'\n try {\n const date = new Date(dateStr)\n const localeStr = locale ?? undefined\n switch (granularity) {\n case 'day':\n case 'week':\n return date.toLocaleDateString(localeStr, { month: 'short', day: 'numeric' })\n case 'month':\n return date.toLocaleDateString(localeStr, { month: 'short', year: 'numeric' })\n case 'quarter': {\n const quarter = Math.floor(date.getMonth() / 3) + 1\n return `Q${quarter} ${date.getFullYear()}`\n }\n case 'year':\n return date.toLocaleDateString(localeStr, { year: 'numeric' })\n default:\n return date.toLocaleDateString(localeStr, { month: 'short', day: 'numeric' })\n }\n } catch {\n return String(dateStr)\n }\n}\n\nconst GRANULARITY_OPTIONS: { value: DateGranularity; labelKey: string }[] = [\n { value: 'day', labelKey: 'dashboards.analytics.granularity.day' },\n { value: 'week', labelKey: 'dashboards.analytics.granularity.week' },\n { value: 'month', labelKey: 'dashboards.analytics.granularity.month' },\n { value: 'quarter', labelKey: 'dashboards.analytics.granularity.quarter' },\n { value: 'year', labelKey: 'dashboards.analytics.granularity.year' },\n]\n\nfunction getAutoGranularity(dateRange: DateRangePreset): DateGranularity {\n switch (dateRange) {\n case 'today':\n case 'yesterday':\n case 'last_7_days':\n return 'day'\n case 'this_week':\n case 'last_week':\n case 'last_30_days':\n return 'day'\n case 'this_month':\n case 'last_month':\n case 'last_90_days':\n return 'week'\n case 'this_quarter':\n case 'last_quarter':\n return 'week'\n case 'this_year':\n case 'last_year':\n return 'month'\n default:\n return 'day'\n }\n}\n\nconst RevenueTrendWidget: React.FC<DashboardWidgetComponentProps<RevenueTrendSettings>> = ({\n mode,\n settings = DEFAULT_SETTINGS,\n onSettingsChange,\n refreshToken,\n onRefreshStateChange,\n}) => {\n const t = useT()\n const locale = useLocale()\n const hydrated = React.useMemo(() => hydrateSettings(settings), [settings])\n const [data, setData] = React.useState<LineChartDataItem[]>([])\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n\n const refresh = React.useCallback(async () => {\n onRefreshStateChange?.(true)\n setLoading(true)\n setError(null)\n try {\n const result = await fetchRevenueTrendData(hydrated)\n const sortedData = [...result.data].sort((a, b) => {\n const aTime = new Date(a.groupKey as string || 0).getTime()\n const bTime = new Date(b.groupKey as string || 0).getTime()\n return aTime - bTime\n })\n const chartData = sortedData.map((item) => ({\n date: formatDate(item.groupKey as string | null, hydrated.granularity, locale),\n Revenue: item.value ?? 0,\n }))\n setData(chartData)\n } catch (err) {\n console.error('Failed to load revenue trend data', err)\n setError(t('dashboards.analytics.widgets.revenueTrend.error', 'Failed to load data'))\n } finally {\n setLoading(false)\n onRefreshStateChange?.(false)\n }\n }, [hydrated, locale, 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 <DateRangeSelect\n id=\"revenue-trend-date-range\"\n label={t('dashboards.analytics.settings.dateRange', 'Date Range')}\n value={hydrated.dateRange}\n onChange={(dateRange: DateRangePreset) => onSettingsChange({ ...hydrated, dateRange })}\n />\n <div className=\"space-y-1.5\">\n <label\n htmlFor=\"revenue-trend-granularity\"\n className=\"text-xs font-semibold uppercase text-muted-foreground\"\n >\n {t('dashboards.analytics.settings.granularity', 'Granularity')}\n </label>\n <select\n id=\"revenue-trend-granularity\"\n className=\"w-full rounded-md border bg-background px-2 py-1 text-sm text-foreground focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary\"\n value={hydrated.granularity}\n onChange={(e) => onSettingsChange({ ...hydrated, granularity: e.target.value as DateGranularity })}\n >\n {GRANULARITY_OPTIONS.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {t(opt.labelKey, opt.value)}\n </option>\n ))}\n </select>\n </div>\n <div className=\"space-y-1.5\">\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={hydrated.showArea}\n onChange={(e) => onSettingsChange({ ...hydrated, showArea: e.target.checked })}\n className=\"h-4 w-4 rounded border focus:ring-primary\"\n />\n {t('dashboards.analytics.settings.showArea', 'Show area fill')}\n </label>\n </div>\n </div>\n )\n }\n\n const effectiveGranularity = hydrated.granularity === 'day' ? getAutoGranularity(hydrated.dateRange) : hydrated.granularity\n\n return (\n <div>\n <div className=\"mb-2 flex justify-end\">\n <InlineDateRangeSelect\n value={hydrated.dateRange}\n onChange={(dateRange) => onSettingsChange({ ...hydrated, dateRange, granularity: getAutoGranularity(dateRange) })}\n />\n </div>\n <LineChart\n data={data}\n index=\"date\"\n categories={['Revenue']}\n categoryLabels={{ Revenue: t('dashboards.analytics.widgets.topCustomers.column.revenue', 'Revenue') }}\n loading={loading}\n error={error}\n showArea={hydrated.showArea}\n valueFormatter={formatCurrencyCompact}\n colors={['blue']}\n emptyMessage={t('dashboards.analytics.widgets.revenueTrend.empty', 'No revenue data for this period')}\n />\n </div>\n )\n}\n\nexport default RevenueTrendWidget\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,25 @@
1
+ import RevenueTrendWidget from "./widget.client.js";
2
+ import { DEFAULT_SETTINGS, hydrateSettings } from "./config.js";
3
+ const widget = {
4
+ metadata: {
5
+ id: "dashboards.analytics.revenueTrend",
6
+ title: "Revenue Trend",
7
+ description: "Revenue over time with customizable granularity",
8
+ features: ["analytics.view", "sales.orders.view"],
9
+ defaultSize: "lg",
10
+ defaultEnabled: false,
11
+ defaultSettings: DEFAULT_SETTINGS,
12
+ tags: ["analytics", "sales", "chart"],
13
+ category: "analytics",
14
+ icon: "line-chart",
15
+ supportsRefresh: true
16
+ },
17
+ Widget: RevenueTrendWidget,
18
+ hydrateSettings,
19
+ dehydrateSettings: (s) => ({ dateRange: s.dateRange, granularity: s.granularity, showArea: s.showArea })
20
+ };
21
+ var widget_default = widget;
22
+ export {
23
+ widget_default as default
24
+ };
25
+ //# sourceMappingURL=widget.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/dashboards/widgets/dashboard/revenue-trend/widget.ts"],
4
+ "sourcesContent": ["import type { DashboardWidgetModule } from '@open-mercato/shared/modules/dashboard/widgets'\nimport RevenueTrendWidget from './widget.client'\nimport { DEFAULT_SETTINGS, hydrateSettings, type RevenueTrendSettings } from './config'\n\nconst widget: DashboardWidgetModule<RevenueTrendSettings> = {\n metadata: {\n id: 'dashboards.analytics.revenueTrend',\n title: 'Revenue Trend',\n description: 'Revenue over time with customizable granularity',\n features: ['analytics.view', 'sales.orders.view'],\n defaultSize: 'lg',\n defaultEnabled: false,\n defaultSettings: DEFAULT_SETTINGS,\n tags: ['analytics', 'sales', 'chart'],\n category: 'analytics',\n icon: 'line-chart',\n supportsRefresh: true,\n },\n Widget: RevenueTrendWidget,\n hydrateSettings,\n dehydrateSettings: (s) => ({ dateRange: s.dateRange, granularity: s.granularity, showArea: s.showArea }),\n}\n\nexport default widget\n"],
5
+ "mappings": "AACA,OAAO,wBAAwB;AAC/B,SAAS,kBAAkB,uBAAkD;AAE7E,MAAM,SAAsD;AAAA,EAC1D,UAAU;AAAA,IACR,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,aAAa;AAAA,IACb,UAAU,CAAC,kBAAkB,mBAAmB;AAAA,IAChD,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,MAAM,CAAC,aAAa,SAAS,OAAO;AAAA,IACpC,UAAU;AAAA,IACV,MAAM;AAAA,IACN,iBAAiB;AAAA,EACnB;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA,mBAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,WAAW,aAAa,EAAE,aAAa,UAAU,EAAE,SAAS;AACxG;AAEA,IAAO,iBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,19 @@
1
+ import { isValidDateRangePreset } from "@open-mercato/ui/backend/date-range";
2
+ const DEFAULT_SETTINGS = {
3
+ dateRange: "this_month",
4
+ limit: 10
5
+ };
6
+ function hydrateSettings(raw) {
7
+ if (!raw || typeof raw !== "object") return { ...DEFAULT_SETTINGS };
8
+ const obj = raw;
9
+ const parsedLimit = Number(obj.limit);
10
+ return {
11
+ dateRange: isValidDateRangePreset(obj.dateRange) ? obj.dateRange : DEFAULT_SETTINGS.dateRange,
12
+ limit: Number.isFinite(parsedLimit) && parsedLimit >= 1 && parsedLimit <= 20 ? Math.floor(parsedLimit) : DEFAULT_SETTINGS.limit
13
+ };
14
+ }
15
+ export {
16
+ DEFAULT_SETTINGS,
17
+ hydrateSettings
18
+ };
19
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/dashboards/widgets/dashboard/sales-by-region/config.ts"],
4
+ "sourcesContent": ["import { type DateRangePreset, isValidDateRangePreset } from '@open-mercato/ui/backend/date-range'\n\nexport type SalesByRegionSettings = {\n dateRange: DateRangePreset\n limit: number\n}\n\nexport const DEFAULT_SETTINGS: SalesByRegionSettings = {\n dateRange: 'this_month',\n limit: 10,\n}\n\nexport function hydrateSettings(raw: unknown): SalesByRegionSettings {\n if (!raw || typeof raw !== 'object') return { ...DEFAULT_SETTINGS }\n const obj = raw as Record<string, unknown>\n const parsedLimit = Number(obj.limit)\n return {\n dateRange: isValidDateRangePreset(obj.dateRange) ? obj.dateRange : DEFAULT_SETTINGS.dateRange,\n limit: Number.isFinite(parsedLimit) && parsedLimit >= 1 && parsedLimit <= 20 ? Math.floor(parsedLimit) : DEFAULT_SETTINGS.limit,\n }\n}\n"],
5
+ "mappings": "AAAA,SAA+B,8BAA8B;AAOtD,MAAM,mBAA0C;AAAA,EACrD,WAAW;AAAA,EACX,OAAO;AACT;AAEO,SAAS,gBAAgB,KAAqC;AACnE,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO,EAAE,GAAG,iBAAiB;AAClE,QAAM,MAAM;AACZ,QAAM,cAAc,OAAO,IAAI,KAAK;AACpC,SAAO;AAAA,IACL,WAAW,uBAAuB,IAAI,SAAS,IAAI,IAAI,YAAY,iBAAiB;AAAA,IACpF,OAAO,OAAO,SAAS,WAAW,KAAK,eAAe,KAAK,eAAe,KAAK,KAAK,MAAM,WAAW,IAAI,iBAAiB;AAAA,EAC5H;AACF;",
6
+ "names": []
7
+ }