@axboot-mcp/mcp-server 1.0.0

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 (78) hide show
  1. package/CLAUDE.md +119 -0
  2. package/MCP_TOOL_PLAN.md +710 -0
  3. package/MCP_USAGE.md +914 -0
  4. package/README.md +168 -0
  5. package/REPOSITORY_CONVENTIONS.md +250 -0
  6. package/SEARCH_PARAMS_MCP_TOOL_COMPLETE_PLAN.md +646 -0
  7. package/SEARCH_PARAMS_PLAN.md +2570 -0
  8. package/STORE_PATTERNS.md +1178 -0
  9. package/debug-dto.js +72 -0
  10. package/generate-banner-store.js +62 -0
  11. package/generation-plan.json +2176 -0
  12. package/generation-results.json +1817 -0
  13. package/package.json +45 -0
  14. package/scripts/batch-generate-all.js +159 -0
  15. package/scripts/batch-generate-mcp.js +329 -0
  16. package/scripts/batch-generate-stores-v2.js +272 -0
  17. package/scripts/batch-generate-stores.js +179 -0
  18. package/scripts/batch-plan.json +3810 -0
  19. package/scripts/batch-process.py +90 -0
  20. package/scripts/batch-regenerate.js +356 -0
  21. package/scripts/direct-generate.js +227 -0
  22. package/scripts/execute-batches.js +1911 -0
  23. package/scripts/generate-all-stores.js +144 -0
  24. package/scripts/generate-stores-mcp.js +161 -0
  25. package/scripts/generate-stores-v2.js +450 -0
  26. package/scripts/generate-stores-v3.js +412 -0
  27. package/scripts/generate-stores-v4.js +521 -0
  28. package/scripts/generate-stores.js +382 -0
  29. package/scripts/repos-to-process.json +1899 -0
  30. package/src/config/nh-layout-patterns.ts +166 -0
  31. package/src/docs/HOOK_GENERATION_PLAN.md +2226 -0
  32. package/src/docs/NH_STORE_PATTERNS.md +297 -0
  33. package/src/docs/README.md +216 -0
  34. package/src/docs/index.ts +28 -0
  35. package/src/docs/loader.ts +568 -0
  36. package/src/docs/patterns.json +419 -0
  37. package/src/docs/practical-examples.md +732 -0
  38. package/src/docs/quick-start.md +257 -0
  39. package/src/docs/requirements-analysis-guide.md +364 -0
  40. package/src/docs/rules.json +321 -0
  41. package/src/docs/store-pattern-analysis.md +664 -0
  42. package/src/docs/store-patterns-rules.md +1168 -0
  43. package/src/docs/store-patterns-usage-guide.md +1835 -0
  44. package/src/docs/troubleshooting.md +544 -0
  45. package/src/docs/type-selection-guide.md +572 -0
  46. package/src/docs//354/202/254/354/232/251/353/262/225/AntD-/354/273/264/355/217/254/353/204/214/355/212/270-/354/202/254/354/232/251/353/262/225.md +1515 -0
  47. package/src/docs//354/202/254/354/232/251/353/262/225/DataGrid-/354/202/254/354/232/251/353/262/225.md +866 -0
  48. package/src/docs//354/202/254/354/232/251/353/262/225/FormItem-/354/202/254/354/232/251/353/262/225.md +903 -0
  49. package/src/docs//354/202/254/354/232/251/353/262/225/FormModal-/354/202/254/354/232/251/353/262/225.md +1155 -0
  50. package/src/docs//354/202/254/354/232/251/353/262/225/MCP-/353/260/224/354/235/264/353/270/214/354/275/224/353/224/251-/352/260/200/354/235/264/353/223/234.md +1133 -0
  51. package/src/docs//354/202/254/354/232/251/353/262/225/MSW-Mock-/353/215/260/354/235/264/355/204/260-/354/202/254/354/232/251/353/262/225.md +579 -0
  52. package/src/docs//354/202/254/354/232/251/353/262/225/Search-/354/273/264/355/217/254/353/204/214/355/212/270-/354/202/254/354/232/251/353/262/225.md +738 -0
  53. package/src/docs//354/202/254/354/232/251/353/262/225/Store-/355/214/250/355/204/264-/354/202/254/354/232/251/353/262/225.md +1135 -0
  54. package/src/docs//354/202/254/354/232/251/353/262/225//355/231/224/353/251/264/352/265/254/354/204/261-/355/203/200/354/236/205/353/263/204-/352/260/234/353/260/234/354/210/234/354/204/234.md +1805 -0
  55. package/src/docs//354/202/254/354/232/251/353/262/225//355/231/224/353/251/264/355/203/200/354/236/205/353/263/204-/352/260/234/353/260/234-/355/224/204/353/241/254/355/224/204/355/212/270-/352/260/200/354/235/264/353/223/234.md +946 -0
  56. package/src/docs//354/202/254/354/232/251/353/262/225//355/231/225/354/236/245/355/231/224/353/251/264/355/203/200/354/236/205/353/263/204-/354/203/201/354/204/270-/355/224/204/353/241/254/355/224/204/355/212/270/352/260/200/354/235/264/353/223/234.md +2422 -0
  57. package/src/features/store-features.ts +232 -0
  58. package/src/handlers/analyze-requirements.ts +403 -0
  59. package/src/handlers/analyze.ts +1373 -0
  60. package/src/handlers/generate-from-requirements.ts +250 -0
  61. package/src/handlers/generate-hook.ts +950 -0
  62. package/src/handlers/generate-interactive.ts +840 -0
  63. package/src/handlers/generate-listdatagrid.ts +521 -0
  64. package/src/handlers/generate-multi-stores.ts +577 -0
  65. package/src/handlers/generate-requirements-from-layout.ts +160 -0
  66. package/src/handlers/generate-search-params.ts +717 -0
  67. package/src/handlers/generate.ts +911 -0
  68. package/src/handlers/list-templates.ts +104 -0
  69. package/src/handlers/scan-metadata.ts +485 -0
  70. package/src/handlers/suggest-layout.ts +326 -0
  71. package/src/index.ts +959 -0
  72. package/src/prompts/search-params.md +793 -0
  73. package/src/templates/index.ts +107 -0
  74. package/src/templates/unified.ts +462 -0
  75. package/store-generation-error-patterns.md +225 -0
  76. package/test/useAgentStore.ts +136 -0
  77. package/test-server.js +78 -0
  78. package/tsconfig.json +20 -0
