@timbal-ai/timbal-react 1.3.0 → 1.5.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 +32 -0
- package/README.md +28 -4
- package/dist/app.cjs +3610 -1489
- package/dist/app.d.cts +75 -30
- package/dist/app.d.ts +75 -30
- package/dist/app.esm.js +29 -7
- package/dist/{chart-artifact-Q5QgMtbj.d.ts → chart-artifact-2OTDTRwM.d.ts} +212 -41
- package/dist/{chart-artifact-WDEW9dHT.d.cts → chart-artifact-CS3qyGIY.d.cts} +212 -41
- package/dist/chat.cjs +264 -107
- package/dist/chat.d.cts +2 -2
- package/dist/chat.d.ts +2 -2
- package/dist/chat.esm.js +4 -3
- package/dist/chunk-5ECRZ5O7.esm.js +899 -0
- package/dist/{chunk-QU7ET55D.esm.js → chunk-AZL2WANO.esm.js} +320 -177
- package/dist/{chunk-OH23AX2V.esm.js → chunk-B4XAC4G7.esm.js} +430 -780
- package/dist/chunk-EDEKQYSU.esm.js +10 -0
- package/dist/{chunk-GQBYZRD7.esm.js → chunk-IGHBLJV3.esm.js} +38 -27
- package/dist/{chunk-OFWC4MIY.esm.js → chunk-JYDJRGDE.esm.js} +5 -3
- package/dist/{chunk-YCXN67SD.esm.js → chunk-SZDYIRMB.esm.js} +1639 -490
- package/dist/chunk-TZI3ID3C.esm.js +232 -0
- package/dist/{chunk-THBA27QY.esm.js → chunk-WMKPT5BV.esm.js} +242 -123
- package/dist/{chunk-VXMM2HX7.esm.js → chunk-ZNYAETFD.esm.js} +1 -1
- package/dist/{circular-progress-Ci8L-Hfa.d.cts → circular-progress-CDsJwIPF.d.cts} +19 -77
- package/dist/{circular-progress-Ci8L-Hfa.d.ts → circular-progress-CDsJwIPF.d.ts} +19 -77
- package/dist/index.cjs +5564 -3612
- package/dist/index.d.cts +7 -6
- package/dist/index.d.ts +7 -6
- package/dist/index.esm.js +45 -33
- package/dist/kanban-U5xNe9py.d.cts +212 -0
- package/dist/kanban-U5xNe9py.d.ts +212 -0
- package/dist/{layout-BTJyU8wd.d.ts → layout-B8r6Jbat.d.ts} +1 -1
- package/dist/{layout-C2G-FcER.d.cts → layout-Cu7Ijn04.d.cts} +1 -1
- package/dist/site.cjs +358 -0
- package/dist/site.d.cts +184 -0
- package/dist/site.d.ts +184 -0
- package/dist/site.esm.js +322 -0
- package/dist/studio.cjs +702 -343
- package/dist/studio.d.cts +1 -1
- package/dist/studio.d.ts +1 -1
- package/dist/studio.esm.js +7 -5
- package/dist/styles.css +56 -0
- package/dist/{timbal-v2-button-CNfdwGq4.d.cts → timbal-v2-button-B7vPs7gg.d.cts} +2 -2
- package/dist/{timbal-v2-button-CNfdwGq4.d.ts → timbal-v2-button-B7vPs7gg.d.ts} +2 -2
- package/dist/ui.cjs +1504 -659
- package/dist/ui.d.cts +11 -4
- package/dist/ui.d.ts +11 -4
- package/dist/ui.esm.js +35 -26
- package/dist/{welcome-DXqsGTwH.d.ts → welcome-DduQAC4K.d.ts} +4 -0
- package/dist/{welcome-BFGRoNfK.d.cts → welcome-NXZlcihe.d.cts} +4 -0
- 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
|
@@ -15,27 +15,36 @@ import {
|
|
|
15
15
|
studioIntegrationCardClass,
|
|
16
16
|
studioTopbarPillHeightClass,
|
|
17
17
|
toNum
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-IGHBLJV3.esm.js";
|
|
19
19
|
import {
|
|
20
20
|
Checkbox,
|
|
21
|
+
CopyButton,
|
|
22
|
+
Popover,
|
|
23
|
+
PopoverContent,
|
|
24
|
+
PopoverTrigger,
|
|
21
25
|
Skeleton
|
|
22
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-5ECRZ5O7.esm.js";
|
|
23
27
|
import {
|
|
24
28
|
PillSegmentedTabs
|
|
25
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-ZNYAETFD.esm.js";
|
|
26
30
|
import {
|
|
31
|
+
Avatar,
|
|
32
|
+
AvatarFallback,
|
|
27
33
|
Button,
|
|
28
34
|
Dialog,
|
|
29
35
|
DialogContent,
|
|
30
36
|
DialogTitle,
|
|
37
|
+
TIMBAL_V2_ELEVATED_GRADIENT,
|
|
31
38
|
TIMBAL_V2_ELEVATED_SURFACE,
|
|
32
39
|
TIMBAL_V2_LOGO_TILE,
|
|
33
40
|
TIMBAL_V2_SWITCH_THUMB,
|
|
34
41
|
TIMBAL_V2_SWITCH_TRACK_OFF,
|
|
35
42
|
TimbalV2Button,
|
|
36
|
-
cn,
|
|
37
43
|
controlClass
|
|
38
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-AZL2WANO.esm.js";
|
|
45
|
+
import {
|
|
46
|
+
cn
|
|
47
|
+
} from "./chunk-EDEKQYSU.esm.js";
|
|
39
48
|
|
|
40
49
|
// src/design/ui-vocabulary.ts
|
|
41
50
|
var SEMANTIC_COLOR_TOKENS = [
|
|
@@ -191,6 +200,13 @@ var HOUSE_RULES = [
|
|
|
191
200
|
rule: "Don't nest a bordered card inside another bordered card. Group with spacing or a Section instead.",
|
|
192
201
|
why: "Card-in-card doubles borders and shadows for no information gain."
|
|
193
202
|
},
|
|
203
|
+
{
|
|
204
|
+
id: "no-table-in-card",
|
|
205
|
+
rule: "Never wrap a DataTable or table inside a Card, SurfaceCard, or ArtifactCard.",
|
|
206
|
+
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.",
|
|
207
|
+
slop: "<Card><DataTable columns={columns} rows={rows} getRowKey={getRowKey} /></Card>",
|
|
208
|
+
good: "<DataTable columns={columns} rows={rows} getRowKey={getRowKey} />"
|
|
209
|
+
},
|
|
194
210
|
{
|
|
195
211
|
id: "no-row-dividers",
|
|
196
212
|
rule: "Don't put a divider between every list row. Use spacing or zebra striping.",
|
|
@@ -212,6 +228,27 @@ var HOUSE_RULES = [
|
|
|
212
228
|
why: "Hand-rolled controls drift from the shared control-surface skin and look foreign next to kit controls.",
|
|
213
229
|
slop: `<button className="rounded-lg border border-input bg-transparent px-3 h-9">`,
|
|
214
230
|
good: `<SelectTrigger><SelectValue /></SelectTrigger>`
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
id: "no-title-repetition",
|
|
234
|
+
rule: "Never repeat the page title/description inside a Section, Card, or Table header.",
|
|
235
|
+
why: "If the Page already has a title, repeating it in the first child section or table is redundant and wastes vertical space.",
|
|
236
|
+
slop: `<Page title="Orders"><Section title="Orders"><DataTable ... /></Section></Page>`,
|
|
237
|
+
good: `<Page title="Orders"><DataTable ... /></Page>`
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
id: "no-chat-wrapping",
|
|
241
|
+
rule: "Never wrap TimbalChat or AppChatPanel in a Card, Section, or custom bordered/padded container, and never add custom heading/status blocks above it.",
|
|
242
|
+
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.",
|
|
243
|
+
slop: `<Page title="Assistant"><Card><div className="flex justify-between"><h3>TIBA Concierge</h3><span>Online</span></div><TimbalChat /></Card></Page>`,
|
|
244
|
+
good: `<Page fill><TimbalChat workforceId="..." welcome={{ heading: "Hola, soy el Concierge de TIBA", subheading: "Preg\xFAntame sobre..." }} /></Page>`
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
id: "no-colored-hover",
|
|
248
|
+
rule: "Interactive cards and list items must use neutral hover states \u2014 never hard-code colored backgrounds or borders on hover.",
|
|
249
|
+
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.",
|
|
250
|
+
slop: `<Card className="hover:bg-emerald-500/10 hover:border-emerald-500/30">`,
|
|
251
|
+
good: `<AlertCard onClick={handleClick}>`
|
|
215
252
|
}
|
|
216
253
|
];
|
|
217
254
|
|
|
@@ -239,6 +276,45 @@ You are **not** required to copy any example layout, page title, section order,
|
|
|
239
276
|
|
|
240
277
|
When in doubt: compose from the **component menu** + **guidelines**, then adapt creatively to the request.
|
|
241
278
|
|
|
279
|
+
### Layout archetypes \u2014 pick the shape that fits (don't default to one)
|
|
280
|
+
|
|
281
|
+
The most common failure is shipping the **same** layout every time: sidebar + topbar + \`Page\` + one \`MetricRow\` + one full-width \`DataTable\`. That is *one* archetype, not *the* layout. Choose deliberately \u2014 different domains want different shapes, and varying the shell/page composition is encouraged.
|
|
282
|
+
|
|
283
|
+
| Archetype | When | Compose |
|
|
284
|
+
|-----------|------|---------|
|
|
285
|
+
| **Sidebar dashboard** | Multi-section product (CRM, billing, ops) with nav | \`StudioSidebar\` in \`AppShell.sidebar\` + \`Page\` \u2192 \`Section\` |
|
|
286
|
+
| **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 |
|
|
287
|
+
| **Bento overview** | Home / at-a-glance dashboards | \`Page\` + an **asymmetric grid** of \`SurfaceCard\` / \`ChartPanel\` / \`StatTile\` spanning different widths (not a uniform row + table) |
|
|
288
|
+
| **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\` |
|
|
289
|
+
| **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\`) |
|
|
290
|
+
| **Copilot overlay** | A data app that also wants an assistant | any of the above + \`AppShell chat={<AppChatPanel />}\` (floating, never a second column) |
|
|
291
|
+
| **Section-switcher** | One page, several views | \`SubNav\` / \`PillSegmentedTabs\` (\`trackVariant="flush"\`) switching panels with state/router |
|
|
292
|
+
|
|
293
|
+
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.
|
|
294
|
+
|
|
295
|
+
### Full-height pages (chat, canvas, split views)
|
|
296
|
+
|
|
297
|
+
The content region is a **padded scroll area** by default \u2014 great for stacked \`Page\` \u2192 \`Section\` content, wrong for a surface that must fill the viewport. For full-bleed pages:
|
|
298
|
+
|
|
299
|
+
- Pass **\`contentFill\`** to \`AppShell\` \u2192 the content region becomes a bounded, non-scrolling flex column (clipped, no bottom padding).
|
|
300
|
+
- Pass **\`fill\`** to \`Page\` \u2192 the page becomes a \`min-h-0 flex-1\` flex column.
|
|
301
|
+
- 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\`.
|
|
302
|
+
|
|
303
|
+
\`\`\`tsx
|
|
304
|
+
<AppShell contentFill topbar={<div className="flex justify-end p-4"><ModeToggle /></div>}>
|
|
305
|
+
<Page fill> {/* headerless: omit title */}
|
|
306
|
+
<TimbalChat workforceId="\u2026" className="min-h-0 flex-1" />
|
|
307
|
+
</Page>
|
|
308
|
+
</AppShell>
|
|
309
|
+
\`\`\`
|
|
310
|
+
|
|
311
|
+
**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.
|
|
312
|
+
|
|
313
|
+
**Full-page Assistant Guidelines (Hardened Layout):**
|
|
314
|
+
When creating a full-page assistant/chat page, let the chat component own the layout and welcome state completely.
|
|
315
|
+
- **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.
|
|
316
|
+
- **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..." }} />\`.
|
|
317
|
+
|
|
242
318
|
### Module layout (source folders)
|
|
243
319
|
|
|
244
320
|
Presentational groups \u2014 import from the package root, not from these paths:
|
|
@@ -248,11 +324,11 @@ Presentational groups \u2014 import from the package root, not from these paths:
|
|
|
248
324
|
| \`data/\` | \`MetricRow\`, \`MetricChartCard\`, \`MetricTile\`, \`DataTable\`, \`FilterBar\`, \`FilterField\`, \`ChartPanel\` |
|
|
249
325
|
| \`integrations/\` | \`IntegrationCard\`, \`ConnectionRow\`, \`ConnectionRowList\`, \`IntegrationsEmptyState\`, \`PlanBadge\` |
|
|
250
326
|
| \`settings/\` | \`SettingsSection\`, \`FieldRow\`, \`DangerZone\`, \`FloatingUnsavedChangesBar\` |
|
|
251
|
-
| \`surfaces/\` | \`StatTile\`, \`InfoCard\`, \`ResourceCard\`, \`DescriptionList\`, \`ExpandableSection\`, \`StatusDot\`, \`StatusBadge\`, \`EmptyState\` |
|
|
252
|
-
| \`layout/\` | \`AppShell\`, \`Page
|
|
327
|
+
| \`surfaces/\` | \`StatTile\`, \`InfoCard\`, \`AlertCard\`, \`CatalogCard\`, \`ResourceCard\`, \`DescriptionList\`, \`ExpandableSection\`, \`StatusDot\`, \`StatusBadge\`, \`EmptyState\` |
|
|
328
|
+
| \`layout/\` | \`AppShell\`, \`Page\` (auto-stacks children; \`width\` ladder), \`Section\`, \`Stack\` |
|
|
253
329
|
| \`charts\` (re-exported) | \`LineAreaChart\`, \`PieChart\`, \`RadialChart\`, \`RadarChart\`, \`Sparkline\`, \`CHART_PALETTE\` |
|
|
254
330
|
|
|
255
|
-
Also re-exported
|
|
331
|
+
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\`.
|
|
256
332
|
|
|
257
333
|
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.
|
|
258
334
|
|
|
@@ -264,9 +340,12 @@ Theming helpers (import from the package root or \`/app\`): \`createTimbalTheme\
|
|
|
264
340
|
| **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. |
|
|
265
341
|
| **Context** | Do not show raw JSON context in the panel header; keep context in \`AppCopilotProvider\` only. |
|
|
266
342
|
| **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\`. |
|
|
267
|
-
| **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
|
|
343
|
+
| **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. |
|
|
344
|
+
| **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-*\`. |
|
|
345
|
+
| **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\`. |
|
|
268
346
|
| **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. |
|
|
269
347
|
| **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. |
|
|
348
|
+
| **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. |
|
|
270
349
|
| **Modals** | Use \`AppConfirmDialog\` for destructive/export confirmations. |
|
|
271
350
|
| **Metrics** | Overview KPIs \u2192 \`MetricRow\` or \`MetricChartCard\` (not four separate heavy cards). Values use **normal** font weight, not bold. |
|
|
272
351
|
| **Integrations** | Catalog \u2192 \`IntegrationCard\` grid; connected list \u2192 \`ConnectionRow\` inside \`ConnectionRowList\`. Footer CTAs: \`Button variant="secondary"\`. |
|
|
@@ -297,17 +376,16 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
|
|
|
297
376
|
|
|
298
377
|
| Component | Use for |
|
|
299
378
|
|-----------|---------|
|
|
300
|
-
| \`AppShell\` | Shell: optional \`sidebar\`, \`topbar\`, main \`children\`, optional floating \`chat\`. Props: \`chatTriggerLabel\`, \`chatCollapsible\`, \`chatWidth\`, \`chatHeight\`, controlled \`chatOpen
|
|
301
|
-
| \`AppShellTopbar\` | Full-width top bar: \`start\`, \`actions\` slots. |
|
|
379
|
+
| \`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). |
|
|
302
380
|
| \`AppCopilotProvider\` | React context for copilot-aware tools (page, filters, selection, etc.). |
|
|
303
381
|
| \`AppChatPanel\` | Floating thread: \`workforceId\`, \`welcome\`, \`debug\`. |
|
|
304
382
|
| \`useAppShellChat\` | Custom open/close trigger when \`hideChatTrigger\` on shell. |
|
|
305
|
-
| \`Page\` | Page title, description, \`breadcrumbs\`, \`actions\`, \`density\` (\`"default"\` | \`"compact"\`), children. |
|
|
383
|
+
| \`Page\` | Page title, description, \`breadcrumbs\`, \`actions\`, \`density\` (\`"default"\` | \`"compact"\`), children. **\`title\` is optional** \u2014 omit it for a headerless page (no \`<h1>\`). **\`fill\`** makes it a \`min-h-0 flex-1\` column for full-height content (pair with \`AppShell contentFill\`). |
|
|
306
384
|
| \`Section\` | Titled block inside a page. Optional \`density\` overrides inherited page density. |
|
|
307
385
|
| \`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. |
|
|
308
386
|
| **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\`. |
|
|
309
387
|
| \`Breadcrumbs\` | Trail: \`items: [{ label, href? }]\`. |
|
|
310
|
-
| \`Button\` | Actions
|
|
388
|
+
| \`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. |
|
|
311
389
|
| \`StatTile\` | Single KPI in its own card (grid of scattered stats). Prefer \`MetricRow\` for a unified overview strip. |
|
|
312
390
|
| \`StatusBadge\` | Status pill: \`tone\` (\`default\`\\|\`primary\`\\|\`success\`\\|\`warn\`\\|\`danger\`\\|\`muted\`), children. Use \`danger\` for critical/error severity. |
|
|
313
391
|
| \`FilterBar\` | Horizontal filter row \u2014 bottom-aligns controls. Mix \`SearchInput\` with labeled \`FilterField\` + \`Select\` (or \`Field\` + \`Select\`); labels sit above, control baselines match. |
|
|
@@ -316,7 +394,7 @@ The cause of slop is dropping **below** the curated block layer into raw primiti
|
|
|
316
394
|
| \`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\`). |
|
|
317
395
|
| \`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\`. |
|
|
318
396
|
| \`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. |
|
|
319
|
-
| \`FieldInput\`, \`FieldTextarea\`, \`FieldSelect\`, \`FieldSwitch\` | Settings-style forms with labels and hints. |
|
|
397
|
+
| \`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\`. |
|
|
320
398
|
| \`FormSection\` | Grouped form block. |
|
|
321
399
|
| \`AppConfirmDialog\` | Confirm/cancel modal: \`open\`, \`onOpenChange\`, \`title\`, \`description\`, \`onConfirm\`. |
|
|
322
400
|
| \`SurfaceCard\`, \`EmptyState\` | Generic surfaces when needed. |
|
|
@@ -362,9 +440,13 @@ Charts run on **recharts** with shadcn \`ChartContainer\` / \`ChartTooltipConten
|
|
|
362
440
|
| Component | Use for |
|
|
363
441
|
|-----------|---------|
|
|
364
442
|
| \`InfoCard\` | Soft callout: \`icon\`, \`title\`, body, \`action\`, \`tone\` (\`info\`/\`success\`/\`warn\`/\`danger\`). |
|
|
443
|
+
| \`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. |
|
|
444
|
+
| \`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). |
|
|
365
445
|
| \`DescriptionList\` | Read-only key/value metadata: \`items: [{ label, value }]\`, optional \`stacked\`. |
|
|
366
446
|
| \`ExpandableSection\` | Collapsible block: \`title\`, \`icon\`, \`count\`, animated body (\`aria-expanded\` + \`aria-controls\`). |
|
|
367
447
|
| \`StatusDot\` | Status indicator dot: \`tone\`, \`label\`, \`pulse\`. |
|
|
448
|
+
| \`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. |
|
|
449
|
+
| \`Timeline\` | Vertical event log: \`items: [{ id, title, description?, meta?, tone?, icon? }]\`. Presentational \u2014 pass already-formatted timestamps in \`meta\`. |
|
|
368
450
|
|
|
369
451
|
Studio chrome (\`StudioSidebar\`, \`ModeToggle\`, \u2026) lives in \`@timbal-ai/timbal-react/studio\` \u2014 optional, not required for every dashboard.
|
|
370
452
|
|
|
@@ -395,8 +477,11 @@ Ready-made **section patterns** assembled from the components above. Each is a c
|
|
|
395
477
|
- **Empty states** \u2014 no-data / no-results / first-run. Compose \`EmptyState\` + \`Card\` + \`Button\`.
|
|
396
478
|
- **Sign-in card** \u2014 centered auth entry. Compose \`Card\` + \`Input\` + \`Label\` + \`Button\`.
|
|
397
479
|
|
|
398
|
-
**Shells &
|
|
480
|
+
**Shells & layouts**
|
|
399
481
|
- **Minimal shell** \u2014 \`AppShell\` + \`Page\` (no sidebar/chat).
|
|
482
|
+
- **Bento dashboard** \u2014 \`Page\` + an asymmetric grid of \`SurfaceCard\` / \`ChartPanel\` / \`StatTile\` (varied spans) for overview/home screens.
|
|
483
|
+
- **Split view** \u2014 master\u2013detail: \`AppShell contentFill\` + \`Page fill\` + a two-pane flex row (list + detail), each pane \`min-h-0 overflow-y-auto\`.
|
|
484
|
+
- **Full-page chat** \u2014 \`AppShell contentFill\` + headerless \`Page fill\` + \`TimbalChat className="min-h-0 flex-1"\` (composer pinned; no \`h-[calc(...)]\`).
|
|
400
485
|
- **Copilot overlay** \u2014 \`AppShell\` + floating \`AppChatPanel\`.
|
|
401
486
|
- **Theme presets** \u2014 apply a brand preset programmatically (\`applyThemePreset\` / \`applyTimbalTheme\`); never hand-author OKLCH and don't expose a theme picker to end users.
|
|
402
487
|
|
|
@@ -429,6 +514,8 @@ import {
|
|
|
429
514
|
DataTable,
|
|
430
515
|
FilterBar,
|
|
431
516
|
FilterField,
|
|
517
|
+
AlertCard,
|
|
518
|
+
CatalogCard,
|
|
432
519
|
} from "@timbal-ai/timbal-react/app";
|
|
433
520
|
\`\`\`
|
|
434
521
|
|
|
@@ -439,6 +526,17 @@ import {
|
|
|
439
526
|
| \`examples/app-kit/recipes/*\` | **Recipes** \u2014 one pattern each (~20\u201380 lines). Use for capability, not layout. |
|
|
440
527
|
| \`examples/app-kit/reference/operations-dashboard.tsx\` | **Reference only** \u2014 full wired app; do not treat as the default generated layout. |
|
|
441
528
|
|
|
529
|
+
### API gotchas \u2014 props that do NOT exist (don't guess, don't retry variations)
|
|
530
|
+
|
|
531
|
+
The compiler rejects these every time; write against the documented shapes above instead:
|
|
532
|
+
|
|
533
|
+
- \`FieldInput\` / \`FieldTextarea\` / \`FieldSelect\` / \`FieldSwitch\` **require \`label\`** \u2014 TS2741 if omitted. Label-less control \u2192 \`FilterField\` or \`SearchInput\`.
|
|
534
|
+
- Full-height layout: \`fill\` lives on \`Page\` (paired with \`AppShell contentFill\`) \u2014 there is no \`fill\` on \`Section\` or \`AppShell\`.
|
|
535
|
+
- \`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"\`.
|
|
536
|
+
- There is **no \`Tabs\` export** \u2014 section switching uses \`SubNav\` or \`PillSegmentedTabs\`.
|
|
537
|
+
- \`Banner\` and \`Timeline\` exist (see menu) \u2014 import them from \`/app\`, \`/ui\`, or the root.
|
|
538
|
+
- 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.
|
|
539
|
+
|
|
442
540
|
### Rules
|
|
443
541
|
|
|
444
542
|
- Prefer stable props documented above; avoid undocumented \`design/*\` class exports (\`connectionRowListClass\` is exported but \`ConnectionRowList\` is preferred).
|
|
@@ -469,6 +567,7 @@ var GRADIENT_DIRECTIONS = /* @__PURE__ */ new Set([
|
|
|
469
567
|
]);
|
|
470
568
|
var ICON_IMPORT_RE = /from\s+["']lucide-react["']/;
|
|
471
569
|
var RAW_CONTROL_SURFACE_RE = /\bborder-input\b/;
|
|
570
|
+
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/;
|
|
472
571
|
var RESERVED_GRADIENT_SET = new Set(RESERVED_GRADIENT_TOKENS);
|
|
473
572
|
function stripVariants(util) {
|
|
474
573
|
return util.replace(/^(?:[a-z-]+:)*/, "");
|
|
@@ -485,7 +584,14 @@ function lintGeneratedUi(source, options = {}) {
|
|
|
485
584
|
let usesLucide = false;
|
|
486
585
|
let iconUsageCount = 0;
|
|
487
586
|
let dividerRunCount = 0;
|
|
587
|
+
let pageTitle = null;
|
|
588
|
+
const pageTitleMatch = source.match(/<Page\s+[^>]*\btitle=(?:"([^"]+)"|\{["']([^"']+)["']\})/);
|
|
589
|
+
if (pageTitleMatch) {
|
|
590
|
+
pageTitle = (pageTitleMatch[1] || pageTitleMatch[2]).trim().toLowerCase();
|
|
591
|
+
}
|
|
592
|
+
const hasChat = /\b(?:TimbalChat|AppChatPanel|Thread)\b/.test(source);
|
|
488
593
|
const lucideNames = /* @__PURE__ */ new Set();
|
|
594
|
+
const openCards = [];
|
|
489
595
|
for (let i = 0; i < lines.length; i++) {
|
|
490
596
|
const line = lines[i];
|
|
491
597
|
const lineNo = i + 1;
|
|
@@ -501,6 +607,33 @@ function lintGeneratedUi(source, options = {}) {
|
|
|
501
607
|
continue;
|
|
502
608
|
}
|
|
503
609
|
if (isCommentOrImport(line)) continue;
|
|
610
|
+
const cardMatch = line.match(/<(Card|SurfaceCard|ArtifactCard)\b/);
|
|
611
|
+
if (cardMatch) {
|
|
612
|
+
const isSelfClosing = /\/>/.test(line) && line.indexOf(cardMatch[0]) < line.indexOf("/>");
|
|
613
|
+
if (!isSelfClosing) {
|
|
614
|
+
openCards.push({ type: cardMatch[1], line: lineNo });
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
const closeMatch = line.match(/<\/(Card|SurfaceCard|ArtifactCard)\b/);
|
|
618
|
+
if (closeMatch && openCards.length > 0) {
|
|
619
|
+
const idx = openCards.map((c) => c.type).lastIndexOf(closeMatch[1]);
|
|
620
|
+
if (idx !== -1) {
|
|
621
|
+
openCards.splice(idx, 1);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
if (openCards.length > 0) {
|
|
625
|
+
const tableMatch = line.match(/<(DataTable|table|Table)\b/);
|
|
626
|
+
if (tableMatch) {
|
|
627
|
+
const parentCard = openCards[openCards.length - 1];
|
|
628
|
+
findings.push({
|
|
629
|
+
rule: "no-table-in-card",
|
|
630
|
+
severity: "error",
|
|
631
|
+
line: lineNo,
|
|
632
|
+
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.`,
|
|
633
|
+
snippet: line.trim().slice(0, 120)
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
}
|
|
504
637
|
const rawColors = line.match(RAW_COLOR_RE);
|
|
505
638
|
if (rawColors) {
|
|
506
639
|
for (const m of rawColors) {
|
|
@@ -541,6 +674,15 @@ function lintGeneratedUi(source, options = {}) {
|
|
|
541
674
|
snippet: line.trim().slice(0, 120)
|
|
542
675
|
});
|
|
543
676
|
}
|
|
677
|
+
if (COLORED_HOVER_RE.test(line)) {
|
|
678
|
+
findings.push({
|
|
679
|
+
rule: "no-colored-hover",
|
|
680
|
+
severity: "warn",
|
|
681
|
+
line: lineNo,
|
|
682
|
+
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.",
|
|
683
|
+
snippet: line.trim().slice(0, 120)
|
|
684
|
+
});
|
|
685
|
+
}
|
|
544
686
|
if (BOLD_VALUE_RE.test(line)) {
|
|
545
687
|
findings.push({
|
|
546
688
|
rule: "bold-metric",
|
|
@@ -587,6 +729,44 @@ function lintGeneratedUi(source, options = {}) {
|
|
|
587
729
|
if (hits) iconUsageCount += hits.length;
|
|
588
730
|
}
|
|
589
731
|
}
|
|
732
|
+
if (pageTitle) {
|
|
733
|
+
const titleMatch = line.match(/<(Section|ChartPanel|Card|DataTable|SurfaceCard)\s+[^>]*\btitle=(?:"([^"]+)"|\{["']([^"']+)["']\})/);
|
|
734
|
+
if (titleMatch) {
|
|
735
|
+
const element = titleMatch[1];
|
|
736
|
+
const titleVal = (titleMatch[2] || titleMatch[3]).trim().toLowerCase();
|
|
737
|
+
if (titleVal === pageTitle || titleVal.includes(pageTitle) || pageTitle.includes(titleVal)) {
|
|
738
|
+
findings.push({
|
|
739
|
+
rule: "no-title-repetition",
|
|
740
|
+
severity: "warn",
|
|
741
|
+
line: lineNo,
|
|
742
|
+
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.`,
|
|
743
|
+
snippet: line.trim().slice(0, 120)
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
if (hasChat) {
|
|
749
|
+
const wrappingMatch = line.match(/<(Card|Section|SurfaceCard|FormSection|SettingsSection)\b/);
|
|
750
|
+
if (wrappingMatch) {
|
|
751
|
+
findings.push({
|
|
752
|
+
rule: "no-chat-wrapping",
|
|
753
|
+
severity: "error",
|
|
754
|
+
line: lineNo,
|
|
755
|
+
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.`,
|
|
756
|
+
snippet: line.trim().slice(0, 120)
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
const headingMatch = line.match(/<(h[1-6])\b/);
|
|
760
|
+
if (headingMatch) {
|
|
761
|
+
findings.push({
|
|
762
|
+
rule: "no-chat-wrapping",
|
|
763
|
+
severity: "error",
|
|
764
|
+
line: lineNo,
|
|
765
|
+
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.`,
|
|
766
|
+
snippet: line.trim().slice(0, 120)
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
}
|
|
590
770
|
}
|
|
591
771
|
if (usesLucide && iconUsageCount > maxIcons) {
|
|
592
772
|
findings.push({
|
|
@@ -1319,7 +1499,20 @@ var TimbalThemeStyle = ({
|
|
|
1319
1499
|
|
|
1320
1500
|
// src/design/app-classes.ts
|
|
1321
1501
|
var appPageColumnClass = "mx-auto w-full max-w-[100rem] px-4 md:px-6 lg:px-8";
|
|
1322
|
-
var
|
|
1502
|
+
var PAGE_WIDTH_MAXW = {
|
|
1503
|
+
full: "max-w-none",
|
|
1504
|
+
wide: "max-w-[100rem]",
|
|
1505
|
+
default: "max-w-7xl",
|
|
1506
|
+
centered: "max-w-5xl",
|
|
1507
|
+
narrow: "max-w-3xl",
|
|
1508
|
+
prose: "max-w-2xl"
|
|
1509
|
+
};
|
|
1510
|
+
function appPageColumn(width, density = "default") {
|
|
1511
|
+
const lateral = density === "compact" ? "px-3 md:px-4" : "px-4 md:px-6 lg:px-8";
|
|
1512
|
+
const maxW = width ? PAGE_WIDTH_MAXW[width] : density === "compact" ? "max-w-none" : "max-w-[100rem]";
|
|
1513
|
+
return cn("mx-auto w-full", maxW, lateral);
|
|
1514
|
+
}
|
|
1515
|
+
var appShellTopbarInsetClass = "mx-auto w-full max-w-[100rem] px-4 md:px-6 lg:px-8";
|
|
1323
1516
|
var appShellInsetTopClass = "pt-4 md:pt-6";
|
|
1324
1517
|
var appShellInsetBottomClass = "pb-8 md:pb-10";
|
|
1325
1518
|
var appShellTopbarRowClass = cn(
|
|
@@ -1386,6 +1579,15 @@ var APP_DENSITY_CLASSES = {
|
|
|
1386
1579
|
default: appPageHeaderClass,
|
|
1387
1580
|
compact: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between pb-2 pt-1"
|
|
1388
1581
|
},
|
|
1582
|
+
/**
|
|
1583
|
+
* Vertical rhythm between a `Page`'s direct content blocks. Page-level gap so
|
|
1584
|
+
* stacked blocks (FilterBar + DataTable, MetricRow + ChartPanel, bare cards)
|
|
1585
|
+
* never render flush — the #1 source of gap-less generated layouts.
|
|
1586
|
+
*/
|
|
1587
|
+
pageStack: {
|
|
1588
|
+
default: "flex flex-col gap-6",
|
|
1589
|
+
compact: "flex flex-col gap-4"
|
|
1590
|
+
},
|
|
1389
1591
|
section: {
|
|
1390
1592
|
default: appSectionClass,
|
|
1391
1593
|
compact: "flex flex-col gap-2 py-2"
|
|
@@ -1411,12 +1613,12 @@ var APP_DENSITY_CLASSES = {
|
|
|
1411
1613
|
compact: "relative flex min-w-0 flex-1 flex-col gap-1 px-3 py-2 text-left font-normal"
|
|
1412
1614
|
},
|
|
1413
1615
|
metricChartRegion: {
|
|
1414
|
-
default: "relative min-h-0 w-full overflow-x-hidden
|
|
1415
|
-
compact: "relative min-h-0 w-full overflow-x-hidden
|
|
1616
|
+
default: "relative min-h-0 w-full overflow-x-hidden pt-2",
|
|
1617
|
+
compact: "relative min-h-0 w-full overflow-x-hidden pt-1"
|
|
1416
1618
|
},
|
|
1417
1619
|
metricChartPlotRegion: {
|
|
1418
|
-
default: "relative min-h-0 w-full overflow-x-hidden
|
|
1419
|
-
compact: "relative min-h-0 w-full overflow-x-hidden
|
|
1620
|
+
default: "relative min-h-0 w-full overflow-x-hidden px-0 pt-5 pb-3",
|
|
1621
|
+
compact: "relative min-h-0 w-full overflow-x-hidden px-0 pt-3 pb-2"
|
|
1420
1622
|
},
|
|
1421
1623
|
chartPanelBody: {
|
|
1422
1624
|
default: "pt-2 pb-3",
|
|
@@ -1445,8 +1647,71 @@ function useAppDensityClass(key, override) {
|
|
|
1445
1647
|
return appDensityClass(key, override ?? inherited);
|
|
1446
1648
|
}
|
|
1447
1649
|
|
|
1650
|
+
// src/charts/sparkline.tsx
|
|
1651
|
+
import { useId } from "react";
|
|
1652
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1653
|
+
var Sparkline = ({
|
|
1654
|
+
data,
|
|
1655
|
+
dataKey = "value",
|
|
1656
|
+
color = "var(--primary, #6366f1)",
|
|
1657
|
+
area = true,
|
|
1658
|
+
width = 96,
|
|
1659
|
+
height = 28,
|
|
1660
|
+
strokeWidth = 1.5,
|
|
1661
|
+
className,
|
|
1662
|
+
ariaLabel = "Trend"
|
|
1663
|
+
}) => {
|
|
1664
|
+
const uid = useId();
|
|
1665
|
+
const values = data.map((d) => typeof d === "number" ? d : toNum(d[dataKey]));
|
|
1666
|
+
if (values.length === 0) {
|
|
1667
|
+
return /* @__PURE__ */ jsx3("span", { className: cn("inline-block", className), style: { width, height } });
|
|
1668
|
+
}
|
|
1669
|
+
const pad = strokeWidth + 1;
|
|
1670
|
+
const min = Math.min(...values);
|
|
1671
|
+
const max = Math.max(...values);
|
|
1672
|
+
const range = max - min || 1;
|
|
1673
|
+
const innerW = width - pad * 2;
|
|
1674
|
+
const innerH = height - pad * 2;
|
|
1675
|
+
const points = values.map((v, i) => ({
|
|
1676
|
+
x: pad + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
|
|
1677
|
+
y: pad + innerH - (v - min) / range * innerH
|
|
1678
|
+
}));
|
|
1679
|
+
return /* @__PURE__ */ jsxs2(
|
|
1680
|
+
"svg",
|
|
1681
|
+
{
|
|
1682
|
+
width,
|
|
1683
|
+
height,
|
|
1684
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
1685
|
+
className: cn("block", className),
|
|
1686
|
+
role: "img",
|
|
1687
|
+
"aria-label": ariaLabel,
|
|
1688
|
+
preserveAspectRatio: "none",
|
|
1689
|
+
children: [
|
|
1690
|
+
area && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1691
|
+
/* @__PURE__ */ jsx3("defs", { children: /* @__PURE__ */ jsxs2("linearGradient", { id: `${uid}-spark`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
|
|
1692
|
+
/* @__PURE__ */ jsx3("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
|
|
1693
|
+
/* @__PURE__ */ jsx3("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
|
|
1694
|
+
] }) }),
|
|
1695
|
+
/* @__PURE__ */ jsx3("path", { d: monotoneAreaPath(points, height - pad), fill: `url(#${uid}-spark)` })
|
|
1696
|
+
] }),
|
|
1697
|
+
/* @__PURE__ */ jsx3(
|
|
1698
|
+
"path",
|
|
1699
|
+
{
|
|
1700
|
+
d: monotoneLinePath(points),
|
|
1701
|
+
fill: "none",
|
|
1702
|
+
stroke: color,
|
|
1703
|
+
strokeWidth,
|
|
1704
|
+
strokeLinecap: "round",
|
|
1705
|
+
strokeLinejoin: "round"
|
|
1706
|
+
}
|
|
1707
|
+
)
|
|
1708
|
+
]
|
|
1709
|
+
}
|
|
1710
|
+
);
|
|
1711
|
+
};
|
|
1712
|
+
|
|
1448
1713
|
// src/app/data/metrics-shared.tsx
|
|
1449
|
-
import { jsx as
|
|
1714
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1450
1715
|
var metricCardShellClass = cn(
|
|
1451
1716
|
studioIntegrationCardClass,
|
|
1452
1717
|
"aui-app-metric-card shadow-none",
|
|
@@ -1456,18 +1721,22 @@ var metricTilesRowClass = "grid w-full min-w-0";
|
|
|
1456
1721
|
var metricCellDividerClass = "border-r border-border/40";
|
|
1457
1722
|
var MetricCardHeader = ({
|
|
1458
1723
|
title,
|
|
1724
|
+
titleTag,
|
|
1459
1725
|
titleId,
|
|
1460
1726
|
description,
|
|
1461
1727
|
actions
|
|
1462
1728
|
}) => {
|
|
1463
1729
|
const headerClass = useAppDensityClass("metricCardHeader");
|
|
1464
1730
|
if (!title && !description && !actions) return null;
|
|
1465
|
-
return /* @__PURE__ */
|
|
1466
|
-
/* @__PURE__ */
|
|
1467
|
-
|
|
1468
|
-
|
|
1731
|
+
return /* @__PURE__ */ jsxs3("header", { className: cn(headerClass, "items-center"), children: [
|
|
1732
|
+
/* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
|
|
1733
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2.5", children: [
|
|
1734
|
+
title ? /* @__PURE__ */ jsx4("h3", { id: titleId, className: "text-sm font-semibold text-foreground tracking-tight select-none", children: title }) : null,
|
|
1735
|
+
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
|
|
1736
|
+
] }),
|
|
1737
|
+
description ? /* @__PURE__ */ jsx4("p", { className: "mt-1 text-xs text-muted-foreground leading-normal select-none", children: description }) : null
|
|
1469
1738
|
] }),
|
|
1470
|
-
actions ? /* @__PURE__ */
|
|
1739
|
+
actions ? /* @__PURE__ */ jsx4("div", { className: "shrink-0 flex items-center", children: actions }) : null
|
|
1471
1740
|
] });
|
|
1472
1741
|
};
|
|
1473
1742
|
function metricTilesGridColsClass(n) {
|
|
@@ -1478,6 +1747,8 @@ function metricTilesGridColsClass(n) {
|
|
|
1478
1747
|
return "grid-cols-2";
|
|
1479
1748
|
case 3:
|
|
1480
1749
|
return "grid-cols-3";
|
|
1750
|
+
case 4:
|
|
1751
|
+
return "grid-cols-2 md:grid-cols-4";
|
|
1481
1752
|
case 5:
|
|
1482
1753
|
return "grid-cols-2 sm:grid-cols-5";
|
|
1483
1754
|
case 6:
|
|
@@ -1488,12 +1759,25 @@ function metricTilesGridColsClass(n) {
|
|
|
1488
1759
|
}
|
|
1489
1760
|
|
|
1490
1761
|
// src/app/data/MetricTile.tsx
|
|
1491
|
-
import { Fragment as
|
|
1762
|
+
import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1492
1763
|
var trendToneClass = {
|
|
1493
1764
|
up: "border-border/80 bg-muted/40 text-muted-foreground",
|
|
1494
1765
|
down: "border-border/80 bg-muted/40 text-muted-foreground",
|
|
1495
1766
|
neutral: "border-border/80 bg-muted/30 text-muted-foreground"
|
|
1496
1767
|
};
|
|
1768
|
+
var inlineTrendToneClass = {
|
|
1769
|
+
up: "text-blue-500/90 dark:text-blue-400/95 font-medium",
|
|
1770
|
+
down: "text-rose-500/90 dark:text-rose-400/95 font-medium",
|
|
1771
|
+
neutral: "text-muted-foreground/80"
|
|
1772
|
+
};
|
|
1773
|
+
var activeToneClass = {
|
|
1774
|
+
default: "bg-foreground dark:bg-white",
|
|
1775
|
+
primary: "bg-primary",
|
|
1776
|
+
success: "bg-emerald-500",
|
|
1777
|
+
warn: "bg-amber-500",
|
|
1778
|
+
danger: "bg-destructive",
|
|
1779
|
+
neutral: "bg-border"
|
|
1780
|
+
};
|
|
1497
1781
|
var metricTileInteractiveExtraClass = cn(
|
|
1498
1782
|
"bg-transparent hover:bg-transparent active:bg-transparent",
|
|
1499
1783
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-foreground/10"
|
|
@@ -1504,58 +1788,86 @@ var MetricTile = ({
|
|
|
1504
1788
|
unit,
|
|
1505
1789
|
trend,
|
|
1506
1790
|
trendTone = "neutral",
|
|
1791
|
+
trendVariant = "pill",
|
|
1507
1792
|
active = false,
|
|
1793
|
+
activeTone = "default",
|
|
1508
1794
|
showDivider = false,
|
|
1509
1795
|
onSelect,
|
|
1796
|
+
sparklineData,
|
|
1797
|
+
sparklineConfig,
|
|
1798
|
+
sparkline,
|
|
1510
1799
|
ariaLabel,
|
|
1511
1800
|
className
|
|
1512
1801
|
}) => {
|
|
1513
1802
|
const metricTileBaseClass = useAppDensityClass("metricTile");
|
|
1514
|
-
const
|
|
1515
|
-
|
|
1803
|
+
const hasSparkline = Boolean(sparkline || sparklineData);
|
|
1804
|
+
const content = /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1805
|
+
active ? /* @__PURE__ */ jsx5(
|
|
1516
1806
|
"span",
|
|
1517
1807
|
{
|
|
1518
1808
|
"aria-hidden": true,
|
|
1519
|
-
className:
|
|
1809
|
+
className: cn(
|
|
1810
|
+
"absolute inset-x-0 bottom-0 h-0.5 z-20 transition-colors duration-200",
|
|
1811
|
+
activeToneClass[activeTone]
|
|
1812
|
+
)
|
|
1520
1813
|
}
|
|
1521
1814
|
) : null,
|
|
1522
|
-
/* @__PURE__ */
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
"
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1815
|
+
hasSparkline ? /* @__PURE__ */ jsx5("div", { className: "absolute inset-x-0 bottom-0.5 h-9 w-full overflow-hidden pointer-events-none z-0 opacity-45 dark:opacity-35 select-none", children: sparkline ?? /* @__PURE__ */ jsx5(
|
|
1816
|
+
Sparkline,
|
|
1817
|
+
{
|
|
1818
|
+
data: sparklineData,
|
|
1819
|
+
width: 160,
|
|
1820
|
+
height: 36,
|
|
1821
|
+
className: "w-full h-full",
|
|
1822
|
+
color: trendTone === "up" ? "var(--primary, #3b82f6)" : trendTone === "down" ? "var(--destructive, #f43f5e)" : "var(--muted-foreground, #64748b)",
|
|
1823
|
+
...sparklineConfig
|
|
1824
|
+
}
|
|
1825
|
+
) }) : null,
|
|
1826
|
+
/* @__PURE__ */ jsxs4("div", { className: "relative z-10 flex flex-col gap-1 w-full text-left", children: [
|
|
1827
|
+
/* @__PURE__ */ jsx5("span", { className: "text-xs font-semibold text-muted-foreground/80 tracking-tight", children: label }),
|
|
1828
|
+
/* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-2", children: [
|
|
1829
|
+
/* @__PURE__ */ jsxs4("span", { className: "flex items-baseline gap-1", children: [
|
|
1830
|
+
/* @__PURE__ */ jsx5("span", { className: "text-2xl font-normal tracking-tight text-foreground tabular-nums", children: value }),
|
|
1831
|
+
unit ? /* @__PURE__ */ jsx5("span", { className: "text-xs font-medium text-muted-foreground", children: unit }) : null
|
|
1832
|
+
] }),
|
|
1833
|
+
trend ? trendVariant === "inline" ? /* @__PURE__ */ jsx5("span", { className: cn("text-xs leading-none select-none", inlineTrendToneClass[trendTone]), children: trend }) : /* @__PURE__ */ jsx5(
|
|
1834
|
+
"span",
|
|
1835
|
+
{
|
|
1836
|
+
className: cn(
|
|
1837
|
+
"rounded-full border px-1.5 py-0.5 text-xs font-normal select-none",
|
|
1838
|
+
trendToneClass[trendTone]
|
|
1839
|
+
),
|
|
1840
|
+
children: trend
|
|
1841
|
+
}
|
|
1842
|
+
) : null
|
|
1843
|
+
] })
|
|
1538
1844
|
] })
|
|
1539
1845
|
] });
|
|
1540
1846
|
const divider = showDivider ? metricCellDividerClass : void 0;
|
|
1541
1847
|
if (onSelect) {
|
|
1542
|
-
return /* @__PURE__ */
|
|
1848
|
+
return /* @__PURE__ */ jsx5(
|
|
1543
1849
|
"button",
|
|
1544
1850
|
{
|
|
1545
1851
|
type: "button",
|
|
1546
1852
|
onClick: onSelect,
|
|
1547
1853
|
"aria-pressed": active,
|
|
1548
1854
|
"aria-label": ariaLabel,
|
|
1549
|
-
className: cn(
|
|
1855
|
+
className: cn(
|
|
1856
|
+
metricTileBaseClass,
|
|
1857
|
+
"transition-all duration-200 hover:bg-muted/10",
|
|
1858
|
+
metricTileInteractiveExtraClass,
|
|
1859
|
+
divider,
|
|
1860
|
+
className
|
|
1861
|
+
),
|
|
1550
1862
|
children: content
|
|
1551
1863
|
}
|
|
1552
1864
|
);
|
|
1553
1865
|
}
|
|
1554
|
-
return /* @__PURE__ */
|
|
1866
|
+
return /* @__PURE__ */ jsx5("div", { className: cn(metricTileBaseClass, divider, className), children: content });
|
|
1555
1867
|
};
|
|
1556
1868
|
|
|
1557
1869
|
// src/app/theme/ThemePresetGallery.tsx
|
|
1558
|
-
import { jsx as
|
|
1870
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1559
1871
|
|
|
1560
1872
|
// src/app/layout/app-shell-chat-context.tsx
|
|
1561
1873
|
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
@@ -1581,8 +1893,8 @@ function useAppShellNav() {
|
|
|
1581
1893
|
|
|
1582
1894
|
// src/app/layout/AppShell.tsx
|
|
1583
1895
|
import { motion, useReducedMotion } from "motion/react";
|
|
1584
|
-
import { useCallback, useMemo, useState } from "react";
|
|
1585
|
-
import { jsx as
|
|
1896
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
1897
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1586
1898
|
var floatingTriggerClass = cn(
|
|
1587
1899
|
"aui-app-shell-chat-trigger-fixed fixed z-50 rounded-full px-5 py-2.5 text-sm font-medium shadow-card-elevated",
|
|
1588
1900
|
"bg-primary text-primary-foreground transition-colors hover:bg-primary/90",
|
|
@@ -1600,39 +1912,57 @@ var AppShellBody = ({
|
|
|
1600
1912
|
sidebar,
|
|
1601
1913
|
topbarContent,
|
|
1602
1914
|
mainClassName,
|
|
1915
|
+
contentFill = false,
|
|
1603
1916
|
insetPaddingPx,
|
|
1604
1917
|
insetExpanded,
|
|
1605
1918
|
children
|
|
1606
1919
|
}) => {
|
|
1920
|
+
const [isMobile, setIsMobile] = useState(() => {
|
|
1921
|
+
if (typeof window === "undefined") return false;
|
|
1922
|
+
return window.innerWidth < 768;
|
|
1923
|
+
});
|
|
1924
|
+
useEffect(() => {
|
|
1925
|
+
if (typeof window === "undefined") return;
|
|
1926
|
+
const onResize = () => setIsMobile(window.innerWidth < 768);
|
|
1927
|
+
onResize();
|
|
1928
|
+
window.addEventListener("resize", onResize);
|
|
1929
|
+
return () => window.removeEventListener("resize", onResize);
|
|
1930
|
+
}, []);
|
|
1607
1931
|
const reducedMotion = useReducedMotion();
|
|
1608
1932
|
const layoutDirection = insetExpanded ? "expand" : "collapse";
|
|
1609
1933
|
const layoutTransition = studioSidebarWidthTransition(
|
|
1610
1934
|
!!reducedMotion,
|
|
1611
1935
|
layoutDirection
|
|
1612
1936
|
);
|
|
1613
|
-
const insetPadding = sidebar ? insetPaddingPx : 0;
|
|
1614
|
-
return /* @__PURE__ */
|
|
1937
|
+
const insetPadding = sidebar && !isMobile ? insetPaddingPx : 0;
|
|
1938
|
+
return /* @__PURE__ */ jsx7(
|
|
1615
1939
|
motion.div,
|
|
1616
1940
|
{
|
|
1617
1941
|
className: "aui-app-shell-body relative z-10 flex min-h-0 min-w-0 flex-1 flex-col",
|
|
1618
1942
|
initial: false,
|
|
1619
1943
|
animate: { paddingLeft: insetPadding },
|
|
1620
1944
|
transition: layoutTransition,
|
|
1621
|
-
children: /* @__PURE__ */
|
|
1945
|
+
children: /* @__PURE__ */ jsxs6(
|
|
1622
1946
|
"div",
|
|
1623
1947
|
{
|
|
1624
1948
|
className: cn(
|
|
1625
|
-
"aui-app-shell-scroll flex min-h-0 flex-1 flex-col
|
|
1949
|
+
"aui-app-shell-scroll flex min-h-0 flex-1 flex-col",
|
|
1950
|
+
// Padded scroll region by default; a full-bleed page (chat / canvas) owns
|
|
1951
|
+
// its own scroll, so clip here and let the bounded `main` fill exactly.
|
|
1952
|
+
contentFill ? "overflow-hidden" : "overflow-y-auto",
|
|
1626
1953
|
!topbarContent && appShellInsetTopClass
|
|
1627
1954
|
),
|
|
1628
1955
|
children: [
|
|
1629
|
-
topbarContent ? /* @__PURE__ */
|
|
1630
|
-
/* @__PURE__ */
|
|
1956
|
+
topbarContent ? /* @__PURE__ */ jsx7("header", { className: cn("aui-app-shell-topbar-region", appShellTopbarStickyClass), children: /* @__PURE__ */ jsx7("div", { className: appShellTopbarInsetClass, children: topbarContent }) }) : null,
|
|
1957
|
+
/* @__PURE__ */ jsx7(
|
|
1631
1958
|
"main",
|
|
1632
1959
|
{
|
|
1633
1960
|
className: cn(
|
|
1634
|
-
|
|
1635
|
-
|
|
1961
|
+
// Bounded flex column by default so `h-full` / `flex-1 min-h-0` children
|
|
1962
|
+
// (full-page chat, canvas) resolve a height without `mainClassName` surgery.
|
|
1963
|
+
"aui-app-shell-main flex min-h-0 min-w-0 flex-1 flex-col",
|
|
1964
|
+
// Bottom breathing room for scrolling content; full-bleed pages skip it.
|
|
1965
|
+
!contentFill && appShellInsetBottomClass,
|
|
1636
1966
|
mainClassName
|
|
1637
1967
|
),
|
|
1638
1968
|
children
|
|
@@ -1662,7 +1992,8 @@ var AppShell = ({
|
|
|
1662
1992
|
defaultNavOpen = false,
|
|
1663
1993
|
onNavOpenChange,
|
|
1664
1994
|
className,
|
|
1665
|
-
mainClassName
|
|
1995
|
+
mainClassName,
|
|
1996
|
+
contentFill = false
|
|
1666
1997
|
}) => {
|
|
1667
1998
|
const topbarContent = topbar ?? header;
|
|
1668
1999
|
const hasChat = Boolean(chat);
|
|
@@ -1703,18 +2034,19 @@ var AppShell = ({
|
|
|
1703
2034
|
setInsetPaddingPx(insetPx);
|
|
1704
2035
|
}, []);
|
|
1705
2036
|
const insetExpanded = insetPaddingPx >= SIDEBAR_INSET_PX_EXPANDED;
|
|
1706
|
-
const shellBody = /* @__PURE__ */
|
|
2037
|
+
const shellBody = /* @__PURE__ */ jsx7(
|
|
1707
2038
|
AppShellBody,
|
|
1708
2039
|
{
|
|
1709
2040
|
sidebar,
|
|
1710
2041
|
topbarContent,
|
|
1711
2042
|
mainClassName,
|
|
2043
|
+
contentFill,
|
|
1712
2044
|
insetPaddingPx,
|
|
1713
2045
|
insetExpanded,
|
|
1714
2046
|
children
|
|
1715
2047
|
}
|
|
1716
2048
|
);
|
|
1717
|
-
const tree = /* @__PURE__ */
|
|
2049
|
+
const tree = /* @__PURE__ */ jsx7(ShellInsetProvider, { value: sidebar ? reportShellInset : null, children: /* @__PURE__ */ jsxs6(
|
|
1718
2050
|
"div",
|
|
1719
2051
|
{
|
|
1720
2052
|
className: cn(
|
|
@@ -1724,7 +2056,7 @@ var AppShell = ({
|
|
|
1724
2056
|
style: studioChromeShellStyle,
|
|
1725
2057
|
children: [
|
|
1726
2058
|
sidebar,
|
|
1727
|
-
sidebar && navOpen ? /* @__PURE__ */
|
|
2059
|
+
sidebar && navOpen ? /* @__PURE__ */ jsx7(
|
|
1728
2060
|
"button",
|
|
1729
2061
|
{
|
|
1730
2062
|
type: "button",
|
|
@@ -1734,7 +2066,7 @@ var AppShell = ({
|
|
|
1734
2066
|
}
|
|
1735
2067
|
) : null,
|
|
1736
2068
|
shellBody,
|
|
1737
|
-
hasChat && chatOpen ? /* @__PURE__ */
|
|
2069
|
+
hasChat && chatOpen ? /* @__PURE__ */ jsx7(
|
|
1738
2070
|
"div",
|
|
1739
2071
|
{
|
|
1740
2072
|
className: floatingPanelClass,
|
|
@@ -1747,7 +2079,7 @@ var AppShell = ({
|
|
|
1747
2079
|
children: chat
|
|
1748
2080
|
}
|
|
1749
2081
|
) : null,
|
|
1750
|
-
hasChat && chatCollapsible && !chatOpen && !hideChatTrigger ? /* @__PURE__ */
|
|
2082
|
+
hasChat && chatCollapsible && !chatOpen && !hideChatTrigger ? /* @__PURE__ */ jsx7(
|
|
1751
2083
|
"button",
|
|
1752
2084
|
{
|
|
1753
2085
|
type: "button",
|
|
@@ -1760,11 +2092,11 @@ var AppShell = ({
|
|
|
1760
2092
|
]
|
|
1761
2093
|
}
|
|
1762
2094
|
) });
|
|
1763
|
-
const withNav = /* @__PURE__ */
|
|
2095
|
+
const withNav = /* @__PURE__ */ jsx7(AppShellNavProvider, { value: navControls, children: tree });
|
|
1764
2096
|
if (!hasChat) {
|
|
1765
2097
|
return withNav;
|
|
1766
2098
|
}
|
|
1767
|
-
return /* @__PURE__ */
|
|
2099
|
+
return /* @__PURE__ */ jsx7(
|
|
1768
2100
|
AppShellChatProvider,
|
|
1769
2101
|
{
|
|
1770
2102
|
value: {
|
|
@@ -1778,23 +2110,6 @@ var AppShell = ({
|
|
|
1778
2110
|
);
|
|
1779
2111
|
};
|
|
1780
2112
|
|
|
1781
|
-
// src/app/layout/AppShellTopbar.tsx
|
|
1782
|
-
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1783
|
-
var AppShellTopbar = ({
|
|
1784
|
-
start,
|
|
1785
|
-
actions,
|
|
1786
|
-
children,
|
|
1787
|
-
className
|
|
1788
|
-
}) => {
|
|
1789
|
-
return /* @__PURE__ */ jsxs6("div", { className: cn("aui-app-shell-topbar", appShellTopbarRowClass, className), children: [
|
|
1790
|
-
/* @__PURE__ */ jsxs6("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
1791
|
-
start,
|
|
1792
|
-
children
|
|
1793
|
-
] }),
|
|
1794
|
-
actions ? /* @__PURE__ */ jsx7("div", { className: "aui-app-shell-topbar-actions flex shrink-0 items-center gap-2", children: actions }) : null
|
|
1795
|
-
] });
|
|
1796
|
-
};
|
|
1797
|
-
|
|
1798
2113
|
// src/app/layout/AppShellChatTrigger.tsx
|
|
1799
2114
|
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1800
2115
|
var floatingPositionClass = "fixed bottom-6 right-6 z-50 max-sm:bottom-4 max-sm:right-4";
|
|
@@ -1858,11 +2173,14 @@ var PageHeader = ({
|
|
|
1858
2173
|
className
|
|
1859
2174
|
}) => {
|
|
1860
2175
|
const pageHeaderClass = useAppDensityClass("pageHeader");
|
|
2176
|
+
if (title == null && description == null && actions == null) {
|
|
2177
|
+
return null;
|
|
2178
|
+
}
|
|
1861
2179
|
return /* @__PURE__ */ jsxs7("header", { className: cn("aui-app-page-header", pageHeaderClass, className), children: [
|
|
1862
|
-
/* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
|
|
1863
|
-
/* @__PURE__ */ jsx10("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }),
|
|
2180
|
+
title != null || description != null ? /* @__PURE__ */ jsxs7("div", { className: "min-w-0", children: [
|
|
2181
|
+
title != null ? /* @__PURE__ */ jsx10("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: title }) : null,
|
|
1864
2182
|
description ? /* @__PURE__ */ jsx10("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
|
|
1865
|
-
] }),
|
|
2183
|
+
] }) : null,
|
|
1866
2184
|
actions ? /* @__PURE__ */ jsx10("div", { className: "aui-app-page-header-actions flex shrink-0 flex-wrap items-center gap-2", children: actions }) : null
|
|
1867
2185
|
] });
|
|
1868
2186
|
};
|
|
@@ -1872,32 +2190,47 @@ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
|
1872
2190
|
var PageFrame = ({
|
|
1873
2191
|
children,
|
|
1874
2192
|
breadcrumbs,
|
|
2193
|
+
width,
|
|
2194
|
+
fill = false,
|
|
2195
|
+
fillPadded = false,
|
|
1875
2196
|
className,
|
|
1876
2197
|
...headerProps
|
|
1877
2198
|
}) => {
|
|
1878
2199
|
const density = useAppDensity();
|
|
1879
|
-
const
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
}
|
|
1891
|
-
);
|
|
2200
|
+
const stackClass = useAppDensityClass("pageStack");
|
|
2201
|
+
const columnClass = appPageColumn(width, density);
|
|
2202
|
+
const rootClass = fill ? cn(
|
|
2203
|
+
"flex min-h-0 min-w-0 flex-1 flex-col",
|
|
2204
|
+
fillPadded && columnClass
|
|
2205
|
+
) : columnClass;
|
|
2206
|
+
return /* @__PURE__ */ jsxs8("div", { className: cn("aui-app-page", rootClass, className), "data-density": density, children: [
|
|
2207
|
+
breadcrumbs,
|
|
2208
|
+
/* @__PURE__ */ jsx11(PageHeader, { ...headerProps }),
|
|
2209
|
+
fill ? children : /* @__PURE__ */ jsx11("div", { className: cn("aui-app-page-stack", stackClass), children })
|
|
2210
|
+
] });
|
|
1892
2211
|
};
|
|
1893
2212
|
var Page = ({
|
|
1894
2213
|
density = "default",
|
|
1895
2214
|
children,
|
|
1896
2215
|
breadcrumbs,
|
|
2216
|
+
width,
|
|
2217
|
+
fill = false,
|
|
2218
|
+
fillPadded = false,
|
|
1897
2219
|
className,
|
|
1898
2220
|
...headerProps
|
|
1899
2221
|
}) => {
|
|
1900
|
-
return /* @__PURE__ */ jsx11(AppDensityProvider, { density, children: /* @__PURE__ */ jsx11(
|
|
2222
|
+
return /* @__PURE__ */ jsx11(AppDensityProvider, { density, children: /* @__PURE__ */ jsx11(
|
|
2223
|
+
PageFrame,
|
|
2224
|
+
{
|
|
2225
|
+
breadcrumbs,
|
|
2226
|
+
width,
|
|
2227
|
+
fill,
|
|
2228
|
+
fillPadded,
|
|
2229
|
+
className,
|
|
2230
|
+
...headerProps,
|
|
2231
|
+
children
|
|
2232
|
+
}
|
|
2233
|
+
) });
|
|
1901
2234
|
};
|
|
1902
2235
|
|
|
1903
2236
|
// src/app/layout/Section.tsx
|
|
@@ -1917,15 +2250,61 @@ var Section = ({
|
|
|
1917
2250
|
] });
|
|
1918
2251
|
};
|
|
1919
2252
|
|
|
2253
|
+
// src/app/layout/Stack.tsx
|
|
2254
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
2255
|
+
var GAP_CLASS = {
|
|
2256
|
+
none: "gap-0",
|
|
2257
|
+
xs: "gap-1",
|
|
2258
|
+
sm: "gap-2",
|
|
2259
|
+
md: "gap-4",
|
|
2260
|
+
lg: "gap-6",
|
|
2261
|
+
xl: "gap-8"
|
|
2262
|
+
};
|
|
2263
|
+
var ALIGN_CLASS = {
|
|
2264
|
+
start: "items-start",
|
|
2265
|
+
center: "items-center",
|
|
2266
|
+
end: "items-end",
|
|
2267
|
+
stretch: "items-stretch"
|
|
2268
|
+
};
|
|
2269
|
+
var JUSTIFY_CLASS = {
|
|
2270
|
+
start: "justify-start",
|
|
2271
|
+
center: "justify-center",
|
|
2272
|
+
end: "justify-end",
|
|
2273
|
+
between: "justify-between"
|
|
2274
|
+
};
|
|
2275
|
+
var Stack = ({
|
|
2276
|
+
children,
|
|
2277
|
+
direction = "vertical",
|
|
2278
|
+
gap = "md",
|
|
2279
|
+
align,
|
|
2280
|
+
justify,
|
|
2281
|
+
wrap = false,
|
|
2282
|
+
className
|
|
2283
|
+
}) => /* @__PURE__ */ jsx13(
|
|
2284
|
+
"div",
|
|
2285
|
+
{
|
|
2286
|
+
className: cn(
|
|
2287
|
+
"flex min-w-0",
|
|
2288
|
+
direction === "vertical" ? "flex-col" : "flex-row",
|
|
2289
|
+
GAP_CLASS[gap],
|
|
2290
|
+
align && ALIGN_CLASS[align],
|
|
2291
|
+
justify && JUSTIFY_CLASS[justify],
|
|
2292
|
+
wrap && "flex-wrap",
|
|
2293
|
+
className
|
|
2294
|
+
),
|
|
2295
|
+
children
|
|
2296
|
+
}
|
|
2297
|
+
);
|
|
2298
|
+
|
|
1920
2299
|
// src/app/copilot/app-copilot-context.tsx
|
|
1921
2300
|
import { createContext as createContext4, useContext as useContext4 } from "react";
|
|
1922
|
-
import { jsx as
|
|
2301
|
+
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1923
2302
|
var AppCopilotContext = createContext4(null);
|
|
1924
2303
|
var AppCopilotProvider = ({
|
|
1925
2304
|
value,
|
|
1926
2305
|
children
|
|
1927
2306
|
}) => {
|
|
1928
|
-
return /* @__PURE__ */
|
|
2307
|
+
return /* @__PURE__ */ jsx14(AppCopilotContext.Provider, { value, children });
|
|
1929
2308
|
};
|
|
1930
2309
|
function useAppCopilotContext() {
|
|
1931
2310
|
return useContext4(AppCopilotContext) ?? {};
|
|
@@ -1933,7 +2312,7 @@ function useAppCopilotContext() {
|
|
|
1933
2312
|
|
|
1934
2313
|
// src/app/chat/AppChatPanel.tsx
|
|
1935
2314
|
import { XIcon } from "lucide-react";
|
|
1936
|
-
import { jsx as
|
|
2315
|
+
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1937
2316
|
var shellClass = "aui-app-chat-panel flex h-full min-h-0 flex-col overflow-hidden";
|
|
1938
2317
|
var chromeClass = cn(
|
|
1939
2318
|
"aui-app-chat-panel-chrome relative z-20 flex min-h-10 shrink-0 items-center justify-end px-2 pt-2"
|
|
@@ -1979,17 +2358,17 @@ var AppChatPanel = ({
|
|
|
1979
2358
|
}) => {
|
|
1980
2359
|
const shellChat = useAppShellChat();
|
|
1981
2360
|
return /* @__PURE__ */ jsxs10("div", { className: cn(shellClass, className), children: [
|
|
1982
|
-
shellChat?.collapsible ? /* @__PURE__ */
|
|
2361
|
+
shellChat?.collapsible ? /* @__PURE__ */ jsx15("div", { className: chromeClass, children: /* @__PURE__ */ jsx15(
|
|
1983
2362
|
"button",
|
|
1984
2363
|
{
|
|
1985
2364
|
type: "button",
|
|
1986
2365
|
className: closeButtonClass,
|
|
1987
2366
|
onClick: () => shellChat.setOpen(false),
|
|
1988
2367
|
"aria-label": "Close assistant",
|
|
1989
|
-
children: /* @__PURE__ */
|
|
2368
|
+
children: /* @__PURE__ */ jsx15(XIcon, { className: "size-4", "aria-hidden": true })
|
|
1990
2369
|
}
|
|
1991
2370
|
) }) : null,
|
|
1992
|
-
/* @__PURE__ */
|
|
2371
|
+
/* @__PURE__ */ jsx15("div", { className: bodyClass, children: /* @__PURE__ */ jsx15(
|
|
1993
2372
|
TimbalRuntimeProvider,
|
|
1994
2373
|
{
|
|
1995
2374
|
workforceId,
|
|
@@ -1999,7 +2378,7 @@ var AppChatPanel = ({
|
|
|
1999
2378
|
attachmentsUploadUrl,
|
|
2000
2379
|
attachmentsAccept,
|
|
2001
2380
|
debug,
|
|
2002
|
-
children: /* @__PURE__ */
|
|
2381
|
+
children: /* @__PURE__ */ jsx15(
|
|
2003
2382
|
Thread,
|
|
2004
2383
|
{
|
|
2005
2384
|
variant: "panel",
|
|
@@ -2020,41 +2399,121 @@ var AppChatPanel = ({
|
|
|
2020
2399
|
};
|
|
2021
2400
|
|
|
2022
2401
|
// src/app/surfaces/SurfaceCard.tsx
|
|
2023
|
-
import { jsx as
|
|
2024
|
-
var
|
|
2402
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
2403
|
+
var surfaceVariantClass = {
|
|
2404
|
+
default: "",
|
|
2405
|
+
muted: "bg-none bg-muted/40 shadow-none border border-border/20",
|
|
2406
|
+
outline: "bg-none bg-transparent border border-border shadow-none",
|
|
2407
|
+
elevated: "shadow-card-elevated border border-border/80",
|
|
2408
|
+
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",
|
|
2409
|
+
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"
|
|
2410
|
+
};
|
|
2411
|
+
var surfaceToneClass = {
|
|
2412
|
+
default: "",
|
|
2413
|
+
primary: "ring-1 ring-inset ring-primary/25",
|
|
2414
|
+
success: "ring-1 ring-inset ring-emerald-500/25",
|
|
2415
|
+
warn: "ring-1 ring-inset ring-amber-500/30",
|
|
2416
|
+
danger: "ring-1 ring-inset ring-destructive/25"
|
|
2417
|
+
};
|
|
2418
|
+
var SurfaceCard = ({
|
|
2419
|
+
children,
|
|
2420
|
+
variant = "default",
|
|
2421
|
+
tone = "default",
|
|
2422
|
+
hoverable,
|
|
2423
|
+
onClick,
|
|
2424
|
+
ariaLabel,
|
|
2425
|
+
className
|
|
2426
|
+
}) => {
|
|
2025
2427
|
const surfaceCardClass = useAppDensityClass("surfaceCard");
|
|
2026
|
-
|
|
2428
|
+
const isInteractive = Boolean(onClick);
|
|
2429
|
+
const shouldHover = hoverable ?? isInteractive;
|
|
2430
|
+
const baseClass = cn(
|
|
2431
|
+
"aui-app-surface-card",
|
|
2432
|
+
surfaceCardClass,
|
|
2433
|
+
surfaceVariantClass[variant],
|
|
2434
|
+
surfaceToneClass[tone],
|
|
2435
|
+
shouldHover && "transition-all duration-200 hover:border-border/80 hover:shadow-md",
|
|
2436
|
+
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",
|
|
2437
|
+
className
|
|
2438
|
+
);
|
|
2439
|
+
if (isInteractive) {
|
|
2440
|
+
return /* @__PURE__ */ jsx16(
|
|
2441
|
+
"button",
|
|
2442
|
+
{
|
|
2443
|
+
type: "button",
|
|
2444
|
+
onClick,
|
|
2445
|
+
"aria-label": ariaLabel,
|
|
2446
|
+
className: baseClass,
|
|
2447
|
+
children
|
|
2448
|
+
}
|
|
2449
|
+
);
|
|
2450
|
+
}
|
|
2451
|
+
return /* @__PURE__ */ jsx16("div", { className: baseClass, children });
|
|
2027
2452
|
};
|
|
2028
2453
|
|
|
2029
2454
|
// src/app/surfaces/StatTile.tsx
|
|
2030
|
-
import { jsx as
|
|
2031
|
-
var
|
|
2455
|
+
import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2456
|
+
var statValueToneClass = {
|
|
2457
|
+
default: "",
|
|
2458
|
+
primary: "text-primary",
|
|
2459
|
+
success: "text-emerald-700 dark:text-emerald-400",
|
|
2460
|
+
warn: "text-amber-700 dark:text-amber-400",
|
|
2461
|
+
danger: "text-destructive"
|
|
2462
|
+
};
|
|
2463
|
+
var StatTile = ({
|
|
2464
|
+
label,
|
|
2465
|
+
value,
|
|
2466
|
+
hint,
|
|
2467
|
+
tone = "default",
|
|
2468
|
+
className
|
|
2469
|
+
}) => {
|
|
2032
2470
|
const statTileClass = useAppDensityClass("statTile");
|
|
2033
2471
|
return /* @__PURE__ */ jsxs11("div", { className: cn("aui-app-stat-tile", statTileClass, className), children: [
|
|
2034
|
-
/* @__PURE__ */
|
|
2035
|
-
/* @__PURE__ */
|
|
2036
|
-
hint ? /* @__PURE__ */
|
|
2472
|
+
/* @__PURE__ */ jsx17("span", { className: appStatLabelClass, children: label }),
|
|
2473
|
+
/* @__PURE__ */ jsx17("span", { className: cn(appStatValueClass, statValueToneClass[tone]), children: value }),
|
|
2474
|
+
hint ? /* @__PURE__ */ jsx17("span", { className: "text-xs text-muted-foreground", children: hint }) : null
|
|
2037
2475
|
] });
|
|
2038
2476
|
};
|
|
2039
2477
|
|
|
2040
2478
|
// src/app/surfaces/EmptyState.tsx
|
|
2041
|
-
import { jsx as
|
|
2479
|
+
import { jsx as jsx18, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2042
2480
|
var EmptyState = ({
|
|
2043
2481
|
title,
|
|
2044
2482
|
description,
|
|
2045
2483
|
action,
|
|
2046
|
-
className
|
|
2484
|
+
className,
|
|
2485
|
+
variant = "default"
|
|
2047
2486
|
}) => {
|
|
2048
|
-
const
|
|
2049
|
-
|
|
2050
|
-
/* @__PURE__ */
|
|
2051
|
-
|
|
2052
|
-
|
|
2487
|
+
const densityClass = useAppDensityClass("emptyState");
|
|
2488
|
+
if (variant === "layered") {
|
|
2489
|
+
return /* @__PURE__ */ jsx18(
|
|
2490
|
+
"div",
|
|
2491
|
+
{
|
|
2492
|
+
className: cn(
|
|
2493
|
+
"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",
|
|
2494
|
+
className
|
|
2495
|
+
),
|
|
2496
|
+
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: [
|
|
2497
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-1.5 items-center justify-center", children: [
|
|
2498
|
+
/* @__PURE__ */ jsx18("h3", { className: "text-base font-semibold text-foreground tracking-tight", children: title }),
|
|
2499
|
+
description ? /* @__PURE__ */ jsx18("p", { className: "max-w-sm text-sm text-muted-foreground leading-relaxed", children: description }) : null
|
|
2500
|
+
] }),
|
|
2501
|
+
action ? /* @__PURE__ */ jsx18("div", { className: "mt-1", children: action }) : null
|
|
2502
|
+
] })
|
|
2503
|
+
}
|
|
2504
|
+
);
|
|
2505
|
+
}
|
|
2506
|
+
const isCompact = variant === "compact";
|
|
2507
|
+
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);
|
|
2508
|
+
return /* @__PURE__ */ jsxs12("div", { className: cn(finalClass, className), children: [
|
|
2509
|
+
/* @__PURE__ */ jsx18("p", { className: cn(appEmptyStateTitleClass, "font-semibold tracking-tight"), children: title }),
|
|
2510
|
+
description ? /* @__PURE__ */ jsx18("p", { className: appEmptyStateDescriptionClass, children: description }) : null,
|
|
2511
|
+
action ? /* @__PURE__ */ jsx18("div", { className: "mt-1", children: action }) : null
|
|
2053
2512
|
] });
|
|
2054
2513
|
};
|
|
2055
2514
|
|
|
2056
2515
|
// src/app/surfaces/StatusBadge.tsx
|
|
2057
|
-
import { jsx as
|
|
2516
|
+
import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2058
2517
|
var statusBadgeToneClass = {
|
|
2059
2518
|
default: "bg-muted text-foreground ring-border",
|
|
2060
2519
|
primary: "bg-primary/10 text-primary ring-primary/20",
|
|
@@ -2087,7 +2546,7 @@ var StatusBadge = ({
|
|
|
2087
2546
|
className
|
|
2088
2547
|
),
|
|
2089
2548
|
children: [
|
|
2090
|
-
dot ? /* @__PURE__ */
|
|
2549
|
+
dot ? /* @__PURE__ */ jsx19(
|
|
2091
2550
|
"span",
|
|
2092
2551
|
{
|
|
2093
2552
|
"aria-hidden": true,
|
|
@@ -2104,7 +2563,7 @@ var StatusBadge = ({
|
|
|
2104
2563
|
};
|
|
2105
2564
|
|
|
2106
2565
|
// src/app/surfaces/AppConfirmDialog.tsx
|
|
2107
|
-
import { jsx as
|
|
2566
|
+
import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2108
2567
|
var bodyClass2 = "flex flex-col gap-4 p-6";
|
|
2109
2568
|
var titleClass = "pr-8";
|
|
2110
2569
|
var actionsClass = "flex flex-wrap justify-end gap-2";
|
|
@@ -2119,15 +2578,15 @@ var AppConfirmDialog = ({
|
|
|
2119
2578
|
destructive = false,
|
|
2120
2579
|
className
|
|
2121
2580
|
}) => {
|
|
2122
|
-
return /* @__PURE__ */
|
|
2581
|
+
return /* @__PURE__ */ jsx20(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx20(
|
|
2123
2582
|
DialogContent,
|
|
2124
2583
|
{
|
|
2125
2584
|
className: cn("gap-0 p-0 sm:max-w-md", className),
|
|
2126
2585
|
children: /* @__PURE__ */ jsxs14("div", { className: bodyClass2, children: [
|
|
2127
|
-
/* @__PURE__ */
|
|
2128
|
-
description ? /* @__PURE__ */
|
|
2586
|
+
/* @__PURE__ */ jsx20(DialogTitle, { className: titleClass, children: title }),
|
|
2587
|
+
description ? /* @__PURE__ */ jsx20("p", { className: "text-sm text-muted-foreground", children: description }) : null,
|
|
2129
2588
|
/* @__PURE__ */ jsxs14("div", { className: actionsClass, children: [
|
|
2130
|
-
/* @__PURE__ */
|
|
2589
|
+
/* @__PURE__ */ jsx20(
|
|
2131
2590
|
TimbalV2Button,
|
|
2132
2591
|
{
|
|
2133
2592
|
type: "button",
|
|
@@ -2137,7 +2596,7 @@ var AppConfirmDialog = ({
|
|
|
2137
2596
|
children: cancelLabel
|
|
2138
2597
|
}
|
|
2139
2598
|
),
|
|
2140
|
-
/* @__PURE__ */
|
|
2599
|
+
/* @__PURE__ */ jsx20(
|
|
2141
2600
|
TimbalV2Button,
|
|
2142
2601
|
{
|
|
2143
2602
|
type: "button",
|
|
@@ -2157,7 +2616,7 @@ var AppConfirmDialog = ({
|
|
|
2157
2616
|
};
|
|
2158
2617
|
|
|
2159
2618
|
// src/app/surfaces/InfoCard.tsx
|
|
2160
|
-
import { jsx as
|
|
2619
|
+
import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2161
2620
|
var toneClass = {
|
|
2162
2621
|
neutral: "border-border bg-muted/40",
|
|
2163
2622
|
info: "border-primary/25 bg-primary/5",
|
|
@@ -2165,34 +2624,67 @@ var toneClass = {
|
|
|
2165
2624
|
warn: "border-amber-500/25 bg-amber-500/5",
|
|
2166
2625
|
danger: "border-destructive/25 bg-destructive/5"
|
|
2167
2626
|
};
|
|
2627
|
+
var toneVerticalClass = {
|
|
2628
|
+
neutral: "border-border/60 bg-card hover:border-border dark:bg-card/40",
|
|
2629
|
+
info: "border-primary/20 bg-primary/5 hover:border-primary/45",
|
|
2630
|
+
success: "border-emerald-500/20 bg-emerald-500/5 hover:border-emerald-500/45",
|
|
2631
|
+
warn: "border-amber-500/25 bg-amber-500/5 hover:border-amber-500/45",
|
|
2632
|
+
danger: "border-destructive/20 bg-destructive/5 hover:border-destructive/45"
|
|
2633
|
+
};
|
|
2168
2634
|
var InfoCard = ({
|
|
2169
2635
|
title,
|
|
2170
2636
|
children,
|
|
2171
2637
|
icon,
|
|
2172
2638
|
action,
|
|
2173
2639
|
tone = "neutral",
|
|
2640
|
+
layout = "horizontal",
|
|
2641
|
+
onClick,
|
|
2642
|
+
ariaLabel,
|
|
2174
2643
|
className
|
|
2175
|
-
}) =>
|
|
2176
|
-
|
|
2177
|
-
{
|
|
2178
|
-
|
|
2179
|
-
"flex items-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
children: [
|
|
2184
|
-
icon ? /* @__PURE__ */ jsx20("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: icon }) : null,
|
|
2185
|
-
/* @__PURE__ */ jsxs15("div", { className: "min-w-0 flex-1", children: [
|
|
2186
|
-
title ? /* @__PURE__ */ jsx20("p", { className: "text-sm font-medium text-foreground", children: title }) : null,
|
|
2187
|
-
children ? /* @__PURE__ */ jsx20("div", { className: cn("text-sm text-muted-foreground", title && "mt-1"), children }) : null
|
|
2644
|
+
}) => {
|
|
2645
|
+
const isInteractive = Boolean(onClick);
|
|
2646
|
+
if (layout === "vertical") {
|
|
2647
|
+
const cardContent = /* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
2648
|
+
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,
|
|
2649
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-1.5 w-full text-left", children: [
|
|
2650
|
+
title ? /* @__PURE__ */ jsx21("p", { className: "text-sm font-semibold text-foreground tracking-tight leading-tight", children: title }) : null,
|
|
2651
|
+
children ? /* @__PURE__ */ jsx21("div", { className: "text-xs text-muted-foreground leading-relaxed", children }) : null
|
|
2188
2652
|
] }),
|
|
2189
|
-
action ? /* @__PURE__ */
|
|
2190
|
-
]
|
|
2653
|
+
action ? /* @__PURE__ */ jsx21("div", { className: "w-full mt-1", children: action }) : null
|
|
2654
|
+
] });
|
|
2655
|
+
const baseClass2 = cn(
|
|
2656
|
+
"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)]",
|
|
2657
|
+
toneVerticalClass[tone],
|
|
2658
|
+
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",
|
|
2659
|
+
className
|
|
2660
|
+
);
|
|
2661
|
+
if (isInteractive) {
|
|
2662
|
+
return /* @__PURE__ */ jsx21("button", { type: "button", onClick, "aria-label": ariaLabel, className: baseClass2, children: cardContent });
|
|
2663
|
+
}
|
|
2664
|
+
return /* @__PURE__ */ jsx21("div", { className: baseClass2, children: cardContent });
|
|
2191
2665
|
}
|
|
2192
|
-
|
|
2666
|
+
const horizontalContent = /* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
2667
|
+
icon ? /* @__PURE__ */ jsx21("span", { className: "mt-0.5 shrink-0 text-muted-foreground", children: icon }) : null,
|
|
2668
|
+
/* @__PURE__ */ jsxs15("div", { className: "min-w-0 flex-1 text-left", children: [
|
|
2669
|
+
title ? /* @__PURE__ */ jsx21("p", { className: "text-sm font-medium text-foreground", children: title }) : null,
|
|
2670
|
+
children ? /* @__PURE__ */ jsx21("div", { className: cn("text-sm text-muted-foreground", title && "mt-1"), children }) : null
|
|
2671
|
+
] }),
|
|
2672
|
+
action ? /* @__PURE__ */ jsx21("div", { className: "shrink-0", children: action }) : null
|
|
2673
|
+
] });
|
|
2674
|
+
const baseClass = cn(
|
|
2675
|
+
"flex items-start gap-3 rounded-xl border p-4 transition-all duration-200",
|
|
2676
|
+
toneClass[tone],
|
|
2677
|
+
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",
|
|
2678
|
+
className
|
|
2679
|
+
);
|
|
2680
|
+
if (isInteractive) {
|
|
2681
|
+
return /* @__PURE__ */ jsx21("button", { type: "button", onClick, "aria-label": ariaLabel, className: baseClass, children: horizontalContent });
|
|
2682
|
+
}
|
|
2683
|
+
return /* @__PURE__ */ jsx21("div", { className: baseClass, children: horizontalContent });
|
|
2684
|
+
};
|
|
2193
2685
|
|
|
2194
2686
|
// src/app/surfaces/StatusDot.tsx
|
|
2195
|
-
import { jsx as
|
|
2687
|
+
import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2196
2688
|
var dotClass = {
|
|
2197
2689
|
online: "bg-emerald-500",
|
|
2198
2690
|
busy: "bg-amber-500",
|
|
@@ -2207,7 +2699,7 @@ var StatusDot = ({
|
|
|
2207
2699
|
className
|
|
2208
2700
|
}) => /* @__PURE__ */ jsxs16("span", { className: cn("inline-flex items-center gap-1.5", className), children: [
|
|
2209
2701
|
/* @__PURE__ */ jsxs16("span", { className: "relative flex size-2", children: [
|
|
2210
|
-
pulse ? /* @__PURE__ */
|
|
2702
|
+
pulse ? /* @__PURE__ */ jsx22(
|
|
2211
2703
|
"span",
|
|
2212
2704
|
{
|
|
2213
2705
|
className: cn(
|
|
@@ -2216,18 +2708,18 @@ var StatusDot = ({
|
|
|
2216
2708
|
)
|
|
2217
2709
|
}
|
|
2218
2710
|
) : null,
|
|
2219
|
-
/* @__PURE__ */
|
|
2711
|
+
/* @__PURE__ */ jsx22("span", { className: cn("relative inline-flex size-2 rounded-full", dotClass[tone]) })
|
|
2220
2712
|
] }),
|
|
2221
|
-
label ? /* @__PURE__ */
|
|
2713
|
+
label ? /* @__PURE__ */ jsx22("span", { className: "text-xs text-muted-foreground", children: label }) : null
|
|
2222
2714
|
] });
|
|
2223
2715
|
|
|
2224
2716
|
// src/app/surfaces/DescriptionList.tsx
|
|
2225
|
-
import { jsx as
|
|
2717
|
+
import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2226
2718
|
var DescriptionList = ({
|
|
2227
2719
|
items,
|
|
2228
2720
|
stacked = false,
|
|
2229
2721
|
className
|
|
2230
|
-
}) => /* @__PURE__ */
|
|
2722
|
+
}) => /* @__PURE__ */ jsx23(
|
|
2231
2723
|
"dl",
|
|
2232
2724
|
{
|
|
2233
2725
|
className: cn(
|
|
@@ -2239,16 +2731,18 @@ var DescriptionList = ({
|
|
|
2239
2731
|
{
|
|
2240
2732
|
className: cn(
|
|
2241
2733
|
"px-4 py-3",
|
|
2242
|
-
stacked ? "flex flex-col gap-0.5" : "flex items-center justify-between gap-4"
|
|
2734
|
+
stacked ? "flex flex-col gap-0.5" : "flex items-center justify-between gap-4",
|
|
2735
|
+
item.className
|
|
2243
2736
|
),
|
|
2244
2737
|
children: [
|
|
2245
|
-
/* @__PURE__ */
|
|
2246
|
-
/* @__PURE__ */
|
|
2738
|
+
/* @__PURE__ */ jsx23("dt", { className: cn("text-sm text-muted-foreground", item.labelClassName), children: item.label }),
|
|
2739
|
+
/* @__PURE__ */ jsx23(
|
|
2247
2740
|
"dd",
|
|
2248
2741
|
{
|
|
2249
2742
|
className: cn(
|
|
2250
2743
|
"text-sm text-foreground",
|
|
2251
|
-
!stacked && "text-right tabular-nums"
|
|
2744
|
+
!stacked && "text-right tabular-nums",
|
|
2745
|
+
item.valueClassName
|
|
2252
2746
|
),
|
|
2253
2747
|
children: item.value
|
|
2254
2748
|
}
|
|
@@ -2261,10 +2755,10 @@ var DescriptionList = ({
|
|
|
2261
2755
|
);
|
|
2262
2756
|
|
|
2263
2757
|
// src/app/surfaces/ExpandableSection.tsx
|
|
2264
|
-
import { useId, useState as useState2 } from "react";
|
|
2758
|
+
import { useId as useId2, useState as useState2 } from "react";
|
|
2265
2759
|
import { AnimatePresence, motion as motion2, useReducedMotion as useReducedMotion2 } from "motion/react";
|
|
2266
|
-
import { jsx as
|
|
2267
|
-
var Chevron = ({ open }) => /* @__PURE__ */
|
|
2760
|
+
import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2761
|
+
var Chevron = ({ open }) => /* @__PURE__ */ jsx24(
|
|
2268
2762
|
"svg",
|
|
2269
2763
|
{
|
|
2270
2764
|
viewBox: "0 0 24 24",
|
|
@@ -2278,7 +2772,7 @@ var Chevron = ({ open }) => /* @__PURE__ */ jsx23(
|
|
|
2278
2772
|
strokeLinecap: "round",
|
|
2279
2773
|
strokeLinejoin: "round",
|
|
2280
2774
|
"aria-hidden": true,
|
|
2281
|
-
children: /* @__PURE__ */
|
|
2775
|
+
children: /* @__PURE__ */ jsx24("path", { d: "m6 9 6 6 6-6" })
|
|
2282
2776
|
}
|
|
2283
2777
|
);
|
|
2284
2778
|
var ExpandableSection = ({
|
|
@@ -2292,7 +2786,7 @@ var ExpandableSection = ({
|
|
|
2292
2786
|
className
|
|
2293
2787
|
}) => {
|
|
2294
2788
|
const reduceMotion = useReducedMotion2();
|
|
2295
|
-
const panelId =
|
|
2789
|
+
const panelId = useId2();
|
|
2296
2790
|
const [internalOpen, setInternalOpen] = useState2(defaultOpen);
|
|
2297
2791
|
const open = openProp ?? internalOpen;
|
|
2298
2792
|
const toggle = () => {
|
|
@@ -2310,15 +2804,15 @@ var ExpandableSection = ({
|
|
|
2310
2804
|
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",
|
|
2311
2805
|
children: [
|
|
2312
2806
|
/* @__PURE__ */ jsxs18("span", { className: "flex min-w-0 items-center gap-3", children: [
|
|
2313
|
-
icon ? /* @__PURE__ */
|
|
2314
|
-
/* @__PURE__ */
|
|
2315
|
-
count != null ? /* @__PURE__ */
|
|
2807
|
+
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,
|
|
2808
|
+
/* @__PURE__ */ jsx24("span", { className: "truncate text-sm font-medium text-foreground", children: title }),
|
|
2809
|
+
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
|
|
2316
2810
|
] }),
|
|
2317
|
-
/* @__PURE__ */
|
|
2811
|
+
/* @__PURE__ */ jsx24(Chevron, { open })
|
|
2318
2812
|
]
|
|
2319
2813
|
}
|
|
2320
2814
|
),
|
|
2321
|
-
/* @__PURE__ */
|
|
2815
|
+
/* @__PURE__ */ jsx24(AnimatePresence, { initial: false, children: open ? /* @__PURE__ */ jsx24(
|
|
2322
2816
|
motion2.div,
|
|
2323
2817
|
{
|
|
2324
2818
|
id: panelId,
|
|
@@ -2327,7 +2821,7 @@ var ExpandableSection = ({
|
|
|
2327
2821
|
exit: reduceMotion ? void 0 : { height: 0, opacity: 0 },
|
|
2328
2822
|
transition: { duration: 0.2, ease: "easeOut" },
|
|
2329
2823
|
className: "overflow-hidden",
|
|
2330
|
-
children: /* @__PURE__ */
|
|
2824
|
+
children: /* @__PURE__ */ jsx24("div", { className: "bg-muted/20", children })
|
|
2331
2825
|
},
|
|
2332
2826
|
"body"
|
|
2333
2827
|
) : null })
|
|
@@ -2335,19 +2829,14 @@ var ExpandableSection = ({
|
|
|
2335
2829
|
};
|
|
2336
2830
|
|
|
2337
2831
|
// src/app/surfaces/ResourceCard.tsx
|
|
2338
|
-
import { Fragment as
|
|
2339
|
-
var resourceCardShellClass = cn(
|
|
2340
|
-
"flex min-h-[8.5rem] flex-col rounded-2xl p-4 text-left font-normal",
|
|
2341
|
-
TIMBAL_V2_ELEVATED_SURFACE
|
|
2342
|
-
);
|
|
2832
|
+
import { Fragment as Fragment5, jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2343
2833
|
var mediaShellClass = cn(
|
|
2344
2834
|
"flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-xl text-sm font-normal text-foreground",
|
|
2345
2835
|
TIMBAL_V2_LOGO_TILE
|
|
2346
2836
|
);
|
|
2347
2837
|
var resourceCardInteractiveClass = cn(
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
2838
|
+
"flex min-h-[8.5rem] flex-col rounded-2xl p-4 text-left font-normal border border-border shadow-card",
|
|
2839
|
+
TIMBAL_V2_ELEVATED_SURFACE
|
|
2351
2840
|
);
|
|
2352
2841
|
var ResourceCard = ({
|
|
2353
2842
|
title,
|
|
@@ -2358,37 +2847,236 @@ var ResourceCard = ({
|
|
|
2358
2847
|
action,
|
|
2359
2848
|
onClick,
|
|
2360
2849
|
ariaLabel,
|
|
2361
|
-
className
|
|
2850
|
+
className,
|
|
2851
|
+
variant = "flat",
|
|
2852
|
+
avatarShape = "circle"
|
|
2362
2853
|
}) => {
|
|
2363
|
-
const
|
|
2854
|
+
const isInteractive = Boolean(onClick);
|
|
2855
|
+
const shellClass3 = cn(
|
|
2856
|
+
"flex min-h-[8.5rem] flex-col rounded-2xl p-5 text-left font-normal transition-all duration-200",
|
|
2857
|
+
variant === "default" && TIMBAL_V2_ELEVATED_SURFACE,
|
|
2858
|
+
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",
|
|
2859
|
+
variant === "outline" && "border border-border/70 bg-transparent shadow-none hover:bg-muted/5",
|
|
2860
|
+
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"
|
|
2861
|
+
);
|
|
2862
|
+
const dynamicMediaShellClass = cn(
|
|
2863
|
+
"flex size-10 shrink-0 items-center justify-center overflow-hidden text-sm font-normal text-foreground transition-all duration-200",
|
|
2864
|
+
avatarShape === "circle" ? "rounded-full" : "rounded-xl",
|
|
2865
|
+
TIMBAL_V2_LOGO_TILE
|
|
2866
|
+
);
|
|
2867
|
+
const body = /* @__PURE__ */ jsxs19(Fragment5, { children: [
|
|
2364
2868
|
/* @__PURE__ */ jsxs19("div", { className: "flex items-start gap-3", children: [
|
|
2365
|
-
media ? /* @__PURE__ */
|
|
2869
|
+
media ? /* @__PURE__ */ jsx25("span", { className: dynamicMediaShellClass, children: media }) : null,
|
|
2366
2870
|
/* @__PURE__ */ jsxs19("div", { className: "min-w-0 flex-1 pt-0.5", children: [
|
|
2367
|
-
/* @__PURE__ */
|
|
2368
|
-
subtitle ? /* @__PURE__ */
|
|
2871
|
+
/* @__PURE__ */ jsx25("p", { className: "truncate text-sm font-semibold leading-snug text-foreground tracking-tight", children: title }),
|
|
2872
|
+
subtitle ? /* @__PURE__ */ jsx25("p", { className: "mt-1 line-clamp-2 text-xs font-normal text-muted-foreground/90 leading-relaxed", children: subtitle }) : null
|
|
2369
2873
|
] }),
|
|
2370
|
-
badge ? /* @__PURE__ */
|
|
2874
|
+
badge ? /* @__PURE__ */ jsx25("span", { className: "shrink-0 pt-0.5", children: badge }) : null
|
|
2371
2875
|
] }),
|
|
2372
2876
|
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: [
|
|
2373
|
-
/* @__PURE__ */
|
|
2374
|
-
action ? /* @__PURE__ */
|
|
2877
|
+
/* @__PURE__ */ jsx25("span", { className: "min-w-0 truncate", children: footer }),
|
|
2878
|
+
action ? /* @__PURE__ */ jsx25("span", { className: "shrink-0 opacity-90", children: action }) : null
|
|
2879
|
+
] }) : null
|
|
2880
|
+
] });
|
|
2881
|
+
if (onClick) {
|
|
2882
|
+
return /* @__PURE__ */ jsx25(
|
|
2883
|
+
"button",
|
|
2884
|
+
{
|
|
2885
|
+
type: "button",
|
|
2886
|
+
onClick,
|
|
2887
|
+
"aria-label": ariaLabel,
|
|
2888
|
+
className: cn(shellClass3, className),
|
|
2889
|
+
children: body
|
|
2890
|
+
}
|
|
2891
|
+
);
|
|
2892
|
+
}
|
|
2893
|
+
return /* @__PURE__ */ jsx25("article", { className: cn(shellClass3, className), children: body });
|
|
2894
|
+
};
|
|
2895
|
+
|
|
2896
|
+
// src/app/surfaces/AlertCard.tsx
|
|
2897
|
+
import { ChevronRight } from "lucide-react";
|
|
2898
|
+
import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2899
|
+
var alertCardShellClass = cn(
|
|
2900
|
+
"flex flex-col rounded-2xl p-4 text-left font-normal border border-border shadow-card",
|
|
2901
|
+
TIMBAL_V2_ELEVATED_GRADIENT
|
|
2902
|
+
);
|
|
2903
|
+
var alertCardInteractiveClass = cn(
|
|
2904
|
+
"flex flex-col rounded-2xl p-4 text-left font-normal border border-border shadow-card cursor-pointer",
|
|
2905
|
+
TIMBAL_V2_ELEVATED_GRADIENT,
|
|
2906
|
+
"transition-[background-color,box-shadow,border-color] duration-150 ease-in-out",
|
|
2907
|
+
"hover:border-foreground/20",
|
|
2908
|
+
"hover:from-secondary-fill-hover-from hover:to-secondary-fill-hover-to",
|
|
2909
|
+
"active:from-secondary-fill-active-from active:to-secondary-fill-active-to",
|
|
2910
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
2911
|
+
);
|
|
2912
|
+
var AlertCard = ({
|
|
2913
|
+
title,
|
|
2914
|
+
description,
|
|
2915
|
+
category,
|
|
2916
|
+
categoryTone = "default",
|
|
2917
|
+
status,
|
|
2918
|
+
statusTone = "default",
|
|
2919
|
+
action,
|
|
2920
|
+
trailing,
|
|
2921
|
+
onClick,
|
|
2922
|
+
ariaLabel,
|
|
2923
|
+
className
|
|
2924
|
+
}) => {
|
|
2925
|
+
const showTags = Boolean(category || status);
|
|
2926
|
+
const showTrailing = Boolean(trailing || onClick);
|
|
2927
|
+
const bodyContent = /* @__PURE__ */ jsxs20("div", { className: "flex-1 min-w-0 flex flex-col h-full", children: [
|
|
2928
|
+
showTags ? /* @__PURE__ */ jsxs20("div", { className: "flex flex-wrap items-center gap-1.5 mb-2.5", children: [
|
|
2929
|
+
category ? /* @__PURE__ */ jsx26(StatusBadge, { tone: categoryTone, children: category }) : null,
|
|
2930
|
+
status ? /* @__PURE__ */ jsx26(StatusBadge, { tone: statusTone, children: status }) : null
|
|
2931
|
+
] }) : null,
|
|
2932
|
+
/* @__PURE__ */ jsx26("h4", { className: "text-sm font-medium leading-snug text-foreground", children: title }),
|
|
2933
|
+
description ? /* @__PURE__ */ jsx26("p", { className: "mt-1.5 text-xs text-muted-foreground leading-normal", children: description }) : null,
|
|
2934
|
+
action ? /* @__PURE__ */ jsxs20("div", { className: "mt-auto pt-3 text-xs text-muted-foreground/90 leading-normal", children: [
|
|
2935
|
+
/* @__PURE__ */ jsx26("strong", { className: "font-semibold text-foreground/80", children: "Action: " }),
|
|
2936
|
+
action
|
|
2375
2937
|
] }) : null
|
|
2376
2938
|
] });
|
|
2939
|
+
const cardContent = /* @__PURE__ */ jsxs20("div", { className: "flex items-start justify-between gap-4 w-full h-full", children: [
|
|
2940
|
+
bodyContent,
|
|
2941
|
+
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
|
|
2942
|
+
] });
|
|
2943
|
+
if (onClick) {
|
|
2944
|
+
return /* @__PURE__ */ jsx26(
|
|
2945
|
+
"button",
|
|
2946
|
+
{
|
|
2947
|
+
type: "button",
|
|
2948
|
+
onClick,
|
|
2949
|
+
"aria-label": ariaLabel,
|
|
2950
|
+
className: cn(alertCardInteractiveClass, className),
|
|
2951
|
+
children: cardContent
|
|
2952
|
+
}
|
|
2953
|
+
);
|
|
2954
|
+
}
|
|
2955
|
+
return /* @__PURE__ */ jsx26("article", { className: cn(alertCardShellClass, className), children: cardContent });
|
|
2956
|
+
};
|
|
2957
|
+
|
|
2958
|
+
// src/app/surfaces/CatalogCard.tsx
|
|
2959
|
+
import { ExternalLink } from "lucide-react";
|
|
2960
|
+
import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2961
|
+
var catalogCardShellClass = cn(
|
|
2962
|
+
"flex flex-col rounded-2xl border border-border shadow-card overflow-hidden text-left font-normal",
|
|
2963
|
+
TIMBAL_V2_ELEVATED_GRADIENT
|
|
2964
|
+
);
|
|
2965
|
+
var catalogCardInteractiveClass = cn(
|
|
2966
|
+
"flex flex-col rounded-2xl border border-border shadow-card overflow-hidden text-left font-normal cursor-pointer",
|
|
2967
|
+
TIMBAL_V2_ELEVATED_GRADIENT,
|
|
2968
|
+
"transition-[background-color,box-shadow,border-color] duration-150 ease-in-out",
|
|
2969
|
+
"hover:border-foreground/20",
|
|
2970
|
+
"hover:from-secondary-fill-hover-from hover:to-secondary-fill-hover-to",
|
|
2971
|
+
"active:from-secondary-fill-active-from active:to-secondary-fill-active-to",
|
|
2972
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
2973
|
+
);
|
|
2974
|
+
var CatalogCard = ({
|
|
2975
|
+
title,
|
|
2976
|
+
subtitle,
|
|
2977
|
+
logo,
|
|
2978
|
+
href,
|
|
2979
|
+
badge,
|
|
2980
|
+
description,
|
|
2981
|
+
tags,
|
|
2982
|
+
footerLinks,
|
|
2983
|
+
copyValue,
|
|
2984
|
+
copyLabel = "Copy ID",
|
|
2985
|
+
actions,
|
|
2986
|
+
onClick,
|
|
2987
|
+
ariaLabel,
|
|
2988
|
+
className
|
|
2989
|
+
}) => {
|
|
2990
|
+
const showHeaderTags = Boolean(subtitle || badge);
|
|
2991
|
+
const showFooter = Boolean(footerLinks && footerLinks.length > 0 || copyValue || actions);
|
|
2992
|
+
const mainContent = /* @__PURE__ */ jsxs21("div", { className: "flex-1 p-5 flex flex-col h-full min-h-0", children: [
|
|
2993
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex items-start gap-3", children: [
|
|
2994
|
+
logo ? /* @__PURE__ */ jsx27("div", { className: "size-8 shrink-0 flex items-center justify-center overflow-hidden rounded-lg bg-muted/40", children: logo }) : null,
|
|
2995
|
+
/* @__PURE__ */ jsxs21("div", { className: "min-w-0 flex-1 pt-0.5", children: [
|
|
2996
|
+
/* @__PURE__ */ jsx27("div", { className: "flex items-center gap-1.5 flex-wrap", children: href ? /* @__PURE__ */ jsxs21(
|
|
2997
|
+
"a",
|
|
2998
|
+
{
|
|
2999
|
+
href,
|
|
3000
|
+
target: "_blank",
|
|
3001
|
+
rel: "noopener noreferrer",
|
|
3002
|
+
className: "inline-flex items-center gap-1 hover:underline text-sm font-medium leading-snug text-foreground focus-visible:outline-none focus-visible:underline",
|
|
3003
|
+
onClick: (e) => e.stopPropagation(),
|
|
3004
|
+
children: [
|
|
3005
|
+
/* @__PURE__ */ jsx27("span", { className: "truncate", children: title }),
|
|
3006
|
+
/* @__PURE__ */ jsx27(ExternalLink, { className: "size-3.5 shrink-0 text-muted-foreground/60" })
|
|
3007
|
+
]
|
|
3008
|
+
}
|
|
3009
|
+
) : /* @__PURE__ */ jsx27("h4", { className: "text-sm font-medium leading-snug text-foreground truncate", children: title }) }),
|
|
3010
|
+
showHeaderTags ? /* @__PURE__ */ jsxs21("div", { className: "flex flex-wrap items-center gap-2 mt-1", children: [
|
|
3011
|
+
subtitle ? /* @__PURE__ */ jsx27("span", { className: "text-xs font-medium text-muted-foreground", children: subtitle }) : null,
|
|
3012
|
+
badge ? /* @__PURE__ */ jsx27("div", { className: "shrink-0 flex items-center", children: badge }) : null
|
|
3013
|
+
] }) : null
|
|
3014
|
+
] })
|
|
3015
|
+
] }),
|
|
3016
|
+
description ? /* @__PURE__ */ jsx27("p", { className: "mt-3 text-xs leading-relaxed text-muted-foreground line-clamp-3", children: description }) : null,
|
|
3017
|
+
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: [
|
|
3018
|
+
idx > 0 && /* @__PURE__ */ jsx27("span", { className: "mr-2 text-muted-foreground/30", children: "\u2022" }),
|
|
3019
|
+
tag
|
|
3020
|
+
] }, idx)) }) : null
|
|
3021
|
+
] });
|
|
3022
|
+
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: [
|
|
3023
|
+
/* @__PURE__ */ jsx27("div", { className: "flex flex-wrap items-center gap-4", children: footerLinks && footerLinks.length > 0 ? footerLinks.map((link, idx) => /* @__PURE__ */ jsxs21(
|
|
3024
|
+
"a",
|
|
3025
|
+
{
|
|
3026
|
+
href: link.href,
|
|
3027
|
+
target: "_blank",
|
|
3028
|
+
rel: "noopener noreferrer",
|
|
3029
|
+
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",
|
|
3030
|
+
onClick: (e) => e.stopPropagation(),
|
|
3031
|
+
children: [
|
|
3032
|
+
/* @__PURE__ */ jsx27("span", { children: link.label }),
|
|
3033
|
+
/* @__PURE__ */ jsx27(ExternalLink, { className: "size-2.5 shrink-0 text-muted-foreground/50" })
|
|
3034
|
+
]
|
|
3035
|
+
},
|
|
3036
|
+
idx
|
|
3037
|
+
)) : null }),
|
|
3038
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex items-center gap-2", onClick: (e) => e.stopPropagation(), children: [
|
|
3039
|
+
actions ? actions : null,
|
|
3040
|
+
!actions && copyValue ? /* @__PURE__ */ jsx27(
|
|
3041
|
+
CopyButton,
|
|
3042
|
+
{
|
|
3043
|
+
value: copyValue,
|
|
3044
|
+
className: "h-7 text-xs px-2 hover:bg-muted/40 text-muted-foreground/80 hover:text-foreground",
|
|
3045
|
+
children: copyLabel
|
|
3046
|
+
}
|
|
3047
|
+
) : null
|
|
3048
|
+
] })
|
|
3049
|
+
] }) : null;
|
|
2377
3050
|
if (onClick) {
|
|
2378
|
-
return /* @__PURE__ */
|
|
3051
|
+
return /* @__PURE__ */ jsxs21(
|
|
3052
|
+
"button",
|
|
3053
|
+
{
|
|
3054
|
+
type: "button",
|
|
3055
|
+
onClick,
|
|
3056
|
+
"aria-label": ariaLabel,
|
|
3057
|
+
className: cn(catalogCardInteractiveClass, className),
|
|
3058
|
+
children: [
|
|
3059
|
+
mainContent,
|
|
3060
|
+
footerMarkup
|
|
3061
|
+
]
|
|
3062
|
+
}
|
|
3063
|
+
);
|
|
2379
3064
|
}
|
|
2380
|
-
return /* @__PURE__ */
|
|
3065
|
+
return /* @__PURE__ */ jsxs21("article", { className: cn(catalogCardShellClass, className), children: [
|
|
3066
|
+
mainContent,
|
|
3067
|
+
footerMarkup
|
|
3068
|
+
] });
|
|
2381
3069
|
};
|
|
2382
3070
|
|
|
2383
3071
|
// src/app/settings/SettingsSection.tsx
|
|
2384
|
-
import { jsx as
|
|
3072
|
+
import { jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2385
3073
|
var SettingsSectionHeader = ({
|
|
2386
3074
|
title,
|
|
2387
3075
|
description,
|
|
2388
3076
|
className
|
|
2389
|
-
}) => /* @__PURE__ */
|
|
2390
|
-
/* @__PURE__ */
|
|
2391
|
-
description ? /* @__PURE__ */
|
|
3077
|
+
}) => /* @__PURE__ */ jsxs22("div", { className: cn("flex flex-col", className), children: [
|
|
3078
|
+
/* @__PURE__ */ jsx28("h3", { className: "text-[17px] font-medium leading-tight text-foreground", children: title }),
|
|
3079
|
+
description ? /* @__PURE__ */ jsx28("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null
|
|
2392
3080
|
] });
|
|
2393
3081
|
var SettingsSection = ({
|
|
2394
3082
|
title,
|
|
@@ -2397,7 +3085,7 @@ var SettingsSection = ({
|
|
|
2397
3085
|
children,
|
|
2398
3086
|
noBorderTop = false,
|
|
2399
3087
|
className
|
|
2400
|
-
}) => /* @__PURE__ */
|
|
3088
|
+
}) => /* @__PURE__ */ jsxs22(
|
|
2401
3089
|
"section",
|
|
2402
3090
|
{
|
|
2403
3091
|
className: cn(
|
|
@@ -2406,18 +3094,18 @@ var SettingsSection = ({
|
|
|
2406
3094
|
className
|
|
2407
3095
|
),
|
|
2408
3096
|
children: [
|
|
2409
|
-
/* @__PURE__ */
|
|
2410
|
-
/* @__PURE__ */
|
|
2411
|
-
description ? /* @__PURE__ */
|
|
2412
|
-
descriptionFooter ? /* @__PURE__ */
|
|
3097
|
+
/* @__PURE__ */ jsxs22("div", { className: "min-w-0", children: [
|
|
3098
|
+
/* @__PURE__ */ jsx28("h2", { className: "text-sm font-medium text-foreground", children: title }),
|
|
3099
|
+
description ? /* @__PURE__ */ jsx28("p", { className: "mt-1 text-sm text-muted-foreground", children: description }) : null,
|
|
3100
|
+
descriptionFooter ? /* @__PURE__ */ jsx28("div", { className: "mt-3 min-w-0", children: descriptionFooter }) : null
|
|
2413
3101
|
] }),
|
|
2414
|
-
/* @__PURE__ */
|
|
3102
|
+
/* @__PURE__ */ jsx28("div", { className: "min-w-0 space-y-3", children })
|
|
2415
3103
|
]
|
|
2416
3104
|
}
|
|
2417
3105
|
);
|
|
2418
3106
|
|
|
2419
3107
|
// src/app/settings/FieldRow.tsx
|
|
2420
|
-
import { jsx as
|
|
3108
|
+
import { jsx as jsx29, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2421
3109
|
var FieldRow = ({
|
|
2422
3110
|
label,
|
|
2423
3111
|
children,
|
|
@@ -2427,7 +3115,7 @@ var FieldRow = ({
|
|
|
2427
3115
|
className
|
|
2428
3116
|
}) => {
|
|
2429
3117
|
if (inline) {
|
|
2430
|
-
return /* @__PURE__ */
|
|
3118
|
+
return /* @__PURE__ */ jsxs23(
|
|
2431
3119
|
"div",
|
|
2432
3120
|
{
|
|
2433
3121
|
className: cn(
|
|
@@ -2435,8 +3123,8 @@ var FieldRow = ({
|
|
|
2435
3123
|
className
|
|
2436
3124
|
),
|
|
2437
3125
|
children: [
|
|
2438
|
-
/* @__PURE__ */
|
|
2439
|
-
/* @__PURE__ */
|
|
3126
|
+
/* @__PURE__ */ jsxs23("div", { className: "min-w-0", children: [
|
|
3127
|
+
/* @__PURE__ */ jsx29(
|
|
2440
3128
|
"label",
|
|
2441
3129
|
{
|
|
2442
3130
|
htmlFor,
|
|
@@ -2444,25 +3132,25 @@ var FieldRow = ({
|
|
|
2444
3132
|
children: label
|
|
2445
3133
|
}
|
|
2446
3134
|
),
|
|
2447
|
-
description ? /* @__PURE__ */
|
|
3135
|
+
description ? /* @__PURE__ */ jsx29("p", { className: "mt-0.5 text-xs text-muted-foreground", children: description }) : null
|
|
2448
3136
|
] }),
|
|
2449
|
-
/* @__PURE__ */
|
|
3137
|
+
/* @__PURE__ */ jsx29("div", { className: "shrink-0", children })
|
|
2450
3138
|
]
|
|
2451
3139
|
}
|
|
2452
3140
|
);
|
|
2453
3141
|
}
|
|
2454
|
-
return /* @__PURE__ */
|
|
2455
|
-
/* @__PURE__ */
|
|
3142
|
+
return /* @__PURE__ */ jsxs23("div", { className: cn("flex flex-col gap-1.5", className), children: [
|
|
3143
|
+
/* @__PURE__ */ jsx29("label", { htmlFor, className: "text-sm font-medium text-foreground", children: label }),
|
|
2456
3144
|
children,
|
|
2457
|
-
description ? /* @__PURE__ */
|
|
3145
|
+
description ? /* @__PURE__ */ jsx29("p", { className: "text-xs text-muted-foreground", children: description }) : null
|
|
2458
3146
|
] });
|
|
2459
3147
|
};
|
|
2460
3148
|
|
|
2461
3149
|
// src/app/settings/FloatingUnsavedChangesBar.tsx
|
|
2462
|
-
import { useEffect, useState as useState3 } from "react";
|
|
3150
|
+
import { useEffect as useEffect2, useState as useState3 } from "react";
|
|
2463
3151
|
import { createPortal } from "react-dom";
|
|
2464
3152
|
import { AnimatePresence as AnimatePresence2, motion as motion3, useReducedMotion as useReducedMotion3 } from "motion/react";
|
|
2465
|
-
import { jsx as
|
|
3153
|
+
import { jsx as jsx30, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2466
3154
|
var FloatingUnsavedChangesBar = ({
|
|
2467
3155
|
visible,
|
|
2468
3156
|
message = "Unsaved changes",
|
|
@@ -2477,10 +3165,10 @@ var FloatingUnsavedChangesBar = ({
|
|
|
2477
3165
|
}) => {
|
|
2478
3166
|
const reduceMotion = useReducedMotion3();
|
|
2479
3167
|
const [mounted, setMounted] = useState3(false);
|
|
2480
|
-
|
|
3168
|
+
useEffect2(() => setMounted(true), []);
|
|
2481
3169
|
if (!mounted || typeof document === "undefined") return null;
|
|
2482
3170
|
return createPortal(
|
|
2483
|
-
/* @__PURE__ */
|
|
3171
|
+
/* @__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(
|
|
2484
3172
|
motion3.div,
|
|
2485
3173
|
{
|
|
2486
3174
|
role: "region",
|
|
@@ -2494,10 +3182,10 @@ var FloatingUnsavedChangesBar = ({
|
|
|
2494
3182
|
className
|
|
2495
3183
|
),
|
|
2496
3184
|
children: [
|
|
2497
|
-
/* @__PURE__ */
|
|
2498
|
-
/* @__PURE__ */
|
|
2499
|
-
/* @__PURE__ */
|
|
2500
|
-
/* @__PURE__ */
|
|
3185
|
+
/* @__PURE__ */ jsx30("span", { className: "text-sm text-muted-foreground", children: message }),
|
|
3186
|
+
/* @__PURE__ */ jsxs24("span", { className: "flex items-center gap-1.5", children: [
|
|
3187
|
+
/* @__PURE__ */ jsx30(Button, { variant: "ghost", size: "sm", onClick: onDiscard, disabled: isSaving, children: discardLabel }),
|
|
3188
|
+
/* @__PURE__ */ jsx30(Button, { size: "sm", onClick: onSave, disabled: saveDisabled || isSaving, children: isSaving ? "Saving\u2026" : saveLabel })
|
|
2501
3189
|
] })
|
|
2502
3190
|
]
|
|
2503
3191
|
}
|
|
@@ -2507,13 +3195,13 @@ var FloatingUnsavedChangesBar = ({
|
|
|
2507
3195
|
};
|
|
2508
3196
|
|
|
2509
3197
|
// src/app/settings/DangerZone.tsx
|
|
2510
|
-
import { jsx as
|
|
3198
|
+
import { jsx as jsx31, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2511
3199
|
var DangerZoneAction = ({
|
|
2512
3200
|
title,
|
|
2513
3201
|
description,
|
|
2514
3202
|
action,
|
|
2515
3203
|
className
|
|
2516
|
-
}) => /* @__PURE__ */
|
|
3204
|
+
}) => /* @__PURE__ */ jsxs25(
|
|
2517
3205
|
"div",
|
|
2518
3206
|
{
|
|
2519
3207
|
className: cn(
|
|
@@ -2521,11 +3209,11 @@ var DangerZoneAction = ({
|
|
|
2521
3209
|
className
|
|
2522
3210
|
),
|
|
2523
3211
|
children: [
|
|
2524
|
-
/* @__PURE__ */
|
|
2525
|
-
/* @__PURE__ */
|
|
2526
|
-
description ? /* @__PURE__ */
|
|
3212
|
+
/* @__PURE__ */ jsxs25("div", { className: "min-w-0", children: [
|
|
3213
|
+
/* @__PURE__ */ jsx31("p", { className: "text-sm font-medium text-foreground", children: title }),
|
|
3214
|
+
description ? /* @__PURE__ */ jsx31("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
|
|
2527
3215
|
] }),
|
|
2528
|
-
/* @__PURE__ */
|
|
3216
|
+
/* @__PURE__ */ jsx31("div", { className: "shrink-0", children: action })
|
|
2529
3217
|
]
|
|
2530
3218
|
}
|
|
2531
3219
|
);
|
|
@@ -2534,7 +3222,7 @@ var DangerZone = ({
|
|
|
2534
3222
|
description,
|
|
2535
3223
|
children,
|
|
2536
3224
|
className
|
|
2537
|
-
}) => /* @__PURE__ */
|
|
3225
|
+
}) => /* @__PURE__ */ jsxs25(
|
|
2538
3226
|
"section",
|
|
2539
3227
|
{
|
|
2540
3228
|
className: cn(
|
|
@@ -2542,18 +3230,18 @@ var DangerZone = ({
|
|
|
2542
3230
|
className
|
|
2543
3231
|
),
|
|
2544
3232
|
children: [
|
|
2545
|
-
(title || description) && /* @__PURE__ */
|
|
2546
|
-
title ? /* @__PURE__ */
|
|
2547
|
-
description ? /* @__PURE__ */
|
|
3233
|
+
(title || description) && /* @__PURE__ */ jsxs25("header", { className: "border-b border-destructive/20 bg-destructive/5 px-4 py-3", children: [
|
|
3234
|
+
title ? /* @__PURE__ */ jsx31("h3", { className: "text-sm font-semibold text-destructive", children: title }) : null,
|
|
3235
|
+
description ? /* @__PURE__ */ jsx31("p", { className: "mt-0.5 text-sm text-muted-foreground", children: description }) : null
|
|
2548
3236
|
] }),
|
|
2549
|
-
/* @__PURE__ */
|
|
3237
|
+
/* @__PURE__ */ jsx31("div", { className: "divide-y divide-border bg-card", children })
|
|
2550
3238
|
]
|
|
2551
3239
|
}
|
|
2552
3240
|
);
|
|
2553
3241
|
|
|
2554
3242
|
// src/app/integrations/IntegrationCard.tsx
|
|
2555
|
-
import { useId as
|
|
2556
|
-
import { Fragment as
|
|
3243
|
+
import { useId as useId3 } from "react";
|
|
3244
|
+
import { Fragment as Fragment6, jsx as jsx32, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2557
3245
|
var INTEGRATION_CATALOG_CARD_HEIGHT_CLASS = "h-[12.25rem] min-h-[12.25rem] max-h-[12.25rem]";
|
|
2558
3246
|
var statusLabel = {
|
|
2559
3247
|
available: null,
|
|
@@ -2561,14 +3249,14 @@ var statusLabel = {
|
|
|
2561
3249
|
disabled: "Disabled",
|
|
2562
3250
|
locked: "Locked"
|
|
2563
3251
|
};
|
|
2564
|
-
var
|
|
3252
|
+
var catalogCardShellClass2 = cn(
|
|
2565
3253
|
"group relative box-border flex flex-col overflow-hidden rounded-2xl px-4 pb-4 pt-4 text-left font-normal",
|
|
2566
3254
|
INTEGRATION_CATALOG_CARD_HEIGHT_CLASS,
|
|
2567
3255
|
TIMBAL_V2_ELEVATED_SURFACE,
|
|
2568
3256
|
"transition-opacity duration-200 ease-out"
|
|
2569
3257
|
);
|
|
2570
|
-
var
|
|
2571
|
-
|
|
3258
|
+
var catalogCardInteractiveClass2 = cn(
|
|
3259
|
+
catalogCardShellClass2,
|
|
2572
3260
|
"cursor-pointer bg-transparent hover:bg-transparent active:bg-transparent",
|
|
2573
3261
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-foreground/15 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
|
2574
3262
|
);
|
|
@@ -2591,15 +3279,15 @@ var IntegrationCard = ({
|
|
|
2591
3279
|
ariaLabel,
|
|
2592
3280
|
className
|
|
2593
3281
|
}) => {
|
|
2594
|
-
const titleId =
|
|
3282
|
+
const titleId = useId3();
|
|
2595
3283
|
const locked = status === "locked";
|
|
2596
3284
|
const dimmed = status === "disabled" || locked;
|
|
2597
|
-
const body = /* @__PURE__ */
|
|
2598
|
-
/* @__PURE__ */
|
|
2599
|
-
logo ? /* @__PURE__ */
|
|
2600
|
-
/* @__PURE__ */
|
|
2601
|
-
/* @__PURE__ */
|
|
2602
|
-
/* @__PURE__ */
|
|
3285
|
+
const body = /* @__PURE__ */ jsxs26("div", { className: "flex h-full min-h-0 flex-col", children: [
|
|
3286
|
+
/* @__PURE__ */ jsxs26("div", { className: "flex shrink-0 items-start gap-3 pr-2", children: [
|
|
3287
|
+
logo ? /* @__PURE__ */ jsx32("span", { className: logoShellClass, "aria-hidden": Boolean(ariaLabel), children: logo }) : null,
|
|
3288
|
+
/* @__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: [
|
|
3289
|
+
/* @__PURE__ */ jsxs26("div", { className: "min-w-0", children: [
|
|
3290
|
+
/* @__PURE__ */ jsx32(
|
|
2603
3291
|
"h4",
|
|
2604
3292
|
{
|
|
2605
3293
|
id: onClick && !action ? void 0 : titleId,
|
|
@@ -2607,12 +3295,12 @@ var IntegrationCard = ({
|
|
|
2607
3295
|
children: name
|
|
2608
3296
|
}
|
|
2609
3297
|
),
|
|
2610
|
-
statusLabel[status] ? /* @__PURE__ */
|
|
3298
|
+
statusLabel[status] ? /* @__PURE__ */ jsx32("p", { className: "mt-0.5 text-xs text-muted-foreground", children: statusLabel[status] }) : null
|
|
2611
3299
|
] }),
|
|
2612
|
-
badge ? /* @__PURE__ */
|
|
3300
|
+
badge ? /* @__PURE__ */ jsx32("span", { className: "shrink-0", children: badge }) : null
|
|
2613
3301
|
] }) })
|
|
2614
3302
|
] }),
|
|
2615
|
-
description ? /* @__PURE__ */
|
|
3303
|
+
description ? /* @__PURE__ */ jsx32(
|
|
2616
3304
|
"p",
|
|
2617
3305
|
{
|
|
2618
3306
|
className: cn(
|
|
@@ -2622,19 +3310,19 @@ var IntegrationCard = ({
|
|
|
2622
3310
|
children: description
|
|
2623
3311
|
}
|
|
2624
3312
|
) : null,
|
|
2625
|
-
action ? /* @__PURE__ */
|
|
2626
|
-
/* @__PURE__ */
|
|
2627
|
-
/* @__PURE__ */
|
|
3313
|
+
action ? /* @__PURE__ */ jsxs26(Fragment6, { children: [
|
|
3314
|
+
/* @__PURE__ */ jsx32("div", { className: "min-h-0 flex-1", "aria-hidden": true }),
|
|
3315
|
+
/* @__PURE__ */ jsx32("div", { className: "relative mt-3 shrink-0", children: action })
|
|
2628
3316
|
] }) : null
|
|
2629
3317
|
] });
|
|
2630
3318
|
const shellClass3 = cn(
|
|
2631
|
-
|
|
3319
|
+
catalogCardShellClass2,
|
|
2632
3320
|
dimmed && catalogCardMutedClass,
|
|
2633
3321
|
locked && "cursor-default opacity-75",
|
|
2634
3322
|
className
|
|
2635
3323
|
);
|
|
2636
3324
|
if (onClick && !action) {
|
|
2637
|
-
return /* @__PURE__ */
|
|
3325
|
+
return /* @__PURE__ */ jsx32(
|
|
2638
3326
|
"button",
|
|
2639
3327
|
{
|
|
2640
3328
|
type: "button",
|
|
@@ -2642,7 +3330,7 @@ var IntegrationCard = ({
|
|
|
2642
3330
|
disabled: locked,
|
|
2643
3331
|
"aria-label": ariaLabel,
|
|
2644
3332
|
className: cn(
|
|
2645
|
-
|
|
3333
|
+
catalogCardInteractiveClass2,
|
|
2646
3334
|
dimmed && catalogCardMutedClass,
|
|
2647
3335
|
locked && "cursor-default opacity-75",
|
|
2648
3336
|
className
|
|
@@ -2651,12 +3339,12 @@ var IntegrationCard = ({
|
|
|
2651
3339
|
}
|
|
2652
3340
|
);
|
|
2653
3341
|
}
|
|
2654
|
-
return /* @__PURE__ */
|
|
3342
|
+
return /* @__PURE__ */ jsx32("article", { className: shellClass3, "aria-labelledby": titleId, children: body });
|
|
2655
3343
|
};
|
|
2656
3344
|
|
|
2657
3345
|
// src/app/integrations/IntegrationsEmptyState.tsx
|
|
2658
|
-
import { useId as
|
|
2659
|
-
import { jsx as
|
|
3346
|
+
import { useId as useId4 } from "react";
|
|
3347
|
+
import { jsx as jsx33, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2660
3348
|
var IntegrationsEmptyState = ({
|
|
2661
3349
|
title = "No integrations yet",
|
|
2662
3350
|
description = "Connect a provider to start syncing data and powering your workforce.",
|
|
@@ -2664,8 +3352,8 @@ var IntegrationsEmptyState = ({
|
|
|
2664
3352
|
action,
|
|
2665
3353
|
className
|
|
2666
3354
|
}) => {
|
|
2667
|
-
const titleId =
|
|
2668
|
-
return /* @__PURE__ */
|
|
3355
|
+
const titleId = useId4();
|
|
3356
|
+
return /* @__PURE__ */ jsxs27(
|
|
2669
3357
|
"section",
|
|
2670
3358
|
{
|
|
2671
3359
|
className: cn(
|
|
@@ -2675,7 +3363,7 @@ var IntegrationsEmptyState = ({
|
|
|
2675
3363
|
),
|
|
2676
3364
|
"aria-labelledby": titleId,
|
|
2677
3365
|
children: [
|
|
2678
|
-
icon ? /* @__PURE__ */
|
|
3366
|
+
icon ? /* @__PURE__ */ jsx33(
|
|
2679
3367
|
"span",
|
|
2680
3368
|
{
|
|
2681
3369
|
className: cn(
|
|
@@ -2687,21 +3375,21 @@ var IntegrationsEmptyState = ({
|
|
|
2687
3375
|
children: icon
|
|
2688
3376
|
}
|
|
2689
3377
|
) : null,
|
|
2690
|
-
/* @__PURE__ */
|
|
2691
|
-
description ? /* @__PURE__ */
|
|
2692
|
-
action ? /* @__PURE__ */
|
|
3378
|
+
/* @__PURE__ */ jsx33("h3", { id: titleId, className: "text-base font-normal text-foreground", children: title }),
|
|
3379
|
+
description ? /* @__PURE__ */ jsx33("p", { className: "max-w-sm text-sm text-muted-foreground", children: description }) : null,
|
|
3380
|
+
action ? /* @__PURE__ */ jsx33("div", { className: "mt-1", children: action }) : null
|
|
2693
3381
|
]
|
|
2694
3382
|
}
|
|
2695
3383
|
);
|
|
2696
3384
|
};
|
|
2697
3385
|
|
|
2698
3386
|
// src/app/integrations/PlanBadge.tsx
|
|
2699
|
-
import { jsx as
|
|
3387
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
2700
3388
|
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";
|
|
2701
|
-
var PlanBadge = ({ children, className }) => /* @__PURE__ */
|
|
3389
|
+
var PlanBadge = ({ children, className }) => /* @__PURE__ */ jsx34("span", { className: cn(planBadgeClass, className), children });
|
|
2702
3390
|
|
|
2703
3391
|
// src/app/integrations/ConnectionRow.tsx
|
|
2704
|
-
import { Fragment as
|
|
3392
|
+
import { Fragment as Fragment7, jsx as jsx35, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
2705
3393
|
var logoShellClass2 = cn(
|
|
2706
3394
|
"flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-lg",
|
|
2707
3395
|
TIMBAL_V2_LOGO_TILE
|
|
@@ -2716,14 +3404,14 @@ var ConnectionRow = ({
|
|
|
2716
3404
|
ariaLabel,
|
|
2717
3405
|
className
|
|
2718
3406
|
}) => {
|
|
2719
|
-
const inner = /* @__PURE__ */
|
|
2720
|
-
logo ? /* @__PURE__ */
|
|
2721
|
-
/* @__PURE__ */
|
|
2722
|
-
/* @__PURE__ */
|
|
2723
|
-
meta ? /* @__PURE__ */
|
|
3407
|
+
const inner = /* @__PURE__ */ jsxs28(Fragment7, { children: [
|
|
3408
|
+
logo ? /* @__PURE__ */ jsx35("span", { className: logoShellClass2, children: logo }) : null,
|
|
3409
|
+
/* @__PURE__ */ jsxs28("div", { className: "min-w-0 flex-1", children: [
|
|
3410
|
+
/* @__PURE__ */ jsx35("p", { className: "truncate text-sm font-normal text-foreground", children: name }),
|
|
3411
|
+
meta ? /* @__PURE__ */ jsx35("p", { className: "truncate text-xs text-muted-foreground", children: meta }) : null
|
|
2724
3412
|
] }),
|
|
2725
|
-
badge ? /* @__PURE__ */
|
|
2726
|
-
action ? /* @__PURE__ */
|
|
3413
|
+
badge ? /* @__PURE__ */ jsx35("span", { className: "shrink-0", children: badge }) : null,
|
|
3414
|
+
action ? /* @__PURE__ */ jsx35("span", { className: "shrink-0", children: action }) : null
|
|
2727
3415
|
] });
|
|
2728
3416
|
const rowClass2 = cn(
|
|
2729
3417
|
"flex w-full items-center gap-3 px-4 py-3 text-left",
|
|
@@ -2731,7 +3419,7 @@ var ConnectionRow = ({
|
|
|
2731
3419
|
className
|
|
2732
3420
|
);
|
|
2733
3421
|
if (onClick) {
|
|
2734
|
-
return /* @__PURE__ */
|
|
3422
|
+
return /* @__PURE__ */ jsx35(
|
|
2735
3423
|
"button",
|
|
2736
3424
|
{
|
|
2737
3425
|
type: "button",
|
|
@@ -2743,7 +3431,7 @@ var ConnectionRow = ({
|
|
|
2743
3431
|
}
|
|
2744
3432
|
);
|
|
2745
3433
|
}
|
|
2746
|
-
return /* @__PURE__ */
|
|
3434
|
+
return /* @__PURE__ */ jsx35("div", { role: "listitem", className: rowClass2, children: inner });
|
|
2747
3435
|
};
|
|
2748
3436
|
var connectionRowListClass = cn(
|
|
2749
3437
|
"overflow-hidden rounded-2xl",
|
|
@@ -2751,12 +3439,12 @@ var connectionRowListClass = cn(
|
|
|
2751
3439
|
);
|
|
2752
3440
|
|
|
2753
3441
|
// src/app/integrations/ConnectionRowList.tsx
|
|
2754
|
-
import { jsx as
|
|
3442
|
+
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
2755
3443
|
var ConnectionRowList = ({
|
|
2756
3444
|
children,
|
|
2757
3445
|
"aria-label": ariaLabel = "Connected integrations",
|
|
2758
3446
|
className
|
|
2759
|
-
}) => /* @__PURE__ */
|
|
3447
|
+
}) => /* @__PURE__ */ jsx36(
|
|
2760
3448
|
"div",
|
|
2761
3449
|
{
|
|
2762
3450
|
role: "list",
|
|
@@ -2767,7 +3455,7 @@ var ConnectionRowList = ({
|
|
|
2767
3455
|
);
|
|
2768
3456
|
|
|
2769
3457
|
// src/app/navigation/SubNav.tsx
|
|
2770
|
-
import { jsx as
|
|
3458
|
+
import { jsx as jsx37 } from "react/jsx-runtime";
|
|
2771
3459
|
var SubNav = ({
|
|
2772
3460
|
items,
|
|
2773
3461
|
activeId,
|
|
@@ -2776,7 +3464,7 @@ var SubNav = ({
|
|
|
2776
3464
|
"aria-label": ariaLabel = "Section navigation",
|
|
2777
3465
|
layoutId
|
|
2778
3466
|
}) => {
|
|
2779
|
-
return /* @__PURE__ */
|
|
3467
|
+
return /* @__PURE__ */ jsx37("nav", { className: cn("aui-app-sub-nav", className), "aria-label": ariaLabel, children: /* @__PURE__ */ jsx37(
|
|
2780
3468
|
PillSegmentedTabs,
|
|
2781
3469
|
{
|
|
2782
3470
|
value: activeId,
|
|
@@ -2790,13 +3478,13 @@ var SubNav = ({
|
|
|
2790
3478
|
};
|
|
2791
3479
|
|
|
2792
3480
|
// src/app/navigation/Breadcrumbs.tsx
|
|
2793
|
-
import { jsx as
|
|
3481
|
+
import { jsx as jsx38, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2794
3482
|
var Breadcrumbs = ({ items, className }) => {
|
|
2795
|
-
return /* @__PURE__ */
|
|
3483
|
+
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) => {
|
|
2796
3484
|
const isLast = index === items.length - 1;
|
|
2797
|
-
return /* @__PURE__ */
|
|
2798
|
-
index > 0 ? /* @__PURE__ */
|
|
2799
|
-
isLast ? /* @__PURE__ */
|
|
3485
|
+
return /* @__PURE__ */ jsxs29("li", { className: "inline-flex items-center gap-1.5", children: [
|
|
3486
|
+
index > 0 ? /* @__PURE__ */ jsx38("span", { className: "text-muted-foreground/50", "aria-hidden": true, children: "/" }) : null,
|
|
3487
|
+
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(
|
|
2800
3488
|
"button",
|
|
2801
3489
|
{
|
|
2802
3490
|
type: "button",
|
|
@@ -2810,7 +3498,8 @@ var Breadcrumbs = ({ items, className }) => {
|
|
|
2810
3498
|
};
|
|
2811
3499
|
|
|
2812
3500
|
// src/app/forms/Field.tsx
|
|
2813
|
-
import {
|
|
3501
|
+
import { useId as useId5 } from "react";
|
|
3502
|
+
import { jsx as jsx39, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2814
3503
|
var Field = ({
|
|
2815
3504
|
label,
|
|
2816
3505
|
hint,
|
|
@@ -2819,11 +3508,11 @@ var Field = ({
|
|
|
2819
3508
|
className,
|
|
2820
3509
|
htmlFor
|
|
2821
3510
|
}) => {
|
|
2822
|
-
return /* @__PURE__ */
|
|
2823
|
-
/* @__PURE__ */
|
|
3511
|
+
return /* @__PURE__ */ jsxs30("div", { className: cn("aui-app-field", appFieldClass, className), children: [
|
|
3512
|
+
/* @__PURE__ */ jsx39("label", { className: appFieldLabelClass, htmlFor, children: label }),
|
|
2824
3513
|
children,
|
|
2825
|
-
hint && !error ? /* @__PURE__ */
|
|
2826
|
-
error ? /* @__PURE__ */
|
|
3514
|
+
hint && !error ? /* @__PURE__ */ jsx39("p", { className: appFieldHintClass, children: hint }) : null,
|
|
3515
|
+
error ? /* @__PURE__ */ jsx39("p", { className: "text-xs text-destructive", role: "alert", children: error }) : null
|
|
2827
3516
|
] });
|
|
2828
3517
|
};
|
|
2829
3518
|
var FieldInput = ({
|
|
@@ -2835,8 +3524,9 @@ var FieldInput = ({
|
|
|
2835
3524
|
id,
|
|
2836
3525
|
...inputProps
|
|
2837
3526
|
}) => {
|
|
2838
|
-
const
|
|
2839
|
-
|
|
3527
|
+
const autoId = useId5();
|
|
3528
|
+
const inputId = id ?? inputProps.name ?? autoId;
|
|
3529
|
+
return /* @__PURE__ */ jsx39(
|
|
2840
3530
|
Field,
|
|
2841
3531
|
{
|
|
2842
3532
|
label,
|
|
@@ -2844,7 +3534,7 @@ var FieldInput = ({
|
|
|
2844
3534
|
error,
|
|
2845
3535
|
htmlFor: inputId,
|
|
2846
3536
|
className: fieldClassName,
|
|
2847
|
-
children: /* @__PURE__ */
|
|
3537
|
+
children: /* @__PURE__ */ jsx39(
|
|
2848
3538
|
"input",
|
|
2849
3539
|
{
|
|
2850
3540
|
id: inputId,
|
|
@@ -2858,7 +3548,8 @@ var FieldInput = ({
|
|
|
2858
3548
|
};
|
|
2859
3549
|
|
|
2860
3550
|
// src/app/forms/FieldTextarea.tsx
|
|
2861
|
-
import {
|
|
3551
|
+
import { useId as useId6 } from "react";
|
|
3552
|
+
import { jsx as jsx40 } from "react/jsx-runtime";
|
|
2862
3553
|
var textareaClass = cn(
|
|
2863
3554
|
appInputClass,
|
|
2864
3555
|
"min-h-[5.5rem] resize-y py-2.5 leading-relaxed"
|
|
@@ -2872,8 +3563,9 @@ var FieldTextarea = ({
|
|
|
2872
3563
|
id,
|
|
2873
3564
|
...props
|
|
2874
3565
|
}) => {
|
|
2875
|
-
const
|
|
2876
|
-
|
|
3566
|
+
const autoId = useId6();
|
|
3567
|
+
const textareaId = id ?? props.name ?? autoId;
|
|
3568
|
+
return /* @__PURE__ */ jsx40(
|
|
2877
3569
|
Field,
|
|
2878
3570
|
{
|
|
2879
3571
|
label,
|
|
@@ -2881,7 +3573,7 @@ var FieldTextarea = ({
|
|
|
2881
3573
|
error,
|
|
2882
3574
|
htmlFor: textareaId,
|
|
2883
3575
|
className: fieldClassName,
|
|
2884
|
-
children: /* @__PURE__ */
|
|
3576
|
+
children: /* @__PURE__ */ jsx40(
|
|
2885
3577
|
"textarea",
|
|
2886
3578
|
{
|
|
2887
3579
|
id: textareaId,
|
|
@@ -2895,8 +3587,9 @@ var FieldTextarea = ({
|
|
|
2895
3587
|
};
|
|
2896
3588
|
|
|
2897
3589
|
// src/app/forms/FieldSelect.tsx
|
|
3590
|
+
import { useId as useId7 } from "react";
|
|
2898
3591
|
import { ChevronDownIcon } from "lucide-react";
|
|
2899
|
-
import { jsx as
|
|
3592
|
+
import { jsx as jsx41, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
2900
3593
|
var selectWrapClass = "relative";
|
|
2901
3594
|
var selectClass = cn(
|
|
2902
3595
|
appInputClass,
|
|
@@ -2912,8 +3605,9 @@ var FieldSelect = ({
|
|
|
2912
3605
|
id,
|
|
2913
3606
|
...props
|
|
2914
3607
|
}) => {
|
|
2915
|
-
const
|
|
2916
|
-
|
|
3608
|
+
const autoId = useId7();
|
|
3609
|
+
const selectId = id ?? props.name ?? autoId;
|
|
3610
|
+
return /* @__PURE__ */ jsx41(
|
|
2917
3611
|
Field,
|
|
2918
3612
|
{
|
|
2919
3613
|
label,
|
|
@@ -2921,8 +3615,8 @@ var FieldSelect = ({
|
|
|
2921
3615
|
error,
|
|
2922
3616
|
htmlFor: selectId,
|
|
2923
3617
|
className: fieldClassName,
|
|
2924
|
-
children: /* @__PURE__ */
|
|
2925
|
-
/* @__PURE__ */
|
|
3618
|
+
children: /* @__PURE__ */ jsxs31("div", { className: selectWrapClass, children: [
|
|
3619
|
+
/* @__PURE__ */ jsx41(
|
|
2926
3620
|
"select",
|
|
2927
3621
|
{
|
|
2928
3622
|
id: selectId,
|
|
@@ -2932,7 +3626,7 @@ var FieldSelect = ({
|
|
|
2932
3626
|
children
|
|
2933
3627
|
}
|
|
2934
3628
|
),
|
|
2935
|
-
/* @__PURE__ */
|
|
3629
|
+
/* @__PURE__ */ jsx41(
|
|
2936
3630
|
ChevronDownIcon,
|
|
2937
3631
|
{
|
|
2938
3632
|
className: "pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2 text-muted-foreground",
|
|
@@ -2945,7 +3639,8 @@ var FieldSelect = ({
|
|
|
2945
3639
|
};
|
|
2946
3640
|
|
|
2947
3641
|
// src/app/forms/FieldSwitch.tsx
|
|
2948
|
-
import {
|
|
3642
|
+
import { useId as useId8 } from "react";
|
|
3643
|
+
import { jsx as jsx42, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
2949
3644
|
var trackClass = cn(
|
|
2950
3645
|
"relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-[background,box-shadow,border-color] duration-200",
|
|
2951
3646
|
"peer-focus-visible:ring-2 peer-focus-visible:ring-foreground/10",
|
|
@@ -2965,8 +3660,9 @@ var FieldSwitch = ({
|
|
|
2965
3660
|
id,
|
|
2966
3661
|
...props
|
|
2967
3662
|
}) => {
|
|
2968
|
-
const
|
|
2969
|
-
|
|
3663
|
+
const autoId = useId8();
|
|
3664
|
+
const inputId = id ?? props.name ?? autoId;
|
|
3665
|
+
return /* @__PURE__ */ jsxs32(
|
|
2970
3666
|
"label",
|
|
2971
3667
|
{
|
|
2972
3668
|
className: cn(
|
|
@@ -2975,8 +3671,8 @@ var FieldSwitch = ({
|
|
|
2975
3671
|
),
|
|
2976
3672
|
htmlFor: inputId,
|
|
2977
3673
|
children: [
|
|
2978
|
-
/* @__PURE__ */
|
|
2979
|
-
/* @__PURE__ */
|
|
3674
|
+
/* @__PURE__ */ jsxs32("span", { className: "relative mt-0.5", children: [
|
|
3675
|
+
/* @__PURE__ */ jsx42(
|
|
2980
3676
|
"input",
|
|
2981
3677
|
{
|
|
2982
3678
|
id: inputId,
|
|
@@ -2986,11 +3682,11 @@ var FieldSwitch = ({
|
|
|
2986
3682
|
...props
|
|
2987
3683
|
}
|
|
2988
3684
|
),
|
|
2989
|
-
/* @__PURE__ */
|
|
3685
|
+
/* @__PURE__ */ jsx42("span", { className: trackClass, "aria-hidden": true, children: /* @__PURE__ */ jsx42("span", { className: thumbClass }) })
|
|
2990
3686
|
] }),
|
|
2991
|
-
/* @__PURE__ */
|
|
2992
|
-
/* @__PURE__ */
|
|
2993
|
-
description ? /* @__PURE__ */
|
|
3687
|
+
/* @__PURE__ */ jsxs32("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
|
|
3688
|
+
/* @__PURE__ */ jsx42("span", { className: "text-sm font-medium text-foreground", children: label }),
|
|
3689
|
+
description ? /* @__PURE__ */ jsx42("span", { className: "text-xs text-muted-foreground", children: description }) : null
|
|
2994
3690
|
] })
|
|
2995
3691
|
]
|
|
2996
3692
|
}
|
|
@@ -2999,13 +3695,13 @@ var FieldSwitch = ({
|
|
|
2999
3695
|
|
|
3000
3696
|
// src/app/forms/SearchInput.tsx
|
|
3001
3697
|
import { SearchIcon } from "lucide-react";
|
|
3002
|
-
import { jsx as
|
|
3698
|
+
import { jsx as jsx43, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
3003
3699
|
var SearchInput = ({
|
|
3004
3700
|
className,
|
|
3005
3701
|
placeholder = "Search\u2026",
|
|
3006
3702
|
...props
|
|
3007
3703
|
}) => {
|
|
3008
|
-
return /* @__PURE__ */
|
|
3704
|
+
return /* @__PURE__ */ jsxs33(
|
|
3009
3705
|
"label",
|
|
3010
3706
|
{
|
|
3011
3707
|
className: cn(
|
|
@@ -3014,8 +3710,8 @@ var SearchInput = ({
|
|
|
3014
3710
|
className
|
|
3015
3711
|
),
|
|
3016
3712
|
children: [
|
|
3017
|
-
/* @__PURE__ */
|
|
3018
|
-
/* @__PURE__ */
|
|
3713
|
+
/* @__PURE__ */ jsx43(SearchIcon, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": true }),
|
|
3714
|
+
/* @__PURE__ */ jsx43(
|
|
3019
3715
|
"input",
|
|
3020
3716
|
{
|
|
3021
3717
|
type: "search",
|
|
@@ -3030,26 +3726,26 @@ var SearchInput = ({
|
|
|
3030
3726
|
};
|
|
3031
3727
|
|
|
3032
3728
|
// src/app/forms/FormSection.tsx
|
|
3033
|
-
import { jsx as
|
|
3729
|
+
import { jsx as jsx44, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
3034
3730
|
var FormSection = ({ title, children, className }) => {
|
|
3035
3731
|
const density = useAppDensity();
|
|
3036
3732
|
const sectionClass = useAppDensityClass("section");
|
|
3037
|
-
return /* @__PURE__ */
|
|
3733
|
+
return /* @__PURE__ */ jsxs34(
|
|
3038
3734
|
"fieldset",
|
|
3039
3735
|
{
|
|
3040
3736
|
className: cn("aui-app-form-section", sectionClass, "border-0 p-0", className),
|
|
3041
3737
|
children: [
|
|
3042
|
-
title ? /* @__PURE__ */
|
|
3043
|
-
/* @__PURE__ */
|
|
3738
|
+
title ? /* @__PURE__ */ jsx44("legend", { className: cn(appSectionTitleClass, "mb-3 px-0"), children: title }) : null,
|
|
3739
|
+
/* @__PURE__ */ jsx44("div", { className: cn("flex flex-col", density === "compact" ? "gap-2" : "gap-4"), children })
|
|
3044
3740
|
]
|
|
3045
3741
|
}
|
|
3046
3742
|
);
|
|
3047
3743
|
};
|
|
3048
3744
|
|
|
3049
3745
|
// src/app/data/FilterBar.tsx
|
|
3050
|
-
import { jsx as
|
|
3746
|
+
import { jsx as jsx45 } from "react/jsx-runtime";
|
|
3051
3747
|
var FilterBar = ({ children, className }) => {
|
|
3052
|
-
return /* @__PURE__ */
|
|
3748
|
+
return /* @__PURE__ */ jsx45(
|
|
3053
3749
|
"div",
|
|
3054
3750
|
{
|
|
3055
3751
|
className: cn("aui-app-filter-bar", appFilterBarClass, className),
|
|
@@ -3061,41 +3757,539 @@ var FilterBar = ({ children, className }) => {
|
|
|
3061
3757
|
};
|
|
3062
3758
|
|
|
3063
3759
|
// src/app/data/FilterField.tsx
|
|
3064
|
-
import { jsx as
|
|
3760
|
+
import { jsx as jsx46, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
3065
3761
|
var FilterField = ({
|
|
3066
3762
|
label,
|
|
3067
3763
|
children,
|
|
3068
3764
|
className
|
|
3069
3765
|
}) => {
|
|
3070
|
-
return /* @__PURE__ */
|
|
3071
|
-
label ? /* @__PURE__ */
|
|
3766
|
+
return /* @__PURE__ */ jsxs35("div", { className: cn("aui-app-filter-field", appFieldClass, className), children: [
|
|
3767
|
+
label ? /* @__PURE__ */ jsx46("span", { className: appFieldLabelClass, children: label }) : null,
|
|
3072
3768
|
children
|
|
3073
3769
|
] });
|
|
3074
3770
|
};
|
|
3075
3771
|
|
|
3772
|
+
// src/app/data/FilterDropdown.tsx
|
|
3773
|
+
import { useState as useState4, useMemo as useMemo2, useEffect as useEffect3 } from "react";
|
|
3774
|
+
import {
|
|
3775
|
+
CalendarIcon,
|
|
3776
|
+
ChevronDownIcon as ChevronDownIcon2,
|
|
3777
|
+
ChevronRightIcon,
|
|
3778
|
+
CircleDollarSignIcon,
|
|
3779
|
+
ListFilterIcon,
|
|
3780
|
+
SearchIcon as SearchIcon2,
|
|
3781
|
+
TrendingUpIcon,
|
|
3782
|
+
UserIcon,
|
|
3783
|
+
WalletIcon
|
|
3784
|
+
} from "lucide-react";
|
|
3785
|
+
import { jsx as jsx47, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
3786
|
+
var DEFAULT_CONTACTS = [
|
|
3787
|
+
{ id: "1", name: "Pedro Olivares Sanchez", email: "polivares@timbal.ai", initials: "PE" },
|
|
3788
|
+
{ id: "2", name: "John Doe", email: "john@example.com", initials: "JD" },
|
|
3789
|
+
{ id: "3", name: "Sarah Smith", email: "sarah@example.com", initials: "SS" }
|
|
3790
|
+
];
|
|
3791
|
+
var OPERATORS = [
|
|
3792
|
+
{ id: "greater_than", label: "Greater than..." },
|
|
3793
|
+
{ id: "less_than", label: "Less than..." },
|
|
3794
|
+
{ id: "equals", label: "Equals..." }
|
|
3795
|
+
];
|
|
3796
|
+
function FilterDropdown({
|
|
3797
|
+
filters,
|
|
3798
|
+
onFiltersChange,
|
|
3799
|
+
initialFilters,
|
|
3800
|
+
contacts = DEFAULT_CONTACTS,
|
|
3801
|
+
className
|
|
3802
|
+
}) {
|
|
3803
|
+
const [isOpen, setIsOpen] = useState4(false);
|
|
3804
|
+
const [activeMenu, setActiveMenu] = useState4("contact");
|
|
3805
|
+
const [isMobile, setIsMobile] = useState4(false);
|
|
3806
|
+
useEffect3(() => {
|
|
3807
|
+
const checkMobile = () => setIsMobile(window.innerWidth < 768);
|
|
3808
|
+
checkMobile();
|
|
3809
|
+
window.addEventListener("resize", checkMobile);
|
|
3810
|
+
return () => window.removeEventListener("resize", checkMobile);
|
|
3811
|
+
}, []);
|
|
3812
|
+
const [selectedContacts, setSelectedContacts] = useState4(
|
|
3813
|
+
filters?.contacts ?? initialFilters?.contacts ?? []
|
|
3814
|
+
);
|
|
3815
|
+
const [walletInput, setWalletInput] = useState4(filters?.walletAddress ?? initialFilters?.walletAddress ?? "");
|
|
3816
|
+
const [appliedWallet, setAppliedWallet] = useState4(filters?.walletAddress ?? initialFilters?.walletAddress ?? "");
|
|
3817
|
+
const [selectedDatePreset, setSelectedDatePreset] = useState4(
|
|
3818
|
+
filters?.lastInvoiceDate ?? initialFilters?.lastInvoiceDate ?? null
|
|
3819
|
+
);
|
|
3820
|
+
const [customDateFrom, setCustomDateFrom] = useState4(
|
|
3821
|
+
filters?.customDateRange?.from ?? initialFilters?.customDateRange?.from ?? ""
|
|
3822
|
+
);
|
|
3823
|
+
const [customDateTo, setCustomDateTo] = useState4(
|
|
3824
|
+
filters?.customDateRange?.to ?? initialFilters?.customDateRange?.to ?? ""
|
|
3825
|
+
);
|
|
3826
|
+
const [ltvOperator, setLtvOperator] = useState4(
|
|
3827
|
+
filters?.lifetimeValue?.operator ?? initialFilters?.lifetimeValue?.operator ?? "greater_than"
|
|
3828
|
+
);
|
|
3829
|
+
const [ltvValue, setLtvValue] = useState4(filters?.lifetimeValue?.value ?? initialFilters?.lifetimeValue?.value ?? "");
|
|
3830
|
+
const [isLtvOperatorOpen, setLtvOperatorOpen] = useState4(false);
|
|
3831
|
+
const [outstandingOperator, setOutstandingOperator] = useState4(
|
|
3832
|
+
filters?.outstanding?.operator ?? initialFilters?.outstanding?.operator ?? "greater_than"
|
|
3833
|
+
);
|
|
3834
|
+
const [outstandingValue, setOutstandingValue] = useState4(filters?.outstanding?.value ?? initialFilters?.outstanding?.value ?? "");
|
|
3835
|
+
const [isOutstandingOperatorOpen, setOutstandingOperatorOpen] = useState4(false);
|
|
3836
|
+
useEffect3(() => {
|
|
3837
|
+
if (filters) {
|
|
3838
|
+
setSelectedContacts(filters.contacts ?? []);
|
|
3839
|
+
setWalletInput(filters.walletAddress ?? "");
|
|
3840
|
+
setAppliedWallet(filters.walletAddress ?? "");
|
|
3841
|
+
setSelectedDatePreset(filters.lastInvoiceDate ?? null);
|
|
3842
|
+
setCustomDateFrom(filters.customDateRange?.from ?? "");
|
|
3843
|
+
setCustomDateTo(filters.customDateRange?.to ?? "");
|
|
3844
|
+
setLtvOperator(filters.lifetimeValue?.operator ?? "greater_than");
|
|
3845
|
+
setLtvValue(filters.lifetimeValue?.value ?? "");
|
|
3846
|
+
setOutstandingOperator(filters.outstanding?.operator ?? "greater_than");
|
|
3847
|
+
setOutstandingValue(filters.outstanding?.value ?? "");
|
|
3848
|
+
}
|
|
3849
|
+
}, [filters]);
|
|
3850
|
+
const [contactSearch, setContactSearch] = useState4("");
|
|
3851
|
+
const [walletSearch, setWalletSearch] = useState4("");
|
|
3852
|
+
const refDate = useMemo2(() => new Date(2026, 5, 26), []);
|
|
3853
|
+
const presets = useMemo2(() => {
|
|
3854
|
+
const year = refDate.getFullYear();
|
|
3855
|
+
const month = refDate.getMonth();
|
|
3856
|
+
const lastMonthDate = new Date(year, month - 1, 1);
|
|
3857
|
+
const lastMonthLabel = lastMonthDate.toLocaleDateString("en-US", { month: "short", year: "numeric" });
|
|
3858
|
+
const thisMonthLabel = refDate.toLocaleDateString("en-US", { month: "short", year: "numeric" });
|
|
3859
|
+
const thisQuarter = Math.floor(month / 3) + 1;
|
|
3860
|
+
const thisQuarterLabel = `Q${thisQuarter} ${year}`;
|
|
3861
|
+
const lastQuarter = thisQuarter === 1 ? 4 : thisQuarter - 1;
|
|
3862
|
+
const lastQuarterYear = thisQuarter === 1 ? year - 1 : year;
|
|
3863
|
+
const lastQuarterLabel = `Q${lastQuarter} ${lastQuarterYear}`;
|
|
3864
|
+
const thisYearLabel = `${year}`;
|
|
3865
|
+
const last30 = new Date(refDate);
|
|
3866
|
+
last30.setDate(refDate.getDate() - 30);
|
|
3867
|
+
const formatDate = (d) => d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
3868
|
+
const last30Label = `${formatDate(last30)} - ${formatDate(refDate)}`;
|
|
3869
|
+
const last90 = new Date(refDate);
|
|
3870
|
+
last90.setDate(refDate.getDate() - 90);
|
|
3871
|
+
const last90Label = `${formatDate(last90)} - ${formatDate(refDate)}`;
|
|
3872
|
+
return [
|
|
3873
|
+
{ id: "last_month", label: "Last month", date: lastMonthLabel },
|
|
3874
|
+
{ id: "this_month", label: "This month", date: thisMonthLabel },
|
|
3875
|
+
{ id: "this_quarter", label: "This quarter", date: thisQuarterLabel },
|
|
3876
|
+
{ id: "last_quarter", label: "Last quarter", date: lastQuarterLabel },
|
|
3877
|
+
{ id: "this_year", label: "This year", date: thisYearLabel },
|
|
3878
|
+
{ id: "last_30_days", label: "Last 30 days", date: last30Label },
|
|
3879
|
+
{ id: "last_90_days", label: "Last 90 days", date: last90Label },
|
|
3880
|
+
{ id: "custom", label: "Custom range", date: "" }
|
|
3881
|
+
];
|
|
3882
|
+
}, [refDate]);
|
|
3883
|
+
const filteredContacts = useMemo2(() => {
|
|
3884
|
+
if (!contactSearch) return contacts;
|
|
3885
|
+
const query = contactSearch.toLowerCase();
|
|
3886
|
+
return contacts.filter(
|
|
3887
|
+
(c) => c.name.toLowerCase().includes(query) || c.email.toLowerCase().includes(query)
|
|
3888
|
+
);
|
|
3889
|
+
}, [contacts, contactSearch]);
|
|
3890
|
+
const handleContactToggle = (contactName) => {
|
|
3891
|
+
const next = selectedContacts.includes(contactName) ? selectedContacts.filter((name) => name !== contactName) : [...selectedContacts, contactName];
|
|
3892
|
+
setSelectedContacts(next);
|
|
3893
|
+
notifyChanges({ contacts: next });
|
|
3894
|
+
setIsOpen(false);
|
|
3895
|
+
};
|
|
3896
|
+
const handleWalletApply = () => {
|
|
3897
|
+
setAppliedWallet(walletInput);
|
|
3898
|
+
notifyChanges({ walletAddress: walletInput });
|
|
3899
|
+
setIsOpen(false);
|
|
3900
|
+
};
|
|
3901
|
+
const handleWalletClear = () => {
|
|
3902
|
+
setWalletInput("");
|
|
3903
|
+
setAppliedWallet("");
|
|
3904
|
+
notifyChanges({ walletAddress: "" });
|
|
3905
|
+
setIsOpen(false);
|
|
3906
|
+
};
|
|
3907
|
+
const handleDatePresetSelect = (presetId) => {
|
|
3908
|
+
setSelectedDatePreset(presetId);
|
|
3909
|
+
if (presetId !== "custom") {
|
|
3910
|
+
notifyChanges({ lastInvoiceDate: presetId, customDateRange: void 0 });
|
|
3911
|
+
setIsOpen(false);
|
|
3912
|
+
}
|
|
3913
|
+
};
|
|
3914
|
+
const handleCustomDateApply = () => {
|
|
3915
|
+
notifyChanges({
|
|
3916
|
+
lastInvoiceDate: "custom",
|
|
3917
|
+
customDateRange: { from: customDateFrom, to: customDateTo }
|
|
3918
|
+
});
|
|
3919
|
+
setIsOpen(false);
|
|
3920
|
+
};
|
|
3921
|
+
const handleLtvApply = () => {
|
|
3922
|
+
notifyChanges({
|
|
3923
|
+
lifetimeValue: ltvValue ? { operator: ltvOperator, value: ltvValue } : null
|
|
3924
|
+
});
|
|
3925
|
+
setIsOpen(false);
|
|
3926
|
+
};
|
|
3927
|
+
const handleLtvClear = () => {
|
|
3928
|
+
setLtvValue("");
|
|
3929
|
+
notifyChanges({ lifetimeValue: null });
|
|
3930
|
+
setIsOpen(false);
|
|
3931
|
+
};
|
|
3932
|
+
const handleOutstandingApply = () => {
|
|
3933
|
+
notifyChanges({
|
|
3934
|
+
outstanding: outstandingValue ? { operator: outstandingOperator, value: outstandingValue } : null
|
|
3935
|
+
});
|
|
3936
|
+
setIsOpen(false);
|
|
3937
|
+
};
|
|
3938
|
+
const handleOutstandingClear = () => {
|
|
3939
|
+
setOutstandingValue("");
|
|
3940
|
+
notifyChanges({ outstanding: null });
|
|
3941
|
+
setIsOpen(false);
|
|
3942
|
+
};
|
|
3943
|
+
const notifyChanges = (overrides) => {
|
|
3944
|
+
const current = {
|
|
3945
|
+
contacts: selectedContacts,
|
|
3946
|
+
walletAddress: appliedWallet,
|
|
3947
|
+
lastInvoiceDate: selectedDatePreset,
|
|
3948
|
+
customDateRange: customDateFrom || customDateTo ? { from: customDateFrom, to: customDateTo } : void 0,
|
|
3949
|
+
lifetimeValue: ltvValue ? { operator: ltvOperator, value: ltvValue } : null,
|
|
3950
|
+
outstanding: outstandingValue ? { operator: outstandingOperator, value: outstandingValue } : null,
|
|
3951
|
+
...overrides
|
|
3952
|
+
};
|
|
3953
|
+
onFiltersChange?.(current);
|
|
3954
|
+
};
|
|
3955
|
+
const menuItems = [
|
|
3956
|
+
{ id: "contact", label: "Contact", icon: /* @__PURE__ */ jsx47(UserIcon, { className: "size-4" }) },
|
|
3957
|
+
{ id: "wallet", label: "Wallet address", icon: /* @__PURE__ */ jsx47(WalletIcon, { className: "size-4" }) },
|
|
3958
|
+
{ id: "date", label: "Last invoice date", icon: /* @__PURE__ */ jsx47(CalendarIcon, { className: "size-4" }) },
|
|
3959
|
+
{ id: "ltv", label: "Lifetime value", icon: /* @__PURE__ */ jsx47(TrendingUpIcon, { className: "size-4" }) },
|
|
3960
|
+
{ id: "outstanding", label: "Outstanding", icon: /* @__PURE__ */ jsx47(CircleDollarSignIcon, { className: "size-4" }) }
|
|
3961
|
+
];
|
|
3962
|
+
const activeIdx = menuItems.findIndex((item) => item.id === activeMenu);
|
|
3963
|
+
return /* @__PURE__ */ jsx47("div", { className: cn("inline-block", className), children: /* @__PURE__ */ jsxs36(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [
|
|
3964
|
+
/* @__PURE__ */ jsx47(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx47(
|
|
3965
|
+
Button,
|
|
3966
|
+
{
|
|
3967
|
+
variant: "outline",
|
|
3968
|
+
size: "sm",
|
|
3969
|
+
shape: "pill",
|
|
3970
|
+
className: "border-dashed font-medium text-muted-foreground hover:text-foreground",
|
|
3971
|
+
iconLeading: /* @__PURE__ */ jsx47(ListFilterIcon, { className: "size-4" }),
|
|
3972
|
+
children: "Filter"
|
|
3973
|
+
}
|
|
3974
|
+
) }),
|
|
3975
|
+
/* @__PURE__ */ jsx47(
|
|
3976
|
+
PopoverContent,
|
|
3977
|
+
{
|
|
3978
|
+
variant: "list",
|
|
3979
|
+
align: "start",
|
|
3980
|
+
className: "overflow-visible border-none bg-transparent p-0 shadow-none max-w-[calc(100vw-32px)] md:max-w-none",
|
|
3981
|
+
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: [
|
|
3982
|
+
/* @__PURE__ */ jsx47("div", { className: "w-full md:w-56 rounded-xl border border-border bg-popover p-1.5 shadow-lg", children: menuItems.map((item) => {
|
|
3983
|
+
const isActive = activeMenu === item.id;
|
|
3984
|
+
return /* @__PURE__ */ jsxs36(
|
|
3985
|
+
"button",
|
|
3986
|
+
{
|
|
3987
|
+
type: "button",
|
|
3988
|
+
className: cn(
|
|
3989
|
+
"flex w-full items-center justify-between rounded-lg px-3 py-2 text-sm text-left transition-colors outline-none",
|
|
3990
|
+
isActive ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
3991
|
+
),
|
|
3992
|
+
onMouseEnter: () => !isMobile && setActiveMenu(item.id),
|
|
3993
|
+
onClick: () => setActiveMenu(item.id),
|
|
3994
|
+
children: [
|
|
3995
|
+
/* @__PURE__ */ jsxs36("span", { className: "flex items-center gap-2", children: [
|
|
3996
|
+
item.icon,
|
|
3997
|
+
/* @__PURE__ */ jsx47("span", { children: item.label })
|
|
3998
|
+
] }),
|
|
3999
|
+
/* @__PURE__ */ jsx47(ChevronRightIcon, { className: "size-4 text-muted-foreground/50" })
|
|
4000
|
+
]
|
|
4001
|
+
},
|
|
4002
|
+
item.id
|
|
4003
|
+
);
|
|
4004
|
+
}) }),
|
|
4005
|
+
activeMenu && /* @__PURE__ */ jsxs36(
|
|
4006
|
+
"div",
|
|
4007
|
+
{
|
|
4008
|
+
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",
|
|
4009
|
+
style: isMobile ? {} : { top: `${activeIdx * 36 + 6}px` },
|
|
4010
|
+
children: [
|
|
4011
|
+
activeMenu === "contact" && /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2.5", children: [
|
|
4012
|
+
/* @__PURE__ */ jsxs36("div", { className: "relative flex items-center", children: [
|
|
4013
|
+
/* @__PURE__ */ jsx47(SearchIcon2, { className: "absolute left-2.5 size-4 text-muted-foreground/60" }),
|
|
4014
|
+
/* @__PURE__ */ jsx47(
|
|
4015
|
+
"input",
|
|
4016
|
+
{
|
|
4017
|
+
type: "text",
|
|
4018
|
+
placeholder: "Search by name or email...",
|
|
4019
|
+
value: contactSearch,
|
|
4020
|
+
onChange: (e) => setContactSearch(e.target.value),
|
|
4021
|
+
className: "w-full rounded-lg border border-border bg-background py-1.5 pl-8 pr-3 text-sm outline-none placeholder:text-muted-foreground/60 focus:border-border"
|
|
4022
|
+
}
|
|
4023
|
+
)
|
|
4024
|
+
] }),
|
|
4025
|
+
/* @__PURE__ */ jsx47("div", { className: "max-h-48 overflow-y-auto flex flex-col gap-1 pr-1", children: filteredContacts.length === 0 ? /* @__PURE__ */ jsx47("p", { className: "py-4 text-center text-xs text-muted-foreground", children: "No contacts found" }) : filteredContacts.map((contact) => {
|
|
4026
|
+
const isChecked = selectedContacts.includes(contact.name);
|
|
4027
|
+
return /* @__PURE__ */ jsxs36(
|
|
4028
|
+
"label",
|
|
4029
|
+
{
|
|
4030
|
+
className: "flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 hover:bg-muted/50 transition-colors",
|
|
4031
|
+
children: [
|
|
4032
|
+
/* @__PURE__ */ jsx47(
|
|
4033
|
+
Checkbox,
|
|
4034
|
+
{
|
|
4035
|
+
checked: isChecked,
|
|
4036
|
+
onCheckedChange: () => handleContactToggle(contact.name)
|
|
4037
|
+
}
|
|
4038
|
+
),
|
|
4039
|
+
/* @__PURE__ */ jsx47(Avatar, { variant: "secondary", children: /* @__PURE__ */ jsx47(AvatarFallback, { children: contact.initials }) }),
|
|
4040
|
+
/* @__PURE__ */ jsx47("span", { className: "text-sm font-medium text-foreground", children: contact.name })
|
|
4041
|
+
]
|
|
4042
|
+
},
|
|
4043
|
+
contact.id
|
|
4044
|
+
);
|
|
4045
|
+
}) })
|
|
4046
|
+
] }),
|
|
4047
|
+
activeMenu === "wallet" && /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2.5", children: [
|
|
4048
|
+
/* @__PURE__ */ jsx47("div", { className: "relative flex items-center", children: /* @__PURE__ */ jsx47(
|
|
4049
|
+
"input",
|
|
4050
|
+
{
|
|
4051
|
+
type: "text",
|
|
4052
|
+
placeholder: "Search by wallet...",
|
|
4053
|
+
value: walletInput,
|
|
4054
|
+
onChange: (e) => setWalletInput(e.target.value),
|
|
4055
|
+
className: "w-full rounded-lg border border-border bg-background px-3 py-1.5 text-sm outline-none placeholder:text-muted-foreground/60"
|
|
4056
|
+
}
|
|
4057
|
+
) }),
|
|
4058
|
+
/* @__PURE__ */ jsxs36("div", { className: "flex items-center justify-end gap-2 pt-1 border-t border-border/40", children: [
|
|
4059
|
+
/* @__PURE__ */ jsx47(
|
|
4060
|
+
Button,
|
|
4061
|
+
{
|
|
4062
|
+
variant: "ghost",
|
|
4063
|
+
size: "sm",
|
|
4064
|
+
onClick: handleWalletClear,
|
|
4065
|
+
className: "text-muted-foreground hover:text-foreground h-8 px-3",
|
|
4066
|
+
children: "Clear"
|
|
4067
|
+
}
|
|
4068
|
+
),
|
|
4069
|
+
/* @__PURE__ */ jsx47(
|
|
4070
|
+
Button,
|
|
4071
|
+
{
|
|
4072
|
+
color: "primary",
|
|
4073
|
+
size: "sm",
|
|
4074
|
+
onClick: handleWalletApply,
|
|
4075
|
+
className: "h-8 px-3",
|
|
4076
|
+
children: "Apply"
|
|
4077
|
+
}
|
|
4078
|
+
)
|
|
4079
|
+
] })
|
|
4080
|
+
] }),
|
|
4081
|
+
activeMenu === "date" && /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-1", children: [
|
|
4082
|
+
presets.map((preset) => {
|
|
4083
|
+
const isSelected = selectedDatePreset === preset.id;
|
|
4084
|
+
return /* @__PURE__ */ jsxs36(
|
|
4085
|
+
"button",
|
|
4086
|
+
{
|
|
4087
|
+
type: "button",
|
|
4088
|
+
onClick: () => handleDatePresetSelect(preset.id),
|
|
4089
|
+
className: cn(
|
|
4090
|
+
"flex w-full items-center justify-between rounded-lg px-2.5 py-1.5 text-sm text-left transition-colors outline-none",
|
|
4091
|
+
isSelected ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
4092
|
+
),
|
|
4093
|
+
children: [
|
|
4094
|
+
/* @__PURE__ */ jsx47("span", { children: preset.label }),
|
|
4095
|
+
preset.date && /* @__PURE__ */ jsx47("span", { className: "text-xs text-muted-foreground/70", children: preset.date })
|
|
4096
|
+
]
|
|
4097
|
+
},
|
|
4098
|
+
preset.id
|
|
4099
|
+
);
|
|
4100
|
+
}),
|
|
4101
|
+
selectedDatePreset === "custom" && /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2 mt-2 pt-2 border-t border-border/40", children: [
|
|
4102
|
+
/* @__PURE__ */ jsxs36("div", { className: "flex items-center gap-2", children: [
|
|
4103
|
+
/* @__PURE__ */ jsx47(
|
|
4104
|
+
"input",
|
|
4105
|
+
{
|
|
4106
|
+
type: "date",
|
|
4107
|
+
value: customDateFrom,
|
|
4108
|
+
onChange: (e) => setCustomDateFrom(e.target.value),
|
|
4109
|
+
className: "w-full rounded-lg border border-border bg-background px-2 py-1 text-xs outline-none"
|
|
4110
|
+
}
|
|
4111
|
+
),
|
|
4112
|
+
/* @__PURE__ */ jsx47("span", { className: "text-xs text-muted-foreground", children: "to" }),
|
|
4113
|
+
/* @__PURE__ */ jsx47(
|
|
4114
|
+
"input",
|
|
4115
|
+
{
|
|
4116
|
+
type: "date",
|
|
4117
|
+
value: customDateTo,
|
|
4118
|
+
onChange: (e) => setCustomDateTo(e.target.value),
|
|
4119
|
+
className: "w-full rounded-lg border border-border bg-background px-2 py-1 text-xs outline-none"
|
|
4120
|
+
}
|
|
4121
|
+
)
|
|
4122
|
+
] }),
|
|
4123
|
+
/* @__PURE__ */ jsx47("div", { className: "flex justify-end gap-2", children: /* @__PURE__ */ jsx47(
|
|
4124
|
+
Button,
|
|
4125
|
+
{
|
|
4126
|
+
color: "primary",
|
|
4127
|
+
size: "sm",
|
|
4128
|
+
onClick: handleCustomDateApply,
|
|
4129
|
+
className: "h-7 text-xs px-2.5",
|
|
4130
|
+
children: "Apply"
|
|
4131
|
+
}
|
|
4132
|
+
) })
|
|
4133
|
+
] })
|
|
4134
|
+
] }),
|
|
4135
|
+
activeMenu === "ltv" && /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2.5", children: [
|
|
4136
|
+
/* @__PURE__ */ jsxs36("div", { className: "flex items-center gap-2", children: [
|
|
4137
|
+
/* @__PURE__ */ jsxs36("div", { className: "relative shrink-0", children: [
|
|
4138
|
+
/* @__PURE__ */ jsxs36(
|
|
4139
|
+
"button",
|
|
4140
|
+
{
|
|
4141
|
+
type: "button",
|
|
4142
|
+
onClick: () => setLtvOperatorOpen(!isLtvOperatorOpen),
|
|
4143
|
+
className: "flex h-9 items-center gap-1 rounded-lg border border-border bg-background px-2.5 text-xs font-normal text-muted-foreground hover:bg-muted/50 hover:text-foreground outline-none whitespace-nowrap",
|
|
4144
|
+
children: [
|
|
4145
|
+
/* @__PURE__ */ jsx47("span", { children: OPERATORS.find((op) => op.id === ltvOperator)?.label.replace("...", "") }),
|
|
4146
|
+
/* @__PURE__ */ jsx47(ChevronDownIcon2, { className: "size-3" })
|
|
4147
|
+
]
|
|
4148
|
+
}
|
|
4149
|
+
),
|
|
4150
|
+
isLtvOperatorOpen && /* @__PURE__ */ jsx47("div", { className: "absolute left-0 top-full z-50 mt-1 w-32 rounded-lg border border-border bg-popover p-1 shadow-md", children: OPERATORS.map((op) => /* @__PURE__ */ jsx47(
|
|
4151
|
+
"button",
|
|
4152
|
+
{
|
|
4153
|
+
type: "button",
|
|
4154
|
+
onClick: () => {
|
|
4155
|
+
setLtvOperator(op.id);
|
|
4156
|
+
setLtvOperatorOpen(false);
|
|
4157
|
+
},
|
|
4158
|
+
className: "w-full rounded-md px-2 py-1 text-left text-xs text-foreground hover:bg-muted outline-none",
|
|
4159
|
+
children: op.label
|
|
4160
|
+
},
|
|
4161
|
+
op.id
|
|
4162
|
+
)) })
|
|
4163
|
+
] }),
|
|
4164
|
+
/* @__PURE__ */ jsx47(
|
|
4165
|
+
"input",
|
|
4166
|
+
{
|
|
4167
|
+
type: "text",
|
|
4168
|
+
placeholder: "0.00",
|
|
4169
|
+
value: ltvValue,
|
|
4170
|
+
onChange: (e) => setLtvValue(e.target.value),
|
|
4171
|
+
className: "h-9 flex-1 min-w-0 rounded-lg border border-border bg-background px-3 py-1 text-sm outline-none placeholder:text-muted-foreground/60"
|
|
4172
|
+
}
|
|
4173
|
+
)
|
|
4174
|
+
] }),
|
|
4175
|
+
/* @__PURE__ */ jsxs36("div", { className: "flex items-center justify-end gap-2 pt-1 border-t border-border/40", children: [
|
|
4176
|
+
/* @__PURE__ */ jsx47(
|
|
4177
|
+
Button,
|
|
4178
|
+
{
|
|
4179
|
+
variant: "ghost",
|
|
4180
|
+
size: "sm",
|
|
4181
|
+
onClick: handleLtvClear,
|
|
4182
|
+
className: "text-muted-foreground hover:text-foreground h-8 px-3",
|
|
4183
|
+
children: "Clear"
|
|
4184
|
+
}
|
|
4185
|
+
),
|
|
4186
|
+
/* @__PURE__ */ jsx47(
|
|
4187
|
+
Button,
|
|
4188
|
+
{
|
|
4189
|
+
color: "primary",
|
|
4190
|
+
size: "sm",
|
|
4191
|
+
onClick: handleLtvApply,
|
|
4192
|
+
className: "h-8 px-3",
|
|
4193
|
+
children: "Apply"
|
|
4194
|
+
}
|
|
4195
|
+
)
|
|
4196
|
+
] })
|
|
4197
|
+
] }),
|
|
4198
|
+
activeMenu === "outstanding" && /* @__PURE__ */ jsxs36("div", { className: "flex flex-col gap-2.5", children: [
|
|
4199
|
+
/* @__PURE__ */ jsxs36("div", { className: "flex items-center gap-2", children: [
|
|
4200
|
+
/* @__PURE__ */ jsxs36("div", { className: "relative shrink-0", children: [
|
|
4201
|
+
/* @__PURE__ */ jsxs36(
|
|
4202
|
+
"button",
|
|
4203
|
+
{
|
|
4204
|
+
type: "button",
|
|
4205
|
+
onClick: () => setOutstandingOperatorOpen(!isOutstandingOperatorOpen),
|
|
4206
|
+
className: "flex h-9 items-center gap-1 rounded-lg border border-border bg-background px-2.5 text-xs font-normal text-muted-foreground hover:bg-muted/50 hover:text-foreground outline-none whitespace-nowrap",
|
|
4207
|
+
children: [
|
|
4208
|
+
/* @__PURE__ */ jsx47("span", { children: OPERATORS.find((op) => op.id === outstandingOperator)?.label.replace("...", "") }),
|
|
4209
|
+
/* @__PURE__ */ jsx47(ChevronDownIcon2, { className: "size-3" })
|
|
4210
|
+
]
|
|
4211
|
+
}
|
|
4212
|
+
),
|
|
4213
|
+
isOutstandingOperatorOpen && /* @__PURE__ */ jsx47("div", { className: "absolute left-0 top-full z-50 mt-1 w-32 rounded-lg border border-border bg-popover p-1 shadow-md", children: OPERATORS.map((op) => /* @__PURE__ */ jsx47(
|
|
4214
|
+
"button",
|
|
4215
|
+
{
|
|
4216
|
+
type: "button",
|
|
4217
|
+
onClick: () => {
|
|
4218
|
+
setOutstandingOperator(op.id);
|
|
4219
|
+
setOutstandingOperatorOpen(false);
|
|
4220
|
+
},
|
|
4221
|
+
className: "w-full rounded-md px-2 py-1 text-left text-xs text-foreground hover:bg-muted outline-none",
|
|
4222
|
+
children: op.label
|
|
4223
|
+
},
|
|
4224
|
+
op.id
|
|
4225
|
+
)) })
|
|
4226
|
+
] }),
|
|
4227
|
+
/* @__PURE__ */ jsx47(
|
|
4228
|
+
"input",
|
|
4229
|
+
{
|
|
4230
|
+
type: "text",
|
|
4231
|
+
placeholder: "0.00",
|
|
4232
|
+
value: outstandingValue,
|
|
4233
|
+
onChange: (e) => setOutstandingValue(e.target.value),
|
|
4234
|
+
className: "h-9 flex-1 min-w-0 rounded-lg border border-border bg-background px-3 py-1 text-sm outline-none placeholder:text-muted-foreground/60"
|
|
4235
|
+
}
|
|
4236
|
+
)
|
|
4237
|
+
] }),
|
|
4238
|
+
/* @__PURE__ */ jsxs36("div", { className: "flex items-center justify-end gap-2 pt-1 border-t border-border/40", children: [
|
|
4239
|
+
/* @__PURE__ */ jsx47(
|
|
4240
|
+
Button,
|
|
4241
|
+
{
|
|
4242
|
+
variant: "ghost",
|
|
4243
|
+
size: "sm",
|
|
4244
|
+
onClick: handleOutstandingClear,
|
|
4245
|
+
className: "text-muted-foreground hover:text-foreground h-8 px-3",
|
|
4246
|
+
children: "Clear"
|
|
4247
|
+
}
|
|
4248
|
+
),
|
|
4249
|
+
/* @__PURE__ */ jsx47(
|
|
4250
|
+
Button,
|
|
4251
|
+
{
|
|
4252
|
+
color: "primary",
|
|
4253
|
+
size: "sm",
|
|
4254
|
+
onClick: handleOutstandingApply,
|
|
4255
|
+
className: "h-8 px-3",
|
|
4256
|
+
children: "Apply"
|
|
4257
|
+
}
|
|
4258
|
+
)
|
|
4259
|
+
] })
|
|
4260
|
+
] })
|
|
4261
|
+
]
|
|
4262
|
+
}
|
|
4263
|
+
)
|
|
4264
|
+
] })
|
|
4265
|
+
}
|
|
4266
|
+
)
|
|
4267
|
+
] }) });
|
|
4268
|
+
}
|
|
4269
|
+
|
|
3076
4270
|
// src/app/data/DataTable.tsx
|
|
3077
|
-
import { useEffect as
|
|
4271
|
+
import { useEffect as useEffect4, useMemo as useMemo3, useState as useState5 } from "react";
|
|
3078
4272
|
import {
|
|
3079
4273
|
ArrowDownIcon,
|
|
3080
4274
|
ArrowUpDownIcon,
|
|
3081
4275
|
ArrowUpIcon,
|
|
3082
4276
|
ChevronLeftIcon,
|
|
3083
|
-
ChevronRightIcon
|
|
4277
|
+
ChevronRightIcon as ChevronRightIcon2
|
|
3084
4278
|
} from "lucide-react";
|
|
3085
|
-
import { jsx as
|
|
3086
|
-
var shellClass2 = "
|
|
3087
|
-
var tableClass = "w-full border-
|
|
3088
|
-
var headRowClass = "
|
|
3089
|
-
var headCellClass = "border-b border-border
|
|
3090
|
-
var bodyCellClass = "border-b border-border/40 bg-transparent px-4 py-
|
|
3091
|
-
var rowClass = "bg-transparent transition-colors hover:bg-muted/
|
|
3092
|
-
var footCellClass = "border-t border-border/
|
|
4279
|
+
import { jsx as jsx48, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
4280
|
+
var shellClass2 = "w-full";
|
|
4281
|
+
var tableClass = "w-full border-separate border-spacing-0 bg-transparent text-sm";
|
|
4282
|
+
var headRowClass = "";
|
|
4283
|
+
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";
|
|
4284
|
+
var bodyCellClass = "border-b border-border/40 bg-transparent px-4 py-4 text-foreground";
|
|
4285
|
+
var rowClass = "bg-transparent transition-colors hover:bg-muted/20 data-[clickable=true]:cursor-pointer data-[selected=true]:bg-primary/[0.04]";
|
|
4286
|
+
var footCellClass = "border-t border-border/40 bg-transparent px-4 py-3 text-xs text-muted-foreground";
|
|
3093
4287
|
var footInnerClass = "flex flex-wrap items-center gap-2";
|
|
3094
4288
|
var emptyCellClass = "bg-transparent px-4 py-10 text-center text-sm text-muted-foreground";
|
|
3095
|
-
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-
|
|
3096
|
-
var stickyHeadClass = "sticky top-0 z-[1] bg-
|
|
3097
|
-
var selectCellClass = "w-10 px-4 py-
|
|
3098
|
-
var pagerButtonClass = "inline-flex size-7 items-center justify-center rounded-md border border-border bg-
|
|
4289
|
+
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";
|
|
4290
|
+
var stickyHeadClass = "sticky top-0 z-[1] bg-background/95 shadow-[0_1px_0_0_hsl(var(--border)/0.4)] backdrop-blur-sm";
|
|
4291
|
+
var selectCellClass = "w-10 px-4 py-4 align-middle";
|
|
4292
|
+
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";
|
|
3099
4293
|
var alignClass = {
|
|
3100
4294
|
left: "text-left",
|
|
3101
4295
|
center: "text-center",
|
|
@@ -3125,12 +4319,12 @@ function SortIndicator({
|
|
|
3125
4319
|
}) {
|
|
3126
4320
|
const iconClass = "size-3.5 shrink-0 opacity-60 group-hover:opacity-100";
|
|
3127
4321
|
if (!active) {
|
|
3128
|
-
return /* @__PURE__ */
|
|
4322
|
+
return /* @__PURE__ */ jsx48(ArrowUpDownIcon, { className: iconClass, "aria-hidden": true });
|
|
3129
4323
|
}
|
|
3130
4324
|
if (direction === "desc") {
|
|
3131
|
-
return /* @__PURE__ */
|
|
4325
|
+
return /* @__PURE__ */ jsx48(ArrowDownIcon, { className: iconClass, "aria-hidden": true });
|
|
3132
4326
|
}
|
|
3133
|
-
return /* @__PURE__ */
|
|
4327
|
+
return /* @__PURE__ */ jsx48(ArrowUpIcon, { className: iconClass, "aria-hidden": true });
|
|
3134
4328
|
}
|
|
3135
4329
|
function DataTable({
|
|
3136
4330
|
columns,
|
|
@@ -3162,7 +4356,7 @@ function DataTable({
|
|
|
3162
4356
|
defaultPageIndex = 0,
|
|
3163
4357
|
onPageChange
|
|
3164
4358
|
}) {
|
|
3165
|
-
const [uncontrolledSort, setUncontrolledSort] =
|
|
4359
|
+
const [uncontrolledSort, setUncontrolledSort] = useState5(
|
|
3166
4360
|
defaultSort
|
|
3167
4361
|
);
|
|
3168
4362
|
const isSortControlled = sortProp !== void 0;
|
|
@@ -3173,19 +4367,19 @@ function DataTable({
|
|
|
3173
4367
|
}
|
|
3174
4368
|
onSortChange?.(next);
|
|
3175
4369
|
};
|
|
3176
|
-
const [uncontrolledSelected, setUncontrolledSelected] =
|
|
4370
|
+
const [uncontrolledSelected, setUncontrolledSelected] = useState5(
|
|
3177
4371
|
defaultSelectedKeys ?? []
|
|
3178
4372
|
);
|
|
3179
4373
|
const isSelectionControlled = selectedKeysProp !== void 0;
|
|
3180
4374
|
const selectedKeys = isSelectionControlled ? selectedKeysProp : uncontrolledSelected;
|
|
3181
|
-
const selectedSet =
|
|
4375
|
+
const selectedSet = useMemo3(() => new Set(selectedKeys), [selectedKeys]);
|
|
3182
4376
|
const setSelected = (next) => {
|
|
3183
4377
|
if (!isSelectionControlled) {
|
|
3184
4378
|
setUncontrolledSelected(next);
|
|
3185
4379
|
}
|
|
3186
4380
|
onSelectionChange?.(next);
|
|
3187
4381
|
};
|
|
3188
|
-
const [uncontrolledPage, setUncontrolledPage] =
|
|
4382
|
+
const [uncontrolledPage, setUncontrolledPage] = useState5(defaultPageIndex);
|
|
3189
4383
|
const isPageControlled = pageIndexProp !== void 0;
|
|
3190
4384
|
const rawPageIndex = isPageControlled ? pageIndexProp : uncontrolledPage;
|
|
3191
4385
|
const setPage = (next) => {
|
|
@@ -3194,7 +4388,7 @@ function DataTable({
|
|
|
3194
4388
|
}
|
|
3195
4389
|
onPageChange?.(next);
|
|
3196
4390
|
};
|
|
3197
|
-
const sortedRows =
|
|
4391
|
+
const sortedRows = useMemo3(() => {
|
|
3198
4392
|
if (!sort) return rows;
|
|
3199
4393
|
const column = columns.find((col) => col.id === sort.columnId);
|
|
3200
4394
|
if (!column?.sortable) return rows;
|
|
@@ -3213,13 +4407,13 @@ function DataTable({
|
|
|
3213
4407
|
const paginated = typeof pageSize === "number" && pageSize > 0;
|
|
3214
4408
|
const pageCount = paginated ? Math.max(1, Math.ceil(sortedRows.length / pageSize)) : 1;
|
|
3215
4409
|
const pageIndex = Math.min(Math.max(0, rawPageIndex), pageCount - 1);
|
|
3216
|
-
|
|
4410
|
+
useEffect4(() => {
|
|
3217
4411
|
if (!paginated || isPageControlled) return;
|
|
3218
4412
|
if (uncontrolledPage > pageCount - 1) {
|
|
3219
4413
|
setUncontrolledPage(pageCount - 1);
|
|
3220
4414
|
}
|
|
3221
4415
|
}, [paginated, isPageControlled, uncontrolledPage, pageCount]);
|
|
3222
|
-
const visibleRows =
|
|
4416
|
+
const visibleRows = useMemo3(() => {
|
|
3223
4417
|
if (!paginated) return sortedRows;
|
|
3224
4418
|
const start = pageIndex * pageSize;
|
|
3225
4419
|
return sortedRows.slice(start, start + pageSize);
|
|
@@ -3228,7 +4422,7 @@ function DataTable({
|
|
|
3228
4422
|
const headPad = dense ? "px-3 py-2" : void 0;
|
|
3229
4423
|
const colSpan = columns.length + (selectable ? 1 : 0);
|
|
3230
4424
|
if (!loading && rows.length === 0 && emptyMode === "replace") {
|
|
3231
|
-
return /* @__PURE__ */
|
|
4425
|
+
return /* @__PURE__ */ jsx48(EmptyState, { title: emptyTitle, description: emptyDescription, className });
|
|
3232
4426
|
}
|
|
3233
4427
|
const allKeys = sortedRows.map(getRowKey);
|
|
3234
4428
|
const allSelected = allKeys.length > 0 && allKeys.every((k) => selectedSet.has(k));
|
|
@@ -3253,10 +4447,10 @@ function DataTable({
|
|
|
3253
4447
|
const hasPager = paginated && !loading && sortedRows.length > 0;
|
|
3254
4448
|
const hasFoot = (showRowCount || footer || hasPager) && (loading || sortedRows.length > 0);
|
|
3255
4449
|
const skeletonCount = loadingRows ?? pageSize ?? 5;
|
|
3256
|
-
return /* @__PURE__ */
|
|
3257
|
-
caption ? /* @__PURE__ */
|
|
3258
|
-
/* @__PURE__ */
|
|
3259
|
-
selectable ? /* @__PURE__ */
|
|
4450
|
+
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: [
|
|
4451
|
+
caption ? /* @__PURE__ */ jsx48("caption", { className: "sr-only", children: caption }) : null,
|
|
4452
|
+
/* @__PURE__ */ jsx48("thead", { className: cn(headRowClass, stickyHeader && stickyHeadClass), children: /* @__PURE__ */ jsxs37("tr", { children: [
|
|
4453
|
+
selectable ? /* @__PURE__ */ jsx48("th", { scope: "col", className: cn(selectCellClass, headPad), children: /* @__PURE__ */ jsx48(
|
|
3260
4454
|
Checkbox,
|
|
3261
4455
|
{
|
|
3262
4456
|
checked: headerCheckedState,
|
|
@@ -3268,19 +4462,19 @@ function DataTable({
|
|
|
3268
4462
|
columns.map((col) => {
|
|
3269
4463
|
const isSorted = sort?.columnId === col.id;
|
|
3270
4464
|
const ariaSort = col.sortable ? isSorted ? sort.direction === "asc" ? "ascending" : "descending" : "none" : void 0;
|
|
3271
|
-
const headerContent = col.sortable ? /* @__PURE__ */
|
|
4465
|
+
const headerContent = col.sortable ? /* @__PURE__ */ jsxs37(
|
|
3272
4466
|
"button",
|
|
3273
4467
|
{
|
|
3274
4468
|
type: "button",
|
|
3275
4469
|
className: sortButtonClass,
|
|
3276
4470
|
onClick: () => setSort(nextSort(sort, col.id)),
|
|
3277
4471
|
children: [
|
|
3278
|
-
/* @__PURE__ */
|
|
3279
|
-
/* @__PURE__ */
|
|
4472
|
+
/* @__PURE__ */ jsx48("span", { className: "truncate", children: col.header }),
|
|
4473
|
+
/* @__PURE__ */ jsx48(SortIndicator, { active: Boolean(isSorted), direction: sort?.direction })
|
|
3280
4474
|
]
|
|
3281
4475
|
}
|
|
3282
4476
|
) : col.header;
|
|
3283
|
-
return /* @__PURE__ */
|
|
4477
|
+
return /* @__PURE__ */ jsx48(
|
|
3284
4478
|
"th",
|
|
3285
4479
|
{
|
|
3286
4480
|
scope: "col",
|
|
@@ -3297,9 +4491,9 @@ function DataTable({
|
|
|
3297
4491
|
);
|
|
3298
4492
|
})
|
|
3299
4493
|
] }) }),
|
|
3300
|
-
/* @__PURE__ */
|
|
3301
|
-
selectable ? /* @__PURE__ */
|
|
3302
|
-
columns.map((col) => /* @__PURE__ */
|
|
4494
|
+
/* @__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: [
|
|
4495
|
+
selectable ? /* @__PURE__ */ jsx48("td", { className: cn(selectCellClass, cellPad), children: /* @__PURE__ */ jsx48(Skeleton, { className: "size-4 rounded-[4px]" }) }) : null,
|
|
4496
|
+
columns.map((col) => /* @__PURE__ */ jsx48(
|
|
3303
4497
|
"td",
|
|
3304
4498
|
{
|
|
3305
4499
|
className: cn(
|
|
@@ -3308,17 +4502,17 @@ function DataTable({
|
|
|
3308
4502
|
col.align && alignClass[col.align],
|
|
3309
4503
|
col.className
|
|
3310
4504
|
),
|
|
3311
|
-
children: /* @__PURE__ */
|
|
4505
|
+
children: /* @__PURE__ */ jsx48(Skeleton, { className: "h-4 w-[60%]" })
|
|
3312
4506
|
},
|
|
3313
4507
|
col.id
|
|
3314
4508
|
))
|
|
3315
|
-
] }, `skeleton-${rowIdx}`)) : visibleRows.length === 0 ? /* @__PURE__ */
|
|
3316
|
-
/* @__PURE__ */
|
|
3317
|
-
emptyDescription ? /* @__PURE__ */
|
|
4509
|
+
] }, `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: [
|
|
4510
|
+
/* @__PURE__ */ jsx48("p", { className: "font-medium text-foreground", children: emptyTitle }),
|
|
4511
|
+
emptyDescription ? /* @__PURE__ */ jsx48("p", { className: "max-w-sm text-muted-foreground", children: emptyDescription }) : null
|
|
3318
4512
|
] }) }) }) : visibleRows.map((row) => {
|
|
3319
4513
|
const key = getRowKey(row);
|
|
3320
4514
|
const isSelected = selectedSet.has(key);
|
|
3321
|
-
return /* @__PURE__ */
|
|
4515
|
+
return /* @__PURE__ */ jsxs37(
|
|
3322
4516
|
"tr",
|
|
3323
4517
|
{
|
|
3324
4518
|
className: rowClass,
|
|
@@ -3334,12 +4528,12 @@ function DataTable({
|
|
|
3334
4528
|
tabIndex: onRowClick ? 0 : void 0,
|
|
3335
4529
|
role: onRowClick ? "button" : void 0,
|
|
3336
4530
|
children: [
|
|
3337
|
-
selectable ? /* @__PURE__ */
|
|
4531
|
+
selectable ? /* @__PURE__ */ jsx48(
|
|
3338
4532
|
"td",
|
|
3339
4533
|
{
|
|
3340
4534
|
className: cn(selectCellClass, cellPad),
|
|
3341
4535
|
onClick: (event) => event.stopPropagation(),
|
|
3342
|
-
children: /* @__PURE__ */
|
|
4536
|
+
children: /* @__PURE__ */ jsx48(
|
|
3343
4537
|
Checkbox,
|
|
3344
4538
|
{
|
|
3345
4539
|
checked: isSelected,
|
|
@@ -3349,7 +4543,7 @@ function DataTable({
|
|
|
3349
4543
|
)
|
|
3350
4544
|
}
|
|
3351
4545
|
) : null,
|
|
3352
|
-
columns.map((col) => /* @__PURE__ */
|
|
4546
|
+
columns.map((col) => /* @__PURE__ */ jsx48(
|
|
3353
4547
|
"td",
|
|
3354
4548
|
{
|
|
3355
4549
|
className: cn(
|
|
@@ -3359,7 +4553,7 @@ function DataTable({
|
|
|
3359
4553
|
col.align && alignClass[col.align],
|
|
3360
4554
|
col.className
|
|
3361
4555
|
),
|
|
3362
|
-
children: col.truncate ? /* @__PURE__ */
|
|
4556
|
+
children: col.truncate ? /* @__PURE__ */ jsx48("div", { className: "truncate", children: col.cell(row) }) : col.cell(row)
|
|
3363
4557
|
},
|
|
3364
4558
|
col.id
|
|
3365
4559
|
))
|
|
@@ -3368,7 +4562,7 @@ function DataTable({
|
|
|
3368
4562
|
key
|
|
3369
4563
|
);
|
|
3370
4564
|
}) }),
|
|
3371
|
-
hasFoot ? /* @__PURE__ */
|
|
4565
|
+
hasFoot ? /* @__PURE__ */ jsx48("tfoot", { children: /* @__PURE__ */ jsx48("tr", { children: /* @__PURE__ */ jsx48("td", { colSpan, className: footCellClass, children: /* @__PURE__ */ jsxs37(
|
|
3372
4566
|
"div",
|
|
3373
4567
|
{
|
|
3374
4568
|
className: cn(
|
|
@@ -3376,18 +4570,18 @@ function DataTable({
|
|
|
3376
4570
|
(showRowCount || footer || hasPager) && "justify-between"
|
|
3377
4571
|
),
|
|
3378
4572
|
children: [
|
|
3379
|
-
/* @__PURE__ */
|
|
3380
|
-
showRowCount ? /* @__PURE__ */
|
|
4573
|
+
/* @__PURE__ */ jsxs37("div", { className: footInnerClass, children: [
|
|
4574
|
+
showRowCount ? /* @__PURE__ */ jsxs37("span", { children: [
|
|
3381
4575
|
rowCountText,
|
|
3382
4576
|
selectable && selectedSet.size > 0 ? ` \xB7 ${selectedSet.size} selected` : null
|
|
3383
|
-
] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */
|
|
4577
|
+
] }) : selectable && selectedSet.size > 0 ? /* @__PURE__ */ jsxs37("span", { children: [
|
|
3384
4578
|
selectedSet.size,
|
|
3385
4579
|
" selected"
|
|
3386
4580
|
] }) : null,
|
|
3387
4581
|
footer
|
|
3388
4582
|
] }),
|
|
3389
|
-
hasPager ? /* @__PURE__ */
|
|
3390
|
-
/* @__PURE__ */
|
|
4583
|
+
hasPager ? /* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-2", children: [
|
|
4584
|
+
/* @__PURE__ */ jsxs37("span", { className: "tabular-nums", children: [
|
|
3391
4585
|
pageIndex * pageSize + 1,
|
|
3392
4586
|
"\u2013",
|
|
3393
4587
|
Math.min(
|
|
@@ -3398,8 +4592,8 @@ function DataTable({
|
|
|
3398
4592
|
"of ",
|
|
3399
4593
|
sortedRows.length
|
|
3400
4594
|
] }),
|
|
3401
|
-
/* @__PURE__ */
|
|
3402
|
-
/* @__PURE__ */
|
|
4595
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-1", children: [
|
|
4596
|
+
/* @__PURE__ */ jsx48(
|
|
3403
4597
|
"button",
|
|
3404
4598
|
{
|
|
3405
4599
|
type: "button",
|
|
@@ -3407,10 +4601,10 @@ function DataTable({
|
|
|
3407
4601
|
onClick: () => setPage(pageIndex - 1),
|
|
3408
4602
|
disabled: pageIndex <= 0,
|
|
3409
4603
|
"aria-label": "Previous page",
|
|
3410
|
-
children: /* @__PURE__ */
|
|
4604
|
+
children: /* @__PURE__ */ jsx48(ChevronLeftIcon, { className: "size-4", "aria-hidden": true })
|
|
3411
4605
|
}
|
|
3412
4606
|
),
|
|
3413
|
-
/* @__PURE__ */
|
|
4607
|
+
/* @__PURE__ */ jsx48(
|
|
3414
4608
|
"button",
|
|
3415
4609
|
{
|
|
3416
4610
|
type: "button",
|
|
@@ -3418,7 +4612,7 @@ function DataTable({
|
|
|
3418
4612
|
onClick: () => setPage(pageIndex + 1),
|
|
3419
4613
|
disabled: pageIndex >= pageCount - 1,
|
|
3420
4614
|
"aria-label": "Next page",
|
|
3421
|
-
children: /* @__PURE__ */
|
|
4615
|
+
children: /* @__PURE__ */ jsx48(ChevronRightIcon2, { className: "size-4", "aria-hidden": true })
|
|
3422
4616
|
}
|
|
3423
4617
|
)
|
|
3424
4618
|
] })
|
|
@@ -3430,8 +4624,8 @@ function DataTable({
|
|
|
3430
4624
|
}
|
|
3431
4625
|
|
|
3432
4626
|
// src/app/data/ChartPanel.tsx
|
|
3433
|
-
import { useId as
|
|
3434
|
-
import { jsx as
|
|
4627
|
+
import { useId as useId9 } from "react";
|
|
4628
|
+
import { jsx as jsx49, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
3435
4629
|
var ChartPanel = ({
|
|
3436
4630
|
title,
|
|
3437
4631
|
description,
|
|
@@ -3446,17 +4640,17 @@ var ChartPanel = ({
|
|
|
3446
4640
|
const height = heightProp ?? APP_DENSITY_CHART_HEIGHT[density];
|
|
3447
4641
|
const metricChartPlotRegionClass = useAppDensityClass("metricChartPlotRegion");
|
|
3448
4642
|
const chartPanelBodyClass = useAppDensityClass("chartPanelBody");
|
|
3449
|
-
const titleId =
|
|
4643
|
+
const titleId = useId9();
|
|
3450
4644
|
const resolvedTitle = title ?? artifact?.title;
|
|
3451
4645
|
const hasHeader = Boolean(resolvedTitle || description || actions);
|
|
3452
|
-
const body = loading ? /* @__PURE__ */
|
|
3453
|
-
return /* @__PURE__ */
|
|
4646
|
+
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);
|
|
4647
|
+
return /* @__PURE__ */ jsxs38(
|
|
3454
4648
|
"section",
|
|
3455
4649
|
{
|
|
3456
4650
|
className: cn(metricCardShellClass, "aui-app-chart-panel", className),
|
|
3457
4651
|
"aria-labelledby": resolvedTitle ? titleId : void 0,
|
|
3458
4652
|
children: [
|
|
3459
|
-
/* @__PURE__ */
|
|
4653
|
+
/* @__PURE__ */ jsx49(
|
|
3460
4654
|
MetricCardHeader,
|
|
3461
4655
|
{
|
|
3462
4656
|
title: resolvedTitle,
|
|
@@ -3465,14 +4659,14 @@ var ChartPanel = ({
|
|
|
3465
4659
|
actions
|
|
3466
4660
|
}
|
|
3467
4661
|
),
|
|
3468
|
-
/* @__PURE__ */
|
|
4662
|
+
/* @__PURE__ */ jsx49(
|
|
3469
4663
|
"div",
|
|
3470
4664
|
{
|
|
3471
4665
|
className: cn(
|
|
3472
4666
|
"relative min-h-0 w-full",
|
|
3473
4667
|
hasHeader ? metricChartPlotRegionClass : chartPanelBodyClass
|
|
3474
4668
|
),
|
|
3475
|
-
children: body ?? /* @__PURE__ */
|
|
4669
|
+
children: body ?? /* @__PURE__ */ jsx49(
|
|
3476
4670
|
"div",
|
|
3477
4671
|
{
|
|
3478
4672
|
className: "flex items-center justify-center text-sm font-normal text-muted-foreground",
|
|
@@ -3489,10 +4683,11 @@ var ChartPanel = ({
|
|
|
3489
4683
|
};
|
|
3490
4684
|
|
|
3491
4685
|
// src/app/data/MetricRow.tsx
|
|
3492
|
-
import { useId as
|
|
3493
|
-
import { jsx as
|
|
4686
|
+
import { useId as useId10, useState as useState6 } from "react";
|
|
4687
|
+
import { jsx as jsx50, jsxs as jsxs39 } from "react/jsx-runtime";
|
|
3494
4688
|
var MetricRow = ({
|
|
3495
4689
|
title,
|
|
4690
|
+
titleTag,
|
|
3496
4691
|
description,
|
|
3497
4692
|
actions,
|
|
3498
4693
|
metrics,
|
|
@@ -3504,9 +4699,9 @@ var MetricRow = ({
|
|
|
3504
4699
|
className
|
|
3505
4700
|
}) => {
|
|
3506
4701
|
const metricTileClass = useAppDensityClass("metricTile");
|
|
3507
|
-
const titleId =
|
|
4702
|
+
const titleId = useId10();
|
|
3508
4703
|
const selectable = onMetricChange != null || activeMetricId != null;
|
|
3509
|
-
const [internalId, setInternalId] =
|
|
4704
|
+
const [internalId, setInternalId] = useState6(
|
|
3510
4705
|
defaultActiveMetricId ?? metrics[0]?.id
|
|
3511
4706
|
);
|
|
3512
4707
|
const activeId = activeMetricId ?? internalId;
|
|
@@ -3514,22 +4709,24 @@ var MetricRow = ({
|
|
|
3514
4709
|
if (activeMetricId == null) setInternalId(id);
|
|
3515
4710
|
onMetricChange?.(id);
|
|
3516
4711
|
};
|
|
3517
|
-
|
|
4712
|
+
const hasHeader = Boolean(title || titleTag || description || actions);
|
|
4713
|
+
return /* @__PURE__ */ jsxs39(
|
|
3518
4714
|
"section",
|
|
3519
4715
|
{
|
|
3520
4716
|
className: cn(metricCardShellClass, className),
|
|
3521
4717
|
"aria-labelledby": title ? titleId : void 0,
|
|
3522
4718
|
children: [
|
|
3523
|
-
/* @__PURE__ */
|
|
4719
|
+
/* @__PURE__ */ jsx50(
|
|
3524
4720
|
MetricCardHeader,
|
|
3525
4721
|
{
|
|
3526
4722
|
title,
|
|
4723
|
+
titleTag,
|
|
3527
4724
|
titleId,
|
|
3528
4725
|
description,
|
|
3529
4726
|
actions
|
|
3530
4727
|
}
|
|
3531
4728
|
),
|
|
3532
|
-
/* @__PURE__ */
|
|
4729
|
+
/* @__PURE__ */ jsx50(
|
|
3533
4730
|
"div",
|
|
3534
4731
|
{
|
|
3535
4732
|
role: selectable ? "group" : void 0,
|
|
@@ -3538,20 +4735,20 @@ var MetricRow = ({
|
|
|
3538
4735
|
className: cn(
|
|
3539
4736
|
metricTilesRowClass,
|
|
3540
4737
|
metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
|
|
3541
|
-
|
|
4738
|
+
hasHeader && "mt-3.5 border-t border-border/40"
|
|
3542
4739
|
),
|
|
3543
|
-
children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */
|
|
4740
|
+
children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ jsxs39(
|
|
3544
4741
|
"div",
|
|
3545
4742
|
{
|
|
3546
4743
|
className: cn("flex min-w-0 flex-1 flex-col gap-2", metricTileClass),
|
|
3547
4744
|
"aria-hidden": true,
|
|
3548
4745
|
children: [
|
|
3549
|
-
/* @__PURE__ */
|
|
3550
|
-
/* @__PURE__ */
|
|
4746
|
+
/* @__PURE__ */ jsx50(Skeleton, { className: "h-3 w-20" }),
|
|
4747
|
+
/* @__PURE__ */ jsx50(Skeleton, { className: "h-7 w-24" })
|
|
3551
4748
|
]
|
|
3552
4749
|
},
|
|
3553
4750
|
`skeleton-${index}`
|
|
3554
|
-
)) : metrics.map((m, index) => /* @__PURE__ */
|
|
4751
|
+
)) : metrics.map((m, index) => /* @__PURE__ */ jsx50(
|
|
3555
4752
|
MetricTile,
|
|
3556
4753
|
{
|
|
3557
4754
|
label: m.label,
|
|
@@ -3559,6 +4756,11 @@ var MetricRow = ({
|
|
|
3559
4756
|
unit: m.unit,
|
|
3560
4757
|
trend: m.trend,
|
|
3561
4758
|
trendTone: m.trendTone,
|
|
4759
|
+
trendVariant: m.trendVariant,
|
|
4760
|
+
activeTone: m.activeTone,
|
|
4761
|
+
sparklineData: m.sparklineData,
|
|
4762
|
+
sparklineConfig: m.sparklineConfig,
|
|
4763
|
+
sparkline: m.sparkline,
|
|
3562
4764
|
active: selectable && m.id === activeId,
|
|
3563
4765
|
showDivider: index < metrics.length - 1,
|
|
3564
4766
|
onSelect: selectable ? () => select(m.id) : void 0
|
|
@@ -3573,10 +4775,11 @@ var MetricRow = ({
|
|
|
3573
4775
|
};
|
|
3574
4776
|
|
|
3575
4777
|
// src/app/data/MetricChartCard.tsx
|
|
3576
|
-
import { useId as
|
|
3577
|
-
import { jsx as
|
|
4778
|
+
import { useId as useId11, useState as useState7 } from "react";
|
|
4779
|
+
import { jsx as jsx51, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
3578
4780
|
var MetricChartCard = ({
|
|
3579
4781
|
title,
|
|
4782
|
+
titleTag,
|
|
3580
4783
|
description,
|
|
3581
4784
|
actions,
|
|
3582
4785
|
metrics,
|
|
@@ -3597,8 +4800,8 @@ var MetricChartCard = ({
|
|
|
3597
4800
|
const height = heightProp ?? APP_DENSITY_CHART_HEIGHT[density];
|
|
3598
4801
|
const metricChartRegionClass = useAppDensityClass("metricChartRegion");
|
|
3599
4802
|
const metricTileClass = useAppDensityClass("metricTile");
|
|
3600
|
-
const titleId =
|
|
3601
|
-
const [internalId, setInternalId] =
|
|
4803
|
+
const titleId = useId11();
|
|
4804
|
+
const [internalId, setInternalId] = useState7(
|
|
3602
4805
|
defaultActiveMetricId ?? metrics[0]?.id
|
|
3603
4806
|
);
|
|
3604
4807
|
const activeId = activeMetricId ?? internalId;
|
|
@@ -3607,24 +4810,25 @@ var MetricChartCard = ({
|
|
|
3607
4810
|
if (activeMetricId == null) setInternalId(id);
|
|
3608
4811
|
onMetricChange?.(id);
|
|
3609
4812
|
};
|
|
3610
|
-
const hasHeader = Boolean(title || description || actions);
|
|
4813
|
+
const hasHeader = Boolean(title || titleTag || description || actions);
|
|
3611
4814
|
const chartAriaLabel = typeof active?.label === "string" ? `${active.label} over time` : "Metric chart";
|
|
3612
|
-
return /* @__PURE__ */
|
|
4815
|
+
return /* @__PURE__ */ jsxs40(
|
|
3613
4816
|
"section",
|
|
3614
4817
|
{
|
|
3615
4818
|
className: cn(metricCardShellClass, className),
|
|
3616
4819
|
"aria-labelledby": title ? titleId : void 0,
|
|
3617
4820
|
children: [
|
|
3618
|
-
/* @__PURE__ */
|
|
4821
|
+
/* @__PURE__ */ jsx51(
|
|
3619
4822
|
MetricCardHeader,
|
|
3620
4823
|
{
|
|
3621
4824
|
title,
|
|
3622
|
-
|
|
4825
|
+
titleTag,
|
|
3623
4826
|
description,
|
|
3624
|
-
actions
|
|
4827
|
+
actions,
|
|
4828
|
+
titleId
|
|
3625
4829
|
}
|
|
3626
4830
|
),
|
|
3627
|
-
/* @__PURE__ */
|
|
4831
|
+
/* @__PURE__ */ jsx51(
|
|
3628
4832
|
"div",
|
|
3629
4833
|
{
|
|
3630
4834
|
role: "group",
|
|
@@ -3633,20 +4837,20 @@ var MetricChartCard = ({
|
|
|
3633
4837
|
className: cn(
|
|
3634
4838
|
metricTilesRowClass,
|
|
3635
4839
|
metricTilesGridColsClass(loading ? metrics.length || 4 : metrics.length),
|
|
3636
|
-
hasHeader && "mt-3"
|
|
4840
|
+
hasHeader && "mt-3.5 border-t border-border/40"
|
|
3637
4841
|
),
|
|
3638
|
-
children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */
|
|
4842
|
+
children: loading ? Array.from({ length: metrics.length || 4 }).map((_, index) => /* @__PURE__ */ jsxs40(
|
|
3639
4843
|
"div",
|
|
3640
4844
|
{
|
|
3641
4845
|
className: cn("flex min-w-0 flex-1 flex-col gap-2", metricTileClass),
|
|
3642
4846
|
"aria-hidden": true,
|
|
3643
4847
|
children: [
|
|
3644
|
-
/* @__PURE__ */
|
|
3645
|
-
/* @__PURE__ */
|
|
4848
|
+
/* @__PURE__ */ jsx51(Skeleton, { className: "h-3 w-20" }),
|
|
4849
|
+
/* @__PURE__ */ jsx51(Skeleton, { className: "h-7 w-24" })
|
|
3646
4850
|
]
|
|
3647
4851
|
},
|
|
3648
4852
|
`skeleton-${index}`
|
|
3649
|
-
)) : metrics.map((m, index) => /* @__PURE__ */
|
|
4853
|
+
)) : metrics.map((m, index) => /* @__PURE__ */ jsx51(
|
|
3650
4854
|
MetricTile,
|
|
3651
4855
|
{
|
|
3652
4856
|
label: m.label,
|
|
@@ -3654,6 +4858,11 @@ var MetricChartCard = ({
|
|
|
3654
4858
|
unit: m.unit,
|
|
3655
4859
|
trend: m.trend,
|
|
3656
4860
|
trendTone: m.trendTone,
|
|
4861
|
+
trendVariant: m.trendVariant,
|
|
4862
|
+
activeTone: m.activeTone,
|
|
4863
|
+
sparklineData: m.sparklineData,
|
|
4864
|
+
sparklineConfig: m.sparklineConfig,
|
|
4865
|
+
sparkline: m.sparkline,
|
|
3657
4866
|
active: m.id === active?.id,
|
|
3658
4867
|
showDivider: index < metrics.length - 1,
|
|
3659
4868
|
onSelect: () => select(m.id)
|
|
@@ -3662,14 +4871,14 @@ var MetricChartCard = ({
|
|
|
3662
4871
|
))
|
|
3663
4872
|
}
|
|
3664
4873
|
),
|
|
3665
|
-
/* @__PURE__ */
|
|
4874
|
+
/* @__PURE__ */ jsx51("div", { className: metricChartRegionClass, "aria-live": "polite", "aria-atomic": "true", children: loading ? /* @__PURE__ */ jsx51(
|
|
3666
4875
|
Skeleton,
|
|
3667
4876
|
{
|
|
3668
4877
|
className: "w-full rounded-lg",
|
|
3669
4878
|
style: { height },
|
|
3670
4879
|
"aria-hidden": true
|
|
3671
4880
|
}
|
|
3672
|
-
) : active?.data && active.data.length > 0 ? /* @__PURE__ */
|
|
4881
|
+
) : active?.data && active.data.length > 0 ? /* @__PURE__ */ jsx51(
|
|
3673
4882
|
LineAreaChart,
|
|
3674
4883
|
{
|
|
3675
4884
|
data: active.data,
|
|
@@ -3689,7 +4898,7 @@ var MetricChartCard = ({
|
|
|
3689
4898
|
ariaLabel: chartAriaLabel
|
|
3690
4899
|
},
|
|
3691
4900
|
active.id
|
|
3692
|
-
) : /* @__PURE__ */
|
|
4901
|
+
) : /* @__PURE__ */ jsx51(
|
|
3693
4902
|
"div",
|
|
3694
4903
|
{
|
|
3695
4904
|
className: "flex w-full items-center justify-center text-sm font-normal text-muted-foreground",
|
|
@@ -3704,13 +4913,13 @@ var MetricChartCard = ({
|
|
|
3704
4913
|
};
|
|
3705
4914
|
|
|
3706
4915
|
// src/hooks/use-live-query.ts
|
|
3707
|
-
import { useCallback as useCallback2, useEffect as
|
|
4916
|
+
import { useCallback as useCallback2, useEffect as useEffect5, useRef, useState as useState8 } from "react";
|
|
3708
4917
|
function useInterval(callback, delayMs) {
|
|
3709
4918
|
const saved = useRef(callback);
|
|
3710
|
-
|
|
4919
|
+
useEffect5(() => {
|
|
3711
4920
|
saved.current = callback;
|
|
3712
4921
|
}, [callback]);
|
|
3713
|
-
|
|
4922
|
+
useEffect5(() => {
|
|
3714
4923
|
if (delayMs === null) return;
|
|
3715
4924
|
const id = setInterval(() => saved.current(), delayMs);
|
|
3716
4925
|
return () => clearInterval(id);
|
|
@@ -3723,19 +4932,19 @@ function useLiveQuery(fetcher, options = {}) {
|
|
|
3723
4932
|
immediate = true,
|
|
3724
4933
|
refetchOnFocus = true
|
|
3725
4934
|
} = options;
|
|
3726
|
-
const [data, setData] =
|
|
3727
|
-
const [error, setError] =
|
|
3728
|
-
const [loading, setLoading] =
|
|
3729
|
-
const [refreshing, setRefreshing] =
|
|
3730
|
-
const [lastUpdated, setLastUpdated] =
|
|
4935
|
+
const [data, setData] = useState8(void 0);
|
|
4936
|
+
const [error, setError] = useState8(void 0);
|
|
4937
|
+
const [loading, setLoading] = useState8(enabled);
|
|
4938
|
+
const [refreshing, setRefreshing] = useState8(false);
|
|
4939
|
+
const [lastUpdated, setLastUpdated] = useState8(null);
|
|
3731
4940
|
const fetcherRef = useRef(fetcher);
|
|
3732
|
-
|
|
4941
|
+
useEffect5(() => {
|
|
3733
4942
|
fetcherRef.current = fetcher;
|
|
3734
4943
|
}, [fetcher]);
|
|
3735
4944
|
const mounted = useRef(true);
|
|
3736
4945
|
const requestId = useRef(0);
|
|
3737
4946
|
const hasData = useRef(false);
|
|
3738
|
-
|
|
4947
|
+
useEffect5(() => {
|
|
3739
4948
|
mounted.current = true;
|
|
3740
4949
|
return () => {
|
|
3741
4950
|
mounted.current = false;
|
|
@@ -3767,11 +4976,11 @@ function useLiveQuery(fetcher, options = {}) {
|
|
|
3767
4976
|
if (!enabled) return;
|
|
3768
4977
|
run();
|
|
3769
4978
|
}, [enabled, run]);
|
|
3770
|
-
|
|
4979
|
+
useEffect5(() => {
|
|
3771
4980
|
if (!enabled) return;
|
|
3772
4981
|
if (immediate) run();
|
|
3773
4982
|
}, [enabled, immediate, run]);
|
|
3774
|
-
|
|
4983
|
+
useEffect5(() => {
|
|
3775
4984
|
if (!enabled || intervalMs === null) return;
|
|
3776
4985
|
const tick = () => {
|
|
3777
4986
|
if (refetchOnFocus && typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
@@ -3782,7 +4991,7 @@ function useLiveQuery(fetcher, options = {}) {
|
|
|
3782
4991
|
const handle = setInterval(tick, intervalMs);
|
|
3783
4992
|
return () => clearInterval(handle);
|
|
3784
4993
|
}, [enabled, intervalMs, refetchOnFocus, run]);
|
|
3785
|
-
|
|
4994
|
+
useEffect5(() => {
|
|
3786
4995
|
if (!enabled || !refetchOnFocus || typeof document === "undefined") return;
|
|
3787
4996
|
const onVisible = () => {
|
|
3788
4997
|
if (document.visibilityState === "visible") run();
|
|
@@ -3793,69 +5002,6 @@ function useLiveQuery(fetcher, options = {}) {
|
|
|
3793
5002
|
return { data, error, loading, refreshing, lastUpdated, refetch };
|
|
3794
5003
|
}
|
|
3795
5004
|
|
|
3796
|
-
// src/charts/sparkline.tsx
|
|
3797
|
-
import { useId as useId7 } from "react";
|
|
3798
|
-
import { Fragment as Fragment6, jsx as jsx48, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
3799
|
-
var Sparkline = ({
|
|
3800
|
-
data,
|
|
3801
|
-
dataKey = "value",
|
|
3802
|
-
color = "var(--primary, #6366f1)",
|
|
3803
|
-
area = true,
|
|
3804
|
-
width = 96,
|
|
3805
|
-
height = 28,
|
|
3806
|
-
strokeWidth = 1.5,
|
|
3807
|
-
className,
|
|
3808
|
-
ariaLabel = "Trend"
|
|
3809
|
-
}) => {
|
|
3810
|
-
const uid = useId7();
|
|
3811
|
-
const values = data.map((d) => typeof d === "number" ? d : toNum(d[dataKey]));
|
|
3812
|
-
if (values.length === 0) {
|
|
3813
|
-
return /* @__PURE__ */ jsx48("span", { className: cn("inline-block", className), style: { width, height } });
|
|
3814
|
-
}
|
|
3815
|
-
const pad = strokeWidth + 1;
|
|
3816
|
-
const min = Math.min(...values);
|
|
3817
|
-
const max = Math.max(...values);
|
|
3818
|
-
const range = max - min || 1;
|
|
3819
|
-
const innerW = width - pad * 2;
|
|
3820
|
-
const innerH = height - pad * 2;
|
|
3821
|
-
const points = values.map((v, i) => ({
|
|
3822
|
-
x: pad + (values.length > 1 ? i / (values.length - 1) * innerW : innerW / 2),
|
|
3823
|
-
y: pad + innerH - (v - min) / range * innerH
|
|
3824
|
-
}));
|
|
3825
|
-
return /* @__PURE__ */ jsxs38(
|
|
3826
|
-
"svg",
|
|
3827
|
-
{
|
|
3828
|
-
width,
|
|
3829
|
-
height,
|
|
3830
|
-
viewBox: `0 0 ${width} ${height}`,
|
|
3831
|
-
className: cn("block", className),
|
|
3832
|
-
role: "img",
|
|
3833
|
-
"aria-label": ariaLabel,
|
|
3834
|
-
preserveAspectRatio: "none",
|
|
3835
|
-
children: [
|
|
3836
|
-
area && /* @__PURE__ */ jsxs38(Fragment6, { children: [
|
|
3837
|
-
/* @__PURE__ */ jsx48("defs", { children: /* @__PURE__ */ jsxs38("linearGradient", { id: `${uid}-spark`, x1: "0", x2: "0", y1: "0", y2: "1", children: [
|
|
3838
|
-
/* @__PURE__ */ jsx48("stop", { offset: "0%", style: { stopColor: color, stopOpacity: 0.25 } }),
|
|
3839
|
-
/* @__PURE__ */ jsx48("stop", { offset: "100%", style: { stopColor: color, stopOpacity: 0 } })
|
|
3840
|
-
] }) }),
|
|
3841
|
-
/* @__PURE__ */ jsx48("path", { d: monotoneAreaPath(points, height - pad), fill: `url(#${uid}-spark)` })
|
|
3842
|
-
] }),
|
|
3843
|
-
/* @__PURE__ */ jsx48(
|
|
3844
|
-
"path",
|
|
3845
|
-
{
|
|
3846
|
-
d: monotoneLinePath(points),
|
|
3847
|
-
fill: "none",
|
|
3848
|
-
stroke: color,
|
|
3849
|
-
strokeWidth,
|
|
3850
|
-
strokeLinecap: "round",
|
|
3851
|
-
strokeLinejoin: "round"
|
|
3852
|
-
}
|
|
3853
|
-
)
|
|
3854
|
-
]
|
|
3855
|
-
}
|
|
3856
|
-
);
|
|
3857
|
-
};
|
|
3858
|
-
|
|
3859
5005
|
export {
|
|
3860
5006
|
SEMANTIC_COLOR_TOKENS,
|
|
3861
5007
|
RESERVED_GRADIENT_TOKENS,
|
|
@@ -3892,16 +5038,17 @@ export {
|
|
|
3892
5038
|
AppDensityProvider,
|
|
3893
5039
|
useAppDensity,
|
|
3894
5040
|
useAppDensityClass,
|
|
5041
|
+
Sparkline,
|
|
3895
5042
|
MetricTile,
|
|
3896
5043
|
useAppShellChat,
|
|
3897
5044
|
useAppShellNav,
|
|
3898
5045
|
AppShell,
|
|
3899
|
-
AppShellTopbar,
|
|
3900
5046
|
AppShellChatTrigger,
|
|
3901
5047
|
AppShellSidebarTrigger,
|
|
3902
5048
|
PageHeader,
|
|
3903
5049
|
Page,
|
|
3904
5050
|
Section,
|
|
5051
|
+
Stack,
|
|
3905
5052
|
AppCopilotProvider,
|
|
3906
5053
|
useAppCopilotContext,
|
|
3907
5054
|
AppChatPanel,
|
|
@@ -3915,6 +5062,8 @@ export {
|
|
|
3915
5062
|
DescriptionList,
|
|
3916
5063
|
ExpandableSection,
|
|
3917
5064
|
ResourceCard,
|
|
5065
|
+
AlertCard,
|
|
5066
|
+
CatalogCard,
|
|
3918
5067
|
SettingsSectionHeader,
|
|
3919
5068
|
SettingsSection,
|
|
3920
5069
|
FieldRow,
|
|
@@ -3939,11 +5088,11 @@ export {
|
|
|
3939
5088
|
FormSection,
|
|
3940
5089
|
FilterBar,
|
|
3941
5090
|
FilterField,
|
|
5091
|
+
FilterDropdown,
|
|
3942
5092
|
DataTable,
|
|
3943
5093
|
ChartPanel,
|
|
3944
5094
|
MetricRow,
|
|
3945
5095
|
MetricChartCard,
|
|
3946
5096
|
useInterval,
|
|
3947
|
-
useLiveQuery
|
|
3948
|
-
Sparkline
|
|
5097
|
+
useLiveQuery
|
|
3949
5098
|
};
|