@creezio/shell-ui 0.22.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 +336 -0
- package/dist/adapters/nav-shell.d.ts +54 -0
- package/dist/adapters/nav-shell.d.ts.map +1 -0
- package/dist/adapters/nav-shell.js +112 -0
- package/dist/adapters/nav-shell.js.map +1 -0
- package/dist/brand.d.ts +64 -0
- package/dist/brand.d.ts.map +1 -0
- package/dist/brand.js +59 -0
- package/dist/brand.js.map +1 -0
- package/dist/core-nav.d.ts +17 -0
- package/dist/core-nav.d.ts.map +1 -0
- package/dist/core-nav.js +40 -0
- package/dist/core-nav.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api-scopes.d.ts +24 -0
- package/dist/lib/api-scopes.d.ts.map +1 -0
- package/dist/lib/api-scopes.js +78 -0
- package/dist/lib/api-scopes.js.map +1 -0
- package/dist/lib/catalog-suspense-key.d.ts +3 -0
- package/dist/lib/catalog-suspense-key.d.ts.map +1 -0
- package/dist/lib/catalog-suspense-key.js +12 -0
- package/dist/lib/catalog-suspense-key.js.map +1 -0
- package/dist/lib/data-changed.d.ts +51 -0
- package/dist/lib/data-changed.d.ts.map +1 -0
- package/dist/lib/data-changed.js +227 -0
- package/dist/lib/data-changed.js.map +1 -0
- package/dist/lib/desktop-home-path.d.ts +14 -0
- package/dist/lib/desktop-home-path.d.ts.map +1 -0
- package/dist/lib/desktop-home-path.js +29 -0
- package/dist/lib/desktop-home-path.js.map +1 -0
- package/dist/lib/geo-distance.d.ts +6 -0
- package/dist/lib/geo-distance.d.ts.map +1 -0
- package/dist/lib/geo-distance.js +27 -0
- package/dist/lib/geo-distance.js.map +1 -0
- package/dist/lib/img.d.ts +2 -0
- package/dist/lib/img.d.ts.map +1 -0
- package/dist/lib/img.js +40 -0
- package/dist/lib/img.js.map +1 -0
- package/dist/lib/keepalive-eviction.d.ts +14 -0
- package/dist/lib/keepalive-eviction.d.ts.map +1 -0
- package/dist/lib/keepalive-eviction.js +44 -0
- package/dist/lib/keepalive-eviction.js.map +1 -0
- package/dist/lib/ops-track.d.ts +29 -0
- package/dist/lib/ops-track.d.ts.map +1 -0
- package/dist/lib/ops-track.js +37 -0
- package/dist/lib/ops-track.js.map +1 -0
- package/dist/lib/optimize-cover-url.d.ts +10 -0
- package/dist/lib/optimize-cover-url.d.ts.map +1 -0
- package/dist/lib/optimize-cover-url.js +80 -0
- package/dist/lib/optimize-cover-url.js.map +1 -0
- package/dist/lib/page-trails.d.ts +9 -0
- package/dist/lib/page-trails.d.ts.map +1 -0
- package/dist/lib/page-trails.js +20 -0
- package/dist/lib/page-trails.js.map +1 -0
- package/dist/lib/public-origin.d.ts +32 -0
- package/dist/lib/public-origin.d.ts.map +1 -0
- package/dist/lib/public-origin.js +125 -0
- package/dist/lib/public-origin.js.map +1 -0
- package/dist/lib/server-incident.d.ts +12 -0
- package/dist/lib/server-incident.d.ts.map +1 -0
- package/dist/lib/server-incident.js +72 -0
- package/dist/lib/server-incident.js.map +1 -0
- package/dist/lib/tab-document-url.d.ts +13 -0
- package/dist/lib/tab-document-url.d.ts.map +1 -0
- package/dist/lib/tab-document-url.js +60 -0
- package/dist/lib/tab-document-url.js.map +1 -0
- package/dist/lib/utils.d.ts +20 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +87 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/nav-catalog.d.ts +97 -0
- package/dist/nav-catalog.d.ts.map +1 -0
- package/dist/nav-catalog.js +282 -0
- package/dist/nav-catalog.js.map +1 -0
- package/dist/registry.d.ts +11 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +60 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +22 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist-cjs/adapters/nav-shell.js +115 -0
- package/dist-cjs/adapters/nav-shell.js.map +1 -0
- package/dist-cjs/brand.js +66 -0
- package/dist-cjs/brand.js.map +1 -0
- package/dist-cjs/core-nav.js +43 -0
- package/dist-cjs/core-nav.js.map +1 -0
- package/dist-cjs/index.js +95 -0
- package/dist-cjs/index.js.map +1 -0
- package/dist-cjs/lib/api-scopes.js +86 -0
- package/dist-cjs/lib/api-scopes.js.map +1 -0
- package/dist-cjs/lib/catalog-suspense-key.js +15 -0
- package/dist-cjs/lib/catalog-suspense-key.js.map +1 -0
- package/dist-cjs/lib/data-changed.js +236 -0
- package/dist-cjs/lib/data-changed.js.map +1 -0
- package/dist-cjs/lib/desktop-home-path.js +34 -0
- package/dist-cjs/lib/desktop-home-path.js.map +1 -0
- package/dist-cjs/lib/geo-distance.js +30 -0
- package/dist-cjs/lib/geo-distance.js.map +1 -0
- package/dist-cjs/lib/img.js +43 -0
- package/dist-cjs/lib/img.js.map +1 -0
- package/dist-cjs/lib/keepalive-eviction.js +49 -0
- package/dist-cjs/lib/keepalive-eviction.js.map +1 -0
- package/dist-cjs/lib/ops-track.js +41 -0
- package/dist-cjs/lib/ops-track.js.map +1 -0
- package/dist-cjs/lib/optimize-cover-url.js +83 -0
- package/dist-cjs/lib/optimize-cover-url.js.map +1 -0
- package/dist-cjs/lib/page-trails.js +25 -0
- package/dist-cjs/lib/page-trails.js.map +1 -0
- package/dist-cjs/lib/public-origin.js +130 -0
- package/dist-cjs/lib/public-origin.js.map +1 -0
- package/dist-cjs/lib/server-incident.js +75 -0
- package/dist-cjs/lib/server-incident.js.map +1 -0
- package/dist-cjs/lib/tab-document-url.js +65 -0
- package/dist-cjs/lib/tab-document-url.js.map +1 -0
- package/dist-cjs/lib/utils.js +97 -0
- package/dist-cjs/lib/utils.js.map +1 -0
- package/dist-cjs/nav-catalog.js +293 -0
- package/dist-cjs/nav-catalog.js.map +1 -0
- package/dist-cjs/package.json +3 -0
- package/dist-cjs/registry.js +64 -0
- package/dist-cjs/registry.js.map +1 -0
- package/dist-cjs/types.js +3 -0
- package/dist-cjs/types.js.map +1 -0
- package/package.json +163 -0
- package/ui/app-error-boundary.tsx +59 -0
- package/ui/data-table.tsx +121 -0
- package/ui/desktop/auth-window-chrome.tsx +51 -0
- package/ui/desktop/desktop-bridge.tsx +87 -0
- package/ui/desktop/external-site-slot.tsx +313 -0
- package/ui/desktop/external-site-surface.ts +58 -0
- package/ui/desktop/site-link.tsx +105 -0
- package/ui/desktop/window-chrome-controls.tsx +5 -0
- package/ui/desktop-types.ts +30 -0
- package/ui/faceted-filters.tsx +119 -0
- package/ui/global-search-host.ts +20 -0
- package/ui/global-search.tsx +66 -0
- package/ui/index.ts +178 -0
- package/ui/kit.ts +30 -0
- package/ui/layout/app-shell.tsx +58 -0
- package/ui/layout/desktop-update-banner.tsx +100 -0
- package/ui/layout/entity-header.tsx +44 -0
- package/ui/layout/native-os-nav.ts +104 -0
- package/ui/layout/nav-catalog-loader.tsx +98 -0
- package/ui/layout/nav-icons.ts +67 -0
- package/ui/layout/page-chrome.tsx +90 -0
- package/ui/layout/page-toolbar-context.tsx +82 -0
- package/ui/layout/sandbox-banner.tsx +38 -0
- package/ui/layout/section-view-shell.tsx +73 -0
- package/ui/layout/sidebar-host.ts +144 -0
- package/ui/layout/sidebar.tsx +1150 -0
- package/ui/lib/ai-screencast-hub.ts +127 -0
- package/ui/lib/ai-workspace-client.ts +46 -0
- package/ui/lib/aid.ts +22 -0
- package/ui/lib/desktop-host.ts +114 -0
- package/ui/lib/fleet-tracker-client.ts +237 -0
- package/ui/lib/hermes-ui.ts +114 -0
- package/ui/lib/n8n-ui.ts +115 -0
- package/ui/lib/use-creezio-resource.tsx +121 -0
- package/ui/lib/use-shell-ui-brand.ts +22 -0
- package/ui/list-toolbar.tsx +309 -0
- package/ui/os-pages/desktop-settings-page.tsx +158 -0
- package/ui/os-pages/index.ts +8 -0
- package/ui/page-loading/entity-page-loading.tsx +59 -0
- package/ui/page-loading/list-page-loading.tsx +61 -0
- package/ui/pagination.tsx +56 -0
- package/ui/primitives/avatar.tsx +33 -0
- package/ui/primitives/badge.tsx +36 -0
- package/ui/primitives/breadcrumb.tsx +93 -0
- package/ui/primitives/button.tsx +44 -0
- package/ui/primitives/card.tsx +44 -0
- package/ui/primitives/chart.tsx +317 -0
- package/ui/primitives/command.tsx +111 -0
- package/ui/primitives/dialog.tsx +100 -0
- package/ui/primitives/dropdown-menu.tsx +187 -0
- package/ui/primitives/input.tsx +19 -0
- package/ui/primitives/label.tsx +17 -0
- package/ui/primitives/resizable.tsx +45 -0
- package/ui/primitives/scroll-area.tsx +41 -0
- package/ui/primitives/select.tsx +73 -0
- package/ui/primitives/separator.tsx +26 -0
- package/ui/primitives/sheet.tsx +90 -0
- package/ui/primitives/skeleton.tsx +7 -0
- package/ui/primitives/sonner.tsx +22 -0
- package/ui/primitives/tabs.tsx +51 -0
- package/ui/primitives/textarea.tsx +22 -0
- package/ui/primitives/tooltip.tsx +31 -0
- package/ui/pwa/client-error-reporter.tsx +62 -0
- package/ui/pwa/register-sw.tsx +50 -0
- package/ui/range-filters.tsx +174 -0
- package/ui/search/global-search-config.ts +32 -0
- package/ui/search/global-search-provider.tsx +528 -0
- package/ui/search/search-history.ts +74 -0
- package/ui/search-input.tsx +146 -0
- package/ui/settings/account-settings.tsx +140 -0
- package/ui/settings/agent-profile-settings.tsx +184 -0
- package/ui/settings/api-keys-settings.tsx +349 -0
- package/ui/settings/desktop-background-settings.tsx +128 -0
- package/ui/settings/desktop-connection-settings.tsx +251 -0
- package/ui/settings/desktop-embed-env-panel.tsx +257 -0
- package/ui/settings/desktop-fleet-telemetry-settings.tsx +269 -0
- package/ui/settings/desktop-hermes-settings.tsx +341 -0
- package/ui/settings/desktop-llm-keys.tsx +192 -0
- package/ui/settings/desktop-n8n-settings.tsx +306 -0
- package/ui/settings/desktop-tunnel.tsx +264 -0
- package/ui/settings/desktop-update-settings.tsx +175 -0
- package/ui/settings/factory-reset-settings.tsx +89 -0
- package/ui/settings/host-only-settings.tsx +62 -0
- package/ui/settings/locked-config-field.tsx +30 -0
- package/ui/settings/ops-diagnostic-settings.tsx +242 -0
- package/ui/settings/search-reindex-settings.tsx +158 -0
- package/ui/settings/server-mode-cards.tsx +801 -0
- package/ui/theme/tailwind-preset.cjs +82 -0
- package/ui/theme/theme.css +328 -0
- package/ui/workspace/ai-activity-panel-host.tsx +19 -0
- package/ui/workspace/ai-workspace-agent-host.tsx +54 -0
- package/ui/workspace/ai-workspace-banner.tsx +168 -0
- package/ui/workspace/impersonation-banner.tsx +39 -0
- package/ui/workspace/keep-alive.tsx +220 -0
- package/ui/workspace/tab-workspace-context.tsx +1172 -0
- package/ui/workspace/tab-workspace-host.ts +89 -0
- package/ui/workspace/types.ts +500 -0
- package/ui/workspace/use-location-search.tsx +50 -0
- package/ui/workspace/window-chrome-controls.tsx +116 -0
- package/ui/workspace/workspace-config.ts +47 -0
- package/ui/workspace/workspace-root.tsx +147 -0
- package/ui/workspace/workspace-shell.tsx +292 -0
- package/ui/workspace/workspace-tab-bar.tsx +572 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Page OS réglages desktop — composition kit (pas de métier marque).
|
|
5
|
+
* Les marques ne font que `export { DesktopSettingsPage as default }`.
|
|
6
|
+
*
|
|
7
|
+
* Parité Configuration TF (onglets Connexion / Hosts / Compte / Support / Système).
|
|
8
|
+
*/
|
|
9
|
+
import { AccountSettings } from "../settings/account-settings";
|
|
10
|
+
import { AgentProfileSettings } from "../settings/agent-profile-settings";
|
|
11
|
+
import { ApiKeysSettings } from "../settings/api-keys-settings";
|
|
12
|
+
import { DesktopConnectionSettings } from "../settings/desktop-connection-settings";
|
|
13
|
+
import { DesktopHermesSettings } from "../settings/desktop-hermes-settings";
|
|
14
|
+
import { DesktopN8nSettings } from "../settings/desktop-n8n-settings";
|
|
15
|
+
import { DesktopTunnel } from "../settings/desktop-tunnel";
|
|
16
|
+
import { DesktopLlmKeys } from "../settings/desktop-llm-keys";
|
|
17
|
+
import { DesktopUpdateSettings } from "../settings/desktop-update-settings";
|
|
18
|
+
import { DesktopBackgroundSettings } from "../settings/desktop-background-settings";
|
|
19
|
+
import { DesktopFleetTelemetrySettings } from "../settings/desktop-fleet-telemetry-settings";
|
|
20
|
+
import { FactoryResetSettings } from "../settings/factory-reset-settings";
|
|
21
|
+
import { OpsDiagnosticSettings } from "../settings/ops-diagnostic-settings";
|
|
22
|
+
import { SearchReindexSettings } from "../settings/search-reindex-settings";
|
|
23
|
+
import {
|
|
24
|
+
HostManagedNotice,
|
|
25
|
+
HostOnlySettings,
|
|
26
|
+
} from "../settings/host-only-settings";
|
|
27
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../primitives/tabs";
|
|
28
|
+
import type { ReactNode } from "react";
|
|
29
|
+
|
|
30
|
+
export type DesktopSettingsExtraTab = {
|
|
31
|
+
value: string;
|
|
32
|
+
label: string;
|
|
33
|
+
content: ReactNode;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type DesktopSettingsPageProps = {
|
|
37
|
+
/**
|
|
38
|
+
* Onglets additionnels injectés par la marque / le wrapper os-ui —
|
|
39
|
+
* ex. Email (`MailSettings` de @creezio/mails/ui, dépendance inverse
|
|
40
|
+
* interdite depuis shell-ui). Insérés avant Support.
|
|
41
|
+
*/
|
|
42
|
+
extraTabs?: DesktopSettingsExtraTab[];
|
|
43
|
+
/** Onglet ouvert par défaut (ex. `?tab=email`). Défaut : connexion. */
|
|
44
|
+
defaultTab?: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export function DesktopSettingsPage({
|
|
48
|
+
extraTabs = [],
|
|
49
|
+
defaultTab,
|
|
50
|
+
}: DesktopSettingsPageProps = {}) {
|
|
51
|
+
const known = new Set([
|
|
52
|
+
"connexion",
|
|
53
|
+
"hermes",
|
|
54
|
+
"n8n",
|
|
55
|
+
"tunnel",
|
|
56
|
+
"compte",
|
|
57
|
+
"support",
|
|
58
|
+
"systeme",
|
|
59
|
+
...extraTabs.map((t) => t.value),
|
|
60
|
+
]);
|
|
61
|
+
const initial = defaultTab && known.has(defaultTab) ? defaultTab : "connexion";
|
|
62
|
+
return (
|
|
63
|
+
<section className="mx-auto max-w-4xl px-0 py-4">
|
|
64
|
+
<h1 className="mb-4 text-2xl font-semibold tracking-tight text-slate-900">
|
|
65
|
+
Configuration
|
|
66
|
+
</h1>
|
|
67
|
+
<p className="mb-4 text-sm text-slate-600">
|
|
68
|
+
Chaque service a son onglet — paramètres verrouillés visibles, actions et
|
|
69
|
+
statut
|
|
70
|
+
</p>
|
|
71
|
+
<Tabs defaultValue={initial} className="w-full">
|
|
72
|
+
<TabsList className="mb-4 flex h-auto w-full flex-wrap justify-start gap-1">
|
|
73
|
+
<TabsTrigger value="connexion">Connexion</TabsTrigger>
|
|
74
|
+
<TabsTrigger value="hermes">Hermes</TabsTrigger>
|
|
75
|
+
<TabsTrigger value="n8n">n8n</TabsTrigger>
|
|
76
|
+
<TabsTrigger value="tunnel">Accès distant</TabsTrigger>
|
|
77
|
+
<TabsTrigger value="compte">Compte & clés</TabsTrigger>
|
|
78
|
+
{extraTabs.map((t) => (
|
|
79
|
+
<TabsTrigger key={t.value} value={t.value}>
|
|
80
|
+
{t.label}
|
|
81
|
+
</TabsTrigger>
|
|
82
|
+
))}
|
|
83
|
+
<TabsTrigger value="support">Support</TabsTrigger>
|
|
84
|
+
<TabsTrigger value="systeme">Système</TabsTrigger>
|
|
85
|
+
</TabsList>
|
|
86
|
+
|
|
87
|
+
<TabsContent value="connexion" className="mt-0 space-y-4">
|
|
88
|
+
<DesktopConnectionSettings />
|
|
89
|
+
</TabsContent>
|
|
90
|
+
|
|
91
|
+
<TabsContent value="hermes" className="mt-0 space-y-4">
|
|
92
|
+
<HostOnlySettings
|
|
93
|
+
fallback={<HostManagedNotice label="l'agent Hermes" />}
|
|
94
|
+
>
|
|
95
|
+
<DesktopHermesSettings />
|
|
96
|
+
</HostOnlySettings>
|
|
97
|
+
</TabsContent>
|
|
98
|
+
|
|
99
|
+
<TabsContent value="n8n" className="mt-0 space-y-4">
|
|
100
|
+
<HostOnlySettings fallback={<HostManagedNotice label="n8n" />}>
|
|
101
|
+
<DesktopN8nSettings />
|
|
102
|
+
</HostOnlySettings>
|
|
103
|
+
</TabsContent>
|
|
104
|
+
|
|
105
|
+
<TabsContent value="tunnel" className="mt-0 space-y-4">
|
|
106
|
+
<HostOnlySettings
|
|
107
|
+
fallback={<HostManagedNotice label="l'accès distant (tunnel)" />}
|
|
108
|
+
>
|
|
109
|
+
<DesktopTunnel />
|
|
110
|
+
</HostOnlySettings>
|
|
111
|
+
</TabsContent>
|
|
112
|
+
|
|
113
|
+
<TabsContent value="compte" className="mt-0 space-y-4">
|
|
114
|
+
<HostOnlySettings
|
|
115
|
+
fallback={
|
|
116
|
+
<HostManagedNotice label="le compte propriétaire et les clés IA" />
|
|
117
|
+
}
|
|
118
|
+
>
|
|
119
|
+
<AccountSettings />
|
|
120
|
+
<DesktopLlmKeys />
|
|
121
|
+
</HostOnlySettings>
|
|
122
|
+
<AgentProfileSettings />
|
|
123
|
+
<ApiKeysSettings />
|
|
124
|
+
</TabsContent>
|
|
125
|
+
|
|
126
|
+
{extraTabs.map((t) => (
|
|
127
|
+
<TabsContent key={t.value} value={t.value} className="mt-0 space-y-4">
|
|
128
|
+
{t.content}
|
|
129
|
+
</TabsContent>
|
|
130
|
+
))}
|
|
131
|
+
|
|
132
|
+
<TabsContent value="support" className="mt-0 space-y-4">
|
|
133
|
+
<HostOnlySettings
|
|
134
|
+
fallback={<HostManagedNotice label="la télémétrie support" />}
|
|
135
|
+
>
|
|
136
|
+
<DesktopFleetTelemetrySettings />
|
|
137
|
+
</HostOnlySettings>
|
|
138
|
+
</TabsContent>
|
|
139
|
+
|
|
140
|
+
<TabsContent value="systeme" className="mt-0 space-y-4">
|
|
141
|
+
<DesktopUpdateSettings />
|
|
142
|
+
<DesktopBackgroundSettings />
|
|
143
|
+
<HostOnlySettings
|
|
144
|
+
fallback={
|
|
145
|
+
<HostManagedNotice label="l'index de recherche et la remise à zéro" />
|
|
146
|
+
}
|
|
147
|
+
>
|
|
148
|
+
<SearchReindexSettings />
|
|
149
|
+
<OpsDiagnosticSettings />
|
|
150
|
+
<FactoryResetSettings />
|
|
151
|
+
</HostOnlySettings>
|
|
152
|
+
</TabsContent>
|
|
153
|
+
</Tabs>
|
|
154
|
+
</section>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export default DesktopSettingsPage;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Surfaces UI OS — imports ciblés (évite le barrel /ui qui tire workspace+assistant Node).
|
|
3
|
+
*/
|
|
4
|
+
export { DesktopSettingsPage } from "./desktop-settings-page";
|
|
5
|
+
export type {
|
|
6
|
+
DesktopSettingsPageProps,
|
|
7
|
+
DesktopSettingsExtraTab,
|
|
8
|
+
} from "./desktop-settings-page";
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { AppShell } from "../layout/app-shell";
|
|
2
|
+
import { Skeleton } from "../primitives/skeleton";
|
|
3
|
+
import type { TrailCrumb } from "../workspace/types";
|
|
4
|
+
|
|
5
|
+
/** Squelette fiche entité (galerie + panneau) — pas un tableau liste. */
|
|
6
|
+
export function EntityPageLoading({
|
|
7
|
+
title = "Produit",
|
|
8
|
+
subtitle = "Chargement de la fiche…",
|
|
9
|
+
trail,
|
|
10
|
+
}: {
|
|
11
|
+
title?: string;
|
|
12
|
+
subtitle?: string;
|
|
13
|
+
trail?: TrailCrumb[];
|
|
14
|
+
}) {
|
|
15
|
+
return (
|
|
16
|
+
<AppShell title={title} subtitle={subtitle} kind="entity" trail={trail}>
|
|
17
|
+
<div
|
|
18
|
+
className="space-y-6"
|
|
19
|
+
aria-busy="true"
|
|
20
|
+
aria-label="Chargement de la fiche"
|
|
21
|
+
>
|
|
22
|
+
<div className="grid gap-6 lg:grid-cols-12 lg:items-start">
|
|
23
|
+
<div className="lg:col-span-7">
|
|
24
|
+
<Skeleton className="aspect-square w-full rounded-xl" />
|
|
25
|
+
</div>
|
|
26
|
+
<aside className="lg:col-span-5">
|
|
27
|
+
<div className="rounded-xl border border-slate-200 bg-white p-5 sm:p-6">
|
|
28
|
+
<Skeleton className="h-7 w-3/4" />
|
|
29
|
+
<div className="mt-3 flex flex-wrap gap-2">
|
|
30
|
+
<Skeleton className="h-5 w-20 rounded-full" />
|
|
31
|
+
<Skeleton className="h-5 w-16 rounded-full" />
|
|
32
|
+
</div>
|
|
33
|
+
<div className="mt-5 space-y-2">
|
|
34
|
+
<Skeleton className="h-3 w-24" />
|
|
35
|
+
<Skeleton className="h-9 w-36" />
|
|
36
|
+
<Skeleton className="h-4 w-28" />
|
|
37
|
+
</div>
|
|
38
|
+
<div className="mt-5 grid gap-3 border-t border-slate-100 pt-5 sm:grid-cols-2">
|
|
39
|
+
<Skeleton className="h-10 w-full" />
|
|
40
|
+
<Skeleton className="h-10 w-full" />
|
|
41
|
+
<Skeleton className="h-10 w-full" />
|
|
42
|
+
<Skeleton className="h-10 w-full" />
|
|
43
|
+
</div>
|
|
44
|
+
<Skeleton className="mt-6 h-10 w-full rounded-md" />
|
|
45
|
+
</div>
|
|
46
|
+
</aside>
|
|
47
|
+
</div>
|
|
48
|
+
<div className="rounded-xl border border-slate-200 bg-white p-4">
|
|
49
|
+
<Skeleton className="mb-3 h-4 w-40" />
|
|
50
|
+
<div className="space-y-2">
|
|
51
|
+
<Skeleton className="h-8 w-full" />
|
|
52
|
+
<Skeleton className="h-8 w-full" />
|
|
53
|
+
<Skeleton className="h-8 w-2/3" />
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</AppShell>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { AppShell } from "../layout/app-shell";
|
|
2
|
+
import { Skeleton } from "../primitives/skeleton";
|
|
3
|
+
import type { PageKind, TrailCrumb } from "../workspace/types";
|
|
4
|
+
|
|
5
|
+
/** Shell immédiat pour pages liste lourdes — même pattern JustRent. */
|
|
6
|
+
export function ListPageLoading({
|
|
7
|
+
title,
|
|
8
|
+
subtitle = "Chargement des données…",
|
|
9
|
+
kpiCount = 0,
|
|
10
|
+
filterRows = 1,
|
|
11
|
+
tableRows = 8,
|
|
12
|
+
kind = "section",
|
|
13
|
+
trail,
|
|
14
|
+
}: {
|
|
15
|
+
title: string;
|
|
16
|
+
subtitle?: string;
|
|
17
|
+
kpiCount?: number;
|
|
18
|
+
filterRows?: number;
|
|
19
|
+
tableRows?: number;
|
|
20
|
+
kind?: PageKind;
|
|
21
|
+
trail?: TrailCrumb[];
|
|
22
|
+
}) {
|
|
23
|
+
return (
|
|
24
|
+
<AppShell title={title} subtitle={subtitle} kind={kind} trail={trail}>
|
|
25
|
+
<div className="space-y-4" aria-busy="true" aria-label="Chargement de la page">
|
|
26
|
+
{kpiCount > 0 ? (
|
|
27
|
+
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
|
28
|
+
{Array.from({ length: kpiCount }).map((_, i) => (
|
|
29
|
+
<Skeleton key={i} className="h-28 rounded-xl" />
|
|
30
|
+
))}
|
|
31
|
+
</div>
|
|
32
|
+
) : null}
|
|
33
|
+
<div className="space-y-2 rounded-xl border border-slate-200 bg-white p-3 shadow-sm">
|
|
34
|
+
<Skeleton className="h-9 w-full max-w-md" />
|
|
35
|
+
{Array.from({ length: filterRows }).map((_, i) => (
|
|
36
|
+
<div key={i} className="flex flex-wrap gap-2">
|
|
37
|
+
<Skeleton className="h-8 w-28 rounded-md" />
|
|
38
|
+
<Skeleton className="h-8 w-32 rounded-md" />
|
|
39
|
+
<Skeleton className="h-8 w-24 rounded-md" />
|
|
40
|
+
</div>
|
|
41
|
+
))}
|
|
42
|
+
</div>
|
|
43
|
+
<div className="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
|
|
44
|
+
<div className="border-b border-slate-100 px-4 py-3">
|
|
45
|
+
<Skeleton className="h-4 w-40" />
|
|
46
|
+
</div>
|
|
47
|
+
<div className="divide-y divide-slate-100">
|
|
48
|
+
{Array.from({ length: tableRows }).map((_, i) => (
|
|
49
|
+
<div key={i} className="flex items-center gap-3 px-4 py-3">
|
|
50
|
+
<Skeleton className="h-4 w-24" />
|
|
51
|
+
<Skeleton className="h-4 flex-1" />
|
|
52
|
+
<Skeleton className="h-4 w-16" />
|
|
53
|
+
<Skeleton className="h-4 w-20" />
|
|
54
|
+
</div>
|
|
55
|
+
))}
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</AppShell>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { usePathname, useSearchParams } from "next/navigation";
|
|
5
|
+
|
|
6
|
+
export function Pagination({
|
|
7
|
+
page,
|
|
8
|
+
pageSize,
|
|
9
|
+
total,
|
|
10
|
+
}: {
|
|
11
|
+
page: number;
|
|
12
|
+
pageSize: number;
|
|
13
|
+
total: number;
|
|
14
|
+
}) {
|
|
15
|
+
const pathname = usePathname();
|
|
16
|
+
const params = useSearchParams();
|
|
17
|
+
const pages = Math.max(1, Math.ceil(total / pageSize));
|
|
18
|
+
|
|
19
|
+
function href(p: number) {
|
|
20
|
+
const next = new URLSearchParams(params.toString());
|
|
21
|
+
next.set("page", String(p));
|
|
22
|
+
return `${pathname}?${next.toString()}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const from = total === 0 ? 0 : (page - 1) * pageSize + 1;
|
|
26
|
+
const to = Math.min(page * pageSize, total);
|
|
27
|
+
const btn =
|
|
28
|
+
"inline-flex h-8 items-center rounded-md border border-slate-200 bg-white px-3 text-xs font-medium hover:bg-slate-50 disabled:opacity-40";
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className="flex flex-wrap items-center justify-between gap-3 pt-2">
|
|
32
|
+
<p className="text-sm text-slate-500">
|
|
33
|
+
{from}–{to} sur {total.toLocaleString("fr-FR")}
|
|
34
|
+
</p>
|
|
35
|
+
<div className="flex items-center gap-2">
|
|
36
|
+
{page > 1 ? (
|
|
37
|
+
<Link className={btn} href={href(page - 1)}>
|
|
38
|
+
Précédent
|
|
39
|
+
</Link>
|
|
40
|
+
) : (
|
|
41
|
+
<span className={`${btn} opacity-40`}>Précédent</span>
|
|
42
|
+
)}
|
|
43
|
+
<span className="text-sm tabular-nums text-slate-600">
|
|
44
|
+
Page {page} / {pages}
|
|
45
|
+
</span>
|
|
46
|
+
{page < pages ? (
|
|
47
|
+
<Link className={btn} href={href(page + 1)}>
|
|
48
|
+
Suivant
|
|
49
|
+
</Link>
|
|
50
|
+
) : (
|
|
51
|
+
<span className={`${btn} opacity-40`}>Suivant</span>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
4
|
+
import { cn } from "@creezio/shell-ui";
|
|
5
|
+
|
|
6
|
+
function Avatar({
|
|
7
|
+
className,
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>) {
|
|
10
|
+
return (
|
|
11
|
+
<AvatarPrimitive.Root
|
|
12
|
+
className={cn("relative flex h-9 w-9 shrink-0 overflow-hidden rounded-full", className)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function AvatarFallback({
|
|
19
|
+
className,
|
|
20
|
+
...props
|
|
21
|
+
}: React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>) {
|
|
22
|
+
return (
|
|
23
|
+
<AvatarPrimitive.Fallback
|
|
24
|
+
className={cn(
|
|
25
|
+
"flex h-full w-full items-center justify-center rounded-full bg-slate-200 text-xs font-medium text-slate-700",
|
|
26
|
+
className,
|
|
27
|
+
)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { Avatar, AvatarFallback };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
import { cn } from "@creezio/shell-ui";
|
|
3
|
+
|
|
4
|
+
const badgeVariants = cva(
|
|
5
|
+
"inline-flex items-center rounded-md border px-2 py-0.5 text-xs font-medium transition-colors",
|
|
6
|
+
{
|
|
7
|
+
variants: {
|
|
8
|
+
variant: {
|
|
9
|
+
default: "border-transparent bg-slate-900 text-white",
|
|
10
|
+
secondary: "border-transparent bg-slate-100 text-slate-700",
|
|
11
|
+
outline: "border-slate-200 text-slate-700",
|
|
12
|
+
success: "border-transparent bg-emerald-100 text-emerald-800",
|
|
13
|
+
warning: "border-transparent bg-amber-100 text-amber-800",
|
|
14
|
+
danger: "border-transparent bg-red-100 text-red-800",
|
|
15
|
+
info: "border-transparent bg-sky-100 text-sky-800",
|
|
16
|
+
muted: "border-transparent bg-slate-50 text-slate-500",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: { variant: "default" },
|
|
20
|
+
},
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export interface BadgeProps
|
|
24
|
+
extends React.HTMLAttributes<HTMLSpanElement>,
|
|
25
|
+
VariantProps<typeof badgeVariants> {}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* <span> et non <div> : un badge est utilisé DANS des <p>, <a> et <span>
|
|
29
|
+
* (fiche produit, dashboard, préférences…). Un <div> y est du HTML invalide :
|
|
30
|
+
* le parseur navigateur restructure le DOM (ferme le <p> avant le div) et
|
|
31
|
+
* l'hydratation React échoue (« Hydration failed » #418/#422, cascade
|
|
32
|
+
* possible en #310) — écran « Application error » dans l'app desktop.
|
|
33
|
+
*/
|
|
34
|
+
export function Badge({ className, variant, ...props }: BadgeProps) {
|
|
35
|
+
return <span className={cn(badgeVariants({ variant }), className)} {...props} />;
|
|
36
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { ChevronRight } from "lucide-react";
|
|
4
|
+
import { cn } from "@creezio/shell-ui";
|
|
5
|
+
|
|
6
|
+
const Breadcrumb = React.forwardRef<
|
|
7
|
+
HTMLElement,
|
|
8
|
+
React.ComponentPropsWithoutRef<"nav"> & { separator?: React.ReactNode }
|
|
9
|
+
>(({ ...props }, ref) => <nav ref={ref} aria-label="Fil d'Ariane" {...props} />);
|
|
10
|
+
Breadcrumb.displayName = "Breadcrumb";
|
|
11
|
+
|
|
12
|
+
const BreadcrumbList = React.forwardRef<
|
|
13
|
+
HTMLOListElement,
|
|
14
|
+
React.ComponentPropsWithoutRef<"ol">
|
|
15
|
+
>(({ className, ...props }, ref) => (
|
|
16
|
+
<ol
|
|
17
|
+
ref={ref}
|
|
18
|
+
className={cn(
|
|
19
|
+
"flex min-w-0 flex-wrap items-center gap-1.5 overflow-hidden text-xs text-slate-500",
|
|
20
|
+
className,
|
|
21
|
+
)}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
));
|
|
25
|
+
BreadcrumbList.displayName = "BreadcrumbList";
|
|
26
|
+
|
|
27
|
+
const BreadcrumbItem = React.forwardRef<
|
|
28
|
+
HTMLLIElement,
|
|
29
|
+
React.ComponentPropsWithoutRef<"li">
|
|
30
|
+
>(({ className, ...props }, ref) => (
|
|
31
|
+
<li
|
|
32
|
+
ref={ref}
|
|
33
|
+
className={cn("inline-flex min-w-0 items-center gap-1.5", className)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
));
|
|
37
|
+
BreadcrumbItem.displayName = "BreadcrumbItem";
|
|
38
|
+
|
|
39
|
+
const BreadcrumbLink = React.forwardRef<
|
|
40
|
+
HTMLAnchorElement,
|
|
41
|
+
React.ComponentPropsWithoutRef<"a"> & { asChild?: boolean }
|
|
42
|
+
>(({ asChild, className, ...props }, ref) => {
|
|
43
|
+
const Comp = asChild ? Slot : "a";
|
|
44
|
+
return (
|
|
45
|
+
<Comp
|
|
46
|
+
ref={ref}
|
|
47
|
+
className={cn("truncate text-sky-700 transition-colors hover:underline", className)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
BreadcrumbLink.displayName = "BreadcrumbLink";
|
|
53
|
+
|
|
54
|
+
const BreadcrumbPage = React.forwardRef<
|
|
55
|
+
HTMLSpanElement,
|
|
56
|
+
React.ComponentPropsWithoutRef<"span">
|
|
57
|
+
>(({ className, ...props }, ref) => (
|
|
58
|
+
<span
|
|
59
|
+
ref={ref}
|
|
60
|
+
role="link"
|
|
61
|
+
aria-disabled="true"
|
|
62
|
+
aria-current="page"
|
|
63
|
+
className={cn("truncate font-medium text-slate-800", className)}
|
|
64
|
+
{...props}
|
|
65
|
+
/>
|
|
66
|
+
));
|
|
67
|
+
BreadcrumbPage.displayName = "BreadcrumbPage";
|
|
68
|
+
|
|
69
|
+
function BreadcrumbSeparator({
|
|
70
|
+
children,
|
|
71
|
+
className,
|
|
72
|
+
...props
|
|
73
|
+
}: React.ComponentProps<"li">) {
|
|
74
|
+
return (
|
|
75
|
+
<li
|
|
76
|
+
role="presentation"
|
|
77
|
+
aria-hidden="true"
|
|
78
|
+
className={cn("shrink-0 text-slate-300 [&>svg]:h-3 [&>svg]:w-3", className)}
|
|
79
|
+
{...props}
|
|
80
|
+
>
|
|
81
|
+
{children ?? <ChevronRight />}
|
|
82
|
+
</li>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export {
|
|
87
|
+
Breadcrumb,
|
|
88
|
+
BreadcrumbList,
|
|
89
|
+
BreadcrumbItem,
|
|
90
|
+
BreadcrumbLink,
|
|
91
|
+
BreadcrumbPage,
|
|
92
|
+
BreadcrumbSeparator,
|
|
93
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
import { cn } from "@creezio/shell-ui";
|
|
5
|
+
|
|
6
|
+
const buttonVariants = cva(
|
|
7
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400 disabled:pointer-events-none disabled:opacity-50",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "bg-slate-900 text-white hover:bg-slate-800",
|
|
12
|
+
secondary: "bg-slate-100 text-slate-900 hover:bg-slate-200",
|
|
13
|
+
outline: "border border-slate-200 bg-white hover:bg-slate-50",
|
|
14
|
+
ghost: "hover:bg-slate-100",
|
|
15
|
+
destructive: "bg-red-600 text-white hover:bg-red-700",
|
|
16
|
+
},
|
|
17
|
+
size: {
|
|
18
|
+
default: "h-9 px-4 py-2",
|
|
19
|
+
sm: "h-8 rounded-md px-3 text-xs",
|
|
20
|
+
lg: "h-10 rounded-md px-8",
|
|
21
|
+
icon: "h-9 w-9",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: { variant: "default", size: "default" },
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export interface ButtonProps
|
|
29
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
30
|
+
VariantProps<typeof buttonVariants> {
|
|
31
|
+
asChild?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
35
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
36
|
+
const Comp = asChild ? Slot : "button";
|
|
37
|
+
return (
|
|
38
|
+
<Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
|
|
39
|
+
);
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
Button.displayName = "Button";
|
|
43
|
+
|
|
44
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@creezio/shell-ui";
|
|
3
|
+
|
|
4
|
+
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
5
|
+
({ className, ...props }, ref) => (
|
|
6
|
+
<div
|
|
7
|
+
ref={ref}
|
|
8
|
+
className={cn("rounded-xl border border-slate-200 bg-white shadow-sm", className)}
|
|
9
|
+
{...props}
|
|
10
|
+
/>
|
|
11
|
+
),
|
|
12
|
+
);
|
|
13
|
+
Card.displayName = "Card";
|
|
14
|
+
|
|
15
|
+
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
16
|
+
({ className, ...props }, ref) => (
|
|
17
|
+
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
|
|
18
|
+
),
|
|
19
|
+
);
|
|
20
|
+
CardHeader.displayName = "CardHeader";
|
|
21
|
+
|
|
22
|
+
const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
|
23
|
+
({ className, ...props }, ref) => (
|
|
24
|
+
<h3 ref={ref} className={cn("font-semibold leading-none tracking-tight", className)} {...props} />
|
|
25
|
+
),
|
|
26
|
+
);
|
|
27
|
+
CardTitle.displayName = "CardTitle";
|
|
28
|
+
|
|
29
|
+
const CardDescription = React.forwardRef<
|
|
30
|
+
HTMLParagraphElement,
|
|
31
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
32
|
+
>(({ className, ...props }, ref) => (
|
|
33
|
+
<p ref={ref} className={cn("text-sm text-slate-500", className)} {...props} />
|
|
34
|
+
));
|
|
35
|
+
CardDescription.displayName = "CardDescription";
|
|
36
|
+
|
|
37
|
+
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
38
|
+
({ className, ...props }, ref) => (
|
|
39
|
+
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
40
|
+
),
|
|
41
|
+
);
|
|
42
|
+
CardContent.displayName = "CardContent";
|
|
43
|
+
|
|
44
|
+
export { Card, CardHeader, CardTitle, CardDescription, CardContent };
|