@sealcode/sealgen 0.15.35 → 0.15.37

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 (85) hide show
  1. package/.xunit +59 -57
  2. package/@types/src/forms/controls/form-control.d.ts +2 -1
  3. package/@types/src/forms/controls/table.d.ts +24 -5
  4. package/@types/src/forms/fields/table.d.ts +4 -2
  5. package/@types/src/templates/form.d.ts +5 -0
  6. package/@types/src/templates/shared/collection-create-form.d.ts +0 -2
  7. package/@types/src/templates/shared/handlers/boolean.d.ts +2 -0
  8. package/@types/src/templates/shared/handlers/date.d.ts +2 -0
  9. package/@types/src/templates/shared/handlers/deep-reverse-single-reference.d.ts +2 -0
  10. package/@types/src/templates/shared/handlers/default.d.ts +2 -0
  11. package/@types/src/templates/shared/handlers/enum.d.ts +2 -0
  12. package/@types/src/templates/shared/handlers/file.d.ts +2 -0
  13. package/@types/src/templates/shared/handlers/image.d.ts +2 -0
  14. package/@types/src/templates/shared/handlers/int.d.ts +2 -0
  15. package/@types/src/templates/shared/handlers/jdd.d.ts +2 -0
  16. package/@types/src/templates/shared/handlers/single-reference.d.ts +2 -0
  17. package/@types/src/templates/shared/handlers/structured-array.d.ts +2 -0
  18. package/@types/src/templates/shared/shared-crud-form-fields.d.ts +29 -6
  19. package/@types/src/utils/extract-fields-from-collection.d.ts +12 -2
  20. package/@types/src/utils/with-fallback.d.ts +1 -1
  21. package/lib/add-crud.js +5 -5
  22. package/lib/cli.js +2 -0
  23. package/lib/forms/controls/form-control.js +2 -2
  24. package/lib/forms/controls/multiple-photos.js +2 -1
  25. package/lib/forms/controls/single-reference.test.js +1 -1
  26. package/lib/forms/controls/table.js +114 -12
  27. package/lib/forms/fields/boolean.js +1 -1
  28. package/lib/forms/fields/field.js +4 -1
  29. package/lib/forms/fields/table.js +8 -3
  30. package/lib/forms/form.js +7 -6
  31. package/lib/page/default-list-filters.js +1 -0
  32. package/lib/templates/form.js +25 -16
  33. package/lib/templates/list.js +6 -167
  34. package/lib/templates/shared/collection-create-form.js +23 -83
  35. package/lib/templates/shared/collection-edit-form.js +53 -96
  36. package/lib/templates/shared/collection-list.js +21 -6
  37. package/lib/templates/shared/confirm-item-delete.js +7 -5
  38. package/lib/templates/shared/handlers/boolean.js +14 -0
  39. package/lib/templates/shared/handlers/date.js +8 -0
  40. package/lib/templates/shared/handlers/deep-reverse-single-reference.js +209 -0
  41. package/lib/templates/shared/handlers/default.js +50 -0
  42. package/lib/templates/shared/handlers/enum.js +19 -0
  43. package/lib/templates/shared/handlers/file.js +18 -0
  44. package/lib/templates/shared/handlers/image.js +32 -0
  45. package/lib/templates/shared/handlers/int.js +11 -0
  46. package/lib/templates/shared/handlers/jdd.js +10 -0
  47. package/lib/templates/shared/handlers/single-reference.js +12 -0
  48. package/lib/templates/shared/handlers/structured-array.js +33 -0
  49. package/lib/templates/shared/item-delete.js +5 -3
  50. package/lib/templates/shared/shared-crud-form-fields.js +72 -82
  51. package/lib/utils/extract-fields-from-collection.js +31 -5
  52. package/package.json +2 -1
  53. package/src/add-crud.ts +153 -0
  54. package/src/cli.ts +2 -0
  55. package/src/forms/controls/form-control.ts +3 -2
  56. package/src/forms/controls/multiple-photos.ts +1 -0
  57. package/src/forms/controls/single-reference.test.ts +1 -1
  58. package/src/forms/controls/table.ts +168 -31
  59. package/src/forms/fields/boolean.ts +1 -1
  60. package/src/forms/fields/field.ts +4 -1
  61. package/src/forms/fields/table.ts +25 -6
  62. package/src/forms/form.ts +13 -7
  63. package/src/page/default-list-filters.ts +1 -0
  64. package/src/templates/form.ts +24 -15
  65. package/src/templates/list.ts +6 -174
  66. package/src/templates/shared/collection-create-form.ts +121 -0
  67. package/src/templates/shared/collection-edit-form.ts +196 -0
  68. package/src/templates/shared/collection-list.ts +252 -0
  69. package/src/templates/shared/confirm-item-delete.ts +78 -0
  70. package/src/templates/shared/handlers/boolean.ts +18 -0
  71. package/src/templates/shared/handlers/date.ts +12 -0
  72. package/src/templates/shared/handlers/deep-reverse-single-reference.ts +244 -0
  73. package/src/templates/shared/handlers/default.ts +58 -0
  74. package/src/templates/shared/handlers/enum.ts +23 -0
  75. package/src/templates/shared/handlers/file.ts +22 -0
  76. package/src/templates/shared/handlers/image.ts +45 -0
  77. package/src/templates/shared/handlers/int.ts +15 -0
  78. package/src/templates/shared/handlers/jdd.ts +14 -0
  79. package/src/templates/shared/handlers/single-reference.ts +16 -0
  80. package/src/templates/shared/handlers/structured-array.ts +42 -0
  81. package/src/templates/shared/item-delete.ts +45 -0
  82. package/src/templates/shared/shared-crud-form-fields.ts +179 -0
  83. package/src/utils/extract-fields-from-collection.ts +45 -6
  84. package/src/utils/with-fallback.ts +1 -1
  85. package/src/utils/write-file.ts +8 -0
