@fougere/admin 0.9.3-alpha.0 → 0.11.0-alpha.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/dist/{extensions.d.ts → AdminExtension.d.ts} +5 -14
- package/dist/AdminExtension.d.ts.map +1 -0
- package/dist/{extensions.js → AdminExtension.js} +3 -3
- package/dist/AdminExtension.js.map +1 -0
- package/dist/AdminFacetRegistry.d.ts +9 -0
- package/dist/AdminFacetRegistry.d.ts.map +1 -0
- package/dist/AdminFacetRegistry.js +2 -0
- package/dist/AdminFacetRegistry.js.map +1 -0
- package/dist/AdminFacets.d.ts +6 -0
- package/dist/AdminFacets.d.ts.map +1 -0
- package/dist/{facets.js → AdminFacets.js} +1 -1
- package/dist/AdminFacets.js.map +1 -0
- package/dist/AdminFieldExtension.d.ts +7 -0
- package/dist/AdminFieldExtension.d.ts.map +1 -0
- package/dist/AdminFieldExtension.js +2 -0
- package/dist/AdminFieldExtension.js.map +1 -0
- package/dist/AdminOperation.d.ts +9 -0
- package/dist/AdminOperation.d.ts.map +1 -0
- package/dist/AdminOperation.js +2 -0
- package/dist/AdminOperation.js.map +1 -0
- package/dist/AdminOperationExtension.d.ts +6 -0
- package/dist/AdminOperationExtension.d.ts.map +1 -0
- package/dist/AdminOperationExtension.js +2 -0
- package/dist/AdminOperationExtension.js.map +1 -0
- package/dist/{resources.d.ts → AdminResource.d.ts} +11 -17
- package/dist/AdminResource.d.ts.map +1 -0
- package/dist/{resources.js → AdminResource.js} +13 -14
- package/dist/AdminResource.js.map +1 -0
- package/dist/AdminRuntime.d.ts +11 -0
- package/dist/AdminRuntime.d.ts.map +1 -0
- package/dist/{runtime.js → AdminRuntime.js} +5 -6
- package/dist/AdminRuntime.js.map +1 -0
- package/dist/AdminRuntimeOptions.d.ts +8 -0
- package/dist/AdminRuntimeOptions.d.ts.map +1 -0
- package/dist/AdminRuntimeOptions.js +2 -0
- package/dist/AdminRuntimeOptions.js.map +1 -0
- package/dist/{topology.d.ts → Drawing.d.ts} +5 -46
- package/dist/Drawing.d.ts.map +1 -0
- package/dist/{topology.js → Drawing.js} +4 -5
- package/dist/Drawing.js.map +1 -0
- package/dist/EditorialFacet.d.ts +13 -0
- package/dist/EditorialFacet.d.ts.map +1 -0
- package/dist/EditorialFacet.js +2 -0
- package/dist/EditorialFacet.js.map +1 -0
- package/dist/FougereAdminProps.d.ts +17 -0
- package/dist/FougereAdminProps.d.ts.map +1 -0
- package/dist/FougereAdminProps.js +2 -0
- package/dist/FougereAdminProps.js.map +1 -0
- package/dist/FougereDashboardContextValue.d.ts +11 -0
- package/dist/FougereDashboardContextValue.d.ts.map +1 -0
- package/dist/FougereDashboardContextValue.js +2 -0
- package/dist/FougereDashboardContextValue.js.map +1 -0
- package/dist/FougereDashboardExtension.d.ts +13 -0
- package/dist/FougereDashboardExtension.d.ts.map +1 -0
- package/dist/FougereDashboardExtension.js +2 -0
- package/dist/FougereDashboardExtension.js.map +1 -0
- package/dist/FougereDashboardMetrics.d.ts +7 -0
- package/dist/FougereDashboardMetrics.d.ts.map +1 -0
- package/dist/FougereDashboardMetrics.js +2 -0
- package/dist/FougereDashboardMetrics.js.map +1 -0
- package/dist/FougereDashboardResource.d.ts +14 -0
- package/dist/FougereDashboardResource.d.ts.map +1 -0
- package/dist/FougereDashboardResource.js +2 -0
- package/dist/FougereDashboardResource.js.map +1 -0
- package/dist/FougereDashboardWidget.d.ts +11 -0
- package/dist/FougereDashboardWidget.d.ts.map +1 -0
- package/dist/FougereDashboardWidget.js +2 -0
- package/dist/FougereDashboardWidget.js.map +1 -0
- package/dist/FougereDashboardZone.d.ts +2 -0
- package/dist/FougereDashboardZone.d.ts.map +1 -0
- package/dist/FougereDashboardZone.js +2 -0
- package/dist/FougereDashboardZone.js.map +1 -0
- package/dist/{provider.d.ts → FougereDataProvider.d.ts} +2 -17
- package/dist/FougereDataProvider.d.ts.map +1 -0
- package/dist/{provider.js → FougereDataProvider.js} +2 -3
- package/dist/FougereDataProvider.js.map +1 -0
- package/dist/FougereResourceOptions.d.ts +16 -0
- package/dist/FougereResourceOptions.d.ts.map +1 -0
- package/dist/FougereResourceOptions.js +2 -0
- package/dist/FougereResourceOptions.js.map +1 -0
- package/dist/LoadedAdmin.d.ts +7 -0
- package/dist/LoadedAdmin.d.ts.map +1 -0
- package/dist/LoadedAdmin.js +2 -0
- package/dist/LoadedAdmin.js.map +1 -0
- package/dist/PlacedEdge.d.ts +17 -0
- package/dist/PlacedEdge.d.ts.map +1 -0
- package/dist/PlacedEdge.js +2 -0
- package/dist/PlacedEdge.js.map +1 -0
- package/dist/PlacedNode.d.ts +10 -0
- package/dist/PlacedNode.d.ts.map +1 -0
- package/dist/PlacedNode.js +2 -0
- package/dist/PlacedNode.js.map +1 -0
- package/dist/Point.d.ts +5 -0
- package/dist/Point.d.ts.map +1 -0
- package/dist/Point.js +2 -0
- package/dist/Point.js.map +1 -0
- package/dist/ProviderOptions.d.ts +10 -0
- package/dist/ProviderOptions.d.ts.map +1 -0
- package/dist/ProviderOptions.js +2 -0
- package/dist/ProviderOptions.js.map +1 -0
- package/dist/ReactAdminFieldContext.d.ts +10 -0
- package/dist/ReactAdminFieldContext.d.ts.map +1 -0
- package/dist/ReactAdminFieldContext.js +2 -0
- package/dist/ReactAdminFieldContext.js.map +1 -0
- package/dist/ReactAdminFieldRenderer.d.ts +4 -0
- package/dist/ReactAdminFieldRenderer.d.ts.map +1 -0
- package/dist/ReactAdminFieldRenderer.js +2 -0
- package/dist/ReactAdminFieldRenderer.js.map +1 -0
- package/dist/ReactAdminInputContext.d.ts +9 -0
- package/dist/ReactAdminInputContext.d.ts.map +1 -0
- package/dist/ReactAdminInputContext.js +2 -0
- package/dist/ReactAdminInputContext.js.map +1 -0
- package/dist/ReactAdminInputRenderer.d.ts +4 -0
- package/dist/ReactAdminInputRenderer.d.ts.map +1 -0
- package/dist/ReactAdminInputRenderer.js +2 -0
- package/dist/ReactAdminInputRenderer.js.map +1 -0
- package/dist/ReactAdminRenderers.d.ts +8 -0
- package/dist/ReactAdminRenderers.d.ts.map +1 -0
- package/dist/ReactAdminRenderers.js +2 -0
- package/dist/ReactAdminRenderers.js.map +1 -0
- package/dist/ReactAdminResourceComponents.d.ts +9 -0
- package/dist/ReactAdminResourceComponents.d.ts.map +1 -0
- package/dist/ReactAdminResourceComponents.js +2 -0
- package/dist/ReactAdminResourceComponents.js.map +1 -0
- package/dist/ResourceKey.d.ts +8 -0
- package/dist/ResourceKey.d.ts.map +1 -0
- package/dist/ResourceKey.js +2 -0
- package/dist/ResourceKey.js.map +1 -0
- package/dist/ResourceRenderOptions.d.ts +7 -0
- package/dist/ResourceRenderOptions.d.ts.map +1 -0
- package/dist/ResourceRenderOptions.js +2 -0
- package/dist/ResourceRenderOptions.js.map +1 -0
- package/dist/TopologyNode.d.ts +18 -0
- package/dist/TopologyNode.d.ts.map +1 -0
- package/dist/TopologyNode.js +2 -0
- package/dist/TopologyNode.js.map +1 -0
- package/dist/UsersFacet.d.ts +13 -0
- package/dist/UsersFacet.d.ts.map +1 -0
- package/dist/UsersFacet.js +2 -0
- package/dist/UsersFacet.js.map +1 -0
- package/dist/dashboard.d.ts +4 -61
- package/dist/dashboard.d.ts.map +1 -1
- package/dist/dashboard.js +9 -9
- package/dist/dashboard.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +21 -65
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +7 -7
- package/dist/react.js.map +1 -1
- package/dist/theme.js +1 -1
- package/dist/topology-page.d.ts.map +1 -1
- package/dist/topology-page.js +3 -3
- package/dist/topology-page.js.map +1 -1
- package/package.json +6 -5
- package/src/{extensions.ts → AdminExtension.ts} +5 -16
- package/src/AdminFacetRegistry.ts +9 -0
- package/src/AdminFacets.ts +22 -0
- package/src/AdminFieldExtension.ts +6 -0
- package/src/AdminOperation.ts +9 -0
- package/src/AdminOperationExtension.ts +5 -0
- package/src/{resources.ts → AdminResource.ts} +18 -26
- package/src/{runtime.ts → AdminRuntime.ts} +6 -24
- package/src/AdminRuntimeOptions.ts +8 -0
- package/src/{topology.ts → Drawing.ts} +7 -60
- package/src/EditorialFacet.ts +12 -0
- package/src/FougereAdminProps.ts +17 -0
- package/src/FougereDashboardContextValue.ts +11 -0
- package/src/FougereDashboardExtension.ts +13 -0
- package/src/FougereDashboardMetrics.ts +6 -0
- package/src/FougereDashboardResource.ts +14 -0
- package/src/FougereDashboardWidget.ts +11 -0
- package/src/FougereDashboardZone.ts +1 -0
- package/src/{provider.ts → FougereDataProvider.ts} +3 -26
- package/src/FougereResourceOptions.ts +13 -0
- package/src/LoadedAdmin.ts +7 -0
- package/src/PlacedEdge.ts +17 -0
- package/src/PlacedNode.ts +10 -0
- package/src/Point.ts +4 -0
- package/src/ProviderOptions.ts +10 -0
- package/src/ReactAdminFieldContext.ts +10 -0
- package/src/ReactAdminFieldRenderer.ts +4 -0
- package/src/ReactAdminInputContext.ts +9 -0
- package/src/ReactAdminInputRenderer.ts +4 -0
- package/src/ReactAdminRenderers.ts +8 -0
- package/src/ReactAdminResourceComponents.ts +9 -0
- package/src/ResourceKey.ts +7 -0
- package/src/ResourceRenderOptions.ts +7 -0
- package/src/TopologyNode.ts +18 -0
- package/src/UsersFacet.ts +12 -0
- package/src/dashboard.tsx +19 -88
- package/src/index.ts +3 -9
- package/src/react.tsx +37 -80
- package/src/theme.tsx +1 -1
- package/src/topology-page.tsx +5 -3
- package/template/index.html +5 -0
- package/template/package.json +22 -0
- package/template/src/main.tsx +27 -0
- package/template/tsconfig.json +12 -0
- package/template/vite.config.ts +16 -0
- package/dist/extensions.d.ts.map +0 -1
- package/dist/extensions.js.map +0 -1
- package/dist/facets.d.ts +0 -36
- package/dist/facets.d.ts.map +0 -1
- package/dist/facets.js.map +0 -1
- package/dist/provider.d.ts.map +0 -1
- package/dist/provider.js.map +0 -1
- package/dist/resources.d.ts.map +0 -1
- package/dist/resources.js.map +0 -1
- package/dist/runtime.d.ts +0 -22
- package/dist/runtime.d.ts.map +0 -1
- package/dist/runtime.js.map +0 -1
- package/dist/topology.d.ts.map +0 -1
- package/dist/topology.js.map +0 -1
- package/src/facets.ts +0 -55
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fougere/admin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0-alpha.0",
|
|
4
4
|
"description": "A back-office derived from what the app already says it hosts — a react-admin DataProvider over the Fougere wire.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fougere",
|
|
@@ -31,13 +31,14 @@
|
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
33
33
|
"dist",
|
|
34
|
-
"src"
|
|
34
|
+
"src",
|
|
35
|
+
"template"
|
|
35
36
|
],
|
|
36
37
|
"dependencies": {
|
|
37
38
|
"@dagrejs/dagre": "^3.1.1",
|
|
38
|
-
"@fougere/app": "0.
|
|
39
|
-
"@fougere/core": "0.
|
|
40
|
-
"@fougere/schema": "0.
|
|
39
|
+
"@fougere/app": "0.11.0-alpha.0",
|
|
40
|
+
"@fougere/core": "0.11.0-alpha.0",
|
|
41
|
+
"@fougere/schema": "0.11.0-alpha.0"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"@mui/material": "^9.3.1",
|
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
/** Additive presentation changes over the model derived from `rpc.discover`. */
|
|
2
1
|
import type { FormField, TableColumn } from '@fougere/app/client';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
label?: string;
|
|
9
|
-
/** Removes the field from derived tables, shows and forms. */
|
|
10
|
-
hidden?: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface AdminOperationExtension {
|
|
14
|
-
label?: string;
|
|
15
|
-
/** `false` explicitly removes a confirmation added by an earlier extension. */
|
|
16
|
-
confirm?: string | false;
|
|
17
|
-
}
|
|
2
|
+
import { type AdminOperation } from './AdminOperation.js';
|
|
3
|
+
import { capabilitiesOf, type AdminResource } from './AdminResource.js';
|
|
4
|
+
import { mergeAdminFacets, type AdminFacets } from './AdminFacets.js';
|
|
5
|
+
import type { AdminFieldExtension } from './AdminFieldExtension.js';
|
|
6
|
+
import type { AdminOperationExtension } from './AdminOperationExtension.js';
|
|
18
7
|
|
|
19
8
|
export interface AdminExtension {
|
|
20
9
|
/** Registration key from the card (`post`, not `Post`). */
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EditorialFacet } from './EditorialFacet.js';
|
|
2
|
+
import type { UsersFacet } from './UsersFacet.js';
|
|
3
|
+
|
|
4
|
+
/** Open registry of the semantic projections renderers understand. */
|
|
5
|
+
export interface AdminFacetRegistry {
|
|
6
|
+
editorial: EditorialFacet;
|
|
7
|
+
users: UsersFacet;
|
|
8
|
+
[facet: string]: unknown;
|
|
9
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { isObject } from '@fougere/schema';
|
|
2
|
+
import type { AdminFacetRegistry } from './AdminFacetRegistry.js';
|
|
3
|
+
|
|
4
|
+
export type AdminFacets = Partial<AdminFacetRegistry>;
|
|
5
|
+
|
|
6
|
+
export function defineAdminFacet<const Name extends string, const Value>(
|
|
7
|
+
name: Name,
|
|
8
|
+
value: Value,
|
|
9
|
+
): Record<Name, Value> {
|
|
10
|
+
return { [name]: value } as Record<Name, Value>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Arrays replace; semantic objects merge recursively so small project deltas stay small. */
|
|
14
|
+
export function mergeAdminFacets(base: AdminFacets, patch: AdminFacets): AdminFacets {
|
|
15
|
+
const merge = (left: unknown, right: unknown): unknown => {
|
|
16
|
+
if (!isObject(left) || !isObject(right)) return right;
|
|
17
|
+
const out: Record<string, unknown> = { ...left };
|
|
18
|
+
for (const [key, value] of Object.entries(right)) out[key] = merge(out[key], value);
|
|
19
|
+
return out;
|
|
20
|
+
};
|
|
21
|
+
return merge(base, patch) as AdminFacets;
|
|
22
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type CardOp } from '@fougere/core/contract';
|
|
2
|
+
|
|
3
|
+
/** One operation as the admin meets it, before a renderer decides its widget. */
|
|
4
|
+
export interface AdminOperation extends CardOp {
|
|
5
|
+
/** Display fallback. An extension may replace it without renaming the call. */
|
|
6
|
+
label: string;
|
|
7
|
+
/** Optional confirmation sentence, interpreted by renderers that support actions. */
|
|
8
|
+
confirm?: string;
|
|
9
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** What the app hosts, read as a back-office. */
|
|
2
1
|
import { Card, FieldSet, type SchemaView } from '@fougere/schema';
|
|
3
2
|
import {
|
|
4
3
|
CALL_ENDPOINT,
|
|
@@ -10,19 +9,12 @@ import {
|
|
|
10
9
|
type FormField,
|
|
11
10
|
type TableColumn,
|
|
12
11
|
} from '@fougere/app/client';
|
|
13
|
-
import { type
|
|
14
|
-
import type { ResourceKey } from './
|
|
15
|
-
import type { AdminFacets } from './
|
|
12
|
+
import { type IdentityCard, Invocation } from '@fougere/core/contract';
|
|
13
|
+
import type { ResourceKey } from './ResourceKey.js';
|
|
14
|
+
import type { AdminFacets } from './AdminFacets.js';
|
|
15
|
+
import type { AdminOperation } from './AdminOperation.js';
|
|
16
16
|
|
|
17
|
-
/** One
|
|
18
|
-
export interface AdminOperation extends CardOp {
|
|
19
|
-
/** Display fallback. An extension may replace it without renaming the call. */
|
|
20
|
-
label: string;
|
|
21
|
-
/** Optional confirmation sentence, interpreted by renderers that support actions. */
|
|
22
|
-
confirm?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** One door, everything the UI needs to render it. */
|
|
17
|
+
/** One facade, everything the UI needs to render it. */
|
|
26
18
|
export interface AdminResource extends ResourceKey {
|
|
27
19
|
/** The frond it belongs to — the menu groups by it, as the card does. */
|
|
28
20
|
frond: string;
|
|
@@ -40,7 +32,7 @@ export interface AdminResource extends ResourceKey {
|
|
|
40
32
|
/** Every callable operation, including the five CRUD verbs. */
|
|
41
33
|
operations: AdminOperation[];
|
|
42
34
|
/**
|
|
43
|
-
* Which of the five the
|
|
35
|
+
* Which of the five the facade actually serves. A facade answering only `list` gets a
|
|
44
36
|
* list and no buttons — the card says so, so the UI never offers what would 404.
|
|
45
37
|
*/
|
|
46
38
|
can: { list: boolean; show: boolean; create: boolean; edit: boolean; delete: boolean };
|
|
@@ -52,12 +44,12 @@ function labelOf(name: string): string {
|
|
|
52
44
|
}
|
|
53
45
|
|
|
54
46
|
/**
|
|
55
|
-
* The five verbs react-admin already has pages for. Everything else a
|
|
47
|
+
* The five verbs react-admin already has pages for. Everything else a facade serves is a
|
|
56
48
|
* business operation, and gets a button rather than a page.
|
|
57
49
|
*/
|
|
58
50
|
export const CRUD_OPS = ['list', 'findById', 'create', 'update', 'delete'] as const;
|
|
59
51
|
|
|
60
|
-
/** What a
|
|
52
|
+
/** What a facade serves beyond CRUD — the only thing this panel has that a generic one has not. */
|
|
61
53
|
export function actionsOf(operations: readonly AdminOperation[]): AdminOperation[] {
|
|
62
54
|
return operations.filter((op) => !(CRUD_OPS as readonly string[]).includes(op.name));
|
|
63
55
|
}
|
|
@@ -74,25 +66,25 @@ export function capabilitiesOf(operations: readonly Pick<AdminOperation, 'name'>
|
|
|
74
66
|
};
|
|
75
67
|
}
|
|
76
68
|
|
|
77
|
-
/** A
|
|
69
|
+
/** A facade with no schema is not a resource. */
|
|
78
70
|
export function resourcesOf(card: IdentityCard): AdminResource[] {
|
|
79
71
|
const out: AdminResource[] = [];
|
|
80
72
|
for (const frond of card.fronds) {
|
|
81
|
-
for (const
|
|
82
|
-
if (!
|
|
83
|
-
const entity = Card.fromDescriptor(
|
|
73
|
+
for (const facade of frond.facades) {
|
|
74
|
+
if (!facade.schema) continue;
|
|
75
|
+
const entity = Card.fromDescriptor(facade.schema).toSchema() as unknown as SchemaView;
|
|
84
76
|
const primary = FieldSet.of(entity.getFields()).primary;
|
|
85
77
|
// No primary means no row identity — a list could be drawn, but nothing could be
|
|
86
78
|
// opened, edited or deleted. Refusing here is the same answer `FieldSet.primary`
|
|
87
79
|
// gives by not defaulting to 'id': the caller decides, and this caller declines.
|
|
88
80
|
if (!primary) continue;
|
|
89
|
-
const operations =
|
|
90
|
-
const columns = tableColumnsOf(entity,
|
|
91
|
-
const fields = formFieldsOf(entity,
|
|
81
|
+
const operations = facade.ops.map((op) => ({ ...op, label: labelOf(op.name) }));
|
|
82
|
+
const columns = tableColumnsOf(entity, facade.name);
|
|
83
|
+
const fields = formFieldsOf(entity, facade.name);
|
|
92
84
|
out.push({
|
|
93
|
-
name:
|
|
85
|
+
name: facade.name,
|
|
94
86
|
frond: frond.name,
|
|
95
|
-
label: labelOf(
|
|
87
|
+
label: labelOf(facade.name),
|
|
96
88
|
facets: {},
|
|
97
89
|
primary,
|
|
98
90
|
columns,
|
|
@@ -105,7 +97,7 @@ export function resourcesOf(card: IdentityCard): AdminResource[] {
|
|
|
105
97
|
return out;
|
|
106
98
|
}
|
|
107
99
|
|
|
108
|
-
/** The provider's index — the half of a resource it needs, keyed by
|
|
100
|
+
/** The provider's index — the half of a resource it needs, keyed by facade name. */
|
|
109
101
|
export function keysOf(resources: AdminResource[]): Record<string, ResourceKey> {
|
|
110
102
|
return Object.fromEntries(resources.map((r) => [r.name, { name: r.name, primary: r.primary }]));
|
|
111
103
|
}
|
|
@@ -1,27 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from '
|
|
7
|
-
import { applyAdminExtensions, type AdminExtension } from './extensions.js';
|
|
8
|
-
import {
|
|
9
|
-
createDataProvider,
|
|
10
|
-
createLazyDataProvider,
|
|
11
|
-
type FougereDataProvider,
|
|
12
|
-
} from './provider.js';
|
|
13
|
-
import { fetchCard, keysOf, resourcesOf, type AdminResource } from './resources.js';
|
|
14
|
-
|
|
15
|
-
export interface AdminRuntimeOptions {
|
|
16
|
-
endpoint?: string;
|
|
17
|
-
fetcher?: Fetcher;
|
|
18
|
-
extensions?: readonly AdminExtension[];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface LoadedAdmin {
|
|
22
|
-
resources: AdminResource[];
|
|
23
|
-
provider: FougereDataProvider;
|
|
24
|
-
}
|
|
1
|
+
import { CALL_ENDPOINT, fetcher as browserFetcher } from '@fougere/app/client';
|
|
2
|
+
import { applyAdminExtensions } from './AdminExtension.js';
|
|
3
|
+
import { createDataProvider, createLazyDataProvider, type FougereDataProvider } from './FougereDataProvider.js';
|
|
4
|
+
import { fetchCard, keysOf, resourcesOf } from './AdminResource.js';
|
|
5
|
+
import type { AdminRuntimeOptions } from './AdminRuntimeOptions.js';
|
|
6
|
+
import type { LoadedAdmin } from './LoadedAdmin.js';
|
|
25
7
|
|
|
26
8
|
export interface AdminRuntime {
|
|
27
9
|
/** Loads and caches the card-derived model. */
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { fetcher as browserFetcher, type Fetcher } from '@fougere/app/client';
|
|
2
|
+
import { type AdminExtension } from './AdminExtension.js';
|
|
3
|
+
|
|
4
|
+
export interface AdminRuntimeOptions {
|
|
5
|
+
endpoint?: string;
|
|
6
|
+
fetcher?: Fetcher;
|
|
7
|
+
extensions?: readonly AdminExtension[];
|
|
8
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** How the system is arranged, as the app itself reports it. */
|
|
2
1
|
import dagre from '@dagrejs/dagre';
|
|
3
2
|
import {
|
|
4
3
|
CALL_ENDPOINT,
|
|
@@ -6,16 +5,11 @@ import {
|
|
|
6
5
|
sendCall,
|
|
7
6
|
type Fetcher,
|
|
8
7
|
} from '@fougere/app/client';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
type TopologyReport,
|
|
15
|
-
Invocation,
|
|
16
|
-
} from '@fougere/core/contract';
|
|
17
|
-
|
|
18
|
-
export type { TopologyReport, FrondPlacement, Edge, DeclaredEdge };
|
|
8
|
+
import { ErrorCode, type FrondPlacement, type TopologyReport, Invocation } from '@fougere/core/contract';
|
|
9
|
+
import type { TopologyNode } from './TopologyNode.js';
|
|
10
|
+
import type { Point } from './Point.js';
|
|
11
|
+
import type { PlacedNode } from './PlacedNode.js';
|
|
12
|
+
import type { PlacedEdge } from './PlacedEdge.js';
|
|
19
13
|
|
|
20
14
|
/** The report, or `undefined` when the app serves no topology at all. */
|
|
21
15
|
export async function fetchTopology(
|
|
@@ -35,23 +29,6 @@ export async function fetchTopology(
|
|
|
35
29
|
}
|
|
36
30
|
}
|
|
37
31
|
|
|
38
|
-
/** One frond as the page draws it: its placement, and the calls observed around it. */
|
|
39
|
-
export interface TopologyNode extends FrondPlacement {
|
|
40
|
-
/** Fronds this one called, with what it cost them. */
|
|
41
|
-
calls: Edge[];
|
|
42
|
-
/** Fronds that called this one. */
|
|
43
|
-
calledBy: Edge[];
|
|
44
|
-
/** Fronds its code reaches, whether or not a call has ever gone down the link. */
|
|
45
|
-
declaredCalls: DeclaredEdge[];
|
|
46
|
-
/** Where the config says to reach it, when it says so. */
|
|
47
|
-
at?: string;
|
|
48
|
-
/**
|
|
49
|
-
* Declared in `remotes:` and never heard from. A different absence from an opaque remote,
|
|
50
|
-
* which answered and keeps its shape to itself: this one may simply be down.
|
|
51
|
-
*/
|
|
52
|
-
silent: boolean;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
32
|
/** The report read as a graph — what runs here first, then what answered, then what never did. */
|
|
56
33
|
export function nodesOf(report: TopologyReport): TopologyNode[] {
|
|
57
34
|
const order = { local: 0, remote: 1 };
|
|
@@ -83,7 +60,7 @@ export function nodesOf(report: TopologyReport): TopologyNode[] {
|
|
|
83
60
|
frond: one.frond,
|
|
84
61
|
placement: 'remote' as const,
|
|
85
62
|
entities: 0,
|
|
86
|
-
|
|
63
|
+
facades: 0,
|
|
87
64
|
...around(one.frond),
|
|
88
65
|
...(one.at ? { at: one.at } : {}),
|
|
89
66
|
silent: true,
|
|
@@ -92,36 +69,6 @@ export function nodesOf(report: TopologyReport): TopologyNode[] {
|
|
|
92
69
|
return [...heard, ...silent];
|
|
93
70
|
}
|
|
94
71
|
|
|
95
|
-
export interface Point {
|
|
96
|
-
x: number;
|
|
97
|
-
y: number;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** A node placed on the drawing. `x`/`y` are its CENTRE, the convention the layout answers in. */
|
|
101
|
-
export interface PlacedNode {
|
|
102
|
-
node: TopologyNode;
|
|
103
|
-
x: number;
|
|
104
|
-
y: number;
|
|
105
|
-
width: number;
|
|
106
|
-
height: number;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* One link between two nodes. `count` is absent on a link nothing has travelled: the config
|
|
111
|
-
* declares it and no call has gone down it, which is drawn broken rather than not drawn.
|
|
112
|
-
*/
|
|
113
|
-
export interface PlacedEdge {
|
|
114
|
-
from: string;
|
|
115
|
-
to: string;
|
|
116
|
-
path: string;
|
|
117
|
-
/** Where this link's figure goes — clear of every node, or absent when the route is a point. */
|
|
118
|
-
at?: Point;
|
|
119
|
-
count?: number;
|
|
120
|
-
errors: number;
|
|
121
|
-
/** Where this link's volume sits among the others, `0` to `1`. Absent when nothing travelled. */
|
|
122
|
-
weight?: number;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
72
|
export interface Drawing {
|
|
126
73
|
nodes: PlacedNode[];
|
|
127
74
|
edges: PlacedEdge[];
|
|
@@ -310,5 +257,5 @@ function labelAt(points: readonly Point[], boxes: readonly PlacedNode[], half: n
|
|
|
310
257
|
* a frond that never answered has the same two zeroes and a different reason for them.
|
|
311
258
|
*/
|
|
312
259
|
export function isOpaque(node: FrondPlacement): boolean {
|
|
313
|
-
return node.placement === 'remote' && node.entities === 0 && node.
|
|
260
|
+
return node.placement === 'remote' && node.entities === 0 && node.facades === 0;
|
|
314
261
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface EditorialFacet {
|
|
2
|
+
/** The field that carries the human-readable title. */
|
|
3
|
+
title: string;
|
|
4
|
+
/** The lifecycle vocabulary, grouped by meaning rather than by raw enum value. */
|
|
5
|
+
state?: {
|
|
6
|
+
field: string;
|
|
7
|
+
draft?: readonly string[];
|
|
8
|
+
published?: readonly string[];
|
|
9
|
+
};
|
|
10
|
+
createdAt?: string;
|
|
11
|
+
updatedAt?: string;
|
|
12
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AdminProps as BaseAdminProps } from 'react-admin';
|
|
2
|
+
import type { FougereDashboardExtension } from './FougereDashboardExtension.js';
|
|
3
|
+
import type { AdminExtension } from './AdminExtension.js';
|
|
4
|
+
import type { Fetcher } from '@fougere/app/client';
|
|
5
|
+
import type { ReactAdminRenderers } from './ReactAdminRenderers.js';
|
|
6
|
+
import type { ReactAdminResourceComponents } from './ReactAdminResourceComponents.js';
|
|
7
|
+
|
|
8
|
+
export type FougereAdminProps = Omit<BaseAdminProps, 'children' | 'dataProvider'> & {
|
|
9
|
+
endpoint?: string;
|
|
10
|
+
fetcher?: Fetcher;
|
|
11
|
+
extensions?: readonly AdminExtension[];
|
|
12
|
+
renderers?: ReactAdminRenderers;
|
|
13
|
+
/** Add, move, resize, replace or hide widgets without snapshotting the dashboard. */
|
|
14
|
+
dashboardExtensions?: readonly FougereDashboardExtension[];
|
|
15
|
+
/** Explicit page-level escape hatches, scoped to one resource and one view. */
|
|
16
|
+
resourceComponents?: Record<string, ReactAdminResourceComponents>;
|
|
17
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FougereDashboardResource } from './FougereDashboardResource.js';
|
|
2
|
+
import type { FougereDashboardMetrics } from './FougereDashboardMetrics.js';
|
|
3
|
+
|
|
4
|
+
export interface FougereDashboardContextValue {
|
|
5
|
+
loading: boolean;
|
|
6
|
+
resources: FougereDashboardResource[];
|
|
7
|
+
editorial: FougereDashboardResource[];
|
|
8
|
+
users: FougereDashboardResource[];
|
|
9
|
+
metrics: FougereDashboardMetrics;
|
|
10
|
+
navigate(view: 'list' | 'show' | 'edit' | 'create', resource: string, id?: string | number): void;
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ComponentType } from 'react';
|
|
2
|
+
import type { FougereDashboardZone } from './FougereDashboardZone.js';
|
|
3
|
+
|
|
4
|
+
/** A delta over stable widget ids; a component on a new id contributes a new widget. */
|
|
5
|
+
export interface FougereDashboardExtension {
|
|
6
|
+
widget: string;
|
|
7
|
+
component?: ComponentType;
|
|
8
|
+
zone?: FougereDashboardZone;
|
|
9
|
+
span?: number;
|
|
10
|
+
hidden?: boolean;
|
|
11
|
+
before?: string;
|
|
12
|
+
after?: string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AdminFacets } from './AdminFacets.js';
|
|
2
|
+
|
|
3
|
+
export interface FougereDashboardResource {
|
|
4
|
+
name: string;
|
|
5
|
+
label: string;
|
|
6
|
+
primary: string;
|
|
7
|
+
facets: AdminFacets;
|
|
8
|
+
hasCreate: boolean;
|
|
9
|
+
hasEdit: boolean;
|
|
10
|
+
hasShow: boolean;
|
|
11
|
+
total: number;
|
|
12
|
+
rows: Record<string, unknown>[];
|
|
13
|
+
states: Record<string, number>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ComponentType } from 'react';
|
|
2
|
+
import type { FougereDashboardZone } from './FougereDashboardZone.js';
|
|
3
|
+
|
|
4
|
+
export interface FougereDashboardWidget {
|
|
5
|
+
id: string;
|
|
6
|
+
zone: FougereDashboardZone;
|
|
7
|
+
/** Twelve-column width for `main`, four-column width for `metrics`. */
|
|
8
|
+
span: number;
|
|
9
|
+
component: ComponentType;
|
|
10
|
+
hidden?: boolean;
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type FougereDashboardZone = 'hero' | 'metrics' | 'main';
|
|
@@ -1,30 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
sendCall,
|
|
4
|
-
fetcher as browserFetcher,
|
|
5
|
-
CALL_ENDPOINT,
|
|
6
|
-
itemsOf,
|
|
7
|
-
pageOf,
|
|
8
|
-
errorsByField,
|
|
9
|
-
type Fetcher,
|
|
10
|
-
} from '@fougere/app/client';
|
|
1
|
+
import { sendCall, fetcher as browserFetcher, CALL_ENDPOINT, itemsOf, pageOf, errorsByField } from '@fougere/app/client';
|
|
11
2
|
import { validationErrorsOf } from '@fougere/core/contract';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export interface ResourceKey {
|
|
15
|
-
/** The registration key its door answers under — `post`, not `Post`. */
|
|
16
|
-
name: string;
|
|
17
|
-
/** The field that identifies an instance. */
|
|
18
|
-
primary: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface ProviderOptions {
|
|
22
|
-
/** Registration key → its identity. Built from the card by `resourcesOf`. */
|
|
23
|
-
resources: Record<string, ResourceKey>;
|
|
24
|
-
/** The call endpoint. A named surface appends `/{surface}` to it. */
|
|
25
|
-
endpoint?: string;
|
|
26
|
-
fetcher?: Fetcher;
|
|
27
|
-
}
|
|
3
|
+
import type { ResourceKey } from './ResourceKey.js';
|
|
4
|
+
import type { ProviderOptions } from './ProviderOptions.js';
|
|
28
5
|
|
|
29
6
|
type Values = Record<string, unknown>;
|
|
30
7
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ResourceOptions } from 'react-admin';
|
|
2
|
+
import type { AdminFacets } from './AdminFacets.js';
|
|
3
|
+
|
|
4
|
+
export interface FougereResourceOptions extends ResourceOptions {
|
|
5
|
+
primary: string;
|
|
6
|
+
facets: AdminFacets;
|
|
7
|
+
/** The frond that owns this facade — the card groups by it, so the panel can too. */
|
|
8
|
+
frond?: string;
|
|
9
|
+
/** What the facade answers, with each op's kind. `query` reads, `command` writes. */
|
|
10
|
+
operations?: readonly { name: string; kind: 'query' | 'command' }[];
|
|
11
|
+
/** How many columns the shape yields — a rough measure of an entity's width. */
|
|
12
|
+
fieldCount?: number;
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Point } from './Point.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* One link between two nodes. `count` is absent on a link nothing has travelled: the config
|
|
5
|
+
* declares it and no call has gone down it, which is drawn broken rather than not drawn.
|
|
6
|
+
*/
|
|
7
|
+
export interface PlacedEdge {
|
|
8
|
+
from: string;
|
|
9
|
+
to: string;
|
|
10
|
+
path: string;
|
|
11
|
+
/** Where this link's figure goes — clear of every node, or absent when the route is a point. */
|
|
12
|
+
at?: Point;
|
|
13
|
+
count?: number;
|
|
14
|
+
errors: number;
|
|
15
|
+
/** Where this link's volume sits among the others, `0` to `1`. Absent when nothing travelled. */
|
|
16
|
+
weight?: number;
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TopologyNode } from './TopologyNode.js';
|
|
2
|
+
|
|
3
|
+
/** A node placed on the drawing. `x`/`y` are its CENTRE, the convention the layout answers in. */
|
|
4
|
+
export interface PlacedNode {
|
|
5
|
+
node: TopologyNode;
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
}
|
package/src/Point.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { fetcher as browserFetcher, type Fetcher } from '@fougere/app/client';
|
|
2
|
+
import type { ResourceKey } from './ResourceKey.js';
|
|
3
|
+
|
|
4
|
+
export interface ProviderOptions {
|
|
5
|
+
/** Registration key → its identity. Built from the card by `resourcesOf`. */
|
|
6
|
+
resources: Record<string, ResourceKey>;
|
|
7
|
+
/** The call endpoint. A named surface appends `/{surface}` to it. */
|
|
8
|
+
endpoint?: string;
|
|
9
|
+
fetcher?: Fetcher;
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ReactElement } from 'react';
|
|
2
|
+
import { type AdminResource } from './AdminResource.js';
|
|
3
|
+
import { type TableColumn } from '@fougere/app/client';
|
|
4
|
+
|
|
5
|
+
export interface ReactAdminFieldContext {
|
|
6
|
+
resource: AdminResource;
|
|
7
|
+
column: TableColumn;
|
|
8
|
+
/** The maintained Fougere renderer. Call it to wrap rather than replace it. */
|
|
9
|
+
defaultRender(): ReactElement;
|
|
10
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ReactElement } from 'react';
|
|
2
|
+
import { type AdminResource } from './AdminResource.js';
|
|
3
|
+
import { type FormField } from '@fougere/app/client';
|
|
4
|
+
|
|
5
|
+
export interface ReactAdminInputContext {
|
|
6
|
+
resource: AdminResource;
|
|
7
|
+
field: FormField;
|
|
8
|
+
defaultRender(): ReactElement;
|
|
9
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ReactAdminFieldRenderer } from './ReactAdminFieldRenderer.js';
|
|
2
|
+
import type { ReactAdminInputRenderer } from './ReactAdminInputRenderer.js';
|
|
3
|
+
|
|
4
|
+
export interface ReactAdminRenderers {
|
|
5
|
+
/** Exact `resource.field` keys. Unmentioned and future fields keep the default renderer. */
|
|
6
|
+
fields?: Record<string, ReactAdminFieldRenderer>;
|
|
7
|
+
inputs?: Record<string, ReactAdminInputRenderer>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** What a resource must tell the provider — the rest of the card is the UI's business. */
|
|
2
|
+
export interface ResourceKey {
|
|
3
|
+
/** The registration key its facade answers under — `post`, not `Post`. */
|
|
4
|
+
name: string;
|
|
5
|
+
/** The field that identifies an instance. */
|
|
6
|
+
primary: string;
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ReactAdminRenderers } from './ReactAdminRenderers.js';
|
|
2
|
+
import type { ReactAdminResourceComponents } from './ReactAdminResourceComponents.js';
|
|
3
|
+
|
|
4
|
+
export interface ResourceRenderOptions {
|
|
5
|
+
renderers?: ReactAdminRenderers;
|
|
6
|
+
components?: ReactAdminResourceComponents;
|
|
7
|
+
}
|