@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
|
@@ -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
|
|
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
|
-
|
|
47
|
+
let { parsed: rows } = await this.table_field.getValue(
|
|
23
48
|
fctx.ctx,
|
|
24
49
|
fctx.data
|
|
25
50
|
);
|
|
26
51
|
if (!rows) {
|
|
27
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
this.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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 {
|
|
@@ -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]:
|
|
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
|
-
|
|
25
|
+
false,
|
|
12
26
|
TableFieldParsed<F>
|
|
13
27
|
> {
|
|
14
28
|
constructor(public columns: F) {
|
|
15
|
-
super(
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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(
|
package/src/templates/form.ts
CHANGED
|
@@ -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
|
|
41
|
-
import { Form,
|
|
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
|
-
|
|
65
|
-
async canAccess(_: Context) {
|
|
66
|
-
return { canAccess: true, message: "" };
|
|
67
|
-
}
|
|
79
|
+
${params.get_initial_values}
|
|
68
80
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
81
|
+
${params.onsubmit}
|
|
82
|
+
|
|
83
|
+
${params.can_access}
|
|
73
84
|
|
|
74
|
-
|
|
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());
|
package/src/templates/list.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
};
|