@@ -1,30 +1,55 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
+ /* eslint-disable @typescript-eslint/no-explicit-any */
4
+ import Router from "@koa/router";
5
+ import { hasShape, is, predicates } from "@sealcode/ts-predicates";
6
+ import { Context } from "koa";
7
+ import qs from "qs";
1
8
  import { FlatTemplatable } from "tempstream";
2
9
  import { FormField } from "../fields/field.js";
3
10
  import { Table as TableField } from "../fields/table.js";
11
+ import { FormReaction } from "../form-types.js";
12
+ import { Form } from "../form.js";
4
13
  import { FormControl, FormControlContext } from "./form-control.js";
5
14
 
15
+ export type TableControlOptions<F extends Record<string, FormField>> = {
16
+ render_fields: {
17
+ [field_name in keyof F]: (
18
+ fctx: FormControlContext,
19
+ name: string,
20
+ value: string
21
+ ) => string;
22
+ };
23
+ allow_removing?: boolean;
24
+ label_add?: string;
25
+ label: string;
26
+ label_remove?: string;
27
+ } & (
28
+ | { allow_adding: false }
29
+ | {
30
+ allow_adding: true;
31
+ make_new_row: (
32
+ ctx: Context
33
+ ) => Promise<{ [field_name in keyof F]: unknown }>;
34
+ }
35
+ );
36
+
6
37
  export class Table<F extends Record<string, FormField>> extends FormControl {
7
38
  role = <const>"input";
8
39
  constructor(
9
40
  public table_field: TableField<F>,
10
- public render_fields: {
11
- [field_name in keyof F]: (
12
- fctx: FormControlContext,
13
- name: string,
14
- value: string
15
- ) => string;
16
- }
41
+ public options: TableControlOptions<F>
17
42
  ) {
18
43
  super();
19
44
  }
20
45
 
21
46
  async render(fctx: FormControlContext): Promise<FlatTemplatable> {
22
- const { parsed: rows } = await this.table_field.getValue(
47
+ let { parsed: rows } = await this.table_field.getValue(
23
48
  fctx.ctx,
24
49
  fctx.data
25
50
  );
26
51
  if (!rows) {
27
- return "";
52
+ rows = [];
28
53
  }
29
54
  return /* HTML */ `<div
30
55
  class="${[
@@ -45,34 +70,146 @@ export class Table<F extends Record<string, FormField>> extends FormControl {
45
70
  : []),
46
71
  ].join(" ")}"
47
72
  >
73
+ <label>${this.options.label}</label>
48
74
  <div class="table__wrapper">
49
75
  <table>
50
76
  <tbody>
51
- <tr>
52
- ${rows
53
- ?.map((row, row_index) => {
54
- return /* HTML */ `<tr>
55
- ${Object.entries(
56
- this.table_field.columns
57
- )
58
- .map(([key]) => {
59
- return `<td>${this.render_fields[
60
- key
61
- ](
62
- fctx,
63
- `${this.table_field.name}[${row_index}][${key}]`,
64
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
65
- row[key] as string
66
- )}</td>`;
67
- })
68
- .join("")}
69
- </tr>`;
70
- })
71
- .join("")}
72
- </tr>
77
+ ${rows
78
+ ?.map((row, row_index) => {
79
+ return /* HTML */ `<tr>
80
+ ${Object.entries(this.table_field.columns)
81
+ .map(([key]) => {
82
+ return `<td>${this.options.render_fields[
83
+ key
84
+ ](
85
+ fctx,
86
+ `${this.table_field.name}[${row_index}][${key}]`,
87
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
88
+ row[key] as string
89
+ )}</td>`;
90
+ })
91
+ .join("")}
92
+ ${this.options.allow_removing
93
+ ? /* HTML */ `<td>
94
+ <input
95
+ type="submit"
96
+ value="${this.options
97
+ .label_remove ||
98
+ "remove"}"
99
+ form="${fctx.form_id}"
100
+ formnovalidate
101
+ formaction="${this.getActionURL(
102
+ this.table_field.name,
103
+ {
104
+ remove: {
105
+ index: row_index,
106
+ },
107
+ }
108
+ )}"
109
+ />
110
+ </td>`
111
+ : ""}
112
+ </tr>`;
113
+ })
114
+ .join("")}
73
115
  </tbody>
74
116
  </table>
117
+ ${this.options.allow_adding
118
+ ? /* HTML */ `<input
119
+ type="submit"
120
+ value="${this.options.label_add || "add"}"
121
+ form="${fctx.form_id}"
122
+ formnovalidate
123
+ formaction="${this.getActionURL(
124
+ this.table_field.name,
125
+ {
126
+ insert: { index: rows.length, value: {} },
127
+ }
128
+ )}"
129
+ />`
130
+ : ""}
75
131
  </div>
76
132
  </div>`;
77
133
  }
