@burdenoff/microfe-bigconsole 2026.925.2 → 2026.928.2
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/bigconsole/components/embed/EmbedWidgetRenderer.js +43 -13
- package/dist/bigconsole/components/embed/EmbedWidgetRenderer.js.map +1 -1
- package/dist/bigconsole/components/embed/renderer-manifest.js +14 -47
- package/dist/bigconsole/components/embed/renderer-manifest.js.map +1 -1
- package/dist/bigconsole/components/embed/widgetShim.js +28 -0
- package/dist/bigconsole/components/embed/widgetShim.js.map +1 -0
- package/dist/bigconsole/components/embed/widgets/CalendarEmbed.js +38 -0
- package/dist/bigconsole/components/embed/widgets/CalendarEmbed.js.map +1 -0
- package/dist/bigconsole/components/embed/widgets/ChartEmbed.js +225 -0
- package/dist/bigconsole/components/embed/widgets/ChartEmbed.js.map +1 -0
- package/dist/bigconsole/components/embed/widgets/EmbedFrame.js +18 -0
- package/dist/bigconsole/components/embed/widgets/EmbedFrame.js.map +1 -0
- package/dist/bigconsole/components/embed/widgets/FormEmbed.js +147 -0
- package/dist/bigconsole/components/embed/widgets/FormEmbed.js.map +1 -0
- package/dist/bigconsole/components/embed/widgets/GaugeEmbed.js +44 -0
- package/dist/bigconsole/components/embed/widgets/GaugeEmbed.js.map +1 -0
- package/dist/bigconsole/components/embed/widgets/HeatmapEmbed.js +35 -0
- package/dist/bigconsole/components/embed/widgets/HeatmapEmbed.js.map +1 -0
- package/dist/bigconsole/components/embed/widgets/KanbanEmbed.js +50 -0
- package/dist/bigconsole/components/embed/widgets/KanbanEmbed.js.map +1 -0
- package/dist/bigconsole/components/embed/widgets/PivotEmbed.js +146 -0
- package/dist/bigconsole/components/embed/widgets/PivotEmbed.js.map +1 -0
- package/dist/bigconsole/components/embed/widgets/ProgressEmbed.js +30 -0
- package/dist/bigconsole/components/embed/widgets/ProgressEmbed.js.map +1 -0
- package/dist/bigconsole/components/embed/widgets/RetentionEmbed.js +41 -0
- package/dist/bigconsole/components/embed/widgets/RetentionEmbed.js.map +1 -0
- package/dist/bigconsole/components/embed/widgets/TimelineEmbed.js +40 -0
- package/dist/bigconsole/components/embed/widgets/TimelineEmbed.js.map +1 -0
- package/dist/bigconsole/components/embed/widgets/shared.js +13 -1
- package/dist/bigconsole/components/embed/widgets/shared.js.map +1 -1
- package/dist/bigconsole/components/widgets/calendar/CalendarView.js +152 -0
- package/dist/bigconsole/components/widgets/calendar/CalendarView.js.map +1 -0
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js +10 -147
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/chart/ChartWidget.js +19 -19
- package/dist/bigconsole/components/widgets/chart/index.js +8 -8
- package/dist/bigconsole/components/widgets/index.js +5 -5
- package/dist/bigconsole/components/widgets/kanban/KanbanView.js +192 -0
- package/dist/bigconsole/components/widgets/kanban/KanbanView.js.map +1 -0
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js +10 -187
- package/dist/bigconsole/components/widgets/kanban/KanbanWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js +20 -20
- package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js +23 -22
- package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/utils/translate.js +9 -0
- package/dist/bigconsole/components/widgets/utils/translate.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { memo as e, useMemo as t } from "react";
|
|
2
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
3
|
+
//#region src/bigconsole/components/widgets/kanban/KanbanView.tsx
|
|
4
|
+
function i(e) {
|
|
5
|
+
switch (e) {
|
|
6
|
+
case "high": return "bg-status-error-bg/20 text-status-error-text";
|
|
7
|
+
case "medium": return "bg-status-warning-bg/20 text-[var(--color-status-warning-text)]";
|
|
8
|
+
case "low": return "bg-status-success-bg/20 text-status-success-text";
|
|
9
|
+
default: return "bg-bg-sunken text-text-secondary";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
var a = [
|
|
13
|
+
"status",
|
|
14
|
+
"state",
|
|
15
|
+
"column",
|
|
16
|
+
"stage",
|
|
17
|
+
"category",
|
|
18
|
+
"phase",
|
|
19
|
+
"group"
|
|
20
|
+
], o = [
|
|
21
|
+
"title",
|
|
22
|
+
"name",
|
|
23
|
+
"summary",
|
|
24
|
+
"label",
|
|
25
|
+
"identifier",
|
|
26
|
+
"id"
|
|
27
|
+
];
|
|
28
|
+
function s(e) {
|
|
29
|
+
return typeof e == "string" && e.length > 0 ? e : void 0;
|
|
30
|
+
}
|
|
31
|
+
function c(e) {
|
|
32
|
+
let t = (e) => e.filter((e) => !!e && typeof e == "object" && !Array.isArray(e));
|
|
33
|
+
if (Array.isArray(e)) return t(e);
|
|
34
|
+
if (e && typeof e == "object") {
|
|
35
|
+
let n = e;
|
|
36
|
+
for (let e of [
|
|
37
|
+
"tasks",
|
|
38
|
+
"rows",
|
|
39
|
+
"items",
|
|
40
|
+
"data",
|
|
41
|
+
"nodes",
|
|
42
|
+
"results",
|
|
43
|
+
"records"
|
|
44
|
+
]) if (Array.isArray(n[e])) return t(n[e]);
|
|
45
|
+
let r = Object.values(n).find((e) => Array.isArray(e));
|
|
46
|
+
if (Array.isArray(r)) return t(r);
|
|
47
|
+
}
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
function l(e, t) {
|
|
51
|
+
for (let n of t) if (n && n in e) return n;
|
|
52
|
+
}
|
|
53
|
+
function u(e) {
|
|
54
|
+
let t = s(e)?.toLowerCase();
|
|
55
|
+
if (t) {
|
|
56
|
+
if (t === "urgent" || t === "high") return "high";
|
|
57
|
+
if (t === "medium" || t === "normal") return "medium";
|
|
58
|
+
if (t === "low") return "low";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function d(e, t) {
|
|
62
|
+
if (e && typeof e == "object" && !Array.isArray(e) && Array.isArray(e.columns)) {
|
|
63
|
+
let t = e;
|
|
64
|
+
return {
|
|
65
|
+
columns: t.columns ?? [],
|
|
66
|
+
tasks: t.tasks ?? []
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
let n = c(e);
|
|
70
|
+
if (n.length === 0) return {
|
|
71
|
+
columns: [],
|
|
72
|
+
tasks: []
|
|
73
|
+
};
|
|
74
|
+
let r = n[0], i = l(r, [
|
|
75
|
+
s(t?.columnField),
|
|
76
|
+
s(t?.groupField),
|
|
77
|
+
s(t?.statusField),
|
|
78
|
+
...a,
|
|
79
|
+
s(t?.xAxisField),
|
|
80
|
+
s(t?.labelField)
|
|
81
|
+
]);
|
|
82
|
+
if (!i) return {
|
|
83
|
+
columns: [],
|
|
84
|
+
tasks: []
|
|
85
|
+
};
|
|
86
|
+
let d = l(r, [
|
|
87
|
+
s(t?.cardTitleField),
|
|
88
|
+
s(t?.titleField),
|
|
89
|
+
...o,
|
|
90
|
+
s(t?.labelField)
|
|
91
|
+
]), f = {}, p = [], m = n.map((e, t) => {
|
|
92
|
+
let n = e[i], r = n == null || n === "" ? "Uncategorized" : String(n);
|
|
93
|
+
r in f || (f[r] = r, p.push(r));
|
|
94
|
+
let a = e.id ?? e.identifier ?? e.key, o = Array.isArray(e.labels) ? e.labels.filter((e) => typeof e == "string") : void 0;
|
|
95
|
+
return {
|
|
96
|
+
id: a == null ? `task-${t}` : String(a),
|
|
97
|
+
title: d && e[d] != null ? String(e[d]) : `Item ${t + 1}`,
|
|
98
|
+
column: r,
|
|
99
|
+
assignee: s(e.assignee),
|
|
100
|
+
priority: u(e.priority ?? e.priorityLabel),
|
|
101
|
+
labels: o && o.length > 0 ? o : void 0
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
columns: p.map((e) => ({
|
|
106
|
+
id: e,
|
|
107
|
+
title: f[e] ?? e
|
|
108
|
+
})),
|
|
109
|
+
tasks: m
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
var f = e(function({ widget: e, data: a, onClick: o, t: s }) {
|
|
113
|
+
let c = e.config, { columns: l, tasks: u } = t(() => d(a, c), [a, c]), f = t(() => {
|
|
114
|
+
let e = {};
|
|
115
|
+
return l.forEach((t) => {
|
|
116
|
+
e[t.id] = u.filter((e) => e.column === t.id);
|
|
117
|
+
}), e;
|
|
118
|
+
}, [l, u]);
|
|
119
|
+
return l.length ? /* @__PURE__ */ n("div", {
|
|
120
|
+
className: "flex h-full p-2 gap-2 overflow-x-auto",
|
|
121
|
+
children: l.map((e) => {
|
|
122
|
+
let t = f[e.id] || [];
|
|
123
|
+
return /* @__PURE__ */ r("div", {
|
|
124
|
+
className: "flex-shrink-0 w-48 flex flex-col bg-bg-sunken/50 rounded-lg",
|
|
125
|
+
children: [/* @__PURE__ */ r("div", {
|
|
126
|
+
className: "flex items-center gap-2 p-2 border-b border-border-default",
|
|
127
|
+
children: [
|
|
128
|
+
/* @__PURE__ */ n("div", {
|
|
129
|
+
className: "w-2 h-2 rounded-full",
|
|
130
|
+
style: { backgroundColor: e.color || "var(--color-text-muted)" }
|
|
131
|
+
}),
|
|
132
|
+
/* @__PURE__ */ n("span", {
|
|
133
|
+
className: "text-xs font-medium text-text-primary truncate",
|
|
134
|
+
children: e.title
|
|
135
|
+
}),
|
|
136
|
+
/* @__PURE__ */ n("span", {
|
|
137
|
+
className: "ml-auto text-xs text-text-muted",
|
|
138
|
+
children: t.length
|
|
139
|
+
})
|
|
140
|
+
]
|
|
141
|
+
}), /* @__PURE__ */ r("div", {
|
|
142
|
+
className: "flex-1 p-2 space-y-2 overflow-y-auto",
|
|
143
|
+
children: [t.map((e) => /* @__PURE__ */ r("div", {
|
|
144
|
+
className: `
|
|
145
|
+
p-2 bg-bg-surface rounded border border-border-default
|
|
146
|
+
${o ? "cursor-pointer hover:border-action-primary-bg" : ""}
|
|
147
|
+
transition-colors
|
|
148
|
+
`,
|
|
149
|
+
onClick: () => o?.(e),
|
|
150
|
+
children: [/* @__PURE__ */ n("p", {
|
|
151
|
+
className: "text-xs font-medium text-text-primary line-clamp-2",
|
|
152
|
+
children: e.title
|
|
153
|
+
}), /* @__PURE__ */ r("div", {
|
|
154
|
+
className: "flex items-center gap-1 mt-2 flex-wrap",
|
|
155
|
+
children: [e.priority && /* @__PURE__ */ n("span", {
|
|
156
|
+
className: `px-1.5 py-0.5 text-[10px] rounded ${i(e.priority)}`,
|
|
157
|
+
children: e.priority
|
|
158
|
+
}), e.assignee && /* @__PURE__ */ n("span", {
|
|
159
|
+
className: "px-1.5 py-0.5 text-[10px] rounded bg-action-primary-bg/10 text-action-primary-text",
|
|
160
|
+
children: e.assignee
|
|
161
|
+
})]
|
|
162
|
+
})]
|
|
163
|
+
}, e.id)), t.length === 0 && /* @__PURE__ */ n("p", {
|
|
164
|
+
className: "text-[10px] text-text-muted text-center py-4",
|
|
165
|
+
children: s("bigconsole.widget.kanban.noTasks", "No tasks")
|
|
166
|
+
})]
|
|
167
|
+
})]
|
|
168
|
+
}, e.id);
|
|
169
|
+
})
|
|
170
|
+
}) : /* @__PURE__ */ n("div", {
|
|
171
|
+
className: "flex items-center justify-center h-full text-text-secondary text-sm",
|
|
172
|
+
children: /* @__PURE__ */ r("div", {
|
|
173
|
+
className: "text-center",
|
|
174
|
+
children: [/* @__PURE__ */ n("svg", {
|
|
175
|
+
className: "w-12 h-12 mx-auto mb-2 text-text-muted",
|
|
176
|
+
fill: "none",
|
|
177
|
+
stroke: "currentColor",
|
|
178
|
+
viewBox: "0 0 24 24",
|
|
179
|
+
children: /* @__PURE__ */ n("path", {
|
|
180
|
+
strokeLinecap: "round",
|
|
181
|
+
strokeLinejoin: "round",
|
|
182
|
+
strokeWidth: 1.5,
|
|
183
|
+
d: "M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2"
|
|
184
|
+
})
|
|
185
|
+
}), /* @__PURE__ */ n("p", { children: s("bigconsole.widget.kanban.emptyState", "No board data") })]
|
|
186
|
+
})
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
//#endregion
|
|
190
|
+
export { f as default };
|
|
191
|
+
|
|
192
|
+
//# sourceMappingURL=KanbanView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KanbanView.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/kanban/KanbanView.tsx"],"sourcesContent":["/**\n * KanbanWidget Component\n *\n * Simple kanban board view with columns and tasks.\n */\n\nimport { type FC, memo, useMemo } from 'react';\nimport type { Widget } from '../../../types';\nimport type { WidgetTranslateFn } from '../utils/translate';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface KanbanViewProps extends KanbanWidgetProps {\n /** Translator — the app passes useI18n().t; the public embed passes a static fallback translator. */\n t: WidgetTranslateFn;\n}\n\nexport interface KanbanWidgetProps {\n widget: Widget;\n data?: KanbanData;\n onClick?: (task: KanbanTask) => void;\n}\n\nexport interface KanbanColumn {\n id: string;\n title: string;\n color?: string;\n}\n\nexport interface KanbanTask {\n id: string;\n title: string;\n column: string;\n priority?: 'high' | 'medium' | 'low';\n assignee?: string;\n labels?: string[];\n dueDate?: string;\n}\n\nexport interface KanbanData {\n columns?: KanbanColumn[];\n tasks?: KanbanTask[];\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction getPriorityColor(priority?: string): string {\n switch (priority) {\n case 'high':\n return 'bg-status-error-bg/20 text-status-error-text';\n case 'medium':\n return 'bg-status-warning-bg/20 text-[var(--color-status-warning-text)]';\n case 'low':\n return 'bg-status-success-bg/20 text-status-success-text';\n default:\n return 'bg-bg-sunken text-text-secondary';\n }\n}\n\n// Field names we try, in order, when the widget config doesn't name one\n// explicitly. A DataSink/parser typically emits a flat array of rows (e.g. the\n// Linear \"Kanban board\" parser -> [{ id, title, status, assignee, ... }]), so\n// the board is derived from a status/column field on those rows.\nconst COLUMN_FIELD_CANDIDATES = ['status', 'state', 'column', 'stage', 'category', 'phase', 'group'];\nconst TITLE_FIELD_CANDIDATES = ['title', 'name', 'summary', 'label', 'identifier', 'id'];\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/** Coerce parser output into an array of object rows (handles common envelopes). */\nfunction asRows(data: unknown): Record<string, unknown>[] {\n const onlyObjects = (arr: unknown[]): Record<string, unknown>[] =>\n arr.filter((r): r is Record<string, unknown> => !!r && typeof r === 'object' && !Array.isArray(r));\n\n if (Array.isArray(data)) return onlyObjects(data);\n\n if (data && typeof data === 'object') {\n const obj = data as Record<string, unknown>;\n for (const key of ['tasks', 'rows', 'items', 'data', 'nodes', 'results', 'records']) {\n if (Array.isArray(obj[key])) return onlyObjects(obj[key] as unknown[]);\n }\n const firstArray = Object.values(obj).find((v) => Array.isArray(v));\n if (Array.isArray(firstArray)) return onlyObjects(firstArray);\n }\n return [];\n}\n\n/** First key that actually exists on the sample row. */\nfunction firstPresent(row: Record<string, unknown>, keys: (string | undefined)[]): string | undefined {\n for (const key of keys) {\n if (key && key in row) return key;\n }\n return undefined;\n}\n\nfunction normalizePriority(value: unknown): KanbanTask['priority'] {\n const v = asString(value)?.toLowerCase();\n if (!v) return undefined;\n if (v === 'urgent' || v === 'high') return 'high';\n if (v === 'medium' || v === 'normal') return 'medium';\n if (v === 'low') return 'low';\n return undefined;\n}\n\n/**\n * Normalize widget data into the board's { columns, tasks } shape.\n *\n * KanbanWidget previously required the exact canonical shape and rendered\n * \"No board data\" for any DataSink/parser output that was a plain array of\n * rows (which is what parsers actually emit) — BOFF-4110. This adapts a flat\n * array into columns (distinct values of a status/column field, in first-seen\n * order) plus one card per row, honoring config field mappings with fallbacks.\n */\nfunction normalizeKanban(\n data: unknown,\n config?: Record<string, unknown>\n): { columns: KanbanColumn[]; tasks: KanbanTask[] } {\n // Canonical shape passes through untouched (back-compat).\n if (data && typeof data === 'object' && !Array.isArray(data) && Array.isArray((data as KanbanData).columns)) {\n const d = data as KanbanData;\n return { columns: d.columns ?? [], tasks: d.tasks ?? [] };\n }\n\n const rows = asRows(data);\n if (rows.length === 0) return { columns: [], tasks: [] };\n\n const sample = rows[0];\n const columnField = firstPresent(sample, [\n asString(config?.columnField),\n asString(config?.groupField),\n asString(config?.statusField),\n ...COLUMN_FIELD_CANDIDATES,\n asString(config?.xAxisField),\n asString(config?.labelField),\n ]);\n if (!columnField) return { columns: [], tasks: [] };\n\n const titleField = firstPresent(sample, [\n asString(config?.cardTitleField),\n asString(config?.titleField),\n ...TITLE_FIELD_CANDIDATES,\n asString(config?.labelField),\n ]);\n\n const columnTitles: Record<string, string> = {};\n const columnOrder: string[] = [];\n const tasks: KanbanTask[] = rows.map((row, i) => {\n const columnRaw = row[columnField];\n const column = columnRaw == null || columnRaw === '' ? 'Uncategorized' : String(columnRaw);\n if (!(column in columnTitles)) {\n columnTitles[column] = column;\n columnOrder.push(column);\n }\n const idRaw = row.id ?? row.identifier ?? row.key;\n const labels = Array.isArray(row.labels) ? row.labels.filter((l): l is string => typeof l === 'string') : undefined;\n return {\n id: idRaw != null ? String(idRaw) : `task-${i}`,\n title: titleField && row[titleField] != null ? String(row[titleField]) : `Item ${i + 1}`,\n column,\n assignee: asString(row.assignee),\n priority: normalizePriority(row.priority ?? row.priorityLabel),\n labels: labels && labels.length > 0 ? labels : undefined,\n };\n });\n\n const columns: KanbanColumn[] = columnOrder.map((id) => ({ id, title: columnTitles[id] ?? id }));\n return { columns, tasks };\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const KanbanView: FC<KanbanViewProps> = memo(function KanbanView({ widget, data, onClick, t }) {\n const config = widget.config as Record<string, unknown> | undefined;\n const { columns, tasks } = useMemo(() => normalizeKanban(data, config), [data, config]);\n\n // Group tasks by column\n const tasksByColumn = useMemo(() => {\n const grouped: Record<string, KanbanTask[]> = {};\n columns.forEach((col) => {\n grouped[col.id] = tasks.filter((t) => t.column === col.id);\n });\n return grouped;\n }, [columns, tasks]);\n\n if (!columns.length) {\n return (\n <div className=\"flex items-center justify-center h-full text-text-secondary text-sm\">\n <div className=\"text-center\">\n <svg className=\"w-12 h-12 mx-auto mb-2 text-text-muted\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2\"\n />\n </svg>\n <p>{t('bigconsole.widget.kanban.emptyState', 'No board data')}</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex h-full p-2 gap-2 overflow-x-auto\">\n {columns.map((column) => {\n const columnTasks = tasksByColumn[column.id] || [];\n\n return (\n <div key={column.id} className=\"flex-shrink-0 w-48 flex flex-col bg-bg-sunken/50 rounded-lg\">\n {/* Column Header */}\n <div className=\"flex items-center gap-2 p-2 border-b border-border-default\">\n <div\n className=\"w-2 h-2 rounded-full\"\n style={{ backgroundColor: column.color || 'var(--color-text-muted)' }}\n />\n <span className=\"text-xs font-medium text-text-primary truncate\">{column.title}</span>\n <span className=\"ml-auto text-xs text-text-muted\">{columnTasks.length}</span>\n </div>\n\n {/* Tasks */}\n <div className=\"flex-1 p-2 space-y-2 overflow-y-auto\">\n {columnTasks.map((task) => (\n <div\n key={task.id}\n className={`\n p-2 bg-bg-surface rounded border border-border-default\n ${onClick ? 'cursor-pointer hover:border-action-primary-bg' : ''}\n transition-colors\n `}\n onClick={() => onClick?.(task)}\n >\n <p className=\"text-xs font-medium text-text-primary line-clamp-2\">{task.title}</p>\n\n <div className=\"flex items-center gap-1 mt-2 flex-wrap\">\n {task.priority && (\n <span className={`px-1.5 py-0.5 text-[10px] rounded ${getPriorityColor(task.priority)}`}>\n {task.priority}\n </span>\n )}\n {task.assignee && (\n <span className=\"px-1.5 py-0.5 text-[10px] rounded bg-action-primary-bg/10 text-action-primary-text\">\n {task.assignee}\n </span>\n )}\n </div>\n </div>\n ))}\n\n {columnTasks.length === 0 && (\n <p className=\"text-[10px] text-text-muted text-center py-4\">\n {t('bigconsole.widget.kanban.noTasks', 'No tasks')}\n </p>\n )}\n </div>\n </div>\n );\n })}\n </div>\n );\n});\n\nexport default KanbanView;\n"],"mappings":"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"}
|
|
@@ -1,193 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import e from "./KanbanView.js";
|
|
2
|
+
import { memo as t } from "react";
|
|
3
|
+
import { jsx as n } from "react/jsx-runtime";
|
|
4
|
+
import { useI18n as r } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
4
5
|
//#region src/bigconsole/components/widgets/kanban/KanbanWidget.tsx
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
default: return "bg-bg-sunken text-text-secondary";
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
var o = [
|
|
14
|
-
"status",
|
|
15
|
-
"state",
|
|
16
|
-
"column",
|
|
17
|
-
"stage",
|
|
18
|
-
"category",
|
|
19
|
-
"phase",
|
|
20
|
-
"group"
|
|
21
|
-
], s = [
|
|
22
|
-
"title",
|
|
23
|
-
"name",
|
|
24
|
-
"summary",
|
|
25
|
-
"label",
|
|
26
|
-
"identifier",
|
|
27
|
-
"id"
|
|
28
|
-
];
|
|
29
|
-
function c(e) {
|
|
30
|
-
return typeof e == "string" && e.length > 0 ? e : void 0;
|
|
31
|
-
}
|
|
32
|
-
function l(e) {
|
|
33
|
-
let t = (e) => e.filter((e) => !!e && typeof e == "object" && !Array.isArray(e));
|
|
34
|
-
if (Array.isArray(e)) return t(e);
|
|
35
|
-
if (e && typeof e == "object") {
|
|
36
|
-
let n = e;
|
|
37
|
-
for (let e of [
|
|
38
|
-
"tasks",
|
|
39
|
-
"rows",
|
|
40
|
-
"items",
|
|
41
|
-
"data",
|
|
42
|
-
"nodes",
|
|
43
|
-
"results",
|
|
44
|
-
"records"
|
|
45
|
-
]) if (Array.isArray(n[e])) return t(n[e]);
|
|
46
|
-
let r = Object.values(n).find((e) => Array.isArray(e));
|
|
47
|
-
if (Array.isArray(r)) return t(r);
|
|
48
|
-
}
|
|
49
|
-
return [];
|
|
50
|
-
}
|
|
51
|
-
function u(e, t) {
|
|
52
|
-
for (let n of t) if (n && n in e) return n;
|
|
53
|
-
}
|
|
54
|
-
function d(e) {
|
|
55
|
-
let t = c(e)?.toLowerCase();
|
|
56
|
-
if (t) {
|
|
57
|
-
if (t === "urgent" || t === "high") return "high";
|
|
58
|
-
if (t === "medium" || t === "normal") return "medium";
|
|
59
|
-
if (t === "low") return "low";
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
function f(e, t) {
|
|
63
|
-
if (e && typeof e == "object" && !Array.isArray(e) && Array.isArray(e.columns)) {
|
|
64
|
-
let t = e;
|
|
65
|
-
return {
|
|
66
|
-
columns: t.columns ?? [],
|
|
67
|
-
tasks: t.tasks ?? []
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
let n = l(e);
|
|
71
|
-
if (n.length === 0) return {
|
|
72
|
-
columns: [],
|
|
73
|
-
tasks: []
|
|
74
|
-
};
|
|
75
|
-
let r = n[0], i = u(r, [
|
|
76
|
-
c(t?.columnField),
|
|
77
|
-
c(t?.groupField),
|
|
78
|
-
c(t?.statusField),
|
|
79
|
-
...o,
|
|
80
|
-
c(t?.xAxisField),
|
|
81
|
-
c(t?.labelField)
|
|
82
|
-
]);
|
|
83
|
-
if (!i) return {
|
|
84
|
-
columns: [],
|
|
85
|
-
tasks: []
|
|
86
|
-
};
|
|
87
|
-
let a = u(r, [
|
|
88
|
-
c(t?.cardTitleField),
|
|
89
|
-
c(t?.titleField),
|
|
90
|
-
...s,
|
|
91
|
-
c(t?.labelField)
|
|
92
|
-
]), f = {}, p = [], m = n.map((e, t) => {
|
|
93
|
-
let n = e[i], r = n == null || n === "" ? "Uncategorized" : String(n);
|
|
94
|
-
r in f || (f[r] = r, p.push(r));
|
|
95
|
-
let o = e.id ?? e.identifier ?? e.key, s = Array.isArray(e.labels) ? e.labels.filter((e) => typeof e == "string") : void 0;
|
|
96
|
-
return {
|
|
97
|
-
id: o == null ? `task-${t}` : String(o),
|
|
98
|
-
title: a && e[a] != null ? String(e[a]) : `Item ${t + 1}`,
|
|
99
|
-
column: r,
|
|
100
|
-
assignee: c(e.assignee),
|
|
101
|
-
priority: d(e.priority ?? e.priorityLabel),
|
|
102
|
-
labels: s && s.length > 0 ? s : void 0
|
|
103
|
-
};
|
|
104
|
-
});
|
|
105
|
-
return {
|
|
106
|
-
columns: p.map((e) => ({
|
|
107
|
-
id: e,
|
|
108
|
-
title: f[e] ?? e
|
|
109
|
-
})),
|
|
110
|
-
tasks: m
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
var p = e(function({ widget: e, data: o, onClick: s }) {
|
|
114
|
-
let { t: c } = i(), l = e.config, { columns: u, tasks: d } = t(() => f(o, l), [o, l]), p = t(() => {
|
|
115
|
-
let e = {};
|
|
116
|
-
return u.forEach((t) => {
|
|
117
|
-
e[t.id] = d.filter((e) => e.column === t.id);
|
|
118
|
-
}), e;
|
|
119
|
-
}, [u, d]);
|
|
120
|
-
return u.length ? /* @__PURE__ */ n("div", {
|
|
121
|
-
className: "flex h-full p-2 gap-2 overflow-x-auto",
|
|
122
|
-
children: u.map((e) => {
|
|
123
|
-
let t = p[e.id] || [];
|
|
124
|
-
return /* @__PURE__ */ r("div", {
|
|
125
|
-
className: "flex-shrink-0 w-48 flex flex-col bg-bg-sunken/50 rounded-lg",
|
|
126
|
-
children: [/* @__PURE__ */ r("div", {
|
|
127
|
-
className: "flex items-center gap-2 p-2 border-b border-border-default",
|
|
128
|
-
children: [
|
|
129
|
-
/* @__PURE__ */ n("div", {
|
|
130
|
-
className: "w-2 h-2 rounded-full",
|
|
131
|
-
style: { backgroundColor: e.color || "var(--color-text-muted)" }
|
|
132
|
-
}),
|
|
133
|
-
/* @__PURE__ */ n("span", {
|
|
134
|
-
className: "text-xs font-medium text-text-primary truncate",
|
|
135
|
-
children: e.title
|
|
136
|
-
}),
|
|
137
|
-
/* @__PURE__ */ n("span", {
|
|
138
|
-
className: "ml-auto text-xs text-text-muted",
|
|
139
|
-
children: t.length
|
|
140
|
-
})
|
|
141
|
-
]
|
|
142
|
-
}), /* @__PURE__ */ r("div", {
|
|
143
|
-
className: "flex-1 p-2 space-y-2 overflow-y-auto",
|
|
144
|
-
children: [t.map((e) => /* @__PURE__ */ r("div", {
|
|
145
|
-
className: `
|
|
146
|
-
p-2 bg-bg-surface rounded border border-border-default
|
|
147
|
-
${s ? "cursor-pointer hover:border-action-primary-bg" : ""}
|
|
148
|
-
transition-colors
|
|
149
|
-
`,
|
|
150
|
-
onClick: () => s?.(e),
|
|
151
|
-
children: [/* @__PURE__ */ n("p", {
|
|
152
|
-
className: "text-xs font-medium text-text-primary line-clamp-2",
|
|
153
|
-
children: e.title
|
|
154
|
-
}), /* @__PURE__ */ r("div", {
|
|
155
|
-
className: "flex items-center gap-1 mt-2 flex-wrap",
|
|
156
|
-
children: [e.priority && /* @__PURE__ */ n("span", {
|
|
157
|
-
className: `px-1.5 py-0.5 text-[10px] rounded ${a(e.priority)}`,
|
|
158
|
-
children: e.priority
|
|
159
|
-
}), e.assignee && /* @__PURE__ */ n("span", {
|
|
160
|
-
className: "px-1.5 py-0.5 text-[10px] rounded bg-action-primary-bg/10 text-action-primary-text",
|
|
161
|
-
children: e.assignee
|
|
162
|
-
})]
|
|
163
|
-
})]
|
|
164
|
-
}, e.id)), t.length === 0 && /* @__PURE__ */ n("p", {
|
|
165
|
-
className: "text-[10px] text-text-muted text-center py-4",
|
|
166
|
-
children: c("bigconsole.widget.kanban.noTasks", "No tasks")
|
|
167
|
-
})]
|
|
168
|
-
})]
|
|
169
|
-
}, e.id);
|
|
170
|
-
})
|
|
171
|
-
}) : /* @__PURE__ */ n("div", {
|
|
172
|
-
className: "flex items-center justify-center h-full text-text-secondary text-sm",
|
|
173
|
-
children: /* @__PURE__ */ r("div", {
|
|
174
|
-
className: "text-center",
|
|
175
|
-
children: [/* @__PURE__ */ n("svg", {
|
|
176
|
-
className: "w-12 h-12 mx-auto mb-2 text-text-muted",
|
|
177
|
-
fill: "none",
|
|
178
|
-
stroke: "currentColor",
|
|
179
|
-
viewBox: "0 0 24 24",
|
|
180
|
-
children: /* @__PURE__ */ n("path", {
|
|
181
|
-
strokeLinecap: "round",
|
|
182
|
-
strokeLinejoin: "round",
|
|
183
|
-
strokeWidth: 1.5,
|
|
184
|
-
d: "M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2"
|
|
185
|
-
})
|
|
186
|
-
}), /* @__PURE__ */ n("p", { children: c("bigconsole.widget.kanban.emptyState", "No board data") })]
|
|
187
|
-
})
|
|
6
|
+
var i = t(function(t) {
|
|
7
|
+
let { t: i } = r();
|
|
8
|
+
return /* @__PURE__ */ n(e, {
|
|
9
|
+
...t,
|
|
10
|
+
t: i
|
|
188
11
|
});
|
|
189
12
|
});
|
|
190
13
|
//#endregion
|
|
191
|
-
export {
|
|
14
|
+
export { i as default };
|
|
192
15
|
|
|
193
16
|
//# sourceMappingURL=KanbanWidget.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KanbanWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/kanban/KanbanWidget.tsx"],"sourcesContent":["/**\n * KanbanWidget Component\n *\n * Simple kanban board view with columns and tasks.\n */\n\nimport { type FC, memo, useMemo } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport type { Widget } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface KanbanWidgetProps {\n widget: Widget;\n data?: KanbanData;\n onClick?: (task: KanbanTask) => void;\n}\n\nexport interface KanbanColumn {\n id: string;\n title: string;\n color?: string;\n}\n\nexport interface KanbanTask {\n id: string;\n title: string;\n column: string;\n priority?: 'high' | 'medium' | 'low';\n assignee?: string;\n labels?: string[];\n dueDate?: string;\n}\n\nexport interface KanbanData {\n columns?: KanbanColumn[];\n tasks?: KanbanTask[];\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction getPriorityColor(priority?: string): string {\n switch (priority) {\n case 'high':\n return 'bg-status-error-bg/20 text-status-error-text';\n case 'medium':\n return 'bg-status-warning-bg/20 text-[var(--color-status-warning-text)]';\n case 'low':\n return 'bg-status-success-bg/20 text-status-success-text';\n default:\n return 'bg-bg-sunken text-text-secondary';\n }\n}\n\n// Field names we try, in order, when the widget config doesn't name one\n// explicitly. A DataSink/parser typically emits a flat array of rows (e.g. the\n// Linear \"Kanban board\" parser -> [{ id, title, status, assignee, ... }]), so\n// the board is derived from a status/column field on those rows.\nconst COLUMN_FIELD_CANDIDATES = ['status', 'state', 'column', 'stage', 'category', 'phase', 'group'];\nconst TITLE_FIELD_CANDIDATES = ['title', 'name', 'summary', 'label', 'identifier', 'id'];\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\n/** Coerce parser output into an array of object rows (handles common envelopes). */\nfunction asRows(data: unknown): Record<string, unknown>[] {\n const onlyObjects = (arr: unknown[]): Record<string, unknown>[] =>\n arr.filter((r): r is Record<string, unknown> => !!r && typeof r === 'object' && !Array.isArray(r));\n\n if (Array.isArray(data)) return onlyObjects(data);\n\n if (data && typeof data === 'object') {\n const obj = data as Record<string, unknown>;\n for (const key of ['tasks', 'rows', 'items', 'data', 'nodes', 'results', 'records']) {\n if (Array.isArray(obj[key])) return onlyObjects(obj[key] as unknown[]);\n }\n const firstArray = Object.values(obj).find((v) => Array.isArray(v));\n if (Array.isArray(firstArray)) return onlyObjects(firstArray);\n }\n return [];\n}\n\n/** First key that actually exists on the sample row. */\nfunction firstPresent(row: Record<string, unknown>, keys: (string | undefined)[]): string | undefined {\n for (const key of keys) {\n if (key && key in row) return key;\n }\n return undefined;\n}\n\nfunction normalizePriority(value: unknown): KanbanTask['priority'] {\n const v = asString(value)?.toLowerCase();\n if (!v) return undefined;\n if (v === 'urgent' || v === 'high') return 'high';\n if (v === 'medium' || v === 'normal') return 'medium';\n if (v === 'low') return 'low';\n return undefined;\n}\n\n/**\n * Normalize widget data into the board's { columns, tasks } shape.\n *\n * KanbanWidget previously required the exact canonical shape and rendered\n * \"No board data\" for any DataSink/parser output that was a plain array of\n * rows (which is what parsers actually emit) — BOFF-4110. This adapts a flat\n * array into columns (distinct values of a status/column field, in first-seen\n * order) plus one card per row, honoring config field mappings with fallbacks.\n */\nfunction normalizeKanban(\n data: unknown,\n config?: Record<string, unknown>\n): { columns: KanbanColumn[]; tasks: KanbanTask[] } {\n // Canonical shape passes through untouched (back-compat).\n if (data && typeof data === 'object' && !Array.isArray(data) && Array.isArray((data as KanbanData).columns)) {\n const d = data as KanbanData;\n return { columns: d.columns ?? [], tasks: d.tasks ?? [] };\n }\n\n const rows = asRows(data);\n if (rows.length === 0) return { columns: [], tasks: [] };\n\n const sample = rows[0];\n const columnField = firstPresent(sample, [\n asString(config?.columnField),\n asString(config?.groupField),\n asString(config?.statusField),\n ...COLUMN_FIELD_CANDIDATES,\n asString(config?.xAxisField),\n asString(config?.labelField),\n ]);\n if (!columnField) return { columns: [], tasks: [] };\n\n const titleField = firstPresent(sample, [\n asString(config?.cardTitleField),\n asString(config?.titleField),\n ...TITLE_FIELD_CANDIDATES,\n asString(config?.labelField),\n ]);\n\n const columnTitles: Record<string, string> = {};\n const columnOrder: string[] = [];\n const tasks: KanbanTask[] = rows.map((row, i) => {\n const columnRaw = row[columnField];\n const column = columnRaw == null || columnRaw === '' ? 'Uncategorized' : String(columnRaw);\n if (!(column in columnTitles)) {\n columnTitles[column] = column;\n columnOrder.push(column);\n }\n const idRaw = row.id ?? row.identifier ?? row.key;\n const labels = Array.isArray(row.labels) ? row.labels.filter((l): l is string => typeof l === 'string') : undefined;\n return {\n id: idRaw != null ? String(idRaw) : `task-${i}`,\n title: titleField && row[titleField] != null ? String(row[titleField]) : `Item ${i + 1}`,\n column,\n assignee: asString(row.assignee),\n priority: normalizePriority(row.priority ?? row.priorityLabel),\n labels: labels && labels.length > 0 ? labels : undefined,\n };\n });\n\n const columns: KanbanColumn[] = columnOrder.map((id) => ({ id, title: columnTitles[id] ?? id }));\n return { columns, tasks };\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const KanbanWidget: FC<KanbanWidgetProps> = memo(function KanbanWidget({ widget, data, onClick }) {\n const { t } = useI18n();\n const config = widget.config as Record<string, unknown> | undefined;\n const { columns, tasks } = useMemo(() => normalizeKanban(data, config), [data, config]);\n\n // Group tasks by column\n const tasksByColumn = useMemo(() => {\n const grouped: Record<string, KanbanTask[]> = {};\n columns.forEach((col) => {\n grouped[col.id] = tasks.filter((t) => t.column === col.id);\n });\n return grouped;\n }, [columns, tasks]);\n\n if (!columns.length) {\n return (\n <div className=\"flex items-center justify-center h-full text-text-secondary text-sm\">\n <div className=\"text-center\">\n <svg className=\"w-12 h-12 mx-auto mb-2 text-text-muted\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2\"\n />\n </svg>\n <p>{t('bigconsole.widget.kanban.emptyState', 'No board data')}</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex h-full p-2 gap-2 overflow-x-auto\">\n {columns.map((column) => {\n const columnTasks = tasksByColumn[column.id] || [];\n\n return (\n <div key={column.id} className=\"flex-shrink-0 w-48 flex flex-col bg-bg-sunken/50 rounded-lg\">\n {/* Column Header */}\n <div className=\"flex items-center gap-2 p-2 border-b border-border-default\">\n <div\n className=\"w-2 h-2 rounded-full\"\n style={{ backgroundColor: column.color || 'var(--color-text-muted)' }}\n />\n <span className=\"text-xs font-medium text-text-primary truncate\">{column.title}</span>\n <span className=\"ml-auto text-xs text-text-muted\">{columnTasks.length}</span>\n </div>\n\n {/* Tasks */}\n <div className=\"flex-1 p-2 space-y-2 overflow-y-auto\">\n {columnTasks.map((task) => (\n <div\n key={task.id}\n className={`\n p-2 bg-bg-surface rounded border border-border-default\n ${onClick ? 'cursor-pointer hover:border-action-primary-bg' : ''}\n transition-colors\n `}\n onClick={() => onClick?.(task)}\n >\n <p className=\"text-xs font-medium text-text-primary line-clamp-2\">{task.title}</p>\n\n <div className=\"flex items-center gap-1 mt-2 flex-wrap\">\n {task.priority && (\n <span className={`px-1.5 py-0.5 text-[10px] rounded ${getPriorityColor(task.priority)}`}>\n {task.priority}\n </span>\n )}\n {task.assignee && (\n <span className=\"px-1.5 py-0.5 text-[10px] rounded bg-action-primary-bg/10 text-action-primary-text\">\n {task.assignee}\n </span>\n )}\n </div>\n </div>\n ))}\n\n {columnTasks.length === 0 && (\n <p className=\"text-[10px] text-text-muted text-center py-4\">\n {t('bigconsole.widget.kanban.noTasks', 'No tasks')}\n </p>\n )}\n </div>\n </div>\n );\n })}\n </div>\n );\n});\n\nexport default KanbanWidget;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"KanbanWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/kanban/KanbanWidget.tsx"],"sourcesContent":["/**\n * KanbanWidget — in-app wrapper around the pure KanbanView (BOFF-7926).\n *\n * The view is i18n-provider-free so the public embed bundle can render it with a\n * static translator (the fe-libs I18nProvider touches localStorage, which throws\n * in third-party iframes when the visitor blocks third-party storage).\n */\n\nimport { type FC, memo } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { KanbanView, type KanbanWidgetProps } from './KanbanView';\n\nexport * from './KanbanView';\n\nexport const KanbanWidget: FC<KanbanWidgetProps> = memo(function KanbanWidget(props) {\n const { t } = useI18n();\n return <KanbanView {...props} t={t} />;\n});\n\nexport default KanbanWidget;\n"],"mappings":";;;;;AAcA,IAAa,IAAsC,EAAK,SAAsB,GAAO;CACnF,IAAM,EAAE,SAAM,GAAS;AACvB,QAAO,kBAAC,GAAD;EAAY,GAAI;EAAU;EAAK,CAAA;EACtC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { firstObjectRow as e, inferValueField as t, unwrapRows as n } from "../utils/inferFields.js";
|
|
2
|
+
import { conditionalStyleToClassName as r, resolveConditionalStyle as i } from "../utils/conditionalFormat.js";
|
|
3
3
|
import a from "./SparklineChart.js";
|
|
4
4
|
import { clampProgressWidth as o, getTargetProgress as s } from "../utils/targetProgress.js";
|
|
5
5
|
import c from "./TrendIndicator.js";
|
|
@@ -56,19 +56,19 @@ function y(e, t) {
|
|
|
56
56
|
var b = u(function({ widget: u, data: h, onClick: b, onDrilldown: x }) {
|
|
57
57
|
let S = u.config, C = f(() => {
|
|
58
58
|
if (!h) return;
|
|
59
|
-
let
|
|
59
|
+
let r = (e, t) => t.split(".").reduce((e, t) => e && typeof e == "object" ? e[t] : void 0, e);
|
|
60
60
|
if (typeof h == "number" || typeof h == "string") return { value: h };
|
|
61
|
-
let
|
|
62
|
-
if (
|
|
63
|
-
let
|
|
64
|
-
if (
|
|
65
|
-
let
|
|
61
|
+
let i = S?.valueField;
|
|
62
|
+
if (i && typeof h == "object") {
|
|
63
|
+
let e = h, t = r(e, i);
|
|
64
|
+
if (t !== void 0) {
|
|
65
|
+
let n = S?.previousValueField, i = n ? r(e, n) : void 0, a = i !== void 0 && i !== 0 && Number.isFinite(i) && typeof t == "number" ? (t - i) / i * 100 : void 0, o = h.changePercent, s = i === void 0 ? {} : {
|
|
66
66
|
previousValue: i,
|
|
67
67
|
changePercent: a ?? (Number.isFinite(o) ? o : void 0)
|
|
68
68
|
};
|
|
69
69
|
return {
|
|
70
70
|
...h,
|
|
71
|
-
value:
|
|
71
|
+
value: t,
|
|
72
72
|
...s
|
|
73
73
|
};
|
|
74
74
|
}
|
|
@@ -98,20 +98,20 @@ var b = u(function({ widget: u, data: h, onClick: b, onDrilldown: x }) {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
if (typeof h == "number" || typeof h == "string") return { value: h };
|
|
101
|
-
let o =
|
|
101
|
+
let o = e(h);
|
|
102
102
|
if (o) {
|
|
103
|
-
let
|
|
104
|
-
if (
|
|
105
|
-
let
|
|
106
|
-
for (let
|
|
107
|
-
let
|
|
108
|
-
if (typeof
|
|
103
|
+
let e = S?.valueField;
|
|
104
|
+
if (e) {
|
|
105
|
+
let t = n(h) ?? [o];
|
|
106
|
+
for (let n of t) if (n && typeof n == "object") {
|
|
107
|
+
let t = r(n, e);
|
|
108
|
+
if (typeof t == "number" || typeof t == "string") return { value: t };
|
|
109
109
|
}
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
|
-
let
|
|
113
|
-
if (
|
|
114
|
-
let e = o[
|
|
112
|
+
let i = t(o);
|
|
113
|
+
if (i) {
|
|
114
|
+
let e = o[i];
|
|
115
115
|
if (typeof e == "number" || typeof e == "string") return { value: e };
|
|
116
116
|
}
|
|
117
117
|
}
|
|
@@ -119,7 +119,7 @@ var b = u(function({ widget: u, data: h, onClick: b, onDrilldown: x }) {
|
|
|
119
119
|
C?.value,
|
|
120
120
|
S,
|
|
121
121
|
D
|
|
122
|
-
]), k = f(() => s(C?.value, C?.target), [C?.value, C?.target]), A = f(() => y(w, E), [w, E]), j = f(() =>
|
|
122
|
+
]), k = f(() => s(C?.value, C?.target), [C?.value, C?.target]), A = f(() => y(w, E), [w, E]), j = f(() => r(i(C?.value, S?.conditionalRules)), [C?.value, S?.conditionalRules]), M = d(() => {
|
|
123
123
|
x ? x({
|
|
124
124
|
value: C?.value,
|
|
125
125
|
changePercent: C?.changePercent,
|
|
@@ -40,20 +40,38 @@ function o(e) {
|
|
|
40
40
|
return e >= 100 ? "bg-status-success-bg" : e >= 50 ? "bg-action-primary-bg" : e > 0 ? "bg-status-warning-bg" : "bg-bg-sunken";
|
|
41
41
|
}
|
|
42
42
|
var s = e(function({ widget: e, data: i, onClick: s }) {
|
|
43
|
-
let c = t(() => a(i), [i]), l = i?.milestones
|
|
44
|
-
|
|
43
|
+
let c = t(() => a(i), [i]), l = t(() => i?.milestones ?? [], [i]), { minDate: u, maxDate: d, totalDays: f } = t(() => {
|
|
44
|
+
let e = c.length ? c.flatMap((e) => [new Date(e.start), new Date(e.end)]) : l.map((e) => new Date(e.date));
|
|
45
|
+
if (!e.length) return {
|
|
45
46
|
minDate: /* @__PURE__ */ new Date(),
|
|
46
47
|
maxDate: /* @__PURE__ */ new Date(),
|
|
47
48
|
totalDays: 1
|
|
48
49
|
};
|
|
49
|
-
let
|
|
50
|
+
let t = new Date(Math.min(...e.map((e) => e.getTime()))), n = new Date(Math.max(...e.map((e) => e.getTime())));
|
|
50
51
|
return {
|
|
51
52
|
minDate: t,
|
|
52
53
|
maxDate: n,
|
|
53
54
|
totalDays: Math.ceil((n.getTime() - t.getTime()) / (1e3 * 60 * 60 * 24)) + 1
|
|
54
55
|
};
|
|
55
|
-
}, [c]);
|
|
56
|
-
return c.length ? /* @__PURE__ */
|
|
56
|
+
}, [c, l]);
|
|
57
|
+
return !c.length && !l.length ? /* @__PURE__ */ n("div", {
|
|
58
|
+
className: "flex items-center justify-center h-full text-text-secondary text-sm",
|
|
59
|
+
children: /* @__PURE__ */ r("div", {
|
|
60
|
+
className: "text-center",
|
|
61
|
+
children: [/* @__PURE__ */ n("svg", {
|
|
62
|
+
className: "w-12 h-12 mx-auto mb-2 text-text-muted",
|
|
63
|
+
fill: "none",
|
|
64
|
+
stroke: "currentColor",
|
|
65
|
+
viewBox: "0 0 24 24",
|
|
66
|
+
children: /* @__PURE__ */ n("path", {
|
|
67
|
+
strokeLinecap: "round",
|
|
68
|
+
strokeLinejoin: "round",
|
|
69
|
+
strokeWidth: 1.5,
|
|
70
|
+
d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
|
|
71
|
+
})
|
|
72
|
+
}), /* @__PURE__ */ n("p", { children: "No timeline data" })]
|
|
73
|
+
})
|
|
74
|
+
}) : /* @__PURE__ */ r("div", {
|
|
57
75
|
className: "flex flex-col h-full p-4 overflow-auto",
|
|
58
76
|
children: [
|
|
59
77
|
/* @__PURE__ */ r("div", {
|
|
@@ -157,23 +175,6 @@ var s = e(function({ widget: e, data: i, onClick: s }) {
|
|
|
157
175
|
})]
|
|
158
176
|
})
|
|
159
177
|
]
|
|
160
|
-
}) : /* @__PURE__ */ n("div", {
|
|
161
|
-
className: "flex items-center justify-center h-full text-text-secondary text-sm",
|
|
162
|
-
children: /* @__PURE__ */ r("div", {
|
|
163
|
-
className: "text-center",
|
|
164
|
-
children: [/* @__PURE__ */ n("svg", {
|
|
165
|
-
className: "w-12 h-12 mx-auto mb-2 text-text-muted",
|
|
166
|
-
fill: "none",
|
|
167
|
-
stroke: "currentColor",
|
|
168
|
-
viewBox: "0 0 24 24",
|
|
169
|
-
children: /* @__PURE__ */ n("path", {
|
|
170
|
-
strokeLinecap: "round",
|
|
171
|
-
strokeLinejoin: "round",
|
|
172
|
-
strokeWidth: 1.5,
|
|
173
|
-
d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
|
|
174
|
-
})
|
|
175
|
-
}), /* @__PURE__ */ n("p", { children: "No timeline data" })]
|
|
176
|
-
})
|
|
177
178
|
});
|
|
178
179
|
});
|
|
179
180
|
//#endregion
|