@burdenoff/microfe-bigconsole 2026.716.2 → 2026.716.3

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.
@@ -1,9 +1,10 @@
1
- import e from "../../hooks/useBigConsoleAnalytics.js";
2
- import t from "../../hooks/useImportDashboard.js";
1
+ import { reportBigConsoleQuotaError as e } from "../../utils/quota.js";
2
+ import t from "../../hooks/useBigConsoleAnalytics.js";
3
+ import n from "../../hooks/useImportDashboard.js";
3
4
  import "../../hooks/index.js";
4
- import { memo as n, useCallback as r, useEffect as i, useRef as a, useState as o } from "react";
5
- import { AlertTriangle as s, ArrowLeft as ee, Check as c, FileJson as l, Info as u, RefreshCw as d, Upload as f, XCircle as p } from "lucide-react";
6
- import { Badge as m, Button as h, Card as g, CardContent as _, Checkbox as v, Dialog as y, DialogContent as b, DialogDescription as x, DialogFooter as S, DialogHeader as C, DialogTitle as w, Input as te, Label as T, Spinner as E } from "@burdenoff/fe-libs/ui";
5
+ import { memo as r, useCallback as i, useEffect as a, useRef as o, useState as s } from "react";
6
+ import { AlertTriangle as c, ArrowLeft as ee, Check as l, FileJson as u, Info as d, RefreshCw as f, Upload as p, XCircle as m } from "lucide-react";
7
+ import { Badge as h, Button as g, Card as _, CardContent as v, Checkbox as te, Dialog as y, DialogContent as b, DialogDescription as x, DialogFooter as S, DialogHeader as C, DialogTitle as w, Input as T, Label as E, Spinner as ne } from "@burdenoff/fe-libs/ui";
7
8
  import { Fragment as D, jsx as O, jsxs as k } from "react/jsx-runtime";
8
9
  //#region src/bigconsole/components/dashboard/ImportDashboardDialog.tsx
9
10
  function A(e) {
@@ -15,12 +16,12 @@ function A(e) {
15
16
  return null;
16
17
  }
17
18
  }
