@vendure/dashboard 3.2.3 → 3.3.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/plugin/utils/ast-utils.d.ts +10 -0
- package/dist/plugin/utils/ast-utils.js +96 -0
- package/dist/plugin/utils/ast-utils.spec.d.ts +1 -0
- package/dist/plugin/utils/ast-utils.spec.js +120 -0
- package/dist/plugin/{config-loader.d.ts → utils/config-loader.d.ts} +22 -8
- package/dist/plugin/utils/config-loader.js +325 -0
- package/dist/plugin/{schema-generator.d.ts → utils/schema-generator.d.ts} +5 -0
- package/dist/plugin/{schema-generator.js → utils/schema-generator.js} +7 -1
- package/dist/plugin/{ui-config.js → utils/ui-config.js} +2 -3
- package/dist/plugin/vite-plugin-admin-api-schema.js +2 -2
- package/dist/plugin/vite-plugin-config-loader.d.ts +2 -3
- package/dist/plugin/vite-plugin-config-loader.js +18 -9
- package/dist/plugin/vite-plugin-config.js +4 -6
- package/dist/plugin/vite-plugin-dashboard-metadata.js +12 -14
- package/dist/plugin/vite-plugin-gql-tada.js +2 -2
- package/dist/plugin/vite-plugin-ui-config.js +3 -2
- package/package.json +16 -11
- package/src/app/app-providers.tsx +9 -9
- package/src/app/main.tsx +1 -1
- package/src/app/routes/_authenticated/_assets/assets.graphql.ts +26 -0
- package/src/app/routes/_authenticated/_assets/assets.tsx +2 -2
- package/src/app/routes/_authenticated/_assets/assets_.$id.tsx +156 -0
- package/src/app/routes/_authenticated/_orders/components/customer-address-selector.tsx +104 -0
- package/src/app/routes/_authenticated/_orders/components/edit-order-table.tsx +228 -0
- package/src/app/routes/_authenticated/_orders/components/money-gross-net.tsx +18 -0
- package/src/app/routes/_authenticated/_orders/components/order-address.tsx +2 -1
- package/src/app/routes/_authenticated/_orders/components/order-line-custom-fields-form.tsx +38 -0
- package/src/app/routes/_authenticated/_orders/components/order-table-totals.tsx +53 -0
- package/src/app/routes/_authenticated/_orders/components/order-table.tsx +8 -49
- package/src/app/routes/_authenticated/_orders/components/shipping-method-selector.tsx +65 -0
- package/src/app/routes/_authenticated/_orders/orders.graphql.ts +187 -2
- package/src/app/routes/_authenticated/_orders/orders.tsx +39 -18
- package/src/app/routes/_authenticated/_orders/orders_.$id.tsx +31 -9
- package/src/app/routes/_authenticated/_orders/orders_.draft.$id.tsx +418 -0
- package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +8 -2
- package/src/app/routes/_authenticated/_products/products.tsx +1 -1
- package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +6 -0
- package/src/app/routes/_authenticated/_system/job-queue.tsx +7 -8
- package/src/app/routes/_authenticated/_system/scheduled-tasks.tsx +241 -0
- package/src/app/routes/_authenticated.tsx +12 -1
- package/src/app/styles.css +15 -0
- package/src/lib/components/data-table/add-filter-menu.tsx +61 -0
- package/src/lib/components/data-table/data-table-column-header.tsx +0 -13
- package/src/lib/components/data-table/data-table-filter-badge.tsx +75 -0
- package/src/lib/components/data-table/data-table-filter-dialog.tsx +27 -28
- package/src/lib/components/data-table/data-table-types.ts +1 -0
- package/src/lib/components/data-table/data-table-view-options.tsx +73 -24
- package/src/lib/components/data-table/data-table.tsx +49 -44
- package/src/lib/components/data-table/filters/data-table-boolean-filter.tsx +57 -0
- package/src/lib/components/data-table/filters/data-table-datetime-filter.tsx +93 -0
- package/src/lib/components/data-table/filters/data-table-id-filter.tsx +58 -0
- package/src/lib/components/data-table/filters/data-table-number-filter.tsx +119 -0
- package/src/lib/components/data-table/filters/data-table-string-filter.tsx +62 -0
- package/src/lib/components/data-table/human-readable-operator.tsx +65 -0
- package/src/lib/components/data-table/refresh-button.tsx +25 -0
- package/src/lib/components/layout/nav-user.tsx +20 -15
- package/src/lib/components/layout/prerelease-popup.tsx +1 -5
- package/src/lib/components/shared/alerts.tsx +19 -1
- package/src/lib/components/shared/asset/asset-focal-point-editor.tsx +93 -0
- package/src/lib/components/shared/{asset-gallery.tsx → asset/asset-gallery.tsx} +51 -20
- package/src/lib/components/shared/{asset-picker-dialog.tsx → asset/asset-picker-dialog.tsx} +1 -1
- package/src/lib/components/shared/{asset-preview-dialog.tsx → asset/asset-preview-dialog.tsx} +1 -7
- package/src/lib/components/shared/asset/asset-preview-selector.tsx +34 -0
- package/src/lib/components/shared/asset/asset-preview.tsx +128 -0
- package/src/lib/components/shared/asset/asset-properties.tsx +46 -0
- package/src/lib/components/shared/{focal-point-control.tsx → asset/focal-point-control.tsx} +1 -1
- package/src/lib/components/shared/custom-fields-form.tsx +4 -3
- package/src/lib/components/shared/customer-selector.tsx +13 -14
- package/src/lib/components/shared/detail-page-button.tsx +2 -2
- package/src/lib/components/shared/entity-assets.tsx +3 -3
- package/src/lib/components/shared/error-page.tsx +2 -2
- package/src/lib/components/shared/navigation-confirmation.tsx +49 -0
- package/src/lib/components/shared/paginated-list-data-table.tsx +10 -1
- package/src/lib/components/shared/product-variant-selector.tsx +111 -0
- package/src/lib/components/shared/vendure-image.tsx +1 -1
- package/src/lib/components/ui/calendar.tsx +508 -63
- package/src/lib/framework/alert/alert-extensions.tsx +31 -0
- package/src/lib/framework/alert/alert-item.tsx +47 -0
- package/src/lib/framework/alert/alerts-indicator.tsx +23 -0
- package/src/lib/framework/alert/types.ts +13 -0
- package/src/lib/framework/dashboard-widget/base-widget.tsx +1 -0
- package/src/lib/framework/defaults.ts +34 -0
- package/src/lib/framework/document-introspection/get-document-structure.spec.ts +113 -3
- package/src/lib/framework/document-introspection/get-document-structure.ts +71 -13
- package/src/lib/framework/extension-api/define-dashboard-extension.ts +15 -5
- package/src/lib/framework/extension-api/extension-api-types.ts +81 -12
- package/src/lib/framework/form-engine/use-generated-form.tsx +8 -7
- package/src/lib/framework/layout-engine/layout-extensions.ts +3 -3
- package/src/lib/framework/layout-engine/page-layout.tsx +196 -35
- package/src/lib/framework/layout-engine/page-provider.tsx +10 -0
- package/src/lib/framework/page/detail-page.tsx +62 -9
- package/src/lib/framework/page/list-page.tsx +42 -4
- package/src/lib/framework/page/page-api.ts +1 -1
- package/src/lib/framework/page/use-detail-page.ts +82 -0
- package/src/lib/framework/registry/registry-types.ts +6 -2
- package/src/lib/graphql/fragments.tsx +8 -0
- package/src/lib/graphql/graphql-env.d.ts +25 -9
- package/src/lib/hooks/use-auth.tsx +13 -1
- package/src/lib/hooks/use-channel.ts +13 -0
- package/src/lib/hooks/use-local-format.ts +28 -1
- package/src/lib/hooks/use-page.tsx +2 -3
- package/src/lib/hooks/use-permissions.ts +13 -0
- package/src/lib/index.ts +7 -8
- package/src/lib/providers/auth.tsx +22 -9
- package/src/lib/providers/channel-provider.tsx +9 -1
- package/src/lib/providers/server-config.tsx +7 -1
- package/src/lib/providers/user-settings.tsx +24 -0
- package/vite/utils/ast-utils.spec.ts +128 -0
- package/vite/utils/ast-utils.ts +119 -0
- package/vite/utils/config-loader.ts +410 -0
- package/vite/{schema-generator.ts → utils/schema-generator.ts} +11 -6
- package/vite/{ui-config.ts → utils/ui-config.ts} +7 -3
- package/vite/vite-plugin-admin-api-schema.ts +2 -12
- package/vite/vite-plugin-config-loader.ts +25 -13
- package/vite/vite-plugin-config.ts +1 -0
- package/vite/vite-plugin-dashboard-metadata.ts +19 -15
- package/vite/vite-plugin-gql-tada.ts +2 -2
- package/vite/vite-plugin-ui-config.ts +3 -2
- package/dist/plugin/config-loader.js +0 -141
- package/src/lib/components/shared/asset-preview.tsx +0 -345
- package/src/lib/components/ui/avatar.tsx +0 -38
- package/vite/config-loader.ts +0 -181
- /package/dist/plugin/{ui-config.d.ts → utils/ui-config.d.ts} +0 -0
|
@@ -2,6 +2,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
|
|
|
2
2
|
import { cn } from '@/lib/utils.js';
|
|
3
3
|
import { Trans } from '@/lib/trans.js';
|
|
4
4
|
import { PropsWithChildren, useRef, useEffect, useState, createContext, useContext } from 'react';
|
|
5
|
+
import type React from 'react';
|
|
5
6
|
|
|
6
7
|
type WidgetDimensions = {
|
|
7
8
|
width: number;
|
|
@@ -122,6 +122,11 @@ export function registerDefaults() {
|
|
|
122
122
|
title: 'Healthchecks',
|
|
123
123
|
url: '/healthchecks',
|
|
124
124
|
},
|
|
125
|
+
{
|
|
126
|
+
id: 'scheduled-tasks',
|
|
127
|
+
title: 'Scheduled Tasks',
|
|
128
|
+
url: '/scheduled-tasks',
|
|
129
|
+
},
|
|
125
130
|
],
|
|
126
131
|
},
|
|
127
132
|
{
|
|
@@ -216,4 +221,33 @@ export function registerDefaults() {
|
|
|
216
221
|
component: OrdersSummaryWidget,
|
|
217
222
|
defaultSize: { w: 6, h: 3, x: 6, y: 0 },
|
|
218
223
|
});
|
|
224
|
+
|
|
225
|
+
// registerAlert<boolean>({
|
|
226
|
+
// id: 'test-alert',
|
|
227
|
+
// title: data => `Test Alert ${String(data)}`,
|
|
228
|
+
// description: 'This is a test alert',
|
|
229
|
+
// severity: 'info',
|
|
230
|
+
// check: () => Promise.resolve(true),
|
|
231
|
+
// actions: [
|
|
232
|
+
// {
|
|
233
|
+
// label: 'Test Action',
|
|
234
|
+
// onClick: () => console.log('Test Action'),
|
|
235
|
+
// },
|
|
236
|
+
// ],
|
|
237
|
+
// });
|
|
238
|
+
|
|
239
|
+
// registerAlert<boolean>({
|
|
240
|
+
// id: 'test-alert-2',
|
|
241
|
+
// title: 'Test Alert 2',
|
|
242
|
+
// description: 'This is a test alert 2',
|
|
243
|
+
// severity: 'info',
|
|
244
|
+
// check: () => Promise.resolve(true),
|
|
245
|
+
// shouldShow: data => data === true,
|
|
246
|
+
// actions: [
|
|
247
|
+
// {
|
|
248
|
+
// label: 'Test Action',
|
|
249
|
+
// onClick: () => console.log('Test Action'),
|
|
250
|
+
// },
|
|
251
|
+
// ],
|
|
252
|
+
// });
|
|
219
253
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { graphql } from 'gql.tada';
|
|
2
2
|
import { describe, expect, it, vi } from 'vitest';
|
|
3
3
|
|
|
4
|
-
import { getListQueryFields } from './get-document-structure.js';
|
|
4
|
+
import { getListQueryFields, getOperationVariablesFields } from './get-document-structure.js';
|
|
5
5
|
|
|
6
6
|
vi.mock('virtual:admin-api-schema', () => {
|
|
7
7
|
return {
|
|
@@ -12,7 +12,10 @@ vi.mock('virtual:admin-api-schema', () => {
|
|
|
12
12
|
product: ['Product', false, false, false],
|
|
13
13
|
collection: ['Collection', false, false, false],
|
|
14
14
|
},
|
|
15
|
-
Mutation: {
|
|
15
|
+
Mutation: {
|
|
16
|
+
updateProduct: ['Product', false, false, false],
|
|
17
|
+
adjustDraftOrderLine: ['Order', false, false, false],
|
|
18
|
+
},
|
|
16
19
|
|
|
17
20
|
Collection: {
|
|
18
21
|
id: ['ID', false, false, false],
|
|
@@ -126,8 +129,25 @@ vi.mock('virtual:admin-api-schema', () => {
|
|
|
126
129
|
languageCode: ['LanguageCode', false, false, false],
|
|
127
130
|
name: ['String', false, false, false],
|
|
128
131
|
},
|
|
132
|
+
Order: {
|
|
133
|
+
id: ['ID', false, false, false],
|
|
134
|
+
lines: ['OrderLine', false, true, false],
|
|
135
|
+
},
|
|
136
|
+
OrderLine: {
|
|
137
|
+
id: ['ID', false, false, false],
|
|
138
|
+
quantity: ['Int', false, false, false],
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
inputs: {
|
|
142
|
+
UpdateProductInput: {
|
|
143
|
+
id: ['ID', false, false, false],
|
|
144
|
+
name: ['String', false, false, false],
|
|
145
|
+
},
|
|
146
|
+
AdjustDraftOrderLineInput: {
|
|
147
|
+
orderLineId: ['ID', false, false, false],
|
|
148
|
+
quantity: ['Int', false, false, false],
|
|
149
|
+
},
|
|
129
150
|
},
|
|
130
|
-
inputs: {},
|
|
131
151
|
scalars: ['ID', 'String', 'Int', 'Boolean', 'Float', 'JSON', 'DateTime', 'Upload', 'Money'],
|
|
132
152
|
enums: {},
|
|
133
153
|
},
|
|
@@ -308,3 +328,93 @@ describe('getListQueryFields', () => {
|
|
|
308
328
|
]);
|
|
309
329
|
});
|
|
310
330
|
});
|
|
331
|
+
|
|
332
|
+
describe('getOperationVariablesFields', () => {
|
|
333
|
+
it('should extract fields from a simple mutation', () => {
|
|
334
|
+
const doc = graphql(`
|
|
335
|
+
mutation UpdateProduct($input: UpdateProductInput!) {
|
|
336
|
+
updateProduct(input: $input) {
|
|
337
|
+
...ProductDetail
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
fragment ProductDetail on Product {
|
|
342
|
+
id
|
|
343
|
+
name
|
|
344
|
+
}
|
|
345
|
+
`);
|
|
346
|
+
|
|
347
|
+
const fields = getOperationVariablesFields(doc, 'input');
|
|
348
|
+
expect(fields).toEqual([
|
|
349
|
+
{
|
|
350
|
+
isPaginatedList: false,
|
|
351
|
+
isScalar: true,
|
|
352
|
+
list: false,
|
|
353
|
+
name: 'id',
|
|
354
|
+
nullable: false,
|
|
355
|
+
type: 'ID',
|
|
356
|
+
typeInfo: undefined,
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
isPaginatedList: false,
|
|
360
|
+
isScalar: true,
|
|
361
|
+
list: false,
|
|
362
|
+
name: 'name',
|
|
363
|
+
nullable: false,
|
|
364
|
+
type: 'String',
|
|
365
|
+
typeInfo: undefined,
|
|
366
|
+
},
|
|
367
|
+
]);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it('should handle a mutation with a nested input', () => {
|
|
371
|
+
const doc = graphql(`
|
|
372
|
+
mutation AdjustDraftOrderLine($orderId: ID!, $input: AdjustDraftOrderLineInput!) {
|
|
373
|
+
adjustDraftOrderLine(orderId: $orderId, input: $input) {
|
|
374
|
+
id
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
`);
|
|
378
|
+
|
|
379
|
+
const fields = getOperationVariablesFields(doc, undefined);
|
|
380
|
+
expect(fields).toEqual([
|
|
381
|
+
{
|
|
382
|
+
name: 'orderId',
|
|
383
|
+
isPaginatedList: false,
|
|
384
|
+
isScalar: true,
|
|
385
|
+
list: false,
|
|
386
|
+
nullable: false,
|
|
387
|
+
type: 'ID',
|
|
388
|
+
typeInfo: undefined,
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
name: 'input',
|
|
392
|
+
isPaginatedList: false,
|
|
393
|
+
isScalar: false,
|
|
394
|
+
list: false,
|
|
395
|
+
nullable: true,
|
|
396
|
+
type: 'AdjustDraftOrderLineInput',
|
|
397
|
+
typeInfo: [
|
|
398
|
+
{
|
|
399
|
+
name: 'orderLineId',
|
|
400
|
+
isPaginatedList: false,
|
|
401
|
+
isScalar: true,
|
|
402
|
+
list: false,
|
|
403
|
+
nullable: false,
|
|
404
|
+
type: 'ID',
|
|
405
|
+
typeInfo: undefined,
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
name: 'quantity',
|
|
409
|
+
isPaginatedList: false,
|
|
410
|
+
isScalar: true,
|
|
411
|
+
list: false,
|
|
412
|
+
nullable: false,
|
|
413
|
+
type: 'Int',
|
|
414
|
+
typeInfo: undefined,
|
|
415
|
+
},
|
|
416
|
+
],
|
|
417
|
+
},
|
|
418
|
+
]);
|
|
419
|
+
});
|
|
420
|
+
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
2
|
+
import { VariablesOf } from 'gql.tada';
|
|
1
3
|
import {
|
|
2
4
|
DocumentNode,
|
|
3
|
-
OperationDefinitionNode,
|
|
4
5
|
FieldNode,
|
|
5
6
|
FragmentDefinitionNode,
|
|
6
7
|
FragmentSpreadNode,
|
|
7
|
-
|
|
8
|
+
OperationDefinitionNode,
|
|
8
9
|
} from 'graphql';
|
|
9
10
|
import { DefinitionNode, NamedTypeNode, SelectionSetNode, TypeNode } from 'graphql/language/ast.js';
|
|
10
11
|
import { schemaInfo } from 'virtual:admin-api-schema';
|
|
@@ -146,7 +147,10 @@ function findNestedPaginatedLists(
|
|
|
146
147
|
*
|
|
147
148
|
* The operation variables fields are the fields of the `UpdateProductInput` type.
|
|
148
149
|
*/
|
|
149
|
-
export function getOperationVariablesFields
|
|
150
|
+
export function getOperationVariablesFields<T extends TypedDocumentNode<any, any>>(
|
|
151
|
+
documentNode: T,
|
|
152
|
+
varName?: keyof VariablesOf<T>,
|
|
153
|
+
): FieldInfo[] {
|
|
150
154
|
const fields: FieldInfo[] = [];
|
|
151
155
|
|
|
152
156
|
const operationDefinition = documentNode.definitions.find(
|
|
@@ -154,11 +158,31 @@ export function getOperationVariablesFields(documentNode: DocumentNode): FieldIn
|
|
|
154
158
|
);
|
|
155
159
|
|
|
156
160
|
if (operationDefinition?.variableDefinitions) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
161
|
+
const variableDefinitions = varName
|
|
162
|
+
? operationDefinition.variableDefinitions.filter(
|
|
163
|
+
variable => variable.variable.name.value === varName,
|
|
164
|
+
)
|
|
165
|
+
: operationDefinition.variableDefinitions;
|
|
166
|
+
variableDefinitions.forEach(variableDef => {
|
|
167
|
+
const unwrappedType = unwrapVariableDefinitionType(variableDef.type);
|
|
168
|
+
const isScalar = isScalarType(unwrappedType.name.value);
|
|
169
|
+
const fieldName = variableDef.variable.name.value;
|
|
170
|
+
const typeName = unwrappedType.name.value;
|
|
171
|
+
const inputTypeInfo = isScalar
|
|
172
|
+
? {
|
|
173
|
+
name: fieldName,
|
|
174
|
+
type: typeName,
|
|
175
|
+
nullable: false,
|
|
176
|
+
list: false,
|
|
177
|
+
isScalar: true,
|
|
178
|
+
isPaginatedList: false,
|
|
179
|
+
}
|
|
180
|
+
: getInputTypeInfo(fieldName, typeName);
|
|
181
|
+
if (varName && inputTypeInfo?.name === varName) {
|
|
182
|
+
fields.push(...(inputTypeInfo.typeInfo ?? []));
|
|
183
|
+
} else {
|
|
184
|
+
fields.push(inputTypeInfo);
|
|
185
|
+
}
|
|
162
186
|
});
|
|
163
187
|
}
|
|
164
188
|
|
|
@@ -331,7 +355,9 @@ export function getOperationTypeInfo(
|
|
|
331
355
|
if (definitionNode.kind === 'OperationDefinition') {
|
|
332
356
|
const firstSelection = definitionNode?.selectionSet.selections[0];
|
|
333
357
|
if (firstSelection?.kind === 'Field') {
|
|
334
|
-
return
|
|
358
|
+
return definitionNode.operation === 'query'
|
|
359
|
+
? getQueryInfo(firstSelection.name.value)
|
|
360
|
+
: getMutationInfo(firstSelection.name.value);
|
|
335
361
|
}
|
|
336
362
|
}
|
|
337
363
|
if (definitionNode.kind === 'Field' && parentTypeName) {
|
|
@@ -349,7 +375,7 @@ export function getTypeFieldInfo(typeName: string): FieldInfo[] {
|
|
|
349
375
|
}
|
|
350
376
|
return fieldInfo;
|
|
351
377
|
})
|
|
352
|
-
.filter(x => x != null)
|
|
378
|
+
.filter(x => x != null);
|
|
353
379
|
}
|
|
354
380
|
|
|
355
381
|
function getQueryInfo(name: string): FieldInfo {
|
|
@@ -364,8 +390,40 @@ function getQueryInfo(name: string): FieldInfo {
|
|
|
364
390
|
};
|
|
365
391
|
}
|
|
366
392
|
|
|
367
|
-
function
|
|
368
|
-
|
|
393
|
+
function getMutationInfo(name: string): FieldInfo {
|
|
394
|
+
const fieldInfo = schemaInfo.types.Mutation[name];
|
|
395
|
+
return {
|
|
396
|
+
name,
|
|
397
|
+
type: fieldInfo[0],
|
|
398
|
+
nullable: fieldInfo[1],
|
|
399
|
+
list: fieldInfo[2],
|
|
400
|
+
isPaginatedList: fieldInfo[3],
|
|
401
|
+
isScalar: schemaInfo.scalars.includes(fieldInfo[0]),
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function getInputTypeInfo(name: string, type: string): FieldInfo {
|
|
406
|
+
const fieldInfo = schemaInfo.inputs[type];
|
|
407
|
+
if (!fieldInfo) {
|
|
408
|
+
throw new Error(`Input type ${type} not found`);
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
name,
|
|
412
|
+
type,
|
|
413
|
+
nullable: true,
|
|
414
|
+
list: false,
|
|
415
|
+
isPaginatedList: false,
|
|
416
|
+
isScalar: false,
|
|
417
|
+
typeInfo: getInputTypeFields(type),
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function getInputTypeFields(name: string): FieldInfo[] {
|
|
422
|
+
const inputType = schemaInfo.inputs[name];
|
|
423
|
+
if (!inputType) {
|
|
424
|
+
throw new Error(`Input type ${name} not found`);
|
|
425
|
+
}
|
|
426
|
+
return Object.entries(inputType).map(([fieldName, fieldInfo]: [string, any]) => {
|
|
369
427
|
const type = fieldInfo[0];
|
|
370
428
|
const isScalar = isScalarType(type);
|
|
371
429
|
const isEnum = isEnumType(type);
|
|
@@ -376,7 +434,7 @@ function getInputTypeInfo(name: string): FieldInfo[] {
|
|
|
376
434
|
list: fieldInfo[2],
|
|
377
435
|
isPaginatedList: fieldInfo[3],
|
|
378
436
|
isScalar,
|
|
379
|
-
typeInfo: !isScalar && !isEnum ?
|
|
437
|
+
typeInfo: !isScalar && !isEnum ? getInputTypeFields(type) : undefined,
|
|
380
438
|
};
|
|
381
439
|
});
|
|
382
440
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { registerDashboardWidget } from '
|
|
2
|
-
import { DashboardExtension } from '@/framework/extension-api/extension-api-types.js';
|
|
3
|
-
import { addNavMenuItem, NavMenuItem } from '@/framework/nav-menu/nav-menu-extensions.js';
|
|
4
|
-
import { registerRoute } from '@/framework/page/page-api.js';
|
|
5
|
-
|
|
1
|
+
import { registerDashboardWidget } from '../dashboard-widget/widget-extensions.js';
|
|
6
2
|
import {
|
|
7
3
|
registerDashboardActionBarItem,
|
|
8
4
|
registerDashboardPageBlock,
|
|
9
5
|
} from '../layout-engine/layout-extensions.js';
|
|
6
|
+
import { addNavMenuItem, NavMenuItem } from '../nav-menu/nav-menu-extensions.js';
|
|
7
|
+
import { registerRoute } from '../page/page-api.js';
|
|
10
8
|
import { globalRegistry } from '../registry/global-registry.js';
|
|
11
9
|
|
|
10
|
+
import { DashboardExtension } from './extension-api-types.js';
|
|
11
|
+
|
|
12
12
|
globalRegistry.register('extensionSourceChangeCallbacks', new Set<() => void>());
|
|
13
13
|
globalRegistry.register('registerDashboardExtensionCallbacks', new Set<() => void>());
|
|
14
14
|
|
|
@@ -22,6 +22,16 @@ export function executeDashboardExtensionCallbacks() {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* @description
|
|
27
|
+
* **Status: Developer Preview**
|
|
28
|
+
*
|
|
29
|
+
* The main entry point for extensions to the React-based dashboard.
|
|
30
|
+
*
|
|
31
|
+
*
|
|
32
|
+
* @docsCategory extensions
|
|
33
|
+
* @since 3.3.0
|
|
34
|
+
*/
|
|
25
35
|
export function defineDashboardExtension(extension: DashboardExtension) {
|
|
26
36
|
globalRegistry.get('registerDashboardExtensionCallbacks').add(() => {
|
|
27
37
|
if (extension.routes) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PageContextValue } from '@/framework/layout-engine/page-provider.js';
|
|
2
2
|
import { AnyRoute, RouteOptions } from '@tanstack/react-router';
|
|
3
|
-
import React from 'react';
|
|
3
|
+
import type React from 'react';
|
|
4
4
|
|
|
5
|
+
import { DashboardAlertDefinition } from '../alert/types.js';
|
|
5
6
|
import { DashboardWidgetDefinition } from '../dashboard-widget/types.js';
|
|
6
|
-
import {
|
|
7
|
+
import { NavMenuItem } from '../nav-menu/nav-menu-extensions.js';
|
|
7
8
|
|
|
8
9
|
export interface DashboardRouteDefinition {
|
|
9
10
|
component: (route: AnyRoute) => React.ReactNode;
|
|
@@ -17,42 +18,110 @@ export interface ActionBarButtonState {
|
|
|
17
18
|
visible: boolean;
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* @description
|
|
23
|
+
* **Status: Developer Preview**
|
|
24
|
+
*
|
|
25
|
+
* Allows you to define custom action bar items for any page in the dashboard.
|
|
26
|
+
*
|
|
27
|
+
* @docsCategory extensions
|
|
28
|
+
* @since 3.3.0
|
|
29
|
+
*/
|
|
20
30
|
export interface DashboardActionBarItem {
|
|
21
|
-
|
|
22
|
-
|
|
31
|
+
/**
|
|
32
|
+
* @description
|
|
33
|
+
* The ID of the page where the action bar item should be displayed.
|
|
34
|
+
*/
|
|
35
|
+
pageId: string;
|
|
36
|
+
/**
|
|
37
|
+
* @description
|
|
38
|
+
* A React component that will be rendered in the action bar.
|
|
39
|
+
*/
|
|
40
|
+
component: React.FunctionComponent<{ context: PageContextValue }>;
|
|
41
|
+
/**
|
|
42
|
+
* @description
|
|
43
|
+
* Any permissions that are required to display this action bar item.
|
|
44
|
+
*/
|
|
23
45
|
requiresPermission?: string | string[];
|
|
24
46
|
}
|
|
25
47
|
|
|
26
48
|
export interface DashboardActionBarDropdownMenuItem {
|
|
27
49
|
locationId: string;
|
|
28
|
-
component: React.FunctionComponent<{ context:
|
|
50
|
+
component: React.FunctionComponent<{ context: PageContextValue }>;
|
|
29
51
|
requiresPermission?: string | string[];
|
|
30
52
|
}
|
|
31
53
|
|
|
32
54
|
export type PageBlockPosition = { blockId: string; order: 'before' | 'after' | 'replace' };
|
|
33
55
|
|
|
56
|
+
/**
|
|
57
|
+
* @description
|
|
58
|
+
* **Status: Developer Preview**
|
|
59
|
+
*
|
|
60
|
+
* The location of a page block in the dashboard. The location can be found by turning on
|
|
61
|
+
* "developer mode" in the dashboard user menu (bottom left corner) and then
|
|
62
|
+
* clicking the `< />` icon when hovering over a page block.
|
|
63
|
+
*
|
|
64
|
+
* @docsCategory extensions
|
|
65
|
+
* @since 3.3.0
|
|
66
|
+
*/
|
|
34
67
|
export type PageBlockLocation = {
|
|
35
68
|
pageId: string;
|
|
36
69
|
position: PageBlockPosition;
|
|
37
70
|
column: 'main' | 'side';
|
|
38
71
|
};
|
|
39
72
|
|
|
73
|
+
/**
|
|
74
|
+
* @description
|
|
75
|
+
* **Status: Developer Preview**
|
|
76
|
+
*
|
|
77
|
+
* This allows you to insert a custom component into a specific location
|
|
78
|
+
* on any page in the dashboard.
|
|
79
|
+
*
|
|
80
|
+
* @docsCategory extensions
|
|
81
|
+
* @since 3.3.0
|
|
82
|
+
*/
|
|
40
83
|
export interface DashboardPageBlockDefinition {
|
|
41
84
|
id: string;
|
|
42
85
|
title?: React.ReactNode;
|
|
43
86
|
location: PageBlockLocation;
|
|
44
|
-
component: React.FunctionComponent<{ context:
|
|
87
|
+
component: React.FunctionComponent<{ context: PageContextValue }>;
|
|
45
88
|
requiresPermission?: string | string[];
|
|
46
89
|
}
|
|
47
90
|
|
|
48
91
|
/**
|
|
49
92
|
* @description
|
|
50
|
-
*
|
|
93
|
+
* **Status: Developer Preview**
|
|
94
|
+
*
|
|
51
95
|
* This is used to define the routes, widgets, etc. that will be displayed in the dashboard.
|
|
96
|
+
*
|
|
97
|
+
* @docsCategory extensions
|
|
98
|
+
* @since 3.3.0
|
|
52
99
|
*/
|
|
53
100
|
export interface DashboardExtension {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
101
|
+
/**
|
|
102
|
+
* @description
|
|
103
|
+
* Allows you to define custom routes such as list or detail views.
|
|
104
|
+
*/
|
|
105
|
+
routes?: DashboardRouteDefinition[];
|
|
106
|
+
/**
|
|
107
|
+
* @description
|
|
108
|
+
* Allows you to define custom page blocks for any page in the dashboard.
|
|
109
|
+
*/
|
|
110
|
+
pageBlocks?: DashboardPageBlockDefinition[];
|
|
111
|
+
/**
|
|
112
|
+
* @description
|
|
113
|
+
* Allows you to define custom action bar items for any page in the dashboard.
|
|
114
|
+
*/
|
|
115
|
+
actionBarItems?: DashboardActionBarItem[];
|
|
116
|
+
/**
|
|
117
|
+
* @description
|
|
118
|
+
* Not yet implemented
|
|
119
|
+
*/
|
|
120
|
+
alerts?: DashboardAlertDefinition[];
|
|
121
|
+
/**
|
|
122
|
+
* @description
|
|
123
|
+
* Allows you to define custom routes for the dashboard, which will render the
|
|
124
|
+
* given components and optionally also add a nav menu item.
|
|
125
|
+
*/
|
|
126
|
+
widgets?: DashboardWidgetDefinition[];
|
|
58
127
|
}
|
|
@@ -13,13 +13,14 @@ import { useForm } from 'react-hook-form';
|
|
|
13
13
|
|
|
14
14
|
export interface GeneratedFormOptions<
|
|
15
15
|
T extends TypedDocumentNode<any, any>,
|
|
16
|
-
VarName extends keyof VariablesOf<T> = 'input',
|
|
16
|
+
VarName extends (keyof VariablesOf<T>) | undefined = 'input',
|
|
17
17
|
E extends Record<string, any> = Record<string, any>,
|
|
18
18
|
> {
|
|
19
19
|
document?: T;
|
|
20
|
+
varName?: VarName;
|
|
20
21
|
entity: E | null | undefined;
|
|
21
|
-
setValues: (entity: NonNullable<E>) => VariablesOf<T>[VarName]
|
|
22
|
-
onSubmit?: (values: VariablesOf<T>[VarName]) => void;
|
|
22
|
+
setValues: (entity: NonNullable<E>) => VarName extends keyof VariablesOf<T> ? VariablesOf<T>[VarName] : VariablesOf<T>;
|
|
23
|
+
onSubmit?: (values: VarName extends keyof VariablesOf<T> ? VariablesOf<T>[VarName] : VariablesOf<T>) => void;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -31,13 +32,13 @@ export interface GeneratedFormOptions<
|
|
|
31
32
|
*/
|
|
32
33
|
export function useGeneratedForm<
|
|
33
34
|
T extends TypedDocumentNode<any, any>,
|
|
34
|
-
VarName extends keyof VariablesOf<T>
|
|
35
|
+
VarName extends keyof VariablesOf<T> | undefined,
|
|
35
36
|
E extends Record<string, any> = Record<string, any>,
|
|
36
37
|
>(options: GeneratedFormOptions<T, VarName, E>) {
|
|
37
|
-
const { document, entity, setValues, onSubmit } = options;
|
|
38
|
+
const { document, entity, setValues, onSubmit, varName } = options;
|
|
38
39
|
const { activeChannel } = useChannel();
|
|
39
40
|
const availableLanguages = useServerConfig()?.availableLanguages || [];
|
|
40
|
-
const updateFields = document ? getOperationVariablesFields(document) : [];
|
|
41
|
+
const updateFields = document ? getOperationVariablesFields(document, varName) : [];
|
|
41
42
|
const schema = createFormSchemaFromFields(updateFields);
|
|
42
43
|
const defaultValues = getDefaultValuesFromFields(updateFields, activeChannel?.defaultLanguageCode);
|
|
43
44
|
const processedEntity = ensureTranslationsForAllLanguages(entity, availableLanguages);
|
|
@@ -58,7 +59,7 @@ export function useGeneratedForm<
|
|
|
58
59
|
};
|
|
59
60
|
if (onSubmit) {
|
|
60
61
|
submitHandler = (event: FormEvent) => {
|
|
61
|
-
form.handleSubmit(onSubmit)(event);
|
|
62
|
+
form.handleSubmit(onSubmit as any)(event);
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
65
|
|
|
@@ -9,13 +9,13 @@ globalRegistry.register('dashboardPageBlockRegistry', new Map<string, DashboardP
|
|
|
9
9
|
|
|
10
10
|
export function registerDashboardActionBarItem(item: DashboardActionBarItem) {
|
|
11
11
|
globalRegistry.set('dashboardActionBarItemRegistry', map => {
|
|
12
|
-
map.set(item.
|
|
12
|
+
map.set(item.pageId, [...(map.get(item.pageId) ?? []), item]);
|
|
13
13
|
return map;
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function getDashboardActionBarItems(
|
|
18
|
-
return globalRegistry.get('dashboardActionBarItemRegistry').get(
|
|
17
|
+
export function getDashboardActionBarItems(pageId: string) {
|
|
18
|
+
return globalRegistry.get('dashboardActionBarItemRegistry').get(pageId) ?? [];
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function registerDashboardPageBlock(block: DashboardPageBlockDefinition) {
|