@rchemist/listgrid 0.2.14 → 0.3.1

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.
@@ -368,10 +368,17 @@ export class EntityForm extends EntityFormExtensions {
368
368
  result.errors = ['삭제할 대상이 없습니다.'];
369
369
  return result;
370
370
  }
371
- const url = `${this.getUrl()}/delete`;
372
- const formData = {};
373
- formData['revisionEntityName'] = this.getRevisionEntityName();
374
- formData['ids'] = idList;
371
+ // v0.3.0+ rcm-framework 0.1.0 endpoint 표준 (Decision #31).
372
+ // bulk delete = DELETE {url} + RequestBody BulkDeleteRequest{ids,revisionEntityName?}.
373
+ // RFC 9110 *서버 명시 지원* 영역. 0.1.0 baseline (axios 최신) 자연 지원.
374
+ const url = this.getUrl();
375
+ const formData = {
376
+ ids: idList,
377
+ };
378
+ const revisionEntityName = this.getRevisionEntityName();
379
+ if (revisionEntityName) {
380
+ formData['revisionEntityName'] = revisionEntityName;
381
+ }
375
382
  const response = await getExternalApiDataWithError({
376
383
  url: url,
377
384
  method: 'DELETE',
@@ -525,7 +532,10 @@ export class EntityForm extends EntityFormExtensions {
525
532
  return { actionType: renderType, entityForm: form.withErrors(submitFormData.errors), errors };
526
533
  }
527
534
  else {
528
- const targetUrl = renderType === 'create' ? `${this.getUrl()}/add` : `${this.getUrl()}/${this.id}`;
535
+ // v0.3.0+ rcm-framework 0.1.0 endpoint 표준 (Decision #31).
536
+ // create = POST {url} (was POST {url}/add in 0.0.5 line)
537
+ // update = PUT {url}/{id} (unchanged)
538
+ const targetUrl = renderType === 'create' ? this.getUrl() : `${this.getUrl()}/${this.id}`;
529
539
  const method = renderType === 'create' ? 'POST' : 'PUT';
530
540
  // 서버 extension 처리를 위한 헤더 추가
531
541
  const extensionPoint = renderType === 'create' ? ExtensionPoint.PRE_CREATE : ExtensionPoint.PRE_UPDATE;
@@ -30,8 +30,10 @@ export class PageResult {
30
30
  /**
31
31
  * 만약 serverSide가 true 라면 넘어 온 url 을 파라미터로 포함해 내부 프록시 api 를 호출하고 그 결과를 response 로 받는다.
32
32
  */
33
+ // v0.3.0+ — rcm-framework 0.1.0 endpoint 표준 (Decision #31).
34
+ // 검색은 POST {url}/search (RequestBody SearchRequest). underscore prefix 거부.
33
35
  const response = await callExternalHttpRequest({
34
- url: url,
36
+ url: `${url}/search`,
35
37
  method: 'POST',
36
38
  formData: searchForm,
37
39
  ...(extensionOptions?.entityFormName !== undefined
@@ -61,7 +63,12 @@ export class PageResult {
61
63
  }
62
64
  return PageResult.createEmptyResult(searchForm).withErrors(response.error ?? '데이터 로딩 중 오류가 발생했습니다.');
63
65
  }
64
- const newSearchForm = SearchForm.deserialize(response.data.searchForm);
66
+ // v0.3.0+ server echo 호환:
67
+ // 0.0.5 line: response.data.searchForm (deserialize)
68
+ // 0.1.0 line (Decision #31 SearchResponse): response.data.searchRequest (echo only)
69
+ // 둘 다 없으면 client 가 보낸 원본 searchForm 보존 (page/pageSize/sorts/filters 유지).
70
+ const echoForm = response.data.searchForm ?? response.data.searchRequest;
71
+ const newSearchForm = echoForm ? SearchForm.deserialize(echoForm) : searchForm;
65
72
  if (searchForm.hasPreservedFilters()) {
66
73
  searchForm.getPreservedFilters().forEach((filter) => {
67
74
  if (isTrue(filter.remove)) {
@@ -75,16 +82,19 @@ export class PageResult {
75
82
  }
76
83
  });
77
84
  }
78
- // list 또는 content 필드 확인
85
+ // list (0.0.5) 또는 content (Spring Data Page<T> / SearchResponse 0.1.0) 흡수.
79
86
  const listData = response.data.list || response.data.content || [];
80
87
  const responseList = listData.map((item) => ({
81
88
  ...item,
82
89
  id: String(item.id), // id를 문자열로 강제 변환
83
90
  }));
91
+ // pagination 메타: totalCount/totalPage (0.0.5) 또는 totalElements/totalPages (0.1.0).
92
+ const totalCount = response.data.totalCount ?? response.data.totalElements ?? 0;
93
+ const totalPage = response.data.totalPage ?? response.data.totalPages ?? 0;
84
94
  return new PageResult({
85
95
  list: responseList,
86
- totalCount: response.data.totalCount,
87
- totalPage: response.data.totalPage,
96
+ totalCount,
97
+ totalPage,
88
98
  searchForm: newSearchForm,
89
99
  });
90
100
  }
@@ -64,10 +64,22 @@ export const ExcelDownload = async (props) => {
64
64
  }),
65
65
  ];
66
66
  let ws = XLSX.utils.aoa_to_sheet(aoaData);
67
+ const wb = XLSX.utils.book_new();
68
+ // 만약 skipHeader 가 false 라면 맨 처음 행을 제거한다.
69
+ if (!skipHeader) {
70
+ // 워크시트 데이터를 JSON으로 변환
71
+ const jsonData = XLSX.utils.sheet_to_json(ws, { header: 1 }); // header: 1 -> 배열 형태
72
+ // jsonData.shift(); // 첫 번째 행 제거
73
+ // jsonData로 새로운 워크시트 생성
74
+ ws = XLSX.utils.aoa_to_sheet(jsonData);
75
+ }
76
+ // 첫 두 행 스타일 적용
77
+ const range = XLSX.utils.decode_range(ws['!ref'] || 'A1');
67
78
  // 필드 정보를 이용해 셀 타입 및 서식 지정
79
+ // 워크시트 재생성(sheet_to_json -> aoa_to_sheet) 이후에 적용해야
80
+ // cell.t / cell.z 가 보존된다. 재생성 이전에 적용하면 라운드트립 과정에서 손실됨.
68
81
  if (props.fields && props.fields.length > 0) {
69
82
  const fieldMap = new Map(props.fields.map((f) => [f.getName(), f]));
70
- const range = XLSX.utils.decode_range(ws['!ref'] || 'A1');
71
83
  for (let R = 1; R <= range.e.r; ++R) {
72
84
  // 헤더 이후 데이터 행부터
73
85
  for (let C = 0; C <= range.e.c; ++C) {
@@ -94,17 +106,6 @@ export const ExcelDownload = async (props) => {
94
106
  }
95
107
  }
96
108
  }
97
- const wb = XLSX.utils.book_new();
98
- // 만약 skipHeader 가 false 라면 맨 처음 행을 제거한다.
99
- if (!skipHeader) {
100
- // 워크시트 데이터를 JSON으로 변환
101
- const jsonData = XLSX.utils.sheet_to_json(ws, { header: 1 }); // header: 1 -> 배열 형태
102
- // jsonData.shift(); // 첫 번째 행 제거
103
- // jsonData로 새로운 워크시트 생성
104
- ws = XLSX.utils.aoa_to_sheet(jsonData);
105
- }
106
- // 첫 두 행 스타일 적용
107
- const range = XLSX.utils.decode_range(ws['!ref'] || 'A1');
108
109
  if (!skipHeader) {
109
110
  for (let C = range.s.c; C <= range.e.c; C++) {
110
111
  const cell_address = XLSX.utils.encode_cell({ r: 0, c: C });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rchemist/listgrid",
3
- "version": "0.2.14",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "description": "Framework-free React CRUD UI engine — primitive-based design system, data-attr theming, and a full list/form renderer for RCM-framework-style entity backends.",
6
6
  "keywords": [