@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.
Files changed (138) hide show
  1. package/README.md +5 -0
  2. package/dist/account.d.ts +1 -0
  3. package/dist/account.js +105 -0
  4. package/dist/account.js.map +1 -0
  5. package/dist/activity.d.ts +65 -0
  6. package/dist/activity.js +299 -0
  7. package/dist/activity.js.map +1 -0
  8. package/dist/admin-access.d.ts +4 -0
  9. package/dist/admin-access.js +177 -0
  10. package/dist/admin-access.js.map +1 -0
  11. package/dist/admin.d.ts +5 -0
  12. package/dist/admin.js +129 -0
  13. package/dist/admin.js.map +1 -0
  14. package/dist/auth-pages.d.ts +13 -0
  15. package/dist/auth-pages.js +207 -0
  16. package/dist/auth-pages.js.map +1 -0
  17. package/dist/brand-art.d.ts +24 -0
  18. package/dist/brand-art.js +33 -0
  19. package/dist/brand-art.js.map +1 -0
  20. package/dist/brand.d.ts +11 -0
  21. package/dist/brand.js +16 -0
  22. package/dist/brand.js.map +1 -0
  23. package/dist/charts.d.ts +23 -0
  24. package/dist/charts.js +259 -0
  25. package/dist/charts.js.map +1 -0
  26. package/dist/command-palette.d.ts +9 -0
  27. package/dist/command-palette.js +115 -0
  28. package/dist/command-palette.js.map +1 -0
  29. package/dist/context.d.ts +242 -0
  30. package/dist/context.js +107 -0
  31. package/dist/context.js.map +1 -0
  32. package/dist/dashboard.d.ts +66 -0
  33. package/dist/dashboard.js +144 -0
  34. package/dist/dashboard.js.map +1 -0
  35. package/dist/fields.d.ts +71 -0
  36. package/dist/fields.js +326 -0
  37. package/dist/fields.js.map +1 -0
  38. package/dist/i18n/catalog.d.ts +7 -0
  39. package/dist/i18n/catalog.js +23 -0
  40. package/dist/i18n/catalog.js.map +1 -0
  41. package/dist/i18n/locale.d.ts +15 -0
  42. package/dist/i18n/locale.js +69 -0
  43. package/dist/i18n/locale.js.map +1 -0
  44. package/dist/i18n/messages/de.d.ts +3 -0
  45. package/dist/i18n/messages/de.js +627 -0
  46. package/dist/i18n/messages/de.js.map +1 -0
  47. package/dist/i18n/messages/en.d.ts +3 -0
  48. package/dist/i18n/messages/en.js +627 -0
  49. package/dist/i18n/messages/en.js.map +1 -0
  50. package/dist/i18n/messages/es.d.ts +3 -0
  51. package/dist/i18n/messages/es.js +627 -0
  52. package/dist/i18n/messages/es.js.map +1 -0
  53. package/dist/i18n/messages/fr.d.ts +3 -0
  54. package/dist/i18n/messages/fr.js +627 -0
  55. package/dist/i18n/messages/fr.js.map +1 -0
  56. package/dist/i18n/messages/pt.d.ts +3 -0
  57. package/dist/i18n/messages/pt.js +627 -0
  58. package/dist/i18n/messages/pt.js.map +1 -0
  59. package/dist/i18n/validation.d.ts +3 -0
  60. package/dist/i18n/validation.js +36 -0
  61. package/dist/i18n/validation.js.map +1 -0
  62. package/dist/illustrations.d.ts +6 -0
  63. package/dist/illustrations.js +16 -0
  64. package/dist/illustrations.js.map +1 -0
  65. package/dist/import-dialog.d.ts +11 -0
  66. package/dist/import-dialog.js +152 -0
  67. package/dist/import-dialog.js.map +1 -0
  68. package/dist/index.d.ts +38 -0
  69. package/dist/index.js +39 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/inline-edit.d.ts +18 -0
  72. package/dist/inline-edit.js +115 -0
  73. package/dist/inline-edit.js.map +1 -0
  74. package/dist/landing.d.ts +79 -0
  75. package/dist/landing.js +122 -0
  76. package/dist/landing.js.map +1 -0
  77. package/dist/locale-switcher.d.ts +4 -0
  78. package/dist/locale-switcher.js +15 -0
  79. package/dist/locale-switcher.js.map +1 -0
  80. package/dist/map.d.ts +26 -0
  81. package/dist/map.js +232 -0
  82. package/dist/map.js.map +1 -0
  83. package/dist/notifications.d.ts +12 -0
  84. package/dist/notifications.js +75 -0
  85. package/dist/notifications.js.map +1 -0
  86. package/dist/record-mutation.d.ts +19 -0
  87. package/dist/record-mutation.js +39 -0
  88. package/dist/record-mutation.js.map +1 -0
  89. package/dist/resource-detail.d.ts +4 -0
  90. package/dist/resource-detail.js +96 -0
  91. package/dist/resource-detail.js.map +1 -0
  92. package/dist/resource-form.d.ts +11 -0
  93. package/dist/resource-form.js +92 -0
  94. package/dist/resource-form.js.map +1 -0
  95. package/dist/resource-list.d.ts +10 -0
  96. package/dist/resource-list.js +154 -0
  97. package/dist/resource-list.js.map +1 -0
  98. package/dist/saved-views.d.ts +16 -0
  99. package/dist/saved-views.js +64 -0
  100. package/dist/saved-views.js.map +1 -0
  101. package/dist/schema-editor-diagram.d.ts +14 -0
  102. package/dist/schema-editor-diagram.js +54 -0
  103. package/dist/schema-editor-diagram.js.map +1 -0
  104. package/dist/schema-editor.d.ts +98 -0
  105. package/dist/schema-editor.js +571 -0
  106. package/dist/schema-editor.js.map +1 -0
  107. package/dist/shell.d.ts +10 -0
  108. package/dist/shell.js +228 -0
  109. package/dist/shell.js.map +1 -0
  110. package/dist/skeleton.d.ts +25 -0
  111. package/dist/skeleton.js +34 -0
  112. package/dist/skeleton.js.map +1 -0
  113. package/dist/theme-catalog.d.ts +71 -0
  114. package/dist/theme-catalog.js +55 -0
  115. package/dist/theme-catalog.js.map +1 -0
  116. package/dist/theme-head.d.ts +8 -0
  117. package/dist/theme-head.js +11 -0
  118. package/dist/theme-head.js.map +1 -0
  119. package/dist/theme-preview.d.ts +2 -0
  120. package/dist/theme-preview.js +23 -0
  121. package/dist/theme-preview.js.map +1 -0
  122. package/dist/theme-switcher.d.ts +20 -0
  123. package/dist/theme-switcher.js +159 -0
  124. package/dist/theme-switcher.js.map +1 -0
  125. package/dist/theme.d.ts +24 -0
  126. package/dist/theme.js +30 -0
  127. package/dist/theme.js.map +1 -0
  128. package/dist/toast.d.ts +21 -0
  129. package/dist/toast.js +51 -0
  130. package/dist/toast.js.map +1 -0
  131. package/dist/ui.d.ts +86 -0
  132. package/dist/ui.js +106 -0
  133. package/dist/ui.js.map +1 -0
  134. package/dist/views.d.ts +23 -0
  135. package/dist/views.js +238 -0
  136. package/dist/views.js.map +1 -0
  137. package/package.json +58 -0
  138. 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
+ };
@@ -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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"}