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.
Files changed (42) hide show
  1. package/dist/api/sonamu.d.ts +1 -1
  2. package/dist/api/sonamu.d.ts.map +1 -1
  3. package/dist/api/sonamu.js +2 -2
  4. package/dist/api/sonamu.js.map +1 -1
  5. package/dist/bin/cli.js +5 -8
  6. package/dist/bin/cli.js.map +1 -1
  7. package/dist/database/upsert-builder.d.ts.map +1 -1
  8. package/dist/database/upsert-builder.js +2 -1
  9. package/dist/database/upsert-builder.js.map +1 -1
  10. package/dist/syncer/syncer.d.ts.map +1 -1
  11. package/dist/syncer/syncer.js +4 -1
  12. package/dist/syncer/syncer.js.map +1 -1
  13. package/dist/templates/model_test.template.d.ts.map +1 -1
  14. package/dist/templates/model_test.template.js +7 -8
  15. package/dist/templates/model_test.template.js.map +1 -1
  16. package/dist/templates/service.template.js +1 -1
  17. package/dist/templates/service.template.js.map +1 -1
  18. package/dist/templates/view_enums_select.template.d.ts.map +1 -1
  19. package/dist/templates/view_enums_select.template.js +6 -8
  20. package/dist/templates/view_enums_select.template.js.map +1 -1
  21. package/dist/templates/view_form.template.d.ts +1 -1
  22. package/dist/templates/view_form.template.d.ts.map +1 -1
  23. package/dist/templates/view_form.template.js +48 -41
  24. package/dist/templates/view_form.template.js.map +1 -1
  25. package/dist/templates/view_list.template.d.ts +1 -1
  26. package/dist/templates/view_list.template.d.ts.map +1 -1
  27. package/dist/templates/view_list.template.js +17 -24
  28. package/dist/templates/view_list.template.js.map +1 -1
  29. package/dist/types/types.d.ts +1 -1
  30. package/dist/types/types.d.ts.map +1 -1
  31. package/dist/types/types.js.map +1 -1
  32. package/package.json +1 -1
  33. package/src/api/sonamu.ts +2 -2
  34. package/src/bin/cli.ts +5 -8
  35. package/src/database/upsert-builder.ts +2 -1
  36. package/src/syncer/syncer.ts +3 -1
  37. package/src/templates/model_test.template.ts +7 -8
  38. package/src/templates/service.template.ts +1 -1
  39. package/src/templates/view_enums_select.template.ts +6 -8
  40. package/src/templates/view_form.template.ts +55 -61
  41. package/src/templates/view_list.template.ts +21 -26
  42. 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 { BadRequestException, FixtureManager } from "sonamu";
24
- import { ${smdId}ListParams, ${smdId}SaveParams } from "../${names.fs}/${names.fs}.types";
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("Model", "Service")} {
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 = Object.entries(${names.constant}.${idConstant}).map(([key, { ko }]) => {
39
- return {
40
- key,
41
- value: key,
42
- text: (textPrefix ?? '${label}: ') + ko,
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 `{form.${col.name}.map((elem, index) => ${this.renderColumn(
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
- col.name !== "id" &&
195
- (["enums", "number-id"].includes(col.renderType) ||
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 'src/typeframe/components/BackLink';
260
- import { LinkInput } from 'src/typeframe/components/LinkInput';
261
- import { ImageUploader } from 'src/typeframe/components/ImageUploader';
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
- ${columns
279
- .filter((col) => ["number-fk_id", "enums"].includes(col.renderType))
280
- .map((col) => {
281
- return this.renderColumnImport(smdId, col);
282
- })
283
- .join("\n")}
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 function ${names.capitalPlural}Form({ id, mode }: ${
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
- }FormProps) {
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
- '"now()"',
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(defCatch);
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 `<img src={${colName}} />`;
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}).toFormat('y-MM-dd')}</span>`;
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}).toFormat('y-MM-dd')}</span>`;
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 => <img src={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 'src/typeframe/components/DelButton';
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:TFColumn<${names.capital}SubsetA>[] = [
467
- ${columns.map((col) => `{ ...dc.${col.name} }`).join(",\n")}
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">
@@ -532,6 +532,7 @@ export type RenderingNode = {
532
532
  | "string-plain"
533
533
  | "string-image"
534
534
  | "string-datetime"
535
+ | "string-date"
535
536
  | "number-plain"
536
537
  | "number-id"
537
538
  | "number-fk_id"