sonamu 0.0.13 → 0.0.16
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/dist/api/sonamu.d.ts +1 -1
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +2 -2
- package/dist/api/sonamu.js.map +1 -1
- package/dist/bin/cli.js +5 -8
- package/dist/bin/cli.js.map +1 -1
- package/dist/database/upsert-builder.d.ts.map +1 -1
- package/dist/database/upsert-builder.js +2 -1
- package/dist/database/upsert-builder.js.map +1 -1
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +4 -1
- package/dist/syncer/syncer.js.map +1 -1
- package/dist/templates/model_test.template.d.ts.map +1 -1
- package/dist/templates/model_test.template.js +7 -8
- package/dist/templates/model_test.template.js.map +1 -1
- package/dist/templates/service.template.js +1 -1
- package/dist/templates/service.template.js.map +1 -1
- package/dist/templates/view_enums_select.template.d.ts.map +1 -1
- package/dist/templates/view_enums_select.template.js +6 -8
- package/dist/templates/view_enums_select.template.js.map +1 -1
- package/dist/templates/view_form.template.d.ts +1 -1
- package/dist/templates/view_form.template.d.ts.map +1 -1
- package/dist/templates/view_form.template.js +48 -41
- package/dist/templates/view_form.template.js.map +1 -1
- package/dist/templates/view_list.template.d.ts +1 -1
- package/dist/templates/view_list.template.d.ts.map +1 -1
- package/dist/templates/view_list.template.js +17 -24
- package/dist/templates/view_list.template.js.map +1 -1
- package/dist/types/types.d.ts +1 -1
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js.map +1 -1
- package/package.json +1 -1
- package/src/api/sonamu.ts +2 -2
- package/src/bin/cli.ts +5 -8
- package/src/database/upsert-builder.ts +2 -1
- package/src/syncer/syncer.ts +3 -1
- package/src/templates/model_test.template.ts +7 -8
- package/src/templates/service.template.ts +1 -1
- package/src/templates/view_enums_select.template.ts +6 -8
- package/src/templates/view_form.template.ts +55 -61
- package/src/templates/view_list.template.ts +21 -26
- package/src/types/types.ts +1 -0
|
@@ -20,18 +20,17 @@ export class Template__model_test extends Template {
|
|
|
20
20
|
return {
|
|
21
21
|
...this.getTargetAndPath(names),
|
|
22
22
|
body: `
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import { ${smdId}Model } from "../${names.fs}/${names.fs}.model";
|
|
26
|
-
|
|
27
|
-
describe.skip("${smdId}Model Model", () => {
|
|
28
|
-
new FixtureManager([
|
|
29
|
-
]);
|
|
23
|
+
import { FixtureManager } from "sonamu";
|
|
24
|
+
import { describe, test, expect } from "vitest";
|
|
30
25
|
|
|
26
|
+
beforeEach(async () => {
|
|
27
|
+
await FixtureManager.cleanAndSeed([]);
|
|
28
|
+
});
|
|
29
|
+
describe.skip("${smdId}ModelTest", () => {
|
|
31
30
|
test("Query", async () => {
|
|
31
|
+
expect(true).toBe(true);
|
|
32
32
|
});
|
|
33
33
|
});
|
|
34
|
-
|
|
35
34
|
`.trim(),
|
|
36
35
|
importKeys: [],
|
|
37
36
|
};
|
|
@@ -142,7 +142,7 @@ export class Template__service extends Template {
|
|
|
142
142
|
})
|
|
143
143
|
.join("\n\n");
|
|
144
144
|
|
|
145
|
-
return `export namespace ${modelName.replace(
|
|
145
|
+
return `export namespace ${modelName.replace(/Model$/, "Service")} {
|
|
146
146
|
${methodCodes}
|
|
147
147
|
}`;
|
|
148
148
|
})
|
|
@@ -28,20 +28,18 @@ import {
|
|
|
28
28
|
DropdownProps,
|
|
29
29
|
} from 'semantic-ui-react';
|
|
30
30
|
|
|
31
|
-
import { ${names.constant} } from 'src/services/${names.fs}/${names.fs}.enums';
|
|
31
|
+
import { ${enumId}, ${names.constant} } from 'src/services/${names.fs}/${names.fs}.enums';
|
|
32
32
|
|
|
33
33
|
export type ${enumId}SelectProps = {
|
|
34
34
|
placeholder?: string;
|
|
35
35
|
textPrefix?: string;
|
|
36
36
|
} & DropdownProps;
|
|
37
37
|
export function ${enumId}Select({placeholder, textPrefix, ...props}: ${enumId}SelectProps) {
|
|
38
|
-
const typeOptions =
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
};
|
|
44
|
-
});
|
|
38
|
+
const typeOptions = ${enumId}.options.map((key) => ({
|
|
39
|
+
key,
|
|
40
|
+
value: key,
|
|
41
|
+
text: (textPrefix ?? '${label}: ') + ${names.constant}.${idConstant}[key].ko,
|
|
42
|
+
}));
|
|
45
43
|
|
|
46
44
|
return (
|
|
47
45
|
<Dropdown
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getEnumInfoFromColName,
|
|
9
9
|
getRelationPropFromColName,
|
|
10
10
|
} from "./view_list.template";
|
|
11
|
+
import { uniq } from "lodash";
|
|
11
12
|
|
|
12
13
|
export class Template__view_form extends Template {
|
|
13
14
|
constructor() {
|
|
@@ -75,6 +76,8 @@ export class Template__view_form extends Template {
|
|
|
75
76
|
}
|
|
76
77
|
case "string-datetime":
|
|
77
78
|
return `<SQLDateTimeInput ${regExpr} />`;
|
|
79
|
+
case "string-date":
|
|
80
|
+
return `<SQLDateInput ${regExpr} />`;
|
|
78
81
|
case "number-id":
|
|
79
82
|
return `<input type="hidden" ${regExpr} />`;
|
|
80
83
|
case "number-plain":
|
|
@@ -114,27 +117,9 @@ export class Template__view_form extends Template {
|
|
|
114
117
|
return `<>${col.name} 찾을 수 없음</>`;
|
|
115
118
|
}
|
|
116
119
|
case "array":
|
|
117
|
-
return
|
|
118
|
-
smdId,
|
|
119
|
-
col.element!,
|
|
120
|
-
names,
|
|
121
|
-
`${parent}${col.name}[\${index}]`
|
|
122
|
-
)})}`;
|
|
120
|
+
return `<>${col.name} array</>`;
|
|
123
121
|
case "object":
|
|
124
|
-
return
|
|
125
|
-
`<Form.Group className="${col.name}"${
|
|
126
|
-
parent !== "" ? " key={index}" : ""
|
|
127
|
-
}>` +
|
|
128
|
-
col
|
|
129
|
-
.children!.map((child) =>
|
|
130
|
-
this.wrapFC(
|
|
131
|
-
this.renderColumn(smdId, child, names, `${parent}.`),
|
|
132
|
-
child.label
|
|
133
|
-
)
|
|
134
|
-
)
|
|
135
|
-
.join("\n") +
|
|
136
|
-
"</Form.Group>"
|
|
137
|
-
);
|
|
122
|
+
return `<>${col.name} object</>`;
|
|
138
123
|
default:
|
|
139
124
|
throw new Error(
|
|
140
125
|
`대응 불가능한 렌더 타입 ${col.renderType} on ${col.name}`
|
|
@@ -190,26 +175,39 @@ export class Template__view_form extends Template {
|
|
|
190
175
|
columns as RenderingNode[]
|
|
191
176
|
)
|
|
192
177
|
.filter((col) => {
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
col.name.endsWith("_id"))
|
|
197
|
-
) {
|
|
178
|
+
if (col.name === "id") {
|
|
179
|
+
return false;
|
|
180
|
+
} else if (col.name.endsWith("_id") || col.renderType === "number-id") {
|
|
198
181
|
try {
|
|
199
182
|
getRelationPropFromColName(smdId, col.name.replace("_id", ""));
|
|
183
|
+
return true;
|
|
184
|
+
} catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
} else if (col.renderType === "enums") {
|
|
188
|
+
try {
|
|
189
|
+
getEnumInfoFromColName(smdId, col.name);
|
|
190
|
+
return true;
|
|
200
191
|
} catch {
|
|
201
192
|
return false;
|
|
202
193
|
}
|
|
203
|
-
return true;
|
|
204
|
-
} else {
|
|
205
|
-
return false;
|
|
206
194
|
}
|
|
195
|
+
return false;
|
|
207
196
|
})
|
|
208
197
|
.map((col) => {
|
|
209
198
|
let key: TemplateKey;
|
|
210
199
|
let targetMdId = smdId;
|
|
200
|
+
let enumId: string | undefined;
|
|
201
|
+
let idConstant: string | undefined;
|
|
211
202
|
if (col.renderType === "enums") {
|
|
212
203
|
key = "view_enums_select";
|
|
204
|
+
const { targetMDNames, id, name } = getEnumInfoFromColName(
|
|
205
|
+
smdId,
|
|
206
|
+
col.name
|
|
207
|
+
);
|
|
208
|
+
targetMdId = targetMDNames.capital;
|
|
209
|
+
enumId = id;
|
|
210
|
+
idConstant = name;
|
|
213
211
|
} else {
|
|
214
212
|
key = "view_id_async_select";
|
|
215
213
|
const relProp = getRelationPropFromColName(
|
|
@@ -224,6 +222,8 @@ export class Template__view_form extends Template {
|
|
|
224
222
|
options: {
|
|
225
223
|
smdId: targetMdId,
|
|
226
224
|
node: col,
|
|
225
|
+
enumId,
|
|
226
|
+
idConstant,
|
|
227
227
|
},
|
|
228
228
|
};
|
|
229
229
|
})
|
|
@@ -242,7 +242,7 @@ export class Template__view_form extends Template {
|
|
|
242
242
|
return {
|
|
243
243
|
...this.getTargetAndPath(names),
|
|
244
244
|
body: `
|
|
245
|
-
import React, { useEffect, useState, Dispatch, SetStateAction } from 'react';
|
|
245
|
+
import React, { useEffect, useState, Dispatch, SetStateAction, forwardRef, Ref, useImperativeHandle, useCallback } from 'react';
|
|
246
246
|
import { useSearchParams } from 'react-router-dom';
|
|
247
247
|
import {
|
|
248
248
|
Button,
|
|
@@ -256,31 +256,26 @@ import {
|
|
|
256
256
|
} from 'semantic-ui-react';
|
|
257
257
|
import { DateTime } from "luxon";
|
|
258
258
|
|
|
259
|
-
import { BackLink } from
|
|
260
|
-
import {
|
|
261
|
-
import { ImageUploader } from 'src/
|
|
262
|
-
import { NumberInput } from 'src/typeframe/components/NumberInput';
|
|
263
|
-
import { BooleanToggle } from 'src/typeframe/components/BooleanToggle';
|
|
264
|
-
import { SQLDateTimeInput } from "src/typeframe/components/SQLDateTimeInput";
|
|
265
|
-
import { defCatch } from 'src/typeframe/fetch';
|
|
259
|
+
import { BackLink, LinkInput, NumberInput, BooleanToggle, SQLDateTimeInput, SQLDateInput, useTypeForm, useGoBack } from "@sonamu-kit/react-sui";
|
|
260
|
+
import { defaultCatch } from 'src/services/sonamu.shared';
|
|
261
|
+
import { ImageUploader } from 'src/components/core/ImageUploader';
|
|
266
262
|
|
|
267
263
|
import { ${names.capital}SaveParams } from 'src/services/${names.fs}/${
|
|
268
264
|
names.fs
|
|
269
265
|
}.types';
|
|
270
|
-
import { useTypeForm, useGoBack } from 'src/typeframe/helpers';
|
|
271
|
-
import { usePubSub } from 'src/typeframe/pubsub';
|
|
272
266
|
import { ${names.capital}Service } from 'src/services/${names.fs}/${
|
|
273
267
|
names.fs
|
|
274
268
|
}.service';
|
|
275
269
|
import { ${names.capital}SubsetA } from 'src/services/${names.fs}/${
|
|
276
270
|
names.fs
|
|
277
271
|
}.generated';
|
|
278
|
-
${
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
272
|
+
${uniq(
|
|
273
|
+
columns
|
|
274
|
+
.filter((col) => ["number-fk_id", "enums"].includes(col.renderType))
|
|
275
|
+
.map((col) => {
|
|
276
|
+
return this.renderColumnImport(smdId, col);
|
|
277
|
+
})
|
|
278
|
+
).join("\n")}
|
|
284
279
|
|
|
285
280
|
export default function ${names.capitalPlural}FormPage() {
|
|
286
281
|
// 라우팅 searchParams
|
|
@@ -297,9 +292,18 @@ type ${names.capitalPlural}FormProps = {
|
|
|
297
292
|
id?: number;
|
|
298
293
|
mode?: 'page' | 'modal';
|
|
299
294
|
};
|
|
300
|
-
export
|
|
295
|
+
export type ${names.capitalPlural}FormHandle = { submit: () => void };
|
|
296
|
+
export const ${names.capitalPlural}Form = forwardRef(
|
|
297
|
+
({ id, mode }: ${names.capitalPlural}FormProps, ref: Ref<${
|
|
301
298
|
names.capitalPlural
|
|
302
|
-
}
|
|
299
|
+
}FormHandle>) => {
|
|
300
|
+
// 폼 핸들
|
|
301
|
+
useImperativeHandle(ref, () => ({
|
|
302
|
+
submit: () => {
|
|
303
|
+
handleSubmit();
|
|
304
|
+
},
|
|
305
|
+
}));
|
|
306
|
+
|
|
303
307
|
// 편집시 기존 row
|
|
304
308
|
const [row, setRow] = useState<${names.capital}SubsetA | undefined>();
|
|
305
309
|
|
|
@@ -307,7 +311,7 @@ export function ${names.capitalPlural}Form({ id, mode }: ${
|
|
|
307
311
|
const { form, setForm, register } = useTypeForm(${
|
|
308
312
|
names.capital
|
|
309
313
|
}SaveParams, ${JSON.stringify(defaultValue).replace(
|
|
310
|
-
|
|
314
|
+
/"now\(\)"/g,
|
|
311
315
|
"DateTime.local().toSQL().slice(0, 19)"
|
|
312
316
|
)});
|
|
313
317
|
|
|
@@ -338,23 +342,13 @@ export function ${names.capitalPlural}Form({ id, mode }: ${
|
|
|
338
342
|
|
|
339
343
|
// 저장
|
|
340
344
|
const { goBack } = useGoBack();
|
|
341
|
-
const handleSubmit = () => {
|
|
345
|
+
const handleSubmit = useCallback(() => {
|
|
342
346
|
${names.capital}Service.save([form]).then(([id]) => {
|
|
343
347
|
if (mode !== 'modal') {
|
|
344
348
|
goBack('/admin/${names.fsPlural}');
|
|
345
349
|
}
|
|
346
|
-
}).catch(
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
// 모달 서브밋 핸들링
|
|
350
|
-
const { subscribe } = usePubSub();
|
|
351
|
-
useEffect(() => {
|
|
352
|
-
if (id) {
|
|
353
|
-
return subscribe(\`${names.fs}#\${id}.submitted\`, () => {
|
|
354
|
-
handleSubmit();
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
}, [form]);
|
|
350
|
+
}).catch(defaultCatch);
|
|
351
|
+
}, [ form, mode, id ]);
|
|
358
352
|
|
|
359
353
|
return (
|
|
360
354
|
<div className="form">
|
|
@@ -388,7 +382,7 @@ export function ${names.capitalPlural}Form({ id, mode }: ${
|
|
|
388
382
|
</Segment>
|
|
389
383
|
</div>
|
|
390
384
|
);
|
|
391
|
-
}
|
|
385
|
+
});
|
|
392
386
|
`.trim(),
|
|
393
387
|
importKeys: [],
|
|
394
388
|
preTemplates,
|
|
@@ -40,6 +40,7 @@ export class Template__view_list extends Template {
|
|
|
40
40
|
|
|
41
41
|
switch (col.renderType) {
|
|
42
42
|
case "string-plain":
|
|
43
|
+
case "string-date":
|
|
43
44
|
case "number-id":
|
|
44
45
|
return `<>{${colName}}</>`;
|
|
45
46
|
case "number-fk_id":
|
|
@@ -49,12 +50,14 @@ export class Template__view_list extends Template {
|
|
|
49
50
|
);
|
|
50
51
|
return `<>${relPropFk.with}#{${colName}}</>`;
|
|
51
52
|
case "string-image":
|
|
52
|
-
return
|
|
53
|
+
return `<>{${
|
|
54
|
+
col.nullable ? `${colName} && ` : ""
|
|
55
|
+
}<img src={${colName}} />}</>`;
|
|
53
56
|
case "string-datetime":
|
|
54
57
|
if (col.nullable) {
|
|
55
|
-
return `<span className="text-tiny">{${colName} === null ? '-' : DateTime.fromSQL(${colName}).
|
|
58
|
+
return `<span className="text-tiny">{${colName} === null ? '-' : DateTime.fromSQL(${colName}).toSQL().slice(0, 10)}</span>`;
|
|
56
59
|
} else {
|
|
57
|
-
return `<span className="text-tiny">{DateTime.fromSQL(${colName}).
|
|
60
|
+
return `<span className="text-tiny">{DateTime.fromSQL(${colName}).toSQL().slice(0, 10)}</span>`;
|
|
58
61
|
}
|
|
59
62
|
case "boolean":
|
|
60
63
|
return `<>{${colName} ? <Label color='green' circular>O</Label> : <Label color='grey' circular>X</Label> }</>`;
|
|
@@ -64,11 +67,11 @@ export class Template__view_list extends Template {
|
|
|
64
67
|
targetMDNames.constant
|
|
65
68
|
}.${name}[${colName}].ko}</>`;
|
|
66
69
|
case "array-images":
|
|
67
|
-
return `<>{ ${colName}.map(r =>
|
|
70
|
+
return `<>{ ${colName}.map(r => ${
|
|
71
|
+
col.nullable ? `r && ` : ""
|
|
72
|
+
}<img src={r} />) }</>`;
|
|
68
73
|
case "number-plain":
|
|
69
|
-
return `<>{${
|
|
70
|
-
col.nullable ? `${colName} && ` : ""
|
|
71
|
-
}new Intl.NumberFormat().format(${colName})}</>`;
|
|
74
|
+
return `<>{${col.nullable ? `${colName} && ` : ""}numF(${colName})}</>`;
|
|
72
75
|
case "object":
|
|
73
76
|
try {
|
|
74
77
|
const relProp = getRelationPropFromColName(smdId, col.name);
|
|
@@ -333,7 +336,7 @@ export class Template__view_list extends Template {
|
|
|
333
336
|
});
|
|
334
337
|
}
|
|
335
338
|
|
|
336
|
-
// 리스트 컬럼
|
|
339
|
+
// 리스트 컬럼
|
|
337
340
|
const columnImports = uniq(
|
|
338
341
|
columnsNode
|
|
339
342
|
.children!.map((col) => {
|
|
@@ -342,14 +345,6 @@ export class Template__view_list extends Template {
|
|
|
342
345
|
.flat()
|
|
343
346
|
.filter((col) => col !== null)
|
|
344
347
|
).join("\n");
|
|
345
|
-
preTemplates.push({
|
|
346
|
-
key: "view_list_columns",
|
|
347
|
-
options: {
|
|
348
|
-
smdId,
|
|
349
|
-
columns,
|
|
350
|
-
columnImports,
|
|
351
|
-
} as TemplateOptions["view_list_columns"],
|
|
352
|
-
});
|
|
353
348
|
|
|
354
349
|
// SearchInput
|
|
355
350
|
preTemplates!.push({
|
|
@@ -381,13 +376,7 @@ import {
|
|
|
381
376
|
} from 'semantic-ui-react';
|
|
382
377
|
import classNames from 'classnames';
|
|
383
378
|
import { DateTime } from "luxon";
|
|
384
|
-
import { DelButton } from '
|
|
385
|
-
import { EditButton } from 'src/typeframe/components/EditButton';
|
|
386
|
-
import { AppBreadcrumbs } from 'src/typeframe/components/AppBreadcrumbs';
|
|
387
|
-
import { AddButton } from 'src/typeframe/components/AddButton';
|
|
388
|
-
import { useSelection, useListParams } from 'src/typeframe/helpers';
|
|
389
|
-
import { TFColumn } from "src/typeframe/iso-types";
|
|
390
|
-
import dc from './_columns';
|
|
379
|
+
import { DelButton, EditButton, AppBreadcrumbs, AddButton, useSelection, useListParams, SonamuCol, numF } from '@sonamu-kit/react-sui';
|
|
391
380
|
|
|
392
381
|
import { ${names.capital}SubsetA } from "src/services/${names.fs}/${
|
|
393
382
|
names.fs
|
|
@@ -463,9 +452,15 @@ export default function ${names.capital}List({}: ${names.capital}ListProps) {
|
|
|
463
452
|
} = useSelection((rows ?? []).map((row) => row.id));
|
|
464
453
|
|
|
465
454
|
// 컬럼
|
|
466
|
-
const columns:
|
|
467
|
-
|
|
468
|
-
|
|
455
|
+
const columns:SonamuCol<${names.capital}SubsetA>[] = [${columns
|
|
456
|
+
.map((col) => {
|
|
457
|
+
return [
|
|
458
|
+
`{ label: "${col.label}",`,
|
|
459
|
+
`tc: ${col.tc}, `,
|
|
460
|
+
`collapsing: ${["Title", "Name"].includes(col.label) === false}, }`,
|
|
461
|
+
].join("\n");
|
|
462
|
+
})
|
|
463
|
+
.join(",\n")}];
|
|
469
464
|
|
|
470
465
|
return (
|
|
471
466
|
<div className="list ${names.fsPlural}-index">
|