18
- var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
19
- let [N, P] = o("select"), [, F] = o(null), [I, L] = o(null), [R, z] = o(""), [B, V] = o("PRIVATE"), [H, U] = o(!1), W = a(null), { loading: G, error: K, validationResult: q, importResult: J, validateImport: Y, importDashboard: X, clearState: Z } = t(), Q = e();
20
- i(() => {
21
- n && (P("select"), F(null), L(null), z(""), V("PRIVATE"), U(!1), Z());
22
- }, [n, Z]);
23
- let ne = r(async (e) => {
19
+ var j = r(function({ isOpen: r, onClose: j, onSuccess: M }) {
20
+ let [N, P] = s("select"), [, F] = s(null), [I, L] = s(null), [R, z] = s(""), [B, V] = s("PRIVATE"), [H, U] = s(!1), W = o(null), { loading: G, error: K, validationResult: q, importResult: J, validateImport: Y, importDashboard: X, clearState: Z } = n(), Q = t();
21
+ a(() => {
22
+ r && (P("select"), F(null), L(null), z(""), V("PRIVATE"), U(!1), Z());
23
+ }, [r, Z]);
24
+ let re = i(async (e) => {
24
25
  let t = e.target.files?.[0];
25
26
  if (!t) return;
26
27
  if (!t.name.endsWith(".json")) {
@@ -37,39 +38,48 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
37
38
  }, n.onerror = () => {
38
39
  alert("Failed to read file");
39
40
  }, n.readAsText(t);
40
- }, [Y]), re = r((e) => {
41
+ }, [Y]), ie = i((e) => {
41
42
  e.preventDefault();
42
43
  let t = e.dataTransfer.files?.[0];
43
44
  if (t) {
44
45
  let e = new DataTransfer();
45
46
  e.items.add(t), W.current && (W.current.files = e.files, W.current.dispatchEvent(new Event("change", { bubbles: !0 })));
46
47
  }
47
- }, []), ie = r(async () => {
48
+ }, []), ae = i(async () => {
48
49
  if (!I) return;
49
50
  P("importing");
50
- let e = {
51
+ let t = {
51
52
  name: R.trim() || void 0,
52
53
  visibility: B,
53
54
  skipWarnings: H
54
55
  };
55
56
  Q("bigconsole.dashboard.import_clicked", { source: "import" });
56
- let t = await X(I, e);
57
- t?.success && t.dashboard ? (P("complete"), Q("bigconsole.dashboard.imported", {
58
- dashboardId: t.dashboard.id,
59
- source: "import",
60
- originalUpdated: t.originalUpdated ?? !1
61
- }), M?.(t.dashboard.id, t.dashboard.name, t.originalUpdated ?? !1)) : P("preview");
57
+ try {
58
+ let e = await X(I, t);
59
+ e?.success && e.dashboard ? (P("complete"), Q("bigconsole.dashboard.imported", {
60
+ dashboardId: e.dashboard.id,
61
+ source: "import",
62
+ originalUpdated: e.originalUpdated ?? !1
63
+ }), M?.(e.dashboard.id, e.dashboard.name, e.originalUpdated ?? !1)) : P("preview");
64
+ } catch (t) {
65
+ if (e(t, "project")) {
66
+ j();
67
+ return;
68
+ }
69
+ P("preview");
70
+ }
62
71
  }, [
63
72
  Q,
64
73
  I,
65
74
  X,
66
75
  R,
76
+ j,
67
77
  M,
68
78
  H,
69
79
  B
70
80
  ]), $ = !G && N !== "importing";
71
81
  return /* @__PURE__ */ O(y, {
72
- open: n,
82
+ open: r,
73
83
  onOpenChange: (e) => !e && $ && j(),
74
84
  children: /* @__PURE__ */ k(b, {
75
85
  className: "sm:max-w-lg max-h-[85vh] flex flex-col",
@@ -78,7 +88,7 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
78
88
  className: "flex items-center gap-3",
79
89
  children: [/* @__PURE__ */ O("div", {
80
90
  className: "flex size-10 items-center justify-center rounded-lg bg-action-primary-bg/10",
81
- children: /* @__PURE__ */ O(f, { className: "size-5 text-primary" })
91
+ children: /* @__PURE__ */ O(p, { className: "size-5 text-primary" })
82
92
  }), /* @__PURE__ */ O(w, { children: "Import Dashboard" })]
83
93
  }), /* @__PURE__ */ O(x, { children: "Upload a Dashboard export file to restore it into this workspace." })] }),
84
94
  /* @__PURE__ */ k("div", {
@@ -86,10 +96,10 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
86
96
  children: [
87
97
  N === "select" && /* @__PURE__ */ k("div", {
88
98
  onDragOver: (e) => e.preventDefault(),
89
- onDrop: re,
99
+ onDrop: ie,
90
100
  className: "border-2 border-dashed rounded-lg p-8 text-center hover:border-primary transition-colors",
91
101
  children: [
92
- /* @__PURE__ */ O(l, { className: "size-12 mx-auto text-text-secondary mb-4" }),
102
+ /* @__PURE__ */ O(u, { className: "size-12 mx-auto text-text-secondary mb-4" }),
93
103
  /* @__PURE__ */ O("p", {
94
104
  className: "text-sm mb-2",
95
105
  children: "Drag and drop a dashboard export file here"
@@ -102,10 +112,10 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
102
112
  ref: W,
103
113
  type: "file",
104
114
  accept: ".json",
105
- onChange: ne,
115
+ onChange: re,
106
116
  className: "hidden"
107
117
  }),
108
- /* @__PURE__ */ O(h, {
118
+ /* @__PURE__ */ O(g, {
109
119
  onClick: () => W.current?.click(),
110
120
  disabled: G,
111
121
  children: "Browse Files"
@@ -117,7 +127,7 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
117
127
  ]
118
128
  }),
119
129
  (N === "preview" || N === "importing") && q && /* @__PURE__ */ k(D, { children: [
120
- /* @__PURE__ */ O(g, { children: /* @__PURE__ */ k(_, {
130
+ /* @__PURE__ */ O(_, { children: /* @__PURE__ */ k(v, {
121
131
  className: "p-4 space-y-2",
122
132
  children: [
123
133
  /* @__PURE__ */ k("div", {
@@ -145,7 +155,7 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
145
155
  children: [/* @__PURE__ */ O("span", {
146
156
  className: "text-sm text-text-secondary",
147
157
  children: "Pages"
148
- }), /* @__PURE__ */ O(m, {
158
+ }), /* @__PURE__ */ O(h, {
149
159
  variant: "secondary",
150
160
  children: q.dashboardInfo.pageCount
151
161
  })]
@@ -155,7 +165,7 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
155
165
  children: [/* @__PURE__ */ O("span", {
156
166
  className: "text-sm text-text-secondary",
157
167
  children: "Widgets"
158
- }), /* @__PURE__ */ O(m, {
168
+ }), /* @__PURE__ */ O(h, {
159
169
  variant: "secondary",
160
170
  children: q.dashboardInfo.widgetCount
161
171
  })]
@@ -182,13 +192,13 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
182
192
  })
183
193
  ]
184
194
  }) }),
185
- q.errors.length > 0 && /* @__PURE__ */ O(g, {
195
+ q.errors.length > 0 && /* @__PURE__ */ O(_, {
186
196
  className: "border-status-error-border/50 bg-status-error-bg/5",
187
- children: /* @__PURE__ */ k(_, {
197
+ children: /* @__PURE__ */ k(v, {
188
198
  className: "p-3",
189
199
  children: [/* @__PURE__ */ k("div", {
190
200
  className: "flex items-center gap-2 mb-2",
191
- children: [/* @__PURE__ */ O(p, { className: "size-4 text-status-error-text" }), /* @__PURE__ */ k("span", {
201
+ children: [/* @__PURE__ */ O(m, { className: "size-4 text-status-error-text" }), /* @__PURE__ */ k("span", {
192
202
  className: "text-sm font-medium text-status-error-text",
193
203
  children: [
194
204
  "Errors (",
@@ -213,13 +223,13 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
213
223
  })]
214
224
  })
215
225
  }),
216
- q.warnings.length > 0 && /* @__PURE__ */ O(g, {
226
+ q.warnings.length > 0 && /* @__PURE__ */ O(_, {
217
227
  className: "border-yellow-500/50 bg-yellow-500/5",
218
- children: /* @__PURE__ */ k(_, {
228
+ children: /* @__PURE__ */ k(v, {
219
229
  className: "p-3",
220
230
  children: [/* @__PURE__ */ k("div", {
221
231
  className: "flex items-center gap-2 mb-2",
222
- children: [/* @__PURE__ */ O(s, { className: "size-4 text-yellow-600" }), /* @__PURE__ */ k("span", {
232
+ children: [/* @__PURE__ */ O(c, { className: "size-4 text-yellow-600" }), /* @__PURE__ */ k("span", {
223
233
  className: "text-sm font-medium text-yellow-600",
224
234
  children: [
225
235
  "Warnings (",
@@ -251,13 +261,13 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
251
261
  className: "text-sm font-medium",
252
262
  children: "Import Options"
253
263
  }),
254
- /* @__PURE__ */ O(g, {
264
+ /* @__PURE__ */ O(_, {
255
265
  className: "border-blue-500/50 bg-blue-500/5",
256
- children: /* @__PURE__ */ O(_, {
266
+ children: /* @__PURE__ */ O(v, {
257
267
  className: "p-3",
258
268
  children: /* @__PURE__ */ k("div", {
259
269
  className: "flex items-start gap-2",
260
- children: [/* @__PURE__ */ O(u, { className: "size-4 text-blue-600 mt-0.5 shrink-0" }), /* @__PURE__ */ O("div", {
270
+ children: [/* @__PURE__ */ O(d, { className: "size-4 text-blue-600 mt-0.5 shrink-0" }), /* @__PURE__ */ O("div", {
261
271
  className: "text-xs text-blue-700 dark:text-blue-400",
262
272
  children: A(I) ? /* @__PURE__ */ k(D, { children: [
263
273
  /* @__PURE__ */ O("p", {
@@ -291,10 +301,10 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
291
301
  }),
292
302
  /* @__PURE__ */ k("div", {
293
303
  className: "space-y-2",
294
- children: [/* @__PURE__ */ O(T, {
304
+ children: [/* @__PURE__ */ O(E, {
295
305
  htmlFor: "import-name",
296
306
  children: "Dashboard Name"
297
- }), /* @__PURE__ */ O(te, {
307
+ }), /* @__PURE__ */ O(T, {
298
308
  id: "import-name",
299
309
  type: "text",
300
310
  value: R,
@@ -305,13 +315,13 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
305
315
  }),
306
316
  /* @__PURE__ */ k("div", {
307
317
  className: "space-y-2",
308
- children: [/* @__PURE__ */ O(T, { children: "Visibility" }), /* @__PURE__ */ O("div", {
318
+ children: [/* @__PURE__ */ O(E, { children: "Visibility" }), /* @__PURE__ */ O("div", {
309
319
  className: "flex gap-2",
310
320
  children: [
311
321
  "PRIVATE",
312
322
  "TEAM",
313
323
  "PUBLIC"
314
- ].map((e) => /* @__PURE__ */ O(h, {
324
+ ].map((e) => /* @__PURE__ */ O(g, {
315
325
  type: "button",
316
326
  variant: B === e ? "default" : "outline",
317
327
  size: "sm",
@@ -323,14 +333,14 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
323
333
  }),
324
334
  q.warnings.length > 0 && /* @__PURE__ */ k("div", {
325
335
  className: "flex items-start space-x-3",
326
- children: [/* @__PURE__ */ O(v, {
336
+ children: [/* @__PURE__ */ O(te, {
327
337
  id: "skipWarnings",
328
338
  checked: H,
329
339
  onCheckedChange: (e) => U(e === !0),
330
340
  disabled: N === "importing"
331
341
  }), /* @__PURE__ */ O("div", {
332
342
  className: "grid gap-1.5 leading-none",
333
- children: /* @__PURE__ */ O(T, {
343
+ children: /* @__PURE__ */ O(E, {
334
344
  htmlFor: "skipWarnings",
335
345
  className: "cursor-pointer",
336
346
  children: "Acknowledge warnings and proceed"
@@ -346,7 +356,7 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
346
356
  J.alreadyExists && /* @__PURE__ */ k(D, { children: [
347
357
  /* @__PURE__ */ O("div", {
348
358
  className: "size-16 mx-auto mb-4 rounded-full bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center",
349
- children: /* @__PURE__ */ O(u, { className: "size-8 text-blue-600" })
359
+ children: /* @__PURE__ */ O(d, { className: "size-8 text-blue-600" })
350
360
  }),
351
361
  /* @__PURE__ */ O("h3", {
352
362
  className: "text-lg font-medium mb-2",
@@ -364,7 +374,7 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
364
374
  J.originalUpdated && !J.alreadyExists && /* @__PURE__ */ k(D, { children: [
365
375
  /* @__PURE__ */ O("div", {
366
376
  className: "size-16 mx-auto mb-4 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center",
367
- children: /* @__PURE__ */ O(d, { className: "size-8 text-green-600" })
377
+ children: /* @__PURE__ */ O(f, { className: "size-8 text-green-600" })
368
378
  }),
369
379
  /* @__PURE__ */ O("h3", {
370
380
  className: "text-lg font-medium mb-2",
@@ -382,7 +392,7 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
382
392
  !J.originalUpdated && !J.alreadyExists && /* @__PURE__ */ k(D, { children: [
383
393
  /* @__PURE__ */ O("div", {
384
394
  className: "size-16 mx-auto mb-4 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center",
385
- children: /* @__PURE__ */ O(c, { className: "size-8 text-green-600" })
395
+ children: /* @__PURE__ */ O(l, { className: "size-8 text-green-600" })
386
396
  }),
387
397
  /* @__PURE__ */ O("h3", {
388
398
  className: "text-lg font-medium mb-2",
@@ -396,9 +406,9 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
396
406
  "\" has been created."
397
407
  ]
398
408
  }),
399
- J.stats && /* @__PURE__ */ O(g, {
409
+ J.stats && /* @__PURE__ */ O(_, {
400
410
  className: "inline-block text-left",
401
- children: /* @__PURE__ */ k(_, {
411
+ children: /* @__PURE__ */ k(v, {
402
412
  className: "p-4",
403
413
  children: [/* @__PURE__ */ O("p", {
404
414
  className: "text-xs font-medium text-text-secondary mb-2",
@@ -428,9 +438,9 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
428
438
  ] })
429
439
  ]
430
440
  }),
431
- K && /* @__PURE__ */ O(g, {
441
+ K && /* @__PURE__ */ O(_, {
432
442
  className: "border-status-error-border/50 bg-status-error-bg/5",
433
- children: /* @__PURE__ */ O(_, {
443
+ children: /* @__PURE__ */ O(v, {
434
444
  className: "p-3",
435
445
  children: /* @__PURE__ */ O("p", {
436
446
  className: "text-sm text-status-error-text",
@@ -438,9 +448,9 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
438
448
  })
439
449
  })
440
450
  }),
441
- J && !J.success && J.errorMessage && /* @__PURE__ */ O(g, {
451
+ J && !J.success && J.errorMessage && /* @__PURE__ */ O(_, {
442
452
  className: "border-status-error-border/50 bg-status-error-bg/5",
443
- children: /* @__PURE__ */ O(_, {
453
+ children: /* @__PURE__ */ O(v, {
444
454
  className: "p-3",
445
455
  children: /* @__PURE__ */ O("p", {
446
456
  className: "text-sm text-status-error-text",
@@ -452,7 +462,7 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
452
462
  }),
453
463
  /* @__PURE__ */ k(S, {
454
464
  className: "flex-row justify-between sm:justify-between",
455
- children: [/* @__PURE__ */ O("div", { children: N === "preview" && /* @__PURE__ */ k(h, {
465
+ children: [/* @__PURE__ */ O("div", { children: N === "preview" && /* @__PURE__ */ k(g, {
456
466
  variant: "ghost",
457
467
  onClick: () => {
458
468
  P("select"), F(null), L(null), Z();
@@ -462,22 +472,22 @@ var j = n(function({ isOpen: n, onClose: j, onSuccess: M }) {
462
472
  }) }), /* @__PURE__ */ k("div", {
463
473
  className: "flex items-center gap-2",
464
474
  children: [
465
- N !== "complete" && /* @__PURE__ */ O(h, {
475
+ N !== "complete" && /* @__PURE__ */ O(g, {
466
476
  variant: "outline",
467
477
  onClick: j,
468
478
  disabled: !$,
469
479
  children: "Cancel"
470
480
  }),
471
- N === "preview" && q?.isValid && /* @__PURE__ */ k(h, {
472
- onClick: ie,
481
+ N === "preview" && q?.isValid && /* @__PURE__ */ k(g, {
482
+ onClick: ae,
473
483
  disabled: G || q.warnings.length > 0 && !H,
474
- children: [/* @__PURE__ */ O(f, { className: "size-4" }), "Import Dashboard"]
484
+ children: [/* @__PURE__ */ O(p, { className: "size-4" }), "Import Dashboard"]
475
485
  }),
476
486
  N === "importing" && /* @__PURE__ */ k("div", {
477
487
  className: "flex items-center gap-2 text-sm text-text-secondary",
478
- children: [/* @__PURE__ */ O(E, { className: "size-4" }), "Importing..."]
488
+ children: [/* @__PURE__ */ O(ne, { className: "size-4" }), "Importing..."]
479
489
  }),
480
- N === "complete" && /* @__PURE__ */ O(h, {
490
+ N === "complete" && /* @__PURE__ */ O(g, {
481
491
  onClick: j,
482
492
  children: "Done"
483
493
  })
@@ -1 +1 @@
1
- {"version":3,"file":"ImportDashboardDialog.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/ImportDashboardDialog.tsx"],"sourcesContent":["/**\n * ImportDashboardDialog Component\n *\n * Modal dialog for importing a dashboard from JSON file.\n * Supports both creating new dashboards and updating existing ones.\n * Uses fe-libs shared UI components for consistent styling.\n */\n\nimport { type FC, memo, useState, useCallback, useEffect, useRef } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Label } from '@burdenoff/fe-libs/ui';\nimport { Checkbox } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { Card, CardContent } from '@burdenoff/fe-libs/ui';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { Upload, FileJson, Check, AlertTriangle, XCircle, ArrowLeft, RefreshCw, Info } from 'lucide-react';\nimport { useImportDashboard, type ImportDashboardOptions } from '../../hooks';\nimport { useBigConsoleAnalytics } from '../../hooks/useBigConsoleAnalytics';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ImportDashboardDialogProps {\n isOpen: boolean;\n onClose: () => void;\n onSuccess?: (dashboardId: string, dashboardName: string, originalUpdated: boolean) => void;\n}\n\ntype ImportStep = 'select' | 'preview' | 'importing' | 'complete';\n\n// ============================================================================\n// Helper: Extract dashboard ID from content\n// ============================================================================\n\nfunction extractDashboardId(content: string | null): string | null {\n if (!content) return null;\n try {\n const parsed = JSON.parse(content);\n return parsed.exportedFrom?.dashboardId || parsed.dashboard?.id || null;\n } catch {\n return null;\n }\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ImportDashboardDialog: FC<ImportDashboardDialogProps> = memo(function ImportDashboardDialog({\n isOpen,\n onClose,\n onSuccess,\n}) {\n const [step, setStep] = useState<ImportStep>('select');\n const [, setSelectedFile] = useState<File | null>(null);\n const [fileContent, setFileContent] = useState<string | null>(null);\n const [nameOverride, setNameOverride] = useState('');\n const [visibility, setVisibility] = useState<'PRIVATE' | 'TEAM' | 'PUBLIC'>('PRIVATE');\n const [skipWarnings, setSkipWarnings] = useState(false);\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const { loading, error, validationResult, importResult, validateImport, importDashboard, clearState } =\n useImportDashboard();\n const emitBigConsoleEvent = useBigConsoleAnalytics();\n\n // Reset state when dialog opens\n useEffect(() => {\n if (isOpen) {\n setStep('select');\n setSelectedFile(null);\n setFileContent(null);\n setNameOverride('');\n setVisibility('PRIVATE');\n setSkipWarnings(false);\n clearState();\n }\n }, [isOpen, clearState]);\n\n // Handle file selection\n const handleFileSelect = useCallback(\n async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n\n if (!file.name.endsWith('.json')) {\n alert('Please select a JSON file');\n return;\n }\n\n setSelectedFile(file);\n\n const reader = new FileReader();\n reader.onload = async (event) => {\n const content = event.target?.result as string;\n setFileContent(content);\n\n const result = await validateImport(content);\n if (result) {\n setStep('preview');\n if (result.dashboardInfo?.name) {\n setNameOverride(result.dashboardInfo.name);\n }\n }\n };\n reader.onerror = () => {\n alert('Failed to read file');\n };\n reader.readAsText(file);\n },\n [validateImport]\n );\n\n // Handle file drop\n const handleDrop = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n const file = e.dataTransfer.files?.[0];\n if (file) {\n const dt = new DataTransfer();\n dt.items.add(file);\n if (fileInputRef.current) {\n fileInputRef.current.files = dt.files;\n fileInputRef.current.dispatchEvent(new Event('change', { bubbles: true }));\n }\n }\n }, []);\n\n // Handle import\n const handleImport = useCallback(async () => {\n if (!fileContent) return;\n\n setStep('importing');\n\n const options: ImportDashboardOptions = {\n name: nameOverride.trim() || undefined,\n visibility,\n skipWarnings,\n };\n\n emitBigConsoleEvent('bigconsole.dashboard.import_clicked', { source: 'import' });\n\n const result = await importDashboard(fileContent, options);\n\n if (result?.success && result.dashboard) {\n setStep('complete');\n emitBigConsoleEvent('bigconsole.dashboard.imported', {\n dashboardId: result.dashboard.id,\n source: 'import',\n originalUpdated: result.originalUpdated ?? false,\n });\n onSuccess?.(result.dashboard.id, result.dashboard.name, result.originalUpdated ?? false);\n } else {\n setStep('preview');\n }\n }, [emitBigConsoleEvent, fileContent, importDashboard, nameOverride, onSuccess, skipWarnings, visibility]);\n\n const canClose = !loading && step !== 'importing';\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && canClose && onClose()}>\n <DialogContent className=\"sm:max-w-lg max-h-[85vh] flex flex-col\">\n <DialogHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-action-primary-bg/10\">\n <Upload className=\"size-5 text-primary\" />\n </div>\n <DialogTitle>Import Dashboard</DialogTitle>\n </div>\n <DialogDescription>Upload a Dashboard export file to restore it into this workspace.</DialogDescription>\n </DialogHeader>\n\n <div className=\"flex-1 overflow-y-auto py-4 space-y-4\">\n {/* Step 1: File Selection */}\n {step === 'select' && (\n <div\n onDragOver={(e) => e.preventDefault()}\n onDrop={handleDrop}\n className=\"border-2 border-dashed rounded-lg p-8 text-center hover:border-primary transition-colors\"\n >\n <FileJson className=\"size-12 mx-auto text-text-secondary mb-4\" />\n <p className=\"text-sm mb-2\">Drag and drop a dashboard export file here</p>\n <p className=\"text-xs text-text-secondary mb-4\">or</p>\n <input ref={fileInputRef} type=\"file\" accept=\".json\" onChange={handleFileSelect} className=\"hidden\" />\n <Button onClick={() => fileInputRef.current?.click()} disabled={loading}>\n Browse Files\n </Button>\n <p className=\"text-xs text-text-secondary mt-4\">\n Only .json files exported from BigConsole are supported\n </p>\n </div>\n )}\n\n {/* Step 2: Preview & Options */}\n {(step === 'preview' || step === 'importing') && validationResult && (\n <>\n {/* Dashboard Info */}\n <Card>\n <CardContent className=\"p-4 space-y-2\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Dashboard</span>\n <span className=\"text-sm font-medium\">{validationResult.dashboardInfo.name}</span>\n </div>\n {validationResult.dashboardInfo.description && (\n <div className=\"flex items-start justify-between\">\n <span className=\"text-sm text-text-secondary\">Description</span>\n <span className=\"text-sm text-right max-w-[60%] truncate\">\n {validationResult.dashboardInfo.description}\n </span>\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Pages</span>\n <Badge variant=\"secondary\">{validationResult.dashboardInfo.pageCount}</Badge>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Widgets</span>\n <Badge variant=\"secondary\">{validationResult.dashboardInfo.widgetCount}</Badge>\n </div>\n {validationResult.dashboardInfo.exportedFrom && (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Source ID</span>\n <span className=\"text-xs text-text-secondary font-mono truncate max-w-[180px]\">\n {validationResult.dashboardInfo.exportedFrom}\n </span>\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Schema Version</span>\n <span className=\"text-xs text-text-secondary\">{validationResult.dashboardInfo.schemaVersion}</span>\n </div>\n </CardContent>\n </Card>\n\n {/* Validation Errors */}\n {validationResult.errors.length > 0 && (\n <Card className=\"border-status-error-border/50 bg-status-error-bg/5\">\n <CardContent className=\"p-3\">\n <div className=\"flex items-center gap-2 mb-2\">\n <XCircle className=\"size-4 text-status-error-text\" />\n <span className=\"text-sm font-medium text-status-error-text\">\n Errors ({validationResult.errors.length})\n </span>\n </div>\n <ul className=\"text-xs text-status-error-text space-y-1\">\n {validationResult.errors.map((err, idx) => (\n <li key={idx}>\n <span className=\"font-mono\">[{err.code}]</span> {err.message}\n </li>\n ))}\n </ul>\n </CardContent>\n </Card>\n )}\n\n {/* Validation Warnings */}\n {validationResult.warnings.length > 0 && (\n <Card className=\"border-yellow-500/50 bg-yellow-500/5\">\n <CardContent className=\"p-3\">\n <div className=\"flex items-center gap-2 mb-2\">\n <AlertTriangle className=\"size-4 text-yellow-600\" />\n <span className=\"text-sm font-medium text-yellow-600\">\n Warnings ({validationResult.warnings.length})\n </span>\n </div>\n <ul className=\"text-xs text-yellow-600 space-y-1\">\n {validationResult.warnings.map((warn, idx) => (\n <li key={idx}>\n <span className=\"font-mono\">[{warn.code}]</span> {warn.message}\n </li>\n ))}\n </ul>\n </CardContent>\n </Card>\n )}\n\n {/* Import Options */}\n {validationResult.isValid && (\n <div className=\"space-y-4\">\n <p className=\"text-sm font-medium\">Import Options</p>\n\n {/* Import Info */}\n <Card className=\"border-blue-500/50 bg-blue-500/5\">\n <CardContent className=\"p-3\">\n <div className=\"flex items-start gap-2\">\n <Info className=\"size-4 text-blue-600 mt-0.5 shrink-0\" />\n <div className=\"text-xs text-blue-700 dark:text-blue-400\">\n {extractDashboardId(fileContent) ? (\n <>\n <p className=\"font-medium mb-1\">Import will check for existing dashboard</p>\n <p>\n If the original dashboard (ID:{' '}\n <code className=\"font-mono bg-blue-200/50 px-1 rounded\">\n {extractDashboardId(fileContent)?.slice(0, 8)}...\n </code>\n ) exists:\n </p>\n <ul className=\"list-disc list-inside mt-1 space-y-0.5\">\n <li>With changes → Update the existing dashboard</li>\n <li>No changes → Show &quot;already exists&quot; message</li>\n </ul>\n <p className=\"mt-1\">If original doesn&apos;t exist → Create a new dashboard</p>\n </>\n ) : (\n <>\n <p className=\"font-medium mb-1\">A new dashboard will be created</p>\n <p>Import as a new dashboard with the specified configuration.</p>\n </>\n )}\n </div>\n </div>\n </CardContent>\n </Card>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"import-name\">Dashboard Name</Label>\n <Input\n id=\"import-name\"\n type=\"text\"\n value={nameOverride}\n onChange={(e) => setNameOverride(e.target.value)}\n placeholder={validationResult.dashboardInfo.name}\n disabled={step === 'importing'}\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Visibility</Label>\n <div className=\"flex gap-2\">\n {(['PRIVATE', 'TEAM', 'PUBLIC'] as const).map((v) => (\n <Button\n key={v}\n type=\"button\"\n variant={visibility === v ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setVisibility(v)}\n disabled={step === 'importing'}\n >\n {v.charAt(0) + v.slice(1).toLowerCase()}\n </Button>\n ))}\n </div>\n </div>\n\n {validationResult.warnings.length > 0 && (\n <div className=\"flex items-start space-x-3\">\n <Checkbox\n id=\"skipWarnings\"\n checked={skipWarnings}\n onCheckedChange={(checked) => setSkipWarnings(checked === true)}\n disabled={step === 'importing'}\n />\n <div className=\"grid gap-1.5 leading-none\">\n <Label htmlFor=\"skipWarnings\" className=\"cursor-pointer\">\n Acknowledge warnings and proceed\n </Label>\n </div>\n </div>\n )}\n </div>\n )}\n </>\n )}\n\n {/* Step 3: Complete */}\n {step === 'complete' && importResult?.success && (\n <div className=\"text-center py-6\">\n {/* Already Exists - No Changes */}\n {importResult.alreadyExists && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center\">\n <Info className=\"size-8 text-blue-600\" />\n </div>\n <h3 className=\"text-lg font-medium mb-2\">Dashboard Already Exists</h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n Dashboard &quot;{importResult.dashboard?.name}&quot; already exists with the same data. No changes\n were made.\n </p>\n </>\n )}\n\n {/* Updated Existing */}\n {importResult.originalUpdated && !importResult.alreadyExists && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center\">\n <RefreshCw className=\"size-8 text-green-600\" />\n </div>\n <h3 className=\"text-lg font-medium mb-2\">Dashboard Updated!</h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n Dashboard &quot;{importResult.dashboard?.name}&quot; has been updated with the imported data.\n </p>\n </>\n )}\n\n {/* Created New */}\n {!importResult.originalUpdated && !importResult.alreadyExists && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center\">\n <Check className=\"size-8 text-green-600\" />\n </div>\n <h3 className=\"text-lg font-medium mb-2\">Import Successful!</h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n Dashboard &quot;{importResult.dashboard?.name}&quot; has been created.\n </p>\n\n {importResult.stats && (\n <Card className=\"inline-block text-left\">\n <CardContent className=\"p-4\">\n <p className=\"text-xs font-medium text-text-secondary mb-2\">Import Summary</p>\n <div className=\"grid grid-cols-2 gap-x-6 gap-y-1 text-xs\">\n <span className=\"text-text-secondary\">Pages:</span>\n <span>{importResult.stats.pagesCreated}</span>\n <span className=\"text-text-secondary\">Widgets:</span>\n <span>{importResult.stats.widgetsCreated}</span>\n <span className=\"text-text-secondary\">Actions:</span>\n <span>{importResult.stats.actionsCreated}</span>\n </div>\n </CardContent>\n </Card>\n )}\n </>\n )}\n </div>\n )}\n\n {/* Error */}\n {error && (\n <Card className=\"border-status-error-border/50 bg-status-error-bg/5\">\n <CardContent className=\"p-3\">\n <p className=\"text-sm text-status-error-text\">{error.message}</p>\n </CardContent>\n </Card>\n )}\n\n {importResult && !importResult.success && importResult.errorMessage && (\n <Card className=\"border-status-error-border/50 bg-status-error-bg/5\">\n <CardContent className=\"p-3\">\n <p className=\"text-sm text-status-error-text\">{importResult.errorMessage}</p>\n </CardContent>\n </Card>\n )}\n </div>\n\n <DialogFooter className=\"flex-row justify-between sm:justify-between\">\n <div>\n {step === 'preview' && (\n <Button\n variant=\"ghost\"\n onClick={() => {\n setStep('select');\n setSelectedFile(null);\n setFileContent(null);\n clearState();\n }}\n disabled={loading}\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </Button>\n )}\n </div>\n\n <div className=\"flex items-center gap-2\">\n {step !== 'complete' && (\n <Button variant=\"outline\" onClick={onClose} disabled={!canClose}>\n Cancel\n </Button>\n )}\n\n {step === 'preview' && validationResult?.isValid && (\n <Button\n onClick={handleImport}\n disabled={loading || (validationResult.warnings.length > 0 && !skipWarnings)}\n >\n <Upload className=\"size-4\" />\n Import Dashboard\n </Button>\n )}\n\n {step === 'importing' && (\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Spinner className=\"size-4\" />\n Importing...\n </div>\n )}\n\n {step === 'complete' && <Button onClick={onClose}>Done</Button>}\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n});\n\nexport default ImportDashboardDialog;\n"],"mappings":";;;;;;;;AA4CA,SAAS,EAAmB,GAAuC;AACjE,KAAI,CAAC,EAAS,QAAO;AACrB,KAAI;EACF,IAAM,IAAS,KAAK,MAAM,EAAQ;AAClC,SAAO,EAAO,cAAc,eAAe,EAAO,WAAW,MAAM;SAC7D;AACN,SAAO;;;AAQX,IAAa,IAAwD,EAAK,SAA+B,EACvG,WACA,YACA,gBACC;CACD,IAAM,CAAC,GAAM,KAAW,EAAqB,SAAS,EAChD,GAAG,KAAmB,EAAsB,KAAK,EACjD,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAC7D,CAAC,GAAc,KAAmB,EAAS,GAAG,EAC9C,CAAC,GAAY,KAAiB,EAAwC,UAAU,EAChF,CAAC,GAAc,KAAmB,EAAS,GAAM,EAEjD,IAAe,EAAyB,KAAK,EAE7C,EAAE,YAAS,UAAO,qBAAkB,iBAAc,mBAAgB,oBAAiB,kBACvF,GAAoB,EAChB,IAAsB,GAAwB;AAGpD,SAAgB;AACd,EAAI,MACF,EAAQ,SAAS,EACjB,EAAgB,KAAK,EACrB,EAAe,KAAK,EACpB,EAAgB,GAAG,EACnB,EAAc,UAAU,EACxB,EAAgB,GAAM,EACtB,GAAY;IAEb,CAAC,GAAQ,EAAW,CAAC;CAGxB,IAAM,KAAmB,EACvB,OAAO,MAA2C;EAChD,IAAM,IAAO,EAAE,OAAO,QAAQ;AAC9B,MAAI,CAAC,EAAM;AAEX,MAAI,CAAC,EAAK,KAAK,SAAS,QAAQ,EAAE;AAChC,SAAM,4BAA4B;AAClC;;AAGF,IAAgB,EAAK;EAErB,IAAM,IAAS,IAAI,YAAY;AAgB/B,EAfA,EAAO,SAAS,OAAO,MAAU;GAC/B,IAAM,IAAU,EAAM,QAAQ;AAC9B,KAAe,EAAQ;GAEvB,IAAM,IAAS,MAAM,EAAe,EAAQ;AAC5C,GAAI,MACF,EAAQ,UAAU,EACd,EAAO,eAAe,QACxB,EAAgB,EAAO,cAAc,KAAK;KAIhD,EAAO,gBAAgB;AACrB,SAAM,sBAAsB;KAE9B,EAAO,WAAW,EAAK;IAEzB,CAAC,EAAe,CACjB,EAGK,KAAa,GAAa,MAAuB;AACrD,IAAE,gBAAgB;EAClB,IAAM,IAAO,EAAE,aAAa,QAAQ;AACpC,MAAI,GAAM;GACR,IAAM,IAAK,IAAI,cAAc;AAE7B,GADA,EAAG,MAAM,IAAI,EAAK,EACd,EAAa,YACf,EAAa,QAAQ,QAAQ,EAAG,OAChC,EAAa,QAAQ,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,IAAM,CAAC,CAAC;;IAG7E,EAAE,CAAC,EAGA,KAAe,EAAY,YAAY;AAC3C,MAAI,CAAC,EAAa;AAElB,IAAQ,YAAY;EAEpB,IAAM,IAAkC;GACtC,MAAM,EAAa,MAAM,IAAI,KAAA;GAC7B;GACA;GACD;AAED,IAAoB,uCAAuC,EAAE,QAAQ,UAAU,CAAC;EAEhF,IAAM,IAAS,MAAM,EAAgB,GAAa,EAAQ;AAE1D,EAAI,GAAQ,WAAW,EAAO,aAC5B,EAAQ,WAAW,EACnB,EAAoB,iCAAiC;GACnD,aAAa,EAAO,UAAU;GAC9B,QAAQ;GACR,iBAAiB,EAAO,mBAAmB;GAC5C,CAAC,EACF,IAAY,EAAO,UAAU,IAAI,EAAO,UAAU,MAAM,EAAO,mBAAmB,GAAM,IAExF,EAAQ,UAAU;IAEnB;EAAC;EAAqB;EAAa;EAAiB;EAAc;EAAW;EAAc;EAAW,CAAC,EAEpG,IAAW,CAAC,KAAW,MAAS;AAEtC,QACE,kBAAC,GAAD;EAAQ,MAAM;EAAQ,eAAe,MAAS,CAAC,KAAQ,KAAY,GAAS;YAC1E,kBAAC,GAAD;GAAe,WAAU;aAAzB;IACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD,EAAQ,WAAU,uBAAwB,CAAA;MACtC,CAAA,EACN,kBAAC,GAAD,EAAA,UAAa,oBAA8B,CAAA,CACvC;QACN,kBAAC,GAAD,EAAA,UAAmB,qEAAqF,CAAA,CAC3F,EAAA,CAAA;IAEf,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEG,MAAS,YACR,kBAAC,OAAD;OACE,aAAa,MAAM,EAAE,gBAAgB;OACrC,QAAQ;OACR,WAAU;iBAHZ;QAKE,kBAAC,GAAD,EAAU,WAAU,4CAA6C,CAAA;QACjE,kBAAC,KAAD;SAAG,WAAU;mBAAe;SAA8C,CAAA;QAC1E,kBAAC,KAAD;SAAG,WAAU;mBAAmC;SAAM,CAAA;QACtD,kBAAC,SAAD;SAAO,KAAK;SAAc,MAAK;SAAO,QAAO;SAAQ,UAAU;SAAkB,WAAU;SAAW,CAAA;QACtG,kBAAC,GAAD;SAAQ,eAAe,EAAa,SAAS,OAAO;SAAE,UAAU;mBAAS;SAEhE,CAAA;QACT,kBAAC,KAAD;SAAG,WAAU;mBAAmC;SAE5C,CAAA;QACA;;OAIN,MAAS,aAAa,MAAS,gBAAgB,KAC/C,kBAAA,GAAA,EAAA,UAAA;OAEE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;QAAa,WAAU;kBAAvB;SACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,QAAD;WAAM,WAAU;qBAA8B;WAAgB,CAAA,EAC9D,kBAAC,QAAD;WAAM,WAAU;qBAAuB,EAAiB,cAAc;WAAY,CAAA,CAC9E;;SACL,EAAiB,cAAc,eAC9B,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,QAAD;WAAM,WAAU;qBAA8B;WAAkB,CAAA,EAChE,kBAAC,QAAD;WAAM,WAAU;qBACb,EAAiB,cAAc;WAC3B,CAAA,CACH;;SAER,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,QAAD;WAAM,WAAU;qBAA8B;WAAY,CAAA,EAC1D,kBAAC,GAAD;WAAO,SAAQ;qBAAa,EAAiB,cAAc;WAAkB,CAAA,CACzE;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,QAAD;WAAM,WAAU;qBAA8B;WAAc,CAAA,EAC5D,kBAAC,GAAD;WAAO,SAAQ;qBAAa,EAAiB,cAAc;WAAoB,CAAA,CAC3E;;SACL,EAAiB,cAAc,gBAC9B,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,QAAD;WAAM,WAAU;qBAA8B;WAAgB,CAAA,EAC9D,kBAAC,QAAD;WAAM,WAAU;qBACb,EAAiB,cAAc;WAC3B,CAAA,CACH;;SAER,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,QAAD;WAAM,WAAU;qBAA8B;WAAqB,CAAA,EACnE,kBAAC,QAAD;WAAM,WAAU;qBAA+B,EAAiB,cAAc;WAAqB,CAAA,CAC/F;;SACM;WACT,CAAA;OAGN,EAAiB,OAAO,SAAS,KAChC,kBAAC,GAAD;QAAM,WAAU;kBACd,kBAAC,GAAD;SAAa,WAAU;mBAAvB,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD,EAAS,WAAU,iCAAkC,CAAA,EACrD,kBAAC,QAAD;WAAM,WAAU;qBAAhB;YAA6D;YAClD,EAAiB,OAAO;YAAO;YACnC;aACH;aACN,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAiB,OAAO,KAAK,GAAK,MACjC,kBAAC,MAAD,EAAA,UAAA;WACE,kBAAC,QAAD;YAAM,WAAU;sBAAhB;aAA4B;aAAE,EAAI;aAAK;aAAQ;;;WAAE,EAAI;WAClD,EAAA,EAFI,EAEJ,CACL;UACC,CAAA,CACO;;QACT,CAAA;OAIR,EAAiB,SAAS,SAAS,KAClC,kBAAC,GAAD;QAAM,WAAU;kBACd,kBAAC,GAAD;SAAa,WAAU;mBAAvB,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD,EAAe,WAAU,0BAA2B,CAAA,EACpD,kBAAC,QAAD;WAAM,WAAU;qBAAhB;YAAsD;YACzC,EAAiB,SAAS;YAAO;YACvC;aACH;aACN,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAiB,SAAS,KAAK,GAAM,MACpC,kBAAC,MAAD,EAAA,UAAA;WACE,kBAAC,QAAD;YAAM,WAAU;sBAAhB;aAA4B;aAAE,EAAK;aAAK;aAAQ;;;WAAE,EAAK;WACpD,EAAA,EAFI,EAEJ,CACL;UACC,CAAA,CACO;;QACT,CAAA;OAIR,EAAiB,WAChB,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,KAAD;UAAG,WAAU;oBAAsB;UAAkB,CAAA;SAGrD,kBAAC,GAAD;UAAM,WAAU;oBACd,kBAAC,GAAD;WAAa,WAAU;qBACrB,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,wCAAyC,CAAA,EACzD,kBAAC,OAAD;aAAK,WAAU;uBACZ,EAAmB,EAAY,GAC9B,kBAAA,GAAA,EAAA,UAAA;cACE,kBAAC,KAAD;eAAG,WAAU;yBAAmB;eAA4C,CAAA;cAC5E,kBAAC,KAAD,EAAA,UAAA;eAAG;eAC8B;eAC/B,kBAAC,QAAD;gBAAM,WAAU;0BAAhB,CACG,EAAmB,EAAY,EAAE,MAAM,GAAG,EAAE,EAAC,MACzC;;;eAEL,EAAA,CAAA;cACJ,kBAAC,MAAD;eAAI,WAAU;yBAAd,CACE,kBAAC,MAAD,EAAA,UAAI,gDAAiD,CAAA,EACrD,kBAAC,MAAD,EAAA,UAAI,gDAAyD,CAAA,CAC1D;;cACL,kBAAC,KAAD;eAAG,WAAU;yBAAO;eAA2D,CAAA;cAC9E,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,KAAD;cAAG,WAAU;wBAAmB;cAAmC,CAAA,EACnE,kBAAC,KAAD,EAAA,UAAG,+DAA+D,CAAA,CACjE,EAAA,CAAA;aAED,CAAA,CACF;;WACM,CAAA;UACT,CAAA;SAEP,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD;WAAO,SAAQ;qBAAc;WAAsB,CAAA,EACnD,kBAAC,IAAD;WACE,IAAG;WACH,MAAK;WACL,OAAO;WACP,WAAW,MAAM,EAAgB,EAAE,OAAO,MAAM;WAChD,aAAa,EAAiB,cAAc;WAC5C,UAAU,MAAS;WACnB,CAAA,CACE;;SAEN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD,EAAA,UAAO,cAAkB,CAAA,EACzB,kBAAC,OAAD;WAAK,WAAU;qBACX;YAAC;YAAW;YAAQ;YAAS,CAAW,KAAK,MAC7C,kBAAC,GAAD;YAEE,MAAK;YACL,SAAS,MAAe,IAAI,YAAY;YACxC,MAAK;YACL,eAAe,EAAc,EAAE;YAC/B,UAAU,MAAS;sBAElB,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,aAAa;YAChC,EARF,EAQE,CACT;WACE,CAAA,CACF;;SAEL,EAAiB,SAAS,SAAS,KAClC,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD;WACE,IAAG;WACH,SAAS;WACT,kBAAkB,MAAY,EAAgB,MAAY,GAAK;WAC/D,UAAU,MAAS;WACnB,CAAA,EACF,kBAAC,OAAD;WAAK,WAAU;qBACb,kBAAC,GAAD;YAAO,SAAQ;YAAe,WAAU;sBAAiB;YAEjD,CAAA;WACJ,CAAA,CACF;;SAEJ;;OAEP,EAAA,CAAA;MAIJ,MAAS,cAAc,GAAc,WACpC,kBAAC,OAAD;OAAK,WAAU;iBAAf;QAEG,EAAa,iBACZ,kBAAA,GAAA,EAAA,UAAA;SACE,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,GAAD,EAAM,WAAU,wBAAyB,CAAA;UACrC,CAAA;SACN,kBAAC,MAAD;UAAI,WAAU;oBAA2B;UAA6B,CAAA;SACtE,kBAAC,KAAD;UAAG,WAAU;oBAAb;WAAgD;WAC7B,EAAa,WAAW;WAAK;WAE5C;;SACH,EAAA,CAAA;QAIJ,EAAa,mBAAmB,CAAC,EAAa,iBAC7C,kBAAA,GAAA,EAAA,UAAA;SACE,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,GAAD,EAAW,WAAU,yBAA0B,CAAA;UAC3C,CAAA;SACN,kBAAC,MAAD;UAAI,WAAU;oBAA2B;UAAuB,CAAA;SAChE,kBAAC,KAAD;UAAG,WAAU;oBAAb;WAAgD;WAC7B,EAAa,WAAW;WAAK;WAC5C;;SACH,EAAA,CAAA;QAIJ,CAAC,EAAa,mBAAmB,CAAC,EAAa,iBAC9C,kBAAA,GAAA,EAAA,UAAA;SACE,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,GAAD,EAAO,WAAU,yBAA0B,CAAA;UACvC,CAAA;SACN,kBAAC,MAAD;UAAI,WAAU;oBAA2B;UAAuB,CAAA;SAChE,kBAAC,KAAD;UAAG,WAAU;oBAAb;WAAgD;WAC7B,EAAa,WAAW;WAAK;WAC5C;;SAEH,EAAa,SACZ,kBAAC,GAAD;UAAM,WAAU;oBACd,kBAAC,GAAD;WAAa,WAAU;qBAAvB,CACE,kBAAC,KAAD;YAAG,WAAU;sBAA+C;YAAkB,CAAA,EAC9E,kBAAC,OAAD;YAAK,WAAU;sBAAf;aACE,kBAAC,QAAD;cAAM,WAAU;wBAAsB;cAAa,CAAA;aACnD,kBAAC,QAAD,EAAA,UAAO,EAAa,MAAM,cAAoB,CAAA;aAC9C,kBAAC,QAAD;cAAM,WAAU;wBAAsB;cAAe,CAAA;aACrD,kBAAC,QAAD,EAAA,UAAO,EAAa,MAAM,gBAAsB,CAAA;aAChD,kBAAC,QAAD;cAAM,WAAU;wBAAsB;cAAe,CAAA;aACrD,kBAAC,QAAD,EAAA,UAAO,EAAa,MAAM,gBAAsB,CAAA;aAC5C;cACM;;UACT,CAAA;SAER,EAAA,CAAA;QAED;;MAIP,KACC,kBAAC,GAAD;OAAM,WAAU;iBACd,kBAAC,GAAD;QAAa,WAAU;kBACrB,kBAAC,KAAD;SAAG,WAAU;mBAAkC,EAAM;SAAY,CAAA;QACrD,CAAA;OACT,CAAA;MAGR,KAAgB,CAAC,EAAa,WAAW,EAAa,gBACrD,kBAAC,GAAD;OAAM,WAAU;iBACd,kBAAC,GAAD;QAAa,WAAU;kBACrB,kBAAC,KAAD;SAAG,WAAU;mBAAkC,EAAa;SAAiB,CAAA;QACjE,CAAA;OACT,CAAA;MAEL;;IAEN,kBAAC,GAAD;KAAc,WAAU;eAAxB,CACE,kBAAC,OAAD,EAAA,UACG,MAAS,aACR,kBAAC,GAAD;MACE,SAAQ;MACR,eAAe;AAIb,OAHA,EAAQ,SAAS,EACjB,EAAgB,KAAK,EACrB,EAAe,KAAK,EACpB,GAAY;;MAEd,UAAU;gBARZ,CAUE,kBAAC,IAAD,EAAW,WAAU,UAAW,CAAA,EAAA,OAEzB;SAEP,CAAA,EAEN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACG,MAAS,cACR,kBAAC,GAAD;QAAQ,SAAQ;QAAU,SAAS;QAAS,UAAU,CAAC;kBAAU;QAExD,CAAA;OAGV,MAAS,aAAa,GAAkB,WACvC,kBAAC,GAAD;QACE,SAAS;QACT,UAAU,KAAY,EAAiB,SAAS,SAAS,KAAK,CAAC;kBAFjE,CAIE,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA,EAAA,mBAEtB;;OAGV,MAAS,eACR,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA,EAAA,eAE1B;;OAGP,MAAS,cAAc,kBAAC,GAAD;QAAQ,SAAS;kBAAS;QAAa,CAAA;OAC3D;QACO;;IACD;;EACT,CAAA;EAEX"}
1
+ {"version":3,"file":"ImportDashboardDialog.js","names":[],"sources":["../../../../src/bigconsole/components/dashboard/ImportDashboardDialog.tsx"],"sourcesContent":["/**\n * ImportDashboardDialog Component\n *\n * Modal dialog for importing a dashboard from JSON file.\n * Supports both creating new dashboards and updating existing ones.\n * Uses fe-libs shared UI components for consistent styling.\n */\n\nimport { type FC, memo, useState, useCallback, useEffect, useRef } from 'react';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport { Label } from '@burdenoff/fe-libs/ui';\nimport { Checkbox } from '@burdenoff/fe-libs/ui';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { Card, CardContent } from '@burdenoff/fe-libs/ui';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport { Upload, FileJson, Check, AlertTriangle, XCircle, ArrowLeft, RefreshCw, Info } from 'lucide-react';\nimport { useImportDashboard, type ImportDashboardOptions } from '../../hooks';\nimport { useBigConsoleAnalytics } from '../../hooks/useBigConsoleAnalytics';\nimport { reportBigConsoleQuotaError } from '../../utils/quota';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ImportDashboardDialogProps {\n isOpen: boolean;\n onClose: () => void;\n onSuccess?: (dashboardId: string, dashboardName: string, originalUpdated: boolean) => void;\n}\n\ntype ImportStep = 'select' | 'preview' | 'importing' | 'complete';\n\n// ============================================================================\n// Helper: Extract dashboard ID from content\n// ============================================================================\n\nfunction extractDashboardId(content: string | null): string | null {\n if (!content) return null;\n try {\n const parsed = JSON.parse(content);\n return parsed.exportedFrom?.dashboardId || parsed.dashboard?.id || null;\n } catch {\n return null;\n }\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ImportDashboardDialog: FC<ImportDashboardDialogProps> = memo(function ImportDashboardDialog({\n isOpen,\n onClose,\n onSuccess,\n}) {\n const [step, setStep] = useState<ImportStep>('select');\n const [, setSelectedFile] = useState<File | null>(null);\n const [fileContent, setFileContent] = useState<string | null>(null);\n const [nameOverride, setNameOverride] = useState('');\n const [visibility, setVisibility] = useState<'PRIVATE' | 'TEAM' | 'PUBLIC'>('PRIVATE');\n const [skipWarnings, setSkipWarnings] = useState(false);\n\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const { loading, error, validationResult, importResult, validateImport, importDashboard, clearState } =\n useImportDashboard();\n const emitBigConsoleEvent = useBigConsoleAnalytics();\n\n // Reset state when dialog opens\n useEffect(() => {\n if (isOpen) {\n setStep('select');\n setSelectedFile(null);\n setFileContent(null);\n setNameOverride('');\n setVisibility('PRIVATE');\n setSkipWarnings(false);\n clearState();\n }\n }, [isOpen, clearState]);\n\n // Handle file selection\n const handleFileSelect = useCallback(\n async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0];\n if (!file) return;\n\n if (!file.name.endsWith('.json')) {\n alert('Please select a JSON file');\n return;\n }\n\n setSelectedFile(file);\n\n const reader = new FileReader();\n reader.onload = async (event) => {\n const content = event.target?.result as string;\n setFileContent(content);\n\n const result = await validateImport(content);\n if (result) {\n setStep('preview');\n if (result.dashboardInfo?.name) {\n setNameOverride(result.dashboardInfo.name);\n }\n }\n };\n reader.onerror = () => {\n alert('Failed to read file');\n };\n reader.readAsText(file);\n },\n [validateImport]\n );\n\n // Handle file drop\n const handleDrop = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n const file = e.dataTransfer.files?.[0];\n if (file) {\n const dt = new DataTransfer();\n dt.items.add(file);\n if (fileInputRef.current) {\n fileInputRef.current.files = dt.files;\n fileInputRef.current.dispatchEvent(new Event('change', { bubbles: true }));\n }\n }\n }, []);\n\n // Handle import\n const handleImport = useCallback(async () => {\n if (!fileContent) return;\n\n setStep('importing');\n\n const options: ImportDashboardOptions = {\n name: nameOverride.trim() || undefined,\n visibility,\n skipWarnings,\n };\n\n emitBigConsoleEvent('bigconsole.dashboard.import_clicked', { source: 'import' });\n\n try {\n const result = await importDashboard(fileContent, options);\n\n if (result?.success && result.dashboard) {\n setStep('complete');\n emitBigConsoleEvent('bigconsole.dashboard.imported', {\n dashboardId: result.dashboard.id,\n source: 'import',\n originalUpdated: result.originalUpdated ?? false,\n });\n onSuccess?.(result.dashboard.id, result.dashboard.name, result.originalUpdated ?? false);\n } else {\n setStep('preview');\n }\n } catch (err) {\n if (reportBigConsoleQuotaError(err, 'project')) {\n onClose();\n return;\n }\n setStep('preview');\n }\n }, [emitBigConsoleEvent, fileContent, importDashboard, nameOverride, onClose, onSuccess, skipWarnings, visibility]);\n\n const canClose = !loading && step !== 'importing';\n\n return (\n <Dialog open={isOpen} onOpenChange={(open) => !open && canClose && onClose()}>\n <DialogContent className=\"sm:max-w-lg max-h-[85vh] flex flex-col\">\n <DialogHeader>\n <div className=\"flex items-center gap-3\">\n <div className=\"flex size-10 items-center justify-center rounded-lg bg-action-primary-bg/10\">\n <Upload className=\"size-5 text-primary\" />\n </div>\n <DialogTitle>Import Dashboard</DialogTitle>\n </div>\n <DialogDescription>Upload a Dashboard export file to restore it into this workspace.</DialogDescription>\n </DialogHeader>\n\n <div className=\"flex-1 overflow-y-auto py-4 space-y-4\">\n {/* Step 1: File Selection */}\n {step === 'select' && (\n <div\n onDragOver={(e) => e.preventDefault()}\n onDrop={handleDrop}\n className=\"border-2 border-dashed rounded-lg p-8 text-center hover:border-primary transition-colors\"\n >\n <FileJson className=\"size-12 mx-auto text-text-secondary mb-4\" />\n <p className=\"text-sm mb-2\">Drag and drop a dashboard export file here</p>\n <p className=\"text-xs text-text-secondary mb-4\">or</p>\n <input ref={fileInputRef} type=\"file\" accept=\".json\" onChange={handleFileSelect} className=\"hidden\" />\n <Button onClick={() => fileInputRef.current?.click()} disabled={loading}>\n Browse Files\n </Button>\n <p className=\"text-xs text-text-secondary mt-4\">\n Only .json files exported from BigConsole are supported\n </p>\n </div>\n )}\n\n {/* Step 2: Preview & Options */}\n {(step === 'preview' || step === 'importing') && validationResult && (\n <>\n {/* Dashboard Info */}\n <Card>\n <CardContent className=\"p-4 space-y-2\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Dashboard</span>\n <span className=\"text-sm font-medium\">{validationResult.dashboardInfo.name}</span>\n </div>\n {validationResult.dashboardInfo.description && (\n <div className=\"flex items-start justify-between\">\n <span className=\"text-sm text-text-secondary\">Description</span>\n <span className=\"text-sm text-right max-w-[60%] truncate\">\n {validationResult.dashboardInfo.description}\n </span>\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Pages</span>\n <Badge variant=\"secondary\">{validationResult.dashboardInfo.pageCount}</Badge>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Widgets</span>\n <Badge variant=\"secondary\">{validationResult.dashboardInfo.widgetCount}</Badge>\n </div>\n {validationResult.dashboardInfo.exportedFrom && (\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Source ID</span>\n <span className=\"text-xs text-text-secondary font-mono truncate max-w-[180px]\">\n {validationResult.dashboardInfo.exportedFrom}\n </span>\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Schema Version</span>\n <span className=\"text-xs text-text-secondary\">{validationResult.dashboardInfo.schemaVersion}</span>\n </div>\n </CardContent>\n </Card>\n\n {/* Validation Errors */}\n {validationResult.errors.length > 0 && (\n <Card className=\"border-status-error-border/50 bg-status-error-bg/5\">\n <CardContent className=\"p-3\">\n <div className=\"flex items-center gap-2 mb-2\">\n <XCircle className=\"size-4 text-status-error-text\" />\n <span className=\"text-sm font-medium text-status-error-text\">\n Errors ({validationResult.errors.length})\n </span>\n </div>\n <ul className=\"text-xs text-status-error-text space-y-1\">\n {validationResult.errors.map((err, idx) => (\n <li key={idx}>\n <span className=\"font-mono\">[{err.code}]</span> {err.message}\n </li>\n ))}\n </ul>\n </CardContent>\n </Card>\n )}\n\n {/* Validation Warnings */}\n {validationResult.warnings.length > 0 && (\n <Card className=\"border-yellow-500/50 bg-yellow-500/5\">\n <CardContent className=\"p-3\">\n <div className=\"flex items-center gap-2 mb-2\">\n <AlertTriangle className=\"size-4 text-yellow-600\" />\n <span className=\"text-sm font-medium text-yellow-600\">\n Warnings ({validationResult.warnings.length})\n </span>\n </div>\n <ul className=\"text-xs text-yellow-600 space-y-1\">\n {validationResult.warnings.map((warn, idx) => (\n <li key={idx}>\n <span className=\"font-mono\">[{warn.code}]</span> {warn.message}\n </li>\n ))}\n </ul>\n </CardContent>\n </Card>\n )}\n\n {/* Import Options */}\n {validationResult.isValid && (\n <div className=\"space-y-4\">\n <p className=\"text-sm font-medium\">Import Options</p>\n\n {/* Import Info */}\n <Card className=\"border-blue-500/50 bg-blue-500/5\">\n <CardContent className=\"p-3\">\n <div className=\"flex items-start gap-2\">\n <Info className=\"size-4 text-blue-600 mt-0.5 shrink-0\" />\n <div className=\"text-xs text-blue-700 dark:text-blue-400\">\n {extractDashboardId(fileContent) ? (\n <>\n <p className=\"font-medium mb-1\">Import will check for existing dashboard</p>\n <p>\n If the original dashboard (ID:{' '}\n <code className=\"font-mono bg-blue-200/50 px-1 rounded\">\n {extractDashboardId(fileContent)?.slice(0, 8)}...\n </code>\n ) exists:\n </p>\n <ul className=\"list-disc list-inside mt-1 space-y-0.5\">\n <li>With changes → Update the existing dashboard</li>\n <li>No changes → Show &quot;already exists&quot; message</li>\n </ul>\n <p className=\"mt-1\">If original doesn&apos;t exist → Create a new dashboard</p>\n </>\n ) : (\n <>\n <p className=\"font-medium mb-1\">A new dashboard will be created</p>\n <p>Import as a new dashboard with the specified configuration.</p>\n </>\n )}\n </div>\n </div>\n </CardContent>\n </Card>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"import-name\">Dashboard Name</Label>\n <Input\n id=\"import-name\"\n type=\"text\"\n value={nameOverride}\n onChange={(e) => setNameOverride(e.target.value)}\n placeholder={validationResult.dashboardInfo.name}\n disabled={step === 'importing'}\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Visibility</Label>\n <div className=\"flex gap-2\">\n {(['PRIVATE', 'TEAM', 'PUBLIC'] as const).map((v) => (\n <Button\n key={v}\n type=\"button\"\n variant={visibility === v ? 'default' : 'outline'}\n size=\"sm\"\n onClick={() => setVisibility(v)}\n disabled={step === 'importing'}\n >\n {v.charAt(0) + v.slice(1).toLowerCase()}\n </Button>\n ))}\n </div>\n </div>\n\n {validationResult.warnings.length > 0 && (\n <div className=\"flex items-start space-x-3\">\n <Checkbox\n id=\"skipWarnings\"\n checked={skipWarnings}\n onCheckedChange={(checked) => setSkipWarnings(checked === true)}\n disabled={step === 'importing'}\n />\n <div className=\"grid gap-1.5 leading-none\">\n <Label htmlFor=\"skipWarnings\" className=\"cursor-pointer\">\n Acknowledge warnings and proceed\n </Label>\n </div>\n </div>\n )}\n </div>\n )}\n </>\n )}\n\n {/* Step 3: Complete */}\n {step === 'complete' && importResult?.success && (\n <div className=\"text-center py-6\">\n {/* Already Exists - No Changes */}\n {importResult.alreadyExists && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center\">\n <Info className=\"size-8 text-blue-600\" />\n </div>\n <h3 className=\"text-lg font-medium mb-2\">Dashboard Already Exists</h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n Dashboard &quot;{importResult.dashboard?.name}&quot; already exists with the same data. No changes\n were made.\n </p>\n </>\n )}\n\n {/* Updated Existing */}\n {importResult.originalUpdated && !importResult.alreadyExists && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center\">\n <RefreshCw className=\"size-8 text-green-600\" />\n </div>\n <h3 className=\"text-lg font-medium mb-2\">Dashboard Updated!</h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n Dashboard &quot;{importResult.dashboard?.name}&quot; has been updated with the imported data.\n </p>\n </>\n )}\n\n {/* Created New */}\n {!importResult.originalUpdated && !importResult.alreadyExists && (\n <>\n <div className=\"size-16 mx-auto mb-4 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center\">\n <Check className=\"size-8 text-green-600\" />\n </div>\n <h3 className=\"text-lg font-medium mb-2\">Import Successful!</h3>\n <p className=\"text-sm text-text-secondary mb-4\">\n Dashboard &quot;{importResult.dashboard?.name}&quot; has been created.\n </p>\n\n {importResult.stats && (\n <Card className=\"inline-block text-left\">\n <CardContent className=\"p-4\">\n <p className=\"text-xs font-medium text-text-secondary mb-2\">Import Summary</p>\n <div className=\"grid grid-cols-2 gap-x-6 gap-y-1 text-xs\">\n <span className=\"text-text-secondary\">Pages:</span>\n <span>{importResult.stats.pagesCreated}</span>\n <span className=\"text-text-secondary\">Widgets:</span>\n <span>{importResult.stats.widgetsCreated}</span>\n <span className=\"text-text-secondary\">Actions:</span>\n <span>{importResult.stats.actionsCreated}</span>\n </div>\n </CardContent>\n </Card>\n )}\n </>\n )}\n </div>\n )}\n\n {/* Error */}\n {error && (\n <Card className=\"border-status-error-border/50 bg-status-error-bg/5\">\n <CardContent className=\"p-3\">\n <p className=\"text-sm text-status-error-text\">{error.message}</p>\n </CardContent>\n </Card>\n )}\n\n {importResult && !importResult.success && importResult.errorMessage && (\n <Card className=\"border-status-error-border/50 bg-status-error-bg/5\">\n <CardContent className=\"p-3\">\n <p className=\"text-sm text-status-error-text\">{importResult.errorMessage}</p>\n </CardContent>\n </Card>\n )}\n </div>\n\n <DialogFooter className=\"flex-row justify-between sm:justify-between\">\n <div>\n {step === 'preview' && (\n <Button\n variant=\"ghost\"\n onClick={() => {\n setStep('select');\n setSelectedFile(null);\n setFileContent(null);\n clearState();\n }}\n disabled={loading}\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </Button>\n )}\n </div>\n\n <div className=\"flex items-center gap-2\">\n {step !== 'complete' && (\n <Button variant=\"outline\" onClick={onClose} disabled={!canClose}>\n Cancel\n </Button>\n )}\n\n {step === 'preview' && validationResult?.isValid && (\n <Button\n onClick={handleImport}\n disabled={loading || (validationResult.warnings.length > 0 && !skipWarnings)}\n >\n <Upload className=\"size-4\" />\n Import Dashboard\n </Button>\n )}\n\n {step === 'importing' && (\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Spinner className=\"size-4\" />\n Importing...\n </div>\n )}\n\n {step === 'complete' && <Button onClick={onClose}>Done</Button>}\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n});\n\nexport default ImportDashboardDialog;\n"],"mappings":"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"}