sonamu 0.1.4 → 0.1.6

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 (39) hide show
  1. package/dist/entity/entity.d.ts +1 -0
  2. package/dist/entity/entity.d.ts.map +1 -1
  3. package/dist/entity/entity.js +27 -2
  4. package/dist/entity/entity.js.map +1 -1
  5. package/dist/syncer/syncer.d.ts.map +1 -1
  6. package/dist/syncer/syncer.js.map +1 -1
  7. package/dist/templates/service.template.d.ts.map +1 -1
  8. package/dist/templates/service.template.js +4 -4
  9. package/dist/templates/service.template.js.map +1 -1
  10. package/dist/templates/view_enums_dropdown.template.d.ts +2 -2
  11. package/dist/templates/view_enums_dropdown.template.d.ts.map +1 -1
  12. package/dist/templates/view_enums_dropdown.template.js +14 -13
  13. package/dist/templates/view_enums_dropdown.template.js.map +1 -1
  14. package/dist/templates/view_enums_select.template.d.ts +1 -1
  15. package/dist/templates/view_enums_select.template.d.ts.map +1 -1
  16. package/dist/templates/view_enums_select.template.js +4 -4
  17. package/dist/templates/view_enums_select.template.js.map +1 -1
  18. package/dist/templates/view_form.template.d.ts +3 -6
  19. package/dist/templates/view_form.template.d.ts.map +1 -1
  20. package/dist/templates/view_form.template.js +14 -8
  21. package/dist/templates/view_form.template.js.map +1 -1
  22. package/dist/templates/view_list.template.d.ts +3 -6
  23. package/dist/templates/view_list.template.d.ts.map +1 -1
  24. package/dist/templates/view_list.template.js +17 -20
  25. package/dist/templates/view_list.template.js.map +1 -1
  26. package/dist/types/types.d.ts +0 -15
  27. package/dist/types/types.d.ts.map +1 -1
  28. package/dist/types/types.js +0 -3
  29. package/dist/types/types.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/entity/entity.ts +32 -2
  32. package/src/shared/web.shared.ts.txt +21 -9
  33. package/src/syncer/syncer.ts +4 -1
  34. package/src/templates/service.template.ts +6 -4
  35. package/src/templates/view_enums_dropdown.template.ts +12 -17
  36. package/src/templates/view_enums_select.template.ts +4 -8
  37. package/src/templates/view_form.template.ts +16 -10
  38. package/src/templates/view_list.template.ts +16 -23
  39. package/src/types/types.ts +0 -3
@@ -1,4 +1,4 @@
1
- import _, { uniq } from "lodash";
1
+ import { groupBy, uniq } from "lodash";
2
2
  import { EntityManager as EntityManager } from "./entity-manager";
3
3
  import { dasherize, pluralize, underscore } from "inflection";