134
+
135
+ getFrameID() {
136
+ return `array-frame-${this.table_field.name}`;
137
+ }
138
+
139
+ getActionURL(field_name: string, action: Record<string, unknown>) {
140
+ return `./?${qs.stringify({
141
+ action,
142
+ field_name,
143
+ })}`;
144
+ }
145
+
146
+ mount(router: Router, form: Form<any, any>) {
147
+ router.post("/", async (ctx, next) => {
148
+ const action = ctx.$body.action;
149
+ const field_name = ctx.$body.field_name;
150
+ if (
151
+ !is(action, predicates.object) ||
152
+ !is(field_name, predicates.string)
153
+ ) {
154
+ await next();
155
+ return;
156
+ }
157
+ if (
158
+ this.options.allow_adding &&
159
+ hasShape(
160
+ {
161
+ insert: predicates.shape({ index: predicates.string }),
162
+ },
163
+ action
164
+ )
165
+ ) {
166
+ if (field_name !== this.table_field.name) {
167
+ await next();
168
+ return;
169
+ }
170
+ if (!ctx.$body[this.table_field.name]) {
171
+ ctx.$body[this.table_field.name] = {};
172
+ }
173
+ (ctx.$body[this.table_field.name] as any)[action.insert.index] =
174
+ await this.options.make_new_row(ctx);
175
+ }
176
+ if (
177
+ hasShape(
178
+ {
179
+ remove: predicates.shape({ index: predicates.string }),
180
+ },
181
+ action
182
+ )
183
+ ) {
184
+ if (field_name !== this.table_field.name) {
185
+ await next();
186
+ return;
187
+ }
188
+ if (!ctx.$body[this.table_field.name]) {
189
+ ctx.$body[this.table_field.name] = {};
190
+ }
191
+ (ctx.$body[this.table_field.name] as any) = Object.fromEntries(
192
+ Object.entries(
193
+ Object.values(
194
+ ctx.$body[this.table_field.name] as any
195
+ ).filter(
196
+ (_, index) => index != parseInt(action.remove.index)
197
+ )
198
+ )
199
+ );
200
+ }
201
+ ctx.override_reaction = {
202
+ action: "stay",
203
+ content: form.render(
204
+ ctx,
205
+ {
206
+ raw_values: await form.extractRawValues(ctx),
207
+ messages: [],
208
+ },
209
+ false
210
+ ),
211
+ } as FormReaction;
212
+ await next();
213
+ });
214
+ }
78
215
  }
