@iyulab/enterprise 0.9.1 → 0.10.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.
- package/CHANGELOG.md +27 -0
- package/README.md +13 -1
- package/dist/data/ODataService.d.ts +6 -2
- package/dist/helpers/ProgressHelper.d.ts +10 -4
- package/dist/helpers/constants.d.ts +6 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/index.js +28 -17
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.1
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`ODataService` gained `apiPut<T>(path, body?)`.** The other three custom-REST verbs
|
|
8
|
+
(`apiGet`/`apiPost`/`apiPatch`/`apiDelete`) were already there; `PUT` was the one missing member
|
|
9
|
+
of that set, even though the underlying `@iyulab/http-client` `HttpClient` has always had `.put()`.
|
|
10
|
+
A consumer whose backend models "replace this resource" as `PUT` had no way to reach it through
|
|
11
|
+
this wrapper. Passing a `FormData` body to `apiPost`/`apiPut`/`apiPatch` already worked before this
|
|
12
|
+
change and needed no fix — `HttpClient` never forces JSON serialization on it and lets the browser
|
|
13
|
+
set the multipart boundary itself.
|
|
14
|
+
|
|
15
|
+
## 0.10.0
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **`ProgressHelper.validateProgress`/`ratioToPercent` now return `number | null` instead of
|
|
20
|
+
folding a missing input into `0`.** "No value yet" and "0% progress" are different facts and
|
|
21
|
+
were rendering identically. A call site that already narrows its input to a definite `number`
|
|
22
|
+
keeps getting a definite `number` back (an overload preserves this); a call site that may pass
|
|
23
|
+
`null`/`undefined` now has to handle a `null` result explicitly.
|
|
24
|
+
- **`CurrencyHelper.formatCurrency`, `DateHelper.formatDate`/`formatLocalDate`/`formatDateTime`,
|
|
25
|
+
and `UrgencyHelper.formatDaysRemaining` now render a missing value as an em dash (`—`) instead
|
|
26
|
+
of a hyphen (`-`).** The two glyphs looked almost identical but meant different things; the
|
|
27
|
+
empty-value string is now one shared constant (`EMPTY_VALUE_DISPLAY`, exported from
|
|
28
|
+
`@iyulab/enterprise`) instead of eight independent hardcoded literals across the four helpers.
|
|
29
|
+
|
|
3
30
|
## 0.9.1
|
|
4
31
|
|
|
5
32
|
### Fixed
|
package/README.md
CHANGED
|
@@ -95,9 +95,21 @@ export const svc = createODataService({
|
|
|
95
95
|
|
|
96
96
|
await svc.odataGet<Order>('Orders', { $top: '20' }) // value 배열 언랩
|
|
97
97
|
await svc.odataPost<Order>('Orders', { name: 'A', note: '' }) // '' → null 정규화 + 성공 토스트
|
|
98
|
-
await svc.apiDelete('orders/7') // 204 안전
|
|
99
98
|
svc.odataUrl('Orders') // flex-table useODataSource 엔드포인트
|
|
100
99
|
svc.sourceDefaults // { baseUrl, onUnauthorized } 주입용
|
|
100
|
+
|
|
101
|
+
// custom REST — GET/POST/PUT/PATCH/DELETE 전부, 204 빈 바디 안전 파싱 포함
|
|
102
|
+
await svc.apiGet<Order>('reports/summary')
|
|
103
|
+
await svc.apiPost<Order>('orders', { name: 'A' })
|
|
104
|
+
await svc.apiPut<Order>('orders/7', { name: 'A (revised)' }) // 리소스 전체 교체
|
|
105
|
+
await svc.apiPatch<Order>('orders/7', { note: 'urgent' })
|
|
106
|
+
await svc.apiDelete('orders/7') // 204 안전
|
|
107
|
+
|
|
108
|
+
// body가 FormData 인스턴스면 그대로(직렬화 없이) 멀티파트로 전송된다 —
|
|
109
|
+
// Content-Type은 브라우저가 boundary와 함께 자동 설정한다. apiPost/apiPut/apiPatch 전부 동일.
|
|
110
|
+
const form = new FormData()
|
|
111
|
+
form.append('file', file)
|
|
112
|
+
await svc.apiPost<Order>('orders/7/attachments', form)
|
|
101
113
|
```
|
|
102
114
|
|
|
103
115
|
주입 항목:
|
|
@@ -73,9 +73,13 @@ export interface ODataService {
|
|
|
73
73
|
odataDelete(entity: string, id: string): Promise<void>;
|
|
74
74
|
/** custom REST GET — 204 등 빈 바디를 안전 파싱. */
|
|
75
75
|
apiGet<T>(path: string): Promise<T>;
|
|
76
|
-
/** custom REST POST.
|
|
76
|
+
/** custom REST POST. `body`가 `FormData` 인스턴스면 그대로(직렬화 없이) 멀티파트로
|
|
77
|
+
* 전송된다 — `@iyulab/http-client`가 Content-Type을 브라우저 자동 설정에 맡기고
|
|
78
|
+
* JSON 직렬화 분기를 타지 않는다. `apiPut`/`apiPatch`도 동일하게 동작한다. */
|
|
77
79
|
apiPost<T>(path: string, body?: unknown): Promise<T>;
|
|
78
|
-
/** custom REST
|
|
80
|
+
/** custom REST PUT(리소스 전체 교체/생성). `body`의 `FormData` 처리는 `apiPost` 참조. */
|
|
81
|
+
apiPut<T>(path: string, body?: unknown): Promise<T>;
|
|
82
|
+
/** custom REST PATCH. `body`의 `FormData` 처리는 `apiPost` 참조. */
|
|
79
83
|
apiPatch<T>(path: string, body?: unknown): Promise<T>;
|
|
80
84
|
/** custom REST DELETE — 대부분 204 No Content. */
|
|
81
85
|
apiDelete<T = void>(path: string): Promise<T>;
|
|
@@ -12,17 +12,23 @@ export declare class ProgressHelper {
|
|
|
12
12
|
medium?: number;
|
|
13
13
|
}): string;
|
|
14
14
|
/**
|
|
15
|
-
* Validate and clamp progress to 0-100 range
|
|
15
|
+
* Validate and clamp progress to 0-100 range.
|
|
16
|
+
*
|
|
17
|
+
* Returns `null` for a missing/unparsable input rather than `0` — "no progress value yet"
|
|
18
|
+
* and "0% progress" are different facts and must not render identically.
|
|
16
19
|
* @param progress - Raw progress value
|
|
17
20
|
* @param round - Whether to round to integer (default: true)
|
|
18
21
|
*/
|
|
19
|
-
static validateProgress(progress: number
|
|
22
|
+
static validateProgress(progress: number, round?: boolean): number;
|
|
23
|
+
static validateProgress(progress: number | null | undefined, round?: boolean): number | null;
|
|
20
24
|
/**
|
|
21
|
-
* Convert decimal ratio to percentage
|
|
25
|
+
* Convert decimal ratio to percentage.
|
|
26
|
+
*
|
|
27
|
+
* Returns `null` for a missing/unparsable input — see {@link validateProgress}.
|
|
22
28
|
* @param ratio - Decimal ratio (0-1)
|
|
23
29
|
* @param round - Whether to round to integer
|
|
24
30
|
*/
|
|
25
|
-
static ratioToPercent(ratio: number | null | undefined, round?: boolean): number;
|
|
31
|
+
static ratioToPercent(ratio: number | null | undefined, round?: boolean): number | null;
|
|
26
32
|
/**
|
|
27
33
|
* Get progress label text
|
|
28
34
|
* @param progress - Progress value (0-100)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display string for an absent value (`null`/`undefined`/unparsable), shared by every
|
|
3
|
+
* formatting helper in this package so "no value" renders identically everywhere and
|
|
4
|
+
* never collides with a real `0`.
|
|
5
|
+
*/
|
|
6
|
+
export declare const EMPTY_VALUE_DISPLAY = "\u2014";
|
package/dist/helpers/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -117,6 +117,14 @@ messages.register("ko", {
|
|
|
117
117
|
daysRemaining: "{days}일"
|
|
118
118
|
});
|
|
119
119
|
//#endregion
|
|
120
|
+
//#region src/helpers/constants.ts
|
|
121
|
+
/**
|
|
122
|
+
* Display string for an absent value (`null`/`undefined`/unparsable), shared by every
|
|
123
|
+
* formatting helper in this package so "no value" renders identically everywhere and
|
|
124
|
+
* never collides with a real `0`.
|
|
125
|
+
*/
|
|
126
|
+
var EMPTY_VALUE_DISPLAY = "—";
|
|
127
|
+
//#endregion
|
|
120
128
|
//#region src/helpers/CurrencyHelper.ts
|
|
121
129
|
/**
|
|
122
130
|
* Currency formatting utility class.
|
|
@@ -135,7 +143,7 @@ var CurrencyHelper = class {
|
|
|
135
143
|
* @param locale - Locale for formatting (default: 'ko-KR')
|
|
136
144
|
*/
|
|
137
145
|
static formatCurrency(amount, currency = "KRW", locale = "ko-KR") {
|
|
138
|
-
if (amount === null || amount === void 0) return "
|
|
146
|
+
if (amount === null || amount === void 0) return "—";
|
|
139
147
|
return formatCurrency(amount, currency, {
|
|
140
148
|
minimumFractionDigits: 0,
|
|
141
149
|
maximumFractionDigits: currency === "KRW" ? 0 : 2
|
|
@@ -182,9 +190,9 @@ var DateHelper = class {
|
|
|
182
190
|
* @param date - Date to format (Date object or ISO string)
|
|
183
191
|
*/
|
|
184
192
|
static formatDate(date) {
|
|
185
|
-
if (!date) return "
|
|
193
|
+
if (!date) return "—";
|
|
186
194
|
const d = typeof date === "string" ? new Date(date) : date;
|
|
187
|
-
if (isNaN(d.getTime())) return "
|
|
195
|
+
if (isNaN(d.getTime())) return "—";
|
|
188
196
|
return d.toISOString().slice(0, 10);
|
|
189
197
|
}
|
|
190
198
|
/**
|
|
@@ -194,9 +202,9 @@ var DateHelper = class {
|
|
|
194
202
|
* @param options - Intl.DateTimeFormat options
|
|
195
203
|
*/
|
|
196
204
|
static formatLocalDate(date, locale = "ko-KR", options) {
|
|
197
|
-
if (!date) return "
|
|
205
|
+
if (!date) return "—";
|
|
198
206
|
const d = typeof date === "string" ? new Date(date) : date;
|
|
199
|
-
if (isNaN(d.getTime())) return "
|
|
207
|
+
if (isNaN(d.getTime())) return "—";
|
|
200
208
|
return d.toLocaleDateString(locale, options || {
|
|
201
209
|
year: "numeric",
|
|
202
210
|
month: "2-digit",
|
|
@@ -208,9 +216,9 @@ var DateHelper = class {
|
|
|
208
216
|
* @param date - Date to format
|
|
209
217
|
*/
|
|
210
218
|
static formatDateTime(date) {
|
|
211
|
-
if (!date) return "
|
|
219
|
+
if (!date) return "—";
|
|
212
220
|
const d = typeof date === "string" ? new Date(date) : date;
|
|
213
|
-
if (isNaN(d.getTime())) return "
|
|
221
|
+
if (isNaN(d.getTime())) return "—";
|
|
214
222
|
return d.toISOString().slice(0, 16).replace("T", " ");
|
|
215
223
|
}
|
|
216
224
|
/**
|
|
@@ -299,25 +307,22 @@ var ProgressHelper = class {
|
|
|
299
307
|
if (progress >= medium) return "#FF9800";
|
|
300
308
|
return "#F44336";
|
|
301
309
|
}
|
|
302
|
-
/**
|
|
303
|
-
* Validate and clamp progress to 0-100 range
|
|
304
|
-
* @param progress - Raw progress value
|
|
305
|
-
* @param round - Whether to round to integer (default: true)
|
|
306
|
-
*/
|
|
307
310
|
static validateProgress(progress, round = true) {
|
|
308
|
-
if (progress === null || progress === void 0 || isNaN(progress)) return
|
|
311
|
+
if (progress === null || progress === void 0 || isNaN(progress)) return null;
|
|
309
312
|
let value = progress;
|
|
310
313
|
if (value < 0) value = 0;
|
|
311
314
|
if (value > 100) value = 100;
|
|
312
315
|
return round ? Math.round(value) : value;
|
|
313
316
|
}
|
|
314
317
|
/**
|
|
315
|
-
* Convert decimal ratio to percentage
|
|
318
|
+
* Convert decimal ratio to percentage.
|
|
319
|
+
*
|
|
320
|
+
* Returns `null` for a missing/unparsable input — see {@link validateProgress}.
|
|
316
321
|
* @param ratio - Decimal ratio (0-1)
|
|
317
322
|
* @param round - Whether to round to integer
|
|
318
323
|
*/
|
|
319
324
|
static ratioToPercent(ratio, round = true) {
|
|
320
|
-
if (ratio === null || ratio === void 0 || isNaN(ratio)) return
|
|
325
|
+
if (ratio === null || ratio === void 0 || isNaN(ratio)) return null;
|
|
321
326
|
const percent = ratio * 100;
|
|
322
327
|
return this.validateProgress(percent, round);
|
|
323
328
|
}
|
|
@@ -452,7 +457,7 @@ var UrgencyHelper = class {
|
|
|
452
457
|
* @param daysRemaining - Days until deadline
|
|
453
458
|
*/
|
|
454
459
|
static formatDaysRemaining(daysRemaining) {
|
|
455
|
-
if (daysRemaining === null || daysRemaining === void 0) return "
|
|
460
|
+
if (daysRemaining === null || daysRemaining === void 0) return "—";
|
|
456
461
|
if (daysRemaining < 0) return messages.text("daysOverdue", { days: Math.abs(daysRemaining) });
|
|
457
462
|
if (daysRemaining === 0) return messages.text("daysToday");
|
|
458
463
|
return messages.text("daysRemaining", { days: daysRemaining });
|
|
@@ -769,6 +774,11 @@ function createODataService(config) {
|
|
|
769
774
|
await throwIfError(res);
|
|
770
775
|
return parseJsonBody(res);
|
|
771
776
|
}
|
|
777
|
+
async function apiPut(path, body) {
|
|
778
|
+
const res = await client.put(apiUrl(path), body ?? {});
|
|
779
|
+
await throwIfError(res);
|
|
780
|
+
return parseJsonBody(res);
|
|
781
|
+
}
|
|
772
782
|
async function apiPatch(path, body) {
|
|
773
783
|
const res = await client.patch(apiUrl(path), body ?? {});
|
|
774
784
|
await throwIfError(res);
|
|
@@ -796,6 +806,7 @@ function createODataService(config) {
|
|
|
796
806
|
odataDelete,
|
|
797
807
|
apiGet,
|
|
798
808
|
apiPost,
|
|
809
|
+
apiPut,
|
|
799
810
|
apiPatch,
|
|
800
811
|
apiDelete,
|
|
801
812
|
fetchRaw,
|
|
@@ -977,4 +988,4 @@ function createAuthClient(config) {
|
|
|
977
988
|
};
|
|
978
989
|
}
|
|
979
990
|
//#endregion
|
|
980
|
-
export { ApiConfig, ApiError, CurrencyHelper, DateHelper, FormRow, FormSection, ProgressHelper, UrgencyHelper, clearPermissions, createAuthClient, createODataService, createPermissionStore, defaultPermissionStore, getPermissions, hasAllPermissions, hasAnyPermission, hasPermission, messages, setPermissions };
|
|
991
|
+
export { ApiConfig, ApiError, CurrencyHelper, DateHelper, EMPTY_VALUE_DISPLAY, FormRow, FormSection, ProgressHelper, UrgencyHelper, clearPermissions, createAuthClient, createODataService, createPermissionStore, defaultPermissionStore, getPermissions, hasAllPermissions, hasAnyPermission, hasPermission, messages, setPermissions };
|