@sealcode/sealgen 0.8.47 → 0.8.48
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/@types/index.d.ts +1 -0
- package/@types/page/stateful-page.d.ts +34 -0
- package/@types/templates/stateful-page.d.ts +1 -0
- package/@types/utils/base64.d.ts +2 -0
- package/@types/utils/utils.d.ts +3 -0
- package/lib/add-route.js +3 -0
- package/lib/cli.js +0 -0
- package/lib/generate-routes.js +2 -1
- package/lib/index.js +1 -0
- package/lib/page/stateful-page.js +117 -0
- package/lib/templates/stateful-page.js +66 -0
- package/lib/utils/base64.js +12 -0
- package/lib/utils/utils.js +6 -1
- package/package.json +2 -1
- package/.arcconfig +0 -12
- package/.arclint +0 -18
- package/.eslintrc.js +0 -38
- package/.mocharc.js +0 -10
- package/.nycrc +0 -6
- package/.prettierrc +0 -21
- package/jenkins.sanity.sh +0 -3
- package/lib/add-collection.js.map +0 -1
- package/lib/add-route.js.map +0 -1
- package/lib/add-route.test.js.map +0 -1
- package/lib/build.js.map +0 -1
- package/lib/cli.js.map +0 -1
- package/lib/components/components.js.map +0 -1
- package/lib/components/tabs.js.map +0 -1
- package/lib/components/toggle-modal.js.map +0 -1
- package/lib/example.test.js.map +0 -1
- package/lib/find-scss-includes.js.map +0 -1
- package/lib/forms/controls/array.js.map +0 -1
- package/lib/forms/controls/autocomplete.js.map +0 -1
- package/lib/forms/controls/big-radio-group.js.map +0 -1
- package/lib/forms/controls/checkbox-group.js.map +0 -1
- package/lib/forms/controls/checkbox.js.map +0 -1
- package/lib/forms/controls/checkboxed-list-input.js.map +0 -1
- package/lib/forms/controls/collapsible-checkbox-group.js.map +0 -1
- package/lib/forms/controls/collapsible-field-group.js.map +0 -1
- package/lib/forms/controls/controls.js.map +0 -1
- package/lib/forms/controls/derived-frame.js.map +0 -1
- package/lib/forms/controls/derived.js.map +0 -1
- package/lib/forms/controls/dropdown.js.map +0 -1
- package/lib/forms/controls/editable-collection-subset.js.map +0 -1
- package/lib/forms/controls/field-group.js.map +0 -1
- package/lib/forms/controls/file.js.map +0 -1
- package/lib/forms/controls/form-control.js.map +0 -1
- package/lib/forms/controls/form-field-control.js.map +0 -1
- package/lib/forms/controls/form-header.js.map +0 -1
- package/lib/forms/controls/form-paragraph.js.map +0 -1
- package/lib/forms/controls/frame.js.map +0 -1
- package/lib/forms/controls/hidden-input.js.map +0 -1
- package/lib/forms/controls/html.js.map +0 -1
- package/lib/forms/controls/multiple-files.js.map +0 -1
- package/lib/forms/controls/multiple-photos.js.map +0 -1
- package/lib/forms/controls/number.js.map +0 -1
- package/lib/forms/controls/photo.js.map +0 -1
- package/lib/forms/controls/radio.js.map +0 -1
- package/lib/forms/controls/reverse-single-reference.js.map +0 -1
- package/lib/forms/controls/simple-input.js.map +0 -1
- package/lib/forms/controls/textarea.js.map +0 -1
- package/lib/forms/controls/tickable.js.map +0 -1
- package/lib/forms/fields/boolean.js.map +0 -1
- package/lib/forms/fields/boolean.test.js.map +0 -1
- package/lib/forms/fields/checkboxed-list.js.map +0 -1
- package/lib/forms/fields/collection-field.js.map +0 -1
- package/lib/forms/fields/email.js.map +0 -1
- package/lib/forms/fields/email.test.js.map +0 -1
- package/lib/forms/fields/enum-multiple.js.map +0 -1
- package/lib/forms/fields/enum-multiple.test.js.map +0 -1
- package/lib/forms/fields/enum.js.map +0 -1
- package/lib/forms/fields/enum.test.js.map +0 -1
- package/lib/forms/fields/existing-sealious-value.js.map +0 -1
- package/lib/forms/fields/field.js.map +0 -1
- package/lib/forms/fields/file.js.map +0 -1
- package/lib/forms/fields/multiple-files.js.map +0 -1
- package/lib/forms/fields/number.js.map +0 -1
- package/lib/forms/fields/number.test.js.map +0 -1
- package/lib/forms/fields/pick-from-list.js.map +0 -1
- package/lib/forms/fields/pick-from-list.test.js.map +0 -1
- package/lib/forms/fields/proxy-field.js.map +0 -1
- package/lib/forms/fields/regexp.js.map +0 -1
- package/lib/forms/fields/regexp.test.js.map +0 -1
- package/lib/forms/fields/reverse-single-reference.js.map +0 -1
- package/lib/forms/fields/simple-form-field.js.map +0 -1
- package/lib/forms/fields/structured-array.js.map +0 -1
- package/lib/forms/form.js.map +0 -1
- package/lib/forms/form.test.js.map +0 -1
- package/lib/forms/multiform.js.map +0 -1
- package/lib/forms/multiform.test.js.map +0 -1
- package/lib/generate-collections.js.map +0 -1
- package/lib/generate-routes.js.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/make-env.js.map +0 -1
- package/lib/make-hidden-inputs.js.map +0 -1
- package/lib/mount.js.map +0 -1
- package/lib/page/access-control.js.map +0 -1
- package/lib/page/list.js.map +0 -1
- package/lib/page/mountable-with-fields.js.map +0 -1
- package/lib/page/mountable.js.map +0 -1
- package/lib/page/page.js.map +0 -1
- package/lib/page/props-parser.js.map +0 -1
- package/lib/run.js.map +0 -1
- package/lib/sanitize.js.map +0 -1
- package/lib/templates/collection.js.map +0 -1
- package/lib/templates/form.js.map +0 -1
- package/lib/templates/list.js.map +0 -1
- package/lib/templates/multiform.js.map +0 -1
- package/lib/templates/page.js.map +0 -1
- package/lib/templates/post.js.map +0 -1
- package/lib/templates/redirect.js.map +0 -1
- package/lib/templates/route-test.js.map +0 -1
- package/lib/test_utils/locator_is_visible.js.map +0 -1
- package/lib/utils/assert-throws.js.map +0 -1
- package/lib/utils/browser-creator.js.map +0 -1
- package/lib/utils/escape-url-params.js.map +0 -1
- package/lib/utils/exec.js.map +0 -1
- package/lib/utils/format-external-url.js.map +0 -1
- package/lib/utils/import-path.js.map +0 -1
- package/lib/utils/input-wrapper.js.map +0 -1
- package/lib/utils/list-collections.js.map +0 -1
- package/lib/utils/make-slug.js.map +0 -1
- package/lib/utils/natural-numbers.js.map +0 -1
- package/lib/utils/prettier.js.map +0 -1
- package/lib/utils/question.js.map +0 -1
- package/lib/utils/render-attributes.js.map +0 -1
- package/lib/utils/sleep.js.map +0 -1
- package/lib/utils/translate.js.map +0 -1
- package/lib/utils/url-with-new-params.js.map +0 -1
- package/lib/utils/utils.js.map +0 -1
- package/lib/utils/walk.js.map +0 -1
- package/mocha_root_hooks.js +0 -6
- package/src/add-collection.ts +0 -31
- package/src/add-route.test.ts +0 -58
- package/src/add-route.ts +0 -86
- package/src/build.ts +0 -94
- package/src/cli.ts +0 -36
- package/src/components/components.ts +0 -2
- package/src/components/tabs.ts +0 -95
- package/src/components/toggle-modal.ts +0 -52
- package/src/example.test.ts +0 -3
- package/src/find-scss-includes.ts +0 -32
- package/src/forms/controls/array.ts +0 -226
- package/src/forms/controls/autocomplete.ts +0 -33
- package/src/forms/controls/big-radio-group.ts +0 -48
- package/src/forms/controls/checkbox-group.ts +0 -71
- package/src/forms/controls/checkbox.ts +0 -34
- package/src/forms/controls/checkboxed-list-input.ts +0 -50
- package/src/forms/controls/collapsible-checkbox-group.ts +0 -28
- package/src/forms/controls/collapsible-field-group.ts +0 -94
- package/src/forms/controls/controls.ts +0 -30
- package/src/forms/controls/derived-frame.ts +0 -91
- package/src/forms/controls/derived.ts +0 -108
- package/src/forms/controls/dropdown.ts +0 -87
- package/src/forms/controls/editable-collection-subset.ts +0 -97
- package/src/forms/controls/field-group.ts +0 -69
- package/src/forms/controls/file.ts +0 -47
- package/src/forms/controls/form-control.ts +0 -24
- package/src/forms/controls/form-field-control.ts +0 -22
- package/src/forms/controls/form-header.ts +0 -19
- package/src/forms/controls/form-paragraph.ts +0 -11
- package/src/forms/controls/frame.ts +0 -26
- package/src/forms/controls/hidden-input.ts +0 -22
- package/src/forms/controls/html.ts +0 -38
- package/src/forms/controls/multiple-files.ts +0 -256
- package/src/forms/controls/multiple-photos.ts +0 -44
- package/src/forms/controls/number.ts +0 -38
- package/src/forms/controls/photo.ts +0 -74
- package/src/forms/controls/radio.ts +0 -43
- package/src/forms/controls/reverse-single-reference.ts +0 -50
- package/src/forms/controls/simple-input.ts +0 -148
- package/src/forms/controls/textarea.ts +0 -60
- package/src/forms/controls/tickable.ts +0 -127
- package/src/forms/fields/boolean.test.ts +0 -36
- package/src/forms/fields/boolean.ts +0 -63
- package/src/forms/fields/checkboxed-list.ts +0 -66
- package/src/forms/fields/collection-field.ts +0 -62
- package/src/forms/fields/email.test.ts +0 -24
- package/src/forms/fields/email.ts +0 -20
- package/src/forms/fields/enum-multiple.test.ts +0 -31
- package/src/forms/fields/enum-multiple.ts +0 -62
- package/src/forms/fields/enum.test.ts +0 -25
- package/src/forms/fields/enum.ts +0 -56
- package/src/forms/fields/existing-sealious-value.ts +0 -22
- package/src/forms/fields/field.ts +0 -172
- package/src/forms/fields/file.ts +0 -89
- package/src/forms/fields/multiple-files.ts +0 -20
- package/src/forms/fields/number.test.ts +0 -34
- package/src/forms/fields/number.ts +0 -70
- package/src/forms/fields/pick-from-list.test.ts +0 -19
- package/src/forms/fields/pick-from-list.ts +0 -52
- package/src/forms/fields/proxy-field.ts +0 -24
- package/src/forms/fields/regexp.test.ts +0 -25
- package/src/forms/fields/regexp.ts +0 -53
- package/src/forms/fields/reverse-single-reference.ts +0 -20
- package/src/forms/fields/simple-form-field.ts +0 -30
- package/src/forms/fields/structured-array.ts +0 -27
- package/src/forms/form.test.ts +0 -351
- package/src/forms/form.ts +0 -294
- package/src/forms/forms.scss +0 -85
- package/src/forms/multiform.test.ts +0 -540
- package/src/forms/multiform.ts +0 -236
- package/src/generate-collections.ts +0 -77
- package/src/generate-routes.ts +0 -120
- package/src/index.ts +0 -24
- package/src/make-env.ts +0 -15
- package/src/make-hidden-inputs.ts +0 -24
- package/src/mount.ts +0 -85
- package/src/page/access-control.ts +0 -18
- package/src/page/list.ts +0 -240
- package/src/page/mountable-with-fields.ts +0 -158
- package/src/page/mountable.ts +0 -13
- package/src/page/page.ts +0 -46
- package/src/page/props-parser.ts +0 -38
- package/src/run.ts +0 -0
- package/src/sanitize.ts +0 -9
- package/src/templates/collection.ts +0 -11
- package/src/templates/form.ts +0 -60
- package/src/templates/list.ts +0 -75
- package/src/templates/multiform.ts +0 -58
- package/src/templates/page.ts +0 -25
- package/src/templates/post.ts +0 -21
- package/src/templates/redirect.ts +0 -22
- package/src/templates/route-test.ts +0 -27
- package/src/test_utils/locator_is_visible.ts +0 -17
- package/src/utils/assert-throws.ts +0 -18
- package/src/utils/browser-creator.js +0 -10
- package/src/utils/escape-url-params.ts +0 -14
- package/src/utils/exec.ts +0 -30
- package/src/utils/format-external-url.ts +0 -13
- package/src/utils/import-path.ts +0 -25
- package/src/utils/input-wrapper.ts +0 -21
- package/src/utils/list-collections.ts +0 -16
- package/src/utils/make-slug.ts +0 -5
- package/src/utils/natural-numbers.ts +0 -5
- package/src/utils/prettier.ts +0 -13
- package/src/utils/question.ts +0 -23
- package/src/utils/render-attributes.ts +0 -17
- package/src/utils/sleep.ts +0 -3
- package/src/utils/translate.ts +0 -13
- package/src/utils/url-with-new-params.ts +0 -9
- package/src/utils/utils.ts +0 -14
- package/src/utils/walk.ts +0 -20
package/src/page/list.ts
DELETED
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
import { Context } from "koa";
|
|
2
|
-
import {
|
|
3
|
-
Collection,
|
|
4
|
-
CollectionItem,
|
|
5
|
-
ItemList,
|
|
6
|
-
Context as SealiousContext,
|
|
7
|
-
CollectionInput,
|
|
8
|
-
} from "sealious";
|
|
9
|
-
|
|
10
|
-
import { Templatable, tempstream, FlatTemplatable } from "tempstream";
|
|
11
|
-
import { peopleWhoCan } from "./access-control";
|
|
12
|
-
import { Page } from "./page";
|
|
13
|
-
import { ShapeToType } from "@sealcode/ts-predicates";
|
|
14
|
-
import { FormControl } from "../forms/controls/controls";
|
|
15
|
-
import { FormField, NumberField } from "../forms/fields/field";
|
|
16
|
-
import { naturalNumbers, UrlWithNewParams } from "../utils/utils";
|
|
17
|
-
import { FormDataValue } from "..";
|
|
18
|
-
import { makeHiddenInputs } from "../make-hidden-inputs";
|
|
19
|
-
|
|
20
|
-
export const BasePagePropsShape = <const>{};
|
|
21
|
-
export type BasePageProps = ShapeToType<typeof BasePagePropsShape>;
|
|
22
|
-
|
|
23
|
-
export const BaseListPageFields = <const>{
|
|
24
|
-
page: new NumberField(false, 1),
|
|
25
|
-
itemsPerPage: new NumberField(false, 12),
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export abstract class ListPage<
|
|
29
|
-
ItemType,
|
|
30
|
-
F extends typeof BaseListPageFields
|
|
31
|
-
> extends Page<F> {
|
|
32
|
-
abstract getItems(
|
|
33
|
-
ctx: Context,
|
|
34
|
-
page: number,
|
|
35
|
-
itemsPerPage: number,
|
|
36
|
-
values: Record<string, FormDataValue>
|
|
37
|
-
): Promise<ItemType[]>;
|
|
38
|
-
|
|
39
|
-
abstract getTotalPages(
|
|
40
|
-
ctx: Context,
|
|
41
|
-
itemsPerPage: number,
|
|
42
|
-
values: Record<string, FormDataValue>
|
|
43
|
-
): Promise<number>;
|
|
44
|
-
|
|
45
|
-
abstract renderItem(
|
|
46
|
-
ctx: Context,
|
|
47
|
-
item: ItemType,
|
|
48
|
-
index: number
|
|
49
|
-
): Promise<FlatTemplatable>;
|
|
50
|
-
|
|
51
|
-
filterFields: Record<string, FormField> = {};
|
|
52
|
-
filterControls: FormControl[] = [];
|
|
53
|
-
|
|
54
|
-
init(): void {
|
|
55
|
-
super.init();
|
|
56
|
-
for (const [fieldname, field] of Object.entries(this.filterFields)) {
|
|
57
|
-
field.init(fieldname);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
renderListContainer(_: Context, content: Templatable): FlatTemplatable {
|
|
62
|
-
return tempstream`<div>${content}</div>`;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async getPaginationConfig(ctx: Context) {
|
|
66
|
-
const values = await this.extractRawValues(ctx);
|
|
67
|
-
const { parsed: page } = await this.fields.page.getValue(ctx, values);
|
|
68
|
-
const { parsed: itemsPerPage } =
|
|
69
|
-
await this.fields.itemsPerPage.getValue(ctx, values);
|
|
70
|
-
return { page, itemsPerPage };
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async renderItems(
|
|
74
|
-
ctx: Context,
|
|
75
|
-
values?: Record<string, FormDataValue>,
|
|
76
|
-
items?: ItemType[]
|
|
77
|
-
): Promise<FlatTemplatable> {
|
|
78
|
-
if (!values) {
|
|
79
|
-
values = await this.extractRawValues(ctx);
|
|
80
|
-
}
|
|
81
|
-
const { itemsPerPage, page } = await this.getPaginationConfig(ctx);
|
|
82
|
-
const items_promise = this.getItems(ctx, page, itemsPerPage, values);
|
|
83
|
-
return tempstream`${(items
|
|
84
|
-
? Promise.resolve(items)
|
|
85
|
-
: items_promise
|
|
86
|
-
).then((items) =>
|
|
87
|
-
items.map((item, index) => this.renderItem(ctx, item, index))
|
|
88
|
-
)}`;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async render(ctx: Context): Promise<FlatTemplatable> {
|
|
92
|
-
const values = await this.extractRawValues(ctx);
|
|
93
|
-
|
|
94
|
-
return tempstream`${this.renderPagination(ctx, values)}
|
|
95
|
-
${this.renderFilters(ctx)}
|
|
96
|
-
${this.renderListContainer(ctx, this.renderItems(ctx, values))}`;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async renderPagination(
|
|
100
|
-
ctx: Context,
|
|
101
|
-
values: Record<string, FormDataValue>
|
|
102
|
-
): Promise<FlatTemplatable> {
|
|
103
|
-
const { itemsPerPage, page } = await this.getPaginationConfig(ctx);
|
|
104
|
-
const totalIems = await this.getTotalPages(ctx, itemsPerPage, values);
|
|
105
|
-
|
|
106
|
-
return tempstream/* HTML */ `<center>
|
|
107
|
-
${page > 1 ? this.renderPageButton(ctx, 1, "Pierwsza strona") : ""}
|
|
108
|
-
${page > 1
|
|
109
|
-
? this.renderPageButton(ctx, page - 1, "Poprzednia strona")
|
|
110
|
-
: ""}
|
|
111
|
-
|
|
112
|
-
<select onchange="if (this.value) Turbo.visit(this.value)">
|
|
113
|
-
${Array.from(naturalNumbers(1, totalIems)).map(
|
|
114
|
-
(n) => /* HTML */ `<option
|
|
115
|
-
value="${UrlWithNewParams(
|
|
116
|
-
ctx,
|
|
117
|
-
//eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
118
|
-
this.propsParser.overwriteProp(ctx, {
|
|
119
|
-
page: n,
|
|
120
|
-
} as Partial<Record<string, unknown>>)
|
|
121
|
-
)}"
|
|
122
|
-
${page === n ? "selected" : ""}
|
|
123
|
-
>
|
|
124
|
-
${n}
|
|
125
|
-
</option>`
|
|
126
|
-
)}
|
|
127
|
-
</select>
|
|
128
|
-
${page < totalIems
|
|
129
|
-
? this.renderPageButton(ctx, page + 1, "Następna strona")
|
|
130
|
-
: ""}
|
|
131
|
-
${page < totalIems
|
|
132
|
-
? this.renderPageButton(ctx, totalIems, "Ostatnia strona")
|
|
133
|
-
: ""}
|
|
134
|
-
</center>`;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
private renderPageButton(ctx: Context, page: number, text: string) {
|
|
138
|
-
return /* HTML */ `<a
|
|
139
|
-
href="${UrlWithNewParams(
|
|
140
|
-
ctx,
|
|
141
|
-
//eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
142
|
-
this.propsParser.overwriteProp(ctx, {
|
|
143
|
-
page,
|
|
144
|
-
} as Partial<Record<string, unknown>>)
|
|
145
|
-
)}"
|
|
146
|
-
>${text}</a
|
|
147
|
-
>`;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async getFilterValues(ctx: Context): Promise<Record<string, unknown>> {
|
|
151
|
-
const filter = {} as Record<string, unknown>;
|
|
152
|
-
const raw_values = await this.extractRawValues(ctx);
|
|
153
|
-
for (const [fieldname, field] of Object.entries(this.filterFields)) {
|
|
154
|
-
// eslint-disable-next-line no-await-in-loop
|
|
155
|
-
const { parsed } = await field.getValue(ctx, raw_values);
|
|
156
|
-
filter[fieldname] = field.mapToFilter(parsed);
|
|
157
|
-
}
|
|
158
|
-
return filter;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async renderFilters(ctx: Context): Promise<FlatTemplatable> {
|
|
162
|
-
const values = await this.extractRawValues(ctx);
|
|
163
|
-
return tempstream/* HTML */ `<form method="GET">
|
|
164
|
-
${makeHiddenInputs(ctx, this.fields, values, [
|
|
165
|
-
"page",
|
|
166
|
-
...Object.values(this.filterFields).map((f) => f.name),
|
|
167
|
-
])}
|
|
168
|
-
${this.filterControls.map((control) => {
|
|
169
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
170
|
-
return control.render(
|
|
171
|
-
this.makeFormControlContext(
|
|
172
|
-
ctx,
|
|
173
|
-
{ raw_values: values, messages: [] },
|
|
174
|
-
false
|
|
175
|
-
)
|
|
176
|
-
);
|
|
177
|
-
})}
|
|
178
|
-
</form>`;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export abstract class SealiousItemListPage<
|
|
183
|
-
C extends Collection,
|
|
184
|
-
F extends typeof BaseListPageFields
|
|
185
|
-
> extends ListPage<CollectionItem<C>, F> {
|
|
186
|
-
constructor(public collection: C) {
|
|
187
|
-
super();
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async getContextForList(ctx: Context): Promise<SealiousContext> {
|
|
191
|
-
return ctx.$context;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
async getTotalPages(ctx: Context, itemsPerPage: number): Promise<number> {
|
|
195
|
-
const query = this.collection.list(await this.getContextForList(ctx));
|
|
196
|
-
await this.addFilter(ctx, query);
|
|
197
|
-
const { items } = await query.fetch();
|
|
198
|
-
return Math.ceil(items.length / itemsPerPage);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
protected async addFilter(ctx: Context, list: ItemList<C>): Promise<void> {
|
|
202
|
-
const filters = await this.getFilterValues(ctx);
|
|
203
|
-
|
|
204
|
-
for (const key in filters) {
|
|
205
|
-
if (filters[key] === undefined || filters[key] == "") {
|
|
206
|
-
delete filters[key];
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
if (Object.keys(filters).length) {
|
|
210
|
-
list.filter(filters as Partial<CollectionInput<C>>);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
async makeQuery(ctx: Context): Promise<ItemList<C>> {
|
|
215
|
-
return this.collection.list(await this.getContextForList(ctx));
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
async getItems(
|
|
219
|
-
ctx: Context,
|
|
220
|
-
page: number,
|
|
221
|
-
itemsPerPage: number
|
|
222
|
-
): Promise<CollectionItem<C>[]> {
|
|
223
|
-
const query = await this.makeQuery(ctx);
|
|
224
|
-
await this.addFilter(ctx, query);
|
|
225
|
-
const { items } = await query
|
|
226
|
-
.paginate({ items: itemsPerPage, page })
|
|
227
|
-
.fetch();
|
|
228
|
-
return items;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
async renderItem(
|
|
232
|
-
_: Context,
|
|
233
|
-
item: CollectionItem<C>,
|
|
234
|
-
index: number
|
|
235
|
-
): Promise<FlatTemplatable> {
|
|
236
|
-
return `<div>${item.id}</div>`;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
canAccess = peopleWhoCan("list", this.collection);
|
|
240
|
-
}
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import { ShapeToType } from "@sealcode/ts-predicates";
|
|
2
|
-
import { Context } from "koa";
|
|
3
|
-
import { FlatTemplatable, tempstream } from "tempstream";
|
|
4
|
-
import {
|
|
5
|
-
FormControl,
|
|
6
|
-
FormControlContext,
|
|
7
|
-
} from "../forms/controls/form-control";
|
|
8
|
-
import { FieldsToShape, FormField } from "../forms/fields/field";
|
|
9
|
-
import { FormData, FormDataValue, FormMessage } from "../forms/form";
|
|
10
|
-
import { Mountable } from "./mountable";
|
|
11
|
-
|
|
12
|
-
export type PageErrorMessage = { type: "access" | "internal"; message: string };
|
|
13
|
-
|
|
14
|
-
export type Fields = Record<string, FormField<boolean, any>>;
|
|
15
|
-
|
|
16
|
-
export abstract class MountableWithFields<
|
|
17
|
-
F extends Fields = Fields
|
|
18
|
-
> extends Mountable {
|
|
19
|
-
fields: F;
|
|
20
|
-
field_names_prefix = ""; // useful for multiform, where many forms are merged into one and field assignment is made using the prefix
|
|
21
|
-
form_id = ""; // all fields within this mountable will be tied to form of this id
|
|
22
|
-
abstract controls: FormControl[];
|
|
23
|
-
|
|
24
|
-
constructor() {
|
|
25
|
-
super();
|
|
26
|
-
if (!this.fields) this.fields = {} as F;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
init(): void {
|
|
30
|
-
for (const [name, field] of Object.entries(this.fields)) {
|
|
31
|
-
field.init(name);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
makeFormControlContext(
|
|
36
|
-
ctx: Context,
|
|
37
|
-
data: FormData,
|
|
38
|
-
validate: boolean,
|
|
39
|
-
field_name_prefix = this.field_names_prefix,
|
|
40
|
-
form_id = this.form_id
|
|
41
|
-
) {
|
|
42
|
-
return new FormControlContext(
|
|
43
|
-
ctx,
|
|
44
|
-
data.raw_values,
|
|
45
|
-
data.messages,
|
|
46
|
-
field_name_prefix,
|
|
47
|
-
form_id,
|
|
48
|
-
validate
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// this one is meant to be overwritten
|
|
53
|
-
async validateValues(
|
|
54
|
-
ctx: Context,
|
|
55
|
-
data: Record<string, FormDataValue>
|
|
56
|
-
): Promise<{ valid: boolean; error: string }> {
|
|
57
|
-
return {
|
|
58
|
-
valid: true,
|
|
59
|
-
error: "",
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async validate(
|
|
64
|
-
ctx: Context,
|
|
65
|
-
values: Record<string, FormDataValue>
|
|
66
|
-
): Promise<{
|
|
67
|
-
valid: boolean;
|
|
68
|
-
field_errors: Partial<Record<keyof Fields, string>>;
|
|
69
|
-
form_messages: FormMessage[];
|
|
70
|
-
}> {
|
|
71
|
-
const field_errors = {} as Record<keyof Fields, string>;
|
|
72
|
-
let valid = true;
|
|
73
|
-
const form_messages = [] as FormMessage[];
|
|
74
|
-
await Promise.all(
|
|
75
|
-
Object.entries(values)
|
|
76
|
-
.filter(([fieldname]) => fieldname in this.fields)
|
|
77
|
-
.map(async ([key]: [keyof F, FormDataValue]) => {
|
|
78
|
-
const field = this.fields[key];
|
|
79
|
-
const { valid: fieldvalid, message: fieldmessage } =
|
|
80
|
-
await field.getValue(ctx, values);
|
|
81
|
-
if (!fieldvalid) {
|
|
82
|
-
valid = false;
|
|
83
|
-
field_errors[field.name] = fieldmessage;
|
|
84
|
-
}
|
|
85
|
-
})
|
|
86
|
-
);
|
|
87
|
-
const formValidationResult = await this.validateValues(ctx, values);
|
|
88
|
-
if (!formValidationResult.valid) {
|
|
89
|
-
form_messages.push({
|
|
90
|
-
type: "error",
|
|
91
|
-
text: formValidationResult.error,
|
|
92
|
-
});
|
|
93
|
-
valid = false;
|
|
94
|
-
}
|
|
95
|
-
return { valid, field_errors, form_messages };
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
public renderControls(fctx: FormControlContext): FlatTemplatable {
|
|
99
|
-
return tempstream/* HTML */ `${this.controls.map((control) =>
|
|
100
|
-
control.render(fctx)
|
|
101
|
-
)}`;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async renderError(
|
|
105
|
-
_: Context,
|
|
106
|
-
error: PageErrorMessage
|
|
107
|
-
): Promise<FlatTemplatable> {
|
|
108
|
-
return error.message;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
public renderMessages(
|
|
112
|
-
_: Context,
|
|
113
|
-
data: FormData<keyof Fields>
|
|
114
|
-
): FlatTemplatable {
|
|
115
|
-
return tempstream/* HTML */ `<div class="form-messages">
|
|
116
|
-
${data.messages.map(
|
|
117
|
-
(message) =>
|
|
118
|
-
`<div class="form-message form-message--${message.type}">${message.text}</div>`
|
|
119
|
-
)}
|
|
120
|
-
</div>`;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
abstract extractRawValues(
|
|
124
|
-
ctx: Context
|
|
125
|
-
): Promise<Record<string, FormDataValue>>;
|
|
126
|
-
|
|
127
|
-
async getParsedValues(
|
|
128
|
-
ctx: Context
|
|
129
|
-
): Promise<ShapeToType<FieldsToShape<F>>> {
|
|
130
|
-
const raw_values = await this.extractRawValues(ctx);
|
|
131
|
-
const result: Record<string, unknown> = {};
|
|
132
|
-
const promises = Object.entries(this.fields).map(
|
|
133
|
-
async ([key, field]) => {
|
|
134
|
-
const { parsed } = await field.getValue(ctx, raw_values);
|
|
135
|
-
result[key] = parsed;
|
|
136
|
-
}
|
|
137
|
-
);
|
|
138
|
-
await Promise.all(promises);
|
|
139
|
-
return result as ShapeToType<FieldsToShape<F>>;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async getDatabaseValues(
|
|
143
|
-
ctx: Context
|
|
144
|
-
): Promise<ShapeToType<FieldsToShape<F>>> {
|
|
145
|
-
const raw_values = await this.extractRawValues(ctx);
|
|
146
|
-
const result: Record<string, unknown> = {};
|
|
147
|
-
const promises = Object.entries(this.fields).map(
|
|
148
|
-
async ([key, field]) => {
|
|
149
|
-
const db_value = await field.getDatabaseValue(ctx, raw_values);
|
|
150
|
-
if (db_value !== undefined) {
|
|
151
|
-
result[key] = db_value;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
);
|
|
155
|
-
await Promise.all(promises);
|
|
156
|
-
return result as ShapeToType<FieldsToShape<F>>;
|
|
157
|
-
}
|
|
158
|
-
}
|
package/src/page/mountable.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import Router from "@koa/router";
|
|
2
|
-
|
|
3
|
-
import { Context } from "koa";
|
|
4
|
-
|
|
5
|
-
export abstract class Mountable {
|
|
6
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
7
|
-
init(): void {}
|
|
8
|
-
abstract mount(router: Router, path: string): void;
|
|
9
|
-
|
|
10
|
-
abstract canAccess(
|
|
11
|
-
ctx: Context
|
|
12
|
-
): Promise<{ canAccess: boolean; message: string }>;
|
|
13
|
-
}
|
package/src/page/page.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import Router from "@koa/router";
|
|
2
|
-
import { Context } from "koa";
|
|
3
|
-
|
|
4
|
-
import { FlatTemplatable, tempstream } from "tempstream";
|
|
5
|
-
import { AllQueryParams } from "..";
|
|
6
|
-
import { FormDataValue } from "../forms/form";
|
|
7
|
-
import {
|
|
8
|
-
Fields,
|
|
9
|
-
MountableWithFields,
|
|
10
|
-
PageErrorMessage,
|
|
11
|
-
} from "./mountable-with-fields";
|
|
12
|
-
import { PagePropsParser } from "./props-parser";
|
|
13
|
-
|
|
14
|
-
export abstract class Page<
|
|
15
|
-
F extends Fields = Record<string, never>
|
|
16
|
-
> extends MountableWithFields<F> {
|
|
17
|
-
propsParser: PagePropsParser<Record<keyof Fields, unknown>> =
|
|
18
|
-
new AllQueryParams();
|
|
19
|
-
controls = [];
|
|
20
|
-
|
|
21
|
-
mount(router: Router, path: string): void {
|
|
22
|
-
router.get(path, async (ctx) => {
|
|
23
|
-
ctx.body = await this.render(ctx);
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
28
|
-
async canAccess(_: Context) {
|
|
29
|
-
return { canAccess: true, message: "" };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async renderError(
|
|
33
|
-
_: Context,
|
|
34
|
-
error: PageErrorMessage
|
|
35
|
-
): Promise<FlatTemplatable> {
|
|
36
|
-
return tempstream/* HTML */ `<div>${error.message}</div>`;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
public abstract render(ctx: Context): Promise<FlatTemplatable>;
|
|
40
|
-
|
|
41
|
-
async extractRawValues(
|
|
42
|
-
ctx: Context
|
|
43
|
-
): Promise<Record<string, FormDataValue>> {
|
|
44
|
-
return this.propsParser.decode(ctx) || {};
|
|
45
|
-
}
|
|
46
|
-
}
|
package/src/page/props-parser.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { Context } from "koa";
|
|
2
|
-
import merge from "merge";
|
|
3
|
-
import { FormData, FormDataValue } from "..";
|
|
4
|
-
import { BasePageProps } from "./list";
|
|
5
|
-
|
|
6
|
-
export type EncodedProps = Record<string, unknown>;
|
|
7
|
-
|
|
8
|
-
// page props parser is only tasked with getting the raw props from *somewhere*
|
|
9
|
-
// and being able to put new values back to that somewhere. Parsing to numbers
|
|
10
|
-
// or any other exotic types is done by fields logic.
|
|
11
|
-
|
|
12
|
-
export abstract class PagePropsParser<PropsType> {
|
|
13
|
-
abstract decode(ctx: Context): Record<string, FormDataValue>;
|
|
14
|
-
abstract encode(props: Record<string, FormData>): EncodedProps;
|
|
15
|
-
abstract getHTMLInputName(prop_name: string): string;
|
|
16
|
-
|
|
17
|
-
overwriteProp(ctx: Context, new_props: Partial<PropsType>): EncodedProps {
|
|
18
|
-
const result = {};
|
|
19
|
-
merge.recursive(result, this.decode(ctx), new_props);
|
|
20
|
-
return result;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class AllQueryParams<
|
|
25
|
-
PropsType extends BasePageProps
|
|
26
|
-
> extends PagePropsParser<PropsType> {
|
|
27
|
-
decode(ctx: Context): Record<string, FormDataValue> {
|
|
28
|
-
return ctx.query;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
encode(props: PropsType): Record<string, unknown> {
|
|
32
|
-
return props;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
getHTMLInputName(prop_name: string): string {
|
|
36
|
-
return prop_name;
|
|
37
|
-
}
|
|
38
|
-
}
|
package/src/run.ts
DELETED
|
File without changes
|
package/src/sanitize.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export function collectionTemplate(collection_name: string) {
|
|
2
|
-
return `import { Collection, FieldTypes, Policies } from "sealious";
|
|
3
|
-
|
|
4
|
-
export default class ${collection_name} extends Collection {
|
|
5
|
-
fields = {
|
|
6
|
-
content: new FieldTypes.Text(),
|
|
7
|
-
};
|
|
8
|
-
defaultPolicy = new Policies.Public();
|
|
9
|
-
}
|
|
10
|
-
`;
|
|
11
|
-
}
|
package/src/templates/form.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { curryImportPath } from "../utils/import-path";
|
|
2
|
-
|
|
3
|
-
export async function formTemplate(
|
|
4
|
-
action_name: string,
|
|
5
|
-
newfilefullpath: string
|
|
6
|
-
): Promise<string> {
|
|
7
|
-
const rel = curryImportPath(newfilefullpath);
|
|
8
|
-
return `import { Context } from "koa";
|
|
9
|
-
import { Form, FormData, FormDataValue, Fields, Controls } from "@sealcode/sealgen";
|
|
10
|
-
import html from "${rel("src/back/html")}";
|
|
11
|
-
import { Users } from "${rel("src/back/collections/collections")}";
|
|
12
|
-
|
|
13
|
-
export const actionName = "${action_name}";
|
|
14
|
-
|
|
15
|
-
const fields = {
|
|
16
|
-
email: new Fields.CollectionField(true, Users.fields.email),
|
|
17
|
-
password: new Fields.SimpleFormField(true),
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export const ${action_name}Shape = Fields.fieldsToShape(fields);
|
|
21
|
-
|
|
22
|
-
export default new (class ${action_name}Form extends Form<typeof fields, void> {
|
|
23
|
-
defaultSuccessMessage = "Formularz wypełniony poprawnie";
|
|
24
|
-
fields = fields;
|
|
25
|
-
|
|
26
|
-
controls = [
|
|
27
|
-
new Controls.SimpleInput(fields.email, { label: "Email:", type: "email" }),
|
|
28
|
-
new Controls.SimpleInput(fields.password, { label: "Password:", type: "password" }),
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
async validateValues(
|
|
32
|
-
ctx: Context,
|
|
33
|
-
data: Record<string, FormDataValue>
|
|
34
|
-
): Promise<{ valid: boolean; error: string }> {
|
|
35
|
-
// just an example
|
|
36
|
-
const { parsed: email } = await this.fields.email.getValue(ctx, data);
|
|
37
|
-
const { parsed: password } = await this.fields.password.getValue(ctx, data);
|
|
38
|
-
if ((password || "").length > (email || "").length) {
|
|
39
|
-
return { valid: false, error: "Password cannot be longer than email" };
|
|
40
|
-
} else {
|
|
41
|
-
return { valid: true, error: "" };
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
46
|
-
async canAccess(_: Context) {
|
|
47
|
-
return { canAccess: true, message: "" };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async onSubmit() {
|
|
51
|
-
//noop
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async render(ctx: Context, data: FormData, show_field_errors: boolean) {
|
|
56
|
-
return html(ctx, "${action_name}", await super.render(ctx, data, show_field_errors));
|
|
57
|
-
}
|
|
58
|
-
})();
|
|
59
|
-
`;
|
|
60
|
-
}
|
package/src/templates/list.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { curryImportPath } from "../utils/import-path";
|
|
2
|
-
import prompts = require("prompts");
|
|
3
|
-
import { listCollections } from "../utils/list-collections";
|
|
4
|
-
|
|
5
|
-
export const listTemplate = async (
|
|
6
|
-
action_name: string,
|
|
7
|
-
newfilefullpath: string
|
|
8
|
-
): Promise<string> => {
|
|
9
|
-
const response = await prompts({
|
|
10
|
-
type: "autocomplete",
|
|
11
|
-
name: "collection",
|
|
12
|
-
message: "Which sealious collection do you want to list?",
|
|
13
|
-
choices: (
|
|
14
|
-
await listCollections()
|
|
15
|
-
).map((collection) => ({
|
|
16
|
-
title: collection,
|
|
17
|
-
value: collection,
|
|
18
|
-
})),
|
|
19
|
-
});
|
|
20
|
-
const collection = response.collection as string;
|
|
21
|
-
const uppercase_collection =
|
|
22
|
-
collection[0].toLocaleUpperCase() + collection.slice(1);
|
|
23
|
-
const rel = curryImportPath(newfilefullpath);
|
|
24
|
-
return `import { Context } from "koa";
|
|
25
|
-
import { CollectionItem } from "sealious";
|
|
26
|
-
import { tempstream } from "tempstream";
|
|
27
|
-
import { ${uppercase_collection} } from "${rel(
|
|
28
|
-
"src/back/collections/collections"
|
|
29
|
-
)}";
|
|
30
|
-
import html from "${rel("src/back/html.ts")}";
|
|
31
|
-
import { SealiousItemListPage, BaseListPageFields } from "@sealcode/sealgen";
|
|
32
|
-
|
|
33
|
-
export const actionName = "${action_name}";
|
|
34
|
-
|
|
35
|
-
const filterFields = {};
|
|
36
|
-
|
|
37
|
-
export default new (class ${action_name}Page extends SealiousItemListPage<
|
|
38
|
-
typeof ${uppercase_collection},
|
|
39
|
-
typeof BaseListPageFields
|
|
40
|
-
> {
|
|
41
|
-
fields = BaseListPageFields;
|
|
42
|
-
|
|
43
|
-
filterFields = filterFields;
|
|
44
|
-
filterControls = [];
|
|
45
|
-
|
|
46
|
-
async render(ctx: Context) {
|
|
47
|
-
return html(
|
|
48
|
-
ctx,
|
|
49
|
-
"${uppercase_collection}",
|
|
50
|
-
tempstream/* HTML */ \`<div>
|
|
51
|
-
<h2>${uppercase_collection} List</h2>
|
|
52
|
-
<table>
|
|
53
|
-
<thead>
|
|
54
|
-
<th>id</th>
|
|
55
|
-
\${Object.keys(${uppercase_collection}.fields).map(
|
|
56
|
-
(fieldname) => \`<th>\${fieldname}</th>\`
|
|
57
|
-
)}
|
|
58
|
-
</thead>
|
|
59
|
-
<tbody>
|
|
60
|
-
\${super.render(ctx)}
|
|
61
|
-
</tbody>
|
|
62
|
-
</table>
|
|
63
|
-
</div>\`
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async renderItem(_: Context, item: CollectionItem<typeof ${uppercase_collection}>) {
|
|
68
|
-
return tempstream\`<tr><td>\${item.id}</td>\${Object.keys(${uppercase_collection}.fields).map(
|
|
69
|
-
(fieldname: keyof typeof ${uppercase_collection}["fields"]) =>
|
|
70
|
-
tempstream\`<td>\${item.get(fieldname)}</td>\`
|
|
71
|
-
)}</tr>\`;
|
|
72
|
-
}
|
|
73
|
-
})(${uppercase_collection});
|
|
74
|
-
`;
|
|
75
|
-
};
|