@@ -46,7 +46,7 @@ export class Boolean<Required extends boolean> extends FormField<
46
46
  parsed_value: false,
47
47
  error: null,
48
48
  };
49
- } else if (value === null) {
49
+ } else if (value === null || value === "") {
50
50
  return { parsable: true, parsed_value: false, error: null };
51
51
  } else {
52
52
  return {
@@ -77,7 +77,10 @@ export abstract class FormField<
77
77
  ),
78
78
  };
79
79
  } else {
80
- return { valid: true, message: "Empty, but not required" };
80
+ return {
81
+ valid: true,
82
+ message: "",
83
+ };
81
84
  }
82
85
  }
83
86
  return this.isValueValid(ctx, value);
@@ -3,16 +3,30 @@ import { Context } from "koa";
3
3
  import { FormDataValue } from "../form-types.js";
4
4
  import { FieldParseResult, FormField } from "./field.js";
5
5
 
6
+ type ExtractFormFieldParsed<Field extends FormField> = Field extends FormField<
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ true,
9
+ infer T
10
+ >
11
+ ? NonNullable<T>
12
+ : Field extends FormField<
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ true,
15
+ infer T
16
+ >
17
+ ? T
18
+ : never;
19
+
6
20
  export type TableFieldParsed<F extends Record<string, FormField>> = {
7
- [field_name in keyof F]: unknown;
21
+ [field_name in keyof F]: ExtractFormFieldParsed<F[field_name]>;
8
22
  }[];
9
23
 
10
24
  export class Table<F extends Record<string, FormField>> extends FormField<
11
- true,
25
+ false,
12
26
  TableFieldParsed<F>
13
27
  > {
14
28
  constructor(public columns: F) {
15
- super(true);
29
+ super(false);
16
30
  }
17
31
 
18
32
  getEmptyValue() {
@@ -36,6 +50,7 @@ export class Table<F extends Record<string, FormField>> extends FormField<
36
50
  const row_data = raw_value[row_number];
37
51
  const parsed_row: Record<string, unknown> = {};
38
52
  if (!is(row_data, predicates.object)) {
53
+ console.log("table.ts<fields>:55", this.name);
39
54
  throw new Error(`Expected object, got ${String(row_data)}`);
40
55
  }
41
56
  for (const key in row_data) {
@@ -47,16 +62,20 @@ export class Table<F extends Record<string, FormField>> extends FormField<
47
62
  parsed_value: null,
48
63
  };
49
64
  }
50
- // eslint-disable-next-line no-await-in-loop, @typescript-eslint/no-explicit-any, @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
65
+ // eslint-disable-next-line no-await-in-loop
51
66
  const parse_result = await field.parse(
52
67
  ctx,
68
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
53
69
  (row_data as any)[key]
54
70
  );
55
71
  if (!parse_result.parsable) {
56
- console.debug("table.ts:51");
72
+ const message = `Invalid value for row ${row_number}.${key}: ${parse_result.error}`;
73
+ console.warn(
74
+ `Invalid value for row ${row_number}.${key}: ${parse_result.error}`
75
+ );
57
76
  return {
58
77
  parsable: false,
59
- error: `Invalid value for row ${row_number}.${key}: ${parse_result.error}`,
78
+ error: message,
60
79
  parsed_value: null,
61
80
  };
62
81
  }
package/src/forms/form.ts CHANGED
@@ -241,6 +241,15 @@ export abstract class Form<
241
241
  }
