@tekki-forge/runtime-admin 0.1.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 +5 -0
- package/dist/account.d.ts +1 -0
- package/dist/account.js +105 -0
- package/dist/account.js.map +1 -0
- package/dist/activity.d.ts +65 -0
- package/dist/activity.js +299 -0
- package/dist/activity.js.map +1 -0
- package/dist/admin-access.d.ts +4 -0
- package/dist/admin-access.js +177 -0
- package/dist/admin-access.js.map +1 -0
- package/dist/admin.d.ts +5 -0
- package/dist/admin.js +129 -0
- package/dist/admin.js.map +1 -0
- package/dist/auth-pages.d.ts +13 -0
- package/dist/auth-pages.js +207 -0
- package/dist/auth-pages.js.map +1 -0
- package/dist/brand-art.d.ts +24 -0
- package/dist/brand-art.js +33 -0
- package/dist/brand-art.js.map +1 -0
- package/dist/brand.d.ts +11 -0
- package/dist/brand.js +16 -0
- package/dist/brand.js.map +1 -0
- package/dist/charts.d.ts +23 -0
- package/dist/charts.js +259 -0
- package/dist/charts.js.map +1 -0
- package/dist/command-palette.d.ts +9 -0
- package/dist/command-palette.js +115 -0
- package/dist/command-palette.js.map +1 -0
- package/dist/context.d.ts +242 -0
- package/dist/context.js +107 -0
- package/dist/context.js.map +1 -0
- package/dist/dashboard.d.ts +66 -0
- package/dist/dashboard.js +144 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/fields.d.ts +71 -0
- package/dist/fields.js +326 -0
- package/dist/fields.js.map +1 -0
- package/dist/i18n/catalog.d.ts +7 -0
- package/dist/i18n/catalog.js +23 -0
- package/dist/i18n/catalog.js.map +1 -0
- package/dist/i18n/locale.d.ts +15 -0
- package/dist/i18n/locale.js +69 -0
- package/dist/i18n/locale.js.map +1 -0
- package/dist/i18n/messages/de.d.ts +3 -0
- package/dist/i18n/messages/de.js +627 -0
- package/dist/i18n/messages/de.js.map +1 -0
- package/dist/i18n/messages/en.d.ts +3 -0
- package/dist/i18n/messages/en.js +627 -0
- package/dist/i18n/messages/en.js.map +1 -0
- package/dist/i18n/messages/es.d.ts +3 -0
- package/dist/i18n/messages/es.js +627 -0
- package/dist/i18n/messages/es.js.map +1 -0
- package/dist/i18n/messages/fr.d.ts +3 -0
- package/dist/i18n/messages/fr.js +627 -0
- package/dist/i18n/messages/fr.js.map +1 -0
- package/dist/i18n/messages/pt.d.ts +3 -0
- package/dist/i18n/messages/pt.js +627 -0
- package/dist/i18n/messages/pt.js.map +1 -0
- package/dist/i18n/validation.d.ts +3 -0
- package/dist/i18n/validation.js +36 -0
- package/dist/i18n/validation.js.map +1 -0
- package/dist/illustrations.d.ts +6 -0
- package/dist/illustrations.js +16 -0
- package/dist/illustrations.js.map +1 -0
- package/dist/import-dialog.d.ts +11 -0
- package/dist/import-dialog.js +152 -0
- package/dist/import-dialog.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/inline-edit.d.ts +18 -0
- package/dist/inline-edit.js +115 -0
- package/dist/inline-edit.js.map +1 -0
- package/dist/landing.d.ts +79 -0
- package/dist/landing.js +122 -0
- package/dist/landing.js.map +1 -0
- package/dist/locale-switcher.d.ts +4 -0
- package/dist/locale-switcher.js +15 -0
- package/dist/locale-switcher.js.map +1 -0
- package/dist/map.d.ts +26 -0
- package/dist/map.js +232 -0
- package/dist/map.js.map +1 -0
- package/dist/notifications.d.ts +12 -0
- package/dist/notifications.js +75 -0
- package/dist/notifications.js.map +1 -0
- package/dist/record-mutation.d.ts +19 -0
- package/dist/record-mutation.js +39 -0
- package/dist/record-mutation.js.map +1 -0
- package/dist/resource-detail.d.ts +4 -0
- package/dist/resource-detail.js +96 -0
- package/dist/resource-detail.js.map +1 -0
- package/dist/resource-form.d.ts +11 -0
- package/dist/resource-form.js +92 -0
- package/dist/resource-form.js.map +1 -0
- package/dist/resource-list.d.ts +10 -0
- package/dist/resource-list.js +154 -0
- package/dist/resource-list.js.map +1 -0
- package/dist/saved-views.d.ts +16 -0
- package/dist/saved-views.js +64 -0
- package/dist/saved-views.js.map +1 -0
- package/dist/schema-editor-diagram.d.ts +14 -0
- package/dist/schema-editor-diagram.js +54 -0
- package/dist/schema-editor-diagram.js.map +1 -0
- package/dist/schema-editor.d.ts +98 -0
- package/dist/schema-editor.js +571 -0
- package/dist/schema-editor.js.map +1 -0
- package/dist/shell.d.ts +10 -0
- package/dist/shell.js +228 -0
- package/dist/shell.js.map +1 -0
- package/dist/skeleton.d.ts +25 -0
- package/dist/skeleton.js +34 -0
- package/dist/skeleton.js.map +1 -0
- package/dist/theme-catalog.d.ts +71 -0
- package/dist/theme-catalog.js +55 -0
- package/dist/theme-catalog.js.map +1 -0
- package/dist/theme-head.d.ts +8 -0
- package/dist/theme-head.js +11 -0
- package/dist/theme-head.js.map +1 -0
- package/dist/theme-preview.d.ts +2 -0
- package/dist/theme-preview.js +23 -0
- package/dist/theme-preview.js.map +1 -0
- package/dist/theme-switcher.d.ts +20 -0
- package/dist/theme-switcher.js +159 -0
- package/dist/theme-switcher.js.map +1 -0
- package/dist/theme.d.ts +24 -0
- package/dist/theme.js +30 -0
- package/dist/theme.js.map +1 -0
- package/dist/toast.d.ts +21 -0
- package/dist/toast.js +51 -0
- package/dist/toast.js.map +1 -0
- package/dist/ui.d.ts +86 -0
- package/dist/ui.js +106 -0
- package/dist/ui.js.map +1 -0
- package/dist/views.d.ts +23 -0
- package/dist/views.js +238 -0
- package/dist/views.js.map +1 -0
- package/package.json +58 -0
- package/styles.css +778 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import type { AppIR, EntityIR, ResourceMeta } from "@tekki-forge/compiler";
|
|
2
|
+
import { type ForgeAuthClient } from "@tekki-forge/runtime-auth/client";
|
|
3
|
+
import type { Principal } from "@tekki-forge/runtime-permissions";
|
|
4
|
+
import { type ComponentType, type ReactNode } from "react";
|
|
5
|
+
import { type Messages, type TranslateVars } from "./i18n/locale.js";
|
|
6
|
+
export declare class ApiError extends Error {
|
|
7
|
+
status: number;
|
|
8
|
+
body: {
|
|
9
|
+
message?: string;
|
|
10
|
+
issues?: {
|
|
11
|
+
path: string;
|
|
12
|
+
message: string;
|
|
13
|
+
}[];
|
|
14
|
+
} | null;
|
|
15
|
+
constructor(status: number, body: {
|
|
16
|
+
message?: string;
|
|
17
|
+
issues?: {
|
|
18
|
+
path: string;
|
|
19
|
+
message: string;
|
|
20
|
+
}[];
|
|
21
|
+
} | null);
|
|
22
|
+
}
|
|
23
|
+
/** Pages contributed by plugins, keyed by path ("billing", "admin/jobs"). */
|
|
24
|
+
export type PluginPages = Record<string, ComponentType<{
|
|
25
|
+
path: string;
|
|
26
|
+
}>>;
|
|
27
|
+
export interface ForgeContextValue {
|
|
28
|
+
pages: PluginPages;
|
|
29
|
+
app: AppIR;
|
|
30
|
+
resources: Record<string, ResourceMeta>;
|
|
31
|
+
auth: ForgeAuthClient;
|
|
32
|
+
api: <T = any>(path: string, init?: RequestInit & {
|
|
33
|
+
query?: Record<string, unknown>;
|
|
34
|
+
}) => Promise<T>;
|
|
35
|
+
entityByPath: (path: string) => {
|
|
36
|
+
entity: EntityIR;
|
|
37
|
+
resource: ResourceMeta;
|
|
38
|
+
} | undefined;
|
|
39
|
+
/** Current UI language (one of app.i18n.locales). */
|
|
40
|
+
locale: string;
|
|
41
|
+
setLocale: (locale: string) => void;
|
|
42
|
+
/** Translate a built-in UI text (or a schema key such as "entity.Invoice.label"). */
|
|
43
|
+
t: (key: string, vars?: TranslateVars) => string;
|
|
44
|
+
/** Translate a validation message coming from the schema (client-side or API). */
|
|
45
|
+
tv: (message: string) => string;
|
|
46
|
+
}
|
|
47
|
+
/** Current locale outside of hooks that require the provider (falls back to English). */
|
|
48
|
+
export declare function useLocale(): string;
|
|
49
|
+
/** Translation function; works outside the provider too (English). */
|
|
50
|
+
export declare function useT(): ForgeContextValue["t"];
|
|
51
|
+
export declare function useForge(): ForgeContextValue;
|
|
52
|
+
export declare function ForgeProvider({ app: baseApp, resources: baseResources, pages, locale: initialLocale, messages, children, }: {
|
|
53
|
+
app: AppIR;
|
|
54
|
+
resources: Record<string, ResourceMeta>;
|
|
55
|
+
pages?: PluginPages;
|
|
56
|
+
/** Language resolved by the server (cookie / Accept-Language), so the first render matches. */
|
|
57
|
+
locale?: string;
|
|
58
|
+
/** Schema translations by locale (apps/web/forge.locales.ts). */
|
|
59
|
+
messages?: Record<string, Messages>;
|
|
60
|
+
children: ReactNode;
|
|
61
|
+
}): import("react").JSX.Element;
|
|
62
|
+
export interface Me {
|
|
63
|
+
user: {
|
|
64
|
+
id: string;
|
|
65
|
+
name: string;
|
|
66
|
+
email: string;
|
|
67
|
+
role: string | null;
|
|
68
|
+
} | null;
|
|
69
|
+
roles: string[];
|
|
70
|
+
organizationId: string | null;
|
|
71
|
+
impersonating: boolean;
|
|
72
|
+
teamIds?: string[];
|
|
73
|
+
}
|
|
74
|
+
export declare function useMe(): {
|
|
75
|
+
principal: Principal | null;
|
|
76
|
+
isAdmin: boolean;
|
|
77
|
+
data: Me;
|
|
78
|
+
error: Error;
|
|
79
|
+
isError: true;
|
|
80
|
+
isPending: false;
|
|
81
|
+
isLoading: false;
|
|
82
|
+
isLoadingError: false;
|
|
83
|
+
isRefetchError: true;
|
|
84
|
+
isSuccess: false;
|
|
85
|
+
isPlaceholderData: false;
|
|
86
|
+
status: "error";
|
|
87
|
+
dataUpdatedAt: number;
|
|
88
|
+
errorUpdatedAt: number;
|
|
89
|
+
failureCount: number;
|
|
90
|
+
failureReason: Error | null;
|
|
91
|
+
errorUpdateCount: number;
|
|
92
|
+
isFetched: boolean;
|
|
93
|
+
isFetchedAfterMount: boolean;
|
|
94
|
+
isFetching: boolean;
|
|
95
|
+
isInitialLoading: boolean;
|
|
96
|
+
isPaused: boolean;
|
|
97
|
+
isRefetching: boolean;
|
|
98
|
+
isStale: boolean;
|
|
99
|
+
isEnabled: boolean;
|
|
100
|
+
refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<Me, Error>>;
|
|
101
|
+
fetchStatus: import("@tanstack/react-query").FetchStatus;
|
|
102
|
+
} | {
|
|
103
|
+
principal: Principal | null;
|
|
104
|
+
isAdmin: boolean;
|
|
105
|
+
data: Me;
|
|
106
|
+
error: null;
|
|
107
|
+
isError: false;
|
|
108
|
+
isPending: false;
|
|
109
|
+
isLoading: false;
|
|
110
|
+
isLoadingError: false;
|
|
111
|
+
isRefetchError: false;
|
|
112
|
+
isSuccess: true;
|
|
113
|
+
isPlaceholderData: false;
|
|
114
|
+
status: "success";
|
|
115
|
+
dataUpdatedAt: number;
|
|
116
|
+
errorUpdatedAt: number;
|
|
117
|
+
failureCount: number;
|
|
118
|
+
failureReason: Error | null;
|
|
119
|
+
errorUpdateCount: number;
|
|
120
|
+
isFetched: boolean;
|
|
121
|
+
isFetchedAfterMount: boolean;
|
|
122
|
+
isFetching: boolean;
|
|
123
|
+
isInitialLoading: boolean;
|
|
124
|
+
isPaused: boolean;
|
|
125
|
+
isRefetching: boolean;
|
|
126
|
+
isStale: boolean;
|
|
127
|
+
isEnabled: boolean;
|
|
128
|
+
refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<Me, Error>>;
|
|
129
|
+
fetchStatus: import("@tanstack/react-query").FetchStatus;
|
|
130
|
+
} | {
|
|
131
|
+
principal: Principal | null;
|
|
132
|
+
isAdmin: boolean;
|
|
133
|
+
data: undefined;
|
|
134
|
+
error: Error;
|
|
135
|
+
isError: true;
|
|
136
|
+
isPending: false;
|
|
137
|
+
isLoading: false;
|
|
138
|
+
isLoadingError: true;
|
|
139
|
+
isRefetchError: false;
|
|
140
|
+
isSuccess: false;
|
|
141
|
+
isPlaceholderData: false;
|
|
142
|
+
status: "error";
|
|
143
|
+
dataUpdatedAt: number;
|
|
144
|
+
errorUpdatedAt: number;
|
|
145
|
+
failureCount: number;
|
|
146
|
+
failureReason: Error | null;
|
|
147
|
+
errorUpdateCount: number;
|
|
148
|
+
isFetched: boolean;
|
|
149
|
+
isFetchedAfterMount: boolean;
|
|
150
|
+
isFetching: boolean;
|
|
151
|
+
isInitialLoading: boolean;
|
|
152
|
+
isPaused: boolean;
|
|
153
|
+
isRefetching: boolean;
|
|
154
|
+
isStale: boolean;
|
|
155
|
+
isEnabled: boolean;
|
|
156
|
+
refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<Me, Error>>;
|
|
157
|
+
fetchStatus: import("@tanstack/react-query").FetchStatus;
|
|
158
|
+
} | {
|
|
159
|
+
principal: Principal | null;
|
|
160
|
+
isAdmin: boolean;
|
|
161
|
+
data: undefined;
|
|
162
|
+
error: null;
|
|
163
|
+
isError: false;
|
|
164
|
+
isPending: true;
|
|
165
|
+
isLoading: true;
|
|
166
|
+
isLoadingError: false;
|
|
167
|
+
isRefetchError: false;
|
|
168
|
+
isSuccess: false;
|
|
169
|
+
isPlaceholderData: false;
|
|
170
|
+
status: "pending";
|
|
171
|
+
dataUpdatedAt: number;
|
|
172
|
+
errorUpdatedAt: number;
|
|
173
|
+
failureCount: number;
|
|
174
|
+
failureReason: Error | null;
|
|
175
|
+
errorUpdateCount: number;
|
|
176
|
+
isFetched: boolean;
|
|
177
|
+
isFetchedAfterMount: boolean;
|
|
178
|
+
isFetching: boolean;
|
|
179
|
+
isInitialLoading: boolean;
|
|
180
|
+
isPaused: boolean;
|
|
181
|
+
isRefetching: boolean;
|
|
182
|
+
isStale: boolean;
|
|
183
|
+
isEnabled: boolean;
|
|
184
|
+
refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<Me, Error>>;
|
|
185
|
+
fetchStatus: import("@tanstack/react-query").FetchStatus;
|
|
186
|
+
} | {
|
|
187
|
+
principal: Principal | null;
|
|
188
|
+
isAdmin: boolean;
|
|
189
|
+
data: undefined;
|
|
190
|
+
error: null;
|
|
191
|
+
isError: false;
|
|
192
|
+
isPending: true;
|
|
193
|
+
isLoadingError: false;
|
|
194
|
+
isRefetchError: false;
|
|
195
|
+
isSuccess: false;
|
|
196
|
+
isPlaceholderData: false;
|
|
197
|
+
status: "pending";
|
|
198
|
+
dataUpdatedAt: number;
|
|
199
|
+
errorUpdatedAt: number;
|
|
200
|
+
failureCount: number;
|
|
201
|
+
failureReason: Error | null;
|
|
202
|
+
errorUpdateCount: number;
|
|
203
|
+
isFetched: boolean;
|
|
204
|
+
isFetchedAfterMount: boolean;
|
|
205
|
+
isFetching: boolean;
|
|
206
|
+
isLoading: boolean;
|
|
207
|
+
isInitialLoading: boolean;
|
|
208
|
+
isPaused: boolean;
|
|
209
|
+
isRefetching: boolean;
|
|
210
|
+
isStale: boolean;
|
|
211
|
+
isEnabled: boolean;
|
|
212
|
+
refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<Me, Error>>;
|
|
213
|
+
fetchStatus: import("@tanstack/react-query").FetchStatus;
|
|
214
|
+
} | {
|
|
215
|
+
principal: Principal | null;
|
|
216
|
+
isAdmin: boolean;
|
|
217
|
+
data: Me;
|
|
218
|
+
isError: false;
|
|
219
|
+
error: null;
|
|
220
|
+
isPending: false;
|
|
221
|
+
isLoading: false;
|
|
222
|
+
isLoadingError: false;
|
|
223
|
+
isRefetchError: false;
|
|
224
|
+
isSuccess: true;
|
|
225
|
+
isPlaceholderData: true;
|
|
226
|
+
status: "success";
|
|
227
|
+
dataUpdatedAt: number;
|
|
228
|
+
errorUpdatedAt: number;
|
|
229
|
+
failureCount: number;
|
|
230
|
+
failureReason: Error | null;
|
|
231
|
+
errorUpdateCount: number;
|
|
232
|
+
isFetched: boolean;
|
|
233
|
+
isFetchedAfterMount: boolean;
|
|
234
|
+
isFetching: boolean;
|
|
235
|
+
isInitialLoading: boolean;
|
|
236
|
+
isPaused: boolean;
|
|
237
|
+
isRefetching: boolean;
|
|
238
|
+
isStale: boolean;
|
|
239
|
+
isEnabled: boolean;
|
|
240
|
+
refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<Me, Error>>;
|
|
241
|
+
fetchStatus: import("@tanstack/react-query").FetchStatus;
|
|
242
|
+
};
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
// Browser-safe subpath: the package root also exports the config loader (Node only).
|
|
4
|
+
import { localizeSchema } from "@tekki-forge/compiler/i18n";
|
|
5
|
+
import { createForgeAuthClient } from "@tekki-forge/runtime-auth/client";
|
|
6
|
+
import { QueryClient, QueryClientProvider, useQuery } from "@tanstack/react-query";
|
|
7
|
+
import { createContext, useCallback, useContext, useMemo, useState } from "react";
|
|
8
|
+
import { uiCatalogs } from "./i18n/catalog.js";
|
|
9
|
+
import { LOCALE_COOKIE, translate } from "./i18n/locale.js";
|
|
10
|
+
import { translateValidation } from "./i18n/validation.js";
|
|
11
|
+
import { NotificationsPage } from "./notifications.js";
|
|
12
|
+
import { SchemaEditorPage } from "./schema-editor.js";
|
|
13
|
+
import { ApiKeysPage, TeamsPage } from "./admin-access.js";
|
|
14
|
+
import { ToastProvider } from "./toast.js";
|
|
15
|
+
export class ApiError extends Error {
|
|
16
|
+
status;
|
|
17
|
+
body;
|
|
18
|
+
constructor(status, body) {
|
|
19
|
+
super(body?.message ?? `Request failed (${status})`);
|
|
20
|
+
this.status = status;
|
|
21
|
+
this.body = body;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const ForgeContext = createContext(null);
|
|
25
|
+
const NO_MESSAGES = {};
|
|
26
|
+
/** Current locale outside of hooks that require the provider (falls back to English). */
|
|
27
|
+
export function useLocale() {
|
|
28
|
+
return useContext(ForgeContext)?.locale ?? "en";
|
|
29
|
+
}
|
|
30
|
+
/** Translation function; works outside the provider too (English). */
|
|
31
|
+
export function useT() {
|
|
32
|
+
const ctx = useContext(ForgeContext);
|
|
33
|
+
return ctx?.t ?? ((key, vars) => translate(uiCatalogs("en"), "en", key, vars));
|
|
34
|
+
}
|
|
35
|
+
export function useForge() {
|
|
36
|
+
const ctx = useContext(ForgeContext);
|
|
37
|
+
if (!ctx)
|
|
38
|
+
throw new Error("useForge must be used inside <ForgeProvider>");
|
|
39
|
+
return ctx;
|
|
40
|
+
}
|
|
41
|
+
export function ForgeProvider({ app: baseApp, resources: baseResources, pages = {}, locale: initialLocale, messages = NO_MESSAGES, children, }) {
|
|
42
|
+
const [queryClient] = useState(() => new QueryClient({ defaultOptions: { queries: { staleTime: 15_000, retry: 1, refetchOnWindowFocus: false } } }));
|
|
43
|
+
const i18n = baseApp.i18n ?? { defaultLocale: "en", locales: ["en"] };
|
|
44
|
+
const [locale, setLocaleState] = useState(() => (initialLocale && i18n.locales.includes(initialLocale) ? initialLocale : i18n.defaultLocale));
|
|
45
|
+
const setLocale = useCallback((next) => {
|
|
46
|
+
if (!i18n.locales.includes(next))
|
|
47
|
+
return;
|
|
48
|
+
document.cookie = `${LOCALE_COOKIE}=${encodeURIComponent(next)}; path=/; max-age=31536000; samesite=lax`;
|
|
49
|
+
document.documentElement.lang = next;
|
|
50
|
+
setLocaleState(next);
|
|
51
|
+
}, [i18n.locales]);
|
|
52
|
+
// Labels from the config, translated for the current locale (default locale = the config's text).
|
|
53
|
+
const { app, resources } = useMemo(() => (locale === i18n.defaultLocale ? { app: baseApp, resources: baseResources } : localizeSchema(baseApp, baseResources, messages[locale])), [locale, i18n.defaultLocale, baseApp, baseResources, messages]);
|
|
54
|
+
const t = useCallback((key, vars) => translate([...uiCatalogs(locale), messages[locale] ?? {}], locale, key, vars), [locale, messages]);
|
|
55
|
+
const tv = useCallback((message) => translateValidation(message, t), [t]);
|
|
56
|
+
const [base] = useState(() => {
|
|
57
|
+
const app = baseApp;
|
|
58
|
+
const auth = createForgeAuthClient({ prefix: app.api.prefix, tenancy: app.tenancy.enabled, twoFactor: app.auth.twoFactor, passkeys: app.auth.passkeys });
|
|
59
|
+
const api = async (path, init = {}) => {
|
|
60
|
+
const { query, ...rest } = init;
|
|
61
|
+
const params = new URLSearchParams();
|
|
62
|
+
for (const [k, v] of Object.entries(query ?? {}))
|
|
63
|
+
if (v !== undefined && v !== null && v !== "")
|
|
64
|
+
params.set(k, String(v));
|
|
65
|
+
const qs = params.toString();
|
|
66
|
+
const res = await fetch(`${app.api.prefix}${path}${qs ? `?${qs}` : ""}`, {
|
|
67
|
+
credentials: "include",
|
|
68
|
+
...rest,
|
|
69
|
+
headers: { accept: "application/json", ...(rest.body ? { "content-type": "application/json" } : {}), ...(rest.headers ?? {}) },
|
|
70
|
+
});
|
|
71
|
+
if (res.status === 204)
|
|
72
|
+
return undefined;
|
|
73
|
+
const isJson = res.headers.get("content-type")?.includes("json");
|
|
74
|
+
const body = isJson ? await res.json().catch(() => null) : await res.text();
|
|
75
|
+
if (!res.ok)
|
|
76
|
+
throw new ApiError(res.status, typeof body === "object" ? body : { message: String(body) });
|
|
77
|
+
return body;
|
|
78
|
+
};
|
|
79
|
+
// Built-in pages reached through the generic [resource] route; plugins may add more.
|
|
80
|
+
return { pages: { notifications: NotificationsPage, "admin/schema": SchemaEditorPage, "admin/teams": TeamsPage, "admin/api-keys": ApiKeysPage, ...pages }, auth, api };
|
|
81
|
+
});
|
|
82
|
+
const value = useMemo(() => {
|
|
83
|
+
const byPath = new Map(app.entities.map((e) => [e.path, e]));
|
|
84
|
+
return {
|
|
85
|
+
...base,
|
|
86
|
+
app,
|
|
87
|
+
resources,
|
|
88
|
+
locale,
|
|
89
|
+
setLocale,
|
|
90
|
+
t,
|
|
91
|
+
tv,
|
|
92
|
+
entityByPath: (path) => {
|
|
93
|
+
const entity = byPath.get(path);
|
|
94
|
+
const resource = entity && resources[entity.name];
|
|
95
|
+
return entity && resource ? { entity, resource } : undefined;
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}, [base, app, resources, locale, setLocale, t, tv]);
|
|
99
|
+
return (_jsx(QueryClientProvider, { client: queryClient, children: _jsx(ForgeContext.Provider, { value: value, children: _jsx(ToastProvider, { children: children }) }) }));
|
|
100
|
+
}
|
|
101
|
+
export function useMe() {
|
|
102
|
+
const { api } = useForge();
|
|
103
|
+
const query = useQuery({ queryKey: ["forge", "me"], queryFn: () => api("/me"), staleTime: 60_000 });
|
|
104
|
+
const principal = query.data?.user ? { id: query.data.user.id, roles: query.data.roles, organizationId: query.data.organizationId, teamIds: query.data.teamIds ?? [] } : null;
|
|
105
|
+
return { ...query, principal, isAdmin: !!query.data?.roles.includes("admin") };
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { FieldIR } from "@tekki-forge/compiler";
|
|
2
|
+
import { type Datum } from "./charts.js";
|
|
3
|
+
interface WidgetBase {
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
span: 1 | 2 | 3;
|
|
7
|
+
entity: string;
|
|
8
|
+
path: string;
|
|
9
|
+
/** The aggregated field (absent for counts). */
|
|
10
|
+
field?: string;
|
|
11
|
+
/** Set when this widget failed on the server (e.g. an invalid filter); the others still render. */
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Period-over-period comparison of a metric (see `compare` on metric widgets). */
|
|
15
|
+
export interface MetricTrendData {
|
|
16
|
+
period: "week" | "month" | "quarter" | "year";
|
|
17
|
+
trend: "up-is-good" | "down-is-good";
|
|
18
|
+
current: number | null;
|
|
19
|
+
previous: number | null;
|
|
20
|
+
change: number | null;
|
|
21
|
+
direction: "up" | "down" | "flat";
|
|
22
|
+
good: boolean | null;
|
|
23
|
+
series: {
|
|
24
|
+
start: string;
|
|
25
|
+
end: string;
|
|
26
|
+
value: number | null;
|
|
27
|
+
}[];
|
|
28
|
+
}
|
|
29
|
+
export type DashboardWidgetData = (WidgetBase & {
|
|
30
|
+
type: "metric";
|
|
31
|
+
value: number | string | null;
|
|
32
|
+
trend?: MetricTrendData;
|
|
33
|
+
}) | (WidgetBase & {
|
|
34
|
+
type: "chart";
|
|
35
|
+
chart: "bar" | "line" | "donut";
|
|
36
|
+
groupBy?: {
|
|
37
|
+
field: string;
|
|
38
|
+
by?: "day" | "week" | "month" | "year";
|
|
39
|
+
};
|
|
40
|
+
data: (Datum & {
|
|
41
|
+
key?: string | null;
|
|
42
|
+
})[];
|
|
43
|
+
}) | (WidgetBase & {
|
|
44
|
+
type: "list";
|
|
45
|
+
columns: string[];
|
|
46
|
+
rows: Record<string, unknown>[];
|
|
47
|
+
});
|
|
48
|
+
/** Formats aggregate values like the field they come from (money keeps its currency, counts get separators). */
|
|
49
|
+
export declare function widgetFormatter(field?: FieldIR, locale?: string): (v: number) => string;
|
|
50
|
+
/** Chart labels in the viewer's language: enum labels, yes/no, none/other, and date buckets. */
|
|
51
|
+
export declare function localizeSeries(data: (Datum & {
|
|
52
|
+
key?: string | null;
|
|
53
|
+
})[], field: FieldIR | undefined, by: "day" | "week" | "month" | "year" | undefined, locale: string, t: (key: string) => string): Datum[];
|
|
54
|
+
/** A tiny line of the last periods; decorative (the trend text carries the information). */
|
|
55
|
+
export declare function Sparkline({ values, className }: {
|
|
56
|
+
values: (number | null)[];
|
|
57
|
+
className?: string;
|
|
58
|
+
}): import("react").JSX.Element | null;
|
|
59
|
+
/** "▲ +12% vs previous month", coloured by whether the change is good news, with a full sentence for screen readers. */
|
|
60
|
+
export declare function TrendBadge({ trend, format }: {
|
|
61
|
+
trend: MetricTrendData;
|
|
62
|
+
format: (v: number) => string;
|
|
63
|
+
}): import("react").JSX.Element;
|
|
64
|
+
/** Home page: the widgets declared in `dashboard` (computed server-side within the viewer's permissions), else record counts. */
|
|
65
|
+
export declare function DashboardPage(): import("react").JSX.Element;
|
|
66
|
+
export {};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatValue } from "@tekki-forge/runtime-schema";
|
|
4
|
+
import { useQuery } from "@tanstack/react-query";
|
|
5
|
+
import { Minus, TrendingDown, TrendingUp } from "lucide-react";
|
|
6
|
+
import Link from "next/link";
|
|
7
|
+
import { BarChart, DonutChart, LineChart } from "./charts.js";
|
|
8
|
+
import { useForge, useMe } from "./context.js";
|
|
9
|
+
import { FieldValue } from "./fields.js";
|
|
10
|
+
import { CardGridSkeleton } from "./skeleton.js";
|
|
11
|
+
import { Alert, Card, EmptyState, PageHeader, ResourceIcon, cn } from "./ui.js";
|
|
12
|
+
const SPAN = { 1: "lg:col-span-1", 2: "sm:col-span-2 lg:col-span-2", 3: "sm:col-span-2 lg:col-span-3" };
|
|
13
|
+
/** Formats aggregate values like the field they come from (money keeps its currency, counts get separators). */
|
|
14
|
+
export function widgetFormatter(field, locale = "en") {
|
|
15
|
+
if (field?.kind === "percentage")
|
|
16
|
+
return (v) => new Intl.NumberFormat(locale, { style: "percent", maximumFractionDigits: 1 }).format(v / 100);
|
|
17
|
+
if (field && ["money", "decimal"].includes(field.kind))
|
|
18
|
+
return (v) => formatValue(field, v, locale);
|
|
19
|
+
return (v) => (Number.isInteger(v) ? v.toLocaleString(locale) : v.toLocaleString(locale, { maximumFractionDigits: 2 }));
|
|
20
|
+
}
|
|
21
|
+
/** Chart labels in the viewer's language: enum labels, yes/no, none/other, and date buckets. */
|
|
22
|
+
export function localizeSeries(data, field, by, locale, t) {
|
|
23
|
+
const date = (key, options) => {
|
|
24
|
+
try {
|
|
25
|
+
return new Intl.DateTimeFormat(locale, { ...options, timeZone: "UTC" }).format(new Date(key.length === 7 ? `${key}-01T00:00:00Z` : key.length === 4 ? `${key}-01-01T00:00:00Z` : `${key}T00:00:00Z`));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return key;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return data.map(({ key, ...d }) => {
|
|
32
|
+
if (key === undefined)
|
|
33
|
+
return d;
|
|
34
|
+
if (key === "__other")
|
|
35
|
+
return { ...d, label: t("dashboard.other") };
|
|
36
|
+
if (key === null)
|
|
37
|
+
return { ...d, label: t("common.none") };
|
|
38
|
+
if (by === "month")
|
|
39
|
+
return { ...d, label: date(key, { month: "short", year: "numeric" }) };
|
|
40
|
+
if (by === "day" || by === "week")
|
|
41
|
+
return { ...d, label: date(key, { day: "numeric", month: "short" }) };
|
|
42
|
+
if (field?.kind === "enum")
|
|
43
|
+
return { ...d, label: field.enumValues?.find((v) => v.value === key)?.label ?? d.label };
|
|
44
|
+
if (field?.kind === "boolean")
|
|
45
|
+
return { ...d, label: t(key === "true" ? "common.yes" : "common.no") };
|
|
46
|
+
return d;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/** A tiny line of the last periods; decorative (the trend text carries the information). */
|
|
50
|
+
export function Sparkline({ values, className }) {
|
|
51
|
+
const W = 96;
|
|
52
|
+
const H = 32;
|
|
53
|
+
const known = values.filter((v) => v !== null);
|
|
54
|
+
if (known.length < 2)
|
|
55
|
+
return null;
|
|
56
|
+
const min = Math.min(...known);
|
|
57
|
+
const max = Math.max(...known);
|
|
58
|
+
const x = (i) => (values.length === 1 ? W / 2 : (i / (values.length - 1)) * (W - 4) + 2);
|
|
59
|
+
const y = (v) => (max === min ? H / 2 : H - 3 - ((v - min) / (max - min)) * (H - 6));
|
|
60
|
+
// Gaps (periods without a value, e.g. an average over no rows) break the line.
|
|
61
|
+
const segments = [];
|
|
62
|
+
let current = "";
|
|
63
|
+
values.forEach((v, i) => {
|
|
64
|
+
if (v === null) {
|
|
65
|
+
if (current)
|
|
66
|
+
segments.push(current);
|
|
67
|
+
current = "";
|
|
68
|
+
}
|
|
69
|
+
else
|
|
70
|
+
current += `${current ? "L" : "M"}${x(i).toFixed(1)} ${y(v).toFixed(1)}`;
|
|
71
|
+
});
|
|
72
|
+
if (current)
|
|
73
|
+
segments.push(current);
|
|
74
|
+
const lastIndex = values.length - 1;
|
|
75
|
+
const last = values[lastIndex];
|
|
76
|
+
return (_jsxs("svg", { className: cn("forge-sparkline shrink-0", className), width: W, height: H, viewBox: `0 0 ${W} ${H}`, "aria-hidden": true, focusable: "false", children: [segments.map((d) => (_jsx("path", { d: d, fill: "none", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round", strokeLinejoin: "round" }, d))), last !== null && last !== undefined && _jsx("circle", { cx: x(lastIndex), cy: y(last), r: 2.75, fill: "currentColor" })] }));
|
|
77
|
+
}
|
|
78
|
+
/** "▲ +12% vs previous month", coloured by whether the change is good news, with a full sentence for screen readers. */
|
|
79
|
+
export function TrendBadge({ trend, format }) {
|
|
80
|
+
const { locale, t } = useForge();
|
|
81
|
+
const { current, previous, change, direction } = trend;
|
|
82
|
+
const vs = t(`dashboard.trend.vs.${trend.period}`);
|
|
83
|
+
if (current === null || previous === null)
|
|
84
|
+
return _jsx("p", { className: "forge-trend mt-2 text-xs text-ink-muted", children: t("dashboard.trend.noData") });
|
|
85
|
+
const delta = current - previous;
|
|
86
|
+
// A percentage when there is a base to compare with, else the absolute change ("+4").
|
|
87
|
+
const amount = change !== null
|
|
88
|
+
? new Intl.NumberFormat(locale, { style: "percent", maximumFractionDigits: Math.abs(change) < 0.1 ? 1 : 0, signDisplay: "exceptZero" }).format(change)
|
|
89
|
+
: `${delta > 0 ? "+" : delta < 0 ? "−" : ""}${format(Math.abs(delta))}`;
|
|
90
|
+
const Icon = direction === "up" ? TrendingUp : direction === "down" ? TrendingDown : Minus;
|
|
91
|
+
const tone = trend.good === null ? "neutral" : trend.good ? "good" : "bad";
|
|
92
|
+
const unsigned = amount.replace(/^[+−-]/, "");
|
|
93
|
+
const sentence = [
|
|
94
|
+
direction === "flat" ? t("dashboard.trend.flat") : t(`dashboard.trend.${direction}`, { change: unsigned }),
|
|
95
|
+
`${vs}${tone === "neutral" ? "" : `, ${t(tone === "good" ? "dashboard.trend.better" : "dashboard.trend.worse")}`}.`,
|
|
96
|
+
t("dashboard.trend.values", { current: format(current), previous: format(previous) }),
|
|
97
|
+
].join(" ");
|
|
98
|
+
return (_jsxs("p", { className: "mt-2 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs", children: [_jsxs("span", { className: "forge-trend inline-flex items-center gap-1 rounded-full px-1.5 py-0.5 font-medium tabular-nums", "data-forge-trend": tone, "aria-hidden": true, children: [_jsx(Icon, { className: "size-3.5", strokeWidth: 2.25 }), direction === "flat" ? t("dashboard.trend.flat") : amount] }), _jsx("span", { className: "text-ink-muted", "aria-hidden": true, children: vs }), _jsx("span", { className: "sr-only", children: sentence })] }));
|
|
99
|
+
}
|
|
100
|
+
function Widget({ w: raw }) {
|
|
101
|
+
const { app, locale, resources, t } = useForge();
|
|
102
|
+
const entity = app.entities.find((e) => e.name === raw.entity);
|
|
103
|
+
const fieldOf = (name) => entity?.fields.find((f) => f.name === name);
|
|
104
|
+
// Titles and category labels come from the (localized) schema, not from the server's default-language text.
|
|
105
|
+
const title = app.dashboard?.widgets.find((x) => x.id === raw.id)?.title ?? raw.title;
|
|
106
|
+
const w = raw.type === "chart" ? { ...raw, title, data: localizeSeries(raw.data, raw.groupBy ? fieldOf(raw.groupBy.field) : undefined, raw.groupBy?.by, locale, t) } : { ...raw, title };
|
|
107
|
+
const fmt = widgetFormatter(w.field ? fieldOf(w.field) : undefined, locale);
|
|
108
|
+
return (_jsxs(Card, { className: cn("forge-widget flex flex-col p-[var(--forge-pad-card)]", w.type === "metric" && "forge-metric", SPAN[w.span]), children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2.5", children: [w.type === "metric" && (_jsx("span", { className: "forge-chip flex size-9 shrink-0 items-center justify-center", "aria-hidden": true, children: _jsx(ResourceIcon, { name: resources[raw.entity]?.icon ?? "folder" }) })), _jsx("h2", { className: "truncate text-sm font-medium text-ink-muted", children: w.title })] }), _jsx(Link, { href: `/${w.path}`, className: "shrink-0 text-xs text-ink-subtle hover:text-link", children: t("dashboard.viewAll") })] }), w.error ? (_jsx("p", { className: "mt-3 text-sm text-ink-subtle", children: w.error })) : null, !w.error && w.type === "metric" && (_jsxs("div", { className: "mt-4 flex items-end justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "forge-metric-value forge-heading text-4xl font-semibold tracking-tight text-ink tabular-nums", children: w.value === null ? "—" : fmt(Number(w.value)) }), w.trend && _jsx(TrendBadge, { trend: w.trend, format: fmt })] }), w.trend && _jsx(Sparkline, { values: w.trend.series.map((p) => p.value), className: "mb-1 text-link" })] })), !w.error && w.type === "chart" && (_jsx("div", { className: "mt-3", children: w.chart === "line" ? (_jsx(LineChart, { title: w.title, data: w.data, format: fmt, area: true })) : w.chart === "donut" ? (_jsx(DonutChart, { title: w.title, data: w.data, format: fmt })) : (_jsx(BarChart, { title: w.title, data: w.data, format: fmt })) })), !w.error &&
|
|
109
|
+
w.type === "list" &&
|
|
110
|
+
(w.rows.length === 0 ? (_jsx("p", { className: "mt-3 text-sm text-ink-subtle", children: t("dashboard.nothingYet") })) : (_jsx("ul", { className: "mt-2 divide-y divide-line", children: w.rows.map((row) => (_jsx("li", { children: _jsx(Link, { href: `/${w.path}/${String(row.id)}`, className: "flex items-center gap-3 py-2 text-sm hover:text-link", children: w.columns.map((c, i) => {
|
|
111
|
+
const field = fieldOf(c);
|
|
112
|
+
const key = field?.apiName ?? c;
|
|
113
|
+
const value = row[field?.relation?.type === "many-to-one" ? c : key] ?? row[c];
|
|
114
|
+
return (_jsx("span", { className: cn(i === 0 ? "min-w-0 flex-1 truncate font-medium text-ink" : "shrink-0 text-ink-muted"), children: i === 0 ? String(value ?? t("common.untitled")) : _jsx(FieldValue, { field: field, value: value, cell: field ? undefined : "datetime" }) }, c));
|
|
115
|
+
}) }) }, String(row.id)))) })))] }));
|
|
116
|
+
}
|
|
117
|
+
function CountCards() {
|
|
118
|
+
const { api, resources, locale, t } = useForge();
|
|
119
|
+
const stats = useQuery({ queryKey: ["forge", "stats"], queryFn: () => api("/stats") });
|
|
120
|
+
if (stats.isLoading)
|
|
121
|
+
return _jsx(CardGridSkeleton, {});
|
|
122
|
+
if (stats.error)
|
|
123
|
+
return _jsx(Alert, { children: stats.error.message });
|
|
124
|
+
if (!stats.data?.data.length)
|
|
125
|
+
return _jsx(Card, { children: _jsx(EmptyState, { illustration: "dashboard", title: t("dashboard.emptyTitle"), description: t("dashboard.emptyDescription") }) });
|
|
126
|
+
return (_jsx("div", { className: "forge-stagger grid gap-4 sm:grid-cols-2 lg:grid-cols-3", children: stats.data.data.map((s) => (_jsx(Link, { href: `/${s.path}`, className: "forge-focusable group rounded-[var(--forge-radius)]", children: _jsxs(Card, { className: "forge-card-interactive forge-metric p-[var(--forge-pad-card)]", children: [_jsxs("div", { className: "flex items-center gap-2.5 text-sm font-medium text-ink-muted", children: [_jsx("span", { className: "forge-chip flex size-9 shrink-0 items-center justify-center", "aria-hidden": true, children: _jsx(ResourceIcon, { name: resources[s.entity]?.icon ?? "folder" }) }), _jsx("span", { className: "truncate", children: resources[s.entity]?.pluralLabel ?? s.label })] }), _jsx("p", { className: "forge-metric-value forge-heading mt-4 text-4xl font-semibold tracking-tight text-ink tabular-nums", children: s.count.toLocaleString(locale) })] }) }, s.path))) }));
|
|
127
|
+
}
|
|
128
|
+
/** Home page: the widgets declared in `dashboard` (computed server-side within the viewer's permissions), else record counts. */
|
|
129
|
+
export function DashboardPage() {
|
|
130
|
+
const { api, app, t } = useForge();
|
|
131
|
+
const me = useMe();
|
|
132
|
+
const dashboard = useQuery({
|
|
133
|
+
queryKey: ["forge", "dashboard"],
|
|
134
|
+
queryFn: () => api("/dashboard"),
|
|
135
|
+
enabled: !!app.dashboard,
|
|
136
|
+
refetchInterval: 60_000,
|
|
137
|
+
});
|
|
138
|
+
const header = _jsx(PageHeader, { title: me.data?.user ? t("dashboard.welcomeName", { name: me.data.user.name.split(" ")[0] }) : t("dashboard.welcome"), description: app.description ?? t("dashboard.description", { app: app.name }) });
|
|
139
|
+
if (!app.dashboard) {
|
|
140
|
+
return (_jsxs("div", { children: [header, _jsx(CountCards, {})] }));
|
|
141
|
+
}
|
|
142
|
+
return (_jsxs("div", { children: [header, dashboard.isLoading ? (_jsx(CardGridSkeleton, { count: Math.min(6, app.dashboard.widgets.length || 3) })) : dashboard.error ? (_jsx(Alert, { children: dashboard.error.message })) : !dashboard.data?.widgets?.length ? (_jsx(Card, { children: _jsx(EmptyState, { illustration: "dashboard", title: t("dashboard.emptyTitle"), description: t("dashboard.emptyDescription") }) })) : (_jsx("div", { className: "forge-stagger grid grid-flow-row-dense gap-4 sm:grid-cols-2 lg:grid-cols-3", children: dashboard.data.widgets.map((w) => (_jsx(Widget, { w: w }, w.id))) }))] }));
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../src/dashboard.tsx"],"names":[],"mappings":"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"}
|