@timbal-ai/timbal-react 1.4.0 → 1.6.0
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/CHANGELOG.md +30 -0
- package/README.md +43 -4
- package/dist/app.cjs +3770 -1506
- package/dist/app.d.cts +76 -31
- package/dist/app.d.ts +76 -31
- package/dist/app.esm.js +30 -8
- package/dist/{chart-artifact-C8-Py6lc.d.cts → chart-artifact-C2pZQsaP.d.ts} +247 -41
- package/dist/{chart-artifact-CMnDys2t.d.ts → chart-artifact-VAqgH-My.d.cts} +247 -41
- package/dist/{chat-ClmzWzCX.d.cts → chat-DDsp-Vzz.d.cts} +1 -1
- package/dist/{chat-ClmzWzCX.d.ts → chat-DDsp-Vzz.d.ts} +1 -1
- package/dist/chat.cjs +280 -123
- package/dist/chat.d.cts +3 -3
- package/dist/chat.d.ts +3 -3
- package/dist/chat.esm.js +4 -3
- package/dist/chunk-24B4I4XC.esm.js +232 -0
- package/dist/{chunk-VOWNCS3F.esm.js → chunk-6SQMTBPL.esm.js} +1669 -504
- package/dist/chunk-EDEKQYSU.esm.js +10 -0
- package/dist/{chunk-QIABF4KB.esm.js → chunk-ELEY66OH.esm.js} +2 -2
- package/dist/{chunk-THBA27QY.esm.js → chunk-HSL36SJ4.esm.js} +243 -124
- package/dist/chunk-JJOO4PR5.esm.js +391 -0
- package/dist/{chunk-QU7ET55D.esm.js → chunk-MBS7XHV2.esm.js} +335 -192
- package/dist/chunk-NO5AWNWT.esm.js +1066 -0
- package/dist/{chunk-VXMM2HX7.esm.js → chunk-R4RQT2XQ.esm.js} +3 -3
- package/dist/{chunk-OFWC4MIY.esm.js → chunk-TMP7RIA7.esm.js} +5 -3
- package/dist/{chunk-GQBYZRD7.esm.js → chunk-WQIQW7EM.esm.js} +40 -28
- package/dist/{chunk-OH23AX2V.esm.js → chunk-YYEI6XME.esm.js} +441 -957
- package/dist/{circular-progress-Ci8L-Hfa.d.cts → circular-progress-B9nnwzCu.d.cts} +20 -78
- package/dist/{circular-progress-Ci8L-Hfa.d.ts → circular-progress-B9nnwzCu.d.ts} +20 -78
- package/dist/index.cjs +5547 -3192
- package/dist/index.d.cts +10 -8
- package/dist/index.d.ts +10 -8
- package/dist/index.esm.js +76 -44
- package/dist/kanban-FFBeaZPS.d.cts +212 -0
- package/dist/kanban-FFBeaZPS.d.ts +212 -0
- package/dist/{layout-BTJyU8wd.d.ts → layout-CuKeSY74.d.ts} +1 -1
- package/dist/{layout-C2G-FcER.d.cts → layout-PzVwkJyL.d.cts} +1 -1
- package/dist/site.cjs +429 -0
- package/dist/site.d.cts +198 -0
- package/dist/site.d.ts +198 -0
- package/dist/site.esm.js +23 -0
- package/dist/studio.cjs +722 -363
- package/dist/studio.d.cts +2 -2
- package/dist/studio.d.ts +2 -2
- package/dist/studio.esm.js +8 -6
- package/dist/styles.css +56 -0
- package/dist/{timbal-v2-button-CNfdwGq4.d.cts → timbal-v2-button-DCAZNyUx.d.cts} +3 -3
- package/dist/{timbal-v2-button-CNfdwGq4.d.ts → timbal-v2-button-DCAZNyUx.d.ts} +3 -3
- package/dist/ui.cjs +1553 -708
- package/dist/ui.d.cts +11 -4
- package/dist/ui.d.ts +11 -4
- package/dist/ui.esm.js +45 -36
- package/dist/{welcome-DXqsGTwH.d.ts → welcome-B00oH5Io.d.cts} +5 -1
- package/dist/{welcome-BFGRoNfK.d.cts → welcome-DU-4NTjZ.d.ts} +5 -1
- package/package.json +9 -1
- package/dist/button-BoyX5pM_.d.cts +0 -18
- package/dist/button-BoyX5pM_.d.ts +0 -18
- package/dist/chunk-UCGVL7ZY.esm.js +0 -52
|
@@ -4,38 +4,51 @@ import {
|
|
|
4
4
|
ShellInsetProvider,
|
|
5
5
|
studioChromeShellStyle,
|
|
6
6
|
studioSidebarWidthTransition
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-ELEY66OH.esm.js";
|
|
8
8
|
import {
|
|
9
9
|
ChartArtifactView,
|
|
10
10
|
LineAreaChart,
|
|
11
11
|
Thread,
|
|
12
12
|
TimbalRuntimeProvider,
|
|
13
|
+
formatCompact,
|
|
13
14
|
monotoneAreaPath,
|
|
14
15
|
monotoneLinePath,
|
|
15
16
|
studioIntegrationCardClass,
|
|
16
17
|
studioTopbarPillHeightClass,
|
|
17
18
|
toNum
|
|
18
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-WQIQW7EM.esm.js";
|
|
19
20
|
import {
|
|
20
21
|
Checkbox,
|
|
22
|
+
CopyButton,
|
|
23
|
+
Popover,
|
|
24
|
+
PopoverContent,
|
|
25
|
+
PopoverTrigger,
|
|
26
|
+
Select,
|
|
27
|
+
SelectContent,
|
|
28
|
+
SelectItem,
|
|
29
|
+
SelectTrigger,
|
|
30
|
+
SelectValue,
|
|
21
31
|
Skeleton
|
|
22
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-NO5AWNWT.esm.js";
|
|
23
33
|
import {
|
|
24
34
|
PillSegmentedTabs
|
|
25
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-R4RQT2XQ.esm.js";
|
|
26
36
|
import {
|
|
27
37
|
Button,
|
|
28
38
|
Dialog,
|
|
29
39
|
DialogContent,
|
|
30
40
|
DialogTitle,
|
|
41
|
+
TIMBAL_V2_ELEVATED_GRADIENT,
|
|
31
42
|
TIMBAL_V2_ELEVATED_SURFACE,
|
|
32
43
|
TIMBAL_V2_LOGO_TILE,
|
|
33
44
|
TIMBAL_V2_SWITCH_THUMB,
|
|
34
45
|
TIMBAL_V2_SWITCH_TRACK_OFF,
|
|
35
46
|
TimbalV2Button,
|
|
36
|
-
cn,
|
|
37
47
|
controlClass
|
|
38
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-MBS7XHV2.esm.js";
|
|
49
|
+
import {
|
|
50
|
+
cn
|
|
51
|
+
} from "./chunk-EDEKQYSU.esm.js";
|
|
39
52
|
|
|
40
53
|
// src/design/ui-vocabulary.ts
|
|
41
54
|
var SEMANTIC_COLOR_TOKENS = [
|
|
@@ -191,6 +204,13 @@ var HOUSE_RULES = [
|
|
|
191
204
|
rule: "Don't nest a bordered card inside another bordered card. Group with spacing or a Section instead.",
|
|
192
205
|
why: "Card-in-card doubles borders and shadows for no information gain."
|
|
193
206
|
},
|
|
207
|
+
{
|
|
208
|
+
id: "no-table-in-card",
|
|
209
|
+
rule: "Never wrap a DataTable or table inside a Card, SurfaceCard, or ArtifactCard.",
|
|
210
|
+
why: "The DataTable is already designed to sit flat with its own borders and shadows. Wrapping it inside a card adds redundant borders, margins, and shadows, which looks like slop.",
|
|
211
|
+
slop: "<Card><DataTable columns={columns} rows={rows} getRowKey={getRowKey} /></Card>",
|
|
212
|
+
good: "<DataTable columns={columns} rows={rows} getRowKey={getRowKey} />"
|
|
213
|
+
},
|
|
194
214
|
{
|
|
195
215
|
id: "no-row-dividers",
|
|
196
216
|
rule: "Don't put a divider between every list row. Use spacing or zebra striping.",
|
|
@@ -212,6 +232,27 @@ var HOUSE_RULES = [
|
|
|
212
232
|
why: "Hand-rolled controls drift from the shared control-surface skin and look foreign next to kit controls.",
|
|
213
233
|
slop: `<button className="rounded-lg border border-input bg-transparent px-3 h-9">`,
|
|
214
234
|
good: `<SelectTrigger><SelectValue /></SelectTrigger>`
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
id: "no-title-repetition",
|
|
238
|
+
rule: "Never repeat the page title/description inside a Section, Card, or Table header.",
|
|
239
|
+
why: "If the Page already has a title, repeating it in the first child section or table is redundant and wastes vertical space.",
|
|
240
|
+
slop: `<Page title="Orders"><Section title="Orders"><DataTable ... /></Section></Page>`,
|
|
241
|
+
good: `<Page title="Orders"><DataTable ... /></Page>`
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
id: "no-chat-wrapping",
|
|
245
|
+
rule: "Never wrap TimbalChat or AppChatPanel in a Card, Section, or custom bordered/padded container, and never add custom heading/status blocks above it.",
|
|
246
|
+
why: "The chat component is a full-bleed, full-height surface that handles its own layout, welcome screen, and suggestions. Wrapping it or hand-rolling headers/online badges inside the page content is redundant and wastes layout space.",
|
|
247
|
+
slop: `<Page title="Assistant"><Card><div className="flex justify-between"><h3>TIBA Concierge</h3><span>Online</span></div><TimbalChat /></Card></Page>`,
|
|
248
|
+
good: `<Page fill><TimbalChat workforceId="..." welcome={{ heading: "Hola, soy el Concierge de TIBA", subheading: "Preg\xFAntame sobre..." }} /></Page>`
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
id: "no-colored-hover",
|
|
252
|
+
rule: "Interactive cards and list items must use neutral hover states \u2014 never hard-code colored backgrounds or borders on hover.",
|
|
253
|
+
why: "Colored hover highlights (like hover:bg-primary/5 or hover:bg-emerald-500/5) look dirty and break the neutral chrome aesthetic. Use the kit's clean, neutral hover states, or AlertCard, or TIMBAL_V2_SECONDARY_CHROME hover layers.",
|
|
254
|
+
slop: `<Card className="hover:bg-emerald-500/10 hover:border-emerald-500/30">`,
|
|
255
|
+
good: `<AlertCard onClick={handleClick}>`
|
|
215
256
|
}
|
|
216
257
|
];
|
|
217
258
|
|
|
@@ -246,14 +287,14 @@ The most common failure is shipping the **same** layout every time: sidebar + to
|
|
|
246
287
|
| Archetype | When | Compose |
|
|
247
288
|
|-----------|------|---------|
|
|
248
289
|
| **Sidebar dashboard** | Multi-section product (CRM, billing, ops) with nav | \`StudioSidebar\` in \`AppShell.sidebar\` + \`Page\` \u2192 \`Section\` |
|
|
249
|
-
| **Focused / no-chrome** | A single tool or one-screen utility | \`AppShell\` (no sidebar) + \`Page\` (optionally
|
|
290
|
+
| **Focused / no-chrome** | A single tool or one-screen utility | \`AppShell\` (no sidebar) + \`Page width="narrow"\` / \`"prose"\` (optionally with a custom topbar) for a centered focused column |
|
|
250
291
|
| **Bento overview** | Home / at-a-glance dashboards | \`Page\` + an **asymmetric grid** of \`SurfaceCard\` / \`ChartPanel\` / \`StatTile\` spanning different widths (not a uniform row + table) |
|
|
251
292
|
| **Split master\u2013detail** | Inbox, triage queue, record browser, log explorer | \`AppShell contentFill\` + \`Page fill\` + a two-column flex row, each pane \`min-h-0 overflow-y-auto\` |
|
|
252
293
|
| **Full-page chat / canvas** | Chat-first app, editor, map, single full-bleed surface | \`AppShell contentFill\` + headerless \`Page fill\` + a \`min-h-0 flex-1\` child (e.g. \`TimbalChat\`) |
|
|
253
294
|
| **Copilot overlay** | A data app that also wants an assistant | any of the above + \`AppShell chat={<AppChatPanel />}\` (floating, never a second column) |
|
|
254
295
|
| **Section-switcher** | One page, several views | \`SubNav\` / \`PillSegmentedTabs\` (\`trackVariant="flush"\`) switching panels with state/router |
|
|
255
296
|
|
|
256
|
-
Mix them: vary the grid columns, density, header placement (\`Page\` actions vs. a global
|
|
297
|
+
Mix them: vary the grid columns, density, header placement (\`Page\` actions vs. a global topbar), and whether there's a sidebar at all. Two dashboards for two domains should not look identical.
|
|
257
298
|
|
|
258
299
|
### Full-height pages (chat, canvas, split views)
|
|
259
300
|
|
|
@@ -264,7 +305,7 @@ The content region is a **padded scroll area** by default \u2014 great for stack
|
|
|
264
305
|
- Give the filling child **\`min-h-0 flex-1\`** (or \`h-full\`) so its own scroll/footer resolves \u2014 e.g. \`<TimbalChat className="min-h-0 flex-1" />\`, or a two-pane row where each pane is \`min-h-0 overflow-y-auto\`.
|
|
265
306
|
|
|
266
307
|
\`\`\`tsx
|
|
267
|
-
<AppShell contentFill topbar={<
|
|
308
|
+
<AppShell contentFill topbar={<div className="flex justify-end p-4"><ModeToggle /></div>}>
|
|
268
309
|
<Page fill> {/* headerless: omit title */}
|
|
269
310
|
<TimbalChat workforceId="\u2026" className="min-h-0 flex-1" />
|
|
270
311
|
</Page>
|
|
@@ -273,20 +314,25 @@ The content region is a **padded scroll area** by default \u2014 great for stack
|
|
|
273
314
|
|
|
274
315
|
**Don't** size full-height content with \`h-[calc(100dvh-\u2026)]\` (guesses chrome height \u2192 spurious scrollbar) or \`min-h-[\u2026]\` (free-growing floor \u2192 a pinned footer like the chat composer rides down on scroll). Let \`contentFill\` + \`fill\` provide the bounded height. \`Page\` with no \`title\` renders **no header** \u2014 you don't need to abandon \`Page\` to drop a heading.
|
|
275
316
|
|
|
317
|
+
**Full-page Assistant Guidelines (Hardened Layout):**
|
|
318
|
+
When creating a full-page assistant/chat page, let the chat component own the layout and welcome state completely.
|
|
319
|
+
- **Never wrap** \`<TimbalChat>\` or \`<AppChatPanel>\` inside standard card or section elements (like \`<Card>\`, \`<Section>\`, or custom bordered/padded panels). Wrapping degrades the viewport height calculations and wastes valuable layout estate.
|
|
320
|
+
- **Never add custom headings, titles, descriptions, or status badges** (e.g., "Asistente Virtual", "Online", or subtitle text blocks) inside the page. The assistant page context is already implicit. If you need a customized title or greeting, pass it through the \`welcome\` prop config: e.g., \`<TimbalChat welcome={{ heading: "Hola, soy el Concierge de TIBA", subheading: "Preg\xFAntame sobre..." }} />\`.
|
|
321
|
+
|
|
276
322
|
### Module layout (source folders)
|
|
277
323
|
|
|
278
324
|
Presentational groups \u2014 import from the package root, not from these paths:
|
|
279
325
|
|
|
280
326
|
| Folder | Components |
|
|
281
327
|
|--------|------------|
|
|
282
|
-
| \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`FilterField\`, \`ChartPanel\` |
|
|
328
|
+
| \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`FilterField\`, \`FilterDropdown\`, \`ChartPanel\` |
|
|
283
329
|
| \`integrations/\` | \`IntegrationCard\`, \`ConnectionRow\`, \`ConnectionRowList\`, \`IntegrationsEmptyState\`, \`PlanBadge\` |
|
|
284
330
|
| \`settings/\` | \`SettingsSection\`, \`FieldRow\`, \`DangerZone\`, \`FloatingUnsavedChangesBar\` |
|
|
285
|
-
| \`surfaces/\` | \`StatTile\`, \`InfoCard\`, \`ResourceCard\`, \`DescriptionList\`, \`ExpandableSection\`, \`StatusDot\`, \`StatusBadge\`, \`EmptyState\` |
|
|
286
|
-
| \`layout/\` | \`AppShell\`, \`Page
|
|
331
|
+
| \`surfaces/\` | \`StatTile\`, \`InfoCard\`, \`AlertCard\`, \`CatalogCard\`, \`ResourceCard\`, \`DescriptionList\`, \`ExpandableSection\`, \`StatusDot\`, \`StatusBadge\`, \`EmptyState\` |
|
|
332
|
+
| \`layout/\` | \`AppShell\`, \`Page\` (auto-stacks children; \`width\` ladder), \`Section\`, \`Stack\` |
|
|
287
333
|
| \`charts\` (re-exported) | \`LineAreaChart\`, \`PieChart\`, \`RadialChart\`, \`RadarChart\`, \`Sparkline\`, \`CHART_PALETTE\` |
|
|
288
334
|
|
|
289
|
-
Also re-exported
|
|
335
|
+
Also re-exported from \`/app\`: \`Button\`, \`Avatar\` / \`AvatarImage\` / \`AvatarFallback\`, \`Banner\`, \`Timeline\`, \`Kanban\` (drag-and-drop board), \`TimbalChat\`, \`ChartArtifactView\`, \`APP_KIT_AGENT_INSTRUCTIONS\`. Other UI primitives used in block recipes (\`Card\`, \`Input\`, \`Label\`, \`Sheet\`, \`Separator\`, \`AlertDialog\`, \`Badge\`, \`Select\`, \u2026) import from \`@timbal-ai/timbal-react/ui\` or the package root \u2014 **not** from \`/app\`.
|
|
290
336
|
|
|
291
337
|
Theming helpers (import from the package root or \`/app\`): \`createTimbalTheme\`, \`themeToCss\`, \`applyTimbalTheme\`, \`TIMBAL_THEME_PRESETS\`, \`applyThemePreset\`, \`TimbalThemeStyle\`, \`THEME_AGENT_INSTRUCTIONS\`. Theming is **configured by the developer**, not surfaced as an end-user theme picker.
|
|
292
338
|
|
|
@@ -298,9 +344,12 @@ Theming helpers (import from the package root or \`/app\`): \`createTimbalTheme\
|
|
|
298
344
|
| **Chat panel** | \`AppChatPanel\` only; \`Thread\` uses \`variant="panel"\` internally. Dismiss with **X**; trigger is a **text-only** pill (e.g. "Assistant") \u2014 **no** MessageSquare or chat icons on the shell trigger. |
|
|
299
345
|
| **Context** | Do not show raw JSON context in the panel header; keep context in \`AppCopilotProvider\` only. |
|
|
300
346
|
| **Theming** | Use semantic Tailwind tokens (\`bg-background\`, \`text-foreground\`, \`border-border\`, \`bg-elevated-from\`, etc.) from the host app's \`styles.css\`. To rebrand, **never hand-author OKLCH** \u2014 call \`createTimbalTheme({ brand })\` + \`themeToCss\`/\`applyTimbalTheme\`, or apply a catalog preset (\`TIMBAL_THEME_PRESETS\` / \`applyThemePreset\`). Apply the theme **programmatically** \u2014 do **not** add an end-user theme selector to generated apps. See \`THEME_AGENT_INSTRUCTIONS\`. |
|
|
301
|
-
| **Layout chrome** | \`Page\` \u2192 \`Section\` for main content hierarchy. Default to **no global topbar** \u2014 put account/theme/global actions in the \`Page\` \`actions\` slot (or the sidebar). Add
|
|
347
|
+
| **Layout chrome** | \`Page\` \u2192 \`Section\` for main content hierarchy. Default to **no global topbar** \u2014 put account/theme/global actions in the \`Page\` \`actions\` slot (or the sidebar). Add a topbar only when a full-width global bar is explicitly requested. |
|
|
348
|
+
| **Spacing / gaps** | \`Page\` **auto-stacks its direct children with a vertical gap** \u2014 drop blocks straight in (e.g. \`Page\` \u2192 \`FilterBar\` + \`DataTable\`, or \`MetricRow\` + \`ChartPanel\`) and they breathe; do **not** wrap every block in an extra \`<div>\` (that collapses the gap). For ad-hoc clusters inside a card/row use \`Stack\` (\`gap\`, \`direction\`) instead of bare flex with no gap. Grids still need their own \`gap-*\`. |
|
|
349
|
+
| **Width** | \`Page\` defaults to a wide centered column. For focused / reading / form pages pass \`width\` (\`default\`, \`centered\`, \`narrow\`, \`prose\`) instead of always running full-bleed \u2014 not everything needs the full width. \`width="full"\` opts into edge-to-edge. For full-height pages that should stay centered use \`fill\` + \`fillPadded\`. |
|
|
302
350
|
| **Density** | Set \`density="compact"\` on \`Page\` for tighter dashboards (full-width column, smaller section gaps, card padding, metric tiles, default chart height 220). Default is \`"default"\` (platform spacing). Wrap custom layouts with \`AppDensityProvider\` when not using \`Page\`. Per-section override: \`Section density="compact"\`. Do **not** hand-tune five layers of \`className\` padding when density covers the need. |
|
|
303
351
|
| **Data** | Prefer \`DataTable\` with typed \`columns\` / \`rows\` / \`getRowKey\`; use \`ChartPanel\` with a \`ChartArtifact\` for charts (set \`chartType\` + options \u2014 see the chart catalog). Chart colors come from the theme \`--chart-1..6\` tokens; pass \`series[].color\` / \`colors\` only to override, never raw hex on every series. |
|
|
352
|
+
| **Boards** | For status/triage workflows (pipelines, sprint boards, review queues) use \`Kanban\` \u2014 pass \`columns\` (each with \`cards\`) + \`renderCard\`; handle \`onColumnsChange\`/\`onMove\` to persist. It's drag-and-drop **and** keyboard accessible; don't hand-roll columns of cards. |
|
|
304
353
|
| **Modals** | Use \`AppConfirmDialog\` for destructive/export confirmations. |
|
|
305
354
|
| **Metrics** | Overview KPIs \u2192 \`MetricRow\` or \`MetricChartCard\` (not four separate heavy cards). Values use **normal** font weight, not bold. |
|
|
306
355
|
| **Integrations** | Catalog \u2192 \`IntegrationCard\` grid; connected list \u2192 \`ConnectionRow\` inside \`ConnectionRowList\`. Footer CTAs: \`Button variant="secondary"\`. |
|
|
@@ -332,7 +381,6 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
|
|
|
332
381
|
| Component | Use for |
|
|
333
382
|
|-----------|---------|
|
|
334
383
|
| \`AppShell\` | Shell: optional \`sidebar\`, \`topbar\`, main \`children\`, optional floating \`chat\`. Props: \`chatTriggerLabel\`, \`chatCollapsible\`, \`chatWidth\`, \`chatHeight\`, controlled \`chatOpen\`, **\`contentFill\`** (bounded non-scrolling content region for full-bleed pages \u2014 chat/canvas/split view). |
|
|
335
|
-
| \`AppShellTopbar\` | Full-width top bar: \`start\`, \`actions\` slots. |
|
|
336
384
|
| \`AppCopilotProvider\` | React context for copilot-aware tools (page, filters, selection, etc.). |
|
|
337
385
|
| \`AppChatPanel\` | Floating thread: \`workforceId\`, \`welcome\`, \`debug\`. |
|
|
338
386
|
| \`useAppShellChat\` | Custom open/close trigger when \`hideChatTrigger\` on shell. |
|
|
@@ -341,16 +389,17 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
|
|
|
341
389
|
| \`SubNav\` | **Section switcher** (Overview / Reports pill bar): \`items\`, \`activeId\`, \`onChange\`. Never use Radix/shadcn \`Tabs\` \u2014 it is not in this package. Switch panels with state or the router. |
|
|
342
390
|
| **Menus** | **Select** = short list, no search. **Combobox** = searchable (same trigger as Select). **Command** only inside \`PopoverContent variant="list"\` or Combobox \u2014 never padded default Popover. See \`examples/app-kit/src/recipes/primitives-catalog.ts\`. |
|
|
343
391
|
| \`Breadcrumbs\` | Trail: \`items: [{ label, href? }]\`. |
|
|
344
|
-
| \`Button\` | Actions
|
|
392
|
+
| \`Button\` | Actions. Untitled UI-style \`color\` alias (\`primary\` \\| \`secondary\` \\| \`tertiary\` \\| \`link\` \\| \`primary-destructive\` \\| \`secondary-destructive\`) + \`iconLeading\` / \`iconTrailing\`; or legacy \`variant="secondary"\` (catalog CTAs) / \`variant="default"\` (primary). \`color="primary-destructive"\` is the solid red delete CTA. |
|
|
345
393
|
| \`StatTile\` | Single KPI in its own card (grid of scattered stats). Prefer \`MetricRow\` for a unified overview strip. |
|
|
346
394
|
| \`StatusBadge\` | Status pill: \`tone\` (\`default\`\\|\`primary\`\\|\`success\`\\|\`warn\`\\|\`danger\`\\|\`muted\`), children. Use \`danger\` for critical/error severity. |
|
|
347
395
|
| \`FilterBar\` | Horizontal filter row \u2014 bottom-aligns controls. Mix \`SearchInput\` with labeled \`FilterField\` + \`Select\` (or \`Field\` + \`Select\`); labels sit above, control baselines match. |
|
|
348
396
|
| \`FilterField\` | Optional label wrapper for a filter control inside \`FilterBar\` (severity, status, \u2026). Omit \`label\` for search-only fields. |
|
|
397
|
+
| \`FilterDropdown\` | Single-button **multi-facet** filter popover for dense list/table views \u2014 **data-driven**: pass \`fields\` describing your **actual columns** (each \`{ id, label, type }\` where \`type\` is \`multiselect\` \\| \`text\` \\| \`daterange\` \\| \`numeric\`; \`multiselect\` takes \`options: [{ value, label, hint?, icon? }]\`). State is keyed by field \`id\` \u2014 controlled (\`value\` + \`onChange\`) or uncontrolled (\`defaultValue\`). Renders **removable active-filter pills** next to the trigger by default (\`showActiveChips\`); wire \`onChange\` to actually filter your rows. **Always derive \`fields\` from the table's columns/data; never ship the default example facets.** Use when one \`FilterBar\` row isn't enough. |
|
|
349
398
|
| \`SearchInput\` | Filter field with consistent app styling. |
|
|
350
399
|
| \`DataTable\` | Sortable table: \`columns\`, \`rows\`, \`getRowKey\`, optional \`sort\` / \`onSortChange\`, \`emptyTitle\`, \`showRowCount\`, \`caption\`, \`truncate: true\` on columns with long text. **Scales:** \`pageSize\` (built-in client pager), \`selectable\` + \`onSelectionChange\` (checkbox column for bulk actions), \`loading\` (skeleton rows). \`onRowClick\` for row \u2192 detail (open a \`Sheet\`). |
|
|
351
400
|
| \`Avatar\` / \`AvatarFallback\` | User initials: \`variant="secondary"\` (or \`primary\` / \`chart\` alias) on **both** \`Avatar\` and \`AvatarFallback\` \u2014 same chrome as catalog **Action** buttons (\`Button variant="secondary"\`: elevated gradient, \`border-border\`, \`shadow-card\`, \`text-foreground\`). Never dark primary CTA fill or raw \`bg-blue-600\`. |
|
|
352
401
|
| \`ChartPanel\` | Same shell as \`MetricChartCard\`: title row (\`px-4 pt-4\`), flush plot (\`pt-2\` only) with **no axis ticks** \u2014 hover tooltips show category + value. Pass \`title\` + \`artifact\` (omit \`artifact.title\` to avoid duplicates) or \`children\`. \`loading\` renders a plot-height skeleton. Default plot height follows page \`density\` (300 default, 220 compact); pass \`height\` to override. |
|
|
353
|
-
| \`FieldInput\`, \`FieldTextarea\`, \`FieldSelect\`, \`FieldSwitch\` | Settings-style forms with labels and hints. |
|
|
402
|
+
| \`FieldInput\`, \`FieldTextarea\`, \`FieldSelect\`, \`FieldSwitch\` | Settings-style forms with labels and hints. **\`label\` is required** on every \`Field*\` component; for a label-less filter control use \`FilterField\` (optional label) or \`SearchInput\`. |
|
|
354
403
|
| \`FormSection\` | Grouped form block. |
|
|
355
404
|
| \`AppConfirmDialog\` | Confirm/cancel modal: \`open\`, \`onOpenChange\`, \`title\`, \`description\`, \`onConfirm\`. |
|
|
356
405
|
| \`SurfaceCard\`, \`EmptyState\` | Generic surfaces when needed. |
|
|
@@ -396,9 +445,28 @@ Charts run on **recharts** with shadcn \`ChartContainer\` / \`ChartTooltipConten
|
|
|
396
445
|
| Component | Use for |
|
|
397
446
|
|-----------|---------|
|
|
398
447
|
| \`InfoCard\` | Soft callout: \`icon\`, \`title\`, body, \`action\`, \`tone\` (\`info\`/\`success\`/\`warn\`/\`danger\`). |
|
|
448
|
+
| \`AlertCard\` | Actionable / AI-generated alert card with integrated status tags, clear titles, descriptive bodies, and automated action footers. Supports: \`title\`, \`description?\`, \`category?\`, \`categoryTone?\`, \`status?\`, \`statusTone?\`, \`action?\` (action description text), \`trailing?\` (custom element), and \`onClick?\`. Renders with a beautiful neutral hover state, avoiding loud/colored hover background effects. |
|
|
449
|
+
| \`CatalogCard\` | Highly sophisticated marketplace/routing catalog tile (models, datasets, tools). Supports: \`title\`, \`subtitle?\`, \`logo?\` (e.g. brand logo), \`badge?\` (e.g. model type badge), \`description?\`, \`tags?\` (metadata array), \`href?\` (title link), \`footerLinks?\` (bottom-left links), \`copyValue?\` (copy ID button), \`actions?\`, and \`onClick?\` (neutral-hover interactive state). |
|
|
399
450
|
| \`DescriptionList\` | Read-only key/value metadata: \`items: [{ label, value }]\`, optional \`stacked\`. |
|
|
400
451
|
| \`ExpandableSection\` | Collapsible block: \`title\`, \`icon\`, \`count\`, animated body (\`aria-expanded\` + \`aria-controls\`). |
|
|
401
452
|
| \`StatusDot\` | Status indicator dot: \`tone\`, \`label\`, \`pulse\`. |
|
|
453
|
+
| \`Banner\` | Page-level announcement bar: \`tone\` (\`default\`\\|\`primary\`\\|\`success\`\\|\`warn\`\\|\`danger\`), \`icon\`, \`title\`, body as children, right-aligned \`actions\`, \`onDismiss\` (renders the dismiss X). For in-form/field messages use \`InfoCard\` or \`Alert\` instead. |
|
|
454
|
+
| \`Timeline\` | Vertical event log: \`items: [{ id, title, description?, meta?, tone?, icon? }]\`. Presentational \u2014 pass already-formatted timestamps in \`meta\`. |
|
|
455
|
+
|
|
456
|
+
#### More \`/ui\` primitives (import from \`/ui\` or the package root)
|
|
457
|
+
|
|
458
|
+
These ship in the same design system but aren't re-exported from \`/app\`. Reach for them before hand-rolling \u2014 they're all dependency-free and on the shared tokens / control surface.
|
|
459
|
+
|
|
460
|
+
| Component | Use for |
|
|
461
|
+
|-----------|---------|
|
|
462
|
+
| \`Stepper\` | Ordered step indicator for wizards / onboarding (horizontal or vertical; complete / active / upcoming states). |
|
|
463
|
+
| \`Rating\` | Star rating \u2014 interactive (keyboard + hover preview) or \`readOnly\`; controlled or uncontrolled. |
|
|
464
|
+
| \`NumberField\` | Numeric input with \u2212/+ steppers on the control surface; clamps to \`min\`/\`max\`, steps by \`step\`. |
|
|
465
|
+
| \`TagInput\` | Chips / token input; commits on Enter/comma, removes on Backspace, optional \`dedupe\`/\`max\`. |
|
|
466
|
+
| \`AvatarGroup\` | Overlapping avatar stack with an optional \`+N\` overflow chip (\`max\`, \`spacing\`). |
|
|
467
|
+
| \`CircularProgress\` | Lightweight SVG progress ring \u2014 determinate (optional center label) or indeterminate. |
|
|
468
|
+
| \`CopyButton\` | Click-to-copy with a transient check confirmation; icon-only or with a label. |
|
|
469
|
+
| \`Snippet\` | Single-line code / command on the elevated surface with a built-in copy button. |
|
|
402
470
|
|
|
403
471
|
Studio chrome (\`StudioSidebar\`, \`ModeToggle\`, \u2026) lives in \`@timbal-ai/timbal-react/studio\` \u2014 optional, not required for every dashboard.
|
|
404
472
|
|
|
@@ -466,6 +534,9 @@ import {
|
|
|
466
534
|
DataTable,
|
|
467
535
|
FilterBar,
|
|
468
536
|
FilterField,
|
|
537
|
+
FilterDropdown,
|
|
538
|
+
AlertCard,
|
|
539
|
+
CatalogCard,
|
|
469
540
|
} from "@timbal-ai/timbal-react/app";
|
|
470
541
|
\`\`\`
|
|
471
542
|
|
|
@@ -476,6 +547,17 @@ import {
|
|
|
476
547
|
| \`examples/app-kit/recipes/*\` | **Recipes** \u2014 one pattern each (~20\u201380 lines). Use for capability, not layout. |
|
|
477
548
|
| \`examples/app-kit/reference/operations-dashboard.tsx\` | **Reference only** \u2014 full wired app; do not treat as the default generated layout. |
|
|
478
549
|
|
|
550
|
+
### API gotchas \u2014 props that do NOT exist (don't guess, don't retry variations)
|
|
551
|
+
|
|
552
|
+
The compiler rejects these every time; write against the documented shapes above instead:
|
|
553
|
+
|
|
554
|
+
- \`FieldInput\` / \`FieldTextarea\` / \`FieldSelect\` / \`FieldSwitch\` **require \`label\`** \u2014 TS2741 if omitted. Label-less control \u2192 \`FilterField\` or \`SearchInput\`.
|
|
555
|
+
- Full-height layout: \`fill\` lives on \`Page\` (paired with \`AppShell contentFill\`) \u2014 there is no \`fill\` on \`Section\` or \`AppShell\`.
|
|
556
|
+
- \`WorkforceSelector\` (chat subpath / root) is a **controlled** picker: \`workforces\` (e.g. from \`useWorkforces()\`), \`value\`, \`onChange(id)\`, optional \`hideWhenSingle\` / \`placeholder\`. It does **not** fetch and has no \`workforceId\` prop \u2014 for an embedded assistant use \`AppChatPanel workforceId="\u2026"\`.
|
|
557
|
+
- There is **no \`Tabs\` export** \u2014 section switching uses \`SubNav\` or \`PillSegmentedTabs\`.
|
|
558
|
+
- \`Banner\` and \`Timeline\` exist (see menu) \u2014 import them from \`/app\`, \`/ui\`, or the root.
|
|
559
|
+
- If a prop still type-errors, read the actual definitions in \`node_modules/@timbal-ai/timbal-react/dist/app.d.ts\` once instead of retrying guessed prop names.
|
|
560
|
+
|
|
479
561
|
### Rules
|
|
480
562
|
|
|
481
563
|
- Prefer stable props documented above; avoid undocumented \`design/*\` class exports (\`connectionRowListClass\` is exported but \`ConnectionRowList\` is preferred).
|
|
@@ -506,6 +588,7 @@ var GRADIENT_DIRECTIONS = /* @__PURE__ */ new Set([
|
|
|
506
588
|
]);
|
|
507
589
|
var ICON_IMPORT_RE = /from\s+["']lucide-react["']/;
|
|
508
590
|
var RAW_CONTROL_SURFACE_RE = /\bborder-input\b/;
|
|
591
|
+
var COLORED_HOVER_RE = /\bhover:(?:bg|from|to|via)-(?:primary|destructive|success|warn|danger|blue|emerald|green|amber|red|indigo|violet|purple|pink|rose|sky|cyan|teal|lime|yellow|orange|fuchsia)\b/;
|
|
509
592
|
var RESERVED_GRADIENT_SET = new Set(RESERVED_GRADIENT_TOKENS);
|
|
510
593
|
function stripVariants(util) {
|
|
511
594
|
return util.replace(/^(?:[a-z-]+:)*/, "");
|
|
@@ -522,7 +605,14 @@ function lintGeneratedUi(source, options = {}) {
|
|
|
522
605
|
let usesLucide = false;
|
|
523
606
|
let iconUsageCount = 0;
|
|
524
607
|
let dividerRunCount = 0;
|
|
608
|
+
let pageTitle = null;
|
|
609
|
+
const pageTitleMatch = source.match(/<Page\s+[^>]*\btitle=(?:"([^"]+)"|\{["']([^"']+)["']\})/);
|
|
610
|
+
if (pageTitleMatch) {
|
|
611
|
+
pageTitle = (pageTitleMatch[1] || pageTitleMatch[2]).trim().toLowerCase();
|
|
612
|
+
}
|
|
613
|
+
const hasChat = /\b(?:TimbalChat|AppChatPanel|Thread)\b/.test(source);
|
|
525
614
|
const lucideNames = /* @__PURE__ */ new Set();
|
|
615
|
+
const openCards = [];
|
|
526
616
|
for (let i = 0; i < lines.length; i++) {
|
|
527
617
|
const line = lines[i];
|
|
528
618
|
const lineNo = i + 1;
|
|
@@ -538,6 +628,33 @@ function lintGeneratedUi(source, options = {}) {
|
|
|
538
628
|
continue;
|
|
539
629
|
}
|
|
540
630
|
if (isCommentOrImport(line)) continue;
|
|
631
|
+
const cardMatch = line.match(/<(Card|SurfaceCard|ArtifactCard)\b/);
|
|
632
|
+
if (cardMatch) {
|
|
633
|
+
const isSelfClosing = /\/>/.test(line) && line.indexOf(cardMatch[0]) < line.indexOf("/>");
|
|
634
|
+
if (!isSelfClosing) {
|
|
635
|
+
openCards.push({ type: cardMatch[1], line: lineNo });
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
const closeMatch = line.match(/<\/(Card|SurfaceCard|ArtifactCard)\b/);
|
|
639
|
+
if (closeMatch && openCards.length > 0) {
|
|
640
|
+
const idx = openCards.map((c) => c.type).lastIndexOf(closeMatch[1]);
|
|
641
|
+
if (idx !== -1) {
|
|
642
|
+
openCards.splice(idx, 1);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
if (openCards.length > 0) {
|
|
646
|
+
const tableMatch = line.match(/<(DataTable|table|Table)\b/);
|
|
647
|
+
if (tableMatch) {
|
|
648
|
+
const parentCard = openCards[openCards.length - 1];
|
|
649
|
+
findings.push({
|
|
650
|
+
rule: "no-table-in-card",
|
|
651
|
+
severity: "error",
|
|
652
|
+
line: lineNo,
|
|
653
|
+
message: `Table inside card. Never wrap a <${tableMatch[1]}> or table inside a <${parentCard.type}> (opened on L${parentCard.line}). Place the table directly on the Page or Section instead.`,
|
|
654
|
+
snippet: line.trim().slice(0, 120)
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
}
|
|
541
658
|
const rawColors = line.match(RAW_COLOR_RE);
|
|
542
659
|
if (rawColors) {
|
|
543
660
|
for (const m of rawColors) {
|
|
@@ -578,6 +695,15 @@ function lintGeneratedUi(source, options = {}) {
|
|
|
578
695
|
snippet: line.trim().slice(0, 120)
|
|
579
696
|
});
|
|
580
697
|
}
|
|
698
|
+
if (COLORED_HOVER_RE.test(line)) {
|
|
699
|
+
findings.push({
|
|
700
|
+
rule: "no-colored-hover",
|
|
701
|
+
severity: "warn",
|
|
702
|
+
line: lineNo,
|
|
703
|
+
message: "Colored hover background/gradient. House style: interactive cards and list items must use neutral hover states \u2014 never hard-code colored backgrounds or borders on hover.",
|
|
704
|
+
snippet: line.trim().slice(0, 120)
|
|
705
|
+
});
|
|
706
|
+
}
|
|
581
707
|
if (BOLD_VALUE_RE.test(line)) {
|
|
582
708
|
findings.push({
|
|
583
709
|
rule: "bold-metric",
|
|
@@ -624,6 +750,44 @@ function lintGeneratedUi(source, options = {}) {
|
|
|
624
750
|
if (hits) iconUsageCount += hits.length;
|
|
625
751
|
}
|
|
626
752
|
}
|
|
753
|
+
if (pageTitle) {
|
|
754
|
+
const titleMatch = line.match(/<(Section|ChartPanel|Card|DataTable|SurfaceCard)\s+[^>]*\btitle=(?:"([^"]+)"|\{["']([^"']+)["']\})/);
|
|
755
|
+
if (titleMatch) {
|
|
756
|
+
const element = titleMatch[1];
|
|
757
|
+
const titleVal = (titleMatch[2] || titleMatch[3]).trim().toLowerCase();
|
|
758
|
+
if (titleVal === pageTitle || titleVal.includes(pageTitle) || pageTitle.includes(titleVal)) {
|
|
759
|
+
findings.push({
|
|
760
|
+
rule: "no-title-repetition",
|
|
761
|
+
severity: "warn",
|
|
762
|
+
line: lineNo,
|
|
763
|
+
message: `Title repetition. The <${element}> title "${titleVal}" repeats or is very similar to the <Page> title "${pageTitle}". Drop the title from the child element or use a title-less Section to avoid redundant headings.`,
|
|
764
|
+
snippet: line.trim().slice(0, 120)
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
if (hasChat) {
|
|
770
|
+
const wrappingMatch = line.match(/<(Card|Section|SurfaceCard|FormSection|SettingsSection)\b/);
|
|
771
|
+
if (wrappingMatch) {
|
|
772
|
+
findings.push({
|
|
773
|
+
rule: "no-chat-wrapping",
|
|
774
|
+
severity: "error",
|
|
775
|
+
line: lineNo,
|
|
776
|
+
message: `Chat component wrapping. Never wrap TimbalChat or AppChatPanel inside a <${wrappingMatch[1]}> or custom bordered container. Let the chat component fill the page or slot directly.`,
|
|
777
|
+
snippet: line.trim().slice(0, 120)
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
const headingMatch = line.match(/<(h[1-6])\b/);
|
|
781
|
+
if (headingMatch) {
|
|
782
|
+
findings.push({
|
|
783
|
+
rule: "no-chat-wrapping",
|
|
784
|
+
severity: "error",
|
|
785
|
+
line: lineNo,
|
|
786
|
+
message: `Custom heading in chat view. Do not render custom <${headingMatch[1]}> headings on the chat page. Pass welcome.heading to TimbalChat if you need to customize the welcome title.`,
|
|
787
|
+
snippet: line.trim().slice(0, 120)
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
}
|
|
627
791
|
}
|
|
628
792
|
if (usesLucide && iconUsageCount > maxIcons) {
|
|
629
793
|
findings.push({
|
|
@@ -1356,7 +1520,20 @@ var TimbalThemeStyle = ({
|
|
|
1356
1520
|
|
|
1357
1521
|
// src/design/app-classes.ts
|
|
1358
1522
|
var appPageColumnClass = "mx-auto w-full max-w-[100rem] px-4 md:px-6 lg:px-8";
|
|
1359
|
-
var
|
|
1523
|
+
var PAGE_WIDTH_MAXW = {
|
|
1524
|
+
full: "max-w-none",
|
|
1525
|
+
wide: "max-w-[100rem]",
|
|
1526
|
+
default: "max-w-7xl",
|
|
1527
|
+
centered: "max-w-5xl",
|
|
1528
|
+
narrow: "max-w-3xl",
|
|
1529
|
+
prose: "max-w-2xl"
|
|
1530
|
+
};
|
|
1531
|
+
function appPageColumn(width, density = "default") {
|
|
1532
|
+
const lateral = density === "compact" ? "px-3 md:px-4" : "px-4 md:px-6 lg:px-8";
|
|
1533
|
+
const maxW = width ? PAGE_WIDTH_MAXW[width] : density === "compact" ? "max-w-none" : "max-w-[100rem]";
|
|
1534
|
+
return cn("mx-auto w-full", maxW, lateral);
|
|
1535
|
+
}
|
|
1536
|
+
var appShellTopbarInsetClass = "mx-auto w-full max-w-[100rem] px-4 md:px-6 lg:px-8";
|
|
1360
1537
|
var appShellInsetTopClass = "pt-4 md:pt-6";
|
|
1361
1538
|
var appShellInsetBottomClass = "pb-8 md:pb-10";
|
|
1362
1539
|
var appShellTopbarRowClass = cn(
|
|
@@ -1423,6 +1600,15 @@ var APP_DENSITY_CLASSES = {
|
|
|
1423
1600
|
default: appPageHeaderClass,
|
|
1424
1601
|
compact: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between pb-2 pt-1"
|
|
1425
1602
|
},
|
|
1603
|
+
/**
|
|
1604
|
+
* Vertical rhythm between a `Page`'s direct content blocks. Page-level gap so
|
|
1605
|
+
* stacked blocks (FilterBar + DataTable, MetricRow + ChartPanel, bare cards)
|
|
1606
|
+
* never render flush — the #1 source of gap-less generated layouts.
|
|
1607
|
+
*/
|
|
1608
|
+
pageStack: {
|
|
1609
|
+
default: "flex flex-col gap-6",
|
|
1610
|
+
compact: "flex flex-col gap-4"
|
|
1611
|
+
},
|
|
1426
1612
|
section: {
|
|
1427
1613
|
default: appSectionClass,
|
|
1428
1614
|
compact: "flex flex-col gap-2 py-2"
|
|
@@ -1448,12 +1634,12 @@ var APP_DENSITY_CLASSES = {
|
|
|
1448
1634
|
compact: "relative flex min-w-0 flex-1 flex-col gap-1 px-3 py-2 text-left font-normal"
|
|
1449
1635
|
},
|
|
1450
1636
|
metricChartRegion: {
|
|
1451
|
-
default: "relative min-h-0 w-full overflow-x-hidden
|
|
1452
|
-
compact: "relative min-h-0 w-full overflow-x-hidden
|
|
1637
|
+
default: "relative min-h-0 w-full overflow-x-hidden pt-2",
|
|
1638
|
+
compact: "relative min-h-0 w-full overflow-x-hidden pt-1"
|
|
1453
1639
|
},
|
|
1454
1640
|
metricChartPlotRegion: {
|
|
1455
|
-
default: "relative min-h-0 w-full overflow-x-hidden
|
|
1456
|
-
compact: "relative min-h-0 w-full overflow-x-hidden
|
|
1641
|
+
default: "relative min-h-0 w-full overflow-x-hidden px-0 pt-5 pb-3",
|
|
1642
|
+
compact: "relative min-h-0 w-full overflow-x-hidden px-0 pt-3 pb-2"
|
|
1457
1643
|
},
|
|
1458
1644
|
chartPanelBody: {
|
|
1459
1645
|
default: "pt-2 pb-3",
|
|
@@ -1482,8 +1668,155 @@ function useAppDensityClass(key, override) {
|
|
|
1482
1668
|
return appDensityClass(key, override ?? inherited);
|
|
1483
1669
|
}
|
|
1484
1670
|
|
|
1671
|
+
// src/charts/sparkline.tsx
|
|
1672
|
+
import {
|
|
1673
|
+
useId,
|
|
1674
|
+
useRef,
|
|
1675
|
+
useState
|
|
1676
|
+
} from "react";
|
|
1677
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1678
|
+
var Sparkline = ({
|
|
1679
|
+
data,
|
|
1680
|
+
dataKey = "value",
|
|
1681
|
+
color = "var(--primary, #6366f1)",
|
|
1682
|
+
area = true,
|
|
1683
|
+
width = 96,
|
|
1684
|
+
height = 28,
|
|
1685
|
+
strokeWidth = 1.5,
|
|
1686
|
+
className,
|
|
1687
|
+
ariaLabel = "Trend",
|
|
1688
|
+
interactive = false,
|
|
1689
|
+
labels,
|
|
1690
|
+
formatValue
|
|
1691
|
+
}) => {
|
|
1692
|
+
const uid = useId();
|
|
1693
|
+
const containerRef = useRef(null);
|
|
1694
|
+
const [activeIndex, setActiveIndex] = useState(null);
|
|
1695
|
+
const values = data.map((d) => typeof d === "number" ? d : toNum(d[dataKey]));
|
|
1696
|
+
if (values.length === 0) {
|
|
1697
|
+
return /* @__PURE__ */ jsx3("span", { className: cn("inline-block", className), style: { width, height } });
|
|
1698
|
+
}
|
|
1699
|
+
const padX = 0;
|
|
1700
|
+
const padY = strokeWidth + 1;
|
|
1701
|
+
const min = Math.min(...values);
|
|
1702
|
+
const max = Math.max(...values);
|
|
1703
|
+
const range = max - min || 1;
|
|
1704
|
+
const innerW = width - padX * 2;
|
|
1705
|
+
const innerH = height - padY * 2;
|
|
1706
|
+
const points = values.map((v, i) => ({
|
|
1707
|
+
x: padX + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
|
|
1708
|
+
y: padY + innerH - (v - min) / range * innerH
|
|
1709
|
+
}));
|
|
1710
|
+
const svg = /* @__PURE__ */ jsxs2(
|
|
1711
|
+
"svg",
|
|
1712
|
+
{
|
|
1713
|
+
width,
|
|
1714
|
+
height,
|
|
1715
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
1716
|
+
className: cn("block", interactive ? "h-full w-full" : className),
|
|
1717
|
+
role: "img",
|
|
1718
|
+
"aria-label": ariaLabel,
|
|
1719
|
+
preserveAspectRatio: "none",
|
|
1720
|
+
children: [
|
|
1721
|
+
area && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1722
|
+
/* @__PURE__ */ jsx3("defs", { children: /* @__PURE__ */ jsxs2("linearGradient", { id: `${uid}-spark`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
|
|
1723
|
+
/* @__PURE__ */ jsx3("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
|
|
1724
|
+
/* @__PURE__ */ jsx3("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
|
|
1725
|
+
] }) }),
|
|
1726
|
+
/* @__PURE__ */ jsx3("path", { d: monotoneAreaPath(points, height - padY), fill: `url(#${uid}-spark)` })
|
|
1727
|
+
] }),
|
|
1728
|
+
/* @__PURE__ */ jsx3(
|
|
1729
|
+
"path",
|
|
1730
|
+
{
|
|
1731
|
+
d: monotoneLinePath(points),
|
|
1732
|
+
fill: "none",
|
|
1733
|
+
stroke: color,
|
|
1734
|
+
strokeWidth,
|
|
1735
|
+
strokeLinecap: "round",
|
|
1736
|
+
strokeLinejoin: "round"
|
|
1737
|
+
}
|
|
1738
|
+
),
|
|
1739
|
+
interactive && activeIndex != null && points[activeIndex] ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1740
|
+
/* @__PURE__ */ jsx3(
|
|
1741
|
+
"line",
|
|
1742
|
+
{
|
|
1743
|
+
x1: points[activeIndex].x,
|
|
1744
|
+
x2: points[activeIndex].x,
|
|
1745
|
+
y1: 0,
|
|
1746
|
+
y2: height,
|
|
1747
|
+
stroke: color,
|
|
1748
|
+
strokeWidth: 1,
|
|
1749
|
+
strokeOpacity: 0.3,
|
|
1750
|
+
vectorEffect: "non-scaling-stroke"
|
|
1751
|
+
}
|
|
1752
|
+
),
|
|
1753
|
+
/* @__PURE__ */ jsx3(
|
|
1754
|
+
"circle",
|
|
1755
|
+
{
|
|
1756
|
+
cx: points[activeIndex].x,
|
|
1757
|
+
cy: points[activeIndex].y,
|
|
1758
|
+
r: 2.75,
|
|
1759
|
+
fill: color,
|
|
1760
|
+
stroke: "var(--background, #fff)",
|
|
1761
|
+
strokeWidth: 1.5,
|
|
1762
|
+
vectorEffect: "non-scaling-stroke"
|
|
1763
|
+
}
|
|
1764
|
+
)
|
|
1765
|
+
] }) : null
|
|
1766
|
+
]
|
|
1767
|
+
}
|
|
1768
|
+
);
|
|
1769
|
+
if (!interactive) return svg;
|
|
1770
|
+
const onMove = (e) => {
|
|
1771
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
1772
|
+
if (rect.width === 0) return;
|
|
1773
|
+
const fraction = (e.clientX - rect.left) / rect.width;
|
|
1774
|
+
const index = Math.max(
|
|
1775
|
+
0,
|
|
1776
|
+
Math.min(values.length - 1, Math.round(fraction * (values.length - 1)))
|
|
1777
|
+
);
|
|
1778
|
+
setActiveIndex(index);
|
|
1779
|
+
};
|
|
1780
|
+
const active = activeIndex != null ? points[activeIndex] : null;
|
|
1781
|
+
const formattedValue = activeIndex != null ? formatValue ? formatValue(values[activeIndex], activeIndex) : formatCompact(values[activeIndex]) : null;
|
|
1782
|
+
return /* @__PURE__ */ jsxs2(
|
|
1783
|
+
"span",
|
|
1784
|
+
{
|
|
1785
|
+
ref: containerRef,
|
|
1786
|
+
className: cn("relative block touch-none", className),
|
|
1787
|
+
style: { width: "100%", height: "100%" },
|
|
1788
|
+
onPointerMove: onMove,
|
|
1789
|
+
onPointerLeave: () => setActiveIndex(null),
|
|
1790
|
+
children: [
|
|
1791
|
+
svg,
|
|
1792
|
+
active ? /* @__PURE__ */ jsxs2(
|
|
1793
|
+
"span",
|
|
1794
|
+
{
|
|
1795
|
+
"aria-hidden": true,
|
|
1796
|
+
className: cn(
|
|
1797
|
+
"pointer-events-none absolute z-30 -translate-x-1/2 -translate-y-full whitespace-nowrap",
|
|
1798
|
+
"rounded-xl border px-3 py-2 text-[11px] font-medium leading-none tabular-nums shadow-[0_12px_40px_-10px_rgba(0,0,0,0.55)]",
|
|
1799
|
+
"border-white/10 bg-gradient-to-b from-neutral-800 to-neutral-950 text-white",
|
|
1800
|
+
"dark:border-black/10 dark:from-white dark:to-neutral-100 dark:text-neutral-900"
|
|
1801
|
+
),
|
|
1802
|
+
style: {
|
|
1803
|
+
left: `${active.x / width * 100}%`,
|
|
1804
|
+
top: `${active.y / height * 100}%`,
|
|
1805
|
+
marginTop: -8
|
|
1806
|
+
},
|
|
1807
|
+
children: [
|
|
1808
|
+
labels?.[activeIndex] != null ? /* @__PURE__ */ jsx3("span", { className: "mr-1.5 text-neutral-300 dark:text-neutral-500", children: labels[activeIndex] }) : null,
|
|
1809
|
+
/* @__PURE__ */ jsx3("span", { children: formattedValue })
|
|
1810
|
+
]
|
|
1811
|
+
}
|
|
1812
|
+
) : null
|
|
1813
|
+
]
|
|
1814
|
+
}
|
|
1815
|
+
);
|
|
1816
|
+
};
|
|
1817
|
+
|
|
1485
1818
|
// src/app/data/metrics-shared.tsx
|
|
1486
|
-
import { jsx as
|
|
1819
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1487
1820
|
var metricCardShellClass = cn(
|
|
1488
1821
|
studioIntegrationCardClass,
|
|
1489
1822
|
"aui-app-metric-card shadow-none",
|
|
@@ -1493,18 +1826,22 @@ var metricTilesRowClass = "grid w-full min-w-0";
|
|
|
1493
1826
|
var metricCellDividerClass = "border-r border-border/40";
|
|
1494
1827
|
var MetricCardHeader = ({
|
|
1495
1828
|
title,
|
|
1829
|
+
titleTag,
|
|
1496
1830
|
titleId,
|
|
1497
1831
|
description,
|
|
1498
1832
|
actions
|
|
1499
1833
|
}) => {
|
|
1500
1834
|
const headerClass = useAppDensityClass("metricCardHeader");
|
|
1501
1835
|
if (!title && !description && !actions) return null;
|
|
1502
|
-
return /* @__PURE__ */
|
|
1503
|
-
/* @__PURE__ */
|
|
1504
|
-
|
|
1505
|
-
|
|
1836
|
+
return /* @__PURE__ */ jsxs3("header", { className: cn(headerClass, "items-center"), children: [
|
|
1837
|
+
/* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
|
|
1838
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2.5", children: [
|
|
1839
|
+
title ? /* @__PURE__ */ jsx4("h3", { id: titleId, className: "text-sm font-semibold text-foreground tracking-tight select-none", children: title }) : null,
|
|
1840
|
+
titleTag ? /* @__PURE__ */ jsx4("span", { className: "inline-flex rounded-full bg-muted/70 px-2 py-0.5 text-[10px] font-medium text-muted-foreground border border-border/30 tracking-tight leading-none select-none", children: titleTag }) : null
|
|
1841
|
+
] }),
|
|
1842
|
+
description ? /* @__PURE__ */ jsx4("p", { className: "mt-1 text-xs text-muted-foreground leading-normal select-none", children: description }) : null
|
|
1506
1843
|
] }),
|
|
1507
|
-
actions ? /* @__PURE__ */
|
|
1844
|
+
actions ? /* @__PURE__ */ jsx4("div", { className: "shrink-0 flex items-center", children: actions }) : null
|
|
1508
1845
|
] });
|
|
1509
1846
|
};
|
|
1510
1847
|
function metricTilesGridColsClass(n) {
|
|
@@ -1515,6 +1852,8 @@ function metricTilesGridColsClass(n) {
|
|
|
1515
1852
|
return "grid-cols-2";
|
|
1516
1853
|
case 3:
|
|
1517
1854
|
return "grid-cols-3";
|
|
1855
|
+
case 4:
|
|
1856
|
+
return "grid-cols-2 md:grid-cols-4";
|
|
1518
1857
|
case 5:
|
|
1519
1858
|
return "grid-cols-2 sm:grid-cols-5";
|
|
1520
1859
|
case 6:
|
|
@@ -1525,12 +1864,34 @@ function metricTilesGridColsClass(n) {
|
|
|
1525
1864
|
}
|
|
1526
1865
|
|
|
1527
1866
|
// src/app/data/MetricTile.tsx
|
|
1528
|
-
import { Fragment as
|
|
1867
|
+
import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1529
1868
|
var trendToneClass = {
|
|
1530
1869
|
up: "border-border/80 bg-muted/40 text-muted-foreground",
|
|
1531
1870
|
down: "border-border/80 bg-muted/40 text-muted-foreground",
|
|
1532
1871
|
neutral: "border-border/80 bg-muted/30 text-muted-foreground"
|
|
1533
1872
|
};
|
|
1873
|
+
var inlineTrendToneClass = {
|
|
1874
|
+
up: "text-blue-500/90 dark:text-blue-400/95 font-medium",
|
|
1875
|
+
down: "text-rose-500/90 dark:text-rose-400/95 font-medium",
|
|
1876
|
+
neutral: "text-muted-foreground/80"
|
|
1877
|
+
};
|
|
1878
|
+
var sparklineToneColor = {
|
|
1879
|
+
up: "var(--primary, #3b82f6)",
|
|
1880
|
+
down: "var(--destructive, #f43f5e)",
|
|
1881
|
+
neutral: "var(--muted-foreground, #64748b)"
|
|
1882
|
+
};
|
|
1883
|
+
var sparklineBandBleed = {
|
|
1884
|
+
default: "-mx-4 -mb-3 h-10",
|
|
1885
|
+
compact: "-mx-3 -mb-2 h-8"
|
|
1886
|
+
};
|
|
1887
|
+
var activeToneClass = {
|
|
1888
|
+
default: "bg-foreground dark:bg-white",
|
|
1889
|
+
primary: "bg-primary",
|
|
1890
|
+
success: "bg-emerald-500",
|
|
1891
|
+
warn: "bg-amber-500",
|
|
1892
|
+
danger: "bg-destructive",
|
|
1893
|
+
neutral: "bg-border"
|
|
1894
|
+
};
|
|
1534
1895
|
var metricTileInteractiveExtraClass = cn(
|
|
1535
1896
|
"bg-transparent hover:bg-transparent active:bg-transparent",
|
|
1536
1897
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-foreground/10"
|
|
@@ -1541,58 +1902,98 @@ var MetricTile = ({
|
|
|
1541
1902
|
unit,
|
|
1542
1903
|
trend,
|
|
1543
1904
|
trendTone = "neutral",
|
|
1905
|
+
trendVariant = "pill",
|
|
1544
1906
|
active = false,
|
|
1907
|
+
activeTone = "default",
|
|
1545
1908
|
showDivider = false,
|
|
1546
1909
|
onSelect,
|
|
1910
|
+
sparklineData,
|
|
1911
|
+
sparklineConfig,
|
|
1912
|
+
sparkline,
|
|
1547
1913
|
ariaLabel,
|
|
1548
1914
|
className
|
|
1549
1915
|
}) => {
|
|
1916
|
+
const density = useAppDensity();
|
|
1550
1917
|
const metricTileBaseClass = useAppDensityClass("metricTile");
|
|
1551
|
-
const
|
|
1552
|
-
|
|
1918
|
+
const hasSparkline = Boolean(sparkline || sparklineData);
|
|
1919
|
+
const bandBleed = sparklineBandBleed[density === "compact" ? "compact" : "default"];
|
|
1920
|
+
const content = /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1921
|
+
active ? /* @__PURE__ */ jsx5(
|
|
1553
1922
|
"span",
|
|
1554
1923
|
{
|
|
1555
1924
|
"aria-hidden": true,
|
|
1556
|
-
className:
|
|
1925
|
+
className: cn(
|
|
1926
|
+
"absolute inset-x-0 bottom-0 h-0.5 z-20 transition-colors duration-200",
|
|
1927
|
+
activeToneClass[activeTone]
|
|
1928
|
+
)
|
|
1557
1929
|
}
|
|
1558
1930
|
) : null,
|
|
1559
|
-
/* @__PURE__ */
|
|
1560
|
-
|
|
1561
|
-
/* @__PURE__ */
|
|
1562
|
-
/* @__PURE__ */
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
"span",
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1931
|
+
/* @__PURE__ */ jsxs4("div", { className: "relative z-10 flex flex-col gap-1 w-full text-left", children: [
|
|
1932
|
+
/* @__PURE__ */ jsx5("span", { className: "text-xs font-semibold text-muted-foreground/80 tracking-tight", children: label }),
|
|
1933
|
+
/* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-2", children: [
|
|
1934
|
+
/* @__PURE__ */ jsxs4("span", { className: "flex items-baseline gap-1", children: [
|
|
1935
|
+
/* @__PURE__ */ jsx5("span", { className: "text-2xl font-normal tracking-tight text-foreground tabular-nums", children: value }),
|
|
1936
|
+
unit ? /* @__PURE__ */ jsx5("span", { className: "text-xs font-medium text-muted-foreground", children: unit }) : null
|
|
1937
|
+
] }),
|
|
1938
|
+
trend ? trendVariant === "inline" ? /* @__PURE__ */ jsx5("span", { className: cn("text-xs leading-none select-none", inlineTrendToneClass[trendTone]), children: trend }) : /* @__PURE__ */ jsx5(
|
|
1939
|
+
"span",
|
|
1940
|
+
{
|
|
1941
|
+
className: cn(
|
|
1942
|
+
"rounded-full border px-1.5 py-0.5 text-xs font-normal select-none",
|
|
1943
|
+
trendToneClass[trendTone]
|
|
1944
|
+
),
|
|
1945
|
+
children: trend
|
|
1946
|
+
}
|
|
1947
|
+
) : null
|
|
1948
|
+
] })
|
|
1949
|
+
] }),
|
|
1950
|
+
hasSparkline ? /* @__PURE__ */ jsx5(
|
|
1951
|
+
"div",
|
|
1952
|
+
{
|
|
1953
|
+
className: cn(
|
|
1954
|
+
"relative z-10 mt-2",
|
|
1955
|
+
bandBleed
|
|
1956
|
+
),
|
|
1957
|
+
children: sparkline ?? /* @__PURE__ */ jsx5(
|
|
1958
|
+
Sparkline,
|
|
1959
|
+
{
|
|
1960
|
+
data: sparklineData,
|
|
1961
|
+
width: 160,
|
|
1962
|
+
height: 40,
|
|
1963
|
+
interactive: true,
|
|
1964
|
+
className: "h-full w-full opacity-90",
|
|
1965
|
+
color: sparklineToneColor[trendTone],
|
|
1966
|
+
...sparklineConfig
|
|
1967
|
+
}
|
|
1968
|
+
)
|
|
1969
|
+
}
|
|
1970
|
+
) : null
|
|
1576
1971
|
] });
|
|
1577
1972
|
const divider = showDivider ? metricCellDividerClass : void 0;
|
|
1578
1973
|
if (onSelect) {
|
|
1579
|
-
return /* @__PURE__ */
|
|
1974
|
+
return /* @__PURE__ */ jsx5(
|
|
1580
1975
|
"button",
|
|
1581
1976
|
{
|
|
1582
1977
|
type: "button",
|
|
1583
1978
|
onClick: onSelect,
|
|
1584
1979
|
"aria-pressed": active,
|
|
1585
1980
|
"aria-label": ariaLabel,
|
|
1586
|
-
className: cn(
|
|
1981
|
+
className: cn(
|
|
1982
|
+
metricTileBaseClass,
|
|
1983
|
+
"transition-all duration-200 hover:bg-muted/10",
|
|
1984
|
+
metricTileInteractiveExtraClass,
|
|
1985
|
+
divider,
|
|
1986
|
+
className
|
|
1987
|
+
),
|
|
1587
1988
|
children: content
|
|
1588
1989
|
}
|
|
1589
1990
|
);
|
|
1590
1991
|
}
|
|
1591
|
-
return /* @__PURE__ */
|
|
1992
|
+
return /* @__PURE__ */ jsx5("div", { className: cn(metricTileBaseClass, divider, className), children: content });
|
|
1592
1993
|
};
|
|
1593
1994
|
|
|
1594
1995
|
// src/app/theme/ThemePresetGallery.tsx
|
|
1595
|
-
import { jsx as
|
|
1996
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1596
1997
|
|
|
1597
1998
|
// src/app/layout/app-shell-chat-context.tsx
|
|
1598
1999
|
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
@@ -1618,8 +2019,8 @@ function useAppShellNav() {
|
|
|
1618
2019
|
|
|
1619
2020
|
// src/app/layout/AppShell.tsx
|
|
1620
2021
|
import { motion, useReducedMotion } from "motion/react";
|
|
1621
|
-
import { useCallback, useMemo, useState } from "react";
|
|
1622
|
-
import { jsx as
|
|
2022
|
+
import { useCallback, useEffect, useMemo, useState as useState2 } from "react";
|
|
2023
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1623
2024
|
var floatingTriggerClass = cn(
|
|
1624
2025
|
"aui-app-shell-chat-trigger-fixed fixed z-50 rounded-full px-5 py-2.5 text-sm font-medium shadow-card-elevated",
|
|
1625
2026
|
"bg-primary text-primary-foreground transition-colors hover:bg-primary/90",
|
|
@@ -1642,21 +2043,32 @@ var AppShellBody = ({
|
|
|
1642
2043
|
insetExpanded,
|
|
1643
2044
|
children
|
|
1644
2045
|
}) => {
|
|
2046
|
+
const [isMobile, setIsMobile] = useState2(() => {
|
|
2047
|
+
if (typeof window === "undefined") return false;
|
|
2048
|
+
return window.innerWidth < 768;
|
|
2049
|
+
});
|
|
2050
|
+
useEffect(() => {
|
|
2051
|
+
if (typeof window === "undefined") return;
|
|
2052
|
+
const onResize = () => setIsMobile(window.innerWidth < 768);
|
|
2053
|
+
onResize();
|
|
2054
|
+
window.addEventListener("resize", onResize);
|
|
2055
|
+
return () => window.removeEventListener("resize", onResize);
|
|
2056
|
+
}, []);
|
|
1645
2057
|
const reducedMotion = useReducedMotion();
|
|
1646
2058
|
const layoutDirection = insetExpanded ? "expand" : "collapse";
|
|
1647
2059
|
const layoutTransition = studioSidebarWidthTransition(
|
|
1648
2060
|
!!reducedMotion,
|
|
1649
2061
|
layoutDirection
|
|
1650
2062
|
);
|
|
1651
|
-
const insetPadding = sidebar ? insetPaddingPx : 0;
|
|
1652
|
-
return /* @__PURE__ */
|
|
2063
|
+
const insetPadding = sidebar && !isMobile ? insetPaddingPx : 0;
|
|
2064
|
+
return /* @__PURE__ */ jsx7(
|
|
1653
2065
|
motion.div,
|
|
1654
2066
|
{
|
|
1655
2067
|
className: "aui-app-shell-body relative z-10 flex min-h-0 min-w-0 flex-1 flex-col",
|
|
1656
2068
|
initial: false,
|
|
1657
2069
|
animate: { paddingLeft: insetPadding },
|
|
1658
2070
|
transition: layoutTransition,
|
|
1659
|
-
children: /* @__PURE__ */
|
|
2071
|
+
children: /* @__PURE__ */ jsxs6(
|
|
1660
2072
|
"div",
|
|
1661
2073
|
{
|
|
1662
2074
|
className: cn(
|
|
@@ -1667,8 +2079,8 @@ var AppShellBody = ({
|
|
|
1667
2079
|
!topbarContent && appShellInsetTopClass
|
|
1668
2080
|
),
|
|
1669
2081
|
children: [
|
|
1670
|
-
topbarContent ? /* @__PURE__ */
|
|
1671
|
-
/* @__PURE__ */
|
|
2082
|
+
topbarContent ? /* @__PURE__ */ jsx7("header", { className: cn("aui-app-shell-topbar-region", appShellTopbarStickyClass), children: /* @__PURE__ */ jsx7("div", { className: appShellTopbarInsetClass, children: topbarContent }) }) : null,
|
|
2083
|
+
/* @__PURE__ */ jsx7(
|
|
1672
2084
|
"main",
|
|
1673
2085
|
{
|
|
1674
2086
|
className: cn(
|
|
@@ -1711,7 +2123,7 @@ var AppShell = ({
|
|
|
1711
2123
|
}) => {
|
|
1712
2124
|
const topbarContent = topbar ?? header;
|
|
1713
2125
|
const hasChat = Boolean(chat);
|
|
1714
|
-
const [uncontrolledNavOpen, setUncontrolledNavOpen] =
|
|
2126
|
+
const [uncontrolledNavOpen, setUncontrolledNavOpen] = useState2(defaultNavOpen);
|
|
1715
2127
|
const isNavControlled = navOpenProp !== void 0;
|
|
1716
2128
|
const navOpen = isNavControlled ? navOpenProp : uncontrolledNavOpen;
|
|
1717
2129
|
const setNavOpen = useCallback(
|
|
@@ -1726,7 +2138,7 @@ var AppShell = ({
|
|
|
1726
2138
|
() => ({ open: navOpen, setOpen: setNavOpen, toggle: toggleNav }),
|
|
1727
2139
|
[navOpen, setNavOpen, toggleNav]
|
|
1728
2140
|
);
|
|
1729
|
-
const [uncontrolledOpen, setUncontrolledOpen] =
|
|
2141
|
+
const [uncontrolledOpen, setUncontrolledOpen] = useState2(defaultChatOpen);
|
|
1730
2142
|
const isChatControlled = chatOpenProp !== void 0;
|
|
1731
2143
|
const chatOpen = isChatControlled ? chatOpenProp : uncontrolledOpen;
|
|
1732
2144
|
const setChatOpen = useCallback(
|
|
@@ -1741,14 +2153,14 @@ var AppShell = ({
|
|
|
1741
2153
|
const toggleChat = useCallback(() => {
|
|
1742
2154
|
setChatOpen(!chatOpen);
|
|
1743
2155
|
}, [chatOpen, setChatOpen]);
|
|
1744
|
-
const [insetPaddingPx, setInsetPaddingPx] =
|
|
2156
|
+
const [insetPaddingPx, setInsetPaddingPx] = useState2(
|
|
1745
2157
|
sidebar ? SIDEBAR_INSET_PX_EXPANDED : 0
|
|
1746
2158
|
);
|
|
1747
2159
|
const reportShellInset = useCallback((insetPx) => {
|
|
1748
2160
|
setInsetPaddingPx(insetPx);
|
|
1749
2161
|
}, []);
|
|
1750
2162
|
const insetExpanded = insetPaddingPx >= SIDEBAR_INSET_PX_EXPANDED;
|
|
1751
|
-
const shellBody = /* @__PURE__ */
|
|
2163
|
+
const shellBody = /* @__PURE__ */ jsx7(
|
|
1752
2164
|
AppShellBody,
|
|
1753
2165
|
{
|
|
1754
2166
|
sidebar,
|
|
@@ -1760,7 +2172,7 @@ var AppShell = ({
|
|
|
1760
2172
|
children
|
|
1761
2173
|
}
|
|
1762
2174
|
);
|
|
1763
|
-
const tree = /* @__PURE__ */
|
|
2175
|
+
const tree = /* @__PURE__ */ jsx7(ShellInsetProvider, { value: sidebar ? reportShellInset : null, children: /* @__PURE__ */ jsxs6(
|
|
1764
2176
|
"div",
|
|
1765
2177
|
{
|
|
1766
2178
|
className: cn(
|
|
@@ -1770,7 +2182,7 @@ var AppShell = ({
|
|
|
1770
2182
|
style: studioChromeShellStyle,
|
|
1771
2183
|
children: [
|
|
1772
2184
|
sidebar,
|
|
1773
|
-
sidebar && navOpen ? /* @__PURE__ */
|
|
2185
|
+
sidebar && navOpen ? /* @__PURE__ */ jsx7(
|
|
1774
2186
|
"button",
|
|
1775
2187
|
{
|
|
1776
2188
|
type: "button",
|
|
@@ -1780,7 +2192,7 @@ var AppShell = ({
|
|
|
1780
2192
|
}
|
|
1781
2193
|
) : null,
|
|
1782
2194
|
shellBody,
|
|
1783
|
-
hasChat && chatOpen ? /* @__PURE__ */
|
|
2195
|
+
hasChat && chatOpen ? /* @__PURE__ */ jsx7(
|
|
1784
2196
|
"div",
|
|
1785
2197
|
{
|
|
1786
2198
|
className: floatingPanelClass,
|
|
@@ -1793,7 +2205,7 @@ var AppShell = ({
|
|
|
1793
2205
|
children: chat
|
|
1794
2206
|
}
|
|
1795
2207
|
) : null,
|
|
1796
|
-
hasChat && chatCollapsible && !chatOpen && !hideChatTrigger ? /* @__PURE__ */
|
|
2208
|
+
hasChat && chatCollapsible && !chatOpen && !hideChatTrigger ? /* @__PURE__ */ jsx7(
|
|
1797
2209
|
"button",
|
|
1798
2210
|
{
|
|
1799
2211
|
type: "button",
|
|
@@ -1806,11 +2218,11 @@ var AppShell = ({
|
|
|
1806
2218
|
]
|
|
1807
2219
|
}
|
|
1808
2220
|
) });
|
|
1809
|
-
const withNav = /* @__PURE__ */
|
|
2221
|
+
const withNav = /* @__PURE__ */ jsx7(AppShellNavProvider, { value: navControls, children: tree });
|
|
1810
2222
|
if (!hasChat) {
|
|
1811
2223
|
return withNav;
|
|
1812
2224
|
}
|
|
1813
|
-
return /* @__PURE__ */
|
|
2225
|
+
return /* @__PURE__ */ jsx7(
|
|
1814
2226
|
AppShellChatProvider,
|
|
1815
2227
|
{
|
|
1816
2228
|
value: {
|
|
@@ -1824,23 +2236,6 @@ var AppShell = ({
|
|
|
1824
2236
|
);
|
|
1825
2237
|
};
|
|
1826
2238
|
|
|
1827
|
-
// src/app/layout/AppShellTopbar.tsx
|
|
1828
|
-
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1829
|
-
var AppShellTopbar = ({
|
|
1830
|
-
start,
|
|
1831
|
-
actions,
|
|
1832
|
-
children,
|
|
1833
|
-
className
|
|
1834
|
-
}) => {
|
|
1835
|
-
return /* @__PURE__ */ jsxs6("div", { className: cn("aui-app-shell-topbar", appShellTopbarRowClass, className), children: [
|
|
1836
|
-
/* @__PURE__ */ jsxs6("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
1837
|
-
start,
|
|
1838
|
-
children
|
|
1839
|
-
] }),
|
|
1840
|
-
actions ? /* @__PURE__ */ jsx7("div", { className: "aui-app-shell-topbar-actions flex shrink-0 items-center gap-2", children: actions }) : null
|
|
1841
|
-
] });
|
|
1842
|
-
};
|
|
1843
|
-
|
|
1844
2239
|
// src/app/layout/AppShellChatTrigger.tsx
|
|
1845
2240
|
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1846
2241
|
var floatingPositionClass = "fixed bottom-6 right-6 z-50 max-sm:bottom-4 max-sm:right-4";
|
|
@@ -1921,34 +2316,32 @@ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
|
1921
2316
|
var PageFrame = ({
|
|
1922
2317
|
children,
|
|
1923
2318
|
breadcrumbs,
|
|
2319
|
+
width,
|
|
1924
2320
|
fill = false,
|
|
2321
|
+
fillPadded = false,
|
|
1925
2322
|
className,
|
|
1926
2323
|
...headerProps
|
|
1927
2324
|
}) => {
|
|
1928
2325
|
const density = useAppDensity();
|
|
1929
|
-
const
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
breadcrumbs,
|
|
1941
|
-
/* @__PURE__ */ jsx11(PageHeader, { ...headerProps }),
|
|
1942
|
-
children
|
|
1943
|
-
]
|
|
1944
|
-
}
|
|
1945
|
-
);
|
|
2326
|
+
const stackClass = useAppDensityClass("pageStack");
|
|
2327
|
+
const columnClass = appPageColumn(width, density);
|
|
2328
|
+
const rootClass = fill ? cn(
|
|
2329
|
+
"flex min-h-0 min-w-0 flex-1 flex-col",
|
|
2330
|
+
fillPadded && columnClass
|
|
2331
|
+
) : columnClass;
|
|
2332
|
+
return /* @__PURE__ */ jsxs8("div", { className: cn("aui-app-page", rootClass, className), "data-density": density, children: [
|
|
2333
|
+
breadcrumbs,
|
|
2334
|
+
/* @__PURE__ */ jsx11(PageHeader, { ...headerProps }),
|
|
2335
|
+
fill ? children : /* @__PURE__ */ jsx11("div", { className: cn("aui-app-page-stack", stackClass), children })
|
|
2336
|
+
] });
|
|
1946
2337
|
};
|
|
1947
2338
|
var Page = ({
|
|
1948
2339
|
density = "default",
|
|
1949
2340
|
children,
|
|
1950
2341
|
breadcrumbs,
|
|
2342
|
+
width,
|
|
1951
2343
|
fill = false,
|
|
2344
|
+
fillPadded = false,
|
|
1952
2345
|
className,
|
|
1953
2346
|
...headerProps
|
|
1954
2347
|
}) => {
|
|
@@ -1956,7 +2349,9 @@ var Page = ({
|
|
|
1956
2349
|
PageFrame,
|
|
1957
2350
|
{
|
|
1958
2351
|
breadcrumbs,
|
|
2352
|
+
width,
|
|
1959
2353
|
fill,
|
|
2354
|
+
fillPadded,
|
|
1960
2355
|
className,
|
|
1961
2356
|
...headerProps,
|
|
1962
2357
|
children
|
|
@@ -1981,15 +2376,61 @@ var Section = ({
|
|
|
1981
2376
|
] });
|
|
1982
2377
|
};
|
|
1983
2378
|
|
|
2379
|
+
// src/app/layout/Stack.tsx
|
|
2380
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
2381
|
+
var GAP_CLASS = {
|
|
2382
|
+
none: "gap-0",
|
|
2383
|
+
xs: "gap-1",
|
|
2384
|
+
sm: "gap-2",
|
|
2385
|
+
md: "gap-4",
|
|
2386
|
+
lg: "gap-6",
|
|
2387
|
+
xl: "gap-8"
|
|
2388
|
+
};
|
|
2389
|
+
var ALIGN_CLASS = {
|
|
2390
|
+
start: "items-start",
|
|
2391
|
+
center: "items-center",
|
|
2392
|
+
end: "items-end",
|
|
2393
|
+
stretch: "items-stretch"
|
|
2394
|
+
};
|
|
2395
|
+
var JUSTIFY_CLASS = {
|
|
2396
|
+
start: "justify-start",
|
|
2397
|
+
center: "justify-center",
|
|
2398
|
+
end: "justify-end",
|
|
2399
|
+
between: "justify-between"
|
|
2400
|
+
};
|
|
2401
|
+
var Stack = ({
|
|
2402
|
+
children,
|
|
2403
|
+
direction = "vertical",
|
|
2404
|
+
gap = "md",
|
|
2405
|
+
align,
|
|
2406
|
+
justify,
|
|
2407
|
+
wrap = false,
|
|
2408
|
+
className
|
|
2409
|
+
}) => /* @__PURE__ */ jsx13(
|
|
2410
|
+
"div",
|
|
2411
|
+
{
|
|
2412
|
+
className: cn(
|
|
2413
|
+
"flex min-w-0",
|
|
2414
|
+
direction === "vertical" ? "flex-col" : "flex-row",
|
|
2415
|
+
GAP_CLASS[gap],
|
|
2416
|
+
align && ALIGN_CLASS[align],
|
|
2417
|
+
justify && JUSTIFY_CLASS[justify],
|
|
2418
|
+
wrap && "flex-wrap",
|
|
2419
|
+
className
|
|
2420
|
+
),
|
|
2421
|
+
children
|
|
2422
|
+
}
|
|
2423
|
+
);
|
|
2424
|
+
|
|
1984
2425
|
// src/app/copilot/app-copilot-context.tsx
|
|
1985
2426
|
import { createContext as createContext4, useContext as useContext4 } from "react";
|
|
1986
|
-
import { jsx as
|
|
2427
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1987
2428
|
var AppCopilotContext = createContext4(null);
|
|
1988
2429
|
var AppCopilotProvider = ({
|
|
1989
2430
|
value,
|
|
1990
2431
|
children
|
|
1991
2432
|
}) => {
|
|
1992
|
-
return /* @__PURE__ */
|
|
2433
|
+
return /* @__PURE__ */ jsx14(AppCopilotContext.Provider, { value, children });
|
|
1993
2434
|
};
|
|
1994
2435
|
function useAppCopilotContext() {
|
|
1995
2436
|
return useContext4(AppCopilotContext) ?? {};
|
|
@@ -1997,7 +2438,7 @@ function useAppCopilotContext() {
|
|
|
1997
2438
|
|
|
1998
2439
|
// src/app/chat/AppChatPanel.tsx
|
|
1999
2440
|
import { XIcon } from "lucide-react";
|
|
2000
|
-
import { jsx as
|
|
2441
|
+
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2001
2442
|
var shellClass = "aui-app-chat-panel flex h-full min-h-0 flex-col overflow-hidden";
|
|
2002
2443
|
var chromeClass = cn(
|
|
2003
2444
|
"aui-app-chat-panel-chrome relative z-20 flex min-h-10 shrink-0 items-center justify-end px-2 pt-2"
|
|
@@ -2043,17 +2484,17 @@ var AppChatPanel = ({
|
|
|
2043
2484
|
}) => {
|
|
2044
2485
|
const shellChat = useAppShellChat();
|
|
2045
2486
|
return /* @__PURE__ */ jsxs10("div", { className: cn(shellClass, className), children: [
|
|
2046
|
-
shellChat?.collapsible ? /* @__PURE__ */
|
|
2487
|
+
shellChat?.collapsible ? /* @__PURE__ */ jsx15("div", { className: chromeClass, children: /* @__PURE__ */ jsx15(
|
|
2047
2488
|
"button",
|
|
2048
2489
|
{
|
|
2049
2490
|
type: "button",
|
|
2050
2491
|
className: closeButtonClass,
|
|
2051
2492
|
onClick: () => shellChat.setOpen(false),
|
|
2052
2493
|
"aria-label": "Close assistant",
|
|
2053
|
-
children: /* @__PURE__ */
|
|
2494
|
+
children: /* @__PURE__ */ jsx15(XIcon, { className: "size-4", "aria-hidden": true })
|
|
2054
2495
|
}
|
|
2055
2496
|
) }) : null,
|
|
2056
|
-
/* @__PURE__ */
|
|
2497
|
+
/* @__PURE__ */ jsx15("div", { className: bodyClass, children: /* @__PURE__ */ jsx15(
|
|
2057
2498
|
TimbalRuntimeProvider,
|
|
2058
2499
|
{
|
|
2059
2500
|
workforceId,
|
|
@@ -2063,7 +2504,7 @@ var AppChatPanel = ({
|
|
|
2063
2504
|
attachmentsUploadUrl,
|
|
2064
2505
|
attachmentsAccept,
|
|
2065
2506
|
debug,
|
|
2066
|
-
children: /* @__PURE__ */
|
|
2507
|
+
children: /* @__PURE__ */ jsx15(
|
|
2067
2508
|
Thread,
|
|
2068
2509
|
{
|
|
2069
2510
|
variant: "panel",
|
|
@@ -2084,41 +2525,121 @@ var AppChatPanel = ({
|
|
|
2084
2525
|
};
|
|
2085
2526
|
|
|
2086
2527
|
// src/app/surfaces/SurfaceCard.tsx
|
|
2087
|
-
import { jsx as
|
|
2088
|
-
var
|
|
2528
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
2529
|
+
var surfaceVariantClass = {
|
|
2530
|
+
default: "",
|
|
2531
|
+
muted: "bg-none bg-muted/40 shadow-none border border-border/20",
|
|
2532
|
+
outline: "bg-none bg-transparent border border-border shadow-none",
|
|
2533
|
+
elevated: "shadow-card-elevated border border-border/80",
|
|
2534
|
+
flat: "bg-card/70 border border-border/50 shadow-[0_1px_3px_rgba(0,0,0,0.03),0_10px_15px_-3px_rgba(0,0,0,0.01)] dark:bg-card/40",
|
|
2535
|
+
hierarchical: "bg-card/90 border border-border/60 shadow-[0_1px_2px_rgba(0,0,0,0.02),0_8px_16px_rgba(0,0,0,0.01)] rounded-2xl dark:bg-card/50"
|
|
2536
|
+
};
|
|
2537
|
+
var surfaceToneClass = {
|
|
2538
|
+
default: "",
|
|
2539
|
+
primary: "ring-1 ring-inset ring-primary/25",
|
|
2540
|
+
success: "ring-1 ring-inset ring-emerald-500/25",
|
|
2541
|
+
warn: "ring-1 ring-inset ring-amber-500/30",
|
|
2542
|
+
danger: "ring-1 ring-inset ring-destructive/25"
|
|
2543
|
+
};
|
|
2544
|
+
var SurfaceCard = ({
|
|
2545
|
+
children,
|
|
2546
|
+
variant = "default",
|
|
2547
|
+
tone = "default",
|
|
2548
|
+
hoverable,
|
|
2549
|
+
onClick,
|
|
2550
|
+
ariaLabel,
|
|
2551
|
+
className
|
|
2552
|
+
}) => {
|
|
2089
2553
|
const surfaceCardClass = useAppDensityClass("surfaceCard");
|
|
2090
|
-
|
|
2554
|
+
const isInteractive = Boolean(onClick);
|
|
2555
|
+
const shouldHover = hoverable ?? isInteractive;
|
|
2556
|
+
const baseClass = cn(
|
|
2557
|
+
"aui-app-surface-card",
|
|
2558
|
+
surfaceCardClass,
|
|
2559
|
+
surfaceVariantClass[variant],
|
|
2560
|
+
surfaceToneClass[tone],
|
|
2561
|
+
shouldHover && "transition-all duration-200 hover:border-border/80 hover:shadow-md",
|
|
2562
|
+
isInteractive && "cursor-pointer select-none active:scale-[0.99] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
2563
|
+
className
|
|
2564
|
+
);
|
|
2565
|
+
if (isInteractive) {
|
|
2566
|
+
return /* @__PURE__ */ jsx16(
|
|
2567
|
+
"button",
|
|
2568
|
+
{
|
|
2569
|
+
type: "button",
|
|
2570
|
+
onClick,
|
|
2571
|
+
"aria-label": ariaLabel,
|
|
2572
|
+
className: baseClass,
|
|
2573
|
+
children
|
|
2574
|
+
}
|
|
2575
|
+
);
|
|
2576
|
+
}
|
|
2577
|
+
return /* @__PURE__ */ jsx16("div", { className: baseClass, children });
|
|
2091
2578
|
};
|
|
2092
2579
|
|
|
2093
2580
|
// src/app/surfaces/StatTile.tsx
|
|
2094
|
-
import { jsx as
|
|
2095
|
-
var
|
|
2581
|
+
import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2582
|
+
var statValueToneClass = {
|
|
2583
|
+
default: "",
|
|
2584
|
+
primary: "text-primary",
|
|
2585
|
+
success: "text-emerald-700 dark:text-emerald-400",
|
|
2586
|
+
warn: "text-amber-700 dark:text-amber-400",
|
|
2587
|
+
danger: "text-destructive"
|
|
2588
|
+
};
|
|
2589
|
+
var StatTile = ({
|
|
2590
|
+
label,
|
|
2591
|
+
value,
|
|
2592
|
+
hint,
|
|
2593
|
+
tone = "default",
|
|
2594
|
+
className
|
|
2595
|
+
}) => {
|
|
2096
2596
|
const statTileClass = useAppDensityClass("statTile");
|
|
2097
2597
|
return /* @__PURE__ */ jsxs11("div", { className: cn("aui-app-stat-tile", statTileClass, className), children: [
|
|
2098
|
-
/* @__PURE__ */
|
|
2099
|
-
/* @__PURE__ */
|
|
2100
|
-
hint ? /* @__PURE__ */
|
|
2598
|
+
/* @__PURE__ */ jsx17("span", { className: appStatLabelClass, children: label }),
|
|
2599
|
+
/* @__PURE__ */ jsx17("span", { className: cn(appStatValueClass, statValueToneClass[tone]), children: value }),
|
|
2600
|
+
hint ? /* @__PURE__ */ jsx17("span", { className: "text-xs text-muted-foreground", children: hint }) : null
|
|
2101
2601
|
] });
|
|
2102
2602
|
};
|
|
2103
2603
|
|
|
2104
2604
|
// src/app/surfaces/EmptyState.tsx
|
|
2105
|
-
import { jsx as
|
|
2605
|
+
import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2106
2606
|
var EmptyState = ({
|
|
2107
2607
|
title,
|
|
2108
2608
|
description,
|
|
2109
2609
|
action,
|
|
2110
|
-
className
|
|
2610
|
+
className,
|
|
2611
|
+
variant = "default"
|
|
2111
2612
|
}) => {
|
|
2112
|
-
const
|
|
2113
|
-
|
|
2114
|
-
/* @__PURE__ */
|
|
2115
|
-
|
|
2116
|
-
|
|
2613
|
+
const densityClass = useAppDensityClass("emptyState");
|
|
2614
|
+
if (variant === "layered") {
|
|
2615
|
+
return /* @__PURE__ */ jsx18(
|
|
2616
|
+
"div",
|
|
2617
|
+
{
|
|
2618
|
+
className: cn(
|
|
2619
|
+
"aui-app-empty-state-layered w-full rounded-2xl border border-border/40 bg-muted/20 p-6 md:p-8 flex items-center justify-center dark:bg-muted/10",
|
|
2620
|
+
className
|
|
2621
|
+
),
|
|
2622
|
+
children: /* @__PURE__ */ jsxs12("div", { className: "w-full max-w-xl rounded-xl border border-border bg-card p-6 md:p-8 shadow-[0_1px_3px_rgba(0,0,0,0.02),0_10px_15px_-3px_rgba(0,0,0,0.01)] flex flex-col items-center justify-center text-center gap-4 transition-all duration-200 hover:border-border/80 dark:bg-card/45", children: [
|
|
2623
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-1.5 items-center justify-center", children: [
|
|
2624
|
+
/* @__PURE__ */ jsx18("h3", { className: "text-base font-semibold text-foreground tracking-tight", children: title }),
|
|
2625
|
+
description ? /* @__PURE__ */ jsx18("p", { className: "max-w-sm text-sm text-muted-foreground leading-relaxed", children: description }) : null
|
|
2626
|
+
] }),
|
|
2627
|
+
action ? /* @__PURE__ */ jsx18("div", { className: "mt-1", children: action }) : null
|
|
2628
|
+
] })
|
|
2629
|
+
}
|
|
2630
|
+
);
|
|
2631
|
+
}
|
|
2632
|
+
const isCompact = variant === "compact";
|
|
2633
|
+
const finalClass = isCompact ? "aui-app-empty-state rounded-xl border border-border bg-gradient-to-b from-elevated-from to-elevated-to p-3 flex flex-col items-center justify-center gap-2 py-8 text-center" : cn("aui-app-empty-state", densityClass);
|
|
2634
|
+
return /* @__PURE__ */ jsxs12("div", { className: cn(finalClass, className), children: [
|
|
2635
|
+
/* @__PURE__ */ jsx18("p", { className: cn(appEmptyStateTitleClass, "font-semibold tracking-tight"), children: title }),
|
|
2636
|
+
description ? /* @__PURE__ */ jsx18("p", { className: appEmptyStateDescriptionClass, children: description }) : null,
|
|
2637
|
+
action ? /* @__PURE__ */ jsx18("div", { className: "mt-1", children: action }) : null
|
|
2117
2638
|
] });
|
|
2118
2639
|
};
|
|
2119
2640
|
|
|
2120
2641
|
// src/app/surfaces/StatusBadge.tsx
|
|
2121
|
-
import { jsx as
|
|
2642
|
+
import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2122
2643
|
var statusBadgeToneClass = {
|
|
2123
2644
|
default: "bg-muted text-foreground ring-border",
|
|
2124
2645
|
primary: "bg-primary/10 text-primary ring-primary/20",
|
|
@@ -2145,13 +2666,13 @@ var StatusBadge = ({
|
|
|
2145
2666
|
"span",
|
|
2146
2667
|
{
|
|
2147
2668
|
className: cn(
|
|
2148
|
-
"aui-app-status-badge inline-flex items-center gap-1.5 rounded-full px-2 py-0.5",
|
|
2669
|
+
"aui-app-status-badge inline-flex w-fit shrink-0 items-center gap-1.5 rounded-full px-2 py-0.5",
|
|
2149
2670
|
"text-xs font-medium leading-none ring-1 ring-inset",
|
|
2150
2671
|
statusBadgeToneClass[tone],
|
|
2151
2672
|
className
|
|
2152
2673
|
),
|
|
2153
2674
|
children: [
|
|
2154
|
-
dot ? /* @__PURE__ */
|
|
2675
|
+
dot ? /* @__PURE__ */ jsx19(
|
|
2155
2676
|
"span",
|
|
2156
2677
|
{
|
|
2157
2678
|
"aria-hidden": true,
|
|
@@ -2168,7 +2689,7 @@ var StatusBadge = ({
|
|
|
2168
2689
|
};
|
|
2169
2690
|
|
|
2170
2691
|
// src/app/surfaces/AppConfirmDialog.tsx
|
|
2171
|
-
import { jsx as
|
|
2692
|
+
import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2172
2693
|
var bodyClass2 = "flex flex-col gap-4 p-6";
|
|
2173
2694
|
var titleClass = "pr-8";
|
|
2174
2695
|
var actionsClass = "flex flex-wrap justify-end gap-2";
|
|
@@ -2183,15 +2704,15 @@ var AppConfirmDialog = ({
|
|
|
2183
2704
|
destructive = false,
|
|
2184
2705
|
className
|
|
2185
2706
|
}) => {
|
|
2186
|
-
return /* @__PURE__ */
|
|
2707
|
+
return /* @__PURE__ */ jsx20(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx20(
|
|
2187
2708
|
DialogContent,
|
|
2188
2709
|
{
|
|
2189
2710
|
className: cn("gap-0 p-0 sm:max-w-md", className),
|
|
2190
2711
|
children: /* @__PURE__ */ jsxs14("div", { className: bodyClass2, children: [
|
|
2191
|
-
/* @__PURE__ */
|
|
2192
|
-
description ? /* @__PURE__ */
|
|
2712
|
+
/* @__PURE__ */ jsx20(DialogTitle, { className: titleClass, children: title }),
|
|
2713
|
+
description ? /* @__PURE__ */ jsx20("p", { className: "text-sm text-muted-foreground", children: description }) : null,
|
|
2193
2714
|
/* @__PURE__ */ jsxs14("div", { className: actionsClass, children: [
|
|
2194
|
-
/* @__PURE__ */
|
|
2715
|
+
/* @__PURE__ */ jsx20(
|
|
2195
2716
|
TimbalV2Button,
|
|
2196
2717
|
{
|
|
2197
2718
|
type: "button",
|
|
@@ -2201,7 +2722,7 @@ var AppConfirmDialog = ({
|
|
|
2201
2722
|
children: cancelLabel
|
|
2202
2723
|
}
|
|
2203
2724
|
),
|
|
2204
|
-
/* @__PURE__ */
|
|
2725
|
+
/* @__PURE__ */ jsx20(
|
|
2205
2726
|
TimbalV2Button,
|
|
2206
2727
|
{
|
|
2207
2728
|
type: "button",
|
|
@@ -2221,7 +2742,7 @@ var AppConfirmDialog = ({
|
|
|
2221
2742
|
};
|
|
2222
2743
|
|
|
2223
2744
|
// src/app/surfaces/InfoCard.tsx
|
|
2224
|
-
import { jsx as
|
|
2745
|
+
import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2225
2746
|
var toneClass = {
|
|
2226
2747
|
neutral: "border-border bg-muted/40",
|
|
2227
2748
|
info: "border-primary/25 bg-primary/5",
|
|
@@ -2229,34 +2750,67 @@ var toneClass = {
|
|
|
2229
2750
|
warn: "border-amber-500/25 bg-amber-500/5",
|
|
2230
2751
|
danger: "border-destructive/25 bg-destructive/5"
|
|
2231
2752
|
};
|
|
2753
|
+
var toneVerticalClass = {
|
|
2754
|
+
neutral: "border-border/60 bg-card hover:border-border dark:bg-card/40",
|
|
2755
|
+
info: "border-primary/20 bg-primary/5 hover:border-primary/45",
|
|
2756
|
+
success: "border-emerald-500/20 bg-emerald-500/5 hover:border-emerald-500/45",
|
|
2757
|
+
warn: "border-amber-500/25 bg-amber-500/5 hover:border-amber-500/45",
|
|
2758
|
+
danger: "border-destructive/20 bg-destructive/5 hover:border-destructive/45"
|
|
2759
|
+
};
|
|
2232
2760
|
var InfoCard = ({
|
|
2233
2761
|
title,
|
|
2234
2762
|
children,
|
|
2235
2763
|
icon,
|
|
2236
2764
|
action,
|
|
2237
2765
|
tone = "neutral",
|
|
2766
|
+
layout = "horizontal",
|
|
2767
|
+
onClick,
|
|
2768
|
+
ariaLabel,
|
|
2238
2769
|
className
|
|
2239
|
-
}) =>
|
|
2240
|
-
|
|
2241
|
-
{
|
|
2242
|
-
|
|
2243
|
-
"flex items-
|
|
2244
|
-
|
|
2770
|
+
}) => {
|
|
2771
|
+
const isInteractive = Boolean(onClick);
|
|
2772
|
+
if (layout === "vertical") {
|
|
2773
|
+
const cardContent = /* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
2774
|
+
icon ? /* @__PURE__ */ jsx21("span", { className: "flex size-10 items-center justify-center rounded-xl bg-muted/50 border border-border/40 text-muted-foreground/80 transition-transform duration-200 group-hover:scale-105", children: icon }) : null,
|
|
2775
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-1.5 w-full text-left", children: [
|
|
2776
|
+
title ? /* @__PURE__ */ jsx21("p", { className: "text-sm font-semibold text-foreground tracking-tight leading-tight", children: title }) : null,
|
|
2777
|
+
children ? /* @__PURE__ */ jsx21("div", { className: "text-xs text-muted-foreground leading-relaxed", children }) : null
|
|
2778
|
+
] }),
|
|
2779
|
+
action ? /* @__PURE__ */ jsx21("div", { className: "w-full mt-1", children: action }) : null
|
|
2780
|
+
] });
|
|
2781
|
+
const baseClass2 = cn(
|
|
2782
|
+
"group flex flex-col gap-4 items-start rounded-2xl border p-5 transition-all duration-200 shadow-[0_1px_3px_rgba(0,0,0,0.03),0_10px_15px_-3px_rgba(0,0,0,0.01)]",
|
|
2783
|
+
toneVerticalClass[tone],
|
|
2784
|
+
isInteractive && "cursor-pointer select-none hover:shadow-md active:scale-[0.99] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
2245
2785
|
className
|
|
2246
|
-
)
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
children ? /* @__PURE__ */ jsx20("div", { className: cn("text-sm text-muted-foreground", title && "mt-1"), children }) : null
|
|
2252
|
-
] }),
|
|
2253
|
-
action ? /* @__PURE__ */ jsx20("div", { className: "shrink-0", children: action }) : null
|
|
2254
|
-
]
|
|
2786
|
+
);
|
|
2787
|
+
if (isInteractive) {
|
|
2788
|
+
return /* @__PURE__ */ jsx21("button", { type: "button", onClick, "aria-label": ariaLabel, className: baseClass2, children: cardContent });
|
|
2789
|
+
}
|
|
2790
|
+
return /* @__PURE__ */ jsx21("div", { className: baseClass2, children: cardContent });
|
|
2255
2791
|
}
|
|
2256
|
-
|
|
2792
|
+
const horizontalContent = /* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
2793
|
+
icon ? /* @__PURE__ */ jsx21("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: icon }) : null,
|
|
2794
|
+
/* @__PURE__ */ jsxs15("div", { className: "min-w-0 flex-1 text-left", children: [
|
|
2795
|
+
title ? /* @__PURE__ */ jsx21("p", { className: "text-sm font-medium text-foreground", children: title }) : null,
|
|
2796
|
+
children ? /* @__PURE__ */ jsx21("div", { className: cn("text-sm text-muted-foreground", title && "mt-1"), children }) : null
|
|
2797
|
+
] }),
|
|
2798
|
+
action ? /* @__PURE__ */ jsx21("div", { className: "shrink-0", children: action }) : null
|
|
2799
|
+
] });
|
|
2800
|
+
const baseClass = cn(
|
|
2801
|
+
"flex items-start gap-3 rounded-xl border p-4 transition-all duration-200",
|
|
2802
|
+
toneClass[tone],
|
|
2803
|
+
isInteractive && "cursor-pointer select-none hover:bg-muted/30 active:scale-[0.995] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
2804
|
+
className
|
|
2805
|
+
);
|
|
2806
|
+
if (isInteractive) {
|
|
2807
|
+
return /* @__PURE__ */ jsx21("button", { type: "button", onClick, "aria-label": ariaLabel, className: baseClass, children: horizontalContent });
|
|
2808
|
+
}
|
|
2809
|
+
return /* @__PURE__ */ jsx21("div", { className: baseClass, children: horizontalContent });
|
|
2810
|
+
};
|
|
2257
2811
|
|
|
2258
2812
|
// src/app/surfaces/StatusDot.tsx
|
|
2259
|
-
import { jsx as
|
|
2813
|
+
import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2260
2814
|
var dotClass = {
|
|
2261
2815
|
online: "bg-emerald-500",
|
|
2262
2816
|
busy: "bg-amber-500",
|
|
@@ -2271,7 +2825,7 @@ var StatusDot = ({
|
|
|
2271
2825
|
className
|
|
2272
2826
|
}) => /* @__PURE__ */ jsxs16("span", { className: cn("inline-flex items-center gap-1.5", className), children: [
|
|
2273
2827
|
/* @__PURE__ */ jsxs16("span", { className: "relative flex size-2", children: [
|
|
2274
|
-
pulse ? /* @__PURE__ */
|
|
2828
|
+
pulse ? /* @__PURE__ */ jsx22(
|
|
2275
2829
|
"span",
|
|
2276
2830
|
{
|
|
2277
2831
|
className: cn(
|
|
@@ -2280,18 +2834,18 @@ var StatusDot = ({
|
|
|
2280
2834
|
)
|
|
2281
2835
|
}
|
|
2282
2836
|
) : null,
|
|
2283
|
-
/* @__PURE__ */
|
|
2837
|
+
/* @__PURE__ */ jsx22("span", { className: cn("relative inline-flex size-2 rounded-full", dotClass[tone]) })
|
|
2284
2838
|
] }),
|
|
2285
|
-
label ? /* @__PURE__ */
|
|
2839
|
+
label ? /* @__PURE__ */ jsx22("span", { className: "text-xs text-muted-foreground", children: label }) : null
|
|
2286
2840
|
] });
|
|
2287
2841
|
|
|
2288
2842
|
// src/app/surfaces/DescriptionList.tsx
|
|
2289
|
-
import { jsx as
|
|
2843
|
+
import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2290
2844
|
var DescriptionList = ({
|
|
2291
2845
|
items,
|
|
2292
2846
|
stacked = false,
|
|
2293
2847
|
className
|
|
2294
|
-
}) => /* @__PURE__ */
|
|
2848
|
+
}) => /* @__PURE__ */ jsx23(
|
|
2295
2849
|
"dl",
|
|
2296
2850
|
{
|
|
2297
2851
|
className: cn(
|
|
@@ -2303,16 +2857,18 @@ var DescriptionList = ({
|
|
|
2303
2857
|
{
|
|
2304
2858
|
className: cn(
|
|
2305
2859
|
"px-4 py-3",
|
|
2306
|
-
stacked ? "flex flex-col gap-0.5" : "flex items-center justify-between gap-4"
|
|
2860
|
+
stacked ? "flex flex-col gap-0.5" : "flex items-center justify-between gap-4",
|
|
2861
|
+
item.className
|
|
2307
2862
|
),
|
|
2308
2863
|
children: [
|
|
2309
|
-
/* @__PURE__ */
|
|
2310
|
-
/* @__PURE__ */
|
|
2864
|
+
/* @__PURE__ */ jsx23("dt", { className: cn("text-sm text-muted-foreground", item.labelClassName), children: item.label }),
|
|
2865
|
+
/* @__PURE__ */ jsx23(
|
|
2311
2866
|
"dd",
|
|
2312
2867
|
{
|
|
2313
2868
|
className: cn(
|
|
2314
2869
|
"text-sm text-foreground",
|
|
2315
|
-
!stacked && "text-right tabular-nums"
|
|
2870
|
+
!stacked && "text-right tabular-nums",
|
|
2871
|
+
item.valueClassName
|
|
2316
2872
|
),
|
|
2317
2873
|
children: item.value
|
|
2318
2874
|
}
|
|
@@ -2325,10 +2881,10 @@ var DescriptionList = ({
|
|
|
2325
2881
|
);
|
|
2326
2882
|
|
|
2327
2883
|
// src/app/surfaces/ExpandableSection.tsx
|
|
2328
|
-
import { useId, useState as
|
|
2884
|
+
import { useId as useId2, useState as useState3 } from "react";
|
|
2329
2885
|
import { AnimatePresence, motion as motion2, useReducedMotion as useReducedMotion2 } from "motion/react";
|
|
2330
|
-
import { jsx as
|
|
2331
|
-
var Chevron = ({ open }) => /* @__PURE__ */
|
|
2886
|
+
import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2887
|
+
var Chevron = ({ open }) => /* @__PURE__ */ jsx24(
|
|
2332
2888
|
"svg",
|
|
2333
2889
|
{
|
|
2334
2890
|
viewBox: "0 0 24 24",
|
|
@@ -2342,7 +2898,7 @@ var Chevron = ({ open }) => /* @__PURE__ */ jsx23(
|
|
|
2342
2898
|
strokeLinecap: "round",
|
|
2343
2899
|
strokeLinejoin: "round",
|
|
2344
2900
|
"aria-hidden": true,
|
|
2345
|
-
children: /* @__PURE__ */
|
|
2901
|
+
children: /* @__PURE__ */ jsx24("path", { d: "m6 9 6 6 6-6" })
|
|
2346
2902
|
}
|
|
2347
2903
|
);
|
|
2348
2904
|
var ExpandableSection = ({
|
|
@@ -2356,8 +2912,8 @@ var ExpandableSection = ({
|
|
|
2356
2912
|
className
|
|
2357
2913
|
}) => {
|
|
2358
2914
|
const reduceMotion = useReducedMotion2();
|
|
2359
|
-
const panelId =
|
|
2360
|
-
const [internalOpen, setInternalOpen] =
|
|
2915
|
+
const panelId = useId2();
|
|
2916
|
+
const [internalOpen, setInternalOpen] = useState3(defaultOpen);
|
|
2361
2917
|
const open = openProp ?? internalOpen;
|
|
2362
2918
|
const toggle = () => {
|
|
2363
2919
|
if (openProp == null) setInternalOpen((o) => !o);
|
|
@@ -2374,15 +2930,15 @@ var ExpandableSection = ({
|
|
|
2374
2930
|
className: "flex w-full items-center justify-between gap-3 bg-transparent px-4 py-3 text-left hover:bg-transparent active:bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-foreground/10",
|
|
2375
2931
|
children: [
|
|
2376
2932
|
/* @__PURE__ */ jsxs18("span", { className: "flex min-w-0 items-center gap-3", children: [
|
|
2377
|
-
icon ? /* @__PURE__ */
|
|
2378
|
-
/* @__PURE__ */
|
|
2379
|
-
count != null ? /* @__PURE__ */
|
|
2933
|
+
icon ? /* @__PURE__ */ jsx24("span", { className: "flex size-8 items-center justify-center rounded-lg border border-border bg-muted text-muted-foreground", children: icon }) : null,
|
|
2934
|
+
/* @__PURE__ */ jsx24("span", { className: "truncate text-sm font-medium text-foreground", children: title }),
|
|
2935
|
+
count != null ? /* @__PURE__ */ jsx24("span", { className: "rounded-full border border-border bg-muted px-2 py-0.5 text-xs text-muted-foreground", children: count }) : null
|
|
2380
2936
|
] }),
|
|
2381
|
-
/* @__PURE__ */
|
|
2937
|
+
/* @__PURE__ */ jsx24(Chevron, { open })
|
|
2382
2938
|
]
|
|
2383
2939
|
}
|
|
2384
2940
|
),
|
|
2385
|
-
/* @__PURE__ */
|
|
2941
|
+
/* @__PURE__ */ jsx24(AnimatePresence, { initial: false, children: open ? /* @__PURE__ */ jsx24(
|
|
2386
2942
|
motion2.div,
|
|
2387
2943
|
{
|
|
2388
2944
|
id: panelId,
|
|
@@ -2391,7 +2947,7 @@ var ExpandableSection = ({
|
|
|
2391
2947
|
exit: reduceMotion ? void 0 : { height: 0, opacity: 0 },
|
|
2392
2948
|
transition: { duration: 0.2, ease: "easeOut" },
|
|
2393
2949
|
className: "overflow-hidden",
|
|
2394
|
-
children: /* @__PURE__ */
|
|
2950
|
+
children: /* @__PURE__ */ jsx24("div", { className: "bg-muted/20", children })
|
|
2395
2951
|
},
|
|
2396
2952
|
"body"
|
|
2397
2953
|
) : null })
|
|
@@ -2399,19 +2955,14 @@ var ExpandableSection = ({
|
|
|
2399
2955
|
};
|
|
2400
2956
|
|
|
2401
2957
|
// src/app/surfaces/ResourceCard.tsx
|
|
2402
|
-
import { Fragment as
|
|
2403
|
-
var resourceCardShellClass = cn(
|
|
2404
|
-
"flex min-h-[8.5rem] flex-col rounded-2xl p-4 text-left font-normal",
|
|
2405
|
-
TIMBAL_V2_ELEVATED_SURFACE
|
|
2406
|
-
);
|
|
2958
|
+
import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2407
2959
|
var mediaShellClass = cn(
|
|
2408
2960
|
"flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-xl text-sm font-normal text-foreground",
|
|
2409
2961
|
TIMBAL_V2_LOGO_TILE
|
|
2410
2962
|
);
|
|
2411
2963
|
var resourceCardInteractiveClass = cn(
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
2964
|
+
"flex min-h-[8.5rem] flex-col rounded-2xl p-4 text-left font-normal border border-border shadow-card",
|
|
2965
|
+
TIMBAL_V2_ELEVATED_SURFACE
|
|
2415
2966
|
);
|
|
2416
2967
|
var ResourceCard = ({
|
|
2417
2968
|
title,
|
|
@@ -2422,37 +2973,236 @@ var ResourceCard = ({
|
|
|
2422
2973
|
action,
|
|
2423
2974
|
onClick,
|
|
2424
2975
|
ariaLabel,
|
|
2425
|
-
className
|
|
2976
|
+
className,
|
|
2977
|
+
variant = "flat",
|
|
2978
|
+
avatarShape = "circle"
|
|
2426
2979
|
}) => {
|
|
2427
|
-
const
|
|
2980
|
+
const isInteractive = Boolean(onClick);
|
|
2981
|
+
const shellClass3 = cn(
|
|
2982
|
+
"flex min-h-[8.5rem] flex-col rounded-2xl p-5 text-left font-normal transition-all duration-200",
|
|
2983
|
+
variant === "default" && TIMBAL_V2_ELEVATED_SURFACE,
|
|
2984
|
+
variant === "flat" && "border border-border/50 bg-card/65 shadow-[0_1px_3px_rgba(0,0,0,0.05),0_10px_15px_-3px_rgba(0,0,0,0.01)] hover:border-border hover:shadow-md dark:bg-card/45",
|
|
2985
|
+
variant === "outline" && "border border-border/70 bg-transparent shadow-none hover:bg-muted/5",
|
|
2986
|
+
isInteractive && "cursor-pointer select-none active:scale-[0.99] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
2987
|
+
);
|
|
2988
|
+
const dynamicMediaShellClass = cn(
|
|
2989
|
+
"flex size-10 shrink-0 items-center justify-center overflow-hidden text-sm font-normal text-foreground transition-all duration-200",
|
|
2990
|
+
avatarShape === "circle" ? "rounded-full" : "rounded-xl",
|
|
2991
|
+
TIMBAL_V2_LOGO_TILE
|
|
2992
|
+
);
|
|
2993
|
+
const body = /* @__PURE__ */ jsxs19(Fragment5, { children: [
|
|
2428
2994
|
/* @__PURE__ */ jsxs19("div", { className: "flex items-start gap-3", children: [
|
|
2429
|
-
media ? /* @__PURE__ */
|
|
2995
|
+
media ? /* @__PURE__ */ jsx25("span", { className: dynamicMediaShellClass, children: media }) : null,
|
|
2430
2996
|
/* @__PURE__ */ jsxs19("div", { className: "min-w-0 flex-1 pt-0.5", children: [
|
|
2431
|
-
/* @__PURE__ */
|
|
2432
|
-
subtitle ? /* @__PURE__ */
|
|
2997
|
+
/* @__PURE__ */ jsx25("p", { className: "truncate text-sm font-semibold leading-snug text-foreground tracking-tight", children: title }),
|
|
2998
|
+
subtitle ? /* @__PURE__ */ jsx25("p", { className: "mt-1 line-clamp-2 text-xs font-normal text-muted-foreground/90 leading-relaxed", children: subtitle }) : null
|
|
2433
2999
|
] }),
|
|
2434
|
-
badge ? /* @__PURE__ */
|
|
3000
|
+
badge ? /* @__PURE__ */ jsx25("span", { className: "shrink-0 pt-0.5", children: badge }) : null
|
|
2435
3001
|
] }),
|
|
2436
3002
|
footer || action ? /* @__PURE__ */ jsxs19("div", { className: "mt-auto flex items-center justify-between gap-3 border-t border-border/40 pt-3 text-xs font-normal text-muted-foreground", children: [
|
|
2437
|
-
/* @__PURE__ */
|
|
2438
|
-
action ? /* @__PURE__ */
|
|
3003
|
+
/* @__PURE__ */ jsx25("span", { className: "min-w-0 truncate", children: footer }),
|
|
3004
|
+
action ? /* @__PURE__ */ jsx25("span", { className: "shrink-0 opacity-90", children: action }) : null
|
|
2439
3005
|
] }) : null
|
|
2440
3006
|
] });
|
|
2441
3007
|
if (onClick) {
|
|
2442
|
-
return /* @__PURE__ */
|
|
3008
|
+
return /* @__PURE__ */ jsx25(
|
|
3009
|
+
"button",
|
|
3010
|
+
{
|
|
3011
|
+
type: "button",
|
|
3012
|
+
onClick,
|
|
3013
|
+
"aria-label": ariaLabel,
|
|
3014
|
+
className: cn(shellClass3, className),
|
|
3015
|
+
children: body
|
|
3016
|
+
}
|
|
3017
|
+
);
|
|
2443
3018
|
}
|
|
2444
|
-
return /* @__PURE__ */
|
|
3019
|
+
return /* @__PURE__ */ jsx25("article", { className: cn(shellClass3, className), children: body });
|
|
3020
|
+
};
|
|
3021
|
+
|
|
3022
|
+
// src/app/surfaces/AlertCard.tsx
|
|
3023
|
+
import { ChevronRight } from "lucide-react";
|
|
3024
|
+
import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
3025
|
+
var alertCardShellClass = cn(
|
|
3026
|
+
"flex flex-col rounded-2xl p-4 text-left font-normal border border-border shadow-card",
|
|
3027
|
+
TIMBAL_V2_ELEVATED_GRADIENT
|
|
3028
|
+
);
|
|
3029
|
+
var alertCardInteractiveClass = cn(
|
|
3030
|
+
"flex flex-col rounded-2xl p-4 text-left font-normal border border-border shadow-card cursor-pointer",
|
|
3031
|
+
TIMBAL_V2_ELEVATED_GRADIENT,
|
|
3032
|
+
"transition-[background-color,box-shadow,border-color] duration-150 ease-in-out",
|
|
3033
|
+
"hover:border-foreground/20",
|
|
3034
|
+
"hover:from-secondary-fill-hover-from hover:to-secondary-fill-hover-to",
|
|
3035
|
+
"active:from-secondary-fill-active-from active:to-secondary-fill-active-to",
|
|
3036
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
3037
|
+
);
|
|
3038
|
+
var AlertCard = ({
|
|
3039
|
+
title,
|
|
3040
|
+
description,
|
|
3041
|
+
category,
|
|
3042
|
+
categoryTone = "default",
|
|
3043
|
+
status,
|
|
3044
|
+
statusTone = "default",
|
|
3045
|
+
action,
|
|
3046
|
+
trailing,
|
|
3047
|
+
onClick,
|
|
3048
|
+
ariaLabel,
|
|
3049
|
+
className
|
|
3050
|
+
}) => {
|
|
3051
|
+
const showTags = Boolean(category || status);
|
|
3052
|
+
const showTrailing = Boolean(trailing || onClick);
|
|
3053
|
+
const bodyContent = /* @__PURE__ */ jsxs20("div", { className: "flex-1 min-w-0 flex flex-col h-full", children: [
|
|
3054
|
+
showTags ? /* @__PURE__ */ jsxs20("div", { className: "flex flex-wrap items-center gap-1.5 mb-2.5", children: [
|
|
3055
|
+
category ? /* @__PURE__ */ jsx26(StatusBadge, { tone: categoryTone, children: category }) : null,
|
|
3056
|
+
status ? /* @__PURE__ */ jsx26(StatusBadge, { tone: statusTone, children: status }) : null
|
|
3057
|
+
] }) : null,
|
|
3058
|
+
/* @__PURE__ */ jsx26("h4", { className: "text-sm font-medium leading-snug text-foreground", children: title }),
|
|
3059
|
+
description ? /* @__PURE__ */ jsx26("p", { className: "mt-1.5 text-xs text-muted-foreground leading-normal", children: description }) : null,
|
|
3060
|
+
action ? /* @__PURE__ */ jsxs20("div", { className: "mt-auto pt-3 text-xs text-muted-foreground/90 leading-normal", children: [
|
|
3061
|
+
/* @__PURE__ */ jsx26("strong", { className: "font-semibold text-foreground/80", children: "Action: " }),
|
|
3062
|
+
action
|
|
3063
|
+
] }) : null
|
|
3064
|
+
] });
|
|
3065
|
+
const cardContent = /* @__PURE__ */ jsxs20("div", { className: "flex items-start justify-between gap-4 w-full h-full", children: [
|
|
3066
|
+
bodyContent,
|
|
3067
|
+
showTrailing ? /* @__PURE__ */ jsx26("div", { className: "shrink-0 flex items-center justify-center self-center text-muted-foreground/50", children: trailing || /* @__PURE__ */ jsx26(ChevronRight, { className: "size-4" }) }) : null
|
|
3068
|
+
] });
|
|
3069
|
+
if (onClick) {
|
|
3070
|
+
return /* @__PURE__ */ jsx26(
|
|
3071
|
+
"button",
|
|
3072
|
+
{
|
|
3073
|
+
type: "button",
|
|
3074
|
+
onClick,
|
|
3075
|
+
"aria-label": ariaLabel,
|
|
3076
|
+
className: cn(alertCardInteractiveClass, className),
|
|
3077
|
+
children: cardContent
|
|
3078
|
+
}
|
|
3079
|
+
);
|
|
3080
|
+
}
|
|
3081
|
+
return /* @__PURE__ */ jsx26("article", { className: cn(alertCardShellClass, className), children: cardContent });
|
|
3082
|
+
};
|
|
3083
|
+
|
|
3084
|
+
// src/app/surfaces/CatalogCard.tsx
|
|
3085
|
+
import { ExternalLink } from "lucide-react";
|
|
3086
|
+
import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
3087
|
+
var catalogCardShellClass = cn(
|
|
3088
|
+
"flex flex-col rounded-2xl border border-border shadow-card overflow-hidden text-left font-normal",
|
|
3089
|
+
TIMBAL_V2_ELEVATED_GRADIENT
|
|
3090
|
+
);
|
|
3091
|
+
var catalogCardInteractiveClass = cn(
|
|
3092
|
+
"flex flex-col rounded-2xl border border-border shadow-card overflow-hidden text-left font-normal cursor-pointer",
|
|
3093
|
+
TIMBAL_V2_ELEVATED_GRADIENT,
|
|
3094
|
+
"transition-[background-color,box-shadow,border-color] duration-150 ease-in-out",
|
|
3095
|
+
"hover:border-foreground/20",
|
|
3096
|
+
"hover:from-secondary-fill-hover-from hover:to-secondary-fill-hover-to",
|
|
3097
|
+
"active:from-secondary-fill-active-from active:to-secondary-fill-active-to",
|
|
3098
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
3099
|
+
);
|
|
3100
|
+
var CatalogCard = ({
|
|
3101
|
+
title,
|
|
3102
|
+
subtitle,
|
|
3103
|
+
logo,
|
|
3104
|
+
href,
|
|
3105
|
+
badge,
|
|
3106
|
+
description,
|
|
3107
|
+
tags,
|
|
3108
|
+
footerLinks,
|
|
3109
|
+
copyValue,
|
|
3110
|
+
copyLabel = "Copy ID",
|
|
3111
|
+
actions,
|
|
3112
|
+
onClick,
|
|
3113
|
+
ariaLabel,
|
|
3114
|
+
className
|
|
3115
|
+
}) => {
|
|
3116
|
+
const showHeaderTags = Boolean(subtitle || badge);
|
|
3117
|
+
const showFooter = Boolean(footerLinks && footerLinks.length > 0 || copyValue || actions);
|
|
3118
|
+
const mainContent = /* @__PURE__ */ jsxs21("div", { className: "flex-1 p-5 flex flex-col h-full min-h-0", children: [
|
|
3119
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex items-start gap-3", children: [
|
|
3120
|
+
logo ? /* @__PURE__ */ jsx27("div", { className: "size-8 shrink-0 flex items-center justify-center overflow-hidden rounded-lg bg-muted/40", children: logo }) : null,
|
|
3121
|
+
/* @__PURE__ */ jsxs21("div", { className: "min-w-0 flex-1 pt-0.5", children: [
|
|
3122
|
+
/* @__PURE__ */ jsx27("div", { className: "flex items-center gap-1.5 flex-wrap", children: href ? /* @__PURE__ */ jsxs21(
|
|
3123
|
+
"a",
|
|
3124
|
+
{
|
|
3125
|
+
href,
|
|
3126
|
+
target: "_blank",
|
|
3127
|
+
rel: "noopener noreferrer",
|
|
3128
|
+
className: "inline-flex items-center gap-1 hover:underline text-sm font-medium leading-snug text-foreground focus-visible:outline-none focus-visible:underline",
|
|
3129
|
+
onClick: (e) => e.stopPropagation(),
|
|
3130
|
+
children: [
|
|
3131
|
+
/* @__PURE__ */ jsx27("span", { className: "truncate", children: title }),
|
|
3132
|
+
/* @__PURE__ */ jsx27(ExternalLink, { className: "size-3.5 shrink-0 text-muted-foreground/60" })
|
|
3133
|
+
]
|
|
3134
|
+
}
|
|
3135
|
+
) : /* @__PURE__ */ jsx27("h4", { className: "text-sm font-medium leading-snug text-foreground truncate", children: title }) }),
|
|
3136
|
+
showHeaderTags ? /* @__PURE__ */ jsxs21("div", { className: "flex flex-wrap items-center gap-2 mt-1", children: [
|
|
3137
|
+
subtitle ? /* @__PURE__ */ jsx27("span", { className: "text-xs font-medium text-muted-foreground", children: subtitle }) : null,
|
|
3138
|
+
badge ? /* @__PURE__ */ jsx27("div", { className: "shrink-0 flex items-center", children: badge }) : null
|
|
3139
|
+
] }) : null
|
|
3140
|
+
] })
|
|
3141
|
+
] }),
|
|
3142
|
+
description ? /* @__PURE__ */ jsx27("p", { className: "mt-3 text-xs leading-relaxed text-muted-foreground line-clamp-3", children: description }) : null,
|
|
3143
|
+
tags && tags.length > 0 ? /* @__PURE__ */ jsx27("div", { className: "flex flex-wrap items-center gap-2 mt-4", children: tags.map((tag, idx) => /* @__PURE__ */ jsxs21("div", { className: "flex items-center text-xs text-muted-foreground/80", children: [
|
|
3144
|
+
idx > 0 && /* @__PURE__ */ jsx27("span", { className: "mr-2 text-muted-foreground/30", children: "\u2022" }),
|
|
3145
|
+
tag
|
|
3146
|
+
] }, idx)) }) : null
|
|
3147
|
+
] });
|
|
3148
|
+
const footerMarkup = showFooter ? /* @__PURE__ */ jsxs21("div", { className: "border-t border-border/40 px-5 py-3 flex items-center justify-between gap-4 bg-muted/[0.02]", children: [
|
|
3149
|
+
/* @__PURE__ */ jsx27("div", { className: "flex flex-wrap items-center gap-4", children: footerLinks && footerLinks.length > 0 ? footerLinks.map((link, idx) => /* @__PURE__ */ jsxs21(
|
|
3150
|
+
"a",
|
|
3151
|
+
{
|
|
3152
|
+
href: link.href,
|
|
3153
|
+
target: "_blank",
|
|
3154
|
+
rel: "noopener noreferrer",
|
|
3155
|
+
className: "inline-flex items-center gap-1 text-xs font-medium text-muted-foreground hover:text-foreground hover:underline transition-colors focus-visible:outline-none focus-visible:underline",
|
|
3156
|
+
onClick: (e) => e.stopPropagation(),
|
|
3157
|
+
children: [
|
|
3158
|
+
/* @__PURE__ */ jsx27("span", { children: link.label }),
|
|
3159
|
+
/* @__PURE__ */ jsx27(ExternalLink, { className: "size-2.5 shrink-0 text-muted-foreground/50" })
|
|
3160
|
+
]
|
|
3161
|
+
},
|
|
3162
|
+
idx
|
|
3163
|
+
)) : null }),
|
|
3164
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2", onClick: (e) => e.stopPropagation(), children: [
|
|
3165
|
+
actions ? actions : null,
|
|
3166
|
+
!actions && copyValue ? /* @__PURE__ */ jsx27(
|
|
3167
|
+
CopyButton,
|
|
3168
|
+
{
|
|
3169
|
+
value: copyValue,
|
|
3170
|
+
className: "h-7 text-xs px-2 hover:bg-muted/40 text-muted-foreground/80 hover:text-foreground",
|
|
3171
|
+
children: copyLabel
|
|
3172
|
+
}
|
|
3173
|
+
) : null
|
|
3174
|
+
] })
|
|
3175
|
+
] }) : null;
|
|
3176
|
+
if (onClick) {
|
|
3177
|
+
return /* @__PURE__ */ jsxs21(
|
|
3178
|
+
"button",
|
|
3179
|
+
{
|
|
3180
|
+
type: "button",
|
|
3181
|
+
onClick,
|
|
3182
|
+
"aria-label": ariaLabel,
|
|
3183
|
+
className: cn(catalogCardInteractiveClass, className),
|
|
3184
|
+
children: [
|
|
3185
|
+
mainContent,
|
|
3186
|
+
footerMarkup
|
|
3187
|
+
]
|
|
3188
|
+
}
|
|
3189
|
+
);
|
|
3190
|
+
}
|
|
3191
|
+
return /* @__PURE__ */ jsxs21("article", { className: cn(catalogCardShellClass, className), children: [
|
|
3192
|
+
mainContent,
|
|
3193
|
+
footerMarkup
|
|
3194
|
+
] });
|
|
2445
3195
|
};
|
|
2446
3196
|
|
|
2447
3197
|
// src/app/settings/SettingsSection.tsx
|
|
2448
|
-
import { jsx as
|
|
3198
|
+
import { jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2449
3199
|
var SettingsSectionHeader = ({
|
|
2450
3200
|
title,
|
|
2451
3201
|
description,
|
|
2452
3202
|
className
|
|
2453
|
-
}) => /* @__PURE__ */
|
|
2454
|
-
/* @__PURE__ */
|
|
2455
|
-
description ? /* @__PURE__ */
|
|
3203
|
+
}) => /* @__PURE__ */ jsxs22("div", { className: cn("flex flex-col", className), children: [
|
|
3204
|
+
/* @__PURE__ */ jsx28("h3", { className: "text-[17px] font-medium leading-tight text-foreground", children: title }),
|
|
3205
|
+
description ? /* @__PURE__ */ jsx28("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
|
|
2456
3206
|
] });
|
|
2457
3207
|
var SettingsSection = ({
|
|
2458
3208
|
title,
|
|
@@ -2461,7 +3211,7 @@ var SettingsSection = ({
|
|
|
2461
3211
|
children,
|
|
2462
3212
|
noBorderTop = false,
|
|
2463
3213
|
className
|
|
2464
|
-
}) => /* @__PURE__ */
|
|
3214
|
+
}) => /* @__PURE__ */ jsxs22(
|
|
2465
3215
|
"section",
|
|
2466
3216
|
{
|
|
2467
3217
|
className: cn(
|
|
@@ -2470,18 +3220,18 @@ var SettingsSection = ({
|
|
|
2470
3220
|
className
|
|
2471
3221
|
),
|
|
2472
3222
|
children: [
|
|
2473
|
-
/* @__PURE__ */
|
|
2474
|
-
/* @__PURE__ */
|
|
2475
|
-
description ? /* @__PURE__ */
|
|
2476
|
-
descriptionFooter ? /* @__PURE__ */
|
|
3223
|
+
/* @__PURE__ */ jsxs22("div", { className: "min-w-0", children: [
|
|
3224
|
+
/* @__PURE__ */ jsx28("h2", { className: "text-sm font-medium text-foreground", children: title }),
|
|
3225
|
+
description ? /* @__PURE__ */ jsx28("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null,
|
|
3226
|
+
descriptionFooter ? /* @__PURE__ */ jsx28("div", { className: "mt-3 min-w-0", children: descriptionFooter }) : null
|
|
2477
3227
|
] }),
|
|
2478
|
-
/* @__PURE__ */
|
|
3228
|
+
/* @__PURE__ */ jsx28("div", { className: "min-w-0 space-y-3", children })
|
|
2479
3229
|
]
|
|
2480
3230
|
}
|
|
2481
3231
|
);
|
|
2482
3232
|
|
|
2483
3233
|
// src/app/settings/FieldRow.tsx
|
|
2484
|
-
import { jsx as
|
|
3234
|
+
import { jsx as jsx29, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2485
3235
|
var FieldRow = ({
|
|
2486
3236
|
label,
|
|
2487
3237
|
children,
|
|
@@ -2491,7 +3241,7 @@ var FieldRow = ({
|
|
|
2491
3241
|
className
|
|
2492
3242
|
}) => {
|
|
2493
3243
|
if (inline) {
|
|
2494
|
-
return /* @__PURE__ */
|
|
3244
|
+
return /* @__PURE__ */ jsxs23(
|
|
2495
3245
|
"div",
|
|
2496
3246
|
{
|
|
2497
3247
|
className: cn(
|
|
@@ -2499,8 +3249,8 @@ var FieldRow = ({
|
|
|
2499
3249
|
className
|
|
2500
3250
|
),
|
|
2501
3251
|
children: [
|
|
2502
|
-
/* @__PURE__ */
|
|
2503
|
-
/* @__PURE__ */
|
|
3252
|
+
/* @__PURE__ */ jsxs23("div", { className: "min-w-0", children: [
|
|
3253
|
+
/* @__PURE__ */ jsx29(
|
|
2504
3254
|
"label",
|
|
2505
3255
|
{
|
|
2506
3256
|
htmlFor,
|
|
@@ -2508,25 +3258,25 @@ var FieldRow = ({
|
|
|
2508
3258
|
children: label
|
|
2509
3259
|
}
|
|
2510
3260
|
),
|
|
2511
|
-
description ? /* @__PURE__ */
|
|
3261
|
+
description ? /* @__PURE__ */ jsx29("p", { className: "mt-0.5 text-xs text-muted-foreground", children: description }) : null
|
|
2512
3262
|
] }),
|
|
2513
|
-
/* @__PURE__ */
|
|
3263
|
+
/* @__PURE__ */ jsx29("div", { className: "shrink-0", children })
|
|
2514
3264
|
]
|
|
2515
3265
|
}
|
|
2516
3266
|
);
|
|
2517
3267
|
}
|
|
2518
|
-
return /* @__PURE__ */
|
|
2519
|
-
/* @__PURE__ */
|
|
3268
|
+
return /* @__PURE__ */ jsxs23("div", { className: cn("flex flex-col gap-1.5", className), children: [
|
|
3269
|
+
/* @__PURE__ */ jsx29("label", { htmlFor, className: "text-sm font-medium text-foreground", children: label }),
|
|
2520
3270
|
children,
|
|
2521
|
-
description ? /* @__PURE__ */
|
|
3271
|
+
description ? /* @__PURE__ */ jsx29("p", { className: "text-xs text-muted-foreground", children: description }) : null
|
|
2522
3272
|
] });
|
|
2523
3273
|
};
|
|
2524
3274
|
|
|
2525
3275
|
// src/app/settings/FloatingUnsavedChangesBar.tsx
|
|
2526
|
-
import { useEffect, useState as
|
|
3276
|
+
import { useEffect as useEffect2, useState as useState4 } from "react";
|
|
2527
3277
|
import { createPortal } from "react-dom";
|
|
2528
3278
|
import { AnimatePresence as AnimatePresence2, motion as motion3, useReducedMotion as useReducedMotion3 } from "motion/react";
|
|
2529
|
-
import { jsx as
|
|
3279
|
+
import { jsx as jsx30, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2530
3280
|
var FloatingUnsavedChangesBar = ({
|
|
2531
3281
|
visible,
|
|
2532
3282
|
message = "Unsaved changes",
|
|
@@ -2540,11 +3290,11 @@ var FloatingUnsavedChangesBar = ({
|
|
|
2540
3290
|
className
|
|
2541
3291
|
}) => {
|
|
2542
3292
|
const reduceMotion = useReducedMotion3();
|
|
2543
|
-
const [mounted, setMounted] =
|
|
2544
|
-
|
|
3293
|
+
const [mounted, setMounted] = useState4(false);
|
|
3294
|
+
useEffect2(() => setMounted(true), []);
|
|
2545
3295
|
if (!mounted || typeof document === "undefined") return null;
|
|
2546
3296
|
return createPortal(
|
|
2547
|
-
/* @__PURE__ */
|
|
3297
|
+
/* @__PURE__ */ jsx30(AnimatePresence2, { children: visible ? /* @__PURE__ */ jsx30("div", { className: "pointer-events-none fixed inset-x-0 bottom-5 z-50 flex justify-center px-4", children: /* @__PURE__ */ jsxs24(
|
|
2548
3298
|
motion3.div,
|
|
2549
3299
|
{
|
|
2550
3300
|
role: "region",
|
|
@@ -2558,10 +3308,10 @@ var FloatingUnsavedChangesBar = ({
|
|
|
2558
3308
|
className
|
|
2559
3309
|
),
|
|
2560
3310
|
children: [
|
|
2561
|
-
/* @__PURE__ */
|
|
2562
|
-
/* @__PURE__ */
|
|
2563
|
-
/* @__PURE__ */
|
|
2564
|
-
/* @__PURE__ */
|
|
3311
|
+
/* @__PURE__ */ jsx30("span", { className: "text-sm text-muted-foreground", children: message }),
|
|
3312
|
+
/* @__PURE__ */ jsxs24("span", { className: "flex items-center gap-1.5", children: [
|
|
3313
|
+
/* @__PURE__ */ jsx30(Button, { variant: "ghost", size: "sm", onClick: onDiscard, disabled: isSaving, children: discardLabel }),
|
|
3314
|
+
/* @__PURE__ */ jsx30(Button, { size: "sm", onClick: onSave, disabled: saveDisabled || isSaving, children: isSaving ? "Saving\u2026" : saveLabel })
|
|
2565
3315
|
] })
|
|
2566
3316
|
]
|
|
2567
3317
|
}
|
|
@@ -2571,13 +3321,13 @@ var FloatingUnsavedChangesBar = ({
|
|
|
2571
3321
|
};
|
|
2572
3322
|
|
|
2573
3323
|
// src/app/settings/DangerZone.tsx
|
|
2574
|
-
import { jsx as
|
|
3324
|
+
import { jsx as jsx31, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2575
3325
|
var DangerZoneAction = ({
|
|
2576
3326
|
title,
|
|
2577
3327
|
description,
|
|
2578
3328
|
action,
|
|
2579
3329
|
className
|
|
2580
|
-
}) => /* @__PURE__ */
|
|
3330
|
+
}) => /* @__PURE__ */ jsxs25(
|
|
2581
3331
|
"div",
|
|
2582
3332
|
{
|
|
2583
3333
|
className: cn(
|
|
@@ -2585,11 +3335,11 @@ var DangerZoneAction = ({
|
|
|
2585
3335
|
className
|
|
2586
3336
|
),
|
|
2587
3337
|
children: [
|
|
2588
|
-
/* @__PURE__ */
|
|
2589
|
-
/* @__PURE__ */
|
|
2590
|
-
description ? /* @__PURE__ */
|
|
3338
|
+
/* @__PURE__ */ jsxs25("div", { className: "min-w-0", children: [
|
|
3339
|
+
/* @__PURE__ */ jsx31("p", { className: "text-sm font-medium text-foreground", children: title }),
|
|
3340
|
+
description ? /* @__PURE__ */ jsx31("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
|
|
2591
3341
|
] }),
|
|
2592
|
-
/* @__PURE__ */
|
|
3342
|
+
/* @__PURE__ */ jsx31("div", { className: "shrink-0", children: action })
|
|
2593
3343
|
]
|
|
2594
3344
|
}
|
|
2595
3345
|
);
|
|
@@ -2598,7 +3348,7 @@ var DangerZone = ({
|
|
|
2598
3348
|
description,
|
|
2599
3349
|
children,
|
|
2600
3350
|
className
|
|
2601
|
-
}) => /* @__PURE__ */
|
|
3351
|
+
}) => /* @__PURE__ */ jsxs25(
|
|
2602
3352
|
"section",
|
|
2603
3353
|
{
|
|
2604
3354
|
className: cn(
|
|
@@ -2606,18 +3356,18 @@ var DangerZone = ({
|
|
|
2606
3356
|
className
|
|
2607
3357
|
),
|
|
2608
3358
|
children: [
|
|
2609
|
-
(title || description) && /* @__PURE__ */
|
|
2610
|
-
title ? /* @__PURE__ */
|
|
2611
|
-
description ? /* @__PURE__ */
|
|
3359
|
+
(title || description) && /* @__PURE__ */ jsxs25("header", { className: "border-b border-destructive/20 bg-destructive/5 px-4 py-3", children: [
|
|
3360
|
+
title ? /* @__PURE__ */ jsx31("h3", { className: "text-sm font-semibold text-destructive", children: title }) : null,
|
|
3361
|
+
description ? /* @__PURE__ */ jsx31("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
|
|
2612
3362
|
] }),
|
|
2613
|
-
/* @__PURE__ */
|
|
3363
|
+
/* @__PURE__ */ jsx31("div", { className: "divide-y divide-border bg-card", children })
|
|
2614
3364
|
]
|
|
2615
3365
|
}
|
|
2616
3366
|
);
|
|
2617
3367
|
|
|
2618
3368
|
// src/app/integrations/IntegrationCard.tsx
|
|
2619
|
-
import { useId as
|
|
2620
|
-
import { Fragment as
|
|
3369
|
+
import { useId as useId3 } from "react";
|
|
3370
|
+
import { Fragment as Fragment6, jsx as jsx32, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2621
3371
|
var INTEGRATION_CATALOG_CARD_HEIGHT_CLASS = "h-[12.25rem] min-h-[12.25rem] max-h-[12.25rem]";
|
|
2622
3372
|
var statusLabel = {
|
|
2623
3373
|
available: null,
|
|
@@ -2625,14 +3375,14 @@ var statusLabel = {
|
|
|
2625
3375
|
disabled: "Disabled",
|
|
2626
3376
|
locked: "Locked"
|
|
2627
3377
|
};
|
|
2628
|
-
var
|
|
3378
|
+
var catalogCardShellClass2 = cn(
|
|
2629
3379
|
"group relative box-border flex flex-col overflow-hidden rounded-2xl px-4 pb-4 pt-4 text-left font-normal",
|
|
2630
3380
|
INTEGRATION_CATALOG_CARD_HEIGHT_CLASS,
|
|
2631
3381
|
TIMBAL_V2_ELEVATED_SURFACE,
|
|
2632
3382
|
"transition-opacity duration-200 ease-out"
|
|
2633
3383
|
);
|
|
2634
|
-
var
|
|
2635
|
-
|
|
3384
|
+
var catalogCardInteractiveClass2 = cn(
|
|
3385
|
+
catalogCardShellClass2,
|
|
2636
3386
|
"cursor-pointer bg-transparent hover:bg-transparent active:bg-transparent",
|
|
2637
3387
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
2638
3388
|
);
|
|
@@ -2655,15 +3405,15 @@ var IntegrationCard = ({
|
|
|
2655
3405
|
ariaLabel,
|
|
2656
3406
|
className
|
|
2657
3407
|
}) => {
|
|
2658
|
-
const titleId =
|
|
3408
|
+
const titleId = useId3();
|
|
2659
3409
|
const locked = status === "locked";
|
|
2660
3410
|
const dimmed = status === "disabled" || locked;
|
|
2661
|
-
const body = /* @__PURE__ */
|
|
2662
|
-
/* @__PURE__ */
|
|
2663
|
-
logo ? /* @__PURE__ */
|
|
2664
|
-
/* @__PURE__ */
|
|
2665
|
-
/* @__PURE__ */
|
|
2666
|
-
/* @__PURE__ */
|
|
3411
|
+
const body = /* @__PURE__ */ jsxs26("div", { className: "flex h-full min-h-0 flex-col", children: [
|
|
3412
|
+
/* @__PURE__ */ jsxs26("div", { className: "flex shrink-0 items-start gap-3 pr-2", children: [
|
|
3413
|
+
logo ? /* @__PURE__ */ jsx32("span", { className: logoShellClass, "aria-hidden": Boolean(ariaLabel), children: logo }) : null,
|
|
3414
|
+
/* @__PURE__ */ jsx32("div", { className: "min-w-0 flex-1 pt-0.5", children: /* @__PURE__ */ jsxs26("div", { className: "flex items-start justify-between gap-2", children: [
|
|
3415
|
+
/* @__PURE__ */ jsxs26("div", { className: "min-w-0", children: [
|
|
3416
|
+
/* @__PURE__ */ jsx32(
|
|
2667
3417
|
"h4",
|
|
2668
3418
|
{
|
|
2669
3419
|
id: onClick && !action ? void 0 : titleId,
|
|
@@ -2671,12 +3421,12 @@ var IntegrationCard = ({
|
|
|
2671
3421
|
children: name
|
|
2672
3422
|
}
|
|
2673
3423
|
),
|
|
2674
|
-
statusLabel[status] ? /* @__PURE__ */
|
|
3424
|
+
statusLabel[status] ? /* @__PURE__ */ jsx32("p", { className: "mt-0.5 text-xs text-muted-foreground", children: statusLabel[status] }) : null
|
|
2675
3425
|
] }),
|
|
2676
|
-
badge ? /* @__PURE__ */
|
|
3426
|
+
badge ? /* @__PURE__ */ jsx32("span", { className: "shrink-0", children: badge }) : null
|
|
2677
3427
|
] }) })
|
|
2678
3428
|
] }),
|
|
2679
|
-
description ? /* @__PURE__ */
|
|
3429
|
+
description ? /* @__PURE__ */ jsx32(
|
|
2680
3430
|
"p",
|
|
2681
3431
|
{
|
|
2682
3432
|
className: cn(
|
|
@@ -2686,19 +3436,19 @@ var IntegrationCard = ({
|
|
|
2686
3436
|
children: description
|
|
2687
3437
|
}
|
|
2688
3438
|
) : null,
|
|
2689
|
-
action ? /* @__PURE__ */
|
|
2690
|
-
/* @__PURE__ */
|
|
2691
|
-
/* @__PURE__ */
|
|
3439
|
+
action ? /* @__PURE__ */ jsxs26(Fragment6, { children: [
|
|
3440
|
+
/* @__PURE__ */ jsx32("div", { className: "min-h-0 flex-1", "aria-hidden": true }),
|
|
3441
|
+
/* @__PURE__ */ jsx32("div", { className: "relative mt-3 shrink-0", children: action })
|
|
2692
3442
|
] }) : null
|
|
2693
3443
|
] });
|
|
2694
3444
|
const shellClass3 = cn(
|
|
2695
|
-
|
|
3445
|
+
catalogCardShellClass2,
|
|
2696
3446
|
dimmed && catalogCardMutedClass,
|
|
2697
3447
|
locked && "cursor-default opacity-75",
|
|
2698
3448
|
className
|
|
2699
3449
|
);
|
|
2700
3450
|
if (onClick && !action) {
|
|
2701
|
-
return /* @__PURE__ */
|
|
3451
|
+
return /* @__PURE__ */ jsx32(
|
|
2702
3452
|
"button",
|
|
2703
3453
|
{
|
|
2704
3454
|
type: "button",
|
|
@@ -2706,7 +3456,7 @@ var IntegrationCard = ({
|
|
|
2706
3456
|
disabled: locked,
|
|
2707
3457
|
"aria-label": ariaLabel,
|
|
2708
3458
|
className: cn(
|
|
2709
|
-
|
|
3459
|
+
catalogCardInteractiveClass2,
|
|
2710
3460
|
dimmed && catalogCardMutedClass,
|
|
2711
3461
|
locked && "cursor-default opacity-75",
|
|
2712
3462
|
className
|
|
@@ -2715,12 +3465,12 @@ var IntegrationCard = ({
|
|
|
2715
3465
|
}
|
|
2716
3466
|
);
|
|
2717
3467
|
}
|
|
2718
|
-
return /* @__PURE__ */
|
|
3468
|
+
return /* @__PURE__ */ jsx32("article", { className: shellClass3, "aria-labelledby": titleId, children: body });
|
|
2719
3469
|
};
|
|
2720
3470
|
|
|
2721
3471
|
// src/app/integrations/IntegrationsEmptyState.tsx
|
|
2722
|
-
import { useId as
|
|
2723
|
-
import { jsx as
|
|
3472
|
+
import { useId as useId4 } from "react";
|
|
3473
|
+
import { jsx as jsx33, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2724
3474
|
var IntegrationsEmptyState = ({
|
|
2725
3475
|
title = "No integrations yet",
|
|
2726
3476
|
description = "Connect a provider to start syncing data and powering your workforce.",
|
|
@@ -2728,8 +3478,8 @@ var IntegrationsEmptyState = ({
|
|
|
2728
3478
|
action,
|
|
2729
3479
|
className
|
|
2730
3480
|
}) => {
|
|
2731
|
-
const titleId =
|
|
2732
|
-
return /* @__PURE__ */
|
|
3481
|
+
const titleId = useId4();
|
|
3482
|
+
return /* @__PURE__ */ jsxs27(
|
|
2733
3483
|
"section",
|
|
2734
3484
|
{
|
|
2735
3485
|
className: cn(
|
|
@@ -2739,7 +3489,7 @@ var IntegrationsEmptyState = ({
|
|
|
2739
3489
|
),
|
|
2740
3490
|
"aria-labelledby": titleId,
|
|
2741
3491
|
children: [
|
|
2742
|
-
icon ? /* @__PURE__ */
|
|
3492
|
+
icon ? /* @__PURE__ */ jsx33(
|
|
2743
3493
|
"span",
|
|
2744
3494
|
{
|
|
2745
3495
|
className: cn(
|
|
@@ -2751,21 +3501,21 @@ var IntegrationsEmptyState = ({
|
|
|
2751
3501
|
children: icon
|
|
2752
3502
|
}
|
|
2753
3503
|
) : null,
|
|
2754
|
-
/* @__PURE__ */
|
|
2755
|
-
description ? /* @__PURE__ */
|
|
2756
|
-
action ? /* @__PURE__ */
|
|
3504
|
+
/* @__PURE__ */ jsx33("h3", { id: titleId, className: "text-base font-normal text-foreground", children: title }),
|
|
3505
|
+
description ? /* @__PURE__ */ jsx33("p", { className: "max-w-sm text-sm text-muted-foreground", children: description }) : null,
|
|
3506
|
+
action ? /* @__PURE__ */ jsx33("div", { className: "mt-1", children: action }) : null
|
|
2757
3507
|
]
|
|
2758
3508
|
}
|
|
2759
3509
|
);
|
|
2760
3510
|
};
|
|
2761
3511
|
|
|
2762
3512
|
// src/app/integrations/PlanBadge.tsx
|
|
2763
|
-
import { jsx as
|
|
3513
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
2764
3514
|
var planBadgeClass = "inline-flex h-5 max-w-full shrink-0 items-center rounded-md border border-border bg-muted/90 px-2 text-[11px] font-normal text-muted-foreground dark:border-white/10 dark:bg-white/5 dark:text-muted-foreground";
|
|
2765
|
-
var PlanBadge = ({ children, className }) => /* @__PURE__ */
|
|
3515
|
+
var PlanBadge = ({ children, className }) => /* @__PURE__ */ jsx34("span", { className: cn(planBadgeClass, className), children });
|
|
2766
3516
|
|
|
2767
3517
|
// src/app/integrations/ConnectionRow.tsx
|
|
2768
|
-
import { Fragment as
|
|
3518
|
+
import { Fragment as Fragment7, jsx as jsx35, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
2769
3519
|
var logoShellClass2 = cn(
|
|
2770
3520
|
"flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-lg",
|
|
2771
3521
|
TIMBAL_V2_LOGO_TILE
|
|
@@ -2780,14 +3530,14 @@ var ConnectionRow = ({
|
|
|
2780
3530
|
ariaLabel,
|
|
2781
3531
|
className
|
|
2782
3532
|
}) => {
|
|
2783
|
-
const inner = /* @__PURE__ */
|
|
2784
|
-
logo ? /* @__PURE__ */
|
|
2785
|
-
/* @__PURE__ */
|
|
2786
|
-
/* @__PURE__ */
|
|
2787
|
-
meta ? /* @__PURE__ */
|
|
3533
|
+
const inner = /* @__PURE__ */ jsxs28(Fragment7, { children: [
|
|
3534
|
+
logo ? /* @__PURE__ */ jsx35("span", { className: logoShellClass2, children: logo }) : null,
|
|
3535
|
+
/* @__PURE__ */ jsxs28("div", { className: "min-w-0 flex-1", children: [
|
|
3536
|
+
/* @__PURE__ */ jsx35("p", { className: "truncate text-sm font-normal text-foreground", children: name }),
|
|
3537
|
+
meta ? /* @__PURE__ */ jsx35("p", { className: "truncate text-xs text-muted-foreground", children: meta }) : null
|
|
2788
3538
|
] }),
|
|
2789
|
-
badge ? /* @__PURE__ */
|
|
2790
|
-
action ? /* @__PURE__ */
|
|
3539
|
+
badge ? /* @__PURE__ */ jsx35("span", { className: "shrink-0", children: badge }) : null,
|
|
3540
|
+
action ? /* @__PURE__ */ jsx35("span", { className: "shrink-0", children: action }) : null
|
|
2791
3541
|
] });
|
|
2792
3542
|
const rowClass2 = cn(
|
|
2793
3543
|
"flex w-full items-center gap-3 px-4 py-3 text-left",
|
|
@@ -2795,7 +3545,7 @@ var ConnectionRow = ({
|
|
|
2795
3545
|
className
|
|
2796
3546
|
);
|
|
2797
3547
|
if (onClick) {
|
|
2798
|
-
return /* @__PURE__ */
|
|
3548
|
+
return /* @__PURE__ */ jsx35(
|
|
2799
3549
|
"button",
|
|
2800
3550
|
{
|
|
2801
3551
|
type: "button",
|
|
@@ -2807,7 +3557,7 @@ var ConnectionRow = ({
|
|
|
2807
3557
|
}
|
|
2808
3558
|
);
|
|
2809
3559
|
}
|
|
2810
|
-
return /* @__PURE__ */
|
|
3560
|
+
return /* @__PURE__ */ jsx35("div", { role: "listitem", className: rowClass2, children: inner });
|
|
2811
3561
|
};
|
|
2812
3562
|
var connectionRowListClass = cn(
|
|
2813
3563
|
"overflow-hidden rounded-2xl",
|
|
@@ -2815,12 +3565,12 @@ var connectionRowListClass = cn(
|
|
|
2815
3565
|
);
|
|
2816
3566
|
|
|
2817
3567
|
// src/app/integrations/ConnectionRowList.tsx
|
|
2818
|
-
import { jsx as
|
|
3568
|
+
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
2819
3569
|
var ConnectionRowList = ({
|
|
2820
3570
|
children,
|
|
2821
3571
|
"aria-label": ariaLabel = "Connected integrations",
|
|
2822
3572
|
className
|
|
2823
|
-
}) => /* @__PURE__ */
|
|
3573
|
+
}) => /* @__PURE__ */ jsx36(
|
|
2824
3574
|
"div",
|
|
2825
3575
|
{
|
|
2826
3576
|
role: "list",
|
|
@@ -2831,7 +3581,7 @@ var ConnectionRowList = ({
|
|
|
2831
3581
|
);
|
|
2832
3582
|
|
|
2833
3583
|
// src/app/navigation/SubNav.tsx
|
|
2834
|
-
import { jsx as
|
|
3584
|
+
import { jsx as jsx37 } from "react/jsx-runtime";
|
|
2835
3585
|
var SubNav = ({
|
|
2836
3586
|
items,
|
|
2837
3587
|
activeId,
|
|
@@ -2840,7 +3590,7 @@ var SubNav = ({
|
|
|
2840
3590
|
"aria-label": ariaLabel = "Section navigation",
|
|
2841
3591
|
layoutId
|
|
2842
3592
|
}) => {
|
|
2843
|
-
return /* @__PURE__ */
|
|
3593
|
+
return /* @__PURE__ */ jsx37("nav", { className: cn("aui-app-sub-nav", className), "aria-label": ariaLabel, children: /* @__PURE__ */ jsx37(
|
|
2844
3594
|
PillSegmentedTabs,
|
|
2845
3595
|
{
|
|
2846
3596
|
value: activeId,
|
|
@@ -2854,13 +3604,13 @@ var SubNav = ({
|
|
|
2854
3604
|
};
|
|
2855
3605
|
|
|
2856
3606
|
// src/app/navigation/Breadcrumbs.tsx
|
|
2857
|
-
import { jsx as
|
|
3607
|
+
import { jsx as jsx38, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2858
3608
|
var Breadcrumbs = ({ items, className }) => {
|
|
2859
|
-
return /* @__PURE__ */
|
|
3609
|
+
return /* @__PURE__ */ jsx38("nav", { className: cn("aui-app-breadcrumbs", appBreadcrumbsClass, className), "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx38("ol", { className: "flex flex-wrap items-center gap-1.5", children: items.map((item, index) => {
|
|
2860
3610
|
const isLast = index === items.length - 1;
|
|
2861
|
-
return /* @__PURE__ */
|
|
2862
|
-
index > 0 ? /* @__PURE__ */
|
|
2863
|
-
isLast ? /* @__PURE__ */
|
|
3611
|
+
return /* @__PURE__ */ jsxs29("li", { className: "inline-flex items-center gap-1.5", children: [
|
|
3612
|
+
index > 0 ? /* @__PURE__ */ jsx38("span", { className: "text-muted-foreground/50", "aria-hidden": true, children: "/" }) : null,
|
|
3613
|
+
isLast ? /* @__PURE__ */ jsx38("span", { className: "text-foreground", "aria-current": "page", children: item.label }) : item.href ? /* @__PURE__ */ jsx38("a", { href: item.href, className: appBreadcrumbLinkClass, children: item.label }) : /* @__PURE__ */ jsx38(
|
|
2864
3614
|
"button",
|
|
2865
3615
|
{
|
|
2866
3616
|
type: "button",
|
|
@@ -2874,8 +3624,8 @@ var Breadcrumbs = ({ items, className }) => {
|
|
|
2874
3624
|
};
|
|
2875
3625
|
|
|
2876
3626
|
// src/app/forms/Field.tsx
|
|
2877
|
-
import { useId as
|
|
2878
|
-
import { jsx as
|
|
3627
|
+
import { useId as useId5 } from "react";
|
|
3628
|
+
import { jsx as jsx39, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2879
3629
|
var Field = ({
|
|
2880
3630
|
label,
|
|
2881
3631
|
hint,
|
|
@@ -2884,11 +3634,11 @@ var Field = ({
|
|
|
2884
3634
|
className,
|
|
2885
3635
|
htmlFor
|
|
2886
3636
|
}) => {
|
|
2887
|
-
return /* @__PURE__ */
|
|
2888
|
-
/* @__PURE__ */
|
|
3637
|
+
return /* @__PURE__ */ jsxs30("div", { className: cn("aui-app-field", appFieldClass, className), children: [
|
|
3638
|
+
/* @__PURE__ */ jsx39("label", { className: appFieldLabelClass, htmlFor, children: label }),
|
|
2889
3639
|
children,
|
|
2890
|
-
hint && !error ? /* @__PURE__ */
|
|
2891
|
-
error ? /* @__PURE__ */
|
|
3640
|
+
hint && !error ? /* @__PURE__ */ jsx39("p", { className: appFieldHintClass, children: hint }) : null,
|
|
3641
|
+
error ? /* @__PURE__ */ jsx39("p", { className: "text-xs text-destructive", role: "alert", children: error }) : null
|
|
2892
3642
|
] });
|
|
2893
3643
|
};
|
|
2894
3644
|
var FieldInput = ({
|
|
@@ -2900,9 +3650,9 @@ var FieldInput = ({
|
|
|
2900
3650
|
id,
|
|
2901
3651
|
...inputProps
|
|
2902
3652
|
}) => {
|
|
2903
|
-
const autoId =
|
|
3653
|
+
const autoId = useId5();
|
|
2904
3654
|
const inputId = id ?? inputProps.name ?? autoId;
|
|
2905
|
-
return /* @__PURE__ */
|
|
3655
|
+
return /* @__PURE__ */ jsx39(
|
|
2906
3656
|
Field,
|
|
2907
3657
|
{
|
|
2908
3658
|
label,
|
|
@@ -2910,7 +3660,7 @@ var FieldInput = ({
|
|
|
2910
3660
|
error,
|
|
2911
3661
|
htmlFor: inputId,
|
|
2912
3662
|
className: fieldClassName,
|
|
2913
|
-
children: /* @__PURE__ */
|
|
3663
|
+
children: /* @__PURE__ */ jsx39(
|
|
2914
3664
|
"input",
|
|
2915
3665
|
{
|
|
2916
3666
|
id: inputId,
|
|
@@ -2924,8 +3674,8 @@ var FieldInput = ({
|
|
|
2924
3674
|
};
|
|
2925
3675
|
|
|
2926
3676
|
// src/app/forms/FieldTextarea.tsx
|
|
2927
|
-
import { useId as
|
|
2928
|
-
import { jsx as
|
|
3677
|
+
import { useId as useId6 } from "react";
|
|
3678
|
+
import { jsx as jsx40 } from "react/jsx-runtime";
|
|
2929
3679
|
var textareaClass = cn(
|
|
2930
3680
|
appInputClass,
|
|
2931
3681
|
"min-h-[5.5rem] resize-y py-2.5 leading-relaxed"
|
|
@@ -2939,9 +3689,9 @@ var FieldTextarea = ({
|
|
|
2939
3689
|
id,
|
|
2940
3690
|
...props
|
|
2941
3691
|
}) => {
|
|
2942
|
-
const autoId =
|
|
3692
|
+
const autoId = useId6();
|
|
2943
3693
|
const textareaId = id ?? props.name ?? autoId;
|
|
2944
|
-
return /* @__PURE__ */
|
|
3694
|
+
return /* @__PURE__ */ jsx40(
|
|
2945
3695
|
Field,
|
|
2946
3696
|
{
|
|
2947
3697
|
label,
|
|
@@ -2949,7 +3699,7 @@ var FieldTextarea = ({
|
|
|
2949
3699
|
error,
|
|
2950
3700
|
htmlFor: textareaId,
|
|
2951
3701
|
className: fieldClassName,
|
|
2952
|
-
children: /* @__PURE__ */
|
|
3702
|
+
children: /* @__PURE__ */ jsx40(
|
|
2953
3703
|
"textarea",
|
|
2954
3704
|
{
|
|
2955
3705
|
id: textareaId,
|
|
@@ -2963,9 +3713,9 @@ var FieldTextarea = ({
|
|
|
2963
3713
|
};
|
|
2964
3714
|
|
|
2965
3715
|
// src/app/forms/FieldSelect.tsx
|
|
2966
|
-
import { useId as
|
|
3716
|
+
import { useId as useId7 } from "react";
|
|
2967
3717
|
import { ChevronDownIcon } from "lucide-react";
|
|
2968
|
-
import { jsx as
|
|
3718
|
+
import { jsx as jsx41, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
2969
3719
|
var selectWrapClass = "relative";
|
|
2970
3720
|
var selectClass = cn(
|
|
2971
3721
|
appInputClass,
|
|
@@ -2981,9 +3731,9 @@ var FieldSelect = ({
|
|
|
2981
3731
|
id,
|
|
2982
3732
|
...props
|
|
2983
3733
|
}) => {
|
|
2984
|
-
const autoId =
|
|
3734
|
+
const autoId = useId7();
|
|
2985
3735
|
const selectId = id ?? props.name ?? autoId;
|
|
2986
|
-
return /* @__PURE__ */
|
|
3736
|
+
return /* @__PURE__ */ jsx41(
|
|
2987
3737
|
Field,
|
|
2988
3738
|
{
|
|
2989
3739
|
label,
|
|
@@ -2991,8 +3741,8 @@ var FieldSelect = ({
|
|
|
2991
3741
|
error,
|
|
2992
3742
|
htmlFor: selectId,
|
|
2993
3743
|
className: fieldClassName,
|
|
2994
|
-
children: /* @__PURE__ */
|
|
2995
|
-
/* @__PURE__ */
|
|
3744
|
+
children: /* @__PURE__ */ jsxs31("div", { className: selectWrapClass, children: [
|
|
3745
|
+
/* @__PURE__ */ jsx41(
|
|
2996
3746
|
"select",
|
|
2997
3747
|
{
|
|
2998
3748
|
id: selectId,
|
|
@@ -3002,7 +3752,7 @@ var FieldSelect = ({
|
|
|
3002
3752
|
children
|
|
3003
3753
|
}
|
|
3004
3754
|
),
|
|
3005
|
-
/* @__PURE__ */
|
|
3755
|
+
/* @__PURE__ */ jsx41(
|
|
3006
3756
|
ChevronDownIcon,
|
|
3007
3757
|
{
|
|
3008
3758
|
className: "pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2 text-muted-foreground",
|
|
@@ -3015,8 +3765,8 @@ var FieldSelect = ({
|
|
|
3015
3765
|
};
|
|
3016
3766
|
|
|
3017
3767
|
// src/app/forms/FieldSwitch.tsx
|
|
3018
|
-
import { useId as
|
|
3019
|
-
import { jsx as
|
|
3768
|
+
import { useId as useId8 } from "react";
|
|
3769
|
+
import { jsx as jsx42, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
3020
3770
|
var trackClass = cn(
|
|
3021
3771
|
"relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-[background,box-shadow,border-color] duration-200",
|
|
3022
3772
|
"peer-focus-visible:ring-2 peer-focus-visible:ring-foreground/10",
|
|
@@ -3036,9 +3786,9 @@ var FieldSwitch = ({
|
|
|
3036
3786
|
id,
|
|
3037
3787
|
...props
|
|
3038
3788
|
}) => {
|
|
3039
|
-
const autoId =
|
|
3789
|
+
const autoId = useId8();
|
|
3040
3790
|
const inputId = id ?? props.name ?? autoId;
|
|
3041
|
-
return /* @__PURE__ */
|
|
3791
|
+
return /* @__PURE__ */ jsxs32(
|
|
3042
3792
|
"label",
|
|
3043
3793
|
{
|
|
3044
3794
|
className: cn(
|
|
@@ -3047,8 +3797,8 @@ var FieldSwitch = ({
|
|
|
3047
3797
|
),
|
|
3048
3798
|
htmlFor: inputId,
|
|
3049
3799
|
children: [
|
|
3050
|
-
/* @__PURE__ */
|
|
3051
|
-
/* @__PURE__ */
|
|
3800
|
+
/* @__PURE__ */ jsxs32("span", { className: "relative mt-0.5", children: [
|
|
3801
|
+
/* @__PURE__ */ jsx42(
|
|
3052
3802
|
"input",
|
|
3053
3803
|
{
|
|
3054
3804
|
id: inputId,
|
|
@@ -3058,11 +3808,11 @@ var FieldSwitch = ({
|
|
|
3058
3808
|
...props
|
|
3059
3809
|
}
|
|
3060
3810
|
),
|
|
3061
|
-
/* @__PURE__ */
|
|
3811
|
+
/* @__PURE__ */ jsx42("span", { className: trackClass, "aria-hidden": true, children: /* @__PURE__ */ jsx42("span", { className: thumbClass }) })
|
|
3062
3812
|
] }),
|
|
3063
|
-
/* @__PURE__ */
|
|
3064
|
-
/* @__PURE__ */
|
|
3065
|
-
description ? /* @__PURE__ */
|
|
3813
|
+
/* @__PURE__ */ jsxs32("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
|
|
3814
|
+
/* @__PURE__ */ jsx42("span", { className: "text-sm font-medium text-foreground", children: label }),
|
|
3815
|
+
description ? /* @__PURE__ */ jsx42("span", { className: "text-xs text-muted-foreground", children: description }) : null
|
|
3066
3816
|
] })
|
|
3067
3817
|
]
|
|
3068
3818
|
}
|
|
@@ -3071,13 +3821,13 @@ var FieldSwitch = ({
|
|
|
3071
3821
|
|
|
3072
3822
|
// src/app/forms/SearchInput.tsx
|
|
3073
3823
|
import { SearchIcon } from "lucide-react";
|
|
3074
|
-
import { jsx as
|
|
3824
|
+
import { jsx as jsx43, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
3075
3825
|
var SearchInput = ({
|
|
3076
3826
|
className,
|
|
3077
3827
|
placeholder = "Search\u2026",
|
|
3078
3828
|
...props
|
|
3079
3829
|
}) => {
|
|
3080
|
-
return /* @__PURE__ */
|
|
3830
|
+
return /* @__PURE__ */ jsxs33(
|
|
3081
3831
|
"label",
|
|
3082
3832
|
{
|
|
3083
3833
|
className: cn(
|
|
@@ -3086,8 +3836,8 @@ var SearchInput = ({
|
|
|
3086
3836
|
className
|
|
3087
3837
|
),
|
|
3088
3838
|
children: [
|
|
3089
|
-
/* @__PURE__ */
|
|
3090
|
-
/* @__PURE__ */
|
|
3839
|
+
/* @__PURE__ */ jsx43(SearchIcon, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": true }),
|
|
3840
|
+
/* @__PURE__ */ jsx43(
|
|
3091
3841
|
"input",
|
|
3092
3842
|
{
|
|
3093
3843
|
type: "search",
|
|
@@ -3102,26 +3852,26 @@ var SearchInput = ({
|
|
|
3102
3852
|
};
|
|
3103
3853
|
|
|
3104
3854
|
// src/app/forms/FormSection.tsx
|
|
3105
|
-
import { jsx as
|
|
3855
|
+
import { jsx as jsx44, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
3106
3856
|
var FormSection = ({ title, children, className }) => {
|
|
3107
3857
|
const density = useAppDensity();
|
|
3108
3858
|
const sectionClass = useAppDensityClass("section");
|
|
3109
|
-
return /* @__PURE__ */
|
|
3859
|
+
return /* @__PURE__ */ jsxs34(
|
|
3110
3860
|
"fieldset",
|
|
3111
3861
|
{
|
|
3112
3862
|
className: cn("aui-app-form-section", sectionClass, "border-0 p-0", className),
|
|
3113
3863
|
children: [
|
|
3114
|
-
title ? /* @__PURE__ */
|
|
3115
|
-
/* @__PURE__ */
|
|
3864
|
+
title ? /* @__PURE__ */ jsx44("legend", { className: cn(appSectionTitleClass, "mb-3 px-0"), children: title }) : null,
|
|
3865
|
+
/* @__PURE__ */ jsx44("div", { className: cn("flex flex-col", density === "compact" ? "gap-2" : "gap-4"), children })
|
|
3116
3866
|
]
|
|
3117
3867
|
}
|
|
3118
3868
|
);
|
|
3119
3869
|
};
|
|
3120
3870
|
|
|
3121
3871
|
// src/app/data/FilterBar.tsx
|
|
3122
|
-
import { jsx as
|
|
3872
|
+
import { jsx as jsx45 } from "react/jsx-runtime";
|
|
3123
3873
|
var FilterBar = ({ children, className }) => {
|
|
3124
|
-
return /* @__PURE__ */
|
|
3874
|
+
return /* @__PURE__ */ jsx45(
|
|
3125
3875
|
"div",
|
|
3126
3876
|
{
|
|
3127
3877
|
className: cn("aui-app-filter-bar", appFilterBarClass, className),
|
|
@@ -3133,41 +3883,501 @@ var FilterBar = ({ children, className }) => {
|
|
|
3133
3883
|
};
|
|
3134
3884
|
|
|
3135
3885
|
// src/app/data/FilterField.tsx
|
|
3136
|
-
import { jsx as
|
|
3886
|
+
import { jsx as jsx46, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
3137
3887
|
var FilterField = ({
|
|
3138
3888
|
label,
|
|
3139
3889
|
children,
|
|
3140
3890
|
className
|
|
3141
3891
|
}) => {
|
|
3142
|
-
return /* @__PURE__ */
|
|
3143
|
-
label ? /* @__PURE__ */
|
|
3892
|
+
return /* @__PURE__ */ jsxs35("div", { className: cn("aui-app-filter-field", appFieldClass, className), children: [
|
|
3893
|
+
label ? /* @__PURE__ */ jsx46("span", { className: appFieldLabelClass, children: label }) : null,
|
|
3144
3894
|
children
|
|
3145
3895
|
] });
|
|
3146
3896
|
};
|
|
3147
3897
|
|
|
3898
|
+
// src/app/data/FilterDropdown.tsx
|
|
3899
|
+
import { useEffect as useEffect3, useMemo as useMemo2, useState as useState5 } from "react";
|
|
3900
|
+
import { ChevronRightIcon, ListFilterIcon, XIcon as XIcon2 } from "lucide-react";
|
|
3901
|
+
import { jsx as jsx47, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
3902
|
+
var DEFAULT_PRESETS = [
|
|
3903
|
+
{ id: "last_7_days", label: "Last 7 days" },
|
|
3904
|
+
{ id: "last_30_days", label: "Last 30 days" },
|
|
3905
|
+
{ id: "last_90_days", label: "Last 90 days" },
|
|
3906
|
+
{ id: "this_month", label: "This month" },
|
|
3907
|
+
{ id: "this_year", label: "This year" },
|
|
3908
|
+
{ id: "custom", label: "Custom range" }
|
|
3909
|
+
];
|
|
3910
|
+
var DEFAULT_OPERATORS = [
|
|
3911
|
+
{ id: "gt", label: "Greater than" },
|
|
3912
|
+
{ id: "lt", label: "Less than" },
|
|
3913
|
+
{ id: "eq", label: "Equals" }
|
|
3914
|
+
];
|
|
3915
|
+
function asArray(v) {
|
|
3916
|
+
return Array.isArray(v) ? v : [];
|
|
3917
|
+
}
|
|
3918
|
+
function asText(v) {
|
|
3919
|
+
return typeof v === "string" ? v : "";
|
|
3920
|
+
}
|
|
3921
|
+
function asDate(v) {
|
|
3922
|
+
return v && !Array.isArray(v) && typeof v === "object" && "preset" in v ? v : { preset: null };
|
|
3923
|
+
}
|
|
3924
|
+
function asNumeric(v) {
|
|
3925
|
+
return v && !Array.isArray(v) && typeof v === "object" && "operator" in v ? v : { operator: "gt", value: "" };
|
|
3926
|
+
}
|
|
3927
|
+
var OPERATOR_SYMBOL = {
|
|
3928
|
+
gt: ">",
|
|
3929
|
+
lt: "<",
|
|
3930
|
+
eq: "="
|
|
3931
|
+
};
|
|
3932
|
+
function FilterDropdown({
|
|
3933
|
+
fields,
|
|
3934
|
+
value,
|
|
3935
|
+
defaultValue,
|
|
3936
|
+
onChange,
|
|
3937
|
+
label = "Filter",
|
|
3938
|
+
align = "start",
|
|
3939
|
+
showActiveChips = true,
|
|
3940
|
+
className
|
|
3941
|
+
}) {
|
|
3942
|
+
const [isOpen, setIsOpen] = useState5(false);
|
|
3943
|
+
const [activeId, setActiveId] = useState5(fields[0]?.id ?? null);
|
|
3944
|
+
const [isMobile, setIsMobile] = useState5(false);
|
|
3945
|
+
const isControlled = value !== void 0;
|
|
3946
|
+
const [internal, setInternal] = useState5(defaultValue ?? {});
|
|
3947
|
+
const values = isControlled ? value : internal;
|
|
3948
|
+
useEffect3(() => {
|
|
3949
|
+
const checkMobile = () => setIsMobile(window.innerWidth < 768);
|
|
3950
|
+
checkMobile();
|
|
3951
|
+
window.addEventListener("resize", checkMobile);
|
|
3952
|
+
return () => window.removeEventListener("resize", checkMobile);
|
|
3953
|
+
}, []);
|
|
3954
|
+
useEffect3(() => {
|
|
3955
|
+
if (!fields.some((f) => f.id === activeId)) {
|
|
3956
|
+
setActiveId(fields[0]?.id ?? null);
|
|
3957
|
+
}
|
|
3958
|
+
}, [fields, activeId]);
|
|
3959
|
+
const commit = (id, next) => {
|
|
3960
|
+
const merged = { ...values, [id]: next };
|
|
3961
|
+
if (!isControlled) setInternal(merged);
|
|
3962
|
+
onChange?.(merged);
|
|
3963
|
+
};
|
|
3964
|
+
const clearAll = () => {
|
|
3965
|
+
if (!isControlled) setInternal({});
|
|
3966
|
+
onChange?.({});
|
|
3967
|
+
};
|
|
3968
|
+
const activeIdx = fields.findIndex((f) => f.id === activeId);
|
|
3969
|
+
const activeField = activeIdx >= 0 ? fields[activeIdx] : void 0;
|
|
3970
|
+
const chips = [];
|
|
3971
|
+
for (const field of fields) {
|
|
3972
|
+
const v = values[field.id];
|
|
3973
|
+
if (field.type === "multiselect") {
|
|
3974
|
+
const selected = asArray(v);
|
|
3975
|
+
for (const optionValue of selected) {
|
|
3976
|
+
const opt = field.options?.find((o) => o.value === optionValue);
|
|
3977
|
+
chips.push({
|
|
3978
|
+
id: `${field.id}:${optionValue}`,
|
|
3979
|
+
label: `${field.label}: ${opt?.label ?? optionValue}`,
|
|
3980
|
+
remove: () => commit(field.id, selected.filter((x) => x !== optionValue))
|
|
3981
|
+
});
|
|
3982
|
+
}
|
|
3983
|
+
} else if (field.type === "text") {
|
|
3984
|
+
const text = asText(v);
|
|
3985
|
+
if (text) {
|
|
3986
|
+
chips.push({
|
|
3987
|
+
id: field.id,
|
|
3988
|
+
label: `${field.label}: ${text}`,
|
|
3989
|
+
remove: () => commit(field.id, "")
|
|
3990
|
+
});
|
|
3991
|
+
}
|
|
3992
|
+
} else if (field.type === "numeric") {
|
|
3993
|
+
const n = asNumeric(v);
|
|
3994
|
+
if (n.value) {
|
|
3995
|
+
chips.push({
|
|
3996
|
+
id: field.id,
|
|
3997
|
+
label: `${field.label} ${OPERATOR_SYMBOL[n.operator]} ${n.value}`,
|
|
3998
|
+
remove: () => commit(field.id, null)
|
|
3999
|
+
});
|
|
4000
|
+
}
|
|
4001
|
+
} else if (field.type === "daterange") {
|
|
4002
|
+
const d = asDate(v);
|
|
4003
|
+
if (d.preset) {
|
|
4004
|
+
const presetLabel = d.preset === "custom" ? `${d.from || "\u2026"} \u2013 ${d.to || "\u2026"}` : (field.presets ?? DEFAULT_PRESETS).find((p) => p.id === d.preset)?.label ?? d.preset;
|
|
4005
|
+
chips.push({
|
|
4006
|
+
id: field.id,
|
|
4007
|
+
label: `${field.label}: ${presetLabel}`,
|
|
4008
|
+
remove: () => commit(field.id, { preset: null })
|
|
4009
|
+
});
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
return /* @__PURE__ */ jsxs36("div", { className: cn("flex flex-wrap items-center gap-2", className), children: [
|
|
4014
|
+
/* @__PURE__ */ jsxs36(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
|
|
4015
|
+
/* @__PURE__ */ jsx47(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx47(
|
|
4016
|
+
Button,
|
|
4017
|
+
{
|
|
4018
|
+
variant: "outline",
|
|
4019
|
+
size: "sm",
|
|
4020
|
+
className: "border-dashed font-medium text-muted-foreground hover:text-foreground",
|
|
4021
|
+
iconLeading: /* @__PURE__ */ jsx47(ListFilterIcon, { className: "size-4" }),
|
|
4022
|
+
children: label
|
|
4023
|
+
}
|
|
4024
|
+
) }),
|
|
4025
|
+
/* @__PURE__ */ jsx47(
|
|
4026
|
+
PopoverContent,
|
|
4027
|
+
{
|
|
4028
|
+
variant: "list",
|
|
4029
|
+
align,
|
|
4030
|
+
className: "overflow-visible border-none bg-transparent p-0 shadow-none max-w-[calc(100vw-32px)] md:max-w-none",
|
|
4031
|
+
children: /* @__PURE__ */ jsxs36("div", { className: "relative flex flex-col md:flex-row items-stretch md:items-start w-[calc(100vw-32px)] max-w-[340px] md:w-auto md:max-w-none", children: [
|
|
4032
|
+
/* @__PURE__ */ jsx47("div", { className: "w-full md:w-56 rounded-xl border border-border bg-popover p-1.5 shadow-lg", children: fields.map((field) => {
|
|
4033
|
+
const isActive = activeId === field.id;
|
|
4034
|
+
return /* @__PURE__ */ jsxs36(
|
|
4035
|
+
"button",
|
|
4036
|
+
{
|
|
4037
|
+
type: "button",
|
|
4038
|
+
className: cn(
|
|
4039
|
+
"flex w-full items-center justify-between rounded-lg px-3 py-2 text-sm text-left transition-colors outline-none",
|
|
4040
|
+
isActive ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
4041
|
+
),
|
|
4042
|
+
onMouseEnter: () => !isMobile && setActiveId(field.id),
|
|
4043
|
+
onClick: () => setActiveId(field.id),
|
|
4044
|
+
children: [
|
|
4045
|
+
/* @__PURE__ */ jsxs36("span", { className: "flex items-center gap-2", children: [
|
|
4046
|
+
field.icon,
|
|
4047
|
+
/* @__PURE__ */ jsx47("span", { children: field.label })
|
|
4048
|
+
] }),
|
|
4049
|
+
/* @__PURE__ */ jsx47(ChevronRightIcon, { className: "size-4 text-muted-foreground/50" })
|
|
4050
|
+
]
|
|
4051
|
+
},
|
|
4052
|
+
field.id
|
|
4053
|
+
);
|
|
4054
|
+
}) }),
|
|
4055
|
+
activeField && /* @__PURE__ */ jsx47(
|
|
4056
|
+
"div",
|
|
4057
|
+
{
|
|
4058
|
+
className: "relative left-0 mt-2 w-full md:absolute md:left-[calc(100%+6px)] md:w-80 rounded-xl border border-border bg-popover p-3 shadow-lg transition-all duration-150 md:mt-0",
|
|
4059
|
+
style: isMobile ? {} : { top: `${activeIdx * 36 + 6}px` },
|
|
4060
|
+
children: /* @__PURE__ */ jsx47(
|
|
4061
|
+
FilterFieldControl,
|
|
4062
|
+
{
|
|
4063
|
+
field: activeField,
|
|
4064
|
+
value: values[activeField.id],
|
|
4065
|
+
onChange: (next) => commit(activeField.id, next),
|
|
4066
|
+
onClose: () => setIsOpen(false)
|
|
4067
|
+
}
|
|
4068
|
+
)
|
|
4069
|
+
}
|
|
4070
|
+
)
|
|
4071
|
+
] })
|
|
4072
|
+
}
|
|
4073
|
+
)
|
|
4074
|
+
] }),
|
|
4075
|
+
showActiveChips && chips.map((chip) => /* @__PURE__ */ jsx47(FilterChip, { label: chip.label, onRemove: chip.remove }, chip.id)),
|
|
4076
|
+
showActiveChips && chips.length > 0 && /* @__PURE__ */ jsx47(
|
|
4077
|
+
"button",
|
|
4078
|
+
{
|
|
4079
|
+
type: "button",
|
|
4080
|
+
onClick: clearAll,
|
|
4081
|
+
className: "rounded-full px-3 py-1 text-sm font-medium text-muted-foreground outline-none transition-colors hover:text-foreground",
|
|
4082
|
+
children: "Clear all"
|
|
4083
|
+
}
|
|
4084
|
+
)
|
|
4085
|
+
] });
|
|
4086
|
+
}
|
|
4087
|
+
function FilterChip({ label, onRemove }) {
|
|
4088
|
+
return /* @__PURE__ */ jsxs36("span", { className: "inline-flex h-9 items-center gap-1.5 rounded-full border border-border bg-muted/40 pl-3 pr-1.5 text-sm font-medium text-foreground", children: [
|
|
4089
|
+
/* @__PURE__ */ jsx47("span", { className: "truncate", children: label }),
|
|
4090
|
+
/* @__PURE__ */ jsx47(
|
|
4091
|
+
"button",
|
|
4092
|
+
{
|
|
4093
|
+
type: "button",
|
|
4094
|
+
onClick: onRemove,
|
|
4095
|
+
"aria-label": `Remove ${label}`,
|
|
4096
|
+
className: "flex size-5 items-center justify-center rounded-full text-muted-foreground outline-none transition-colors hover:bg-muted hover:text-foreground",
|
|
4097
|
+
children: /* @__PURE__ */ jsx47(XIcon2, { className: "size-3.5" })
|
|
4098
|
+
}
|
|
4099
|
+
)
|
|
4100
|
+
] });
|
|
4101
|
+
}
|
|
4102
|
+
function FilterFieldControl({
|
|
4103
|
+
field,
|
|
4104
|
+
value,
|
|
4105
|
+
onChange,
|
|
4106
|
+
onClose
|
|
4107
|
+
}) {
|
|
4108
|
+
switch (field.type) {
|
|
4109
|
+
case "multiselect":
|
|
4110
|
+
return /* @__PURE__ */ jsx47(MultiSelectControl, { field, value: asArray(value), onChange });
|
|
4111
|
+
case "text":
|
|
4112
|
+
return /* @__PURE__ */ jsx47(TextControl, { field, value: asText(value), onChange, onClose });
|
|
4113
|
+
case "daterange":
|
|
4114
|
+
return /* @__PURE__ */ jsx47(DateRangeControl, { field, value: asDate(value), onChange, onClose });
|
|
4115
|
+
case "numeric":
|
|
4116
|
+
return /* @__PURE__ */ jsx47(NumericControl, { field, value: asNumeric(value), onChange, onClose });
|
|
4117
|
+
default:
|
|
4118
|
+
return null;
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
function MultiSelectControl({
|
|
4122
|
+
field,
|
|
4123
|
+
value,
|
|
4124
|
+
onChange
|
|
4125
|
+
}) {
|
|
4126
|
+
const options = field.options ?? [];
|
|
4127
|
+
const [search, setSearch] = useState5("");
|
|
4128
|
+
const searchable = field.searchable ?? options.length > 8;
|
|
4129
|
+
const filtered = useMemo2(() => {
|
|
4130
|
+
if (!search) return options;
|
|
4131
|
+
const q = search.toLowerCase();
|
|
4132
|
+
return options.filter(
|
|
4133
|
+
(o) => o.label.toLowerCase().includes(q) || o.hint?.toLowerCase().includes(q)
|
|
4134
|
+
);
|
|
4135
|
+
}, [options, search]);
|
|
4136
|
+
const toggle = (optionValue) => {
|
|
4137
|
+
onChange(
|
|
4138
|
+
value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]
|
|
4139
|
+
);
|
|
4140
|
+
};
|
|
4141
|
+
return /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2.5", children: [
|
|
4142
|
+
searchable && /* @__PURE__ */ jsx47(
|
|
4143
|
+
SearchInput,
|
|
4144
|
+
{
|
|
4145
|
+
placeholder: field.searchPlaceholder ?? "Search\u2026",
|
|
4146
|
+
value: search,
|
|
4147
|
+
onChange: (e) => setSearch(e.target.value),
|
|
4148
|
+
className: "w-full min-w-0"
|
|
4149
|
+
}
|
|
4150
|
+
),
|
|
4151
|
+
/* @__PURE__ */ jsx47("div", { className: "flex max-h-48 flex-col gap-1 overflow-y-auto pr-1", children: filtered.length === 0 ? /* @__PURE__ */ jsx47("p", { className: "py-4 text-center text-xs text-muted-foreground", children: "No options found" }) : filtered.map((option) => /* @__PURE__ */ jsxs36(
|
|
4152
|
+
"label",
|
|
4153
|
+
{
|
|
4154
|
+
className: "flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 transition-colors hover:bg-muted/50",
|
|
4155
|
+
children: [
|
|
4156
|
+
/* @__PURE__ */ jsx47(
|
|
4157
|
+
Checkbox,
|
|
4158
|
+
{
|
|
4159
|
+
checked: value.includes(option.value),
|
|
4160
|
+
onCheckedChange: () => toggle(option.value)
|
|
4161
|
+
}
|
|
4162
|
+
),
|
|
4163
|
+
option.icon,
|
|
4164
|
+
/* @__PURE__ */ jsxs36("span", { className: "flex min-w-0 flex-col", children: [
|
|
4165
|
+
/* @__PURE__ */ jsx47("span", { className: "truncate text-sm font-medium text-foreground", children: option.label }),
|
|
4166
|
+
option.hint && /* @__PURE__ */ jsx47("span", { className: "truncate text-xs text-muted-foreground", children: option.hint })
|
|
4167
|
+
] })
|
|
4168
|
+
]
|
|
4169
|
+
},
|
|
4170
|
+
option.value
|
|
4171
|
+
)) })
|
|
4172
|
+
] });
|
|
4173
|
+
}
|
|
4174
|
+
function TextControl({
|
|
4175
|
+
field,
|
|
4176
|
+
value,
|
|
4177
|
+
onChange,
|
|
4178
|
+
onClose
|
|
4179
|
+
}) {
|
|
4180
|
+
const [draft, setDraft] = useState5(value);
|
|
4181
|
+
useEffect3(() => setDraft(value), [value]);
|
|
4182
|
+
return /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2.5", children: [
|
|
4183
|
+
/* @__PURE__ */ jsx47(
|
|
4184
|
+
"input",
|
|
4185
|
+
{
|
|
4186
|
+
type: "text",
|
|
4187
|
+
placeholder: field.placeholder ?? "Type a value\u2026",
|
|
4188
|
+
value: draft,
|
|
4189
|
+
onChange: (e) => setDraft(e.target.value),
|
|
4190
|
+
onKeyDown: (e) => {
|
|
4191
|
+
if (e.key === "Enter") {
|
|
4192
|
+
onChange(draft);
|
|
4193
|
+
onClose();
|
|
4194
|
+
}
|
|
4195
|
+
},
|
|
4196
|
+
className: controlClass({ size: "sm" }, "w-full")
|
|
4197
|
+
}
|
|
4198
|
+
),
|
|
4199
|
+
/* @__PURE__ */ jsx47(
|
|
4200
|
+
ApplyClear,
|
|
4201
|
+
{
|
|
4202
|
+
onClear: () => {
|
|
4203
|
+
setDraft("");
|
|
4204
|
+
onChange("");
|
|
4205
|
+
onClose();
|
|
4206
|
+
},
|
|
4207
|
+
onApply: () => {
|
|
4208
|
+
onChange(draft);
|
|
4209
|
+
onClose();
|
|
4210
|
+
}
|
|
4211
|
+
}
|
|
4212
|
+
)
|
|
4213
|
+
] });
|
|
4214
|
+
}
|
|
4215
|
+
function DateRangeControl({
|
|
4216
|
+
field,
|
|
4217
|
+
value,
|
|
4218
|
+
onChange,
|
|
4219
|
+
onClose
|
|
4220
|
+
}) {
|
|
4221
|
+
const presets = field.presets ?? DEFAULT_PRESETS;
|
|
4222
|
+
const [from, setFrom] = useState5(value.from ?? "");
|
|
4223
|
+
const [to, setTo] = useState5(value.to ?? "");
|
|
4224
|
+
return /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-1", children: [
|
|
4225
|
+
presets.map((preset) => {
|
|
4226
|
+
const isSelected = value.preset === preset.id;
|
|
4227
|
+
return /* @__PURE__ */ jsxs36(
|
|
4228
|
+
"button",
|
|
4229
|
+
{
|
|
4230
|
+
type: "button",
|
|
4231
|
+
onClick: () => {
|
|
4232
|
+
if (preset.id === "custom") {
|
|
4233
|
+
onChange({ preset: "custom", from, to });
|
|
4234
|
+
} else {
|
|
4235
|
+
onChange({ preset: preset.id });
|
|
4236
|
+
onClose();
|
|
4237
|
+
}
|
|
4238
|
+
},
|
|
4239
|
+
className: cn(
|
|
4240
|
+
"flex w-full items-center justify-between rounded-lg px-2.5 py-1.5 text-left text-sm transition-colors outline-none",
|
|
4241
|
+
isSelected ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
4242
|
+
),
|
|
4243
|
+
children: [
|
|
4244
|
+
/* @__PURE__ */ jsx47("span", { children: preset.label }),
|
|
4245
|
+
preset.hint && /* @__PURE__ */ jsx47("span", { className: "text-xs text-muted-foreground/70", children: preset.hint })
|
|
4246
|
+
]
|
|
4247
|
+
},
|
|
4248
|
+
preset.id
|
|
4249
|
+
);
|
|
4250
|
+
}),
|
|
4251
|
+
value.preset === "custom" && /* @__PURE__ */ jsxs36("div", { className: "mt-2 flex flex-col gap-2 border-t border-border/40 pt-2", children: [
|
|
4252
|
+
/* @__PURE__ */ jsxs36("div", { className: "flex items-center gap-2", children: [
|
|
4253
|
+
/* @__PURE__ */ jsx47(
|
|
4254
|
+
"input",
|
|
4255
|
+
{
|
|
4256
|
+
type: "date",
|
|
4257
|
+
value: from,
|
|
4258
|
+
onChange: (e) => setFrom(e.target.value),
|
|
4259
|
+
className: controlClass({ size: "sm" }, "w-full text-xs")
|
|
4260
|
+
}
|
|
4261
|
+
),
|
|
4262
|
+
/* @__PURE__ */ jsx47("span", { className: "text-xs text-muted-foreground", children: "to" }),
|
|
4263
|
+
/* @__PURE__ */ jsx47(
|
|
4264
|
+
"input",
|
|
4265
|
+
{
|
|
4266
|
+
type: "date",
|
|
4267
|
+
value: to,
|
|
4268
|
+
onChange: (e) => setTo(e.target.value),
|
|
4269
|
+
className: controlClass({ size: "sm" }, "w-full text-xs")
|
|
4270
|
+
}
|
|
4271
|
+
)
|
|
4272
|
+
] }),
|
|
4273
|
+
/* @__PURE__ */ jsx47("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx47(
|
|
4274
|
+
Button,
|
|
4275
|
+
{
|
|
4276
|
+
color: "primary",
|
|
4277
|
+
size: "sm",
|
|
4278
|
+
className: "h-8 px-3",
|
|
4279
|
+
onClick: () => {
|
|
4280
|
+
onChange({ preset: "custom", from, to });
|
|
4281
|
+
onClose();
|
|
4282
|
+
},
|
|
4283
|
+
children: "Apply"
|
|
4284
|
+
}
|
|
4285
|
+
) })
|
|
4286
|
+
] })
|
|
4287
|
+
] });
|
|
4288
|
+
}
|
|
4289
|
+
function NumericControl({
|
|
4290
|
+
field,
|
|
4291
|
+
value,
|
|
4292
|
+
onChange,
|
|
4293
|
+
onClose
|
|
4294
|
+
}) {
|
|
4295
|
+
const operators = field.operators ?? DEFAULT_OPERATORS;
|
|
4296
|
+
const [operator, setOperator] = useState5(value.operator);
|
|
4297
|
+
const [draft, setDraft] = useState5(value.value);
|
|
4298
|
+
useEffect3(() => {
|
|
4299
|
+
setOperator(value.operator);
|
|
4300
|
+
setDraft(value.value);
|
|
4301
|
+
}, [value.operator, value.value]);
|
|
4302
|
+
return /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2.5", children: [
|
|
4303
|
+
/* @__PURE__ */ jsxs36("div", { className: "flex items-center gap-2", children: [
|
|
4304
|
+
/* @__PURE__ */ jsxs36(Select, { value: operator, onValueChange: (v) => setOperator(v), children: [
|
|
4305
|
+
/* @__PURE__ */ jsx47(SelectTrigger, { size: "sm", className: "shrink-0", children: /* @__PURE__ */ jsx47(SelectValue, {}) }),
|
|
4306
|
+
/* @__PURE__ */ jsx47(SelectContent, { children: operators.map((op) => /* @__PURE__ */ jsx47(SelectItem, { value: op.id, children: op.label }, op.id)) })
|
|
4307
|
+
] }),
|
|
4308
|
+
/* @__PURE__ */ jsx47(
|
|
4309
|
+
"input",
|
|
4310
|
+
{
|
|
4311
|
+
type: "text",
|
|
4312
|
+
inputMode: "decimal",
|
|
4313
|
+
placeholder: field.placeholder ?? "0.00",
|
|
4314
|
+
value: draft,
|
|
4315
|
+
onChange: (e) => setDraft(e.target.value),
|
|
4316
|
+
onKeyDown: (e) => {
|
|
4317
|
+
if (e.key === "Enter") {
|
|
4318
|
+
onChange(draft ? { operator, value: draft } : null);
|
|
4319
|
+
onClose();
|
|
4320
|
+
}
|
|
4321
|
+
},
|
|
4322
|
+
className: controlClass({ size: "sm" }, "min-w-0 flex-1")
|
|
4323
|
+
}
|
|
4324
|
+
)
|
|
4325
|
+
] }),
|
|
4326
|
+
/* @__PURE__ */ jsx47(
|
|
4327
|
+
ApplyClear,
|
|
4328
|
+
{
|
|
4329
|
+
onClear: () => {
|
|
4330
|
+
setDraft("");
|
|
4331
|
+
onChange(null);
|
|
4332
|
+
onClose();
|
|
4333
|
+
},
|
|
4334
|
+
onApply: () => {
|
|
4335
|
+
onChange(draft ? { operator, value: draft } : null);
|
|
4336
|
+
onClose();
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
)
|
|
4340
|
+
] });
|
|
4341
|
+
}
|
|
4342
|
+
function ApplyClear({ onClear, onApply }) {
|
|
4343
|
+
return /* @__PURE__ */ jsxs36("div", { className: "flex items-center justify-end gap-2 border-t border-border/40 pt-1", children: [
|
|
4344
|
+
/* @__PURE__ */ jsx47(
|
|
4345
|
+
Button,
|
|
4346
|
+
{
|
|
4347
|
+
variant: "ghost",
|
|
4348
|
+
size: "sm",
|
|
4349
|
+
onClick: onClear,
|
|
4350
|
+
className: "h-8 px-3 text-muted-foreground hover:text-foreground",
|
|
4351
|
+
children: "Clear"
|
|
4352
|
+
}
|
|
4353
|
+
),
|
|
4354
|
+
/* @__PURE__ */ jsx47(Button, { color: "primary", size: "sm", onClick: onApply, className: "h-8 px-3", children: "Apply" })
|
|
4355
|
+
] });
|
|
4356
|
+
}
|
|
4357
|
+
|
|
3148
4358
|
// src/app/data/DataTable.tsx
|
|
3149
|
-
import { useEffect as
|
|
4359
|
+
import { useEffect as useEffect4, useMemo as useMemo3, useState as useState6 } from "react";
|
|
3150
4360
|
import {
|
|
3151
4361
|
ArrowDownIcon,
|
|
3152
4362
|
ArrowUpDownIcon,
|
|
3153
4363
|
ArrowUpIcon,
|
|
3154
4364
|
ChevronLeftIcon,
|
|
3155
|
-
ChevronRightIcon
|
|
4365
|
+
ChevronRightIcon as ChevronRightIcon2
|
|
3156
4366
|
} from "lucide-react";
|
|
3157
|
-
import { jsx as
|
|
3158
|
-
var shellClass2 = "
|
|
3159
|
-
var tableClass = "w-full border-
|
|
3160
|
-
var headRowClass = "
|
|
3161
|
-
var headCellClass = "border-b border-border
|
|
3162
|
-
var bodyCellClass = "border-b border-border/40 bg-transparent px-4 py-
|
|
3163
|
-
var rowClass = "bg-transparent transition-colors hover:bg-muted/
|
|
3164
|
-
var footCellClass = "border-t border-border/
|
|
4367
|
+
import { jsx as jsx48, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
4368
|
+
var shellClass2 = "w-full";
|
|
4369
|
+
var tableClass = "w-full border-separate border-spacing-0 bg-transparent text-sm";
|
|
4370
|
+
var headRowClass = "";
|
|
4371
|
+
var headCellClass = "bg-gradient-to-b from-white to-[#F9FAFB] dark:from-[#1F242F] dark:to-[#10121C] border-t border-b border-border px-4 py-2.5 text-left text-xs font-medium text-muted-foreground/90 first:rounded-l-lg first:border-l last:rounded-r-lg last:border-r shadow-skeuomorphic-bordered";
|
|
4372
|
+
var bodyCellClass = "border-b border-border/40 bg-transparent px-4 py-4 text-foreground";
|
|
4373
|
+
var rowClass = "bg-transparent transition-colors hover:bg-muted/20 data-[clickable=true]:cursor-pointer data-[selected=true]:bg-primary/[0.04]";
|
|
4374
|
+
var footCellClass = "border-t border-border/40 bg-transparent px-4 py-3 text-xs text-muted-foreground";
|
|
3165
4375
|
var footInnerClass = "flex flex-wrap items-center gap-2";
|
|
3166
4376
|
var emptyCellClass = "bg-transparent px-4 py-10 text-center text-sm text-muted-foreground";
|
|
3167
|
-
var sortButtonClass = "group inline-flex min-w-0 items-center gap-1.5 rounded-md -mx-1 px-1 py-0.5 text-left font-
|
|
3168
|
-
var stickyHeadClass = "sticky top-0 z-[1] bg-
|
|
3169
|
-
var selectCellClass = "w-10 px-4 py-
|
|
3170
|
-
var pagerButtonClass = "inline-flex size-7 items-center justify-center rounded-md border border-border bg-
|
|
4377
|
+
var sortButtonClass = "group inline-flex min-w-0 items-center gap-1.5 rounded-md -mx-1 px-1 py-0.5 text-left font-medium text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/10";
|
|
4378
|
+
var stickyHeadClass = "sticky top-0 z-[1] bg-background/95 shadow-[0_1px_0_0_hsl(var(--border)/0.4)] backdrop-blur-sm";
|
|
4379
|
+
var selectCellClass = "w-10 px-4 py-4 align-middle";
|
|
4380
|
+
var pagerButtonClass = "inline-flex size-7 items-center justify-center rounded-md border border-border bg-background text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:pointer-events-none disabled:opacity-40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/10";
|
|
3171
4381
|
var alignClass = {
|
|
3172
4382
|
left: "text-left",
|
|
3173
4383
|
center: "text-center",
|
|
@@ -3197,12 +4407,12 @@ function SortIndicator({
|
|
|
3197
4407
|
}) {
|
|
3198
4408
|
const iconClass = "size-3.5 shrink-0 opacity-60 group-hover:opacity-100";
|
|
3199
4409
|
if (!active) {
|
|
3200
|
-
return /* @__PURE__ */
|
|
4410
|
+
return /* @__PURE__ */ jsx48(ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
|
|
3201
4411
|
}
|
|
3202
4412
|
if (direction === "desc") {
|
|
3203
|
-
return /* @__PURE__ */
|
|
4413
|
+
return /* @__PURE__ */ jsx48(ArrowDownIcon, { className: iconClass, "aria-hidden": true });
|
|
3204
4414
|
}
|
|
3205
|
-
return /* @__PURE__ */
|
|
4415
|
+
return /* @__PURE__ */ jsx48(ArrowUpIcon, { className: iconClass, "aria-hidden": true });
|
|
3206
4416
|
}
|
|
3207
4417
|
function DataTable({
|
|
3208
4418
|
columns,
|
|
@@ -3234,7 +4444,7 @@ function DataTable({
|
|
|
3234
4444
|
defaultPageIndex = 0,
|
|
3235
4445
|
onPageChange
|
|
3236
4446
|
}) {
|
|
3237
|
-
const [uncontrolledSort, setUncontrolledSort] =
|
|
4447
|
+
const [uncontrolledSort, setUncontrolledSort] = useState6(
|
|
3238
4448
|
defaultSort
|
|
3239
4449
|
);
|
|
3240
4450
|
const isSortControlled = sortProp !== void 0;
|
|
@@ -3245,19 +4455,19 @@ function DataTable({
|
|
|
3245
4455
|
}
|
|
3246
4456
|
onSortChange?.(next);
|
|
3247
4457
|
};
|
|
3248
|
-
const [uncontrolledSelected, setUncontrolledSelected] =
|
|
4458
|
+
const [uncontrolledSelected, setUncontrolledSelected] = useState6(
|
|
3249
4459
|
defaultSelectedKeys ?? []
|
|
3250
4460
|
);
|
|
3251
4461
|
const isSelectionControlled = selectedKeysProp !== void 0;
|
|
3252
4462
|
const selectedKeys = isSelectionControlled ? selectedKeysProp : uncontrolledSelected;
|
|
3253
|
-
const selectedSet =
|
|
4463
|
+
const selectedSet = useMemo3(() => new Set(selectedKeys), [selectedKeys]);
|
|
3254
4464
|
const setSelected = (next) => {
|
|
3255
4465
|
if (!isSelectionControlled) {
|
|
3256
4466
|
setUncontrolledSelected(next);
|
|
3257
4467
|
}
|
|
3258
4468
|
onSelectionChange?.(next);
|
|
3259
4469
|
};
|
|
3260
|
-
const [uncontrolledPage, setUncontrolledPage] =
|
|
4470
|
+
const [uncontrolledPage, setUncontrolledPage] = useState6(defaultPageIndex);
|
|
3261
4471
|
const isPageControlled = pageIndexProp !== void 0;
|
|
3262
4472
|
const rawPageIndex = isPageControlled ? pageIndexProp : uncontrolledPage;
|
|
3263
4473
|
const setPage = (next) => {
|
|
@@ -3266,7 +4476,7 @@ function DataTable({
|
|
|
3266
4476
|
}
|
|
3267
4477
|
onPageChange?.(next);
|
|
3268
4478
|
};
|
|
3269
|
-
const sortedRows =
|
|
4479
|
+
const sortedRows = useMemo3(() => {
|
|
3270
4480
|
if (!sort) return rows;
|
|
3271
4481
|
const column = columns.find((col) => col.id === sort.columnId);
|
|
3272
4482
|
if (!column?.sortable) return rows;
|
|
@@ -3285,13 +4495,13 @@ function DataTable({
|
|
|
3285
4495
|
const paginated = typeof pageSize === "number" && pageSize > 0;
|
|
3286
4496
|
const pageCount = paginated ? Math.max(1, Math.ceil(sortedRows.length / pageSize)) : 1;
|
|
3287
4497
|
const pageIndex = Math.min(Math.max(0, rawPageIndex), pageCount - 1);
|
|
3288
|
-
|
|
4498
|
+
useEffect4(() => {
|
|
3289
4499
|
if (!paginated || isPageControlled) return;
|
|
3290
4500
|
if (uncontrolledPage > pageCount - 1) {
|
|
3291
4501
|
setUncontrolledPage(pageCount - 1);
|
|
3292
4502
|
}
|
|
3293
4503
|
}, [paginated, isPageControlled, uncontrolledPage, pageCount]);
|
|
3294
|
-
const visibleRows =
|
|
4504
|
+
const visibleRows = useMemo3(() => {
|
|
3295
4505
|
if (!paginated) return sortedRows;
|
|
3296
4506
|
const start = pageIndex * pageSize;
|
|
3297
4507
|
return sortedRows.slice(start, start + pageSize);
|
|
@@ -3300,7 +4510,7 @@ function DataTable({
|
|
|
3300
4510
|
const headPad = dense ? "px-3 py-2" : void 0;
|
|
3301
4511
|
const colSpan = columns.length + (selectable ? 1 : 0);
|
|
3302
4512
|
if (!loading && rows.length === 0 && emptyMode === "replace") {
|
|
3303
|
-
return /* @__PURE__ */
|
|
4513
|
+
return /* @__PURE__ */ jsx48(EmptyState, { title: emptyTitle, description: emptyDescription, className });
|
|
3304
4514
|
}
|
|
3305
4515
|
const allKeys = sortedRows.map(getRowKey);
|
|
3306
4516
|
const allSelected = allKeys.length > 0 && allKeys.every((k) => selectedSet.has(k));
|
|
@@ -3325,10 +4535,10 @@ function DataTable({
|
|
|
3325
4535
|
const hasPager = paginated && !loading && sortedRows.length > 0;
|
|
3326
4536
|
const hasFoot = (showRowCount || footer || hasPager) && (loading || sortedRows.length > 0);
|
|
3327
4537
|
const skeletonCount = loadingRows ?? pageSize ?? 5;
|
|
3328
|
-
return /* @__PURE__ */
|
|
3329
|
-
caption ? /* @__PURE__ */
|
|
3330
|
-
/* @__PURE__ */
|
|
3331
|
-
selectable ? /* @__PURE__ */
|
|
4538
|
+
return /* @__PURE__ */ jsx48("div", { className: cn("aui-app-data-table", shellClass2, className), children: /* @__PURE__ */ jsx48("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs37("table", { className: tableClass, children: [
|
|
4539
|
+
caption ? /* @__PURE__ */ jsx48("caption", { className: "sr-only", children: caption }) : null,
|
|
4540
|
+
/* @__PURE__ */ jsx48("thead", { className: cn(headRowClass, stickyHeader && stickyHeadClass), children: /* @__PURE__ */ jsxs37("tr", { children: [
|
|
4541
|
+
selectable ? /* @__PURE__ */ jsx48("th", { scope: "col", className: cn(selectCellClass, headPad), children: /* @__PURE__ */ jsx48(
|
|
3332
4542
|
Checkbox,
|
|
3333
4543
|
{
|
|
3334
4544
|
checked: headerCheckedState,
|
|
@@ -3340,19 +4550,19 @@ function DataTable({
|
|
|
3340
4550
|
columns.map((col) => {
|
|
3341
4551
|
const isSorted = sort?.columnId === col.id;
|
|
3342
4552
|
const ariaSort = col.sortable ? isSorted ? sort.direction === "asc" ? "ascending" : "descending" : "none" : void 0;
|
|
3343
|
-
const headerContent = col.sortable ? /* @__PURE__ */
|
|
4553
|
+
const headerContent = col.sortable ? /* @__PURE__ */ jsxs37(
|
|
3344
4554
|
"button",
|
|
3345
4555
|
{
|
|
3346
4556
|
type: "button",
|
|
3347
4557
|
className: sortButtonClass,
|
|
3348
4558
|
onClick: () => setSort(nextSort(sort, col.id)),
|
|
3349
4559
|
children: [
|
|
3350
|
-
/* @__PURE__ */
|
|
3351
|
-
/* @__PURE__ */
|
|
4560
|
+
/* @__PURE__ */ jsx48("span", { className: "truncate", children: col.header }),
|
|
4561
|
+
/* @__PURE__ */ jsx48(SortIndicator, { active: Boolean(isSorted), direction: sort?.direction })
|
|
3352
4562
|
]
|
|
3353
4563
|
}
|
|
3354
4564
|
) : col.header;
|
|
3355
|
-
return /* @__PURE__ */
|
|
4565
|
+
return /* @__PURE__ */ jsx48(
|
|
3356
4566
|
"th",
|
|
3357
4567
|
{
|
|
3358
4568
|
scope: "col",
|
|
@@ -3369,9 +4579,9 @@ function DataTable({
|
|
|
3369
4579
|
);
|
|
3370
4580
|
})
|
|
3371
4581
|
] }) }),
|
|
3372
|
-
/* @__PURE__ */
|
|
3373
|
-
selectable ? /* @__PURE__ */
|
|
3374
|
-
columns.map((col) => /* @__PURE__ */
|
|
4582
|
+
/* @__PURE__ */ jsx48("tbody", { className: cn(!hasFoot && "[&_tr:last-child_td]:border-b-0"), children: loading ? Array.from({ length: skeletonCount }).map((_, rowIdx) => /* @__PURE__ */ jsxs37("tr", { className: rowClass, "aria-hidden": true, children: [
|
|
4583
|
+
selectable ? /* @__PURE__ */ jsx48("td", { className: cn(selectCellClass, cellPad), children: /* @__PURE__ */ jsx48(Skeleton, { className: "size-4 rounded-[4px]" }) }) : null,
|
|
4584
|
+
columns.map((col) => /* @__PURE__ */ jsx48(
|
|
3375
4585
|
"td",
|
|
3376
4586
|
{
|
|
3377
4587
|
className: cn(
|
|
@@ -3380,17 +4590,17 @@ function DataTable({
|
|
|
3380
4590
|
col.align && alignClass[col.align],
|
|
3381
4591
|
col.className
|
|
3382
4592
|
),
|
|
3383
|
-
children: /* @__PURE__ */
|
|
4593
|
+
children: /* @__PURE__ */ jsx48(Skeleton, { className: "h-4 w-[60%]" })
|
|
3384
4594
|
},
|
|
3385
4595
|
col.id
|
|
3386
4596
|
))
|
|
3387
|
-
] }, `skeleton-${rowIdx}`)) : visibleRows.length === 0 ? /* @__PURE__ */
|
|
3388
|
-
/* @__PURE__ */
|
|
3389
|
-
emptyDescription ? /* @__PURE__ */
|
|
4597
|
+
] }, `skeleton-${rowIdx}`)) : visibleRows.length === 0 ? /* @__PURE__ */ jsx48("tr", { children: /* @__PURE__ */ jsx48("td", { colSpan, className: emptyCellClass, children: /* @__PURE__ */ jsxs37("div", { className: "flex flex-col items-center gap-1", children: [
|
|
4598
|
+
/* @__PURE__ */ jsx48("p", { className: "font-medium text-foreground", children: emptyTitle }),
|
|
4599
|
+
emptyDescription ? /* @__PURE__ */ jsx48("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
|
|
3390
4600
|
] }) }) }) : visibleRows.map((row) => {
|
|
3391
4601
|
const key = getRowKey(row);
|
|
3392
4602
|
const isSelected = selectedSet.has(key);
|
|
3393
|
-
return /* @__PURE__ */
|
|
4603
|
+
return /* @__PURE__ */ jsxs37(
|
|
3394
4604
|
"tr",
|
|
3395
4605
|
{
|
|
3396
4606
|
className: rowClass,
|
|
@@ -3406,12 +4616,12 @@ function DataTable({
|
|
|
3406
4616
|
tabIndex: onRowClick ? 0 : void 0,
|
|
3407
4617
|
role: onRowClick ? "button" : void 0,
|
|
3408
4618
|
children: [
|
|
3409
|
-
selectable ? /* @__PURE__ */
|
|
4619
|
+
selectable ? /* @__PURE__ */ jsx48(
|
|
3410
4620
|
"td",
|
|
3411
4621
|
{
|
|
3412
4622
|
className: cn(selectCellClass, cellPad),
|
|
3413
4623
|
onClick: (event) => event.stopPropagation(),
|
|
3414
|
-
children: /* @__PURE__ */
|
|
4624
|
+
children: /* @__PURE__ */ jsx48(
|
|
3415
4625
|
Checkbox,
|
|
3416
4626
|
{
|
|
3417
4627
|
checked: isSelected,
|
|
@@ -3421,7 +4631,7 @@ function DataTable({
|
|
|
3421
4631
|
)
|
|
3422
4632
|
}
|
|
3423
4633
|
) : null,
|
|
3424
|
-
columns.map((col) => /* @__PURE__ */
|
|
4634
|
+
columns.map((col) => /* @__PURE__ */ jsx48(
|
|
3425
4635
|
"td",
|
|
3426
4636
|
{
|
|
3427
4637
|
className: cn(
|
|
@@ -3431,7 +4641,7 @@ function DataTable({
|
|
|
3431
4641
|
col.align && alignClass[col.align],
|
|
3432
4642
|
col.className
|
|
3433
4643
|
),
|
|
3434
|
-
children: col.truncate ? /* @__PURE__ */
|
|
4644
|
+
children: col.truncate ? /* @__PURE__ */ jsx48("div", { className: "truncate", children: col.cell(row) }) : col.cell(row)
|
|
3435
4645
|
},
|
|
3436
4646
|
col.id
|
|
3437
4647
|
))
|
|
@@ -3440,7 +4650,7 @@ function DataTable({
|
|
|
3440
4650
|
key
|
|
3441
4651
|
);
|
|
3442
4652
|
}) }),
|
|
3443
|
-
hasFoot ? /* @__PURE__ */
|
|
4653
|
+
hasFoot ? /* @__PURE__ */ jsx48("tfoot", { children: /* @__PURE__ */ jsx48("tr", { children: /* @__PURE__ */ jsx48("td", { colSpan, className: footCellClass, children: /* @__PURE__ */ jsxs37(
|
|
3444
4654
|
"div",
|
|
3445
4655
|
{
|
|
3446
4656
|
className: cn(
|
|
@@ -3448,18 +4658,18 @@ function DataTable({
|
|
|
3448
4658
|
(showRowCount || footer || hasPager) && "justify-between"
|
|
3449
4659
|
),
|
|
3450
4660
|
children: [
|
|
3451
|
-
/* @__PURE__ */
|
|
3452
|
-
showRowCount ? /* @__PURE__ */
|
|
4661
|
+
/* @__PURE__ */ jsxs37("div", { className: footInnerClass, children: [
|
|
4662
|
+
showRowCount ? /* @__PURE__ */ jsxs37("span", { children: [
|
|
3453
4663
|
rowCountText,
|
|
3454
4664
|
selectable && selectedSet.size > 0 ? ` \xB7 ${selectedSet.size} selected` : null
|
|
3455
|
-
] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */
|
|
4665
|
+
] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */ jsxs37("span", { children: [
|
|
3456
4666
|
selectedSet.size,
|
|
3457
4667
|
" selected"
|
|
3458
4668
|
] }) : null,
|
|
3459
4669
|
footer
|
|
3460
4670
|
] }),
|
|
3461
|
-
hasPager ? /* @__PURE__ */
|
|
3462
|
-
/* @__PURE__ */
|
|
4671
|
+
hasPager ? /* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-2", children: [
|
|
4672
|
+
/* @__PURE__ */ jsxs37("span", { className: "tabular-nums", children: [
|
|
3463
4673
|
pageIndex * pageSize + 1,
|
|
3464
4674
|
"\u2013",
|
|
3465
4675
|
Math.min(
|
|
@@ -3470,8 +4680,8 @@ function DataTable({
|
|
|
3470
4680
|
"of ",
|
|
3471
4681
|
sortedRows.length
|
|
3472
4682
|
] }),
|
|
3473
|
-
/* @__PURE__ */
|
|
3474
|
-
/* @__PURE__ */
|
|
4683
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-1", children: [
|
|
4684
|
+
/* @__PURE__ */ jsx48(
|
|
3475
4685
|
"button",
|
|
3476
4686
|
{
|
|
3477
4687
|
type: "button",
|
|
@@ -3479,10 +4689,10 @@ function DataTable({
|
|
|
3479
4689
|
onClick: () => setPage(pageIndex - 1),
|
|
3480
4690
|
disabled: pageIndex <= 0,
|
|
3481
4691
|
"aria-label": "Previous page",
|
|
3482
|
-
children: /* @__PURE__ */
|
|
4692
|
+
children: /* @__PURE__ */ jsx48(ChevronLeftIcon, { className: "size-4", "aria-hidden": true })
|
|
3483
4693
|
}
|
|
3484
4694
|
),
|
|
3485
|
-
/* @__PURE__ */
|
|
4695
|
+
/* @__PURE__ */ jsx48(
|
|
3486
4696
|
"button",
|
|
3487
4697
|
{
|
|
3488
4698
|
type: "button",
|
|
@@ -3490,7 +4700,7 @@ function DataTable({
|
|
|
3490
4700
|
onClick: () => setPage(pageIndex + 1),
|
|
3491
4701
|
disabled: pageIndex >= pageCount - 1,
|
|
3492
4702
|
"aria-label": "Next page",
|
|
3493
|
-
children: /* @__PURE__ */
|
|
4703
|
+
children: /* @__PURE__ */ jsx48(ChevronRightIcon2, { className: "size-4", "aria-hidden": true })
|
|
3494
4704
|
}
|
|
3495
4705
|
)
|
|
3496
4706
|
] })
|
|
@@ -3502,8 +4712,8 @@ function DataTable({
|
|
|
3502
4712
|
}
|
|
3503
4713
|
|
|
3504
4714
|
// src/app/data/ChartPanel.tsx
|
|
3505
|
-
import { useId as
|
|
3506
|
-
import { jsx as
|
|
4715
|
+
import { useId as useId9 } from "react";
|
|
4716
|
+
import { jsx as jsx49, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
3507
4717
|
var ChartPanel = ({
|
|
3508
4718
|
title,
|
|
3509
4719
|
description,
|
|
@@ -3518,17 +4728,17 @@ var ChartPanel = ({
|
|
|
3518
4728
|
const height = heightProp ?? APP_DENSITY_CHART_HEIGHT[density];
|
|
3519
4729
|
const metricChartPlotRegionClass = useAppDensityClass("metricChartPlotRegion");
|
|
3520
4730
|
const chartPanelBodyClass = useAppDensityClass("chartPanelBody");
|
|
3521
|
-
const titleId =
|
|
4731
|
+
const titleId = useId9();
|
|
3522
4732
|
const resolvedTitle = title ?? artifact?.title;
|
|
3523
4733
|
const hasHeader = Boolean(resolvedTitle || description || actions);
|
|
3524
|
-
const body = loading ? /* @__PURE__ */
|
|
3525
|
-
return /* @__PURE__ */
|
|
4734
|
+
const body = loading ? /* @__PURE__ */ jsx49(Skeleton, { className: "w-full rounded-lg", style: { height }, "aria-hidden": true }) : children ?? (artifact ? /* @__PURE__ */ jsx49(ChartArtifactView, { artifact, embedded: true, height }) : null);
|
|
4735
|
+
return /* @__PURE__ */ jsxs38(
|
|
3526
4736
|
"section",
|
|
3527
4737
|
{
|
|
3528
4738
|
className: cn(metricCardShellClass, "aui-app-chart-panel", className),
|
|
3529
4739
|
"aria-labelledby": resolvedTitle ? titleId : void 0,
|
|
3530
4740
|
children: [
|
|
3531
|
-
/* @__PURE__ */
|
|
4741
|
+
/* @__PURE__ */ jsx49(
|
|
3532
4742
|
MetricCardHeader,
|
|
3533
4743
|
{
|
|
3534
4744
|
title: resolvedTitle,
|
|
@@ -3537,14 +4747,14 @@ var ChartPanel = ({
|
|
|
3537
4747
|
actions
|
|
3538
4748
|
}
|
|
3539
4749
|
),
|
|
3540
|
-
/* @__PURE__ */
|
|
4750
|
+
/* @__PURE__ */ jsx49(
|
|
3541
4751
|
"div",
|
|
3542
4752
|
{
|
|
3543
4753
|
className: cn(
|
|
3544
4754
|
"relative min-h-0 w-full",
|
|
3545
4755
|
hasHeader ? metricChartPlotRegionClass : chartPanelBodyClass
|
|
3546
4756
|
),
|
|
3547
|
-
children: body ?? /* @__PURE__ */
|
|
4757
|
+
children: body ?? /* @__PURE__ */ jsx49(
|
|
3548
4758
|
"div",
|
|
3549
4759
|
{
|
|
3550
4760
|
className: "flex items-center justify-center text-sm font-normal text-muted-foreground",
|
|
@@ -3561,10 +4771,11 @@ var ChartPanel = ({
|
|
|
3561
4771
|
};
|
|
3562
4772
|
|
|
3563
4773
|
// src/app/data/MetricRow.tsx
|
|
3564
|
-
import { useId as
|
|
3565
|
-
import { jsx as
|
|
4774
|
+
import { useId as useId10, useState as useState7 } from "react";
|
|
4775
|
+
import { jsx as jsx50, jsxs as jsxs39 } from "react/jsx-runtime";
|
|
3566
4776
|
var MetricRow = ({
|
|
3567
4777
|
title,
|
|
4778
|
+
titleTag,
|
|
3568
4779
|
description,
|
|
3569
4780
|
actions,
|
|
3570
4781
|
metrics,
|
|
@@ -3576,9 +4787,9 @@ var MetricRow = ({
|
|
|
3576
4787
|
className
|
|
3577
4788
|
}) => {
|
|
3578
4789
|
const metricTileClass = useAppDensityClass("metricTile");
|
|
3579
|
-
const titleId =
|
|
4790
|
+
const titleId = useId10();
|
|
3580
4791
|
const selectable = onMetricChange != null || activeMetricId != null;
|
|
3581
|
-
const [internalId, setInternalId] =
|
|
4792
|
+
const [internalId, setInternalId] = useState7(
|
|
3582
4793
|
defaultActiveMetricId ?? metrics[0]?.id
|
|
3583
4794
|
);
|
|
3584
4795
|
const activeId = activeMetricId ?? internalId;
|
|
@@ -3586,22 +4797,24 @@ var MetricRow = ({
|
|
|
3586
4797
|
if (activeMetricId == null) setInternalId(id);
|
|
3587
4798
|
onMetricChange?.(id);
|
|
3588
4799
|
};
|
|
3589
|
-
|
|
4800
|
+
const hasHeader = Boolean(title || titleTag || description || actions);
|
|
4801
|
+
return /* @__PURE__ */ jsxs39(
|
|
3590
4802
|
"section",
|
|
3591
4803
|
{
|
|
3592
4804
|
className: cn(metricCardShellClass, className),
|
|
3593
4805
|
"aria-labelledby": title ? titleId : void 0,
|
|
3594
4806
|
children: [
|
|
3595
|
-
/* @__PURE__ */
|
|
4807
|
+
/* @__PURE__ */ jsx50(
|
|
3596
4808
|
MetricCardHeader,
|
|
3597
4809
|
{
|
|
3598
4810
|
title,
|
|
4811
|
+
titleTag,
|
|
3599
4812
|
titleId,
|
|
3600
4813
|
description,
|
|
3601
4814
|
actions
|
|
3602
4815
|
}
|
|
3603
4816
|
),
|
|
3604
|
-
/* @__PURE__ */
|
|
4817
|
+
/* @__PURE__ */ jsx50(
|
|
3605
4818
|
"div",
|
|
3606
4819
|
{
|
|
3607
4820
|
role: selectable ? "group" : void 0,
|
|
@@ -3610,20 +4823,20 @@ var MetricRow = ({
|
|
|
3610
4823
|
className: cn(
|
|
3611
4824
|
metricTilesRowClass,
|
|
3612
4825
|
metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
|
|
3613
|
-
|
|
4826
|
+
hasHeader && "mt-3.5 border-t border-border/40"
|
|
3614
4827
|
),
|
|
3615
|
-
children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */
|
|
4828
|
+
children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ jsxs39(
|
|
3616
4829
|
"div",
|
|
3617
4830
|
{
|
|
3618
4831
|
className: cn("flex min-w-0 flex-1 flex-col gap-2", metricTileClass),
|
|
3619
4832
|
"aria-hidden": true,
|
|
3620
4833
|
children: [
|
|
3621
|
-
/* @__PURE__ */
|
|
3622
|
-
/* @__PURE__ */
|
|
4834
|
+
/* @__PURE__ */ jsx50(Skeleton, { className: "h-3 w-20" }),
|
|
4835
|
+
/* @__PURE__ */ jsx50(Skeleton, { className: "h-7 w-24" })
|
|
3623
4836
|
]
|
|
3624
4837
|
},
|
|
3625
4838
|
`skeleton-${index}`
|
|
3626
|
-
)) : metrics.map((m, index) => /* @__PURE__ */
|
|
4839
|
+
)) : metrics.map((m, index) => /* @__PURE__ */ jsx50(
|
|
3627
4840
|
MetricTile,
|
|
3628
4841
|
{
|
|
3629
4842
|
label: m.label,
|
|
@@ -3631,6 +4844,11 @@ var MetricRow = ({
|
|
|
3631
4844
|
unit: m.unit,
|
|
3632
4845
|
trend: m.trend,
|
|
3633
4846
|
trendTone: m.trendTone,
|
|
4847
|
+
trendVariant: m.trendVariant,
|
|
4848
|
+
activeTone: m.activeTone,
|
|
4849
|
+
sparklineData: m.sparklineData,
|
|
4850
|
+
sparklineConfig: m.sparklineConfig,
|
|
4851
|
+
sparkline: m.sparkline,
|
|
3634
4852
|
active: selectable && m.id === activeId,
|
|
3635
4853
|
showDivider: index < metrics.length - 1,
|
|
3636
4854
|
onSelect: selectable ? () => select(m.id) : void 0
|
|
@@ -3645,10 +4863,11 @@ var MetricRow = ({
|
|
|
3645
4863
|
};
|
|
3646
4864
|
|
|
3647
4865
|
// src/app/data/MetricChartCard.tsx
|
|
3648
|
-
import { useId as
|
|
3649
|
-
import { jsx as
|
|
4866
|
+
import { useId as useId11, useState as useState8 } from "react";
|
|
4867
|
+
import { jsx as jsx51, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
3650
4868
|
var MetricChartCard = ({
|
|
3651
4869
|
title,
|
|
4870
|
+
titleTag,
|
|
3652
4871
|
description,
|
|
3653
4872
|
actions,
|
|
3654
4873
|
metrics,
|
|
@@ -3669,8 +4888,8 @@ var MetricChartCard = ({
|
|
|
3669
4888
|
const height = heightProp ?? APP_DENSITY_CHART_HEIGHT[density];
|
|
3670
4889
|
const metricChartRegionClass = useAppDensityClass("metricChartRegion");
|
|
3671
4890
|
const metricTileClass = useAppDensityClass("metricTile");
|
|
3672
|
-
const titleId =
|
|
3673
|
-
const [internalId, setInternalId] =
|
|
4891
|
+
const titleId = useId11();
|
|
4892
|
+
const [internalId, setInternalId] = useState8(
|
|
3674
4893
|
defaultActiveMetricId ?? metrics[0]?.id
|
|
3675
4894
|
);
|
|
3676
4895
|
const activeId = activeMetricId ?? internalId;
|
|
@@ -3679,24 +4898,25 @@ var MetricChartCard = ({
|
|
|
3679
4898
|
if (activeMetricId == null) setInternalId(id);
|
|
3680
4899
|
onMetricChange?.(id);
|
|
3681
4900
|
};
|
|
3682
|
-
const hasHeader = Boolean(title || description || actions);
|
|
4901
|
+
const hasHeader = Boolean(title || titleTag || description || actions);
|
|
3683
4902
|
const chartAriaLabel = typeof active?.label === "string" ? `${active.label} over time` : "Metric chart";
|
|
3684
|
-
return /* @__PURE__ */
|
|
4903
|
+
return /* @__PURE__ */ jsxs40(
|
|
3685
4904
|
"section",
|
|
3686
4905
|
{
|
|
3687
4906
|
className: cn(metricCardShellClass, className),
|
|
3688
4907
|
"aria-labelledby": title ? titleId : void 0,
|
|
3689
4908
|
children: [
|
|
3690
|
-
/* @__PURE__ */
|
|
4909
|
+
/* @__PURE__ */ jsx51(
|
|
3691
4910
|
MetricCardHeader,
|
|
3692
4911
|
{
|
|
3693
4912
|
title,
|
|
3694
|
-
|
|
4913
|
+
titleTag,
|
|
3695
4914
|
description,
|
|
3696
|
-
actions
|
|
4915
|
+
actions,
|
|
4916
|
+
titleId
|
|
3697
4917
|
}
|
|
3698
4918
|
),
|
|
3699
|
-
/* @__PURE__ */
|
|
4919
|
+
/* @__PURE__ */ jsx51(
|
|
3700
4920
|
"div",
|
|
3701
4921
|
{
|
|
3702
4922
|
role: "group",
|
|
@@ -3705,20 +4925,20 @@ var MetricChartCard = ({
|
|
|
3705
4925
|
className: cn(
|
|
3706
4926
|
metricTilesRowClass,
|
|
3707
4927
|
metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
|
|
3708
|
-
hasHeader && "mt-3"
|
|
4928
|
+
hasHeader && "mt-3.5 border-t border-border/40"
|
|
3709
4929
|
),
|
|
3710
|
-
children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */
|
|
4930
|
+
children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ jsxs40(
|
|
3711
4931
|
"div",
|
|
3712
4932
|
{
|
|
3713
4933
|
className: cn("flex min-w-0 flex-1 flex-col gap-2", metricTileClass),
|
|
3714
4934
|
"aria-hidden": true,
|
|
3715
4935
|
children: [
|
|
3716
|
-
/* @__PURE__ */
|
|
3717
|
-
/* @__PURE__ */
|
|
4936
|
+
/* @__PURE__ */ jsx51(Skeleton, { className: "h-3 w-20" }),
|
|
4937
|
+
/* @__PURE__ */ jsx51(Skeleton, { className: "h-7 w-24" })
|
|
3718
4938
|
]
|
|
3719
4939
|
},
|
|
3720
4940
|
`skeleton-${index}`
|
|
3721
|
-
)) : metrics.map((m, index) => /* @__PURE__ */
|
|
4941
|
+
)) : metrics.map((m, index) => /* @__PURE__ */ jsx51(
|
|
3722
4942
|
MetricTile,
|
|
3723
4943
|
{
|
|
3724
4944
|
label: m.label,
|
|
@@ -3726,6 +4946,11 @@ var MetricChartCard = ({
|
|
|
3726
4946
|
unit: m.unit,
|
|
3727
4947
|
trend: m.trend,
|
|
3728
4948
|
trendTone: m.trendTone,
|
|
4949
|
+
trendVariant: m.trendVariant,
|
|
4950
|
+
activeTone: m.activeTone,
|
|
4951
|
+
sparklineData: m.sparklineData,
|
|
4952
|
+
sparklineConfig: m.sparklineConfig,
|
|
4953
|
+
sparkline: m.sparkline,
|
|
3729
4954
|
active: m.id === active?.id,
|
|
3730
4955
|
showDivider: index < metrics.length - 1,
|
|
3731
4956
|
onSelect: () => select(m.id)
|
|
@@ -3734,14 +4959,14 @@ var MetricChartCard = ({
|
|
|
3734
4959
|
))
|
|
3735
4960
|
}
|
|
3736
4961
|
),
|
|
3737
|
-
/* @__PURE__ */
|
|
4962
|
+
/* @__PURE__ */ jsx51("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: loading ? /* @__PURE__ */ jsx51(
|
|
3738
4963
|
Skeleton,
|
|
3739
4964
|
{
|
|
3740
4965
|
className: "w-full rounded-lg",
|
|
3741
4966
|
style: { height },
|
|
3742
4967
|
"aria-hidden": true
|
|
3743
4968
|
}
|
|
3744
|
-
) : active?.data && active.data.length > 0 ? /* @__PURE__ */
|
|
4969
|
+
) : active?.data && active.data.length > 0 ? /* @__PURE__ */ jsx51(
|
|
3745
4970
|
LineAreaChart,
|
|
3746
4971
|
{
|
|
3747
4972
|
data: active.data,
|
|
@@ -3761,7 +4986,7 @@ var MetricChartCard = ({
|
|
|
3761
4986
|
ariaLabel: chartAriaLabel
|
|
3762
4987
|
},
|
|
3763
4988
|
active.id
|
|
3764
|
-
) : /* @__PURE__ */
|
|
4989
|
+
) : /* @__PURE__ */ jsx51(
|
|
3765
4990
|
"div",
|
|
3766
4991
|
{
|
|
3767
4992
|
className: "flex w-full items-center justify-center text-sm font-normal text-muted-foreground",
|
|
@@ -3776,13 +5001,13 @@ var MetricChartCard = ({
|
|
|
3776
5001
|
};
|
|
3777
5002
|
|
|
3778
5003
|
// src/hooks/use-live-query.ts
|
|
3779
|
-
import { useCallback as useCallback2, useEffect as
|
|
5004
|
+
import { useCallback as useCallback2, useEffect as useEffect5, useRef as useRef2, useState as useState9 } from "react";
|
|
3780
5005
|
function useInterval(callback, delayMs) {
|
|
3781
|
-
const saved =
|
|
3782
|
-
|
|
5006
|
+
const saved = useRef2(callback);
|
|
5007
|
+
useEffect5(() => {
|
|
3783
5008
|
saved.current = callback;
|
|
3784
5009
|
}, [callback]);
|
|
3785
|
-
|
|
5010
|
+
useEffect5(() => {
|
|
3786
5011
|
if (delayMs === null) return;
|
|
3787
5012
|
const id = setInterval(() => saved.current(), delayMs);
|
|
3788
5013
|
return () => clearInterval(id);
|
|
@@ -3795,19 +5020,19 @@ function useLiveQuery(fetcher, options = {}) {
|
|
|
3795
5020
|
immediate = true,
|
|
3796
5021
|
refetchOnFocus = true
|
|
3797
5022
|
} = options;
|
|
3798
|
-
const [data, setData] =
|
|
3799
|
-
const [error, setError] =
|
|
3800
|
-
const [loading, setLoading] =
|
|
3801
|
-
const [refreshing, setRefreshing] =
|
|
3802
|
-
const [lastUpdated, setLastUpdated] =
|
|
3803
|
-
const fetcherRef =
|
|
3804
|
-
|
|
5023
|
+
const [data, setData] = useState9(void 0);
|
|
5024
|
+
const [error, setError] = useState9(void 0);
|
|
5025
|
+
const [loading, setLoading] = useState9(enabled);
|
|
5026
|
+
const [refreshing, setRefreshing] = useState9(false);
|
|
5027
|
+
const [lastUpdated, setLastUpdated] = useState9(null);
|
|
5028
|
+
const fetcherRef = useRef2(fetcher);
|
|
5029
|
+
useEffect5(() => {
|
|
3805
5030
|
fetcherRef.current = fetcher;
|
|
3806
5031
|
}, [fetcher]);
|
|
3807
|
-
const mounted =
|
|
3808
|
-
const requestId =
|
|
3809
|
-
const hasData =
|
|
3810
|
-
|
|
5032
|
+
const mounted = useRef2(true);
|
|
5033
|
+
const requestId = useRef2(0);
|
|
5034
|
+
const hasData = useRef2(false);
|
|
5035
|
+
useEffect5(() => {
|
|
3811
5036
|
mounted.current = true;
|
|
3812
5037
|
return () => {
|
|
3813
5038
|
mounted.current = false;
|
|
@@ -3839,11 +5064,11 @@ function useLiveQuery(fetcher, options = {}) {
|
|
|
3839
5064
|
if (!enabled) return;
|
|
3840
5065
|
run();
|
|
3841
5066
|
}, [enabled, run]);
|
|
3842
|
-
|
|
5067
|
+
useEffect5(() => {
|
|
3843
5068
|
if (!enabled) return;
|
|
3844
5069
|
if (immediate) run();
|
|
3845
5070
|
}, [enabled, immediate, run]);
|
|
3846
|
-
|
|
5071
|
+
useEffect5(() => {
|
|
3847
5072
|
if (!enabled || intervalMs === null) return;
|
|
3848
5073
|
const tick = () => {
|
|
3849
5074
|
if (refetchOnFocus && typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
@@ -3854,7 +5079,7 @@ function useLiveQuery(fetcher, options = {}) {
|
|
|
3854
5079
|
const handle = setInterval(tick, intervalMs);
|
|
3855
5080
|
return () => clearInterval(handle);
|
|
3856
5081
|
}, [enabled, intervalMs, refetchOnFocus, run]);
|
|
3857
|
-
|
|
5082
|
+
useEffect5(() => {
|
|
3858
5083
|
if (!enabled || !refetchOnFocus || typeof document === "undefined") return;
|
|
3859
5084
|
const onVisible = () => {
|
|
3860
5085
|
if (document.visibilityState === "visible") run();
|
|
@@ -3865,69 +5090,6 @@ function useLiveQuery(fetcher, options = {}) {
|
|
|
3865
5090
|
return { data, error, loading, refreshing, lastUpdated, refetch };
|
|
3866
5091
|
}
|
|
3867
5092
|
|
|
3868
|
-
// src/charts/sparkline.tsx
|
|
3869
|
-
import { useId as useId11 } from "react";
|
|
3870
|
-
import { Fragment as Fragment6, jsx as jsx48, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
3871
|
-
var Sparkline = ({
|
|
3872
|
-
data,
|
|
3873
|
-
dataKey = "value",
|
|
3874
|
-
color = "var(--primary, #6366f1)",
|
|
3875
|
-
area = true,
|
|
3876
|
-
width = 96,
|
|
3877
|
-
height = 28,
|
|
3878
|
-
strokeWidth = 1.5,
|
|
3879
|
-
className,
|
|
3880
|
-
ariaLabel = "Trend"
|
|
3881
|
-
}) => {
|
|
3882
|
-
const uid = useId11();
|
|
3883
|
-
const values = data.map((d) => typeof d === "number" ? d : toNum(d[dataKey]));
|
|
3884
|
-
if (values.length === 0) {
|
|
3885
|
-
return /* @__PURE__ */ jsx48("span", { className: cn("inline-block", className), style: { width, height } });
|
|
3886
|
-
}
|
|
3887
|
-
const pad = strokeWidth + 1;
|
|
3888
|
-
const min = Math.min(...values);
|
|
3889
|
-
const max = Math.max(...values);
|
|
3890
|
-
const range = max - min || 1;
|
|
3891
|
-
const innerW = width - pad * 2;
|
|
3892
|
-
const innerH = height - pad * 2;
|
|
3893
|
-
const points = values.map((v, i) => ({
|
|
3894
|
-
x: pad + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
|
|
3895
|
-
y: pad + innerH - (v - min) / range * innerH
|
|
3896
|
-
}));
|
|
3897
|
-
return /* @__PURE__ */ jsxs38(
|
|
3898
|
-
"svg",
|
|
3899
|
-
{
|
|
3900
|
-
width,
|
|
3901
|
-
height,
|
|
3902
|
-
viewBox: `0 0 ${width} ${height}`,
|
|
3903
|
-
className: cn("block", className),
|
|
3904
|
-
role: "img",
|
|
3905
|
-
"aria-label": ariaLabel,
|
|
3906
|
-
preserveAspectRatio: "none",
|
|
3907
|
-
children: [
|
|
3908
|
-
area && /* @__PURE__ */ jsxs38(Fragment6, { children: [
|
|
3909
|
-
/* @__PURE__ */ jsx48("defs", { children: /* @__PURE__ */ jsxs38("linearGradient", { id: `${uid}-spark`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
|
|
3910
|
-
/* @__PURE__ */ jsx48("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
|
|
3911
|
-
/* @__PURE__ */ jsx48("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
|
|
3912
|
-
] }) }),
|
|
3913
|
-
/* @__PURE__ */ jsx48("path", { d: monotoneAreaPath(points, height - pad), fill: `url(#${uid}-spark)` })
|
|
3914
|
-
] }),
|
|
3915
|
-
/* @__PURE__ */ jsx48(
|
|
3916
|
-
"path",
|
|
3917
|
-
{
|
|
3918
|
-
d: monotoneLinePath(points),
|
|
3919
|
-
fill: "none",
|
|
3920
|
-
stroke: color,
|
|
3921
|
-
strokeWidth,
|
|
3922
|
-
strokeLinecap: "round",
|
|
3923
|
-
strokeLinejoin: "round"
|
|
3924
|
-
}
|
|
3925
|
-
)
|
|
3926
|
-
]
|
|
3927
|
-
}
|
|
3928
|
-
);
|
|
3929
|
-
};
|
|
3930
|
-
|
|
3931
5093
|
export {
|
|
3932
5094
|
SEMANTIC_COLOR_TOKENS,
|
|
3933
5095
|
RESERVED_GRADIENT_TOKENS,
|
|
@@ -3964,16 +5126,17 @@ export {
|
|
|
3964
5126
|
AppDensityProvider,
|
|
3965
5127
|
useAppDensity,
|
|
3966
5128
|
useAppDensityClass,
|
|
5129
|
+
Sparkline,
|
|
3967
5130
|
MetricTile,
|
|
3968
5131
|
useAppShellChat,
|
|
3969
5132
|
useAppShellNav,
|
|
3970
5133
|
AppShell,
|
|
3971
|
-
AppShellTopbar,
|
|
3972
5134
|
AppShellChatTrigger,
|
|
3973
5135
|
AppShellSidebarTrigger,
|
|
3974
5136
|
PageHeader,
|
|
3975
5137
|
Page,
|
|
3976
5138
|
Section,
|
|
5139
|
+
Stack,
|
|
3977
5140
|
AppCopilotProvider,
|
|
3978
5141
|
useAppCopilotContext,
|
|
3979
5142
|
AppChatPanel,
|
|
@@ -3987,6 +5150,8 @@ export {
|
|
|
3987
5150
|
DescriptionList,
|
|
3988
5151
|
ExpandableSection,
|
|
3989
5152
|
ResourceCard,
|
|
5153
|
+
AlertCard,
|
|
5154
|
+
CatalogCard,
|
|
3990
5155
|
SettingsSectionHeader,
|
|
3991
5156
|
SettingsSection,
|
|
3992
5157
|
FieldRow,
|
|
@@ -4011,11 +5176,11 @@ export {
|
|
|
4011
5176
|
FormSection,
|
|
4012
5177
|
FilterBar,
|
|
4013
5178
|
FilterField,
|
|
5179
|
+
FilterDropdown,
|
|
4014
5180
|
DataTable,
|
|
4015
5181
|
ChartPanel,
|
|
4016
5182
|
MetricRow,
|
|
4017
5183
|
MetricChartCard,
|
|
4018
5184
|
useInterval,
|
|
4019
|
-
useLiveQuery
|
|
4020
|
-
Sparkline
|
|
5185
|
+
useLiveQuery
|
|
4021
5186
|
};
|