242
242
  await next();
243
243
  });
244
+
245
+ // for use with other subroutes or middlewares that individual controls
246
+ // might register
247
+ const subrouter = new Router();
248
+ for (const control of this.controls) {
249
+ control.mount(subrouter, this);
250
+ }
251
+ router.use(path, subrouter.routes(), subrouter.allowedMethods());
252
+
244
253
  router.get(path, async (ctx) => {
245
254
  ctx.type = "html";
246
255
  ctx.body = await this.render(
@@ -253,7 +262,10 @@ export abstract class Form<
253
262
  );
254
263
  });
255
264
  router.post(path, async (ctx) => {
256
- const reaction = await this.handlePost(ctx);
265
+ console.log("REGULAR FORM HANDLE");
266
+ const reaction =
267
+ (ctx.override_reaction as FormReaction | undefined) ||
268
+ (await this.handlePost(ctx));
257
269
  if (reaction.action == "stay") {
258
270
  ctx.status = 422;
259
271
  ctx.body = reaction.content;
@@ -262,12 +274,6 @@ export abstract class Form<
262
274
  ctx.redirect(reaction.url);
263
275
  }
264
276
  });
265
- const subrouter = new Router(); // for use with other subroutes that individual controls might register
266
- for (const control of this.controls) {
267
- control.mount(subrouter);
268
- }
269
-
270
- router.use(path, subrouter.routes(), subrouter.allowedMethods());
271
277
  }
272
278
 
273
279
  async extractRawValues(
@@ -45,6 +45,7 @@ export const SimpleTextFilter = {
45
45
  <input
46
46
  type="text"
47
47
  name="filter[${field.name}]"
48
+ autocomplete="off"
48
49
  ${value ? `value="${value}"` : value}
49
50
  />
50
51
  </label>`;
@@ -3,7 +3,9 @@ import { curryImportPath } from "../utils/import-path.js";
3
3
  import { getPrettierConfig } from "../utils/prettier.js";
4
4
 
5
5
  const defaults = {
6
- postimport: ``,
6
+ postimport: `
7
+ import { Fields } from "@sealcode/sealgen";
8
+ import type {FormDataValue} from "@sealcode/sealgen";`,
7
9
  fields: `
8
10
  email: new Fields.CollectionField(true, Users.fields.email),
9
11
  password: new Fields.SimpleFormField(true),`,
@@ -27,6 +29,19 @@ const defaults = {
27
29
  return { valid: true, error: "" };
28
30
  }
29
31
  }`,
32
+ onsubmit: ` async onSubmit() {
33
+ //noop
34
+ return;
35
+ }`,
36
+ can_access: ` // eslint-disable-next-line @typescript-eslint/no-unused-vars
37
+ async canAccess(_: Context) {
38
+ return { canAccess: true, message: "" };
39
+ }`,
40
+ render: `async render(ctx: Context, data: FormData, show_field_errors: boolean) {
41
+ return html(ctx, "Form", await super.render(ctx, data, show_field_errors));
42
+ }`,
43
+ get_initial_values: "",
44
+ other_methods: "",
30
45
  };
31
46
 
32
47
  export async function formTemplate(
@@ -37,10 +52,9 @@ export async function formTemplate(
37
52
  const params = { ...defaults, ..._params };
38
53
  const rel = curryImportPath(newfilefullpath);
39
54
  const content = `import type { Context } from "koa";
40
- import type { FormData, FormDataValue } from "@sealcode/sealgen";
41
- import { Form, Fields, Controls, fieldsToShape } from "@sealcode/sealgen";
55
+ import type { FormData } from "@sealcode/sealgen";
56
+ import { Form, Controls, fieldsToShape } from "@sealcode/sealgen";
42
57
  import html from "${rel("src/back/html.js")}";
43
- import { Users } from "${rel("src/back/collections/collections.js")}";
44
58
 
45
59
  ${params.postimport}
46
60
 
@@ -60,20 +74,15 @@ export default new (class ${action_name}Form extends Form<typeof fields, void> {
60
74
  ${params.controls}
61
75
  ];
62
76
 
77
+ ${params.other_methods}
63
78
 
64
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
65
- async canAccess(_: Context) {
66
- return { canAccess: true, message: "" };
67
- }
79
+ ${params.get_initial_values}
68
80
 
69
- async onSubmit() {
70
- //noop
71
- return;
72
- }
81
+ ${params.onsubmit}
82
+
83
+ ${params.can_access}
73
84
 
74
- async render(ctx: Context, data: FormData, show_field_errors: boolean) {
75
- return html(ctx, "${action_name}", await super.render(ctx, data, show_field_errors));
76
- }
85
+ ${params.render}
77
86
  })();
78
87
  `;
79
88
  return Prettier.format(content, await getPrettierConfig());
@@ -1,13 +1,8 @@
1
1
  import _locreq from "locreq";
2
- import { curryImportPath } from "../utils/import-path.js";
3
2
  import prompts from "prompts";
4
3
  import { listCollections } from "../utils/list-collections.js";
5
- import { extractCollectionClassname } from "../generate-collections.js";
6
- import extract_fields_from_collection from "../utils/extract-fields-from-collection.js";
7
- import { DefaultListFilters } from "../page/default-list-filters.js";
8
4
  import { formatWithPrettier } from "../utils/prettier.js";
9
- import { toKebabCase } from "js-convert-case";
10
- const target_locreq = _locreq(process.cwd());
5
+ import { collectionListTemplate } from "./shared/collection-list.js";
11
6
 
12
7
  export const listTemplate = async (
13
8
  action_name: string,
@@ -25,174 +20,11 @@ export const listTemplate = async (
25
20
  })),
26
21
  });
