@vendure/dashboard 3.3.6-master-202506290242 → 3.3.6-master-202507010731
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/package.json +4 -4
- package/src/app/routes/_authenticated/_administrators/administrators_.$id.tsx +5 -1
- package/src/app/routes/_authenticated/_assets/assets_.$id.tsx +7 -2
- package/src/app/routes/_authenticated/_channels/channels_.$id.tsx +5 -1
- package/src/app/routes/_authenticated/_collections/collections.graphql.ts +16 -0
- package/src/app/routes/_authenticated/_collections/collections.tsx +16 -2
- package/src/app/routes/_authenticated/_collections/collections_.$id.tsx +5 -1
- package/src/app/routes/_authenticated/_collections/components/assign-collections-to-channel-dialog.tsx +110 -0
- package/src/app/routes/_authenticated/_collections/components/collection-bulk-actions.tsx +99 -0
- package/src/app/routes/_authenticated/_countries/countries.graphql.ts +1 -1
- package/src/app/routes/_authenticated/_countries/countries_.$id.tsx +9 -5
- package/src/app/routes/_authenticated/_customer-groups/customer-groups.graphql.ts +1 -1
- package/src/app/routes/_authenticated/_customer-groups/customer-groups_.$id.tsx +8 -5
- package/src/app/routes/_authenticated/_customers/customers_.$id.tsx +5 -1
- package/src/app/routes/_authenticated/_facets/facets_.$id.tsx +5 -1
- package/src/app/routes/_authenticated/_orders/orders_.$id.tsx +5 -2
- package/src/app/routes/_authenticated/_payment-methods/payment-methods_.$id.tsx +5 -1
- package/src/app/routes/_authenticated/_product-variants/components/product-variant-bulk-actions.tsx +184 -0
- package/src/app/routes/_authenticated/_product-variants/product-variants.graphql.ts +62 -1
- package/src/app/routes/_authenticated/_product-variants/product-variants.tsx +33 -3
- package/src/app/routes/_authenticated/_product-variants/product-variants_.$id.tsx +14 -3
- package/src/app/routes/_authenticated/_products/components/assign-facet-values-dialog.tsx +67 -36
- package/src/app/routes/_authenticated/_products/components/assign-to-channel-dialog.tsx +28 -17
- package/src/app/routes/_authenticated/_products/components/product-bulk-actions.tsx +12 -2
- package/src/app/routes/_authenticated/_products/components/product-variants-table.tsx +74 -55
- package/src/app/routes/_authenticated/_products/products_.$id.tsx +6 -1
- package/src/app/routes/_authenticated/_promotions/promotions_.$id.tsx +5 -1
- package/src/app/routes/_authenticated/_roles/roles_.$id.tsx +5 -1
- package/src/app/routes/_authenticated/_sellers/sellers_.$id.tsx +6 -2
- package/src/app/routes/_authenticated/_shipping-methods/shipping-methods_.$id.tsx +5 -1
- package/src/app/routes/_authenticated/_stock-locations/stock-locations_.$id.tsx +5 -1
- package/src/app/routes/_authenticated/_tax-categories/tax-categories.graphql.ts +1 -1
- package/src/app/routes/_authenticated/_tax-categories/tax-categories_.$id.tsx +9 -5
- package/src/app/routes/_authenticated/_tax-rates/tax-rates.graphql.ts +1 -1
- package/src/app/routes/_authenticated/_tax-rates/tax-rates_.$id.tsx +8 -4
- package/src/app/routes/_authenticated/_zones/zones.graphql.ts +1 -1
- package/src/app/routes/_authenticated/_zones/zones_.$id.tsx +8 -4
- package/src/lib/components/shared/detail-page-button.tsx +3 -1
- package/src/lib/components/shared/paginated-list-data-table.tsx +6 -4
- package/src/lib/framework/data-table/data-table-extensions.ts +14 -0
- package/src/lib/framework/document-extension/extend-detail-form-query.ts +50 -0
- package/src/lib/framework/document-extension/extend-document.spec.ts +884 -0
- package/src/lib/framework/document-extension/extend-document.ts +159 -0
- package/src/lib/framework/document-introspection/add-custom-fields.ts +48 -0
- package/src/lib/framework/extension-api/define-dashboard-extension.ts +33 -2
- package/src/lib/framework/extension-api/extension-api-types.ts +21 -2
- package/src/lib/framework/form-engine/custom-form-component-extensions.ts +13 -3
- package/src/lib/framework/layout-engine/page-layout.tsx +1 -0
- package/src/lib/framework/page/detail-page-route-loader.tsx +22 -4
- package/src/lib/framework/page/use-detail-page.ts +11 -2
- package/src/lib/framework/registry/registry-types.ts +3 -0
- package/src/lib/graphql/graphql-env.d.ts +8 -6
- package/src/lib/hooks/use-extended-detail-query.ts +37 -0
- package/src/lib/hooks/use-extended-list-query.ts +73 -0
|
@@ -12,9 +12,13 @@ import { Trans, useLingui } from '@/lib/trans.js';
|
|
|
12
12
|
|
|
13
13
|
import { Permission } from '@vendure/common/lib/generated-types';
|
|
14
14
|
import {
|
|
15
|
+
assignProductsToChannelDocument,
|
|
15
16
|
deleteProductsDocument,
|
|
16
17
|
duplicateEntityDocument,
|
|
18
|
+
getProductsWithFacetValuesByIdsDocument,
|
|
19
|
+
productDetailDocument,
|
|
17
20
|
removeProductsFromChannelDocument,
|
|
21
|
+
updateProductsDocument,
|
|
18
22
|
} from '../products.graphql.js';
|
|
19
23
|
import { AssignFacetValuesDialog } from './assign-facet-values-dialog.js';
|
|
20
24
|
import { AssignToChannelDialog } from './assign-to-channel-dialog.js';
|
|
@@ -84,7 +88,9 @@ export const AssignProductsToChannelBulkAction: BulkActionComponent<any> = ({ se
|
|
|
84
88
|
<AssignToChannelDialog
|
|
85
89
|
open={dialogOpen}
|
|
86
90
|
onOpenChange={setDialogOpen}
|
|
87
|
-
|
|
91
|
+
entityIds={selection.map(s => s.id)}
|
|
92
|
+
entityType="products"
|
|
93
|
+
mutationFn={api.mutate(assignProductsToChannelDocument)}
|
|
88
94
|
onSuccess={handleSuccess}
|
|
89
95
|
/>
|
|
90
96
|
</>
|
|
@@ -156,7 +162,11 @@ export const AssignFacetValuesToProductsBulkAction: BulkActionComponent<any> = (
|
|
|
156
162
|
<AssignFacetValuesDialog
|
|
157
163
|
open={dialogOpen}
|
|
158
164
|
onOpenChange={setDialogOpen}
|
|
159
|
-
|
|
165
|
+
entityIds={selection.map(s => s.id)}
|
|
166
|
+
entityType="products"
|
|
167
|
+
queryFn={variables => api.query(getProductsWithFacetValuesByIdsDocument, variables)}
|
|
168
|
+
mutationFn={api.mutate(updateProductsDocument)}
|
|
169
|
+
detailDocument={productDetailDocument}
|
|
160
170
|
onSuccess={handleSuccess}
|
|
161
171
|
/>
|
|
162
172
|
</>
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { Money } from
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
import { useState } from "react";
|
|
8
|
-
import { productVariantListDocument } from "../products.graphql.js";
|
|
1
|
+
import { Money } from '@/components/data-display/money.js';
|
|
2
|
+
import {
|
|
3
|
+
PaginatedListDataTable,
|
|
4
|
+
PaginatedListRefresherRegisterFn,
|
|
5
|
+
} from '@/components/shared/paginated-list-data-table.js';
|
|
6
|
+
import { StockLevelLabel } from '@/components/shared/stock-level-label.js';
|
|
9
7
|
import { graphql } from '@/graphql/graphql.js';
|
|
8
|
+
import { useLocalFormat } from '@/hooks/use-local-format.js';
|
|
9
|
+
import { DetailPageButton } from '@/index.js';
|
|
10
|
+
import { ColumnFiltersState, SortingState } from '@tanstack/react-table';
|
|
11
|
+
import { useState } from 'react';
|
|
12
|
+
import { productVariantListDocument } from '../products.graphql.js';
|
|
10
13
|
|
|
11
14
|
export const deleteProductVariantDocument = graphql(`
|
|
12
15
|
mutation DeleteProductVariant($id: ID!) {
|
|
@@ -17,62 +20,78 @@ export const deleteProductVariantDocument = graphql(`
|
|
|
17
20
|
}
|
|
18
21
|
`);
|
|
19
22
|
|
|
20
|
-
|
|
21
23
|
interface ProductVariantsTableProps {
|
|
22
24
|
productId: string;
|
|
23
25
|
registerRefresher?: PaginatedListRefresherRegisterFn;
|
|
26
|
+
fromProductDetailPage?: boolean;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
export function ProductVariantsTable({
|
|
29
|
+
export function ProductVariantsTable({
|
|
30
|
+
productId,
|
|
31
|
+
registerRefresher,
|
|
32
|
+
fromProductDetailPage,
|
|
33
|
+
}: ProductVariantsTableProps) {
|
|
27
34
|
const { formatCurrencyName } = useLocalFormat();
|
|
28
35
|
const [page, setPage] = useState(1);
|
|
29
36
|
const [pageSize, setPageSize] = useState(10);
|
|
30
37
|
const [sorting, setSorting] = useState<SortingState>([]);
|
|
31
38
|
const [filters, setFilters] = useState<ColumnFiltersState>([]);
|
|
32
39
|
|
|
33
|
-
return
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
40
|
+
return (
|
|
41
|
+
<PaginatedListDataTable
|
|
42
|
+
registerRefresher={registerRefresher}
|
|
43
|
+
listQuery={productVariantListDocument}
|
|
44
|
+
deleteMutation={deleteProductVariantDocument}
|
|
45
|
+
transformVariables={variables => ({
|
|
46
|
+
...variables,
|
|
47
|
+
productId,
|
|
48
|
+
})}
|
|
49
|
+
defaultVisibility={{
|
|
50
|
+
id: false,
|
|
51
|
+
currencyCode: false,
|
|
52
|
+
}}
|
|
53
|
+
customizeColumns={{
|
|
54
|
+
name: {
|
|
55
|
+
header: 'Variant name',
|
|
56
|
+
cell: ({ row: { original } }) => (
|
|
57
|
+
<DetailPageButton
|
|
58
|
+
href={`../../product-variants/${original.id}`}
|
|
59
|
+
label={original.name}
|
|
60
|
+
search={fromProductDetailPage ? { from: 'product' } : undefined}
|
|
61
|
+
/>
|
|
62
|
+
),
|
|
63
|
+
},
|
|
64
|
+
currencyCode: {
|
|
65
|
+
cell: ({ row: { original } }) => formatCurrencyName(original.currencyCode, 'full'),
|
|
66
|
+
},
|
|
67
|
+
price: {
|
|
68
|
+
cell: ({ row: { original } }) => (
|
|
69
|
+
<Money value={original.price} currency={original.currencyCode} />
|
|
70
|
+
),
|
|
71
|
+
},
|
|
72
|
+
priceWithTax: {
|
|
73
|
+
cell: ({ row: { original } }) => (
|
|
74
|
+
<Money value={original.priceWithTax} currency={original.currencyCode} />
|
|
75
|
+
),
|
|
76
|
+
},
|
|
77
|
+
stockLevels: {
|
|
78
|
+
cell: ({ row: { original } }) => <StockLevelLabel stockLevels={original.stockLevels} />,
|
|
79
|
+
},
|
|
80
|
+
}}
|
|
81
|
+
page={page}
|
|
82
|
+
itemsPerPage={pageSize}
|
|
83
|
+
sorting={sorting}
|
|
84
|
+
columnFilters={filters}
|
|
85
|
+
onPageChange={(_, page, perPage) => {
|
|
86
|
+
setPage(page);
|
|
87
|
+
setPageSize(perPage);
|
|
88
|
+
}}
|
|
89
|
+
onSortChange={(_, sorting) => {
|
|
90
|
+
setSorting(sorting);
|
|
91
|
+
}}
|
|
92
|
+
onFilterChange={(_, filters) => {
|
|
93
|
+
setFilters(filters);
|
|
94
|
+
}}
|
|
95
|
+
/>
|
|
96
|
+
);
|
|
78
97
|
}
|
|
@@ -31,9 +31,12 @@ import { CreateProductVariantsDialog } from './components/create-product-variant
|
|
|
31
31
|
import { ProductVariantsTable } from './components/product-variants-table.js';
|
|
32
32
|
import { createProductDocument, productDetailDocument, updateProductDocument } from './products.graphql.js';
|
|
33
33
|
|
|
34
|
+
const pageId = 'product-detail';
|
|
35
|
+
|
|
34
36
|
export const Route = createFileRoute('/_authenticated/_products/products_/$id')({
|
|
35
37
|
component: ProductDetailPage,
|
|
36
38
|
loader: detailPageRouteLoader({
|
|
39
|
+
pageId,
|
|
37
40
|
queryDocument: productDetailDocument,
|
|
38
41
|
breadcrumb(isNew, entity) {
|
|
39
42
|
return [
|
|
@@ -53,6 +56,7 @@ function ProductDetailPage() {
|
|
|
53
56
|
const refreshRef = useRef<() => void>(() => {});
|
|
54
57
|
|
|
55
58
|
const { form, submitHandler, entity, isPending, refreshEntity, resetForm } = useDetailPage({
|
|
59
|
+
pageId,
|
|
56
60
|
entityName: 'Product',
|
|
57
61
|
queryDocument: productDetailDocument,
|
|
58
62
|
createDocument: createProductDocument,
|
|
@@ -91,7 +95,7 @@ function ProductDetailPage() {
|
|
|
91
95
|
});
|
|
92
96
|
|
|
93
97
|
return (
|
|
94
|
-
<Page pageId=
|
|
98
|
+
<Page pageId={pageId} form={form} submitHandler={submitHandler} entity={entity}>
|
|
95
99
|
<PageTitle>{creatingNewEntity ? <Trans>New product</Trans> : (entity?.name ?? '')}</PageTitle>
|
|
96
100
|
<PageActionBar>
|
|
97
101
|
<PageActionBarRight>
|
|
@@ -148,6 +152,7 @@ function ProductDetailPage() {
|
|
|
148
152
|
registerRefresher={refresher => {
|
|
149
153
|
refreshRef.current = refresher;
|
|
150
154
|
}}
|
|
155
|
+
fromProductDetailPage={true}
|
|
151
156
|
/>
|
|
152
157
|
<div className="mt-4">
|
|
153
158
|
<AddProductVariantDialog
|
|
@@ -31,9 +31,12 @@ import {
|
|
|
31
31
|
updatePromotionDocument,
|
|
32
32
|
} from './promotions.graphql.js';
|
|
33
33
|
|
|
34
|
+
const pageId = 'promotion-detail';
|
|
35
|
+
|
|
34
36
|
export const Route = createFileRoute('/_authenticated/_promotions/promotions_/$id')({
|
|
35
37
|
component: PromotionDetailPage,
|
|
36
38
|
loader: detailPageRouteLoader({
|
|
39
|
+
pageId,
|
|
37
40
|
queryDocument: promotionDetailDocument,
|
|
38
41
|
breadcrumb(isNew, entity) {
|
|
39
42
|
return [
|
|
@@ -52,6 +55,7 @@ function PromotionDetailPage() {
|
|
|
52
55
|
const { i18n } = useLingui();
|
|
53
56
|
|
|
54
57
|
const { form, submitHandler, entity, isPending, resetForm } = useDetailPage({
|
|
58
|
+
pageId,
|
|
55
59
|
queryDocument: promotionDetailDocument,
|
|
56
60
|
createDocument: createPromotionDocument,
|
|
57
61
|
transformCreateInput: values => {
|
|
@@ -114,7 +118,7 @@ function PromotionDetailPage() {
|
|
|
114
118
|
});
|
|
115
119
|
|
|
116
120
|
return (
|
|
117
|
-
<Page pageId=
|
|
121
|
+
<Page pageId={pageId} form={form} submitHandler={submitHandler} entity={entity}>
|
|
118
122
|
<PageTitle>{creatingNewEntity ? <Trans>New promotion</Trans> : (entity?.name ?? '')}</PageTitle>
|
|
119
123
|
<PageActionBar>
|
|
120
124
|
<PageActionBarRight>
|
|
@@ -22,9 +22,12 @@ import { toast } from 'sonner';
|
|
|
22
22
|
import { PermissionsGrid } from './components/permissions-grid.js';
|
|
23
23
|
import { createRoleDocument, roleDetailDocument, updateRoleDocument } from './roles.graphql.js';
|
|
24
24
|
|
|
25
|
+
const pageId = 'role-detail';
|
|
26
|
+
|
|
25
27
|
export const Route = createFileRoute('/_authenticated/_roles/roles_/$id')({
|
|
26
28
|
component: RoleDetailPage,
|
|
27
29
|
loader: detailPageRouteLoader({
|
|
30
|
+
pageId,
|
|
28
31
|
queryDocument: roleDetailDocument,
|
|
29
32
|
breadcrumb(isNew, entity) {
|
|
30
33
|
return [
|
|
@@ -43,6 +46,7 @@ function RoleDetailPage() {
|
|
|
43
46
|
const { i18n } = useLingui();
|
|
44
47
|
|
|
45
48
|
const { form, submitHandler, entity, isPending, resetForm } = useDetailPage({
|
|
49
|
+
pageId,
|
|
46
50
|
queryDocument: roleDetailDocument,
|
|
47
51
|
createDocument: createRoleDocument,
|
|
48
52
|
updateDocument: updateRoleDocument,
|
|
@@ -71,7 +75,7 @@ function RoleDetailPage() {
|
|
|
71
75
|
});
|
|
72
76
|
|
|
73
77
|
return (
|
|
74
|
-
<Page pageId=
|
|
78
|
+
<Page pageId={pageId} form={form} submitHandler={submitHandler} entity={entity}>
|
|
75
79
|
<PageTitle>{creatingNewEntity ? <Trans>New role</Trans> : (entity?.description ?? '')}</PageTitle>
|
|
76
80
|
<PageActionBar>
|
|
77
81
|
<PageActionBarRight>
|
|
@@ -20,9 +20,12 @@ import { createFileRoute, useNavigate } from '@tanstack/react-router';
|
|
|
20
20
|
import { toast } from 'sonner';
|
|
21
21
|
import { createSellerDocument, sellerDetailDocument, updateSellerDocument } from './sellers.graphql.js';
|
|
22
22
|
|
|
23
|
+
const pageId = 'seller-detail';
|
|
24
|
+
|
|
23
25
|
export const Route = createFileRoute('/_authenticated/_sellers/sellers_/$id')({
|
|
24
26
|
component: SellerDetailPage,
|
|
25
27
|
loader: detailPageRouteLoader({
|
|
28
|
+
pageId,
|
|
26
29
|
queryDocument: sellerDetailDocument,
|
|
27
30
|
breadcrumb: (isNew, entity) => [
|
|
28
31
|
{ path: '/sellers', label: 'Sellers' },
|
|
@@ -38,7 +41,8 @@ function SellerDetailPage() {
|
|
|
38
41
|
const creatingNewEntity = params.id === NEW_ENTITY_PATH;
|
|
39
42
|
const { i18n } = useLingui();
|
|
40
43
|
|
|
41
|
-
const { form, submitHandler, entity, isPending } = useDetailPage({
|
|
44
|
+
const { form, submitHandler, entity, isPending, resetForm } = useDetailPage({
|
|
45
|
+
pageId,
|
|
42
46
|
queryDocument: sellerDetailDocument,
|
|
43
47
|
createDocument: createSellerDocument,
|
|
44
48
|
updateDocument: updateSellerDocument,
|
|
@@ -65,7 +69,7 @@ function SellerDetailPage() {
|
|
|
65
69
|
});
|
|
66
70
|
|
|
67
71
|
return (
|
|
68
|
-
<Page pageId=
|
|
72
|
+
<Page pageId={pageId} form={form} submitHandler={submitHandler} entity={entity}>
|
|
69
73
|
<PageTitle>{creatingNewEntity ? <Trans>New seller</Trans> : (entity?.name ?? '')}</PageTitle>
|
|
70
74
|
<PageActionBar>
|
|
71
75
|
<PageActionBarRight>
|
|
@@ -30,9 +30,12 @@ import {
|
|
|
30
30
|
updateShippingMethodDocument,
|
|
31
31
|
} from './shipping-methods.graphql.js';
|
|
32
32
|
|
|
33
|
+
const pageId = 'shipping-method-detail';
|
|
34
|
+
|
|
33
35
|
export const Route = createFileRoute('/_authenticated/_shipping-methods/shipping-methods_/$id')({
|
|
34
36
|
component: ShippingMethodDetailPage,
|
|
35
37
|
loader: detailPageRouteLoader({
|
|
38
|
+
pageId,
|
|
36
39
|
queryDocument: shippingMethodDetailDocument,
|
|
37
40
|
breadcrumb(isNew, entity) {
|
|
38
41
|
return [
|
|
@@ -51,6 +54,7 @@ function ShippingMethodDetailPage() {
|
|
|
51
54
|
const { i18n } = useLingui();
|
|
52
55
|
|
|
53
56
|
const { form, submitHandler, entity, isPending, resetForm } = useDetailPage({
|
|
57
|
+
pageId,
|
|
54
58
|
queryDocument: shippingMethodDetailDocument,
|
|
55
59
|
createDocument: createShippingMethodDocument,
|
|
56
60
|
updateDocument: updateShippingMethodDocument,
|
|
@@ -94,7 +98,7 @@ function ShippingMethodDetailPage() {
|
|
|
94
98
|
});
|
|
95
99
|
|
|
96
100
|
return (
|
|
97
|
-
<Page pageId=
|
|
101
|
+
<Page pageId={pageId} form={form} submitHandler={submitHandler} entity={entity}>
|
|
98
102
|
<PageTitle>
|
|
99
103
|
{creatingNewEntity ? <Trans>New shipping method</Trans> : (entity?.name ?? '')}
|
|
100
104
|
</PageTitle>
|
|
@@ -26,9 +26,12 @@ import {
|
|
|
26
26
|
updateStockLocationDocument,
|
|
27
27
|
} from './stock-locations.graphql.js';
|
|
28
28
|
|
|
29
|
+
const pageId = 'stock-location-detail';
|
|
30
|
+
|
|
29
31
|
export const Route = createFileRoute('/_authenticated/_stock-locations/stock-locations_/$id')({
|
|
30
32
|
component: StockLocationDetailPage,
|
|
31
33
|
loader: detailPageRouteLoader({
|
|
34
|
+
pageId,
|
|
32
35
|
queryDocument: stockLocationDetailQuery,
|
|
33
36
|
breadcrumb(isNew, entity) {
|
|
34
37
|
return [
|
|
@@ -47,6 +50,7 @@ function StockLocationDetailPage() {
|
|
|
47
50
|
const { i18n } = useLingui();
|
|
48
51
|
|
|
49
52
|
const { form, submitHandler, entity, isPending, resetForm } = useDetailPage({
|
|
53
|
+
pageId,
|
|
50
54
|
queryDocument: stockLocationDetailQuery,
|
|
51
55
|
createDocument: createStockLocationDocument,
|
|
52
56
|
updateDocument: updateStockLocationDocument,
|
|
@@ -74,7 +78,7 @@ function StockLocationDetailPage() {
|
|
|
74
78
|
});
|
|
75
79
|
|
|
76
80
|
return (
|
|
77
|
-
<Page pageId=
|
|
81
|
+
<Page pageId={pageId} form={form} submitHandler={submitHandler} entity={entity}>
|
|
78
82
|
<PageTitle>
|
|
79
83
|
{creatingNewEntity ? <Trans>New stock location</Trans> : (entity?.name ?? '')}
|
|
80
84
|
</PageTitle>
|
|
@@ -22,14 +22,17 @@ import { createFileRoute, useNavigate } from '@tanstack/react-router';
|
|
|
22
22
|
import { toast } from 'sonner';
|
|
23
23
|
import {
|
|
24
24
|
createTaxCategoryDocument,
|
|
25
|
-
|
|
25
|
+
taxCategoryDetailDocument,
|
|
26
26
|
updateTaxCategoryDocument,
|
|
27
27
|
} from './tax-categories.graphql.js';
|
|
28
28
|
|
|
29
|
+
const pageId = 'tax-category-detail';
|
|
30
|
+
|
|
29
31
|
export const Route = createFileRoute('/_authenticated/_tax-categories/tax-categories_/$id')({
|
|
30
32
|
component: TaxCategoryDetailPage,
|
|
31
33
|
loader: detailPageRouteLoader({
|
|
32
|
-
|
|
34
|
+
pageId,
|
|
35
|
+
queryDocument: taxCategoryDetailDocument,
|
|
33
36
|
breadcrumb(isNew, entity) {
|
|
34
37
|
return [
|
|
35
38
|
{ path: '/tax-categories', label: 'Tax categories' },
|
|
@@ -46,8 +49,9 @@ function TaxCategoryDetailPage() {
|
|
|
46
49
|
const creatingNewEntity = params.id === NEW_ENTITY_PATH;
|
|
47
50
|
const { i18n } = useLingui();
|
|
48
51
|
|
|
49
|
-
const { form, submitHandler, entity, isPending } = useDetailPage({
|
|
50
|
-
|
|
52
|
+
const { form, submitHandler, entity, isPending, resetForm } = useDetailPage({
|
|
53
|
+
pageId,
|
|
54
|
+
queryDocument: taxCategoryDetailDocument,
|
|
51
55
|
createDocument: createTaxCategoryDocument,
|
|
52
56
|
updateDocument: updateTaxCategoryDocument,
|
|
53
57
|
setValuesForUpdate: entity => {
|
|
@@ -73,7 +77,7 @@ function TaxCategoryDetailPage() {
|
|
|
73
77
|
});
|
|
74
78
|
|
|
75
79
|
return (
|
|
76
|
-
<Page pageId=
|
|
80
|
+
<Page pageId={pageId} form={form} submitHandler={submitHandler} entity={entity}>
|
|
77
81
|
<PageTitle>
|
|
78
82
|
{creatingNewEntity ? <Trans>New tax category</Trans> : (entity?.name ?? '')}
|
|
79
83
|
</PageTitle>
|
|
@@ -23,12 +23,15 @@ import { useDetailPage } from '@/framework/page/use-detail-page.js';
|
|
|
23
23
|
import { Trans, useLingui } from '@/lib/trans.js';
|
|
24
24
|
import { createFileRoute, useNavigate } from '@tanstack/react-router';
|
|
25
25
|
import { toast } from 'sonner';
|
|
26
|
-
import { createTaxRateDocument,
|
|
26
|
+
import { createTaxRateDocument, taxRateDetailDocument, updateTaxRateDocument } from './tax-rates.graphql.js';
|
|
27
|
+
|
|
28
|
+
const pageId = 'tax-rate-detail';
|
|
27
29
|
|
|
28
30
|
export const Route = createFileRoute('/_authenticated/_tax-rates/tax-rates_/$id')({
|
|
29
31
|
component: TaxRateDetailPage,
|
|
30
32
|
loader: detailPageRouteLoader({
|
|
31
|
-
|
|
33
|
+
pageId,
|
|
34
|
+
queryDocument: taxRateDetailDocument,
|
|
32
35
|
breadcrumb(isNew, entity) {
|
|
33
36
|
return [
|
|
34
37
|
{ path: '/tax-rates', label: 'Tax rates' },
|
|
@@ -46,7 +49,8 @@ function TaxRateDetailPage() {
|
|
|
46
49
|
const { i18n } = useLingui();
|
|
47
50
|
|
|
48
51
|
const { form, submitHandler, entity, isPending, resetForm } = useDetailPage({
|
|
49
|
-
|
|
52
|
+
pageId,
|
|
53
|
+
queryDocument: taxRateDetailDocument,
|
|
50
54
|
createDocument: createTaxRateDocument,
|
|
51
55
|
updateDocument: updateTaxRateDocument,
|
|
52
56
|
setValuesForUpdate: entity => {
|
|
@@ -77,7 +81,7 @@ function TaxRateDetailPage() {
|
|
|
77
81
|
});
|
|
78
82
|
|
|
79
83
|
return (
|
|
80
|
-
<Page pageId=
|
|
84
|
+
<Page pageId={pageId} form={form} submitHandler={submitHandler} entity={entity}>
|
|
81
85
|
<PageTitle>{creatingNewEntity ? <Trans>New tax rate</Trans> : (entity?.name ?? '')}</PageTitle>
|
|
82
86
|
<PageActionBar>
|
|
83
87
|
<PageActionBarRight>
|
|
@@ -20,12 +20,15 @@ import { Trans, useLingui } from '@/lib/trans.js';
|
|
|
20
20
|
import { createFileRoute, useNavigate } from '@tanstack/react-router';
|
|
21
21
|
import { toast } from 'sonner';
|
|
22
22
|
import { ZoneCountriesTable } from './components/zone-countries-table.js';
|
|
23
|
-
import { createZoneDocument, updateZoneDocument,
|
|
23
|
+
import { createZoneDocument, updateZoneDocument, zoneDetailDocument } from './zones.graphql.js';
|
|
24
|
+
|
|
25
|
+
const pageId = 'zone-detail';
|
|
24
26
|
|
|
25
27
|
export const Route = createFileRoute('/_authenticated/_zones/zones_/$id')({
|
|
26
28
|
component: ZoneDetailPage,
|
|
27
29
|
loader: detailPageRouteLoader({
|
|
28
|
-
|
|
30
|
+
pageId,
|
|
31
|
+
queryDocument: zoneDetailDocument,
|
|
29
32
|
breadcrumb(isNew, entity) {
|
|
30
33
|
return [{ path: '/zones', label: 'Zones' }, isNew ? <Trans>New zone</Trans> : entity?.name];
|
|
31
34
|
},
|
|
@@ -40,7 +43,8 @@ function ZoneDetailPage() {
|
|
|
40
43
|
const { i18n } = useLingui();
|
|
41
44
|
|
|
42
45
|
const { form, submitHandler, entity, isPending, resetForm } = useDetailPage({
|
|
43
|
-
|
|
46
|
+
pageId,
|
|
47
|
+
queryDocument: zoneDetailDocument,
|
|
44
48
|
createDocument: createZoneDocument,
|
|
45
49
|
updateDocument: updateZoneDocument,
|
|
46
50
|
setValuesForUpdate: entity => {
|
|
@@ -66,7 +70,7 @@ function ZoneDetailPage() {
|
|
|
66
70
|
});
|
|
67
71
|
|
|
68
72
|
return (
|
|
69
|
-
<Page pageId=
|
|
73
|
+
<Page pageId={pageId} form={form} submitHandler={submitHandler} entity={entity}>
|
|
70
74
|
<PageTitle>{creatingNewEntity ? <Trans>New zone</Trans> : (entity?.name ?? '')}</PageTitle>
|
|
71
75
|
<PageActionBar>
|
|
72
76
|
<PageActionBarRight>
|
|
@@ -7,18 +7,20 @@ export function DetailPageButton({
|
|
|
7
7
|
href,
|
|
8
8
|
label,
|
|
9
9
|
disabled,
|
|
10
|
+
search,
|
|
10
11
|
}: {
|
|
11
12
|
label: string | React.ReactNode;
|
|
12
13
|
id?: string;
|
|
13
14
|
href?: string;
|
|
14
15
|
disabled?: boolean;
|
|
16
|
+
search?: Record<string, string>;
|
|
15
17
|
}) {
|
|
16
18
|
if (!id && !href) {
|
|
17
19
|
return <span>{label}</span>;
|
|
18
20
|
}
|
|
19
21
|
return (
|
|
20
22
|
<Button asChild variant="ghost" disabled={disabled}>
|
|
21
|
-
<Link to={href ?? `./${id}`}>
|
|
23
|
+
<Link to={href ?? `./${id}`} search={search ?? {}}>
|
|
22
24
|
{label}
|
|
23
25
|
{!disabled && <ChevronRight className="h-3 w-3 text-muted-foreground" />}
|
|
24
26
|
</Link>
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
import { DisplayComponent } from '@/framework/component-registry/dynamic-component.js';
|
|
31
31
|
import { BulkAction } from '@/framework/data-table/data-table-types.js';
|
|
32
32
|
import { ResultOf } from '@/graphql/graphql.js';
|
|
33
|
+
import { useExtendedListQuery } from '@/hooks/use-extended-list-query.js';
|
|
33
34
|
import { Trans, useLingui } from '@/lib/trans.js';
|
|
34
35
|
import { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
35
36
|
import {
|
|
@@ -276,6 +277,7 @@ export function PaginatedListDataTable<
|
|
|
276
277
|
const [searchTerm, setSearchTerm] = React.useState<string>('');
|
|
277
278
|
const debouncedSearchTerm = useDebounce(searchTerm, 500);
|
|
278
279
|
const queryClient = useQueryClient();
|
|
280
|
+
const extendedListQuery = useExtendedListQuery(listQuery);
|
|
279
281
|
|
|
280
282
|
const sort = sorting?.reduce((acc: any, sort: ColumnSort) => {
|
|
281
283
|
const direction = sort.desc ? 'DESC' : 'ASC';
|
|
@@ -300,7 +302,7 @@ export function PaginatedListDataTable<
|
|
|
300
302
|
|
|
301
303
|
const defaultQueryKey = [
|
|
302
304
|
PaginatedListDataTableKey,
|
|
303
|
-
|
|
305
|
+
extendedListQuery,
|
|
304
306
|
page,
|
|
305
307
|
itemsPerPage,
|
|
306
308
|
sorting,
|
|
@@ -329,14 +331,14 @@ export function PaginatedListDataTable<
|
|
|
329
331
|
} as V;
|
|
330
332
|
|
|
331
333
|
const transformedVariables = transformVariables ? transformVariables(variables) : variables;
|
|
332
|
-
return api.query(
|
|
334
|
+
return api.query(extendedListQuery, transformedVariables);
|
|
333
335
|
},
|
|
334
336
|
queryKey,
|
|
335
337
|
placeholderData: keepPreviousData,
|
|
336
338
|
});
|
|
337
339
|
|
|
338
|
-
const fields = useListQueryFields(
|
|
339
|
-
const paginatedListObjectPath = getObjectPathToPaginatedList(
|
|
340
|
+
const fields = useListQueryFields(extendedListQuery);
|
|
341
|
+
const paginatedListObjectPath = getObjectPathToPaginatedList(extendedListQuery);
|
|
340
342
|
|
|
341
343
|
let listData = data as any;
|
|
342
344
|
for (const path of paginatedListObjectPath) {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { BulkAction } from '@/framework/data-table/data-table-types.js';
|
|
2
|
+
import { DocumentNode } from 'graphql';
|
|
2
3
|
|
|
3
4
|
import { globalRegistry } from '../registry/global-registry.js';
|
|
4
5
|
|
|
5
6
|
globalRegistry.register('bulkActionsRegistry', new Map<string, BulkAction[]>());
|
|
7
|
+
globalRegistry.register('listQueryDocumentRegistry', new Map<string, DocumentNode[]>());
|
|
6
8
|
|
|
7
9
|
export function getBulkActions(pageId: string, blockId = 'list-table'): BulkAction[] {
|
|
8
10
|
const key = createKey(pageId, blockId);
|
|
@@ -16,6 +18,18 @@ export function addBulkAction(pageId: string, blockId: string | undefined, actio
|
|
|
16
18
|
bulkActionsRegistry.set(key, [...existingActions, action]);
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
export function getListQueryDocuments(pageId: string, blockId = 'list-table'): DocumentNode[] {
|
|
22
|
+
const key = createKey(pageId, blockId);
|
|
23
|
+
return globalRegistry.get('listQueryDocumentRegistry').get(key) || [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function addListQueryDocument(pageId: string, blockId: string | undefined, document: DocumentNode) {
|
|
27
|
+
const listQueryDocumentRegistry = globalRegistry.get('listQueryDocumentRegistry');
|
|
28
|
+
const key = createKey(pageId, blockId);
|
|
29
|
+
const existingDocuments = listQueryDocumentRegistry.get(key) || [];
|
|
30
|
+
listQueryDocumentRegistry.set(key, [...existingDocuments, document]);
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
function createKey(pageId: string, blockId: string | undefined): string {
|
|
20
34
|
return `${pageId}__${blockId ?? 'list-table'}`;
|
|
21
35
|
}
|