@@ -0,0 +1,107 @@
1
+ import { unifiedTemplate } from './unified.js';
2
+
3
+ export interface TemplateVariables {
4
+ STORE_NAME: string;
5
+ STORE_NAME_PASCAL: string;
6
+ STORE_INTERFACE: string;
7
+ SERVICE_NAME: string;
8
+ LIST_METHOD_NAME: string;
9
+ DELETE_METHOD_NAME?: string | null;
10
+ SAVE_METHOD_NAME?: string | null;
11
+ DETAIL_METHOD_NAME?: string | null;
12
+ DELETE_API_CALL?: string;
13
+ SAVE_API_CALL?: string;
14
+ LIST_REQUEST: string;
15
+ LIST_REQUEST_EXTENDS: string; // 'extends XXX' or empty string
16
+ LIST_RESPONSE: string;
17
+ ACTIONS_EXTENDS: string; // Actions interface extends clause
18
+ DELETE_REQUEST: string;
19
+ DELETE_REQUEST_EXTENDS: string; // 'extends XXX' or empty string
20
+ SAVE_REQUEST: string;
21
+ SAVE_REQUEST_EXTENDS: string; // 'extends XXX' or empty string
22
+ SAVE_PARAM_TYPE: string; // Save 메서드 파라미터 타입 (배열: DtoItem[], 단일: DtoItem)
23
+ DTO_ITEM: string;
24
+ DTO_ITEM_LOWER: string;
25
+ HAS_PAGE: boolean; // 페이징 여부
26
+ HAS_SAVE: boolean; // 저장 API 여부
27
+ HAS_DELETE: boolean; // 삭제 API 여부
28
+ HAS_DETAIL: boolean; // 상세 조회 API 여부
29
+ HAS_LIST: boolean; // 리스트 존재 여부 (선택 패턴 적용용)
30
+ HAS_REORDER: boolean; // 재정렬 기능 여부 (드래그 앤 드롭)
31
+ HAS_LIST_REQUEST_PARAMS: boolean; // List API에 요청 파라미터가 있는지 여부 (void가 아닌 경우)
32
+ HAS_LIST_RESPONSE: boolean; // 응답 타입이 void가 아닌지 여부 (response.ds 접근용)
33
+ HAS_PAGE_IN_REQUEST: boolean; // Request에 pageNumber/pageSize 필드가 있는지 (listRequestValue 초기값 결정용)
34
+ HAS_DATE_RANGE: boolean; // dateRange 패턴 존재 여부 (쌍이 모두 있는 경우)
35
+ HAS_DAY_JS: boolean; // dayjs import 필요 여부 (Dt/Dtm 필드가 1개 이상 있는 경우)
36
+ IS_BGNDTM: boolean; // bgnDtm/endDtm 패턴 여부 (아니면 startDateTime/endDateTime)
37
+ ID_FIELD: string; // __status__ 패턴용 ID 필드 (예: id, bbscttNo)
38
+ ID_FIELD_TYPE?: string; // ID 필드의 실제 타입 (예: 'string', 'number', 'string | undefined')
39
+ ID_FIELD_NEEDS_STRING_CONVERSION: boolean; // ID 필드 타입에 number가 포함되어 String() 변환이 필요한지 여부
40
+ ID_FIELD_VALUE?: string; // id 변수의 변환된 값 ('id' 또는 'String(id)')
41
+ ID_FIELD_KEY_VALUE?: string; // key 변수의 변환된 값 ('key' 또는 'String(key)')
42
+ REQUIRED_FIELDS_DEFAULTS?: string; // Request 필수 필드 기본값
43
+ HAS_EXCEL?: boolean; // 엑셀 다운로드 API 존재 여부
44
+ EXCEL_METHOD_NAME?: string | null; // 엑셀 메서드 이름
45
+ EXCEL_PARAM_TYPE?: string; // 엑셀 메서드 파라미터 타입 (배열: DtoItem[], 단일: ListRequest)
46
+ IS_EXCEL_REQUEST_ARRAY?: boolean; // 엑셀 메서드 Request가 배열인지 여부
47
+ IS_DELETE_REQUEST_ARRAY?: boolean; // 삭제 메서드 Request가 배열인지 여부
48
+ HAS_ARRAY_DELETE_API?: boolean; // 다른 삭제 메서드 중 배열이 있는지 (callMultiDeleteApi 생성용)
49
+ DELETE_PARAM_TYPE?: string; // 삭제 메서드 파라미터 타입 (배열: React.Key[], 단일: number | string)
50
+ DELETE_PARAM_NAME?: string; // 삭제 메서드 파라미터 이름 (배열: ids, 단일: id)
51
+ IMPORTS: string;
52
+ LIST_METHOD_COMMENT?: string; // List 메서드 주석
53
+ DELETE_METHOD_COMMENT?: string; // Delete 메서드 주석
54
+ SAVE_METHOD_COMMENT?: string; // Save 메서드 주석
55
+ DETAIL_METHOD_COMMENT?: string; // Detail 메서드 주석
56
+ CALL_DELETE_API_DECLARATION?: string; // callDeleteApi Actions interface 선언
57
+
58
+ // === 응답 필드 정보 ===
59
+ LIST_RESPONSE_FIELD?: string; // 리스트 응답 필드명 (ds, list 등)
60
+ DETAIL_RESPONSE_FIELD?: string; // 상세 응답 필드명 (rs, detail 등)
61
+ IS_DIRECT_RESPONSE?: boolean; // 응답이 직접 객체인지 여부 (result 자체를 사용)
62
+
63
+ // === 요청 필드 정보 ===
64
+ REQUEST_FIELDS?: string; // 요청 필드 JSON 문자열 (SearchParams 생성용)
65
+
66
+ // === 다중 리스트 관련 변수 ===
67
+ HAS_MULTI_LIST?: boolean; // 다중 리스트 여부 (왼쪽/상단/하단 목록 등)
68
+ SUB_LIST_DTO_ITEM?: string; // 서브 리스트 DTO 타입
69
+ SUB_LIST_METHOD_NAME?: string; // 서브 리스트 메서드 이름
70
+ SUB_LIST_REQUEST_EXTENDS?: string; // SubListRequest extends 절
71
+ SUB_LIST_RESPONSE_FIELD?: string; // 서브 리스트 응답 필드명
72
+ IS_SUB_LIST_PARENT_KEY?: boolean; // 서브 리스트에 상위 키 필드 사용 여부
73
+ PARENT_KEY_FIELD?: string; // 상위 키 필드명 (예: groupCd, categoryCd)
74
+ HAS_SUB_LIST_CHECKBOX?: boolean; // 서브 리스트 체크박스 여부
75
+ HAS_SUB_LIST_EXCEL?: boolean; // 서브 리스트 엑셀 다운로드 여부
76
+ HAS_FORM?: boolean; // 폼 여부 (다중 리스트에서 저장 기능 사용 시)
77
+ HAS_AUTO_LOAD?: boolean; // 자동 로드 여부 (listSelectedRowKey 변경 시 자동으로 서브 리스트 로드)
78
+ SUB_LIST_REQUIRED_FIELDS_DEFAULTS?: string; // 서브 리스트 필수 필드 기본값
79
+
80
+ // === docs 기반 추가 변수 ===
81
+ METADATA_FIELDS?: string;
82
+ METADATA_REQUIRED?: string;
83
+ METADATA_OPTIONAL?: string;
84
+ STATES_REQUIRED?: string;
85
+ STATES_OPTIONAL?: string;
86
+ ACTIONS_REQUIRED?: string;
87
+ ACTIONS_OPTIONAL?: string;
88
+ SUBSCRIBE_SELECTOR?: string;
89
+ CREATE_STATE_FIELDS?: string;
90
+ CALL_LIST_API_TEMPLATE?: string;
91
+ DOCS_REFERENCE?: string;
92
+ STORE_TYPE_NAME?: string;
93
+ STORE_TYPE_DESCRIPTION?: string;
94
+ }
95
+
96
+ /**
97
+ * 통합 템플릿 export
98
+ */
99
+ export { unifiedTemplate };
100
+
101
+ /**
102
+ * 통합 템플릿 반환 함수
103
+ * 모든 타입에 대해 unified 템플릿을 사용합니다.
104
+ */
105
+ export function getTemplate(storeType: number): string {
106
+ return unifiedTemplate;
107
+ }
@@ -0,0 +1,462 @@
1
+ /**
2
+ * 통합 템플릿 (Unified Template)
3
+ * type11 기반으로 모든 기능을 포함하는 단일 템플릿
4
+ *
5
+ * 지원 기능:
6
+ * - 리스트 조회 (LIST)
7
+ * - 마스터-디테일 (MASTER_DETAIL)
8
+ * - 페이지네이션 (HAS_PAGE)
9
+ * - 모달 (MODAL)
10
+ * - 삭제 (DELETE)
11
+ * - 저장 (SAVE)
12
+ * - 상세 조회 (DETAIL)
13
+ * - 엑셀 다운로드 (EXCEL)
14
+ * - 체크박스 (CHECKBOX)
15
+ * - 날짜 범위 검색 (DATE_RANGE)
16
+ * - 재정렬 (REORDER)
17
+ * - 트리 (TREE)
18
+ * - 다중 리스트 (HAS_MULTI_LIST): 왼쪽/상단/하단 목록 등 2개 이상의 리스트
19
+ */
20
+ export const unifiedTemplate = `import { AXDGDataItem{{#HAS_PAGE}}, AXDGPage{{/HAS_PAGE}}, AXDGSortParam } from "@axboot/datagrid";
21
+ {{#HAS_PAGE}}import { pageStoreActions } from "@core/stores/pageStoreActions";
22
+ {{/HAS_PAGE}}import { StoreActions } from "@core/stores/types";
23
+ {{#HAS_PAGE}}import { PageStoreActions } from "@core/stores/types";
24
+ {{/HAS_PAGE}}import { deleteEmptyValue } from "@core/utils/object";
25
+ {{#HAS_SAVE}}import { errorHandling } from "utils";{{/HAS_SAVE}}
26
+ {{#HAS_DAY_JS}}import { DT_FORMAT } from "@types";
27
+ {{/HAS_DAY_JS}}import { ProgramFn } from "@types";
28
+ {{#HAS_DAY_JS}}import dayjs from "dayjs";{{/HAS_DAY_JS}}
29
+ {{IMPORTS}}
30
+ {{#HAS_DATE_RANGE}}import { dateRangeToDts } from "utils";{{/HAS_DATE_RANGE}}
31
+ import { getTabStoreListener } from "stores";
32
+ import { create } from "zustand";
33
+ import { subscribeWithSelector } from "zustand/middleware";
34
+ import { shallow } from "zustand/shallow";
35
+
36
+ interface ListRequest {{LIST_REQUEST_EXTENDS}}{
37
+ {{#HAS_DATE_RANGE}}dateRange?: [string, string];{{/HAS_DATE_RANGE}}
38
+ }
39
+
40
+ interface DtoItem extends {{DTO_ITEM}} {}
41
+
42
+ // ========== interface MetaData ==========
43
+ interface MetaData {
44
+ programFn?: ProgramFn;
45
+ listRequestValue: ListRequest;
46
+ listColWidths: number[];
47
+ listSortParams: AXDGSortParam[];
48
+ {{#HAS_PAGE}}listSelectedRowKey?: React.Key;
49
+ listCheckedIndexes?: number[];{{/HAS_PAGE}}
50
+ {{#HAS_SAVE}}modalOpen: boolean;{{/HAS_SAVE}}
51
+
52
+ // 다중 리스트 (왼쪽/상단/하단 목록 등)
53
+ {{#HAS_MULTI_LIST}}subListRequestValue: SubListRequest;
54
+ subListColWidths: number[];
55
+ subListSortParams: AXDGSortParam[];
56
+ subListSelectedRowKey?: React.Key;{{/HAS_MULTI_LIST}}
57
+ {{#HAS_MULTI_LIST}}{{#HAS_SUB_LIST_CHECKBOX}}subListCheckedIndexes?: number[];{{/HAS_SUB_LIST_CHECKBOX}}{{/HAS_MULTI_LIST}}
58
+ {{#HAS_MULTI_LIST}}{{#HAS_FORM}}formActive: boolean;
59
+ saveRequestValue?: SaveRequest;
60
+ detail?: DetailResponse;{{/HAS_FORM}}{{/HAS_MULTI_LIST}}
61
+ }
62
+
63
+ // ========== interface States ==========
64
+ interface States extends MetaData {
65
+ routePath?: string;
66
+ listSpinning: boolean;
67
+ {{#HAS_EXCEL}}excelSpinning: boolean;{{/HAS_EXCEL}}
68
+ {{#HAS_DELETE}}deleteSpinning: boolean;{{/HAS_DELETE}}
69
+ {{#HAS_SAVE}}saveSpinning: boolean;{{/HAS_SAVE}}
70
+ {{#HAS_DETAIL}}detailSpinning: boolean;{{/HAS_DETAIL}}
71
+ listData: AXDGDataItem<DtoItem>[];
72
+ {{#HAS_PAGE}}listPage: AXDGPage;{{/HAS_PAGE}}
73
+
74
+ // 마스터-디테일 (오른쪽 상세)
75
+ {{#HAS_LIST}}selectedItem?: DtoItem;{{/HAS_LIST}}
76
+ {{#HAS_DETAIL}}detail?: DtoItem;{{/HAS_DETAIL}}
77
+ {{#HAS_REORDER}}isReordered: boolean;{{/HAS_REORDER}}
78
+
79
+ // 체크박스
80
+ {{#HAS_PAGE}}checkedRowKeys: React.Key[];{{/HAS_PAGE}}
81
+
82
+ // 다중 리스트
83
+ {{#HAS_MULTI_LIST}}subListSpinning: boolean;
84
+ subListData: AXDGDataItem<SubDtoItem>[];{{/HAS_MULTI_LIST}}
85
+ }
86
+
87
+ // ========== const createState ==========
88
+ const createState: States = {
89
+ listRequestValue: {{#HAS_PAGE_IN_REQUEST}}{
90
+ pageNumber: 1,
91
+ pageSize: 100,
92
+ {{REQUIRED_FIELDS_DEFAULTS}}
93
+ {{#HAS_DATE_RANGE}}dateRange: [
94
+ dayjs().add(-1, "month").startOf("day").format(DT_FORMAT.DATETIME_HHMM),
95
+ dayjs().endOf("day").format(DT_FORMAT.DATETIME_HHMM),
96
+ ],{{/HAS_DATE_RANGE}}
97
+ },{{/HAS_PAGE_IN_REQUEST}}{{^HAS_PAGE_IN_REQUEST}}{},{{/HAS_PAGE_IN_REQUEST}}
98
+ listColWidths: [],
99
+ listSpinning: false,
100
+ {{#HAS_EXCEL}}excelSpinning: false,{{/HAS_EXCEL}}
101
+ {{#HAS_DELETE}}deleteSpinning: false,{{/HAS_DELETE}}
102
+ {{#HAS_SAVE}}saveSpinning: false,{{/HAS_SAVE}}
103
+ {{#HAS_DETAIL}}detailSpinning: false,{{/HAS_DETAIL}}
104
+ listData: [],
105
+ {{#HAS_PAGE}}listPage: {
106
+ currentPage: 0,
107
+ totalPages: 0,
108
+ },{{/HAS_PAGE}}
109
+ listSortParams: [],
110
+ {{#HAS_REORDER}}isReordered: false,{{/HAS_REORDER}}
111
+ {{#HAS_LIST}}selectedItem: undefined,{{/HAS_LIST}}
112
+ {{#HAS_DETAIL}}detail: undefined,{{/HAS_DETAIL}}
113
+ {{#HAS_SAVE}}modalOpen: false,{{/HAS_SAVE}}
114
+ {{#HAS_PAGE}}checkedRowKeys: [],
115
+ listSelectedRowKey: "",{{/HAS_PAGE}}
116
+
117
+ // 다중 리스트 초기값
118
+ {{#HAS_MULTI_LIST}}subListRequestValue: {},
119
+ subListColWidths: [],
120
+ subListSortParams: [],
121
+ subListSelectedRowKey: undefined,{{/HAS_MULTI_LIST}}
122
+ {{#HAS_MULTI_LIST}}{{#HAS_SUB_LIST_CHECKBOX}}subListCheckedIndexes: [],{{/HAS_SUB_LIST_CHECKBOX}}{{/HAS_MULTI_LIST}}
123
+ {{#HAS_MULTI_LIST}}{{#HAS_FORM}}formActive: true,
124
+ saveRequestValue: { {{SUB_LIST_REQUIRED_FIELDS_DEFAULTS}} },
125
+ detail: { {{SUB_LIST_REQUIRED_FIELDS_DEFAULTS}} },{{/HAS_FORM}}{{/HAS_MULTI_LIST}}
126
+ {{#HAS_MULTI_LIST}}subListSpinning: false,
127
+ subListData: [],{{/HAS_MULTI_LIST}}
128
+ };
129
+
130
+ // ========== interface Actions ==========
131
+ interface Actions {{ACTIONS_EXTENDS}}{
132
+ setListRequestValue: (requestValue: ListRequest) => void;
133
+ setListColWidths: (colWidths: number[]) => void;
134
+ setListSortParams: (sortParams: AXDGSortParam[]) => void;
135
+ setListSpinning: (spinning: boolean) => void;
136
+ callListApi: (request?: ListRequest) => Promise<void>;
137
+ {{#HAS_EXCEL}}callExcelDownloadApi: (request?: {{EXCEL_PARAM_TYPE}}) => Promise<void>;{{/HAS_EXCEL}}
138
+ {{CALL_DELETE_API_DECLARATION}}
139
+ {{^IS_DELETE_REQUEST_ARRAY}}{{#HAS_ARRAY_DELETE_API}}callMultiDeleteApi: (keys: React.Key[]) => Promise<void>;{{/HAS_ARRAY_DELETE_API}}{{/IS_DELETE_REQUEST_ARRAY}}
140
+ {{#HAS_SAVE}}callSaveApi: (request?: {{SAVE_PARAM_TYPE}}) => Promise<void>;{{/HAS_SAVE}}
141
+ {{#HAS_PAGE}}changeListPage: (currentPage: number, pageSize?: number) => Promise<void>;
142
+ setListSelectedRowKey: (key?: React.Key) => void;{{/HAS_PAGE}}
143
+ {{#HAS_LIST}}setSelectedItem: (selectedItem?: DtoItem) => void;{{/HAS_LIST}}
144
+ {{#HAS_DETAIL}}setDetail: (detail?: DtoItem) => void;
145
+ callDetailApi: (id: number | string) => Promise<void>;{{/HAS_DETAIL}}
146
+ {{#HAS_SAVE}}setModalOpen: (open: boolean) => void;{{/HAS_SAVE}}
147
+ {{#HAS_PAGE}}setCheckedRowKeys: (checkedRowKeys: React.Key[]) => void;{{/HAS_PAGE}}
148
+ {{#HAS_REORDER}}setListData: (listData: AXDGDataItem<DtoItem>[]) => void;
149
+ saveReorder: () => Promise<void>;{{/HAS_REORDER}}
150
+
151
+ // 다중 리스트 액션
152
+ {{#HAS_MULTI_LIST}}callSubListApi: (request?: SubListRequest) => Promise<void>;
153
+ setSubListRequestValue: (requestValue: SubListRequest) => void;
154
+ setSubListColWidths: (colWidths: number[]) => void;
155
+ setSubListSortParams: (sortParams: AXDGSortParam[]) => void;
156
+ setSubListSpinning: (spinning: boolean) => void;
157
+ setSubListSelectedRowKey: (key?: React.Key) => void;{{/HAS_MULTI_LIST}}
158
+ {{#HAS_MULTI_LIST}}{{#HAS_SUB_LIST_CHECKBOX}}setSubListCheckedIndexes: (indexes?: number[]) => void;
159
+ addSubListData: (list: SubDtoItem[]) => void;
160
+ delSubListData: (indexes: number[]) => void;
161
+ setSubListData: (list: AXDGDataItem<SubDtoItem>[], reset?: boolean) => void;{{/HAS_SUB_LIST_CHECKBOX}}{{/HAS_MULTI_LIST}}
162
+ {{#HAS_MULTI_LIST}}{{#HAS_FORM}}cancelFormActive: () => void;
163
+ setFormActive: () => void;{{/HAS_FORM}}{{/HAS_MULTI_LIST}}
164
+ }
165
+
166
+ // ========== const createActions ==========
167
+ const createActions: StoreActions<States & Actions, Actions> = (set, get) => ({
168
+ {{#HAS_PAGE}}...pageStoreActions(set, get, { createState }),
169
+ syncMetadata: (s = createState) => set(s),
170
+ onMountApp: async () => {},{{/HAS_PAGE}}
171
+ setListRequestValue: (requestValues) => set({ listRequestValue: requestValues }),
172
+ setListColWidths: (colWidths) => set({ listColWidths: colWidths }),
173
+ setListSortParams: (sortParams) => set({ listSortParams: sortParams }),
174
+ setListSpinning: (spinning) => set({ listSpinning: spinning }),
175
+
176
+ {{LIST_METHOD_COMMENT}}
177
+ callListApi: async (request) => {
178
+ if (get().listSpinning) return;
179
+ set({ listSpinning: true });
180
+
181
+ try {
182
+ {{#HAS_LIST_REQUEST_PARAMS}}{{#HAS_PAGE}}const apiParam: ListRequest = { ...get().listRequestValue, ...request };{{/HAS_PAGE}}
183
+ {{^HAS_PAGE}}const apiParam: ListRequest = request ?? ({} as ListRequest);{{/HAS_PAGE}}
184
+
185
+ {{#HAS_PAGE}}{{#HAS_DATE_RANGE}}const response = await {{SERVICE_NAME}}.{{LIST_METHOD_NAME}}(
186
+ deleteEmptyValue({
187
+ ...apiParam,
188
+ ...dateRangeToDts(apiParam.dateRange, {
189
+ {{#IS_BGNDTM}}bgnDtm: DT_FORMAT.DATE + " 00:00:00",endDtm: DT_FORMAT.DATE + " 23:59:59",{{/IS_BGNDTM}}
190
+ {{^IS_BGNDTM}}startDateTime: DT_FORMAT.DATETIME_HHMMSS,endDateTime: DT_FORMAT.DATE + " 23:59:59",{{/IS_BGNDTM}}
191
+ }),
192
+ })
193
+ );{{/HAS_DATE_RANGE}}{{^HAS_DATE_RANGE}}const response = await {{SERVICE_NAME}}.{{LIST_METHOD_NAME}}(
194
+ deleteEmptyValue(apiParam)
195
+ );{{/HAS_DATE_RANGE}}{{/HAS_PAGE}}
196
+ {{^HAS_PAGE}}const response = await {{SERVICE_NAME}}.{{LIST_METHOD_NAME}}(
197
+ deleteEmptyValue(apiParam)
198
+ );{{/HAS_PAGE}}{{/HAS_LIST_REQUEST_PARAMS}}
199
+ {{^HAS_LIST_REQUEST_PARAMS}}const apiParam = {} as ListRequest;
200
+ const response = await {{SERVICE_NAME}}.{{LIST_METHOD_NAME}}();{{/HAS_LIST_REQUEST_PARAMS}}
201
+
202
+ set({
203
+ {{#HAS_PAGE_IN_REQUEST}}listRequestValue: apiParam,{{/HAS_PAGE_IN_REQUEST}}
204
+ {{#HAS_LIST_RESPONSE}}listData: response.{{LIST_RESPONSE_FIELD}}.map((values) => ({ values })),{{/HAS_LIST_RESPONSE}}
205
+ {{^HAS_LIST_RESPONSE}}listData: [],{{/HAS_LIST_RESPONSE}}
206
+ {{#HAS_REORDER}}isReordered: false,{{/HAS_REORDER}}
207
+ {{#HAS_PAGE}}listPage: {
208
+ currentPage: response.page.pageNumber ?? 1,
209
+ pageSize: response.page.pageSize ?? 0,
210
+ totalPages: response.page.pageCount ?? 0,
211
+ totalElements: response.page?.totalCount,
212
+ },{{/HAS_PAGE}}
213
+ });
214
+ }{{#HAS_SAVE}} catch (err) {
215
+ await errorHandling(err);
216
+ }{{/HAS_SAVE}} finally {
217
+ set({ listSpinning: false });
218
+ }
219
+ },
220
+
221
+ {{#HAS_PAGE}}changeListPage: async (pageNumber, pageSize) => {
222
+ await get().callListApi({ ...get().listRequestValue, pageNumber, pageSize });
223
+ },
224
+
225
+ setListSelectedRowKey: (key) => set({ listSelectedRowKey: key }),{{/HAS_PAGE}}
226
+
227
+ {{#HAS_REORDER}}setListData: (listData) => set({ listData, isReordered: true }),
228
+
229
+ saveReorder: async () => {
230
+ set({ listSpinning: true });
231
+ try {
232
+ // TODO: 재정렬 저장 API 호출 구현 필요
233
+ // const sortedData = get().listData.map((item, index) => ({
234
+ // ...item.values,
235
+ // sortOrdr: index + 1,
236
+ // }));
237
+ // await {{SERVICE_NAME}}.saveSortOrder(sortedData);
238
+ set({ isReordered: false });
239
+ }{{#HAS_SAVE}} catch (err) {
240
+ await errorHandling(err);
241
+ }{{/HAS_SAVE}} finally {
242
+ set({ listSpinning: false });
243
+ }
244
+ },{{/HAS_REORDER}}
245
+
246
+ {{#HAS_LIST}}setSelectedItem: (selectedItem) => {
247
+ set({ selectedItem{{#HAS_DETAIL}}, detailSpinning: true{{/HAS_DETAIL}} });
248
+ {{#HAS_SAVE}}if (!selectedItem || (selectedItem as any)["__status__"] === "C") {
249
+ set({ modalOpen: false });
250
+ }{{/HAS_SAVE}}
251
+ {{#HAS_DETAIL}}set({ detailSpinning: false });{{/HAS_DETAIL}}
252
+ },{{/HAS_LIST}}
253
+
254
+ {{#HAS_DETAIL}}setDetail: (detail) => {
255
+ set({ detail });
256
+ },{{/HAS_DETAIL}}
257
+
258
+ {{#HAS_SAVE}}setModalOpen: (open) => set({ modalOpen: open }),{{/HAS_SAVE}}
259
+
260
+ {{#HAS_EXCEL}}{{LIST_METHOD_COMMENT}}
261
+ callExcelDownloadApi: async (request) => {
262
+ if (get().excelSpinning) return;
263
+ set({ excelSpinning: true });
264
+
265
+ try {
266
+ {{#IS_EXCEL_REQUEST_ARRAY}}
267
+ if (!request) return;
268
+ await {{SERVICE_NAME}}.{{EXCEL_METHOD_NAME}}(request);
269
+ {{/IS_EXCEL_REQUEST_ARRAY}}
270
+ {{^IS_EXCEL_REQUEST_ARRAY}}
271
+ const { pageNumber, pageSize, ...restParams }: ListRequest = {
272
+ {{#HAS_PAGE}}...get().listRequestValue,{{/HAS_PAGE}}
273
+ ...request,
274
+ };
275
+ await {{SERVICE_NAME}}.{{EXCEL_METHOD_NAME}}(deleteEmptyValue(restParams));
276
+ {{/IS_EXCEL_REQUEST_ARRAY}}
277
+ } finally {
278
+ set({ excelSpinning: false });
279
+ }
280
+ },{{/HAS_EXCEL}}
281
+
282
+ {{#HAS_DELETE}}{{DELETE_METHOD_COMMENT}}
283
+ callDeleteApi: async ({{DELETE_PARAM_NAME}}) => {
284
+ if (get().deleteSpinning) return;
285
+
286
+ try {
287
+ set({ deleteSpinning: true });
288
+ {{DELETE_API_CALL}}
289
+ await get().callListApi();
290
+ }{{#HAS_SAVE}} catch (err) {
291
+ await errorHandling(err);
292
+ }{{/HAS_SAVE}} finally {
293
+ set({ deleteSpinning: false });
294
+ }
295
+ },{{/HAS_DELETE}}
296
+
297
+ {{^IS_DELETE_REQUEST_ARRAY}}{{#HAS_ARRAY_DELETE_API}}{{DELETE_METHOD_COMMENT}}
298
+ callMultiDeleteApi: async (keys: React.Key[]) => {
299
+ if (get().deleteSpinning || keys.length === 0) return;
300
+
301
+ try {
302
+ set({ deleteSpinning: true });
303
+ for (const key of keys) {
304
+ await {{SERVICE_NAME}}.{{DELETE_METHOD_NAME}}({ {{#HAS_PAGE}}...get().listRequestValue,{{/HAS_PAGE}} {{ID_FIELD}}: {{ID_FIELD_KEY_VALUE}} });
305
+ }
306
+ await get().callListApi();
307
+ }{{#HAS_SAVE}} catch (err) {
308
+ await errorHandling(err);
309
+ }{{/HAS_SAVE}} finally {
310
+ set({ deleteSpinning: false });
311
+ }
312
+ },{{/HAS_ARRAY_DELETE_API}}{{/IS_DELETE_REQUEST_ARRAY}}
313
+
314
+ {{#HAS_SAVE}}{{SAVE_METHOD_COMMENT}}
315
+ callSaveApi: async (request) => {
316
+ if (get().saveSpinning) return;
317
+ if (!request) return;
318
+
319
+ try {
320
+ set({ saveSpinning: true });
321
+ {{SAVE_API_CALL}}
322
+ await get().callListApi();
323
+ set({ modalOpen: false });
324
+ } catch (err) {
325
+ await errorHandling(err);
326
+ } finally {
327
+ set({ saveSpinning: false });
328
+ }
329
+ },{{/HAS_SAVE}}
330
+
331
+ {{#HAS_DETAIL}}{{DETAIL_METHOD_COMMENT}}
332
+ callDetailApi: async (id) => {
333
+ if (get().detailSpinning) return;
334
+ set({ detailSpinning: true });
335
+
336
+ try {
337
+ const result = await {{SERVICE_NAME}}.{{DETAIL_METHOD_NAME}}({
338
+ {{#HAS_PAGE_IN_REQUEST}}...get().listRequestValue,{{/HAS_PAGE_IN_REQUEST}}
339
+ {{ID_FIELD}}: {{ID_FIELD_VALUE}}
340
+ });
341
+ {{#IS_DIRECT_RESPONSE}}set({ detail: result });{{/IS_DIRECT_RESPONSE}}
342
+ {{^IS_DIRECT_RESPONSE}}set({ detail: result.{{DETAIL_RESPONSE_FIELD}} });{{/IS_DIRECT_RESPONSE}}
343
+ }{{#HAS_SAVE}} catch (err) {
344
+ await errorHandling(err);
345
+ }{{/HAS_SAVE}} finally {
346
+ set({ detailSpinning: false });
347
+ }
348
+ },{{/HAS_DETAIL}}
349
+
350
+ {{#HAS_PAGE}}setCheckedRowKeys: (checkedRowKeys) => set({ checkedRowKeys }),{{/HAS_PAGE}}
351
+
352
+ // ========== 다중 리스트 액션 ==========
353
+ {{#HAS_MULTI_LIST}}setSubListRequestValue: (requestValues) => set({ subListRequestValue: requestValues }),
354
+ setSubListColWidths: (colWidths) => set({ subListColWidths: colWidths }),
355
+ setSubListSortParams: (sortParams) => set({ subListSortParams: sortParams }),
356
+ setSubListSpinning: (spinning) => set({ subListSpinning: spinning }),
357
+
358
+ callSubListApi: async (request) => {
359
+ set({ subListSpinning: true, subListData: [], subListSelectedRowKey: "" });
360
+
361
+ try {
362
+ {{#HAS_PAGE}}if (!get().listSelectedRowKey) return;{{/HAS_PAGE}}
363
+
364
+ const apiParam = request ?? {
365
+ {{#HAS_PAGE}}{{#IS_SUB_LIST_PARENT_KEY}}{{PARENT_KEY_FIELD}}: get().listSelectedRowKey,{{/IS_SUB_LIST_PARENT_KEY}}{{/HAS_PAGE}}
366
+ {{^HAS_PAGE}}// TODO: 상위 키 필드 설정 필요{{/HAS_PAGE}}
367
+ };
368
+ const response = await {{SERVICE_NAME}}.{{SUB_LIST_METHOD_NAME}}(deleteEmptyValue(apiParam));
369
+
370
+ set({
371
+ subListData: (response.{{SUB_LIST_RESPONSE_FIELD}} ?? []).map((values: {{SUB_LIST_DTO_ITEM}}) => ({
372
+ values,
373
+ })),
374
+ });
375
+ }{{#HAS_SAVE}} catch (err) {
376
+ await errorHandling(err);
377
+ }{{/HAS_SAVE}} finally {
378
+ set({ subListSpinning: false });
379
+ }
380
+ },
381
+
382
+ setSubListSelectedRowKey: (key) => set({ subListSelectedRowKey: key }),{{/HAS_MULTI_LIST}}
383
+ {{#HAS_MULTI_LIST}}{{#HAS_SUB_LIST_CHECKBOX}}setSubListCheckedIndexes: (indexes) => set({ subListCheckedIndexes: indexes }),
384
+
385
+ addSubListData: (list) => {
386
+ const addDataGridList = (await import("@core/utils")).addDataGridList;
387
+ const listData = addDataGridList<{{SUB_LIST_DTO_ITEM}}>(get().subListData ?? [], list);
388
+ set({ subListData: [...listData] });
389
+ },
390
+
391
+ delSubListData: (indexes) => {
392
+ const delDataGridList = (await import("@core/utils")).delDataGridList;
393
+ const listData = delDataGridList(get().subListData ?? [], indexes);
394
+ set({ subListData: [...listData], subListCheckedIndexes: [] });
395
+ },
396
+
397
+ setSubListData: (list, reset) => {
398
+ if (reset) {
399
+ set({
400
+ subListCheckedIndexes: [],
401
+ subListSelectedRowKey: undefined,
402
+ subListData: list,
403
+ });
404
+ } else {
405
+ set({ subListData: list });
406
+ }
407
+ },{{/HAS_SUB_LIST_CHECKBOX}}{{/HAS_MULTI_LIST}}
408
+ {{#HAS_MULTI_LIST}}{{#HAS_FORM}}cancelFormActive: () => {
409
+ set({ formActive: false, listSelectedRowKey: undefined });
410
+ },
411
+
412
+ setFormActive: () => {
413
+ set({ formActive: true, detail: undefined, saveRequestValue: undefined });
414
+ },{{/HAS_FORM}}{{/HAS_MULTI_LIST}}
415
+ });
416
+
417
+ // ========== export ==========
418
+ export interface {{STORE_INTERFACE}} extends States, Actions{{#HAS_PAGE}}
419
+ , PageStoreActions<States>{{/HAS_PAGE}} {}
420
+
421
+ export const {{STORE_NAME}} = create(
422
+ subscribeWithSelector<{{STORE_INTERFACE}}>((set, get) => ({
423
+ ...createState,
424
+ ...createActions(set, get),
425
+ })),
426
+ );
427
+
428
+ // ========== Store.subscribe ==========
429
+ {{STORE_NAME}}.subscribe(
430
+ (s): Record<keyof MetaData, any> => ({
431
+ programFn: s.programFn,
432
+ listRequestValue: s.listRequestValue,
433
+ listColWidths: s.listColWidths,
434
+ listSortParams: s.listSortParams,
435
+ {{#HAS_PAGE}}listSelectedRowKey: s.listSelectedRowKey,
436
+ listCheckedIndexes: s.listCheckedIndexes,{{/HAS_PAGE}}
437
+ {{#HAS_SAVE}}modalOpen: s.modalOpen{{/HAS_SAVE}}
438
+ {{#HAS_MULTI_LIST}},
439
+ subListRequestValue: s.subListRequestValue,
440
+ subListColWidths: s.subListColWidths,
441
+ subListSortParams: s.subListSortParams,
442
+ subListSelectedRowKey: s.subListSelectedRowKey{{/HAS_MULTI_LIST}}
443
+ {{#HAS_MULTI_LIST}}{{#HAS_SUB_LIST_CHECKBOX}},
444
+ subListCheckedIndexes: s.subListCheckedIndexes{{/HAS_SUB_LIST_CHECKBOX}}{{/HAS_MULTI_LIST}}
445
+ {{#HAS_MULTI_LIST}}{{#HAS_FORM}},
446
+ formActive: s.formActive,
447
+ saveRequestValue: s.saveRequestValue,
448
+ detail: s.detail{{/HAS_FORM}}{{/HAS_MULTI_LIST}}
449
+ }),
450
+ getTabStoreListener<MetaData>(createState.routePath),
451
+ { equalityFn: shallow },
452
+ );
453
+
454
+ {{#HAS_AUTO_LOAD}}
455
+ {{STORE_NAME}}.subscribe(
456
+ (s) => [s.listSelectedRowKey],
457
+ ([listSelectedRowKey]) => {
458
+ {{STORE_NAME}}.getState().callSubListApi();
459
+ },
460
+ { equalityFn: shallow },
461
+ );{{/HAS_AUTO_LOAD}};
462
+ `;