27
22
  const collection_name = response.collection as string;
28
- const [uppercase_collection, collection_fields] = await Promise.all([
29
- extractCollectionClassname(
30
- target_locreq.resolve(
31
- "src/back/collections/" + collection_name + ".ts"
32
- )
33
- ),
34
- extract_fields_from_collection(collection_name),
35
- ]);
36
- const importPath = curryImportPath(newfilefullpath);
37
- const result = `import type { Context } from "koa";
38
- import type { CollectionItem, ItemListResult } from "sealious";
39
- import type { FlatTemplatable } from "tempstream";
40
- import { TempstreamJSX } from "tempstream";
41
- import { ${uppercase_collection} } from "${importPath(
42
- target_locreq.resolve("src/back/collections/collections.ts")
43
- )}";
44
- import html from "${importPath(target_locreq.resolve("src/back/html.ts"))}";
45
- import type { ListFilterRender } from "@sealcode/sealgen";
46
- import {
47
- SealiousItemListPage,
48
- BaseListPageFields,
49
- DefaultListFilters,
50
- } from "@sealcode/sealgen";
51
- import qs from "qs";
52
- import type { FilePointer } from "@sealcode/file-manager";
53
- import { imageRouter } from "${importPath(
54
- target_locreq.resolve("src/back/image-router.ts")
55
- )}";
56
23
 
57
- export const actionName = "${action_name}";
58
-
59
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
60
- const filterFields = [
61
- ${collection_fields
62
- .map(
63
- (field) =>
64
- ` { field: "${field.name}", ...${
65
- DefaultListFilters[field.type]
66
- ? `DefaultListFilters["${field.type}"]`
67
- : `DefaultListFilters.fallback`
68
- } }`
69
- )
70
- .join(",\n")}
71
- ] as {
72
- field: keyof (typeof ${uppercase_collection})["fields"];
73
- render?: ListFilterRender;
74
- prepareValue?: (filter_value: unknown)=>unknown; // set this function to change what filter value is passed to Sealious
75
- }[];
76
-
77
- const displayFields = [
78
- ${collection_fields
79
- .map((field) => {
80
- if (field.type == "boolean") {
81
- return ` {
82
- field: "${field.name}",
83
- label: "${field.name}",
84
- format: (v: boolean) => (v ? "YES" : "NO"),
85
- }`;
86
- }
87
- if (field.type == "datetime") {
88
- return ` {
89
- field: "${field.name}",
90
- label: "${field.name}",
91
- format: (v: number) => {
92
- const d = new Date();
93
- d.setTime(v);
94
- return d.toString().split(" ").slice(1, 5).join(" ");
95
- },
96
- }`;
97
- }
98
- if (field.type == "image") {
99
- return `{
100
- field: "${field.name}",
101
- label: "${field.name}",
102
- format: async (value: FilePointer) => {
103
- return imageRouter.image(await value.getPath(), {
104
- container: { width: 45, height: 45 },
105
- alt: "",
106
- });
107
- },
108
- }`;
109
- }
110
- return ` { field: "${field.name}", label: "${field.name}" }`;
111
- })
112
- .join(",\n")}
113
- ] as {
114
- field: string;
115
- label: string;
116
- format?: (value: unknown, item: CollectionItem<typeof Opinions>) => FlatTemplatable;
117
- }[];
118
-
119
- export default new (class ${action_name}Page extends SealiousItemListPage<
120
- typeof ${uppercase_collection},
121
- typeof BaseListPageFields
122
- > {
123
- fields = BaseListPageFields;
124
-
125
- async renderFilters(ctx: Context): Promise<FlatTemplatable> {
126
- const query_params = qs.parse(ctx.search.slice(1));
127
- query_params.page = "1";
128
- const filter_values = await super.getFilterValues(ctx);
129
- return <form>
130
- {Object.entries(query_params).map(([key, value]) => {
131
- if (key == "filter") {
132
- return "";
133
- }
134
- // this is necessary to not lose any query params when the user changes the filter values
135
- return <input type="hidden" name={key} value={value}/>;
136
- })}
137
- {filterFields.map(({ field, render }) => {
138
- if (!render) {
139
- render = DefaultListFilters.fallback;
140
- }
141
- return (
142
- render(filter_values[field] || "", this.collection.fields[field]) ||
143
- ""
144
- );
145
- })}
146
- <input type="submit" />
147
- </form>;
148
- }
149
-
150
- async getFilterValues(ctx: Context) {
151
- // adding opportunity to adjust the values for a given field filter before it's sent to Sealious
152
- const values = await super.getFilterValues(ctx);
153
- for (const filterField of filterFields) {
154
- const key = filterField.field as keyof typeof values;
155
- if (key in values) {
156
- const prepare_fn = filterField.prepareValue;
157
- if (prepare_fn) {
158
- values[key] = prepare_fn(values[key]) as any;
159
- }
160
- }
161
- }
162
- return values;
163
- }
164
-
165
- async renderItem(ctx: Context, item: CollectionItem<typeof ${uppercase_collection}>) {
166
- return <tr>
167
- {displayFields.map(({ field, format }) => {
168
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
169
- const value = item.get(field as any);
170
- return <td>{format ? format(value, item) : value}</td>;
171
- })}
172
- </tr>;
173
- }
174
-
175
- renderListContainer(ctx: Context, content: Templatable): FlatTemplatable {
176
- return (
177
- <table class="sealious-list ${toKebabCase(action_name)}-table">
178
- {this.renderTableHead(ctx, displayFields)}
179
- <tbody>{content}</tbody>
180
- </table>
181
- );
182
- }
183
-
184
- async render(ctx: Context) {
185
- return html(
186
- ctx,
187
- "${action_name}",
188
- <div class="sealious-list-wrapper ${toKebabCase(action_name)}--wrapper">
189
- <h2>${action_name} List</h2>
190
- {super.render(ctx)}
191
- </div>
192
- );
193
- }
194
-
195
- })(${uppercase_collection});
196
- `;
24
+ const result = await collectionListTemplate(
25
+ collection_name,
26
+ action_name,
27
+ newfilefullpath
28
+ );
197
29
  return formatWithPrettier(result);
198
30
  };