@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.
- package/.xunit +59 -57
- package/@types/src/forms/controls/form-control.d.ts +2 -1
- package/@types/src/forms/controls/table.d.ts +24 -5
- package/@types/src/forms/fields/table.d.ts +4 -2
- package/@types/src/templates/form.d.ts +5 -0
- package/@types/src/templates/shared/collection-create-form.d.ts +0 -2
- package/@types/src/templates/shared/handlers/boolean.d.ts +2 -0
- package/@types/src/templates/shared/handlers/date.d.ts +2 -0
- package/@types/src/templates/shared/handlers/deep-reverse-single-reference.d.ts +2 -0
- package/@types/src/templates/shared/handlers/default.d.ts +2 -0
- package/@types/src/templates/shared/handlers/enum.d.ts +2 -0
- package/@types/src/templates/shared/handlers/file.d.ts +2 -0
- package/@types/src/templates/shared/handlers/image.d.ts +2 -0
- package/@types/src/templates/shared/handlers/int.d.ts +2 -0
- package/@types/src/templates/shared/handlers/jdd.d.ts +2 -0
- package/@types/src/templates/shared/handlers/single-reference.d.ts +2 -0
- package/@types/src/templates/shared/handlers/structured-array.d.ts +2 -0
- package/@types/src/templates/shared/shared-crud-form-fields.d.ts +29 -6
- package/@types/src/utils/extract-fields-from-collection.d.ts +12 -2
- package/@types/src/utils/with-fallback.d.ts +1 -1
- package/lib/add-crud.js +5 -5
- package/lib/cli.js +2 -0
- package/lib/forms/controls/form-control.js +2 -2
- package/lib/forms/controls/multiple-photos.js +2 -1
- package/lib/forms/controls/single-reference.test.js +1 -1
- package/lib/forms/controls/table.js +114 -12
- package/lib/forms/fields/boolean.js +1 -1
- package/lib/forms/fields/field.js +4 -1
- package/lib/forms/fields/table.js +8 -3
- package/lib/forms/form.js +7 -6
- package/lib/page/default-list-filters.js +1 -0
- package/lib/templates/form.js +25 -16
- package/lib/templates/list.js +6 -167
- package/lib/templates/shared/collection-create-form.js +23 -83
- package/lib/templates/shared/collection-edit-form.js +53 -96
- package/lib/templates/shared/collection-list.js +21 -6
- package/lib/templates/shared/confirm-item-delete.js +7 -5
- package/lib/templates/shared/handlers/boolean.js +14 -0
- package/lib/templates/shared/handlers/date.js +8 -0
- package/lib/templates/shared/handlers/deep-reverse-single-reference.js +209 -0
- package/lib/templates/shared/handlers/default.js +50 -0
- package/lib/templates/shared/handlers/enum.js +19 -0
- package/lib/templates/shared/handlers/file.js +18 -0
- package/lib/templates/shared/handlers/image.js +32 -0
- package/lib/templates/shared/handlers/int.js +11 -0
- package/lib/templates/shared/handlers/jdd.js +10 -0
- package/lib/templates/shared/handlers/single-reference.js +12 -0
- package/lib/templates/shared/handlers/structured-array.js +33 -0
- package/lib/templates/shared/item-delete.js +5 -3
- package/lib/templates/shared/shared-crud-form-fields.js +72 -82
- package/lib/utils/extract-fields-from-collection.js +31 -5
- package/package.json +2 -1
- package/src/add-crud.ts +153 -0
- package/src/cli.ts +2 -0
- package/src/forms/controls/form-control.ts +3 -2
- package/src/forms/controls/multiple-photos.ts +1 -0
- package/src/forms/controls/single-reference.test.ts +1 -1
- package/src/forms/controls/table.ts +168 -31
- package/src/forms/fields/boolean.ts +1 -1
- package/src/forms/fields/field.ts +4 -1
- package/src/forms/fields/table.ts +25 -6
- package/src/forms/form.ts +13 -7
- package/src/page/default-list-filters.ts +1 -0
- package/src/templates/form.ts +24 -15
- package/src/templates/list.ts +6 -174
- package/src/templates/shared/collection-create-form.ts +121 -0
- package/src/templates/shared/collection-edit-form.ts +196 -0
- package/src/templates/shared/collection-list.ts +252 -0
- package/src/templates/shared/confirm-item-delete.ts +78 -0
- package/src/templates/shared/handlers/boolean.ts +18 -0
- package/src/templates/shared/handlers/date.ts +12 -0
- package/src/templates/shared/handlers/deep-reverse-single-reference.ts +244 -0
- package/src/templates/shared/handlers/default.ts +58 -0
- package/src/templates/shared/handlers/enum.ts +23 -0
- package/src/templates/shared/handlers/file.ts +22 -0
- package/src/templates/shared/handlers/image.ts +45 -0
- package/src/templates/shared/handlers/int.ts +15 -0
- package/src/templates/shared/handlers/jdd.ts +14 -0
- package/src/templates/shared/handlers/single-reference.ts +16 -0
- package/src/templates/shared/handlers/structured-array.ts +42 -0
- package/src/templates/shared/item-delete.ts +45 -0
- package/src/templates/shared/shared-crud-form-fields.ts +179 -0
- package/src/utils/extract-fields-from-collection.ts +45 -6
- package/src/utils/with-fallback.ts +1 -1
- package/src/utils/write-file.ts +8 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { toKebabCase, toPascalCase } from "js-convert-case";
|
|
2
|
+
import extract_fields_from_collection from "../../../utils/extract-fields-from-collection.js";
|
|
3
|
+
import {
|
|
4
|
+
FieldHandler,
|
|
5
|
+
FieldHandlerResult,
|
|
6
|
+
} from "../shared-crud-form-fields.js";
|
|
7
|
+
|
|
8
|
+
export const deep_reverse_single_reference_handler: FieldHandler<
|
|
9
|
+
FieldHandlerResult,
|
|
10
|
+
Partial<FieldHandlerResult>
|
|
11
|
+
> = async (
|
|
12
|
+
collection_name,
|
|
13
|
+
{
|
|
14
|
+
name,
|
|
15
|
+
target,
|
|
16
|
+
referencing_collection,
|
|
17
|
+
intermediary_field_that_points_there,
|
|
18
|
+
referencing_field,
|
|
19
|
+
},
|
|
20
|
+
___,
|
|
21
|
+
{ is_required_expr, fields_var }
|
|
22
|
+
) => {
|
|
23
|
+
if (
|
|
24
|
+
!referencing_collection ||
|
|
25
|
+
!target ||
|
|
26
|
+
!intermediary_field_that_points_there ||
|
|
27
|
+
!referencing_field
|
|
28
|
+
) {
|
|
29
|
+
console.warn(
|
|
30
|
+
"missing referencing_collection, target, intermediary_field_that_points_there, or referencing_field member in extracted field info"
|
|
31
|
+
);
|
|
32
|
+
return { field: "" };
|
|
33
|
+
}
|
|
34
|
+
const referencing_fields = await extract_fields_from_collection(
|
|
35
|
+
referencing_collection
|
|
36
|
+
);
|
|
37
|
+
if (referencing_fields.length == 2) {
|
|
38
|
+
// just a regular n-to-n intermediary collection
|
|
39
|
+
const post_create = (get_id: string) => `{
|
|
40
|
+
const item_id = ${get_id};
|
|
41
|
+
// First, we remove links refering to current item
|
|
42
|
+
const {items: existing_links } = (await ctx.$app.collections["${referencing_collection}"]
|
|
43
|
+
.list(ctx.$context)
|
|
44
|
+
.filter({"${referencing_field}": item_id})
|
|
45
|
+
.fetch()
|
|
46
|
+
)
|
|
47
|
+
const should_exist_ids = Object.entries(
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
49
|
+
(data["${name}"] as unknown as Record<string, string>) || {}
|
|
50
|
+
)
|
|
51
|
+
.filter(([, value]) => value === "on")
|
|
52
|
+
.map(([key]) => key);
|
|
53
|
+
const to_delete = existing_links.filter(
|
|
54
|
+
(link) => !should_exist_ids.includes(link.get("${intermediary_field_that_points_there}"))
|
|
55
|
+
);
|
|
56
|
+
const to_create_ids = should_exist_ids.filter(
|
|
57
|
+
(id) => !existing_links.map(({ id }) => id).includes(id)
|
|
58
|
+
);
|
|
59
|
+
await Promise.all(to_delete.map((item) => item.remove(ctx.$context)));
|
|
60
|
+
// Then, we iterate over all selected values and insert appropriate missing links
|
|
61
|
+
const promises = to_create_ids.map(async (id) => {
|
|
62
|
+
await ctx.$app.collections["${toKebabCase(referencing_collection)}"].create(
|
|
63
|
+
ctx.$context,
|
|
64
|
+
{
|
|
65
|
+
"${referencing_field}": item_id,
|
|
66
|
+
"${intermediary_field_that_points_there}": id,
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
await Promise.all(promises);
|
|
71
|
+
}`;
|
|
72
|
+
return {
|
|
73
|
+
field: `${name}: new Fields.CheckboxedListField(${is_required_expr}, async (ctx) => {
|
|
74
|
+
const { items } = await ctx.$app.collections["${target}"]
|
|
75
|
+
.list(ctx.$context)
|
|
76
|
+
.fetch();
|
|
77
|
+
|
|
78
|
+
return items.map((s) => ({
|
|
79
|
+
label: s.get("name"),
|
|
80
|
+
value: s.id,
|
|
81
|
+
}));
|
|
82
|
+
})`,
|
|
83
|
+
controls: `new Controls.CheckboxedListInput(${fields_var}.${name}, {
|
|
84
|
+
label: "${name}",
|
|
85
|
+
})`,
|
|
86
|
+
post_edit: post_create(`await this.getID(ctx)`),
|
|
87
|
+
post_create: post_create(`created.id`),
|
|
88
|
+
initial_value_edit: `Object.fromEntries((item.get("${name}") || []).map(id=>[id, "on"]))`,
|
|
89
|
+
sealious_value: () => "",
|
|
90
|
+
};
|
|
91
|
+
} else {
|
|
92
|
+
// probably an n-to-n intermediary collection with an additional attribute
|
|
93
|
+
const initial_value_function_name = `get${toPascalCase(
|
|
94
|
+
name
|
|
95
|
+
)}TableInitialValues`;
|
|
96
|
+
const post_create = (get_id: string) => `{
|
|
97
|
+
const item_id = ${get_id};
|
|
98
|
+
// First, we remove links refering to current item
|
|
99
|
+
const { items: to_delete } = await ctx.$app.collections[
|
|
100
|
+
"${toKebabCase(referencing_collection)}"
|
|
101
|
+
]
|
|
102
|
+
.list(ctx.$context)
|
|
103
|
+
.filter({ "${referencing_field}": item_id })
|
|
104
|
+
.fetch();
|
|
105
|
+
await Promise.all(to_delete.map((item) => item.remove(ctx.$context)));
|
|
106
|
+
|
|
107
|
+
// Then, we iterate over all selected values and insert appropriate entries for the subcategories relation
|
|
108
|
+
|
|
109
|
+
const promises = data["${name}"]
|
|
110
|
+
.filter((s) => s.selected == true)
|
|
111
|
+
.map(async (link_info) => {
|
|
112
|
+
await ctx.$app.collections["${toKebabCase(referencing_collection)}"].create(
|
|
113
|
+
ctx.$context,
|
|
114
|
+
{
|
|
115
|
+
"${referencing_field}": item_id,
|
|
116
|
+
"${intermediary_field_that_points_there}": String(link_info["${intermediary_field_that_points_there}"]),
|
|
117
|
+
${referencing_fields
|
|
118
|
+
.filter(
|
|
119
|
+
({ type }) => type !== "single-reference"
|
|
120
|
+
)
|
|
121
|
+
.map(
|
|
122
|
+
(field) =>
|
|
123
|
+
`"${field.name}": link_info["${field.name}"]`
|
|
124
|
+
)
|
|
125
|
+
.join(",\n")}
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
await Promise.all(promises);
|
|
131
|
+
}`;
|
|
132
|
+
const initial_value = `(await ${initial_value_function_name}(ctx,item)) as unknown as FormDataValue`;
|
|
133
|
+
return {
|
|
134
|
+
imports: {
|
|
135
|
+
shared: `import type { CollectionItem } from "sealious"; import type { Context } from "koa";`,
|
|
136
|
+
edit: `import {${initial_value_function_name}} from "../shared.js"; import type { FormDataValue } from "@sealcode/sealgen";`,
|
|
137
|
+
},
|
|
138
|
+
field: `
|
|
139
|
+
${name}: new Fields.Table({
|
|
140
|
+
selected: new Fields.Boolean(false),
|
|
141
|
+
"${referencing_field}": new Fields.SimpleFormField(true),
|
|
142
|
+
"${intermediary_field_that_points_there}": new Fields.SimpleFormField(true),
|
|
143
|
+
${referencing_fields
|
|
144
|
+
.filter(({ type }) => type !== "single-reference")
|
|
145
|
+
.map(
|
|
146
|
+
(field) =>
|
|
147
|
+
`${field.name}: new Fields.SimpleFormField(true)`
|
|
148
|
+
)
|
|
149
|
+
.join(",\n")}
|
|
150
|
+
})`,
|
|
151
|
+
controls: `
|
|
152
|
+
new Controls.Table(${fields_var}.${name}, {
|
|
153
|
+
render_fields: {
|
|
154
|
+
selected: (fctx, name, value) => /* HTML */ \`<input
|
|
155
|
+
type="checkbox"
|
|
156
|
+
name=\${name}
|
|
157
|
+
\${value?.toString() == "true" ? "checked" : ""}
|
|
158
|
+
form="\${fctx.form_id}"
|
|
159
|
+
autocomplete="off"
|
|
160
|
+
/>\`,
|
|
161
|
+
"${referencing_field}": (fctx, name, value) => /* HTML */ \`<input
|
|
162
|
+
type="hidden"
|
|
163
|
+
name=\${name}
|
|
164
|
+
value="\${value}"
|
|
165
|
+
form="\${fctx.form_id}"
|
|
166
|
+
/>\`,
|
|
167
|
+
"${intermediary_field_that_points_there}": (fctx, name, value) => /* HTML */ \`<input
|
|
168
|
+
type="hidden"
|
|
169
|
+
name=\${name}
|
|
170
|
+
value="\${value}"
|
|
171
|
+
form="\${fctx.form_id}"
|
|
172
|
+
/>\`,
|
|
173
|
+
${referencing_fields
|
|
174
|
+
.filter(
|
|
175
|
+
({ type }) => type !== "single-reference"
|
|
176
|
+
)
|
|
177
|
+
.map(
|
|
178
|
+
(field) =>
|
|
179
|
+
`${field.name}: (fctx, name, value) => /* HTML */ \`<input
|
|
180
|
+
name=\${name}
|
|
181
|
+
value="\${value}"
|
|
182
|
+
form="\${fctx.form_id}"
|
|
183
|
+
autocomplete="off"
|
|
184
|
+
/>\``
|
|
185
|
+
)
|
|
186
|
+
.join(",\n")}
|
|
187
|
+
},
|
|
188
|
+
allow_adding: false,
|
|
189
|
+
})`,
|
|
190
|
+
top_level_shared: `export async function ${initial_value_function_name}(
|
|
191
|
+
ctx: Context,
|
|
192
|
+
item: CollectionItem<typeof ${toPascalCase(
|
|
193
|
+
collection_name
|
|
194
|
+
)}>
|
|
195
|
+
) {
|
|
196
|
+
// convert from an array to array-like object
|
|
197
|
+
return Object.fromEntries(
|
|
198
|
+
await Promise.all([
|
|
199
|
+
ctx.$app.collections["${toKebabCase(target)}"].list(ctx.$context).fetch(),
|
|
200
|
+
ctx.$app.collections["${toKebabCase(
|
|
201
|
+
referencing_collection
|
|
202
|
+
)}"].list(ctx.$context).fetch(),
|
|
203
|
+
]).then(([{ items: targets }, { items: links }]) =>
|
|
204
|
+
targets
|
|
205
|
+
.map((target) => {
|
|
206
|
+
return {
|
|
207
|
+
selected: (item.get("${name}") || []).includes(
|
|
208
|
+
target.id
|
|
209
|
+
)
|
|
210
|
+
? "on"
|
|
211
|
+
: "off",
|
|
212
|
+
"${referencing_field}": item.id,
|
|
213
|
+
"${intermediary_field_that_points_there}": target.id,
|
|
214
|
+
${referencing_fields
|
|
215
|
+
.filter(
|
|
216
|
+
({ type }) =>
|
|
217
|
+
type !== "single-reference"
|
|
218
|
+
)
|
|
219
|
+
.map(
|
|
220
|
+
(
|
|
221
|
+
field
|
|
222
|
+
) => `"${field.name}": links
|
|
223
|
+
.find(
|
|
224
|
+
(e) =>
|
|
225
|
+
e.get("${intermediary_field_that_points_there}") == target.id &&
|
|
226
|
+
e.get("${referencing_field}") == item.id
|
|
227
|
+
)
|
|
228
|
+
?.get("rank") || 0,`
|
|
229
|
+
)
|
|
230
|
+
.join(",\n")}
|
|
231
|
+
};
|
|
232
|
+
})
|
|
233
|
+
.entries()
|
|
234
|
+
)
|
|
235
|
+
);
|
|
236
|
+
}`,
|
|
237
|
+
|
|
238
|
+
initial_value_edit: initial_value,
|
|
239
|
+
initial_value_create: initial_value,
|
|
240
|
+
post_edit: post_create(`await this.getID(ctx)`),
|
|
241
|
+
post_create: post_create(`created.id`),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { toPascalCase } from "js-convert-case";
|
|
2
|
+
import {
|
|
3
|
+
FieldHandler,
|
|
4
|
+
FieldHandlerResult,
|
|
5
|
+
} from "../shared-crud-form-fields.js";
|
|
6
|
+
|
|
7
|
+
export const default_handler: FieldHandler<
|
|
8
|
+
Record<string, never>,
|
|
9
|
+
FieldHandlerResult
|
|
10
|
+
> = async (collection_name, { name, is_readonly, is_required }) => {
|
|
11
|
+
const required_s = `${toPascalCase(
|
|
12
|
+
collection_name
|
|
13
|
+
)}.fields.${name}.required`;
|
|
14
|
+
const fields_var = `${toPascalCase(collection_name)}FormFields`;
|
|
15
|
+
const fallback_value_initial = `""`;
|
|
16
|
+
const fallback_value_sealious = `""`;
|
|
17
|
+
|
|
18
|
+
const main_value_initial = `item.get("${name}")`;
|
|
19
|
+
const main_value_sealious = `data["${name}"]`;
|
|
20
|
+
const parsed_value = main_value_initial;
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
is_required,
|
|
24
|
+
name,
|
|
25
|
+
post_create: "",
|
|
26
|
+
imports: { shared: "", edit: "" },
|
|
27
|
+
hide_field: is_readonly,
|
|
28
|
+
field: `${name}: new Fields.CollectionField(${required_s}, ${toPascalCase(
|
|
29
|
+
collection_name
|
|
30
|
+
)}.fields.${name})`,
|
|
31
|
+
hide_control: is_readonly,
|
|
32
|
+
controls: `new Controls.SimpleInput(${fields_var}.${name}, { label: "${name}"})`,
|
|
33
|
+
is_required_expr: `${toPascalCase(
|
|
34
|
+
collection_name
|
|
35
|
+
)}.fields.${name}.required`,
|
|
36
|
+
fields_var,
|
|
37
|
+
top_level_shared: "",
|
|
38
|
+
hide_initial_value_edit: is_readonly,
|
|
39
|
+
initial_value_edit: {
|
|
40
|
+
fallback_value: fallback_value_initial,
|
|
41
|
+
main_value: main_value_initial,
|
|
42
|
+
parsed_value,
|
|
43
|
+
},
|
|
44
|
+
hide_initial_value_create: is_readonly,
|
|
45
|
+
initial_value_create: "",
|
|
46
|
+
pre_edit: "",
|
|
47
|
+
post_edit: "",
|
|
48
|
+
pre_create: "",
|
|
49
|
+
main_value_sealious,
|
|
50
|
+
fallback_value_sealious,
|
|
51
|
+
sealious_value: (result: FieldHandlerResult) => {
|
|
52
|
+
return result.is_required
|
|
53
|
+
? `${result.name}: ${result.main_value_sealious}`
|
|
54
|
+
: `${result.name}: ${result.main_value_sealious} != null ? ${result.main_value_sealious} : ${result.fallback_value_sealious}`;
|
|
55
|
+
},
|
|
56
|
+
hide_sealious_value: is_readonly,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { toPascalCase } from "js-convert-case";
|
|
2
|
+
import {
|
|
3
|
+
FieldHandler,
|
|
4
|
+
FieldHandlerResult,
|
|
5
|
+
} from "../shared-crud-form-fields.js";
|
|
6
|
+
|
|
7
|
+
export const enum_handler: FieldHandler<
|
|
8
|
+
FieldHandlerResult,
|
|
9
|
+
Partial<FieldHandlerResult>
|
|
10
|
+
> = async (collection_name, { name }, _, { fields_var }) => ({
|
|
11
|
+
// this assertion and import is only necessary because required fields
|
|
12
|
+
// in Sealious lose the type information. Once this is fixed, we can remove this import here
|
|
13
|
+
imports: { shared: `import type { FieldTypes } from "sealious";` },
|
|
14
|
+
field: `${name}: new Fields.PickFromListField(
|
|
15
|
+
${toPascalCase(collection_name)}.fields["${name}"].required,
|
|
16
|
+
async (ctx) =>
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
18
|
+
(${toPascalCase(collection_name)}.fields["${name}"])
|
|
19
|
+
.getAllowedValues(ctx.$app)
|
|
20
|
+
.map((value) => ({ value, label: value }))
|
|
21
|
+
)`,
|
|
22
|
+
controls: `new Controls.Dropdown(${fields_var}.${name}, { label: "${name}"})`,
|
|
23
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { toCamelCase } from "js-convert-case";
|
|
2
|
+
import {
|
|
3
|
+
FieldHandler,
|
|
4
|
+
FieldHandlerResult,
|
|
5
|
+
} from "../shared-crud-form-fields.js";
|
|
6
|
+
|
|
7
|
+
export const file_handler: FieldHandler<
|
|
8
|
+
FieldHandlerResult,
|
|
9
|
+
Partial<FieldHandlerResult>
|
|
10
|
+
> = async (_, { name, is_required }, __, { is_required_expr }) => {
|
|
11
|
+
const tempname = toCamelCase("prepared-" + name);
|
|
12
|
+
return {
|
|
13
|
+
field: `${name}: new Fields.File(${is_required_expr}, TheFileManager)`,
|
|
14
|
+
initial_value_edit: `${name}: {old: item.get("${name}")}`,
|
|
15
|
+
pre_edit: `const ${tempname} = data.${name}.new || data.${name}.old;
|
|
16
|
+
if(!${tempname}){
|
|
17
|
+
throw new Error("Missing field: ${name}")
|
|
18
|
+
}`,
|
|
19
|
+
fallback_value_sealious: "null",
|
|
20
|
+
...(is_required ? { main_value_sealious: tempname } : {}),
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { toCamelCase } from "js-convert-case";
|
|
2
|
+
import {
|
|
3
|
+
FieldHandler,
|
|
4
|
+
FieldHandlerResult,
|
|
5
|
+
} from "../shared-crud-form-fields.js";
|
|
6
|
+
|
|
7
|
+
export const image_handler: FieldHandler<
|
|
8
|
+
FieldHandlerResult,
|
|
9
|
+
Partial<FieldHandlerResult>
|
|
10
|
+
> = async (
|
|
11
|
+
_,
|
|
12
|
+
{ name, is_required },
|
|
13
|
+
importPath,
|
|
14
|
+
{ is_required_expr, fields_var }
|
|
15
|
+
) => {
|
|
16
|
+
const tempname = toCamelCase("prepared-" + name);
|
|
17
|
+
return {
|
|
18
|
+
imports: {
|
|
19
|
+
shared: `import { imageRouter } from "${importPath(
|
|
20
|
+
"src/back/image-router.js"
|
|
21
|
+
)}";
|
|
22
|
+
import { TheFileManager } from "${importPath(
|
|
23
|
+
"src/back/file-manager.js"
|
|
24
|
+
)}";`,
|
|
25
|
+
},
|
|
26
|
+
field: `${name}: new Fields.File(${is_required_expr}, TheFileManager)`,
|
|
27
|
+
controls: `new Controls.Photo(${fields_var}.${name}, imageRouter, {
|
|
28
|
+
label: "${name}",
|
|
29
|
+
})`,
|
|
30
|
+
initial_value_edit: `{old: item.get("${name}")}`,
|
|
31
|
+
pre_edit:
|
|
32
|
+
`const ${tempname} = data.${name}.new || data.${name}.old ` +
|
|
33
|
+
(is_required
|
|
34
|
+
? `; if(!${tempname}){
|
|
35
|
+
throw new Error("Missing field: ${name}")
|
|
36
|
+
}`
|
|
37
|
+
: " || undefined;"),
|
|
38
|
+
pre_create: `const ${tempname} = data.${name}.new || data.${name}.old;
|
|
39
|
+
if(!${tempname}){
|
|
40
|
+
throw new Error("Missing field: ${name}")
|
|
41
|
+
}`,
|
|
42
|
+
fallback_value_sealious: "null",
|
|
43
|
+
sealious_value: () => `${name}: ${tempname}`,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FieldHandler,
|
|
3
|
+
FieldHandlerResult,
|
|
4
|
+
} from "../shared-crud-form-fields.js";
|
|
5
|
+
|
|
6
|
+
export const int_handler: FieldHandler<
|
|
7
|
+
FieldHandlerResult,
|
|
8
|
+
Partial<FieldHandlerResult>
|
|
9
|
+
> = async (_, { name }, __, { is_required_expr, fields_var }) => ({
|
|
10
|
+
field: `${name}: new Fields.NumberField(${is_required_expr}, 0)`,
|
|
11
|
+
controls: `new Controls.Number(${fields_var}.${name}, { label: "${name}" })`,
|
|
12
|
+
initial_value_edit: {
|
|
13
|
+
parsed_value: `String(item.get("${name}"))`,
|
|
14
|
+
},
|
|
15
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FieldHandler,
|
|
3
|
+
FieldHandlerResult,
|
|
4
|
+
} from "../shared-crud-form-fields.js";
|
|
5
|
+
|
|
6
|
+
export const jdd_handler: FieldHandler<
|
|
7
|
+
FieldHandlerResult,
|
|
8
|
+
Partial<FieldHandlerResult>
|
|
9
|
+
> = async (_, { name }) => ({
|
|
10
|
+
field: ``,
|
|
11
|
+
initial_value_edit: "",
|
|
12
|
+
controls: "",
|
|
13
|
+
sealious_value: () => `${name}: []`,
|
|
14
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FieldHandler,
|
|
3
|
+
FieldHandlerResult,
|
|
4
|
+
} from "../shared-crud-form-fields.js";
|
|
5
|
+
|
|
6
|
+
export const single_reference_handler: FieldHandler<
|
|
7
|
+
FieldHandlerResult,
|
|
8
|
+
Partial<FieldHandlerResult>
|
|
9
|
+
> = async (_, { name }, __, { fields_var }) => {
|
|
10
|
+
return {
|
|
11
|
+
controls: `new Controls.SingleReferenceDropdown(${fields_var}.${name}, {
|
|
12
|
+
label: "${name}",
|
|
13
|
+
getLabel: (item)=>item.id
|
|
14
|
+
})`,
|
|
15
|
+
};
|
|
16
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FieldHandler,
|
|
3
|
+
FieldHandlerResult,
|
|
4
|
+
} from "../shared-crud-form-fields.js";
|
|
5
|
+
|
|
6
|
+
export const structured_array_handler: FieldHandler<
|
|
7
|
+
FieldHandlerResult,
|
|
8
|
+
Partial<FieldHandlerResult>
|
|
9
|
+
> = async (_, { name, subfields }, __, { fields_var }) => ({
|
|
10
|
+
field: `${name}: new Fields.Table({
|
|
11
|
+
${subfields
|
|
12
|
+
.map(
|
|
13
|
+
(subfield) =>
|
|
14
|
+
`${subfield.name}: new Fields.SimpleFormField(true)`
|
|
15
|
+
)
|
|
16
|
+
.join(",\n")}
|
|
17
|
+
})`,
|
|
18
|
+
controls: `new Controls.Table(${fields_var}.${name}, {
|
|
19
|
+
label: "${name}",
|
|
20
|
+
render_fields: {
|
|
21
|
+
${subfields
|
|
22
|
+
.map(
|
|
23
|
+
({
|
|
24
|
+
name,
|
|
25
|
+
}) => `"${name}": (fctx, name, value) => /* HTML */ \`<input
|
|
26
|
+
name="\${name}"
|
|
27
|
+
value="\${value}"
|
|
28
|
+
form="\${fctx.form_id}"
|
|
29
|
+
autocomplete="off"
|
|
30
|
+
/>\``
|
|
31
|
+
)
|
|
32
|
+
.join(",\n")}
|
|
33
|
+
|
|
34
|
+
},
|
|
35
|
+
allow_adding: true,
|
|
36
|
+
allow_removing: true,
|
|
37
|
+
make_new_row: async () => ({
|
|
38
|
+
${subfields.map(({ name }) => `"${name}": ""`).join(",\n")}
|
|
39
|
+
}),
|
|
40
|
+
})`,
|
|
41
|
+
fallback_value_sealious: `[]`,
|
|
42
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { toKebabCase, toPascalCase } from "js-convert-case";
|
|
2
|
+
import { curryImportPath } from "../../utils/import-path.js";
|
|
3
|
+
import { formatWithPrettier } from "../../utils/prettier.js";
|
|
4
|
+
|
|
5
|
+
export function itemDeleteTemplate(
|
|
6
|
+
delete_action_name: string,
|
|
7
|
+
collection_name: string,
|
|
8
|
+
newfilefullpath: string,
|
|
9
|
+
list_action_name: string
|
|
10
|
+
): Promise<string> {
|
|
11
|
+
const importPath = curryImportPath(newfilefullpath);
|
|
12
|
+
const result = `import type { Context } from "koa";
|
|
13
|
+
import { Mountable } from "@sealcode/sealgen";
|
|
14
|
+
import type Router from "@koa/router";
|
|
15
|
+
|
|
16
|
+
import { ${toPascalCase(collection_name)} } from "${importPath(
|
|
17
|
+
"src/back/collections/collections.js"
|
|
18
|
+
)}";
|
|
19
|
+
|
|
20
|
+
import { ${list_action_name}URL } from "${importPath(
|
|
21
|
+
"src/back/routes/urls.js"
|
|
22
|
+
)}";
|
|
23
|
+
|
|
24
|
+
export const actionName = "${delete_action_name}";
|
|
25
|
+
|
|
26
|
+
export default new (class ${delete_action_name}Redirect extends Mountable {
|
|
27
|
+
canAccess = async (ctx: Context) => {
|
|
28
|
+
const policy = ${toPascalCase(collection_name)}.getPolicy("edit");
|
|
29
|
+
const response = await policy.check(ctx.$context);
|
|
30
|
+
return { canAccess: response?.allowed || false, message: response?.reason || "" };
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
mount(router: Router, path: string) {
|
|
34
|
+
router.get(path, async (ctx) => {
|
|
35
|
+
await ctx.$app.collections["${toKebabCase(
|
|
36
|
+
collection_name
|
|
37
|
+
)}"].removeByID(ctx.$context, ctx.params.id);
|
|
38
|
+
ctx.status = 302;
|
|
39
|
+
ctx.redirect(${list_action_name}URL);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
})();
|
|
43
|
+
`;
|
|
44
|
+
return formatWithPrettier(result);
|
|
45
|
+
}
|