4
4
  import {
@@ -158,7 +158,7 @@ export class Entity {
158
158
  prefix = prefix.replace(/\./g, "__");
159
159
 
160
160
  // 서브셋을 1뎁스만 분리하여 그룹핑
161
- const subsetGroup = _.groupBy(fields, (field) => {
161
+ const subsetGroup = groupBy(fields, (field) => {
162
162
  if (field.includes(".")) {
163
163
  const [rel] = field.split(".");
164
164
  return rel;
@@ -604,6 +604,18 @@ export class Entity {
604
604
  }
605
605
 
606
606
  async save(): Promise<void> {
607
+ // sort: subsets
608
+ const subsetRows = this.getSubsetRows();
609
+ this.subsets = Object.fromEntries(
610
+ Object.entries(this.subsets).map(([subsetKey]) => {
611
+ return [
612
+ subsetKey,
613
+ this.subsetRowsToSubsetFields(subsetRows, subsetKey),
614
+ ];
615
+ })
616
+ );
617
+
618
+ // save
607
619
  const jsonPath = path.join(
608
620
  Sonamu.apiRootPath,
609
621
  `src/application/${this.names.parentFs}/${this.names.fs}.entity.json`
@@ -680,6 +692,24 @@ export class Entity {
680
692
  });
681
693
  }
682
694
 
695
+ subsetRowsToSubsetFields(
696
+ subsetRows: EntitySubsetRow[],
697
+ subsetKey: string
698
+ ): string[] {
699
+ return subsetRows
700
+ .map((subsetRow) => {
701
+ if (subsetRow.children.length > 0) {
702
+ return this.subsetRowsToSubsetFields(subsetRow.children, subsetKey);
703
+ } else if (subsetRow.has[subsetKey]) {
704
+ return subsetRow.prefixes.concat(subsetRow.field).join(".");
705
+ } else {
706
+ return null;
707
+ }
708
+ })
709
+ .filter(nonNullable)
710
+ .flat();
711
+ }
712
+
683
713
  async createProp(prop: EntityProp, at?: number): Promise<void> {
684
714
  if (!at) {
685
715
  this.props.push(prop);
@@ -4,6 +4,7 @@
4
4
  import type { AxiosRequestConfig } from "axios";
5
5
  import axios from "axios";
6
6
  import { z, ZodIssue } from "zod";
7
+ import qs from 'qs';
7
8
 
8
9
  export async function fetch(options: AxiosRequestConfig) {
9
10
  try {
@@ -71,12 +72,23 @@ export type SWRError = {
71
72
  message: string;
72
73
  statusCode: number;
73
74
  };
74
- export async function swrFetcher(
75
- url: string,
76
- params: string = ""
77
- ): Promise<any> {
75
+ export async function swrFetcher(args: [string, object]): Promise<any> {
78
76
  try {
79
- const res = await axios.get(url + "?" + params);
77
+ const [url, params] = args;
78
+ const res = await axios.get(`${url}?${qs.stringify(params)}`);
79
+ return res.data;
80
+ } catch (e: any) {
81
+ const error: any = new Error(
82
+ e.response.data.message ?? e.response.message ?? "Unknown"
83
+ );
84
+ error.statusCode = e.response?.data.statusCode ?? e.response.status;
85
+ throw error;
86
+ }
87
+ }
88
+ export async function swrPostFetcher(args: [string, object]): Promise<any> {
89
+ try {
90
+ const [url, params] = args;
91
+ const res = await axios.post(url, params);
80
92
  return res.data;
81
93
  } catch (e: any) {
82
94
  const error: any = new Error(
@@ -87,13 +99,13 @@ export async function swrFetcher(
87
99
  }
88
100
  }
89
101
  export function handleConditional(
90
- route: string | string[],
102
+ args: [string, object],
91
103
  conditional?: () => boolean
92
- ): string | string[] | null {
104
+ ): [string, object] | null {
93
105
  if (conditional) {
94
- return conditional() ? route : null;
106
+ return conditional() ? args : null;
95
107
  }
96
- return route;
108
+ return args;
97
109
  }
98
110
 
99
111
  /*
@@ -1255,7 +1255,10 @@ export class Syncer {
1255
1255
  }
1256
1256
  }
1257
1257
 
1258
- async getColumnsNode(entityId: string, subsetKey: string) {
1258
+ async getColumnsNode(
1259
+ entityId: string,
1260
+ subsetKey: string
1261
+ ): Promise<RenderingNode> {
1259
1262
  const entity = await EntityManager.get(entityId);
1260
1263
  const subsetA = entity.subsets[subsetKey];
1261
1264
  if (subsetA === undefined) {
@@ -40,7 +40,7 @@ export class Template__service extends Template {
40
40
  `import { z } from 'zod';`,
41
41
  `import qs from "qs";`,
42
42
  `import useSWR, { SWRResponse } from "swr";`,
43
- `import { fetch, ListResult, SWRError, SwrOptions, handleConditional } from '../sonamu.shared';`,
43
+ `import { fetch, ListResult, SWRError, SwrOptions, handleConditional, swrPostFetcher } from '../sonamu.shared';`,
44
44
  ],
45
45
  };
46
46
  }
@@ -241,10 +241,12 @@ export async function ${api.methodName}${typeParamsDef}(
241
241
  )}${typeParamsDef}(${[paramsDef, "options?: SwrOptions"]
242
242
  .filter((p) => p !== "")
243
243
  .join(",")}, ): SWRResponse<${returnTypeDef}, SWRError> {
244
- return useSWR<${returnTypeDef}, SWRError>(handleConditional([
244
+ return useSWR(handleConditional([
245
245
  \`${apiBaseUrl}\`,
246
- qs.stringify(${payloadDef}),
247
- ], options?.conditional));
246
+ ${payloadDef},
247
+ ], options?.conditional)${
248
+ api.options.httpMethod === "POST" ? ", swrPostFetcher" : ""
249
+ });
248
250
  }`;
249
251
  }
250
252
 
@@ -14,13 +14,9 @@ export class Template__view_enums_dropdown extends Template {
14
14
  };
15
15
  }
16
16
 
17
- render({
18
- entityId,
19
- enumId,
20
- idConstant,
21
- }: TemplateOptions["view_enums_dropdown"]) {
17
+ render({ entityId, enumId }: TemplateOptions["view_enums_dropdown"]) {
22
18
  const names = EntityManager.getNamesFromId(entityId);
23
- const label = getLabel(idConstant);
19
+ const label = getLabel(enumId);
24
20
 
25
21
  return {
26
22
  ...this.getTargetAndPath(names, enumId),
@@ -31,14 +27,14 @@ import {
31
27
  DropdownProps,
32
28
  } from 'semantic-ui-react';
33
29
 
34
- import { ${names.constant} } from 'src/services/${names.fs}/${names.fs}.enums';
30
+ import { ${enumId}Label } from 'src/services/${names.fs}/${names.fs}.generated';
35
31
 
36
32
  export function ${enumId}Dropdown(props: DropdownProps) {
37
- const options = Object.entries(${names.constant}.${idConstant}).map(([key, { ko }]) => {
33
+ const options = Object.entries(${enumId}Label).map(([key, label]) => {
38
34
  return {
39
35
  key,
40
36
  value: key,
41
- text: "${label}: " + ko,
37
+ text: "${label}: " + label,
42
38
  };
43
39
  });
44
40
  return (
@@ -55,13 +51,12 @@ export function ${enumId}Dropdown(props: DropdownProps) {
55
51
  }
56
52
  }
57
53
 
58
- export function getLabel(idConstant: string): string {
59
- switch (idConstant) {
60
- case "ORDER_BY":
61
- return "정렬";
62
- case "SEARCH_FIELD":
63
- return "검색";
64
- default:
65
- return idConstant;
54
+ export function getLabel(enumId: string): string {
55
+ if (enumId.endsWith("OrderBy")) {
56
+ return "정렬";
57
+ } else if (enumId.endsWith("SearchField")) {
58
+ return "검색";
59
+ } else {
60
+ return enumId;
66
61
  }
67
62
  }
@@ -15,13 +15,9 @@ export class Template__view_enums_select extends Template {
15
15
  };
16
16
  }
17
17
 
18
- render({
19
- entityId,
20
- enumId,
21
- idConstant,
22
- }: TemplateOptions["view_enums_select"]) {
18
+ render({ entityId, enumId }: TemplateOptions["view_enums_select"]) {
23
19
  const names = EntityManager.getNamesFromId(entityId);
24
- const label = getLabel(idConstant);
20
+ const label = getLabel(enumId);
25
21
 
26
22
  return {
27
23
  ...this.getTargetAndPath(names, enumId),
@@ -32,7 +28,7 @@ import {
32
28
  DropdownProps,
33
29
  } from 'semantic-ui-react';
34
30
 
35
- import { ${enumId}, ${names.constant} } from 'src/services/${names.fs}/${names.fs}.enums';
31
+ import { ${enumId}, ${enumId}Label } from 'src/services/${names.fs}/${names.fs}.generated';
36
32
 
37
33
  export type ${enumId}SelectProps = {
38
34
  placeholder?: string;
@@ -42,7 +38,7 @@ export function ${enumId}Select({placeholder, textPrefix, ...props}: ${enumId}Se
42
38
  const typeOptions = ${enumId}.options.map((key) => ({
43
39
  key,
44
40
  value: key,
45
- text: (textPrefix ?? '${label}: ') + ${names.constant}.${idConstant}[key].ko,
41
+ text: (textPrefix ?? '${label}: ') + ${enumId}Label[key],
46
42
  }));
47
43
 
48
44
  return (
@@ -163,10 +163,17 @@ export class Template__view_form extends Template {
163
163
  { entityId }: TemplateOptions["view_form"],
164
164
  saveParamsNode: RenderingNode
165
165
  ) {
166
+ const entity = EntityManager.get(entityId);
166
167
  const names = EntityManager.getNamesFromId(entityId);
167
- const columns = (saveParamsNode.children as RenderingNode[]).filter(
168
- (col) => col.name !== "id"
169
- );
168
+ const columns = (saveParamsNode.children as RenderingNode[])
169
+ .filter((col) => col.name !== "id")
170
+ .map((col) => {
171
+ const propCandidate = entity.props.find(
172
+ (prop) => prop.name === col.name
173
+ );
174
+ col.label = propCandidate?.desc ?? col.label;
175
+ return col;
176
+ });
170
177
 
171
178
  const defaultValue = this.resolveDefaultValue(columns);
172
179
 
@@ -198,16 +205,14 @@ export class Template__view_form extends Template {
198
205
  let key: TemplateKey;
199
206
  let targetMdId = entityId;
200
207
  let enumId: string | undefined;
201
- let idConstant: string | undefined;
202
208
  if (col.renderType === "enums") {
203
209
  key = "view_enums_select";
204
- const { targetMDNames, id, name } = getEnumInfoFromColName(
210
+ const { targetMDNames, id } = getEnumInfoFromColName(
205
211
  entityId,
206
212
  col.name
207
213
  );
208
214
  targetMdId = targetMDNames.capital;
209
215
  enumId = id;
210
- idConstant = name;
211
216
  } else {
212
217
  key = "view_id_async_select";
213
218
  const relProp = getRelationPropFromColName(
@@ -223,7 +228,6 @@ export class Template__view_form extends Template {
223
228
  entityId: targetMdId,
224
229
  node: col,
225
230
  enumId,
226
- idConstant,
227
231
  },
228
232
  };
229
233
  })
@@ -258,8 +262,8 @@ import { DateTime } from "luxon";
258
262
 
259
263
  import { BackLink, LinkInput, NumberInput, BooleanToggle, SQLDateTimeInput, SQLDateInput, useTypeForm, useGoBack } from "@sonamu-kit/react-sui";
260
264
  import { defaultCatch } from 'src/services/sonamu.shared';
261
- import { ImageUploader } from 'src/components/core/ImageUploader';
262
- import { useCommonModal } from "src/components/core/CommonModal";
265
+ import { ImageUploader } from 'src/admin-common/ImageUploader';
266
+ import { useCommonModal } from "src/admin-common/CommonModal";
263
267
 
264
268
  import { ${names.capital}SaveParams } from 'src/services/${names.fs}/${
265
269
  names.fs
@@ -349,7 +353,9 @@ export function ${names.capitalPlural}Form({ id, mode }: ${
349
353
 
350
354
  // 페이지
351
355
  const PAGE = {
352
- title: \`${names.capital}\${id ? \`#\${id} 수정\` : ' 등록'}\`,
356
+ title: \`${
357
+ entity.title ?? names.capital
358
+ }\${id ? \`#\${id} 수정\` : ' 등록'}\`,
353
359
  }
354
360
 
355
361
  return (
@@ -6,6 +6,7 @@ import { EntityManager, EntityNamesRecord } from "../entity/entity-manager";
6
6
  import { isEnumProp, isRelationProp, RelationProp } from "../types/types";
7
7
  import { RenderedTemplate } from "../syncer/syncer";
8
8
  import { Template } from "./base-template";
9
+
9
10
  export class Template__view_list extends Template {
10
11
  constructor() {
11
12
  super("view_list");
@@ -55,20 +56,17 @@ export class Template__view_list extends Template {
55
56
  }<img src={${colName}} />}</>`;
56
57
  case "string-datetime":
57
58
  if (col.nullable) {
58
- return `<span className="text-tiny">{${colName} === null ? '-' : DateTime.fromSQL(${colName}).toSQL().slice(0, 10)}</span>`;
59
+ return `<span className="text-tiny">{${colName} === null ? '-' : dateF(${colName})}</span>`;
59
60
  } else {
60
- return `<span className="text-tiny">{DateTime.fromSQL(${colName}).toSQL().slice(0, 10)}</span>`;
61
+ return `<span className="text-tiny">{dateF(${colName})}</span>`;
61
62
  }
62
63
  case "boolean":
63
64
  return `<>{${colName} ? <Label color='green' circular>O</Label> : <Label color='grey' circular>X</Label> }</>`;
64
65
  case "enums":
65
- const { targetMDNames, name } = getEnumInfoFromColName(
66
- entityId,
67
- col.name
68
- );
69
- return `<>{${col.nullable ? `${colName} && ` : ""}${
70
- targetMDNames.constant
71
- }.${name}[${colName}].ko}</>`;
66
+ const { id: enumId } = getEnumInfoFromColName(entityId, col.name);
67
+ return `<>{${
68
+ col.nullable ? `${colName} && ` : ""
69
+ }${enumId}Label[${colName}]}</>`;
72
70
  case "array-images":
73
71
  return `<>{ ${colName}.map(r => ${
74
72
  col.nullable ? `r && ` : ""
@@ -110,13 +108,11 @@ export class Template__view_list extends Template {
110
108
  names: EntityNamesRecord
111
109
  ): (string | null)[] {
112
110
  if (col.renderType === "enums") {
113
- const { modulePath, targetMDNames } = getEnumInfoFromColName(
111
+ const { modulePath, id: enumId } = getEnumInfoFromColName(
114
112
  names.capital,
115
113
  col.name
116
114
  );
117
- return [
118
- `import { ${targetMDNames.constant} } from 'src/services/${modulePath}';`,
119
- ];
115
+ return [`import { ${enumId}Label } from 'src/services/${modulePath}';`];
120
116
  } else if (col.renderType === "object") {
121
117
  try {
122
118
  const relProp = getRelationPropFromColName(entityId, col.name);
@@ -246,14 +242,16 @@ export class Template__view_list extends Template {
246
242
  listParamsNode: RenderingNode
247
243
  ) {
248
244
  const names = EntityManager.getNamesFromId(entityId);
245
+ const entity = EntityManager.get(entityId);
249
246
 
250
247
  // 실제 리스트 컬럼
251
248
  const columns = (columnsNode.children as RenderingNode[])
252
249
  .filter((col) => col.name !== "id")
253
250
  .map((col) => {
251
+ const propCandidate = entity.props.find((p) => p.name === col.name);
254
252
  return {
255
253
  name: col.name,
256
- label: col.label,
254
+ label: propCandidate?.desc ?? col.label,
257
255
  tc: `(row) => ${this.renderColumn(entityId, col, names)}`,
258
256
  };
259
257
  });
@@ -277,24 +275,21 @@ export class Template__view_list extends Template {
277
275
  let key: TemplateKey;
278
276
  let targetMdId = entityId;
279
277
  let enumId: string | undefined;
280
- let idConstant: string | undefined;
281
278
 
282
279
  if (col.renderType === "enums") {
283
280
  if (col.name === "search") {
284
281
  key = "view_enums_dropdown";
285
282
  enumId = `${names.capital}SearchField`;
286
283
  targetMdId = names.capital;
287
- idConstant = "SEARCH_FIELD";
288
284
  } else {
289
285
  key = "view_enums_select";
290
286
  try {
291
- const { targetMDNames, id, name } = getEnumInfoFromColName(
287
+ const { targetMDNames, id } = getEnumInfoFromColName(
292
288
  entityId,
293
289
  col.name
294
290
  );
295
291
  targetMdId = targetMDNames.capital;
296
292
  enumId = id;
297
- idConstant = name;
298
293
  } catch {
299
294
  continue;
300
295
  }
@@ -317,7 +312,6 @@ export class Template__view_list extends Template {
317
312
  options: {
318
313
  entityId: targetMdId,
319
314
  enumId,
320
- idConstant,
321
315
  },
322
316
  });
323
317
  }
@@ -362,7 +356,7 @@ import {
362
356
  } from 'semantic-ui-react';
363
357
  import classNames from 'classnames';
364
358
  import { DateTime } from "luxon";
365
- import { DelButton, EditButton, AppBreadcrumbs, AddButton, useSelection, useListParams, SonamuCol, numF } from '@sonamu-kit/react-sui';
359
+ import { DelButton, EditButton, AppBreadcrumbs, AddButton, useSelection, useListParams, SonamuCol, numF, dateF, datetimeF } from '@sonamu-kit/react-sui';
366
360
 
367
361
  import { ${names.capital}SubsetA } from "src/services/${names.fs}/${
368
362
  names.fs
@@ -391,11 +385,10 @@ export default function ${names.capital}List({}: ${names.capital}ListProps) {
391
385
  });
392
386
 
393
387
  // 리스트 쿼리
394
- const { data, mutate, error } = ${names.capital}Service.use${
388
+ const { data, mutate, error, isLoading } = ${names.capital}Service.use${
395
389
  names.capitalPlural
396
390
  }('A', listParams);
397
391
  const { rows, total } = data ?? {};
398
- const isLoading = !error && !data;
399
392
 
400
393
  // 삭제
401
394
  const confirmDel = (ids: number[]) => {
@@ -424,7 +417,7 @@ export default function ${names.capital}List({}: ${names.capital}ListProps) {
424
417
  // 현재 경로와 타이틀
425
418
  const PAGE = {
426
419
  route: '/admin/${names.fsPlural}',
427
- title: '${names.capital}',
420
+ title: '${entity.title ?? names.capital}',
428
421
  };
429
422
 
430
423
  // 선택
@@ -663,17 +663,14 @@ export const TemplateOptions = z.object({
663
663
  view_enums_select: z.object({
664
664
  entityId: z.string(),
665
665
  enumId: z.string(),
666
- idConstant: z.string(),
667
666
  }),
668
667
  view_enums_dropdown: z.object({
669
668
  entityId: z.string(),
670
669
  enumId: z.string(),
671
- idConstant: z.string(),
672
670
  }),
673
671
  view_enums_buttonset: z.object({
674
672
  entityId: z.string(),
675
673
  enumId: z.string(),
676
- idConstant: z.string(),
677
674
  }),
678
675
  });
679
676
  export type TemplateOptions = z.infer<typeof TemplateOptions>;