@antelopejs/dms 0.3.7 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/dist/implementations/dms/guards.d.ts +1 -1
  3. package/dist/implementations/dms/page.js +2 -2
  4. package/dist/implementations/dms/page.js.map +1 -1
  5. package/dist/implementations/dms-base/table-view.d.ts +1 -1
  6. package/dist/pages/settings/users/category.d.ts +1 -1
  7. package/dist/pages/settings/users/members.d.ts +2 -2
  8. package/dist/pages/settings/users/notifications.d.ts +2 -2
  9. package/dist/test/antelope.test.js +16 -5
  10. package/dist/test/antelope.test.js.map +1 -1
  11. package/dist/test/api-endpoint/index.d.ts +5 -0
  12. package/dist/test/api-endpoint/index.js +8 -0
  13. package/dist/test/api-endpoint/index.js.map +1 -0
  14. package/dist/test/attachment-host/native.d.ts +2 -2
  15. package/dist/test/helpers/http.d.ts +1 -0
  16. package/dist/test/helpers/http.js +16 -4
  17. package/dist/test/helpers/http.js.map +1 -1
  18. package/dist/test/unit/implementations/dms/notify-menu-changed.test.js +21 -0
  19. package/dist/test/unit/implementations/dms/notify-menu-changed.test.js.map +1 -1
  20. package/dist/test/unit/interfaces/dms-base/table-view-form-page-urls.test.d.ts +1 -0
  21. package/dist/test/unit/interfaces/dms-base/table-view-form-page-urls.test.js +308 -0
  22. package/dist/test/unit/interfaces/dms-base/table-view-form-page-urls.test.js.map +1 -0
  23. package/dist/test/unit/interfaces/dms-base/table-view-form-redirect.test.d.ts +1 -0
  24. package/dist/test/unit/interfaces/dms-base/table-view-form-redirect.test.js +241 -0
  25. package/dist/test/unit/interfaces/dms-base/table-view-form-redirect.test.js.map +1 -0
  26. package/dist/test/unit/interfaces/dms-base/table-view-route-param-filter-defaults.test.d.ts +1 -0
  27. package/dist/test/unit/interfaces/dms-base/table-view-route-param-filter-defaults.test.js +254 -0
  28. package/dist/test/unit/interfaces/dms-base/table-view-route-param-filter-defaults.test.js.map +1 -0
  29. package/dist/validation/auth.schema.d.ts +18 -18
  30. package/dist/validation/onboarding/register-admin.schema.d.ts +3 -3
  31. package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +49 -20
  32. package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +6 -1
  33. package/frontend-vue/layers/dms-ui/app/build/composables/table/useTable.ts +2 -0
  34. package/frontend-vue/layers/dms-ui/app/build/composables/table/useTableColumns.ts +13 -7
  35. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/registerDefaultFunctions.ts +49 -20
  36. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewConfig.ts +43 -5
  37. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +23 -29
  38. package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +3 -0
  39. package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +35 -25
  40. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +5 -1
  41. package/frontend-vue/pnpm-lock.yaml +4 -4
  42. package/frontend-vue/tests/form-page-url-fill.test.ts +63 -0
  43. package/frontend-vue/tests/form-redirect-placeholders.test.ts +69 -0
  44. package/frontend-vue/tests/route-param-filter-defaults.test.ts +59 -0
  45. package/frontend-vue/tests/route-param-occurrences.test.ts +109 -0
  46. package/package.json +8 -19
  47. package/skills/dms/REFERENCE.md +2 -3
  48. package/skills/dms/SKILL.md +1 -1
  49. package/skills/dms-module/REFERENCE.md +4 -5
  50. package/skills/dms-module/SKILL.md +1 -1
  51. package/frontend-vue/tests/theming-browser.mjs +0 -130
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ var __metadata = (this && this.__metadata) || function (k, v) {
42
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ const interface_api_1 = require("@antelopejs/interface-api");
46
+ const interface_core_1 = require("@antelopejs/interface-core");
47
+ const interface_data_api_1 = require("@antelopejs/interface-data-api");
48
+ const metadata_1 = require("@antelopejs/interface-data-api/metadata");
49
+ const interface_database_decorators_1 = require("@antelopejs/interface-database-decorators");
50
+ const chai_1 = require("chai");
51
+ const pageImpl = __importStar(require("../../../../implementations/dms/page"));
52
+ const permissionsImpl = __importStar(require("../../../../implementations/dms/permissions"));
53
+ const realtimeImpl = __importStar(require("../../../../implementations/dms/realtime"));
54
+ const constants_1 = require("@antelopejs/interface-dms/constants");
55
+ const pageInterface = __importStar(require("@antelopejs/interface-dms/page"));
56
+ const page_1 = require("@antelopejs/interface-dms/page");
57
+ const registry_1 = require("@antelopejs/interface-dms/page/registry");
58
+ const permissionsInterface = __importStar(require("@antelopejs/interface-dms/permissions"));
59
+ const realtimeInterface = __importStar(require("@antelopejs/interface-dms/realtime"));
60
+ const default_types_1 = require("@antelopejs/interface-dms/base/data-types/default-types");
61
+ const table_view_1 = require("@antelopejs/interface-dms/base/table-view");
62
+ const TABLE = "route-param-filter-documents";
63
+ const LOCATION = "/api/route-param-filter-defaults";
64
+ function settle() {
65
+ return new Promise((resolve) => setImmediate(resolve));
66
+ }
67
+ let Document = class Document extends interface_database_decorators_1.Table {
68
+ };
69
+ __decorate([
70
+ (0, interface_database_decorators_1.Field)("string"),
71
+ __metadata("design:type", String)
72
+ ], Document.prototype, "name", void 0);
73
+ __decorate([
74
+ (0, interface_database_decorators_1.Field)("string"),
75
+ __metadata("design:type", String)
76
+ ], Document.prototype, "_instance", void 0);
77
+ Document = __decorate([
78
+ (0, interface_database_decorators_1.RegisterTable)(TABLE, constants_1.CORE_SCHEMA_NAME)
79
+ ], Document);
80
+ class DocumentModel extends (0, interface_database_decorators_1.BasicDataModel)(Document, TABLE) {
81
+ }
82
+ let DocumentAPI = class DocumentAPI extends (0, interface_data_api_1.DataController)(Document, { new: table_view_1.TableViewRoutes.New, edit: table_view_1.TableViewRoutes.Edit }, (0, interface_api_1.Controller)(LOCATION)) {
83
+ };
84
+ __decorate([
85
+ (0, metadata_1.ModelReference)(),
86
+ (0, interface_database_decorators_1.Model)(DocumentModel),
87
+ __metadata("design:type", DocumentModel)
88
+ ], DocumentAPI.prototype, "model", void 0);
89
+ __decorate([
90
+ (0, table_view_1.Column)({ name: "Name", type: new default_types_1.DefaultDataTypes.StringType() }),
91
+ (0, metadata_1.Access)(metadata_1.AccessMode.ReadWrite),
92
+ __metadata("design:type", String)
93
+ ], DocumentAPI.prototype, "name", void 0);
94
+ DocumentAPI = __decorate([
95
+ (0, interface_data_api_1.RegisterDataController)()
96
+ ], DocumentAPI);
97
+ const ROUTE_FILTER = { id: { field: "_instance" } };
98
+ const QUERY_FILTER = { status: { field: "status" } };
99
+ const detailTable = (0, table_view_1.TableView)(DocumentAPI, {
100
+ formContainer: { type: "page" },
101
+ routeParamFilters: ROUTE_FILTER,
102
+ queryParamFilters: QUERY_FILTER,
103
+ realtime: false,
104
+ });
105
+ const namedParamTable = (0, table_view_1.TableView)(DocumentAPI, {
106
+ formContainer: { type: "page" },
107
+ routeParamFilters: { workspace: { field: "_instance" } },
108
+ realtime: false,
109
+ });
110
+ const deepTable = (0, table_view_1.TableView)(DocumentAPI, {
111
+ formContainer: { type: "page" },
112
+ routeParamFilters: ROUTE_FILTER,
113
+ realtime: false,
114
+ });
115
+ const topLevelTable = (0, table_view_1.TableView)(DocumentAPI, {
116
+ formContainer: { type: "page" },
117
+ routeParamFilters: ROUTE_FILTER,
118
+ realtime: false,
119
+ });
120
+ const unfilteredTable = (0, table_view_1.TableView)(DocumentAPI, {
121
+ formContainer: { type: "page" },
122
+ realtime: false,
123
+ });
124
+ const drawerTable = (0, table_view_1.TableView)(DocumentAPI, {
125
+ formContainer: { type: "drawer" },
126
+ routeParamFilters: ROUTE_FILTER,
127
+ realtime: false,
128
+ });
129
+ let DetailPage = class DetailPage extends (0, page_1.RootPageController)("rpf-detail", {
130
+ displayName: "Detail of a workspace",
131
+ urlSlug: "rpf-detail/:id",
132
+ }) {
133
+ static rows = detailTable;
134
+ };
135
+ DetailPage = __decorate([
136
+ (0, page_1.RegisterPage)()
137
+ ], DetailPage);
138
+ let NamedParamPage = class NamedParamPage extends (0, page_1.RootPageController)("rpf-named", {
139
+ displayName: "Detail with a placeholder of its own name",
140
+ urlSlug: "rpf-named/:workspace",
141
+ }) {
142
+ static rows = namedParamTable;
143
+ };
144
+ NamedParamPage = __decorate([
145
+ (0, page_1.RegisterPage)()
146
+ ], NamedParamPage);
147
+ let DeepPage = class DeepPage extends (0, page_1.RootPageController)("rpf-deep", {
148
+ displayName: "Page repeating its own placeholder",
149
+ urlSlug: "rpf-deep/:id/sub/:id",
150
+ }) {
151
+ static rows = deepTable;
152
+ };
153
+ DeepPage = __decorate([
154
+ (0, page_1.RegisterPage)()
155
+ ], DeepPage);
156
+ let TopLevelPage = class TopLevelPage extends (0, page_1.RootPageController)("rpf-top", {
157
+ displayName: "Page without a route parameter",
158
+ }) {
159
+ static rows = topLevelTable;
160
+ };
161
+ TopLevelPage = __decorate([
162
+ (0, page_1.RegisterPage)()
163
+ ], TopLevelPage);
164
+ let UnfilteredPage = class UnfilteredPage extends (0, page_1.RootPageController)("rpf-unfiltered", {
165
+ displayName: "Table view without URL filters",
166
+ }) {
167
+ static rows = unfilteredTable;
168
+ };
169
+ UnfilteredPage = __decorate([
170
+ (0, page_1.RegisterPage)()
171
+ ], UnfilteredPage);
172
+ let DrawerPage = class DrawerPage extends (0, page_1.RootPageController)("rpf-drawer", {
173
+ displayName: "Drawer form container",
174
+ urlSlug: "rpf-drawer/:id",
175
+ }) {
176
+ static rows = drawerTable;
177
+ };
178
+ DrawerPage = __decorate([
179
+ (0, page_1.RegisterPage)()
180
+ ], DrawerPage);
181
+ const pages = [
182
+ DetailPage,
183
+ NamedParamPage,
184
+ DeepPage,
185
+ TopLevelPage,
186
+ UnfilteredPage,
187
+ DrawerPage,
188
+ ];
189
+ async function formPageOptions(formPageFullId) {
190
+ const form = registry_1.pageMetadataByFullId.get(formPageFullId)?.components.form;
191
+ if (!form)
192
+ throw new Error(`No form page registered as ${formPageFullId}`);
193
+ return (await form.serialize()).options;
194
+ }
195
+ async function formComponents(table) {
196
+ return (await table.serialize()).options?.formComponents;
197
+ }
198
+ describe("[unit] interfaces/dms-base — route param filter form defaults", () => {
199
+ before(async () => {
200
+ (0, interface_core_1.ImplementInterface)(permissionsInterface, permissionsImpl);
201
+ (0, interface_core_1.ImplementInterface)(realtimeInterface, realtimeImpl);
202
+ (0, interface_core_1.ImplementInterface)(pageInterface, pageImpl);
203
+ await settle();
204
+ });
205
+ after(() => {
206
+ for (const page of pages) {
207
+ const { pageInfo } = (0, interface_core_1.GetMetadata)(page, page_1.PageMetadata);
208
+ if (pageInfo)
209
+ pageInterface.internal.RegisterPage.unregister(pageInfo);
210
+ }
211
+ });
212
+ it("numbers the page parameter on an edit route repeating its name", async () => {
213
+ const edit = await formPageOptions("rpf-detail.rows.edit");
214
+ (0, chai_1.expect)(edit?.submitDefaults).to.deep.equal({
215
+ status: "{{query.status}}",
216
+ _instance: "{{params.id:1}}",
217
+ });
218
+ (0, chai_1.expect)(edit?.submitUrl).to.equal(`${LOCATION}/edit?id={{params.id}}`);
219
+ });
220
+ it("keeps the bare name on a new route carrying the name once", async () => {
221
+ (0, chai_1.expect)((await formPageOptions("rpf-detail.rows.new"))?.submitDefaults).to.deep.equal({
222
+ status: "{{query.status}}",
223
+ _instance: "{{params.id}}",
224
+ });
225
+ });
226
+ it("gives the view form no defaults", async () => {
227
+ (0, chai_1.expect)((await formPageOptions("rpf-detail.rows.view"))?.submitDefaults).to.equal(undefined);
228
+ });
229
+ it("keeps the bare name when the page parameter is not named id", async () => {
230
+ (0, chai_1.expect)((await formPageOptions("rpf-named.rows.edit"))?.submitDefaults).to.deep.equal({ _instance: "{{params.workspace}}" });
231
+ });
232
+ it("numbers the last occurrence of a name the page slug repeats", async () => {
233
+ (0, chai_1.expect)((await formPageOptions("rpf-deep.rows.new"))?.submitDefaults).to.deep.equal({ _instance: "{{params.id:2}}" });
234
+ (0, chai_1.expect)((await formPageOptions("rpf-deep.rows.edit"))?.submitDefaults).to.deep.equal({ _instance: "{{params.id:2}}" });
235
+ });
236
+ it("leaves the forms of a page without route parameter as they were", async () => {
237
+ for (const kind of ["new", "edit"]) {
238
+ (0, chai_1.expect)((await formPageOptions(`rpf-top.rows.${kind}`))?.submitDefaults).to.deep.equal({ _instance: "{{params.id}}" });
239
+ }
240
+ const unfiltered = await formPageOptions("rpf-unfiltered.rows.edit");
241
+ (0, chai_1.expect)(unfiltered?.submitDefaults).to.equal(undefined);
242
+ });
243
+ it("keeps the bare name on the forms the table view embeds", async () => {
244
+ const embedded = await formComponents(drawerTable);
245
+ (0, chai_1.expect)(embedded?.edit?.options?.submitDefaults).to.deep.equal({
246
+ _instance: "{{params.id}}",
247
+ });
248
+ (0, chai_1.expect)((await formComponents(detailTable))?.edit?.options?.submitDefaults).to.deep.equal({
249
+ status: "{{query.status}}",
250
+ _instance: "{{params.id}}",
251
+ });
252
+ });
253
+ });
254
+ //# sourceMappingURL=table-view-route-param-filter-defaults.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-view-route-param-filter-defaults.test.js","sourceRoot":"","sources":["../../../../../src/test/unit/interfaces/dms-base/table-view-route-param-filter-defaults.test.ts"],"names":[],"mappings":"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"}
@@ -7,29 +7,29 @@ export declare const authSchema: {
7
7
  lang: z.ZodString;
8
8
  token: z.ZodString;
9
9
  }, "strip", z.ZodTypeAny, {
10
- name: string;
11
- lang: string;
12
- email: string;
13
10
  token: string;
14
- password: string;
15
- }, {
16
11
  name: string;
17
- lang: string;
12
+ password: string;
18
13
  email: string;
14
+ lang: string;
15
+ }, {
19
16
  token: string;
17
+ name: string;
20
18
  password: string;
19
+ email: string;
20
+ lang: string;
21
21
  }>;
22
22
  login: z.ZodObject<{
23
23
  email: z.ZodString;
24
24
  password: z.ZodString;
25
25
  keep_login: z.ZodOptional<z.ZodBoolean>;
26
26
  }, "strip", z.ZodTypeAny, {
27
- email: string;
28
27
  password: string;
28
+ email: string;
29
29
  keep_login?: boolean | undefined;
30
30
  }, {
31
- email: string;
32
31
  password: string;
32
+ email: string;
33
33
  keep_login?: boolean | undefined;
34
34
  }>;
35
35
  oauthCallback: z.ZodObject<{
@@ -42,14 +42,14 @@ export declare const authSchema: {
42
42
  code: string;
43
43
  state: string;
44
44
  state_cookie: string;
45
- language?: string | undefined;
46
45
  invite?: string | undefined;
46
+ language?: string | undefined;
47
47
  }, {
48
48
  code: string;
49
49
  state: string;
50
50
  state_cookie: string;
51
- language?: string | undefined;
52
51
  invite?: string | undefined;
52
+ language?: string | undefined;
53
53
  }>;
54
54
  refresh: z.ZodObject<{
55
55
  token: z.ZodString;
@@ -83,37 +83,37 @@ export declare const authSchema: {
83
83
  token: z.ZodString;
84
84
  email: z.ZodString;
85
85
  }, "strip", z.ZodTypeAny, {
86
- email: string;
87
86
  token: string;
88
- }, {
89
87
  email: string;
88
+ }, {
90
89
  token: string;
90
+ email: string;
91
91
  }>;
92
92
  reset: z.ZodObject<{
93
93
  token: z.ZodString;
94
94
  email: z.ZodString;
95
95
  password: z.ZodString;
96
96
  }, "strip", z.ZodTypeAny, {
97
- email: string;
98
97
  token: string;
99
98
  password: string;
100
- }, {
101
99
  email: string;
100
+ }, {
102
101
  token: string;
103
102
  password: string;
103
+ email: string;
104
104
  }>;
105
105
  verify2FA: z.ZodObject<{
106
106
  token: z.ZodString;
107
107
  code: z.ZodString;
108
108
  method: z.ZodEnum<["totp", "email", "backup"]>;
109
109
  }, "strip", z.ZodTypeAny, {
110
- code: string;
111
110
  method: "email" | "totp" | "backup";
112
111
  token: string;
113
- }, {
114
112
  code: string;
113
+ }, {
115
114
  method: "email" | "totp" | "backup";
116
115
  token: string;
116
+ code: string;
117
117
  }>;
118
118
  request2FAEmail: z.ZodObject<{
119
119
  token: z.ZodString;
@@ -133,11 +133,11 @@ export declare const authSchema: {
133
133
  method: z.ZodEnum<["totp", "email"]>;
134
134
  code: z.ZodString;
135
135
  }, "strip", z.ZodTypeAny, {
136
- code: string;
137
136
  method: "email" | "totp";
138
- }, {
139
137
  code: string;
138
+ }, {
140
139
  method: "email" | "totp";
140
+ code: string;
141
141
  }>;
142
142
  regenerateBackup: z.ZodObject<{
143
143
  code: z.ZodString;
@@ -5,18 +5,18 @@ export declare const onboardingRegisterAdminSchema: z.ZodEffects<z.ZodObject<{
5
5
  password: z.ZodString;
6
6
  }, "strip", z.ZodTypeAny, {
7
7
  name: string;
8
- email: string;
9
8
  password: string;
9
+ email: string;
10
10
  }, {
11
11
  name: string;
12
- email: string;
13
12
  password: string;
13
+ email: string;
14
14
  }>, {
15
15
  email: string;
16
16
  name: string;
17
17
  password: string;
18
18
  }, {
19
19
  name: string;
20
- email: string;
21
20
  password: string;
21
+ email: string;
22
22
  }>;
@@ -37,36 +37,65 @@ interface MatchRouteResult {
37
37
  params: Record<string, string>;
38
38
  }
39
39
 
40
- function extractRouteParams(
40
+ // A route pattern can carry the same placeholder name twice: a page-mode form
41
+ // sub-page under a page whose own slug is `:id` resolves to
42
+ // `/workspaces/:id/invoiceTable/:id/edit`. The bare name holds the LAST
43
+ // occurrence, so `params.id` keeps meaning the row id the way it does on every
44
+ // single-placeholder form route, and each occurrence of a repeated name is
45
+ // exposed as well under `<name>:<n>`, numbered from 1 in pattern order, so the
46
+ // id of the carrying page stays reachable as `id:1`. A name that occurs only
47
+ // once gets no indexed key: those bags are exactly what they were.
48
+ const OCCURRENCE_KEY_SEPARATOR = ":";
49
+
50
+ const occurrenceKey = (name: string, occurrence: number): string =>
51
+ `${name}${OCCURRENCE_KEY_SEPARATOR}${occurrence}`;
52
+
53
+ function countByName(names: string[]): Record<string, number> {
54
+ const totals: Record<string, number> = {};
55
+ for (const name of names) {
56
+ totals[name] = (totals[name] ?? 0) + 1;
57
+ }
58
+ return totals;
59
+ }
60
+
61
+ function buildParamBag(
62
+ names: string[],
63
+ values: string[],
64
+ ): Record<string, string> {
65
+ const totals = countByName(names);
66
+ const seen: Record<string, number> = {};
67
+ const params: Record<string, string> = {};
68
+
69
+ names.forEach((name, index) => {
70
+ const value = values[index]!;
71
+ const occurrence = (seen[name] ?? 0) + 1;
72
+ seen[name] = occurrence;
73
+ params[name] = value;
74
+ if (totals[name]! > 1) {
75
+ params[occurrenceKey(name, occurrence)] = value;
76
+ }
77
+ });
78
+
79
+ return params;
80
+ }
81
+
82
+ export function extractRouteParams(
41
83
  pattern: string,
42
84
  path: string,
43
85
  ): Record<string, string> | null {
44
- const regexPattern = pattern
45
- .split("/")
46
- .map((segment) => {
47
- if (segment.startsWith(":")) {
48
- return "([^/]+)";
49
- }
50
- return segment;
51
- })
86
+ const segments = pattern.split("/");
87
+ const regexPattern = segments
88
+ .map((segment) => (segment.startsWith(":") ? "([^/]+)" : segment))
52
89
  .join("/");
53
90
 
54
- const regex = new RegExp(`^${regexPattern}$`);
55
- const match = path.match(regex);
56
-
91
+ const match = path.match(new RegExp(`^${regexPattern}$`));
57
92
  if (!match) return null;
58
93
 
59
- const paramNames = pattern
60
- .split("/")
94
+ const paramNames = segments
61
95
  .filter((segment) => segment.startsWith(":"))
62
96
  .map((segment) => segment.substring(1));
63
97
 
64
- const params: Record<string, string> = {};
65
- paramNames.forEach((name, index) => {
66
- params[name] = match[index + 1]!;
67
- });
68
-
69
- return params;
98
+ return buildParamBag(paramNames, match.slice(1));
70
99
  }
71
100
 
72
101
  export const useSiteLayout = () => {
@@ -26,7 +26,10 @@ import type {
26
26
  CustomButton,
27
27
  TableViewDisplayCapabilities,
28
28
  } from "../../../composables/table-view/types";
29
- import type { FormContainer } from "../../composables/table-view/useTableViewConfig";
29
+ import type {
30
+ FormContainer,
31
+ FormPageUrls,
32
+ } from "../../composables/table-view/useTableViewConfig";
30
33
  import type { CustomRowAction } from "../../../types/row-action";
31
34
  import type { RowActionConfig } from "#dms-core/app/types/row-action";
32
35
  import type { TableTabItem } from "./Tabs.vue";
@@ -97,6 +100,8 @@ export interface TableProps<T> {
97
100
  onCustomRowAction?: (action: CustomRowAction, rowData: T) => void;
98
101
  componentId?: string;
99
102
  formContainer?: FormContainer;
103
+ formPages?: FormPageUrls;
104
+ routeParams?: Record<string, string>;
100
105
  defaultSort?: { field: string; desc?: boolean };
101
106
  initialColumnVisibility?: VisibilityState;
102
107
  presenceByRow?: TableRowPresenceMap;
@@ -92,6 +92,8 @@ export const useTable = <T extends Data>(props: UseTableProps<T>) => {
92
92
  ui: props.ui,
93
93
  componentId: props.tableProps.componentId,
94
94
  formContainer: props.tableProps.formContainer,
95
+ formPages: props.tableProps.formPages,
96
+ routeParams: props.tableProps.routeParams,
95
97
  onCustomRowAction: props.tableProps.onCustomRowAction,
96
98
  });
97
99
 
@@ -28,7 +28,11 @@ import {
28
28
  FormContainerType,
29
29
  DEFAULT_FORM_CONTAINER_TYPE,
30
30
  } from "../table-view/types";
31
- import type { FormContainer } from "../table-view/useTableViewConfig";
31
+ import {
32
+ fillFormPageUrl,
33
+ type FormContainer,
34
+ type FormPageUrls,
35
+ } from "../table-view/useTableViewConfig";
32
36
  import type { DmsAppConfig } from "#dms-core/shared/types/app-config";
33
37
 
34
38
  const SORT_DIRECTION_ICON: Record<SortDirection, string> = {
@@ -84,6 +88,8 @@ interface ColumnConfig<T> {
84
88
  ui: ComputedRef<TableStyleSlots>;
85
89
  componentId?: string;
86
90
  formContainer?: FormContainer;
91
+ formPages?: FormPageUrls;
92
+ routeParams?: Record<string, string>;
87
93
  onCustomRowAction?: (action: CustomRowAction, rowData: T) => void;
88
94
  }
89
95
 
@@ -353,13 +359,13 @@ export const useTableColumns = <T extends Data>(config: ColumnConfig<T>) => {
353
359
 
354
360
  const containerType =
355
361
  config.formContainer?.type ?? DEFAULT_FORM_CONTAINER_TYPE;
356
- if (containerType === FormContainerType.page) {
362
+ const viewPageUrl =
363
+ containerType === FormContainerType.page
364
+ ? config.formPages?.view
365
+ : undefined;
366
+ if (viewPageUrl) {
357
367
  const itemId = get(rowData, config.rowIdKey);
358
- const viewSlug =
359
- config.formContainer?.pages?.view?.urlSlug ||
360
- `${currentRoute.path}/${itemId}/view`;
361
- const viewUrl = viewSlug.replace(":id", itemId);
362
- itemUrl = `${window.location.origin}${viewUrl}`;
368
+ itemUrl = `${window.location.origin}${fillFormPageUrl(viewPageUrl, config.routeParams, itemId)}`;
363
369
  } else {
364
370
  const updatedQuery = {
365
371
  ...currentRoute.query,
@@ -1,32 +1,61 @@
1
1
  import type { LocationQueryValue } from "#dms/frontend-module";
2
+ import { replaceUrlVariables } from "../../../composables/form/useForm";
3
+
4
+ type RouteQuery = Record<string, LocationQueryValue | LocationQueryValue[]>;
5
+
6
+ export interface FormSuccessParams {
7
+ url: string;
8
+ preserveQuery?: string[];
9
+ }
10
+
11
+ export interface FormSuccessRoute {
12
+ query: RouteQuery;
13
+ params?: Record<string, string>;
14
+ }
15
+
16
+ /**
17
+ * Where a page-mode form sends the user on submit: the page carrying the table
18
+ * view. Its URL holds a `{{params.X}}` token per placeholder of that page's
19
+ * slug, resolved against the parameters of the form route being left, the way
20
+ * `redirectOnSuccess` is.
21
+ */
22
+ export function resolveFormSuccessTarget(
23
+ { url, preserveQuery }: FormSuccessParams,
24
+ route: FormSuccessRoute,
25
+ ): { path: string; query?: RouteQuery } {
26
+ const path = replaceUrlVariables(url, {
27
+ routeParams: route.params,
28
+ routeQuery: route.query,
29
+ });
30
+
31
+ // Restore the filtered table view: carry back the query params the form
32
+ // was opened with (those declared in `queryParamFilters`).
33
+ const query: RouteQuery = {};
34
+ for (const key of preserveQuery ?? []) {
35
+ if (route.query[key] !== undefined) {
36
+ query[key] = route.query[key];
37
+ }
38
+ }
39
+
40
+ return { path, ...(Object.keys(query).length > 0 ? { query } : {}) };
41
+ }
2
42
 
3
43
  export function registerDefaultFunctions() {
4
44
  const { registerFunction } = useDefinedFunctions();
5
45
  const route = useDmsRoute();
46
+ const { findMatchingRoute } = useSiteLayout();
6
47
 
7
48
  registerFunction(
8
49
  TableViewFunctions.CUSTOM_PAGE_FORM_SUCCESS,
9
50
  (event: CustomEvent) => {
10
- const { url, preserveQuery } = (
11
- event as CustomEvent & {
12
- params: { url: string; preserveQuery?: string[] };
13
- }
14
- ).params;
15
-
16
- // Restore the filtered table view: carry back the query params the form
17
- // was opened with (those declared in `queryParamFilters`).
18
- const query: Record<string, LocationQueryValue | LocationQueryValue[]> =
19
- {};
20
- for (const key of preserveQuery ?? []) {
21
- if (route.query[key] !== undefined) {
22
- query[key] = route.query[key];
23
- }
24
- }
25
-
26
- navigateDms({
27
- path: url,
28
- ...(Object.keys(query).length > 0 ? { query } : {}),
29
- });
51
+ const { params } = event as CustomEvent & { params: FormSuccessParams };
52
+
53
+ navigateDms(
54
+ resolveFormSuccessTarget(params, {
55
+ query: route.query,
56
+ params: findMatchingRoute(route.path)?.params,
57
+ }),
58
+ );
30
59
  },
31
60
  );
32
61
  }