@burdenoff/microfe-bigconsole 2026.727.2 → 2026.727.4
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/widgets/calendar/CalendarWidget.js +55 -12
- package/dist/bigconsole/components/widgets/calendar/CalendarWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js +57 -22
- package/dist/bigconsole/components/widgets/timeline/TimelineWidget.js.map +1 -1
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js +31 -25
- package/dist/bigconsole/customWidget/CustomWidgetBuilderSheet.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,15 +1,58 @@
|
|
|
1
1
|
import { memo as e, useMemo as t } from "react";
|
|
2
2
|
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
3
3
|
//#region src/bigconsole/components/widgets/calendar/CalendarWidget.tsx
|
|
4
|
-
|
|
5
|
-
let
|
|
4
|
+
function i(e, t) {
|
|
5
|
+
for (let n of t) if (e[n] !== void 0 && e[n] !== null) return e[n];
|
|
6
|
+
}
|
|
7
|
+
function a(e) {
|
|
8
|
+
let t = Array.isArray(e) ? e : Array.isArray(e?.events) ? e.events : Array.isArray(e?.items) ? e.items : Array.isArray(e?.data) ? e.data : void 0;
|
|
9
|
+
return Array.isArray(t) ? t.map((e, t) => {
|
|
10
|
+
if (!e || typeof e != "object") return null;
|
|
11
|
+
let n = e, r = i(n, [
|
|
12
|
+
"start",
|
|
13
|
+
"startDate",
|
|
14
|
+
"date",
|
|
15
|
+
"startTime"
|
|
16
|
+
]);
|
|
17
|
+
if (!r) return null;
|
|
18
|
+
let a = i(n, [
|
|
19
|
+
"end",
|
|
20
|
+
"endDate",
|
|
21
|
+
"endTime"
|
|
22
|
+
]) ?? r, o = i(n, [
|
|
23
|
+
"title",
|
|
24
|
+
"name",
|
|
25
|
+
"label",
|
|
26
|
+
"summary"
|
|
27
|
+
]) ?? "Untitled event", s = i(n, [
|
|
28
|
+
"id",
|
|
29
|
+
"eventId",
|
|
30
|
+
"_id"
|
|
31
|
+
]) ?? `${o}-${r}-${t}`;
|
|
32
|
+
return {
|
|
33
|
+
id: String(s),
|
|
34
|
+
title: String(o),
|
|
35
|
+
start: String(r),
|
|
36
|
+
end: String(a),
|
|
37
|
+
color: typeof n.color == "string" ? n.color : void 0,
|
|
38
|
+
allDay: typeof n.allDay == "boolean" ? n.allDay : void 0,
|
|
39
|
+
category: i(n, [
|
|
40
|
+
"category",
|
|
41
|
+
"group",
|
|
42
|
+
"type"
|
|
43
|
+
])
|
|
44
|
+
};
|
|
45
|
+
}).filter((e) => e !== null) : [];
|
|
46
|
+
}
|
|
47
|
+
var o = e(function({ widget: e, data: i, onClick: o }) {
|
|
48
|
+
let s = t(() => a(i), [i]), c = t(() => {
|
|
6
49
|
let e = /* @__PURE__ */ new Date(), t = new Date(e.getTime() + 10080 * 60 * 1e3);
|
|
7
|
-
return
|
|
50
|
+
return s.filter((n) => {
|
|
8
51
|
let r = new Date(n.start);
|
|
9
52
|
return r >= e && r <= t;
|
|
10
53
|
}).sort((e, t) => new Date(e.start).getTime() - new Date(t.start).getTime()).slice(0, 8);
|
|
11
|
-
}, [
|
|
12
|
-
return
|
|
54
|
+
}, [s]);
|
|
55
|
+
return s.length ? /* @__PURE__ */ r("div", {
|
|
13
56
|
className: "flex flex-col h-full p-4 overflow-auto",
|
|
14
57
|
children: [
|
|
15
58
|
/* @__PURE__ */ r("div", {
|
|
@@ -19,20 +62,20 @@ var i = e(function({ widget: e, data: i, onClick: a }) {
|
|
|
19
62
|
children: "Upcoming Events"
|
|
20
63
|
}), /* @__PURE__ */ r("span", {
|
|
21
64
|
className: "text-xs text-text-secondary",
|
|
22
|
-
children: [
|
|
65
|
+
children: [s.length, " events"]
|
|
23
66
|
})]
|
|
24
67
|
}),
|
|
25
68
|
/* @__PURE__ */ n("div", {
|
|
26
69
|
className: "flex-1 space-y-2 overflow-auto",
|
|
27
|
-
children:
|
|
70
|
+
children: c.map((e) => {
|
|
28
71
|
let t = new Date(e.start), i = t.toDateString() === (/* @__PURE__ */ new Date()).toDateString();
|
|
29
72
|
return /* @__PURE__ */ n("div", {
|
|
30
73
|
className: `
|
|
31
74
|
p-3 rounded-lg border border-border-default
|
|
32
|
-
${
|
|
75
|
+
${o ? "cursor-pointer hover:border-action-primary-bg" : ""}
|
|
33
76
|
transition-colors
|
|
34
77
|
`,
|
|
35
|
-
onClick: () =>
|
|
78
|
+
onClick: () => o?.(e),
|
|
36
79
|
children: /* @__PURE__ */ r("div", {
|
|
37
80
|
className: "flex items-start gap-3",
|
|
38
81
|
children: [/* @__PURE__ */ n("div", {
|
|
@@ -79,11 +122,11 @@ var i = e(function({ widget: e, data: i, onClick: a }) {
|
|
|
79
122
|
}, e.id);
|
|
80
123
|
})
|
|
81
124
|
}),
|
|
82
|
-
|
|
125
|
+
s.length > c.length && /* @__PURE__ */ r("p", {
|
|
83
126
|
className: "text-xs text-text-tertiary text-center mt-3",
|
|
84
127
|
children: [
|
|
85
128
|
"+",
|
|
86
|
-
|
|
129
|
+
s.length - c.length,
|
|
87
130
|
" more events"
|
|
88
131
|
]
|
|
89
132
|
})
|
|
@@ -108,6 +151,6 @@ var i = e(function({ widget: e, data: i, onClick: a }) {
|
|
|
108
151
|
});
|
|
109
152
|
});
|
|
110
153
|
//#endregion
|
|
111
|
-
export {
|
|
154
|
+
export { o as default };
|
|
112
155
|
|
|
113
156
|
//# sourceMappingURL=CalendarWidget.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CalendarWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/calendar/CalendarWidget.tsx"],"sourcesContent":["/**\n * CalendarWidget Component\n *\n * Simple calendar view showing events.\n */\n\nimport { type FC, memo, useMemo } from 'react';\nimport type { Widget } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CalendarWidgetProps {\n widget: Widget;\n data?: CalendarData;\n onClick?: (event: CalendarEvent) => void;\n}\n\nexport interface CalendarEvent {\n id: string;\n title: string;\n start: string;\n end: string;\n color?: string;\n allDay?: boolean;\n category?: string;\n}\n\nexport interface CalendarData {\n events?: CalendarEvent[];\n currentDate?: string;\n view?: 'month' | 'week' | 'day';\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const CalendarWidget: FC<CalendarWidgetProps> = memo(function CalendarWidget({\n widget: _widget,\n data,\n onClick,\n}) {\n const events = useMemo(() => data?.events || [], [data?.events]);\n\n // Get upcoming events (next 7 days)\n const upcomingEvents = useMemo(() => {\n const now = new Date();\n const weekFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);\n return events\n .filter((e) => {\n const eventDate = new Date(e.start);\n return eventDate >= now && eventDate <= weekFromNow;\n })\n .sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime())\n .slice(0, 8);\n }, [events]);\n\n if (!events.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\n className=\"w-12 h-12 mx-auto mb-2 text-text-tertiary\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n <p>No events to display</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4 overflow-auto\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-sm font-medium text-text-primary\">Upcoming Events</h3>\n <span className=\"text-xs text-text-secondary\">{events.length} events</span>\n </div>\n\n {/* Event List */}\n <div className=\"flex-1 space-y-2 overflow-auto\">\n {upcomingEvents.map((event) => {\n const eventDate = new Date(event.start);\n const isToday = eventDate.toDateString() === new Date().toDateString();\n\n return (\n <div\n key={event.id}\n className={`\n p-3 rounded-lg border border-border-default\n ${onClick ? 'cursor-pointer hover:border-action-primary-bg' : ''}\n transition-colors\n `}\n onClick={() => onClick?.(event)}\n >\n <div className=\"flex items-start gap-3\">\n {/* Color indicator */}\n <div\n className=\"w-1 h-full min-h-[40px] rounded-full flex-shrink-0\"\n style={{ backgroundColor: event.color || 'var(--color-action-primary-bg)' }}\n />\n\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{event.title}</p>\n <p className=\"text-xs text-text-secondary mt-1\">\n {isToday ? (\n <span className=\"text-state-success font-medium\">Today</span>\n ) : (\n eventDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })\n )}\n {!event.allDay && (\n <span className=\"ml-2\">\n {eventDate.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}\n </span>\n )}\n {event.allDay && <span className=\"ml-2\">All day</span>}\n </p>\n {event.category && (\n <span className=\"inline-block mt-1 px-2 py-0.5 text-[10px] rounded bg-bg-sunken text-text-secondary\">\n {event.category}\n </span>\n )}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n\n {events.length > upcomingEvents.length && (\n <p className=\"text-xs text-text-tertiary text-center mt-3\">\n +{events.length - upcomingEvents.length} more events\n </p>\n )}\n </div>\n );\n});\n\nexport default CalendarWidget;\n"],"mappings":";;;AAuCA,IAAa,IAA0C,EAAK,SAAwB,EAClF,QAAQ,GACR,SACA,cACC;CACD,IAAM,IAAS,QAAc,GAAM,UAAU,EAAE,EAAE,CAAC,GAAM,OAAO,CAAC,EAG1D,IAAiB,QAAc;EACnC,IAAM,oBAAM,IAAI,MAAM,EAChB,IAAc,IAAI,KAAK,EAAI,SAAS,GAAG,QAAc,KAAK,IAAK;AACrE,SAAO,EACJ,QAAQ,MAAM;GACb,IAAM,IAAY,IAAI,KAAK,EAAE,MAAM;AACnC,UAAO,KAAa,KAAO,KAAa;IACxC,CACD,MAAM,GAAG,MAAM,IAAI,KAAK,EAAE,MAAM,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,CACzE,MAAM,GAAG,EAAE;IACb,CAAC,EAAO,CAAC;AAyBZ,QAvBK,EAAO,SAwBV,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eAAwC;KAAoB,CAAA,EAC1E,kBAAC,QAAD;KAAM,WAAU;eAAhB,CAA+C,EAAO,QAAO,UAAc;OACvE;;GAGN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAe,KAAK,MAAU;KAC7B,IAAM,IAAY,IAAI,KAAK,EAAM,MAAM,EACjC,IAAU,EAAU,cAAc,sBAAK,IAAI,MAAM,EAAC,cAAc;AAEtE,YACE,kBAAC,OAAD;MAEE,WAAW;;oBAEL,IAAU,kDAAkD,GAAG;;;MAGrE,eAAe,IAAU,EAAM;gBAE/B,kBAAC,OAAD;OAAK,WAAU;iBAAf,CAEE,kBAAC,OAAD;QACE,WAAU;QACV,OAAO,EAAE,iBAAiB,EAAM,SAAS,kCAAkC;QAC3E,CAAA,EAEF,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,KAAD;UAAG,WAAU;oBAAkD,EAAM;UAAU,CAAA;SAC/E,kBAAC,KAAD;UAAG,WAAU;oBAAb;WACG,IACC,kBAAC,QAAD;YAAM,WAAU;sBAAiC;YAAY,CAAA,GAE7D,EAAU,mBAAmB,SAAS;YAAE,SAAS;YAAS,OAAO;YAAS,KAAK;YAAW,CAAC;WAE5F,CAAC,EAAM,UACN,kBAAC,QAAD;YAAM,WAAU;sBACb,EAAU,mBAAmB,SAAS;aAAE,MAAM;aAAW,QAAQ;aAAW,CAAC;YACzE,CAAA;WAER,EAAM,UAAU,kBAAC,QAAD;YAAM,WAAU;sBAAO;YAAc,CAAA;WACpD;;SACH,EAAM,YACL,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAM;UACF,CAAA;SAEL;UACF;;MACF,EArCC,EAAM,GAqCP;MAER;IACE,CAAA;GAEL,EAAO,SAAS,EAAe,UAC9B,kBAAC,KAAD;IAAG,WAAU;cAAb;KAA2D;KACvD,EAAO,SAAS,EAAe;KAAO;KACtC;;GAEF;MApFJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IACE,WAAU;IACV,MAAK;IACL,QAAO;IACP,SAAQ;cAER,kBAAC,QAAD;KACE,eAAc;KACd,gBAAe;KACf,aAAa;KACb,GAAE;KACF,CAAA;IACE,CAAA,EACN,kBAAC,KAAD,EAAA,UAAG,wBAAwB,CAAA,CACvB;;EACF,CAAA;EAqEV"}
|
|
1
|
+
{"version":3,"file":"CalendarWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/calendar/CalendarWidget.tsx"],"sourcesContent":["/**\n * CalendarWidget Component\n *\n * Simple calendar view showing events.\n */\n\nimport { type FC, memo, useMemo } from 'react';\nimport type { Widget } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface CalendarWidgetProps {\n widget: Widget;\n data?: CalendarData;\n onClick?: (event: CalendarEvent) => void;\n}\n\nexport interface CalendarEvent {\n id: string;\n title: string;\n start: string;\n end: string;\n color?: string;\n allDay?: boolean;\n category?: string;\n}\n\nexport interface CalendarData {\n events?: CalendarEvent[];\n currentDate?: string;\n view?: 'month' | 'week' | 'day';\n}\n\n// ============================================================================\n// Normalization\n// ============================================================================\n\nfunction firstDefined<T = unknown>(obj: Record<string, unknown>, keys: string[]): T | undefined {\n for (const key of keys) {\n if (obj[key] !== undefined && obj[key] !== null) return obj[key] as T;\n }\n return undefined;\n}\n\n/**\n * Normalize whatever the widget is bound to into `CalendarEvent[]`. Accepts the\n * documented `{ events, ... }` shape, an events array under a different common\n * key, or a bare array of event-like records — a DataSink parser naturally\n * outputs a bare array, and WidgetWrapper has no calendar-specific reshaping\n * step (unlike table/list/chart), so this widget has to tolerate it itself, the\n * same way MapWidget tolerates a bare marker array instead of requiring\n * `{ markers }`. Field names are matched against common aliases so a parser\n * doesn't have to emit the exact `title`/`start`/`end` keys.\n */\nexport function normalizeCalendarEvents(\n data: CalendarData | CalendarEvent[] | Record<string, unknown> | undefined\n): CalendarEvent[] {\n const rawEvents: unknown = Array.isArray(data)\n ? data\n : Array.isArray((data as CalendarData | undefined)?.events)\n ? (data as CalendarData).events\n : Array.isArray((data as Record<string, unknown> | undefined)?.items)\n ? (data as Record<string, unknown>).items\n : Array.isArray((data as Record<string, unknown> | undefined)?.data)\n ? (data as Record<string, unknown>).data\n : undefined;\n\n if (!Array.isArray(rawEvents)) return [];\n\n return rawEvents\n .map((raw, i): CalendarEvent | null => {\n if (!raw || typeof raw !== 'object') return null;\n const r = raw as Record<string, unknown>;\n const start = firstDefined<string>(r, ['start', 'startDate', 'date', 'startTime']);\n if (!start) return null;\n const end = firstDefined<string>(r, ['end', 'endDate', 'endTime']) ?? start;\n const title = firstDefined<string>(r, ['title', 'name', 'label', 'summary']) ?? 'Untitled event';\n const id = firstDefined<string>(r, ['id', 'eventId', '_id']) ?? `${title}-${start}-${i}`;\n return {\n id: String(id),\n title: String(title),\n start: String(start),\n end: String(end),\n color: typeof r.color === 'string' ? r.color : undefined,\n allDay: typeof r.allDay === 'boolean' ? r.allDay : undefined,\n category: firstDefined<string>(r, ['category', 'group', 'type']),\n };\n })\n .filter((e): e is CalendarEvent => e !== null);\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const CalendarWidget: FC<CalendarWidgetProps> = memo(function CalendarWidget({\n widget: _widget,\n data,\n onClick,\n}) {\n const events = useMemo(() => normalizeCalendarEvents(data), [data]);\n\n // Get upcoming events (next 7 days)\n const upcomingEvents = useMemo(() => {\n const now = new Date();\n const weekFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);\n return events\n .filter((e) => {\n const eventDate = new Date(e.start);\n return eventDate >= now && eventDate <= weekFromNow;\n })\n .sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime())\n .slice(0, 8);\n }, [events]);\n\n if (!events.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\n className=\"w-12 h-12 mx-auto mb-2 text-text-tertiary\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n d=\"M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z\"\n />\n </svg>\n <p>No events to display</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4 overflow-auto\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-sm font-medium text-text-primary\">Upcoming Events</h3>\n <span className=\"text-xs text-text-secondary\">{events.length} events</span>\n </div>\n\n {/* Event List */}\n <div className=\"flex-1 space-y-2 overflow-auto\">\n {upcomingEvents.map((event) => {\n const eventDate = new Date(event.start);\n const isToday = eventDate.toDateString() === new Date().toDateString();\n\n return (\n <div\n key={event.id}\n className={`\n p-3 rounded-lg border border-border-default\n ${onClick ? 'cursor-pointer hover:border-action-primary-bg' : ''}\n transition-colors\n `}\n onClick={() => onClick?.(event)}\n >\n <div className=\"flex items-start gap-3\">\n {/* Color indicator */}\n <div\n className=\"w-1 h-full min-h-[40px] rounded-full flex-shrink-0\"\n style={{ backgroundColor: event.color || 'var(--color-action-primary-bg)' }}\n />\n\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">{event.title}</p>\n <p className=\"text-xs text-text-secondary mt-1\">\n {isToday ? (\n <span className=\"text-state-success font-medium\">Today</span>\n ) : (\n eventDate.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })\n )}\n {!event.allDay && (\n <span className=\"ml-2\">\n {eventDate.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}\n </span>\n )}\n {event.allDay && <span className=\"ml-2\">All day</span>}\n </p>\n {event.category && (\n <span className=\"inline-block mt-1 px-2 py-0.5 text-[10px] rounded bg-bg-sunken text-text-secondary\">\n {event.category}\n </span>\n )}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n\n {events.length > upcomingEvents.length && (\n <p className=\"text-xs text-text-tertiary text-center mt-3\">\n +{events.length - upcomingEvents.length} more events\n </p>\n )}\n </div>\n );\n});\n\nexport default CalendarWidget;\n"],"mappings":"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"}
|
|
@@ -1,24 +1,59 @@
|
|
|
1
1
|
import { memo as e, useMemo as t } from "react";
|
|
2
2
|
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
3
3
|
//#region src/bigconsole/components/widgets/timeline/TimelineWidget.tsx
|
|
4
|
-
function i(e) {
|
|
4
|
+
function i(e, t) {
|
|
5
|
+
for (let n of t) if (e[n] !== void 0 && e[n] !== null) return e[n];
|
|
6
|
+
}
|
|
7
|
+
function a(e) {
|
|
8
|
+
let t = Array.isArray(e) ? e : Array.isArray(e?.tasks) ? e.tasks : Array.isArray(e?.items) ? e.items : Array.isArray(e?.data) ? e.data : void 0;
|
|
9
|
+
return Array.isArray(t) ? t.map((e, t) => {
|
|
10
|
+
if (!e || typeof e != "object") return null;
|
|
11
|
+
let n = e, r = i(n, [
|
|
12
|
+
"start",
|
|
13
|
+
"startDate",
|
|
14
|
+
"date"
|
|
15
|
+
]), a = i(n, ["end", "endDate"]) ?? r;
|
|
16
|
+
if (!r || !a) return null;
|
|
17
|
+
let o = i(n, [
|
|
18
|
+
"name",
|
|
19
|
+
"title",
|
|
20
|
+
"label",
|
|
21
|
+
"task"
|
|
22
|
+
]) ?? "Untitled task", s = i(n, [
|
|
23
|
+
"id",
|
|
24
|
+
"taskId",
|
|
25
|
+
"_id"
|
|
26
|
+
]) ?? `${o}-${r}-${t}`;
|
|
27
|
+
return {
|
|
28
|
+
id: String(s),
|
|
29
|
+
name: String(o),
|
|
30
|
+
start: String(r),
|
|
31
|
+
end: String(a),
|
|
32
|
+
progress: typeof n.progress == "number" ? n.progress : void 0,
|
|
33
|
+
color: typeof n.color == "string" ? n.color : void 0,
|
|
34
|
+
duration: typeof n.duration == "number" ? n.duration : void 0,
|
|
35
|
+
dependencies: Array.isArray(n.dependencies) ? n.dependencies : void 0
|
|
36
|
+
};
|
|
37
|
+
}).filter((e) => e !== null) : [];
|
|
38
|
+
}
|
|
39
|
+
function o(e) {
|
|
5
40
|
return e >= 100 ? "bg-state-success" : e >= 50 ? "bg-action-primary-bg" : e > 0 ? "bg-state-warning" : "bg-bg-sunken";
|
|
6
41
|
}
|
|
7
|
-
var
|
|
8
|
-
let
|
|
9
|
-
if (!
|
|
42
|
+
var s = e(function({ widget: e, data: i, onClick: s }) {
|
|
43
|
+
let c = t(() => a(i), [i]), l = i?.milestones || [], { minDate: u, maxDate: d, totalDays: f } = t(() => {
|
|
44
|
+
if (!c.length) return {
|
|
10
45
|
minDate: /* @__PURE__ */ new Date(),
|
|
11
46
|
maxDate: /* @__PURE__ */ new Date(),
|
|
12
47
|
totalDays: 1
|
|
13
48
|
};
|
|
14
|
-
let e =
|
|
49
|
+
let e = c.flatMap((e) => [new Date(e.start), new Date(e.end)]), t = new Date(Math.min(...e.map((e) => e.getTime()))), n = new Date(Math.max(...e.map((e) => e.getTime())));
|
|
15
50
|
return {
|
|
16
51
|
minDate: t,
|
|
17
52
|
maxDate: n,
|
|
18
53
|
totalDays: Math.ceil((n.getTime() - t.getTime()) / (1e3 * 60 * 60 * 24)) + 1
|
|
19
54
|
};
|
|
20
|
-
}, [
|
|
21
|
-
return
|
|
55
|
+
}, [c]);
|
|
56
|
+
return c.length ? /* @__PURE__ */ r("div", {
|
|
22
57
|
className: "flex flex-col h-full p-4 overflow-auto",
|
|
23
58
|
children: [
|
|
24
59
|
/* @__PURE__ */ r("div", {
|
|
@@ -29,13 +64,13 @@ var a = e(function({ widget: e, data: a, onClick: o }) {
|
|
|
29
64
|
}), /* @__PURE__ */ r("span", {
|
|
30
65
|
className: "text-xs text-text-secondary",
|
|
31
66
|
children: [
|
|
32
|
-
|
|
67
|
+
u.toLocaleDateString("en-US", {
|
|
33
68
|
month: "short",
|
|
34
69
|
day: "numeric"
|
|
35
70
|
}),
|
|
36
71
|
" -",
|
|
37
72
|
" ",
|
|
38
|
-
|
|
73
|
+
d.toLocaleDateString("en-US", {
|
|
39
74
|
month: "short",
|
|
40
75
|
day: "numeric"
|
|
41
76
|
})
|
|
@@ -44,13 +79,13 @@ var a = e(function({ widget: e, data: a, onClick: o }) {
|
|
|
44
79
|
}),
|
|
45
80
|
/* @__PURE__ */ n("div", {
|
|
46
81
|
className: "flex-1 space-y-3 overflow-auto",
|
|
47
|
-
children:
|
|
48
|
-
let t = new Date(e.start),
|
|
82
|
+
children: c.map((e) => {
|
|
83
|
+
let t = new Date(e.start), i = new Date(e.end), a = Math.ceil((i.getTime() - t.getTime()) / (1e3 * 60 * 60 * 24)) + 1, c = Math.ceil((t.getTime() - u.getTime()) / (1e3 * 60 * 60 * 24)), l = e.progress ?? 0, d = c / f * 100, p = a / f * 100;
|
|
49
84
|
return /* @__PURE__ */ r("div", {
|
|
50
85
|
className: `
|
|
51
|
-
${
|
|
86
|
+
${s ? "cursor-pointer" : ""}
|
|
52
87
|
`,
|
|
53
|
-
onClick: () =>
|
|
88
|
+
onClick: () => s?.(e),
|
|
54
89
|
children: [/* @__PURE__ */ r("div", {
|
|
55
90
|
className: "flex items-center justify-between mb-1",
|
|
56
91
|
children: [/* @__PURE__ */ n("span", {
|
|
@@ -58,7 +93,7 @@ var a = e(function({ widget: e, data: a, onClick: o }) {
|
|
|
58
93
|
children: e.name
|
|
59
94
|
}), /* @__PURE__ */ r("span", {
|
|
60
95
|
className: "text-[10px] text-text-secondary",
|
|
61
|
-
children: [
|
|
96
|
+
children: [l, "%"]
|
|
62
97
|
})]
|
|
63
98
|
}), /* @__PURE__ */ r("div", {
|
|
64
99
|
className: "relative h-6 bg-bg-sunken rounded overflow-hidden",
|
|
@@ -66,22 +101,22 @@ var a = e(function({ widget: e, data: a, onClick: o }) {
|
|
|
66
101
|
/* @__PURE__ */ n("div", {
|
|
67
102
|
className: "absolute top-1 bottom-1 rounded",
|
|
68
103
|
style: {
|
|
69
|
-
left: `${
|
|
104
|
+
left: `${d}%`,
|
|
70
105
|
width: `${Math.max(p, 5)}%`,
|
|
71
106
|
backgroundColor: e.color || "var(--color-chart-1)",
|
|
72
107
|
opacity: .3
|
|
73
108
|
}
|
|
74
109
|
}),
|
|
75
110
|
/* @__PURE__ */ n("div", {
|
|
76
|
-
className: `absolute top-1 bottom-1 rounded ${
|
|
111
|
+
className: `absolute top-1 bottom-1 rounded ${o(l)}`,
|
|
77
112
|
style: {
|
|
78
|
-
left: `${
|
|
79
|
-
width: `${Math.max(p *
|
|
113
|
+
left: `${d}%`,
|
|
114
|
+
width: `${Math.max(p * l / 100, 2)}%`
|
|
80
115
|
}
|
|
81
116
|
}),
|
|
82
117
|
/* @__PURE__ */ n("div", {
|
|
83
118
|
className: "absolute top-1/2 -translate-y-1/2 text-[9px] text-text-tertiary",
|
|
84
|
-
style: { left: `${
|
|
119
|
+
style: { left: `${d + 1}%` },
|
|
85
120
|
children: t.toLocaleDateString("en-US", {
|
|
86
121
|
month: "short",
|
|
87
122
|
day: "numeric"
|
|
@@ -92,14 +127,14 @@ var a = e(function({ widget: e, data: a, onClick: o }) {
|
|
|
92
127
|
}, e.id);
|
|
93
128
|
})
|
|
94
129
|
}),
|
|
95
|
-
|
|
130
|
+
l.length > 0 && /* @__PURE__ */ r("div", {
|
|
96
131
|
className: "mt-4 pt-3 border-t border-border-default",
|
|
97
132
|
children: [/* @__PURE__ */ n("p", {
|
|
98
133
|
className: "text-xs font-medium text-text-secondary mb-2",
|
|
99
134
|
children: "Milestones"
|
|
100
135
|
}), /* @__PURE__ */ n("div", {
|
|
101
136
|
className: "flex flex-wrap gap-2",
|
|
102
|
-
children:
|
|
137
|
+
children: l.map((e) => /* @__PURE__ */ r("div", {
|
|
103
138
|
className: "flex items-center gap-1.5 px-2 py-1 rounded bg-bg-sunken",
|
|
104
139
|
children: [
|
|
105
140
|
/* @__PURE__ */ n("div", {
|
|
@@ -142,6 +177,6 @@ var a = e(function({ widget: e, data: a, onClick: o }) {
|
|
|
142
177
|
});
|
|
143
178
|
});
|
|
144
179
|
//#endregion
|
|
145
|
-
export {
|
|
180
|
+
export { s as default };
|
|
146
181
|
|
|
147
182
|
//# sourceMappingURL=TimelineWidget.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimelineWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/timeline/TimelineWidget.tsx"],"sourcesContent":["/**\n * TimelineWidget Component\n *\n * Simple timeline/gantt view showing project tasks and milestones.\n */\n\nimport { type FC, memo, useMemo } from 'react';\nimport type { Widget } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface TimelineWidgetProps {\n widget: Widget;\n data?: TimelineData;\n onClick?: (task: TimelineTask) => void;\n}\n\nexport interface TimelineTask {\n id: string;\n name: string;\n start: string;\n end: string;\n progress?: number;\n color?: string;\n duration?: number;\n dependencies?: string[];\n}\n\nexport interface TimelineMilestone {\n id: string;\n name: string;\n date: string;\n color?: string;\n}\n\nexport interface TimelineData {\n tasks?: TimelineTask[];\n milestones?: TimelineMilestone[];\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction getProgressColor(progress: number): string {\n if (progress >= 100) return 'bg-state-success';\n if (progress >= 50) return 'bg-action-primary-bg';\n if (progress > 0) return 'bg-state-warning';\n return 'bg-bg-sunken';\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const TimelineWidget: FC<TimelineWidgetProps> = memo(function TimelineWidget({\n widget: _widget,\n data,\n onClick,\n}) {\n const tasks = useMemo(() => data?.tasks || [], [data?.tasks]);\n const milestones = data?.milestones || [];\n\n // Calculate date range\n const { minDate, maxDate, totalDays } = useMemo(() => {\n if (!tasks.length) {\n return { minDate: new Date(), maxDate: new Date(), totalDays: 1 };\n }\n\n const dates = tasks.flatMap((t) => [new Date(t.start), new Date(t.end)]);\n const min = new Date(Math.min(...dates.map((d) => d.getTime())));\n const max = new Date(Math.max(...dates.map((d) => d.getTime())));\n const days = Math.ceil((max.getTime() - min.getTime()) / (1000 * 60 * 60 * 24)) + 1;\n\n return { minDate: min, maxDate: max, totalDays: days };\n }, [tasks]);\n\n if (!tasks.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\n className=\"w-12 h-12 mx-auto mb-2 text-text-tertiary\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n 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\"\n />\n </svg>\n <p>No timeline data</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4 overflow-auto\">\n {/* Header with date range */}\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-sm font-medium text-text-primary\">Project Timeline</h3>\n <span className=\"text-xs text-text-secondary\">\n {minDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })} -{' '}\n {maxDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}\n </span>\n </div>\n\n {/* Timeline content */}\n <div className=\"flex-1 space-y-3 overflow-auto\">\n {tasks.map((task) => {\n const startDate = new Date(task.start);\n const endDate = new Date(task.end);\n const taskDays = Math.ceil((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)) + 1;\n const startOffset = Math.ceil((startDate.getTime() - minDate.getTime()) / (1000 * 60 * 60 * 24));\n const progress = task.progress ?? 0;\n\n // Calculate bar position and width as percentages\n const leftPercent = (startOffset / totalDays) * 100;\n const widthPercent = (taskDays / totalDays) * 100;\n\n return (\n <div\n key={task.id}\n className={`\n ${onClick ? 'cursor-pointer' : ''}\n `}\n onClick={() => onClick?.(task)}\n >\n {/* Task name and progress */}\n <div className=\"flex items-center justify-between mb-1\">\n <span className=\"text-xs font-medium text-text-primary truncate max-w-[60%]\">{task.name}</span>\n <span className=\"text-[10px] text-text-secondary\">{progress}%</span>\n </div>\n\n {/* Timeline bar */}\n <div className=\"relative h-6 bg-bg-sunken rounded overflow-hidden\">\n {/* Task bar */}\n <div\n className=\"absolute top-1 bottom-1 rounded\"\n style={{\n left: `${leftPercent}%`,\n width: `${Math.max(widthPercent, 5)}%`,\n backgroundColor: task.color || 'var(--color-chart-1)',\n opacity: 0.3,\n }}\n />\n\n {/* Progress fill */}\n <div\n className={`absolute top-1 bottom-1 rounded ${getProgressColor(progress)}`}\n style={{\n left: `${leftPercent}%`,\n width: `${Math.max((widthPercent * progress) / 100, 2)}%`,\n }}\n />\n\n {/* Date labels */}\n <div\n className=\"absolute top-1/2 -translate-y-1/2 text-[9px] text-text-tertiary\"\n style={{ left: `${leftPercent + 1}%` }}\n >\n {startDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n\n {/* Milestones */}\n {milestones.length > 0 && (\n <div className=\"mt-4 pt-3 border-t border-border-default\">\n <p className=\"text-xs font-medium text-text-secondary mb-2\">Milestones</p>\n <div className=\"flex flex-wrap gap-2\">\n {milestones.map((milestone) => (\n <div key={milestone.id} className=\"flex items-center gap-1.5 px-2 py-1 rounded bg-bg-sunken\">\n <div\n className=\"w-2 h-2 rounded-full\"\n style={{ backgroundColor: milestone.color || 'var(--color-chart-negative)' }}\n />\n <span className=\"text-[10px] text-text-primary\">{milestone.name}</span>\n <span className=\"text-[10px] text-text-tertiary\">\n {new Date(milestone.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}\n </span>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n});\n\nexport default TimelineWidget;\n"],"mappings":";;;AA8CA,SAAS,EAAiB,GAA0B;AAIlD,QAHI,KAAY,MAAY,qBACxB,KAAY,KAAW,yBACvB,IAAW,IAAU,qBAClB;;AAOT,IAAa,IAA0C,EAAK,SAAwB,EAClF,QAAQ,GACR,SACA,cACC;CACD,IAAM,IAAQ,QAAc,GAAM,SAAS,EAAE,EAAE,CAAC,GAAM,MAAM,CAAC,EACvD,IAAa,GAAM,cAAc,EAAE,EAGnC,EAAE,YAAS,YAAS,iBAAc,QAAc;AACpD,MAAI,CAAC,EAAM,OACT,QAAO;GAAE,yBAAS,IAAI,MAAM;GAAE,yBAAS,IAAI,MAAM;GAAE,WAAW;GAAG;EAGnE,IAAM,IAAQ,EAAM,SAAS,MAAM,CAAC,IAAI,KAAK,EAAE,MAAM,EAAE,IAAI,KAAK,EAAE,IAAI,CAAC,CAAC,EAClE,IAAM,IAAI,KAAK,KAAK,IAAI,GAAG,EAAM,KAAK,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,EAC1D,IAAM,IAAI,KAAK,KAAK,IAAI,GAAG,EAAM,KAAK,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAGhE,SAAO;GAAE,SAAS;GAAK,SAAS;GAAK,WAFxB,KAAK,MAAM,EAAI,SAAS,GAAG,EAAI,SAAS,KAAK,MAAO,KAAK,KAAK,IAAI,GAAG;GAE5B;IACrD,CAAC,EAAM,CAAC;AAyBX,QAvBK,EAAM,SAwBT,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eAAwC;KAAqB,CAAA,EAC3E,kBAAC,QAAD;KAAM,WAAU;eAAhB;MACG,EAAQ,mBAAmB,SAAS;OAAE,OAAO;OAAS,KAAK;OAAW,CAAC;MAAC;MAAG;MAC3E,EAAQ,mBAAmB,SAAS;OAAE,OAAO;OAAS,KAAK;OAAW,CAAC;MACnE;OACH;;GAGN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAM,KAAK,MAAS;KACnB,IAAM,IAAY,IAAI,KAAK,EAAK,MAAM,EAChC,IAAU,IAAI,KAAK,EAAK,IAAI,EAC5B,IAAW,KAAK,MAAM,EAAQ,SAAS,GAAG,EAAU,SAAS,KAAK,MAAO,KAAK,KAAK,IAAI,GAAG,GAC1F,IAAc,KAAK,MAAM,EAAU,SAAS,GAAG,EAAQ,SAAS,KAAK,MAAO,KAAK,KAAK,IAAI,EAC1F,IAAW,EAAK,YAAY,GAG5B,IAAe,IAAc,IAAa,KAC1C,IAAgB,IAAW,IAAa;AAE9C,YACE,kBAAC,OAAD;MAEE,WAAW;oBACL,IAAU,mBAAmB,GAAG;;MAEtC,eAAe,IAAU,EAAK;gBALhC,CAQE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAA8D,EAAK;QAAY,CAAA,EAC/F,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CAAmD,GAAS,IAAQ;UAChE;UAGN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QAEE,kBAAC,OAAD;SACE,WAAU;SACV,OAAO;UACL,MAAM,GAAG,EAAY;UACrB,OAAO,GAAG,KAAK,IAAI,GAAc,EAAE,CAAC;UACpC,iBAAiB,EAAK,SAAS;UAC/B,SAAS;UACV;SACD,CAAA;QAGF,kBAAC,OAAD;SACE,WAAW,mCAAmC,EAAiB,EAAS;SACxE,OAAO;UACL,MAAM,GAAG,EAAY;UACrB,OAAO,GAAG,KAAK,IAAK,IAAe,IAAY,KAAK,EAAE,CAAC;UACxD;SACD,CAAA;QAGF,kBAAC,OAAD;SACE,WAAU;SACV,OAAO,EAAE,MAAM,GAAG,IAAc,EAAE,IAAI;mBAErC,EAAU,mBAAmB,SAAS;UAAE,OAAO;UAAS,KAAK;UAAW,CAAC;SACtE,CAAA;QACF;SACF;QA1CC,EAAK,GA0CN;MAER;IACE,CAAA;GAGL,EAAW,SAAS,KACnB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eAA+C;KAAc,CAAA,EAC1E,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAW,KAAK,MACf,kBAAC,OAAD;MAAwB,WAAU;gBAAlC;OACE,kBAAC,OAAD;QACE,WAAU;QACV,OAAO,EAAE,iBAAiB,EAAU,SAAS,+BAA+B;QAC5E,CAAA;OACF,kBAAC,QAAD;QAAM,WAAU;kBAAiC,EAAU;QAAY,CAAA;OACvE,kBAAC,QAAD;QAAM,WAAU;kBACb,IAAI,KAAK,EAAU,KAAK,CAAC,mBAAmB,SAAS;SAAE,OAAO;SAAS,KAAK;SAAW,CAAC;QACpF,CAAA;OACH;QATI,EAAU,GASd,CACN;KACE,CAAA,CACF;;GAEJ;MAlHJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IACE,WAAU;IACV,MAAK;IACL,QAAO;IACP,SAAQ;cAER,kBAAC,QAAD;KACE,eAAc;KACd,gBAAe;KACf,aAAa;KACb,GAAE;KACF,CAAA;IACE,CAAA,EACN,kBAAC,KAAD,EAAA,UAAG,oBAAoB,CAAA,CACnB;;EACF,CAAA;EAmGV"}
|
|
1
|
+
{"version":3,"file":"TimelineWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/timeline/TimelineWidget.tsx"],"sourcesContent":["/**\n * TimelineWidget Component\n *\n * Simple timeline/gantt view showing project tasks and milestones.\n */\n\nimport { type FC, memo, useMemo } from 'react';\nimport type { Widget } from '../../../types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface TimelineWidgetProps {\n widget: Widget;\n data?: TimelineData;\n onClick?: (task: TimelineTask) => void;\n}\n\nexport interface TimelineTask {\n id: string;\n name: string;\n start: string;\n end: string;\n progress?: number;\n color?: string;\n duration?: number;\n dependencies?: string[];\n}\n\nexport interface TimelineMilestone {\n id: string;\n name: string;\n date: string;\n color?: string;\n}\n\nexport interface TimelineData {\n tasks?: TimelineTask[];\n milestones?: TimelineMilestone[];\n}\n\n// ============================================================================\n// Normalization\n// ============================================================================\n\nfunction firstDefined<T = unknown>(obj: Record<string, unknown>, keys: string[]): T | undefined {\n for (const key of keys) {\n if (obj[key] !== undefined && obj[key] !== null) return obj[key] as T;\n }\n return undefined;\n}\n\n/**\n * Normalize whatever the widget is bound to into `TimelineTask[]`. Accepts the\n * documented `{ tasks, ... }` shape, a tasks array under a different common key,\n * or a bare array of task-like records — a DataSink parser naturally outputs a\n * bare array, and WidgetWrapper has no timeline-specific reshaping step (unlike\n * table/list/chart), so this widget has to tolerate it itself, the same way\n * MapWidget tolerates a bare marker array instead of requiring `{ markers }`.\n * Field names are matched against common aliases (e.g. a parser emitting\n * `title` instead of `name`) so one exact parser output shape isn't required.\n */\nexport function normalizeTimelineTasks(\n data: TimelineData | TimelineTask[] | Record<string, unknown> | undefined\n): TimelineTask[] {\n const rawTasks: unknown = Array.isArray(data)\n ? data\n : Array.isArray((data as TimelineData | undefined)?.tasks)\n ? (data as TimelineData).tasks\n : Array.isArray((data as Record<string, unknown> | undefined)?.items)\n ? (data as Record<string, unknown>).items\n : Array.isArray((data as Record<string, unknown> | undefined)?.data)\n ? (data as Record<string, unknown>).data\n : undefined;\n\n if (!Array.isArray(rawTasks)) return [];\n\n return rawTasks\n .map((raw, i): TimelineTask | null => {\n if (!raw || typeof raw !== 'object') return null;\n const r = raw as Record<string, unknown>;\n const start = firstDefined<string>(r, ['start', 'startDate', 'date']);\n const end = firstDefined<string>(r, ['end', 'endDate']) ?? start;\n if (!start || !end) return null;\n const name = firstDefined<string>(r, ['name', 'title', 'label', 'task']) ?? 'Untitled task';\n const id = firstDefined<string>(r, ['id', 'taskId', '_id']) ?? `${name}-${start}-${i}`;\n return {\n id: String(id),\n name: String(name),\n start: String(start),\n end: String(end),\n progress: typeof r.progress === 'number' ? r.progress : undefined,\n color: typeof r.color === 'string' ? r.color : undefined,\n duration: typeof r.duration === 'number' ? r.duration : undefined,\n dependencies: Array.isArray(r.dependencies) ? (r.dependencies as string[]) : undefined,\n };\n })\n .filter((t): t is TimelineTask => t !== null);\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction getProgressColor(progress: number): string {\n if (progress >= 100) return 'bg-state-success';\n if (progress >= 50) return 'bg-action-primary-bg';\n if (progress > 0) return 'bg-state-warning';\n return 'bg-bg-sunken';\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const TimelineWidget: FC<TimelineWidgetProps> = memo(function TimelineWidget({\n widget: _widget,\n data,\n onClick,\n}) {\n const tasks = useMemo(() => normalizeTimelineTasks(data), [data]);\n const milestones = data?.milestones || [];\n\n // Calculate date range\n const { minDate, maxDate, totalDays } = useMemo(() => {\n if (!tasks.length) {\n return { minDate: new Date(), maxDate: new Date(), totalDays: 1 };\n }\n\n const dates = tasks.flatMap((t) => [new Date(t.start), new Date(t.end)]);\n const min = new Date(Math.min(...dates.map((d) => d.getTime())));\n const max = new Date(Math.max(...dates.map((d) => d.getTime())));\n const days = Math.ceil((max.getTime() - min.getTime()) / (1000 * 60 * 60 * 24)) + 1;\n\n return { minDate: min, maxDate: max, totalDays: days };\n }, [tasks]);\n\n if (!tasks.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\n className=\"w-12 h-12 mx-auto mb-2 text-text-tertiary\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={1.5}\n 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\"\n />\n </svg>\n <p>No timeline data</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full p-4 overflow-auto\">\n {/* Header with date range */}\n <div className=\"flex items-center justify-between mb-4\">\n <h3 className=\"text-sm font-medium text-text-primary\">Project Timeline</h3>\n <span className=\"text-xs text-text-secondary\">\n {minDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })} -{' '}\n {maxDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}\n </span>\n </div>\n\n {/* Timeline content */}\n <div className=\"flex-1 space-y-3 overflow-auto\">\n {tasks.map((task) => {\n const startDate = new Date(task.start);\n const endDate = new Date(task.end);\n const taskDays = Math.ceil((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)) + 1;\n const startOffset = Math.ceil((startDate.getTime() - minDate.getTime()) / (1000 * 60 * 60 * 24));\n const progress = task.progress ?? 0;\n\n // Calculate bar position and width as percentages\n const leftPercent = (startOffset / totalDays) * 100;\n const widthPercent = (taskDays / totalDays) * 100;\n\n return (\n <div\n key={task.id}\n className={`\n ${onClick ? 'cursor-pointer' : ''}\n `}\n onClick={() => onClick?.(task)}\n >\n {/* Task name and progress */}\n <div className=\"flex items-center justify-between mb-1\">\n <span className=\"text-xs font-medium text-text-primary truncate max-w-[60%]\">{task.name}</span>\n <span className=\"text-[10px] text-text-secondary\">{progress}%</span>\n </div>\n\n {/* Timeline bar */}\n <div className=\"relative h-6 bg-bg-sunken rounded overflow-hidden\">\n {/* Task bar */}\n <div\n className=\"absolute top-1 bottom-1 rounded\"\n style={{\n left: `${leftPercent}%`,\n width: `${Math.max(widthPercent, 5)}%`,\n backgroundColor: task.color || 'var(--color-chart-1)',\n opacity: 0.3,\n }}\n />\n\n {/* Progress fill */}\n <div\n className={`absolute top-1 bottom-1 rounded ${getProgressColor(progress)}`}\n style={{\n left: `${leftPercent}%`,\n width: `${Math.max((widthPercent * progress) / 100, 2)}%`,\n }}\n />\n\n {/* Date labels */}\n <div\n className=\"absolute top-1/2 -translate-y-1/2 text-[9px] text-text-tertiary\"\n style={{ left: `${leftPercent + 1}%` }}\n >\n {startDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n\n {/* Milestones */}\n {milestones.length > 0 && (\n <div className=\"mt-4 pt-3 border-t border-border-default\">\n <p className=\"text-xs font-medium text-text-secondary mb-2\">Milestones</p>\n <div className=\"flex flex-wrap gap-2\">\n {milestones.map((milestone) => (\n <div key={milestone.id} className=\"flex items-center gap-1.5 px-2 py-1 rounded bg-bg-sunken\">\n <div\n className=\"w-2 h-2 rounded-full\"\n style={{ backgroundColor: milestone.color || 'var(--color-chart-negative)' }}\n />\n <span className=\"text-[10px] text-text-primary\">{milestone.name}</span>\n <span className=\"text-[10px] text-text-tertiary\">\n {new Date(milestone.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}\n </span>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n );\n});\n\nexport default TimelineWidget;\n"],"mappings":"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"}
|
|
@@ -12,27 +12,33 @@ var m = {
|
|
|
12
12
|
y: 0,
|
|
13
13
|
width: 6,
|
|
14
14
|
height: 4
|
|
15
|
-
}, h = i(function({ isOpen: i, onClose:
|
|
16
|
-
let { dataSinks:
|
|
17
|
-
consoleId:
|
|
15
|
+
}, h = "default", g = i(function({ isOpen: i, onClose: g, dashboardId: _, pageId: v, source: y }) {
|
|
16
|
+
let { dataSinks: b } = t({ skipFetch: !i }), { createWidget: x } = e(v, _, { skipFetch: !0 }), S = _ === h, { parsers: C, executeParser: w } = n({
|
|
17
|
+
consoleId: _,
|
|
18
18
|
skipFetch: !i
|
|
19
|
-
}), {
|
|
19
|
+
}), { parsers: T } = n({
|
|
20
|
+
consoleId: h,
|
|
21
|
+
skipFetch: !i || S
|
|
22
|
+
}), E = o(() => b.map((e) => ({
|
|
20
23
|
id: e.id,
|
|
21
24
|
key: e.key,
|
|
22
25
|
name: e.name
|
|
23
|
-
})), [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
})), [b]), D = o(() => {
|
|
27
|
+
let e = new Set(C.map((e) => e.id));
|
|
28
|
+
return [...C, ...T.filter((t) => !e.has(t.id))].map((e) => ({
|
|
29
|
+
id: e.id,
|
|
30
|
+
name: e.name,
|
|
31
|
+
inputSinkKey: e.inputSinkKey
|
|
32
|
+
}));
|
|
33
|
+
}, [C, T]), O = a(async (e) => {
|
|
28
34
|
if (!e.binding.parserId) return;
|
|
29
|
-
let t = await
|
|
35
|
+
let t = await w(e.binding.parserId);
|
|
30
36
|
if (!t || !t.success) throw Error(t?.error || "Could not resolve the binding.");
|
|
31
37
|
return t.output;
|
|
32
|
-
}, [
|
|
33
|
-
if (!await
|
|
34
|
-
pageId:
|
|
35
|
-
dashboardId:
|
|
38
|
+
}, [w]), k = a(async (e) => {
|
|
39
|
+
if (!await x({
|
|
40
|
+
pageId: v,
|
|
41
|
+
dashboardId: _,
|
|
36
42
|
type: e.widgetType,
|
|
37
43
|
title: e.presentation.title,
|
|
38
44
|
description: e.presentation.description,
|
|
@@ -48,13 +54,13 @@ var m = {
|
|
|
48
54
|
position: { ...m }
|
|
49
55
|
})) throw Error("The widget could not be created.");
|
|
50
56
|
}, [
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
x,
|
|
58
|
+
v,
|
|
59
|
+
_
|
|
54
60
|
]);
|
|
55
61
|
return /* @__PURE__ */ f(s, {
|
|
56
62
|
open: i,
|
|
57
|
-
onOpenChange: (e) => !e &&
|
|
63
|
+
onOpenChange: (e) => !e && g(),
|
|
58
64
|
children: /* @__PURE__ */ p(c, {
|
|
59
65
|
style: {
|
|
60
66
|
width: 480,
|
|
@@ -75,18 +81,18 @@ var m = {
|
|
|
75
81
|
minHeight: 0
|
|
76
82
|
},
|
|
77
83
|
children: /* @__PURE__ */ f(r, {
|
|
78
|
-
sinks:
|
|
79
|
-
parsers:
|
|
80
|
-
resolvePreviewData:
|
|
81
|
-
onApprove:
|
|
82
|
-
onClose:
|
|
83
|
-
source:
|
|
84
|
+
sinks: E,
|
|
85
|
+
parsers: D,
|
|
86
|
+
resolvePreviewData: O,
|
|
87
|
+
onApprove: k,
|
|
88
|
+
onClose: g,
|
|
89
|
+
source: y
|
|
84
90
|
})
|
|
85
91
|
})]
|
|
86
92
|
})
|
|
87
93
|
});
|
|
88
94
|
});
|
|
89
95
|
//#endregion
|
|
90
|
-
export {
|
|
96
|
+
export { g as default };
|
|
91
97
|
|
|
92
98
|
//# sourceMappingURL=CustomWidgetBuilderSheet.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CustomWidgetBuilderSheet.js","names":[],"sources":["../../../src/bigconsole/customWidget/CustomWidgetBuilderSheet.tsx"],"sourcesContent":["/**\n * Container that wires the custom-widget proposal panel to real workspace data\n * (BOFF-5532).\n *\n * Supplies the panel with the user's actual permitted DataSinks and parsers,\n * resolves previews by running the real parser, and persists an approved\n * proposal through the normal `createWidget` path — so an approved widget is an\n * ordinary widget, editable afterwards through the standard inspector.\n */\n\nimport { type FC, memo, useCallback, useMemo } from 'react';\nimport { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '@burdenoff/fe-libs/ui';\n\nimport { useDataSinkOperations, useParserOperations, useWidgetOperations } from '../hooks';\nimport { CustomWidgetProposalPanel } from './CustomWidgetProposalPanel';\nimport type { CustomWidgetProposal } from './proposalContract';\nimport type { CustomWidgetProposalSource, ProposalCandidateParser, ProposalCandidateSink } from './proposalSource';\n\nexport interface CustomWidgetBuilderSheetProps {\n isOpen: boolean;\n onClose: () => void;\n dashboardId: string;\n pageId: string;\n /** Swap for the GraphQL-backed source once BOFF-3021's backend half lands. */\n source?: CustomWidgetProposalSource;\n}\n\n/** Grid slot for an approved widget; the reviewer can move it afterwards. */\nconst DEFAULT_POSITION = { x: 0, y: 0, width: 6, height: 4 } as const;\n\nexport const CustomWidgetBuilderSheet: FC<CustomWidgetBuilderSheetProps> = memo(function CustomWidgetBuilderSheet({\n isOpen,\n onClose,\n dashboardId,\n pageId,\n source,\n}) {\n const { dataSinks } = useDataSinkOperations({ skipFetch: !isOpen });\n const { parsers, executeParser } = useParserOperations({
|
|
1
|
+
{"version":3,"file":"CustomWidgetBuilderSheet.js","names":[],"sources":["../../../src/bigconsole/customWidget/CustomWidgetBuilderSheet.tsx"],"sourcesContent":["/**\n * Container that wires the custom-widget proposal panel to real workspace data\n * (BOFF-5532).\n *\n * Supplies the panel with the user's actual permitted DataSinks and parsers,\n * resolves previews by running the real parser, and persists an approved\n * proposal through the normal `createWidget` path — so an approved widget is an\n * ordinary widget, editable afterwards through the standard inspector.\n */\n\nimport { type FC, memo, useCallback, useMemo } from 'react';\nimport { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '@burdenoff/fe-libs/ui';\n\nimport { useDataSinkOperations, useParserOperations, useWidgetOperations } from '../hooks';\nimport { CustomWidgetProposalPanel } from './CustomWidgetProposalPanel';\nimport type { CustomWidgetProposal } from './proposalContract';\nimport type { CustomWidgetProposalSource, ProposalCandidateParser, ProposalCandidateSink } from './proposalSource';\n\nexport interface CustomWidgetBuilderSheetProps {\n isOpen: boolean;\n onClose: () => void;\n dashboardId: string;\n pageId: string;\n /** Swap for the GraphQL-backed source once BOFF-3021's backend half lands. */\n source?: CustomWidgetProposalSource;\n}\n\n/** Grid slot for an approved widget; the reviewer can move it afterwards. */\nconst DEFAULT_POSITION = { x: 0, y: 0, width: 6, height: 4 } as const;\n\n/** Sentinel consoleId under which workspace-global parsers are stored. */\nconst GLOBAL_CONSOLE_ID = 'default';\n\nexport const CustomWidgetBuilderSheet: FC<CustomWidgetBuilderSheetProps> = memo(function CustomWidgetBuilderSheet({\n isOpen,\n onClose,\n dashboardId,\n pageId,\n source,\n}) {\n const { dataSinks } = useDataSinkOperations({ skipFetch: !isOpen });\n const { createWidget } = useWidgetOperations(pageId, dashboardId, { skipFetch: true });\n\n // Parsers are either dashboard-scoped or workspace-global (`consoleId: 'default'`),\n // and the widget inspector offers both. Fetch both here too, otherwise a proposal\n // can never bind to a global parser — which is what the parser builder creates by\n // default, so the picker would look empty in a typical workspace.\n const isGlobalConsole = dashboardId === GLOBAL_CONSOLE_ID;\n const { parsers: dashboardParsers, executeParser } = useParserOperations({\n consoleId: dashboardId,\n skipFetch: !isOpen,\n });\n const { parsers: globalParsers } = useParserOperations({\n consoleId: GLOBAL_CONSOLE_ID,\n skipFetch: !isOpen || isGlobalConsole,\n });\n\n const sinks: ProposalCandidateSink[] = useMemo(\n () => dataSinks.map((sink) => ({ id: sink.id, key: sink.key, name: sink.name })),\n [dataSinks]\n );\n\n const parserCandidates: ProposalCandidateParser[] = useMemo(() => {\n const seen = new Set(dashboardParsers.map((parser) => parser.id));\n const merged = [...dashboardParsers, ...globalParsers.filter((parser) => !seen.has(parser.id))];\n return merged.map((parser) => ({ id: parser.id, name: parser.name, inputSinkKey: parser.inputSinkKey }));\n }, [dashboardParsers, globalParsers]);\n\n /**\n * Resolves what the binding actually produces. Rejects on failure so the\n * panel can distinguish \"no data\" from \"you cannot read this\".\n */\n const resolvePreviewData = useCallback(\n async (proposal: CustomWidgetProposal): Promise<unknown> => {\n if (!proposal.binding.parserId) return undefined;\n const execution = await executeParser(proposal.binding.parserId);\n if (!execution || !execution.success) {\n throw new Error(execution?.error || 'Could not resolve the binding.');\n }\n return execution.output;\n },\n [executeParser]\n );\n\n const handleApprove = useCallback(\n async (proposal: CustomWidgetProposal) => {\n const widget = await createWidget({\n pageId,\n dashboardId,\n type: proposal.widgetType,\n title: proposal.presentation.title,\n description: proposal.presentation.description,\n dataSinkId: proposal.binding.dataSinkId || undefined,\n parserId: proposal.binding.parserId,\n config: {\n ...proposal.presentation.config,\n ...proposal.fieldMapping,\n // Provenance, so an approved widget can be traced back to its prompt\n // and survives export/import and clone with its origin intact.\n generatedBy: proposal.generatedBy,\n sourcePrompt: proposal.sourcePrompt,\n proposalVersion: proposal.version,\n },\n position: { ...DEFAULT_POSITION },\n });\n\n if (!widget) throw new Error('The widget could not be created.');\n },\n [createWidget, pageId, dashboardId]\n );\n\n return (\n <Sheet open={isOpen} onOpenChange={(open) => !open && onClose()}>\n <SheetContent style={{ width: 480, maxWidth: '95vw', padding: 0, display: 'flex', flexDirection: 'column' }}>\n <SheetHeader style={{ padding: 16, paddingBottom: 0 }}>\n <SheetTitle>AI custom widget</SheetTitle>\n <SheetDescription>\n Describe a widget, review the proposal, then approve it onto the dashboard.\n </SheetDescription>\n </SheetHeader>\n\n <div style={{ flex: 1, minHeight: 0 }}>\n <CustomWidgetProposalPanel\n sinks={sinks}\n parsers={parserCandidates}\n resolvePreviewData={resolvePreviewData}\n onApprove={handleApprove}\n onClose={onClose}\n source={source}\n />\n </div>\n </SheetContent>\n </Sheet>\n );\n});\n\nexport default CustomWidgetBuilderSheet;\n"],"mappings":";;;;;;;;;AA4BA,IAAM,IAAmB;CAAE,GAAG;CAAG,GAAG;CAAG,OAAO;CAAG,QAAQ;CAAG,EAGtD,IAAoB,WAEb,IAA8D,EAAK,SAAkC,EAChH,WACA,YACA,gBACA,WACA,aACC;CACD,IAAM,EAAE,iBAAc,EAAsB,EAAE,WAAW,CAAC,GAAQ,CAAC,EAC7D,EAAE,oBAAiB,EAAoB,GAAQ,GAAa,EAAE,WAAW,IAAM,CAAC,EAMhF,IAAkB,MAAgB,GAClC,EAAE,SAAS,GAAkB,qBAAkB,EAAoB;EACvE,WAAW;EACX,WAAW,CAAC;EACb,CAAC,EACI,EAAE,SAAS,MAAkB,EAAoB;EACrD,WAAW;EACX,WAAW,CAAC,KAAU;EACvB,CAAC,EAEI,IAAiC,QAC/B,EAAU,KAAK,OAAU;EAAE,IAAI,EAAK;EAAI,KAAK,EAAK;EAAK,MAAM,EAAK;EAAM,EAAE,EAChF,CAAC,EAAU,CACZ,EAEK,IAA8C,QAAc;EAChE,IAAM,IAAO,IAAI,IAAI,EAAiB,KAAK,MAAW,EAAO,GAAG,CAAC;AAEjE,SADe,CAAC,GAAG,GAAkB,GAAG,EAAc,QAAQ,MAAW,CAAC,EAAK,IAAI,EAAO,GAAG,CAAC,CAAC,CACjF,KAAK,OAAY;GAAE,IAAI,EAAO;GAAI,MAAM,EAAO;GAAM,cAAc,EAAO;GAAc,EAAE;IACvG,CAAC,GAAkB,EAAc,CAAC,EAM/B,IAAqB,EACzB,OAAO,MAAqD;AAC1D,MAAI,CAAC,EAAS,QAAQ,SAAU;EAChC,IAAM,IAAY,MAAM,EAAc,EAAS,QAAQ,SAAS;AAChE,MAAI,CAAC,KAAa,CAAC,EAAU,QAC3B,OAAU,MAAM,GAAW,SAAS,iCAAiC;AAEvE,SAAO,EAAU;IAEnB,CAAC,EAAc,CAChB,EAEK,IAAgB,EACpB,OAAO,MAAmC;AAqBxC,MAAI,CApBW,MAAM,EAAa;GAChC;GACA;GACA,MAAM,EAAS;GACf,OAAO,EAAS,aAAa;GAC7B,aAAa,EAAS,aAAa;GACnC,YAAY,EAAS,QAAQ,cAAc,KAAA;GAC3C,UAAU,EAAS,QAAQ;GAC3B,QAAQ;IACN,GAAG,EAAS,aAAa;IACzB,GAAG,EAAS;IAGZ,aAAa,EAAS;IACtB,cAAc,EAAS;IACvB,iBAAiB,EAAS;IAC3B;GACD,UAAU,EAAE,GAAG,GAAkB;GAClC,CAAC,CAEW,OAAU,MAAM,mCAAmC;IAElE;EAAC;EAAc;EAAQ;EAAY,CACpC;AAED,QACE,kBAAC,GAAD;EAAO,MAAM;EAAQ,eAAe,MAAS,CAAC,KAAQ,GAAS;YAC7D,kBAAC,GAAD;GAAc,OAAO;IAAE,OAAO;IAAK,UAAU;IAAQ,SAAS;IAAG,SAAS;IAAQ,eAAe;IAAU;aAA3G,CACE,kBAAC,GAAD;IAAa,OAAO;KAAE,SAAS;KAAI,eAAe;KAAG;cAArD,CACE,kBAAC,GAAD,EAAA,UAAY,oBAA6B,CAAA,EACzC,kBAAC,GAAD,EAAA,UAAkB,+EAEC,CAAA,CACP;OAEd,kBAAC,OAAD;IAAK,OAAO;KAAE,MAAM;KAAG,WAAW;KAAG;cACnC,kBAAC,GAAD;KACS;KACP,SAAS;KACW;KACpB,WAAW;KACF;KACD;KACR,CAAA;IACE,CAAA,CACO;;EACT,CAAA;EAEV"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-bigconsole",
|
|
3
|
-
"version": "2026.727.
|
|
3
|
+
"version": "2026.727.4",
|
|
4
4
|
"description": "BigConsole - AI-powered analytics and dashboard platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -93,6 +93,7 @@
|
|
|
93
93
|
"@vitejs/plugin-react": "^4.3.4",
|
|
94
94
|
"@storybook/addon-essentials": "^8.6.0",
|
|
95
95
|
"@storybook/addon-interactions": "^8.6.0",
|
|
96
|
+
"@storybook/react": "^8.6.0",
|
|
96
97
|
"@storybook/react-vite": "^8.6.0",
|
|
97
98
|
"@storybook/test": "^8.6.0",
|
|
98
99
|
"@storybook/test-runner": "0.22.0",
|