@fayz-ai/ui 0.9.0 → 0.10.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/README.md +2 -2
- package/dist/analytics/AnalyticsCard.d.ts +10 -0
- package/dist/analytics/AnalyticsCard.d.ts.map +1 -0
- package/dist/analytics/AnalyticsControls.d.ts +18 -0
- package/dist/analytics/AnalyticsControls.d.ts.map +1 -0
- package/dist/analytics/AnalyticsGrid.d.ts +16 -0
- package/dist/analytics/AnalyticsGrid.d.ts.map +1 -0
- package/dist/analytics/context.d.ts +58 -0
- package/dist/analytics/context.d.ts.map +1 -0
- package/dist/analytics/derive.d.ts +81 -0
- package/dist/analytics/derive.d.ts.map +1 -0
- package/dist/analytics/index.d.ts +13 -0
- package/dist/analytics/index.d.ts.map +1 -0
- package/dist/analytics/range.d.ts +22 -0
- package/dist/analytics/range.d.ts.map +1 -0
- package/dist/{chunk-PVIB724L.js → chunk-22EVFMS3.js} +5 -3
- package/dist/chunk-22EVFMS3.js.map +1 -0
- package/dist/{chunk-G6OKZ7WL.js → chunk-6V5MGYXH.js} +13 -9
- package/dist/chunk-6V5MGYXH.js.map +1 -0
- package/dist/{chunk-KV3MHB6N.js → chunk-H63TOSQ7.js} +196 -53
- package/dist/chunk-H63TOSQ7.js.map +1 -0
- package/dist/{chunk-OUPJDGKE.js → chunk-I6CPKAOD.js} +134 -37
- package/dist/chunk-I6CPKAOD.js.map +1 -0
- package/dist/{chunk-YRVE4FOD.js → chunk-OKZ6WQB3.js} +3 -3
- package/dist/chunk-OKZ6WQB3.js.map +1 -0
- package/dist/{chunk-S5KNM4D2.js → chunk-SEIY2F5F.js} +235 -107
- package/dist/chunk-SEIY2F5F.js.map +1 -0
- package/dist/{chunk-75EFDLST.js → chunk-VAKWUUVC.js} +157 -6
- package/dist/chunk-VAKWUUVC.js.map +1 -0
- package/dist/dashboard/OnboardingWidget.d.ts +2 -14
- package/dist/dashboard/OnboardingWidget.d.ts.map +1 -1
- package/dist/dashboard/SidebarOnboarding.d.ts +28 -0
- package/dist/dashboard/SidebarOnboarding.d.ts.map +1 -0
- package/dist/dashboard/index.d.ts +2 -0
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +4 -4
- package/dist/dashboard/types.d.ts +18 -0
- package/dist/dashboard/types.d.ts.map +1 -1
- package/dist/dashboard/useOnboardingProgress.d.ts +28 -0
- package/dist/dashboard/useOnboardingProgress.d.ts.map +1 -0
- package/dist/icons.js +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +960 -14
- package/dist/index.js.map +1 -1
- package/dist/layout/AppShell.d.ts +14 -1
- package/dist/layout/AppShell.d.ts.map +1 -1
- package/dist/layout/ModulePage.d.ts +11 -2
- package/dist/layout/ModulePage.d.ts.map +1 -1
- package/dist/layout/Sidebar.d.ts +8 -1
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/index.js +4 -4
- package/dist/lib/cn.d.ts.map +1 -0
- package/dist/preset.d.ts +3 -0
- package/dist/preset.d.ts.map +1 -0
- package/dist/primitives/data-table.d.ts +19 -4
- package/dist/primitives/data-table.d.ts.map +1 -1
- package/dist/primitives/date-picker.d.ts +4 -2
- package/dist/primitives/date-picker.d.ts.map +1 -1
- package/dist/primitives/filter-chip.d.ts +26 -0
- package/dist/primitives/filter-chip.d.ts.map +1 -0
- package/dist/primitives/header-tabs.d.ts +32 -0
- package/dist/primitives/header-tabs.d.ts.map +1 -0
- package/dist/primitives/hint.d.ts +25 -0
- package/dist/primitives/hint.d.ts.map +1 -0
- package/dist/primitives/index.d.ts +7 -0
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/index.js +4 -4
- package/dist/primitives/list-view.d.ts +14 -1
- package/dist/primitives/list-view.d.ts.map +1 -1
- package/dist/primitives/page-header.d.ts +23 -0
- package/dist/primitives/page-header.d.ts.map +1 -0
- package/dist/primitives/stat-tiles.d.ts +30 -0
- package/dist/primitives/stat-tiles.d.ts.map +1 -0
- package/dist/primitives/switch.d.ts +11 -0
- package/dist/primitives/switch.d.ts.map +1 -0
- package/dist/stores/index.d.ts +1 -0
- package/dist/stores/index.d.ts.map +1 -1
- package/dist/stores/page-header.store.d.ts +69 -0
- package/dist/stores/page-header.store.d.ts.map +1 -0
- package/package.json +4 -13
- package/dist/chunk-75EFDLST.js.map +0 -1
- package/dist/chunk-G6OKZ7WL.js.map +0 -1
- package/dist/chunk-KV3MHB6N.js.map +0 -1
- package/dist/chunk-OUPJDGKE.js.map +0 -1
- package/dist/chunk-PVIB724L.js.map +0 -1
- package/dist/chunk-S5KNM4D2.js.map +0 -1
- package/dist/chunk-YRVE4FOD.js.map +0 -1
- package/dist/utils/cn.d.ts.map +0 -1
- package/src/dashboard/ChartWidget.tsx +0 -50
- package/src/dashboard/ChartWidgetInner.tsx +0 -83
- package/src/dashboard/DashboardCanvas.tsx +0 -210
- package/src/dashboard/DashboardCustomizeMenu.tsx +0 -53
- package/src/dashboard/DashboardGrid.tsx +0 -35
- package/src/dashboard/DashboardRange.tsx +0 -62
- package/src/dashboard/GoldCard.tsx +0 -38
- package/src/dashboard/KpiCard.tsx +0 -238
- package/src/dashboard/OnboardingWidget.tsx +0 -140
- package/src/dashboard/TableWidget.tsx +0 -51
- package/src/dashboard/define.ts +0 -53
- package/src/dashboard/icon.ts +0 -13
- package/src/dashboard/index.ts +0 -38
- package/src/dashboard/layout.ts +0 -50
- package/src/dashboard/preferences.ts +0 -71
- package/src/dashboard/types.ts +0 -41
- package/src/icons.ts +0 -66
- package/src/index.ts +0 -238
- package/src/layout/AppShell.tsx +0 -987
- package/src/layout/ContentSplit.tsx +0 -153
- package/src/layout/ModulePage.tsx +0 -466
- package/src/layout/PageTransition.tsx +0 -57
- package/src/layout/SaveBar.tsx +0 -247
- package/src/layout/Sidebar.tsx +0 -535
- package/src/layout/Topbar.tsx +0 -462
- package/src/layout/index.ts +0 -39
- package/src/layout/useCrossModuleBack.ts +0 -30
- package/src/primitives/Avatar.tsx +0 -48
- package/src/primitives/SearchCombobox.tsx +0 -216
- package/src/primitives/badge.tsx +0 -68
- package/src/primitives/breadcrumb.tsx +0 -43
- package/src/primitives/button.tsx +0 -60
- package/src/primitives/card.tsx +0 -79
- package/src/primitives/checkbox.tsx +0 -53
- package/src/primitives/confirm-dialog.tsx +0 -125
- package/src/primitives/currency-input.tsx +0 -121
- package/src/primitives/data-table.tsx +0 -160
- package/src/primitives/date-picker.tsx +0 -225
- package/src/primitives/dropdown.tsx +0 -196
- package/src/primitives/index.ts +0 -87
- package/src/primitives/input.tsx +0 -50
- package/src/primitives/list-view.tsx +0 -252
- package/src/primitives/markdown-editor.tsx +0 -75
- package/src/primitives/markdown.tsx +0 -169
- package/src/primitives/modal.tsx +0 -156
- package/src/primitives/phone-link.tsx +0 -138
- package/src/primitives/popover.tsx +0 -30
- package/src/primitives/search-select.tsx +0 -314
- package/src/primitives/segmented-control.tsx +0 -66
- package/src/primitives/select.tsx +0 -157
- package/src/primitives/separator.tsx +0 -29
- package/src/primitives/sheet.tsx +0 -181
- package/src/primitives/skeleton.tsx +0 -46
- package/src/primitives/tabs.tsx +0 -53
- package/src/primitives/time-picker.tsx +0 -148
- package/src/primitives/toast.tsx +0 -46
- package/src/primitives/tooltip.tsx +0 -28
- package/src/stores/index.ts +0 -10
- package/src/stores/layout.store.ts +0 -31
- package/src/stores/theme.store.ts +0 -457
- package/src/styles.css +0 -461
- package/src/tailwind.ts +0 -43
- package/src/theme/index.ts +0 -94
- package/src/theme/preset.ts +0 -209
- package/src/theme/presets.ts +0 -106
- package/src/utils/cn.ts +0 -6
- /package/dist/{utils → lib}/cn.d.ts +0 -0
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import * as React from 'react'
|
|
2
|
-
import { Check, Circle } from 'lucide-react'
|
|
3
|
-
import { cn } from '../utils/cn'
|
|
4
|
-
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '../primitives/card'
|
|
5
|
-
import { Button } from '../primitives/button'
|
|
6
|
-
import { Skeleton } from '../primitives/skeleton'
|
|
7
|
-
import { renderIcon } from './icon'
|
|
8
|
-
import type { IconRef } from './types'
|
|
9
|
-
|
|
10
|
-
export interface OnboardingStepInput {
|
|
11
|
-
id: string
|
|
12
|
-
title: string
|
|
13
|
-
description?: string
|
|
14
|
-
icon?: IconRef
|
|
15
|
-
/** Async check: resolves true when the step is already complete. */
|
|
16
|
-
check?: () => Promise<boolean>
|
|
17
|
-
/** Pre-resolved completion (takes precedence over `check`). */
|
|
18
|
-
done?: boolean
|
|
19
|
-
/** Route string (uses onNavigate) or a callback. */
|
|
20
|
-
action?: string | (() => void)
|
|
21
|
-
actionLabel?: string
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface OnboardingWidgetProps {
|
|
25
|
-
title?: string
|
|
26
|
-
subtitle?: string
|
|
27
|
-
steps: OnboardingStepInput[]
|
|
28
|
-
onNavigate?: (path: string) => void
|
|
29
|
-
/** Hide the whole widget once every step is complete. Default: true. */
|
|
30
|
-
hideWhenComplete?: boolean
|
|
31
|
-
className?: string
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** Placeholder mirroring the widget's layout while the async checks resolve. */
|
|
35
|
-
function OnboardingSkeleton({ rows, className }: { rows: number; className?: string }) {
|
|
36
|
-
return (
|
|
37
|
-
<Card className={className} aria-busy="true">
|
|
38
|
-
<CardHeader>
|
|
39
|
-
<Skeleton className="h-5 w-40" />
|
|
40
|
-
<Skeleton className="mt-1.5 h-3.5 w-64 max-w-full" />
|
|
41
|
-
<div className="mt-2 flex items-center gap-3">
|
|
42
|
-
<Skeleton className="h-1.5 flex-1 rounded-full" />
|
|
43
|
-
<Skeleton className="h-3 w-8" />
|
|
44
|
-
</div>
|
|
45
|
-
</CardHeader>
|
|
46
|
-
<CardContent className="space-y-1 pt-0">
|
|
47
|
-
{Array.from({ length: Math.max(rows, 1) }).map((_, i) => (
|
|
48
|
-
<div key={i} className="flex items-center gap-3 px-2 py-2.5">
|
|
49
|
-
<Skeleton className="h-7 w-7 shrink-0 rounded-full" />
|
|
50
|
-
<div className="min-w-0 flex-1 space-y-1.5">
|
|
51
|
-
<Skeleton className="h-3.5 w-1/2" />
|
|
52
|
-
<Skeleton className="h-3 w-3/4" />
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
))}
|
|
56
|
-
</CardContent>
|
|
57
|
-
</Card>
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/** Getting-started checklist ("first steps to set up") with progress. */
|
|
62
|
-
export function OnboardingWidget({
|
|
63
|
-
title = 'Getting started', subtitle, steps, onNavigate, hideWhenComplete = true, className,
|
|
64
|
-
}: OnboardingWidgetProps) {
|
|
65
|
-
const [completed, setCompleted] = React.useState<Record<string, boolean>>(() =>
|
|
66
|
-
Object.fromEntries(steps.map((s) => [s.id, Boolean(s.done)])),
|
|
67
|
-
)
|
|
68
|
-
// Async checks start unresolved: don't paint until they settle, or an
|
|
69
|
-
// already-onboarded tenant flashes the checklist for the query round-trip
|
|
70
|
-
// before allDone hides it (mount → visible 0/N → resolve → null).
|
|
71
|
-
const [resolved, setResolved] = React.useState(() => steps.every((s) => s.done != null || !s.check))
|
|
72
|
-
|
|
73
|
-
React.useEffect(() => {
|
|
74
|
-
let cancelled = false
|
|
75
|
-
Promise.all(steps.map(async (s) => {
|
|
76
|
-
if (s.done != null) return [s.id, s.done] as const
|
|
77
|
-
if (!s.check) return [s.id, false] as const
|
|
78
|
-
try { return [s.id, await s.check()] as const } catch { return [s.id, false] as const }
|
|
79
|
-
})).then((entries) => {
|
|
80
|
-
if (cancelled) return
|
|
81
|
-
setCompleted(Object.fromEntries(entries))
|
|
82
|
-
setResolved(true)
|
|
83
|
-
})
|
|
84
|
-
return () => { cancelled = true }
|
|
85
|
-
}, [steps])
|
|
86
|
-
|
|
87
|
-
const doneCount = steps.filter((s) => completed[s.id]).length
|
|
88
|
-
const allDone = doneCount === steps.length
|
|
89
|
-
// Hold the card's real shape while the checks settle. Painting the resolved
|
|
90
|
-
// checklist here would flash 0/N at an already-onboarded tenant, and painting
|
|
91
|
-
// nothing collapsed the row and shifted the dashboard on resolve.
|
|
92
|
-
if (!resolved) return <OnboardingSkeleton rows={steps.length} className={className} />
|
|
93
|
-
if (allDone && hideWhenComplete) return null
|
|
94
|
-
|
|
95
|
-
const runAction = (action: OnboardingStepInput['action']) => {
|
|
96
|
-
if (!action) return
|
|
97
|
-
if (typeof action === 'string') onNavigate?.(action)
|
|
98
|
-
else action()
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<Card className={className}>
|
|
103
|
-
<CardHeader>
|
|
104
|
-
<CardTitle>{title}</CardTitle>
|
|
105
|
-
{subtitle ? <CardDescription>{subtitle}</CardDescription> : null}
|
|
106
|
-
<div className="mt-2 flex items-center gap-3">
|
|
107
|
-
<div className="h-1.5 flex-1 overflow-hidden rounded-full bg-muted">
|
|
108
|
-
<div className="h-full rounded-full bg-primary transition-all"
|
|
109
|
-
style={{ width: `${steps.length ? (doneCount / steps.length) * 100 : 0}%` }} />
|
|
110
|
-
</div>
|
|
111
|
-
<span className="text-xs font-medium text-muted-foreground">{doneCount}/{steps.length}</span>
|
|
112
|
-
</div>
|
|
113
|
-
</CardHeader>
|
|
114
|
-
<CardContent className="space-y-1 pt-0">
|
|
115
|
-
{steps.map((step) => {
|
|
116
|
-
const isDone = completed[step.id]
|
|
117
|
-
return (
|
|
118
|
-
<div key={step.id} className="flex items-center gap-3 rounded-lg px-2 py-2.5 hover:bg-muted/50">
|
|
119
|
-
<span className={cn(
|
|
120
|
-
'flex h-7 w-7 shrink-0 items-center justify-center rounded-full',
|
|
121
|
-
isDone ? 'bg-emerald-100 text-emerald-600' : 'bg-muted text-muted-foreground',
|
|
122
|
-
)}>
|
|
123
|
-
{isDone ? <Check className="h-4 w-4" /> : (renderIcon(step.icon, 'h-4 w-4') ?? <Circle className="h-4 w-4" />)}
|
|
124
|
-
</span>
|
|
125
|
-
<div className="min-w-0 flex-1">
|
|
126
|
-
<p className={cn('text-sm font-medium', isDone && 'text-muted-foreground line-through')}>{step.title}</p>
|
|
127
|
-
{step.description ? <p className="truncate text-xs text-muted-foreground">{step.description}</p> : null}
|
|
128
|
-
</div>
|
|
129
|
-
{!isDone && step.action ? (
|
|
130
|
-
<Button size="sm" variant="outline" onClick={() => runAction(step.action)}>
|
|
131
|
-
{step.actionLabel ?? 'Start'}
|
|
132
|
-
</Button>
|
|
133
|
-
) : null}
|
|
134
|
-
</div>
|
|
135
|
-
)
|
|
136
|
-
})}
|
|
137
|
-
</CardContent>
|
|
138
|
-
</Card>
|
|
139
|
-
)
|
|
140
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import * as React from 'react'
|
|
2
|
-
import type { ColumnDef } from '@tanstack/react-table'
|
|
3
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../primitives/card'
|
|
4
|
-
import { DataTable } from '../primitives/data-table'
|
|
5
|
-
import { renderIcon } from './icon'
|
|
6
|
-
import type { IconRef } from './types'
|
|
7
|
-
|
|
8
|
-
export interface TableWidgetProps<TData> {
|
|
9
|
-
title?: string
|
|
10
|
-
icon?: IconRef
|
|
11
|
-
columns: ColumnDef<TData, unknown>[]
|
|
12
|
-
data: TData[]
|
|
13
|
-
loading?: boolean
|
|
14
|
-
emptyMessage?: string
|
|
15
|
-
onRowClick?: (row: TData) => void
|
|
16
|
-
/** Right-aligned header content (e.g. a "view all" link). */
|
|
17
|
-
headerAction?: React.ReactNode
|
|
18
|
-
className?: string
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** "Recent N" table in a Card — wraps the shared DataTable primitive. */
|
|
22
|
-
export function TableWidget<TData>({
|
|
23
|
-
title, icon, columns, data, loading, emptyMessage, onRowClick, headerAction, className,
|
|
24
|
-
}: TableWidgetProps<TData>) {
|
|
25
|
-
return (
|
|
26
|
-
<Card className={className}>
|
|
27
|
-
{(title || headerAction) && (
|
|
28
|
-
<CardHeader className="flex-row items-center justify-between space-y-0">
|
|
29
|
-
{title ? (
|
|
30
|
-
<CardTitle className="flex items-center gap-2">
|
|
31
|
-
{renderIcon(icon)}
|
|
32
|
-
{title}
|
|
33
|
-
</CardTitle>
|
|
34
|
-
) : <span />}
|
|
35
|
-
{headerAction}
|
|
36
|
-
</CardHeader>
|
|
37
|
-
)}
|
|
38
|
-
<CardContent className="p-0">
|
|
39
|
-
<DataTable
|
|
40
|
-
columns={columns}
|
|
41
|
-
data={data}
|
|
42
|
-
loading={loading}
|
|
43
|
-
emptyMessage={emptyMessage}
|
|
44
|
-
onRowClick={onRowClick}
|
|
45
|
-
variant="flat"
|
|
46
|
-
compact
|
|
47
|
-
/>
|
|
48
|
-
</CardContent>
|
|
49
|
-
</Card>
|
|
50
|
-
)
|
|
51
|
-
}
|
package/src/dashboard/define.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { DashboardWidgetDef, DashboardWidgetKind, DashboardSurface } from '@fayz-ai/core'
|
|
2
|
-
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
// Widget-definition builders. Each sets the right `kind` + default `span` so a
|
|
5
|
-
// plugin author writes data, not layout. The widget's component renders the
|
|
6
|
-
// presentational primitives (KpiCard, ChartWidget, TableWidget, ...).
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
|
|
9
|
-
export interface DefineWidgetInput {
|
|
10
|
-
id: string
|
|
11
|
-
title: string
|
|
12
|
-
component: React.ComponentType<unknown>
|
|
13
|
-
description?: string
|
|
14
|
-
icon?: string
|
|
15
|
-
domain?: string
|
|
16
|
-
span?: 1 | 2 | 3 | 4
|
|
17
|
-
defaultVisible?: boolean
|
|
18
|
-
defaultOrder?: number
|
|
19
|
-
surfaces?: DashboardSurface[]
|
|
20
|
-
verticalId?: DashboardWidgetDef['verticalId']
|
|
21
|
-
permission?: DashboardWidgetDef['permission']
|
|
22
|
-
props?: Record<string, unknown>
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function build(kind: DashboardWidgetKind, defaultSpan: 1 | 2 | 3 | 4, input: DefineWidgetInput): DashboardWidgetDef {
|
|
26
|
-
return {
|
|
27
|
-
kind,
|
|
28
|
-
span: input.span ?? defaultSpan,
|
|
29
|
-
defaultVisible: input.defaultVisible ?? true,
|
|
30
|
-
id: input.id,
|
|
31
|
-
title: input.title,
|
|
32
|
-
description: input.description,
|
|
33
|
-
icon: input.icon,
|
|
34
|
-
domain: input.domain,
|
|
35
|
-
defaultOrder: input.defaultOrder,
|
|
36
|
-
surfaces: input.surfaces,
|
|
37
|
-
verticalId: input.verticalId,
|
|
38
|
-
permission: input.permission,
|
|
39
|
-
component: input.component,
|
|
40
|
-
props: input.props,
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** KPI card widget — spans 1 column (forms the 4-up top row). */
|
|
45
|
-
export const defineKpiWidget = (input: DefineWidgetInput): DashboardWidgetDef => build('kpi', 1, input)
|
|
46
|
-
/** Chart widget — spans 2 columns by default. */
|
|
47
|
-
export const defineChartWidget = (input: DefineWidgetInput): DashboardWidgetDef => build('chart', 2, input)
|
|
48
|
-
/** Table widget — spans the full row by default. */
|
|
49
|
-
export const defineTableWidget = (input: DefineWidgetInput): DashboardWidgetDef => build('table', 4, input)
|
|
50
|
-
/** Onboarding checklist widget — spans half the row (2 cols) by default. */
|
|
51
|
-
export const defineOnboardingWidget = (input: DefineWidgetInput): DashboardWidgetDef => build('onboarding', 2, input)
|
|
52
|
-
/** Custom client-provided widget — spans 2 columns by default. */
|
|
53
|
-
export const defineCustomWidget = (input: DefineWidgetInput): DashboardWidgetDef => build('custom', 2, input)
|
package/src/dashboard/icon.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import * as React from 'react'
|
|
2
|
-
import { getLucideIcon } from '../icons'
|
|
3
|
-
import type { IconRef } from './types'
|
|
4
|
-
|
|
5
|
-
/** Resolve an IconRef (lucide name or component) to a renderable element. */
|
|
6
|
-
export function renderIcon(icon: IconRef | undefined, className = 'h-4 w-4'): React.ReactNode {
|
|
7
|
-
if (!icon) return null
|
|
8
|
-
if (typeof icon === 'string') {
|
|
9
|
-
const Comp = getLucideIcon(icon)
|
|
10
|
-
return Comp ? React.createElement(Comp, { className }) : null
|
|
11
|
-
}
|
|
12
|
-
return React.createElement(icon, { className })
|
|
13
|
-
}
|
package/src/dashboard/index.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
// Dashboard widget kit — rendering engine + presentational primitives + builders.
|
|
2
|
-
export {
|
|
3
|
-
DashboardCanvas,
|
|
4
|
-
WidgetGrid,
|
|
5
|
-
DashboardNavigateProvider,
|
|
6
|
-
useDashboardNavigate,
|
|
7
|
-
type DashboardCanvasProps,
|
|
8
|
-
} from './DashboardCanvas'
|
|
9
|
-
export { resolveDashboardLayout, type LaidOutWidget } from './layout'
|
|
10
|
-
export { useDashboardPreferences, type DashboardPreferencesState } from './preferences'
|
|
11
|
-
export { DashboardCustomizeMenu, type DashboardCustomizeMenuProps } from './DashboardCustomizeMenu'
|
|
12
|
-
export { DashboardGrid, type DashboardGridItem, type DashboardGridProps } from './DashboardGrid'
|
|
13
|
-
export { GoldCard, type GoldCardProps } from './GoldCard'
|
|
14
|
-
export {
|
|
15
|
-
DashboardRangeProvider,
|
|
16
|
-
DashboardRangeControl,
|
|
17
|
-
useDashboardRange,
|
|
18
|
-
DEFAULT_RANGE_OPTIONS,
|
|
19
|
-
type DashboardRange,
|
|
20
|
-
type DashboardRangeProviderProps,
|
|
21
|
-
} from './DashboardRange'
|
|
22
|
-
export type { DashboardRangeOptions } from './DashboardCanvas'
|
|
23
|
-
export { KpiCard, formatKpiValue, type KpiCardProps } from './KpiCard'
|
|
24
|
-
export { ChartWidget, type ChartWidgetProps } from './ChartWidget'
|
|
25
|
-
export { TableWidget, type TableWidgetProps } from './TableWidget'
|
|
26
|
-
export { OnboardingWidget, type OnboardingStepInput, type OnboardingWidgetProps } from './OnboardingWidget'
|
|
27
|
-
export { renderIcon } from './icon'
|
|
28
|
-
export {
|
|
29
|
-
defineKpiWidget,
|
|
30
|
-
defineChartWidget,
|
|
31
|
-
defineTableWidget,
|
|
32
|
-
defineOnboardingWidget,
|
|
33
|
-
defineCustomWidget,
|
|
34
|
-
type DefineWidgetInput,
|
|
35
|
-
} from './define'
|
|
36
|
-
export type {
|
|
37
|
-
KpiFormat, KpiTrend, KpiValue, KpiCurrency, IconRef, ChartType, ChartSeries,
|
|
38
|
-
} from './types'
|
package/src/dashboard/layout.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import type { DashboardLayoutConfig, DashboardWidgetDef } from '@fayz-ai/core'
|
|
2
|
-
|
|
3
|
-
export interface LaidOutWidget {
|
|
4
|
-
def: DashboardWidgetDef
|
|
5
|
-
/** Effective span after applying overrides (1–4). */
|
|
6
|
-
span: number
|
|
7
|
-
order: number
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Resolve the widgets to render on a surface, in order, applying three layers:
|
|
12
|
-
* 1. registration defaults (defaultVisible / defaultOrder / span)
|
|
13
|
-
* 2. app config (the app curates the home surface)
|
|
14
|
-
* 3. user preferences (show/hide/reorder — read but currently empty)
|
|
15
|
-
*
|
|
16
|
-
* Later layers win. Returns only visible widgets, sorted by effective order.
|
|
17
|
-
* Accepts any DashboardWidgetDef (registry-resolved or shim-built).
|
|
18
|
-
*/
|
|
19
|
-
export function resolveDashboardLayout(
|
|
20
|
-
registered: DashboardWidgetDef[],
|
|
21
|
-
appConfig?: DashboardLayoutConfig,
|
|
22
|
-
userPrefs?: DashboardLayoutConfig,
|
|
23
|
-
/** When true, widgets are visible by default regardless of their
|
|
24
|
-
* `defaultVisible` (used on a plugin-home to show everything; the global
|
|
25
|
-
* home leaves this false so `defaultVisible:false` curates the start set). */
|
|
26
|
-
forceVisibleByDefault = false,
|
|
27
|
-
): LaidOutWidget[] {
|
|
28
|
-
type Override = { visible?: boolean; order?: number; span?: number }
|
|
29
|
-
const overrides = new Map<string, Override>()
|
|
30
|
-
const merge = (cfg?: DashboardLayoutConfig) => {
|
|
31
|
-
for (const w of cfg?.widgets ?? []) {
|
|
32
|
-
overrides.set(w.id, { ...overrides.get(w.id), ...w })
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
merge(appConfig)
|
|
36
|
-
merge(userPrefs)
|
|
37
|
-
|
|
38
|
-
return registered
|
|
39
|
-
.map((def, index) => {
|
|
40
|
-
const o = overrides.get(def.id)
|
|
41
|
-
const baseVisible = forceVisibleByDefault ? true : (def.defaultVisible ?? true)
|
|
42
|
-
const visible = o?.visible ?? baseVisible
|
|
43
|
-
const order = o?.order ?? def.defaultOrder ?? index
|
|
44
|
-
const span = Math.min(Math.max(o?.span ?? def.span ?? 1, 1), 4)
|
|
45
|
-
return { def, visible, order, span }
|
|
46
|
-
})
|
|
47
|
-
.filter((x) => x.visible)
|
|
48
|
-
.sort((a, b) => a.order - b.order)
|
|
49
|
-
.map(({ def, span, order }) => ({ def, span, order }))
|
|
50
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import * as React from 'react'
|
|
2
|
-
import type { DashboardLayoutConfig, DashboardSurface } from '@fayz-ai/core'
|
|
3
|
-
|
|
4
|
-
export interface DashboardPreferencesState {
|
|
5
|
-
/** User-level layout overrides for this surface (null when none saved). */
|
|
6
|
-
prefs: DashboardLayoutConfig | null
|
|
7
|
-
/** Persist updated overrides. */
|
|
8
|
-
save: (next: DashboardLayoutConfig) => void
|
|
9
|
-
/** Toggle a single widget's visibility and persist. */
|
|
10
|
-
setVisible: (widgetId: string, visible: boolean) => void
|
|
11
|
-
/** Clear all overrides (reset to registration + app defaults). */
|
|
12
|
-
reset: () => void
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const KEY = (surface: DashboardSurface, scope: string) => `fayz.dashboard.prefs.${scope}.${surface}`
|
|
16
|
-
|
|
17
|
-
function read(key: string): DashboardLayoutConfig | null {
|
|
18
|
-
if (typeof window === 'undefined') return null
|
|
19
|
-
try {
|
|
20
|
-
const raw = window.localStorage.getItem(key)
|
|
21
|
-
return raw ? (JSON.parse(raw) as DashboardLayoutConfig) : null
|
|
22
|
-
} catch {
|
|
23
|
-
return null
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Per-user dashboard preferences, persisted to localStorage.
|
|
29
|
-
*
|
|
30
|
-
* `scope` lets a plugin-home keep its own prefs separate from the global home
|
|
31
|
-
* (pass the domain). A future pass can swap the storage for the tenant/plugin
|
|
32
|
-
* binding store without touching callers — `resolveDashboardLayout` already
|
|
33
|
-
* consumes whatever this returns as the `userPrefs` layer.
|
|
34
|
-
*/
|
|
35
|
-
export function useDashboardPreferences(surface: DashboardSurface, scope = 'home'): DashboardPreferencesState {
|
|
36
|
-
const key = KEY(surface, scope)
|
|
37
|
-
const [prefs, setPrefs] = React.useState<DashboardLayoutConfig | null>(() => read(key))
|
|
38
|
-
|
|
39
|
-
// Re-read when the target surface/scope changes.
|
|
40
|
-
React.useEffect(() => { setPrefs(read(key)) }, [key])
|
|
41
|
-
|
|
42
|
-
const save = React.useCallback((next: DashboardLayoutConfig) => {
|
|
43
|
-
setPrefs(next)
|
|
44
|
-
if (typeof window !== 'undefined') {
|
|
45
|
-
try { window.localStorage.setItem(key, JSON.stringify(next)) } catch { /* ignore quota/availability */ }
|
|
46
|
-
}
|
|
47
|
-
}, [key])
|
|
48
|
-
|
|
49
|
-
const setVisible = React.useCallback((widgetId: string, visible: boolean) => {
|
|
50
|
-
setPrefs((cur) => {
|
|
51
|
-
const widgets = [...(cur?.widgets ?? [])]
|
|
52
|
-
const i = widgets.findIndex((w) => w.id === widgetId)
|
|
53
|
-
if (i >= 0) widgets[i] = { ...widgets[i]!, visible }
|
|
54
|
-
else widgets.push({ id: widgetId, visible })
|
|
55
|
-
const next = { ...cur, widgets }
|
|
56
|
-
if (typeof window !== 'undefined') {
|
|
57
|
-
try { window.localStorage.setItem(key, JSON.stringify(next)) } catch { /* ignore */ }
|
|
58
|
-
}
|
|
59
|
-
return next
|
|
60
|
-
})
|
|
61
|
-
}, [key])
|
|
62
|
-
|
|
63
|
-
const reset = React.useCallback(() => {
|
|
64
|
-
setPrefs(null)
|
|
65
|
-
if (typeof window !== 'undefined') {
|
|
66
|
-
try { window.localStorage.removeItem(key) } catch { /* ignore */ }
|
|
67
|
-
}
|
|
68
|
-
}, [key])
|
|
69
|
-
|
|
70
|
-
return { prefs, save, setVisible, reset }
|
|
71
|
-
}
|
package/src/dashboard/types.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import type React from 'react'
|
|
2
|
-
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
// Shared, dependency-free types for the dashboard widget kit. These mirror the
|
|
5
|
-
// shapes in plugin-dashboard but live here so plugins can render widgets
|
|
6
|
-
// without depending on plugin-dashboard.
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
|
|
9
|
-
export type KpiFormat = 'number' | 'currency' | 'percent' | 'duration'
|
|
10
|
-
export type KpiTrend = 'up' | 'down' | 'neutral'
|
|
11
|
-
|
|
12
|
-
export interface KpiValue {
|
|
13
|
-
value: number
|
|
14
|
-
/** Comparison value used to derive the trend chip. */
|
|
15
|
-
previousValue?: number
|
|
16
|
-
/** Unit suffix (e.g. 'min'). */
|
|
17
|
-
unit?: string
|
|
18
|
-
trend?: KpiTrend
|
|
19
|
-
/** Optional series (oldest → newest) rendered as an inline sparkline. */
|
|
20
|
-
spark?: number[]
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface KpiCurrency {
|
|
24
|
-
code?: string
|
|
25
|
-
locale?: string
|
|
26
|
-
symbol?: string
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** A lucide icon name (resolved at render) or a React component. */
|
|
30
|
-
export type IconRef = string | React.ComponentType<{ className?: string }>
|
|
31
|
-
|
|
32
|
-
export type ChartType = 'line' | 'bar' | 'area' | 'pie'
|
|
33
|
-
|
|
34
|
-
export interface ChartSeries {
|
|
35
|
-
/** Key into each data row. */
|
|
36
|
-
dataKey: string
|
|
37
|
-
/** Display label (legend/tooltip). */
|
|
38
|
-
label?: string
|
|
39
|
-
/** Explicit color; falls back to the theme palette. */
|
|
40
|
-
color?: string
|
|
41
|
-
}
|
package/src/icons.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import type { LucideIcon } from 'lucide-react'
|
|
2
|
-
import {
|
|
3
|
-
Activity, AlertTriangle, Apple, ArrowDownCircle, ArrowDownRight, ArrowLeftRight, ArrowUpCircle, ArrowUpRight,
|
|
4
|
-
Award, BadgeDollarSign, Ban, Banknote, BarChart3, Bell, Bike, BookOpen,
|
|
5
|
-
BookOpenCheck, Box, Boxes, Briefcase, Building2, Calendar, CalendarCheck, CalendarCheck2,
|
|
6
|
-
CalendarClock, CalendarDays, CalendarPlus, CalendarX, Camera, Cat, Check, CheckCircle,
|
|
7
|
-
CheckSquare, ChefHat, ChevronsUpDown, Circle, CircleCheck, CircleDollarSign, Clapperboard, ClipboardList,
|
|
8
|
-
Clock, Clock3, Command, Compass, Contact, CreditCard, Disc3, Dog,
|
|
9
|
-
DollarSign, DoorOpen, Egg, ExternalLink, Eye, FileCheck2, FileDown,
|
|
10
|
-
FileText, Filter, Flame, FolderOpen, Globe, GraduationCap, Handshake, Heart,
|
|
11
|
-
Home, Image, Inbox, Instagram, Landmark, Layers, LayoutDashboard, LayoutTemplate,
|
|
12
|
-
Leaf, LeafyGreen, Link, List, ListChecks, ListMusic, ListPlus, Lock,
|
|
13
|
-
Mail, Map, MapPin, Megaphone, MessageCircle, MessageSquare, Mic, MoreHorizontal,
|
|
14
|
-
MousePointerClick, Music, Newspaper, Package, PackageCheck, Palette, PartyPopper, PawPrint,
|
|
15
|
-
Pencil, Percent, Phone, PieChart, Plug, Plus, PlusCircle, Puzzle, Radio,
|
|
16
|
-
Receipt, Repeat, RotateCcw, Ruler, Search, Send, Settings, Shield,
|
|
17
|
-
ShieldCheck, ShoppingBag, ShoppingCart, Sigma, SlidersHorizontal, Sparkles, Star, Tag,
|
|
18
|
-
Tags, Target, ToggleLeft, TreePalm, TrendingDown, TrendingUp, Trophy, Truck,
|
|
19
|
-
User, UserCheck, UserCog, UserPlus, UserX, Users, UsersRound, Utensils,
|
|
20
|
-
UtensilsCrossed, Wallet, Warehouse, Wheat, Workflow, Wrench, X, XCircle,
|
|
21
|
-
Zap,
|
|
22
|
-
} from 'lucide-react'
|
|
23
|
-
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// Central lucide icon registry. The SDK's manifest-driven surfaces resolve
|
|
26
|
-
// icons by string name; a namespace import (`import * as LucideIcons`) defeats
|
|
27
|
-
// tree-shaking and drags all ~1,500 icons (~500 KB min) into every bundle, so
|
|
28
|
-
// the SDK ships this curated set instead — the union of every icon name the
|
|
29
|
-
// SDK, plugins, and app templates reference. Apps using names outside the set
|
|
30
|
-
// call registerIcons({ Name: Component }) with their own named imports.
|
|
31
|
-
// ---------------------------------------------------------------------------
|
|
32
|
-
|
|
33
|
-
const registry: Record<string, LucideIcon> = {
|
|
34
|
-
Activity, AlertTriangle, Apple, ArrowDownCircle, ArrowDownRight, ArrowLeftRight, ArrowUpCircle, ArrowUpRight,
|
|
35
|
-
Award, BadgeDollarSign, Ban, Banknote, BarChart3, Bell, Bike, BookOpen,
|
|
36
|
-
BookOpenCheck, Box, Boxes, Briefcase, Building2, Calendar, CalendarCheck, CalendarCheck2,
|
|
37
|
-
CalendarClock, CalendarDays, CalendarPlus, CalendarX, Camera, Cat, Check, CheckCircle,
|
|
38
|
-
CheckSquare, ChefHat, ChevronsUpDown, Circle, CircleCheck, CircleDollarSign, Clapperboard, ClipboardList,
|
|
39
|
-
Clock, Clock3, Command, Compass, Contact, CreditCard, Disc3, Dog,
|
|
40
|
-
DollarSign, DoorOpen, Egg, ExternalLink, Eye, FileCheck2, FileDown,
|
|
41
|
-
FileText, Filter, Flame, FolderOpen, Globe, GraduationCap, Handshake, Heart,
|
|
42
|
-
Home, Image, Inbox, Instagram, Landmark, Layers, LayoutDashboard, LayoutTemplate,
|
|
43
|
-
Leaf, LeafyGreen, Link, List, ListChecks, ListMusic, ListPlus, Lock,
|
|
44
|
-
Mail, Map, MapPin, Megaphone, MessageCircle, MessageSquare, Mic, MoreHorizontal,
|
|
45
|
-
MousePointerClick, Music, Newspaper, Package, PackageCheck, Palette, PartyPopper, PawPrint,
|
|
46
|
-
Pencil, Percent, Phone, PieChart, Plug, Plus, PlusCircle, Puzzle, Radio,
|
|
47
|
-
Receipt, Repeat, RotateCcw, Ruler, Search, Send, Settings, Shield,
|
|
48
|
-
ShieldCheck, ShoppingBag, ShoppingCart, Sigma, SlidersHorizontal, Sparkles, Star, Tag,
|
|
49
|
-
Tags, Target, ToggleLeft, TreePalm, TrendingDown, TrendingUp, Trophy, Truck,
|
|
50
|
-
User, UserCheck, UserCog, UserPlus, UserX, Users, UsersRound, Utensils,
|
|
51
|
-
UtensilsCrossed, Wallet, Warehouse, Wheat, Workflow, Wrench, X, XCircle,
|
|
52
|
-
Zap,
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** Add or override icons resolvable by string name (e.g. from app manifests). */
|
|
56
|
-
export function registerIcons(icons: Record<string, LucideIcon>): void {
|
|
57
|
-
Object.assign(registry, icons)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** Resolve a lucide icon by name; undefined when not registered. */
|
|
61
|
-
export function getLucideIcon(name: string): LucideIcon | undefined {
|
|
62
|
-
return registry[name]
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** The registered icon map (live reference — do not mutate directly). */
|
|
66
|
-
export const ICON_MAP = registry
|