@sealcode/sealgen 0.15.35 → 0.15.36

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 +31 -4
  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 +160 -30
  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,18 +1,20 @@
1
+ import { hasShape, is, predicates } from "@sealcode/ts-predicates";
2
+ import qs from "qs";
1
3
  import { FormControl } from "./form-control.js";
2
4
  class Table extends FormControl {
3
- constructor(table_field, render_fields) {
5
+ constructor(table_field, options) {
4
6
  super();
5
7
  this.table_field = table_field;
6
- this.render_fields = render_fields;
8
+ this.options = options;
7
9
  this.role = "input";
8
10
  }
9
11
  async render(fctx) {
10
- const { parsed: rows } = await this.table_field.getValue(
12
+ let { parsed: rows } = await this.table_field.getValue(
11
13
  fctx.ctx,
12
14
  fctx.data
13
15
  );
14
16
  if (!rows) {
15
- return "";
17
+ rows = [];
16
18
  }
17
19
  return (
18
20
  /* HTML */
@@ -27,34 +29,134 @@ class Table extends FormControl {
27
29
  ...rows.length >= 20 ? ["form-input__wrapper--options-count--20-or-more"] : []
28
30
  ].join(" ")}"
29
31
  >
32
+ <label>${this.options.label}</label>
30
33
  <div class="table__wrapper">
31
34
  <table>
32
35
  <tbody>
33
- <tr>
34
- ${rows?.map((row, row_index) => {
36
+ ${rows?.map((row, row_index) => {
35
37
  return (
36
38
  /* HTML */
37
39
  `<tr>
38
- ${Object.entries(
39
- this.table_field.columns
40
- ).map(([key]) => {
41
- return `<td>${this.render_fields[key](
40
+ ${Object.entries(this.table_field.columns).map(([key]) => {
41
+ return `<td>${this.options.render_fields[key](
42
42
  fctx,
43
43
  `${this.table_field.name}[${row_index}][${key}]`,
44
44
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
45
45
  row[key]
46
46
  )}</td>`;
47
47
  }).join("")}
48
- </tr>`
48
+ ${this.options.allow_removing ? (
49
+ /* HTML */
50
+ `<td>
51
+ <input
52
+ type="submit"
53
+ value="${this.options.label_remove || "remove"}"
54
+ form="${fctx.form_id}"
55
+ formnovalidate
56
+ formaction="${this.getActionURL(
57
+ this.table_field.name,
58
+ {
59
+ remove: {
60
+ index: row_index
61
+ }
62
+ }
63
+ )}"
64
+ />
65
+ </td>`
66
+ ) : ""}
67
+ </tr>`
49
68
  );
50
69
  }).join("")}
51
- </tr>
52
70
  </tbody>
53
71
  </table>
72
+ ${this.options.allow_adding ? (
73
+ /* HTML */
74
+ `<input
75
+ type="submit"
76
+ value="${this.options.label_add || "add"}"
77
+ form="${fctx.form_id}"
78
+ formnovalidate
79
+ formaction="${this.getActionURL(
80
+ this.table_field.name,
81
+ {
82
+ insert: { index: rows.length, value: {} }
83
+ }
84
+ )}"
85
+ />`
86
+ ) : ""}
54
87
  </div>
55
88
  </div>`
56
89
  );
57
90
  }
91
+ getFrameID() {
92
+ return `array-frame-${this.table_field.name}`;
93
+ }
94
+ getActionURL(field_name, action) {
95
+ return `./?${qs.stringify({
96
+ action,
97
+ field_name
98
+ })}`;
99
+ }
100
+ mount(router, form) {
101
+ router.post("/", async (ctx, next) => {
102
+ const action = ctx.$body.action;
103
+ const field_name = ctx.$body.field_name;
104
+ if (!is(action, predicates.object) || !is(field_name, predicates.string)) {
105
+ await next();
106
+ return;
107
+ }
108
+ if (hasShape(
109
+ {
110
+ insert: predicates.shape({ index: predicates.string })
111
+ },
112
+ action
113
+ )) {
114
+ if (field_name !== this.table_field.name) {
115
+ await next();
116
+ return;
117
+ }
118
+ if (!ctx.$body[this.table_field.name]) {
119
+ ctx.$body[this.table_field.name] = {};
120
+ }
121
+ ctx.$body[this.table_field.name][action.insert.index] = await this.options.make_new_row(ctx);
122
+ }
123
+ if (hasShape(
124
+ {
125
+ remove: predicates.shape({ index: predicates.string })
126
+ },
127
+ action
128
+ )) {
129
+ if (field_name !== this.table_field.name) {
130
+ await next();
131
+ return;
132
+ }
133
+ if (!ctx.$body[this.table_field.name]) {
134
+ ctx.$body[this.table_field.name] = {};
135
+ }
136
+ ctx.$body[this.table_field.name] = Object.fromEntries(
137
+ Object.entries(
138
+ Object.values(
139
+ ctx.$body[this.table_field.name]
140
+ ).filter(
141
+ (_, index) => index != parseInt(action.remove.index)
142
+ )
143
+ )
144
+ );
145
+ }
146
+ ctx.override_reaction = {
147
+ action: "stay",
148
+ content: form.render(
149
+ ctx,
150
+ {
151
+ raw_values: await form.extractRawValues(ctx),
152
+ messages: []
153
+ },
154
+ false
155
+ )
156
+ };
157
+ await next();
158
+ });
159
+ }
58
160
  }
59
161
  export {
60
162
  Table
@@ -29,7 +29,7 @@ class Boolean extends FormField {
29
29
  parsed_value: false,
30
30
  error: null
31
31
  };
32
- } else if (value === null) {
32
+ } else if (value === null || value === "") {
33
33
  return { parsable: true, parsed_value: false, error: null };
34
34
  } else {
35
35
  return {
@@ -27,7 +27,10 @@ class FormField {
27
27
  )
28
28
  };
29
29
  } else {
30
- return { valid: true, message: "Empty, but not required" };
30
+ return {
31
+ valid: true,
32
+ message: ""
33
+ };
31
34
  }
32
35
  }
33
36
  return this.isValueValid(ctx, value);
@@ -2,7 +2,7 @@ import { is, predicates } from "@sealcode/ts-predicates";
2
2
  import { FormField } from "./field.js";
3
3
  class Table extends FormField {
4
4
  constructor(columns) {
5
- super(true);
5
+ super(false);
6
6
  this.columns = columns;
7
7
  }
8
8
  getEmptyValue() {
@@ -21,6 +21,7 @@ class Table extends FormField {
21
21
  const row_data = raw_value[row_number];
22
22
  const parsed_row = {};
23
23
  if (!is(row_data, predicates.object)) {
24
+ console.log("table.ts<fields>:55", this.name);
24
25
  throw new Error(`Expected object, got ${String(row_data)}`);
25
26
  }
26
27
  for (const key in row_data) {
@@ -34,13 +35,17 @@ class Table extends FormField {
34
35
  }
35
36
  const parse_result = await field.parse(
36
37
  ctx,
38
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
37
39
  row_data[key]
38
40
  );
39
41
  if (!parse_result.parsable) {
40
- console.debug("table.ts:51");
42
+ const message = `Invalid value for row ${row_number}.${key}: ${parse_result.error}`;
43
+ console.warn(
44
+ `Invalid value for row ${row_number}.${key}: ${parse_result.error}`
45
+ );
41
46
  return {
42
47
  parsable: false,
43
- error: `Invalid value for row ${row_number}.${key}: ${parse_result.error}`,
48
+ error: message,
44
49
  parsed_value: null
45
50
  };
46
51
  }
package/lib/forms/form.js CHANGED
@@ -170,6 +170,11 @@ class Form extends MountableWithFields {
170
170
  }
171
171
  await next();
172
172
  });
173
+ const subrouter = new Router();
174
+ for (const control of this.controls) {
175
+ control.mount(subrouter, this);
176
+ }
177
+ router.use(path, subrouter.routes(), subrouter.allowedMethods());
173
178
  router.get(path, async (ctx) => {
174
179
  ctx.type = "html";
175
180
  ctx.body = await this.render(
@@ -182,7 +187,8 @@ class Form extends MountableWithFields {
182
187
  );
183
188
  });
184
189
  router.post(path, async (ctx) => {
185
- const reaction = await this.handlePost(ctx);
190
+ console.log("REGULAR FORM HANDLE");
191
+ const reaction = ctx.override_reaction || await this.handlePost(ctx);
186
192
  if (reaction.action == "stay") {
187
193
  ctx.status = 422;
188
194
  ctx.body = reaction.content;
@@ -191,11 +197,6 @@ class Form extends MountableWithFields {
191
197
  ctx.redirect(reaction.url);
192
198
  }
193
199
  });
194
- const subrouter = new Router();
195
- for (const control of this.controls) {
196
- control.mount(subrouter);
197
- }
198
- router.use(path, subrouter.routes(), subrouter.allowedMethods());
199
200
  }
200
201
  async extractRawValues(ctx) {
201
202
  return Object.keys(ctx.$body).length ? ctx.$body : this.getInitialValues(ctx);
@@ -31,6 +31,7 @@ const SimpleTextFilter = {
31
31
  <input
32
32
  type="text"
33
33
  name="filter[${field.name}]"
34
+ autocomplete="off"
34
35
  ${value ? `value="${value}"` : value}
35
36
  />
36
37
  </label>`
@@ -2,7 +2,9 @@ import Prettier from "prettier";
2
2
  import { curryImportPath } from "../utils/import-path.js";
3
3
  import { getPrettierConfig } from "../utils/prettier.js";
4
4
  const defaults = {
5
- postimport: ``,
5
+ postimport: `
6
+ import { Fields } from "@sealcode/sealgen";
7
+ import type {FormDataValue} from "@sealcode/sealgen";`,
6
8
  fields: `
7
9
  email: new Fields.CollectionField(true, Users.fields.email),
8
10
  password: new Fields.SimpleFormField(true),`,
@@ -25,16 +27,28 @@ const defaults = {
25
27
  } else {
26
28
  return { valid: true, error: "" };
27
29
  }
28
- }`
30
+ }`,
31
+ onsubmit: ` async onSubmit() {
32
+ //noop
33
+ return;
34
+ }`,
35
+ can_access: ` // eslint-disable-next-line @typescript-eslint/no-unused-vars
36
+ async canAccess(_: Context) {
37
+ return { canAccess: true, message: "" };
38
+ }`,
39
+ render: `async render(ctx: Context, data: FormData, show_field_errors: boolean) {
40
+ return html(ctx, "Form", await super.render(ctx, data, show_field_errors));
41
+ }`,
42
+ get_initial_values: "",
43
+ other_methods: ""
29
44
  };
30
45
  async function formTemplate(action_name, newfilefullpath, _params = defaults) {
31
46
  const params = { ...defaults, ..._params };
32
47
  const rel = curryImportPath(newfilefullpath);
33
48
  const content = `import type { Context } from "koa";
34
- import type { FormData, FormDataValue } from "@sealcode/sealgen";
35
- import { Form, Fields, Controls, fieldsToShape } from "@sealcode/sealgen";
49
+ import type { FormData } from "@sealcode/sealgen";
50
+ import { Form, Controls, fieldsToShape } from "@sealcode/sealgen";
36
51
  import html from "${rel("src/back/html.js")}";
37
- import { Users } from "${rel("src/back/collections/collections.js")}";
38
52
 
39
53
  ${params.postimport}
40
54
 
@@ -54,20 +68,15 @@ export default new (class ${action_name}Form extends Form<typeof fields, void> {
54
68
  ${params.controls}
55
69
  ];
56
70
 
71
+ ${params.other_methods}
57
72
 
58
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
59
- async canAccess(_: Context) {
60
- return { canAccess: true, message: "" };
61
- }
73
+ ${params.get_initial_values}
62
74
 
63
- async onSubmit() {
64
- //noop
65
- return;
66
- }
75
+ ${params.onsubmit}
76
+
77
+ ${params.can_access}
67
78
 
68
- async render(ctx: Context, data: FormData, show_field_errors: boolean) {
69
- return html(ctx, "${action_name}", await super.render(ctx, data, show_field_errors));
70
- }
79
+ ${params.render}
71
80
  })();
72
81
  `;
73
82
  return Prettier.format(content, await getPrettierConfig());
@@ -1,13 +1,7 @@
1
- import _locreq from "locreq";
2
- import { curryImportPath } from "../utils/import-path.js";
3
1
  import prompts from "prompts";
4
2
  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
3
  import { formatWithPrettier } from "../utils/prettier.js";
9
- import { toKebabCase } from "js-convert-case";
10
- const target_locreq = _locreq(process.cwd());
4
+ import { collectionListTemplate } from "./shared/collection-list.js";
11
5
  const listTemplate = async (action_name, newfilefullpath) => {
12
6
  const response = await prompts({
13
7
  type: "autocomplete",
@@ -19,166 +13,11 @@ const listTemplate = async (action_name, newfilefullpath) => {
19
13
  }))
20
14
  });
21
15
  const collection_name = response.collection;
22
- const [uppercase_collection, collection_fields] = await Promise.all([
23
- extractCollectionClassname(
24
- target_locreq.resolve(
25
- "src/back/collections/" + collection_name + ".ts"
26
- )
27
- ),
28
- extract_fields_from_collection(collection_name)
29
- ]);
30
- const importPath = curryImportPath(newfilefullpath);
31
- const result = `import type { Context } from "koa";
32
- import type { CollectionItem, ItemListResult } from "sealious";
33
- import type { FlatTemplatable } from "tempstream";
34
- import { TempstreamJSX } from "tempstream";
35
- import { ${uppercase_collection} } from "${importPath(
36
- target_locreq.resolve("src/back/collections/collections.ts")
37
- )}";
38
- import html from "${importPath(target_locreq.resolve("src/back/html.ts"))}";
39
- import type { ListFilterRender } from "@sealcode/sealgen";
40
- import {
41
- SealiousItemListPage,
42
- BaseListPageFields,
43
- DefaultListFilters,
44
- } from "@sealcode/sealgen";
45
- import qs from "qs";
46
- import type { FilePointer } from "@sealcode/file-manager";
47
- import { imageRouter } from "${importPath(
48
- target_locreq.resolve("src/back/image-router.ts")
49
- )}";
50
-
51
- export const actionName = "${action_name}";
52
-
53
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
54
- const filterFields = [
55
- ${collection_fields.map(
56
- (field) => ` { field: "${field.name}", ...${DefaultListFilters[field.type] ? `DefaultListFilters["${field.type}"]` : `DefaultListFilters.fallback`} }`
57
- ).join(",\n")}
58
- ] as {
59
- field: keyof (typeof ${uppercase_collection})["fields"];
60
- render?: ListFilterRender;
61
- prepareValue?: (filter_value: unknown)=>unknown; // set this function to change what filter value is passed to Sealious
62
- }[];
63
-
64
- const displayFields = [
65
- ${collection_fields.map((field) => {
66
- if (field.type == "boolean") {
67
- return ` {
68
- field: "${field.name}",
69
- label: "${field.name}",
70
- format: (v: boolean) => (v ? "YES" : "NO"),
71
- }`;
72
- }
73
- if (field.type == "datetime") {
74
- return ` {
75
- field: "${field.name}",
76
- label: "${field.name}",
77
- format: (v: number) => {
78
- const d = new Date();
79
- d.setTime(v);
80
- return d.toString().split(" ").slice(1, 5).join(" ");
81
- },
82
- }`;
83
- }
84
- if (field.type == "image") {
85
- return `{
86
- field: "${field.name}",
87
- label: "${field.name}",
88
- format: async (value: FilePointer) => {
89
- return imageRouter.image(await value.getPath(), {
90
- container: { width: 45, height: 45 },
91
- alt: "",
92
- });
93
- },
94
- }`;
95
- }
96
- return ` { field: "${field.name}", label: "${field.name}" }`;
97
- }).join(",\n")}
98
- ] as {
99
- field: string;
100
- label: string;
101
- format?: (value: unknown, item: CollectionItem<typeof Opinions>) => FlatTemplatable;
102
- }[];
103
-
104
- export default new (class ${action_name}Page extends SealiousItemListPage<
105
- typeof ${uppercase_collection},
106
- typeof BaseListPageFields
107
- > {
108
- fields = BaseListPageFields;
109
-
110
- async renderFilters(ctx: Context): Promise<FlatTemplatable> {
111
- const query_params = qs.parse(ctx.search.slice(1));
112
- query_params.page = "1";
113
- const filter_values = await super.getFilterValues(ctx);
114
- return <form>
115
- {Object.entries(query_params).map(([key, value]) => {
116
- if (key == "filter") {
117
- return "";
118
- }
119
- // this is necessary to not lose any query params when the user changes the filter values
120
- return <input type="hidden" name={key} value={value}/>;
121
- })}
122
- {filterFields.map(({ field, render }) => {
123
- if (!render) {
124
- render = DefaultListFilters.fallback;
125
- }
126
- return (
127
- render(filter_values[field] || "", this.collection.fields[field]) ||
128
- ""
129
- );
130
- })}
131
- <input type="submit" />
132
- </form>;
133
- }
134
-
135
- async getFilterValues(ctx: Context) {
136
- // adding opportunity to adjust the values for a given field filter before it's sent to Sealious
137
- const values = await super.getFilterValues(ctx);
138
- for (const filterField of filterFields) {
139
- const key = filterField.field as keyof typeof values;
140
- if (key in values) {
141
- const prepare_fn = filterField.prepareValue;
142
- if (prepare_fn) {
143
- values[key] = prepare_fn(values[key]) as any;
144
- }
145
- }
146
- }
147
- return values;
148
- }
149
-
150
- async renderItem(ctx: Context, item: CollectionItem<typeof ${uppercase_collection}>) {
151
- return <tr>
152
- {displayFields.map(({ field, format }) => {
153
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any
154
- const value = item.get(field as any);
155
- return <td>{format ? format(value, item) : value}</td>;
156
- })}
157
- </tr>;
158
- }
159
-
160
- renderListContainer(ctx: Context, content: Templatable): FlatTemplatable {
161
- return (
162
- <table class="sealious-list ${toKebabCase(action_name)}-table">
163
- {this.renderTableHead(ctx, displayFields)}
164
- <tbody>{content}</tbody>
165
- </table>
166
- );
167
- }
168
-
169
- async render(ctx: Context) {
170
- return html(
171
- ctx,
172
- "${action_name}",
173
- <div class="sealious-list-wrapper ${toKebabCase(action_name)}--wrapper">
174
- <h2>${action_name} List</h2>
175
- {super.render(ctx)}
176
- </div>
177
- );
178
- }
179
-
180
- })(${uppercase_collection});
181
- `;
16
+ const result = await collectionListTemplate(
17
+ collection_name,
18
+ action_name,
19
+ newfilefullpath
20
+ );
182
21
  return formatWithPrettier(result);
183
22
  };
184
23
  export {
@@ -1,84 +1,19 @@
1
- import { toCamelCase, toKebabCase, toPascalCase } from "js-convert-case";
1
+ import { toKebabCase, toPascalCase } from "js-convert-case";
2
2
  import { formTemplate } from "../form.js";
3
3
  import { curryImportPath } from "../../utils/import-path.js";
4
4
  import extract_fields_from_collection from "../../utils/extract-fields-from-collection.js";
5
- function renderFieldForSealious({
6
- name,
7
- type,
8
- is_required,
9
- is_readonly
10
- }) {
11
- if (type == "deep-reverse-single-reference" || type == "jdd" || is_readonly) {
12
- return "";
13
- }
14
- let fallback_value = `""`;
15
- let main_value = `data.${name}`;
16
- if (type == "image" || type == "file") {
17
- fallback_value = `null`;
18
- if (is_required) {
19
- main_value = toCamelCase("prepared-" + name);
20
- }
21
- }
22
- if (type == "boolean") {
23
- main_value = `!!data.${name}`;
24
- fallback_value = "false";
25
- }
26
- if (type == "date") {
27
- fallback_value == `undefined`;
28
- }
29
- if (is_required) {
30
- return `${name}: ${main_value}`;
31
- } else {
32
- return `${name}: ${main_value} || ${fallback_value}`;
33
- }
34
- }
35
- function renderFieldPreCreateAction({ name, type }) {
36
- if (type == "image" || type == "file") {
37
- const tempname = toCamelCase("prepared-" + name);
38
- return `const ${tempname} = data.${name}.new || data.${name}.old;
39
- if(!${tempname}){
40
- throw new Error("Missing field: ${name}")
41
- }`;
42
- }
43
- return "";
44
- }
45
- function renderFieldPostCreateAction({
46
- name,
47
- type,
48
- referencing_collection,
49
- referencing_field,
50
- intermediary_field_that_points_there
51
- }) {
52
- if (type == "deep-reverse-single-reference" && referencing_collection && referencing_field && intermediary_field_that_points_there) {
53
- return ` await (async function(){
54
- // We iterate over all selected values and insert appropriate entries for the ${name} relation
55
- const ids = Object.entries(
56
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
57
- data.${name} as unknown as Record<string, string>
58
- )
59
- .filter(([, value]) => value === "on")
60
- .map(([key]) => key);
61
-
62
- const promises = ids.map(async (id) => {
63
- await ctx.$app.collections["${referencing_collection}"].create(
64
- new ctx.$app.SuperContext(),
65
- {
66
- "${referencing_field}": created.id,
67
- "${intermediary_field_that_points_there}": id,
68
- }
69
- );
70
- });
71
-
72
- await Promise.all(promises);
73
- })()`;
74
- }
75
- return "";
76
- }
5
+ import { getFieldHandler } from "./shared-crud-form-fields.js";
77
6
  async function createItemFormTemplate(create_action_name, newfilefullpath, collection_name, list_action) {
78
7
  const collection_fields = await extract_fields_from_collection(
79
8
  collection_name
80
9
  );
81
10
  const importPath = curryImportPath(newfilefullpath);
11
+ const field_handler_results = await Promise.all(
12
+ collection_fields.map(
13
+ (field) => getFieldHandler(collection_name, field, importPath)
14
+ )
15
+ );
16
+ const post_create_actions = field_handler_results.map(({ post_create }) => post_create).filter((e) => e != "");
82
17
  return formTemplate(create_action_name, newfilefullpath, {
83
18
  postimport: `import {${toPascalCase(
84
19
  collection_name
@@ -105,6 +40,13 @@ async function createItemFormTemplate(create_action_name, newfilefullpath, colle
105
40
  const response = await policy.check(ctx.$context);
106
41
  return { canAccess: response?.allowed || false, message: response?.reason || "" };
107
42
  };`,
43
+ get_initial_values: field_handler_results.filter((r) => !r.hide_initial_value_create).map(({ initial_value_create }) => initial_value_create).filter((e) => e != "").length ? `getInitialValues(ctx: Context){
44
+ return {
45
+ ${field_handler_results.filter((r) => !r.hide_initial_value_create).map(
46
+ ({ initial_value_create }) => initial_value_create
47
+ ).join(",\n")}
48
+ }
49
+ }` : ``,
108
50
  onsubmit: `
109
51
  async onSubmit(ctx: Context) {
110
52
  const data = await this.getParsedValues(ctx);
@@ -112,23 +54,22 @@ async function createItemFormTemplate(create_action_name, newfilefullpath, colle
112
54
  throw new Error("Error when parsing the form values");
113
55
  }
114
56
 
115
- ${collection_fields.map((field) => renderFieldPreCreateAction(field)).filter((e) => e !== "").join("\n")}
57
+ ${field_handler_results.map(({ pre_create }) => pre_create).filter((e) => e !== "").join("\n")}
116
58
 
117
- const created = await ctx.$app.collections["${toKebabCase(
118
- collection_name
119
- )}"].create(
59
+ ${post_create_actions.length ? `const created = ` : ""} await ctx.$app.collections["${toKebabCase(collection_name)}"].create(
120
60
  ctx.$context,
121
61
  {
122
- ${collection_fields.map((field) => renderFieldForSealious(field)).filter((e) => e != "").join(",\n")}
123
- }
62
+ ${field_handler_results.filter((result) => !result.hide_sealious_value).map((result) => result.sealious_value(result)).filter((e) => e != "").join(",\n")}
63
+ }
124
64
  );
125
- ${collection_fields.map((field) => renderFieldPostCreateAction(field)).filter((e) => e != "").join("\n")}
65
+ ${post_create_actions.length ? `/* ==== POST CREATE ACTIONS ==== */` : ""}
66
+ ${post_create_actions.join("\n")}
126
67
  }
127
68
  `,
128
69
  render: ` async render(ctx: Context, data: FormData, show_field_errors: boolean) {
129
70
  return html(
130
71
  ctx,
131
- "AddBlogArticle",
72
+ "Create ${toKebabCase(collection_name)}",
132
73
  tempstream/* HTML */ \` <div class="add-opinion-form">
133
74
  <a class="" href="\${${list_action}URL}"
134
75
  >\u2190 Back to ${collection_name} list</a
@@ -140,7 +81,6 @@ async function createItemFormTemplate(create_action_name, newfilefullpath, colle
140
81
  });
141
82
  }
142
83
  export {
143
- createItemFormTemplate,
144
- renderFieldForSealious
84
+ createItemFormTemplate
145
85
  };
146
86
  //# sourceMappingURL=collection-create-form.js.map