@skalfa/skalfa-component 1.0.6 → 1.0.8

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 (59) hide show
  1. package/dist/index.js +10 -10
  2. package/package.json +2 -2
  3. package/src/accordion/Accordion.component.tsx +87 -0
  4. package/src/breadcrumb/Breadcrumb.component.tsx +79 -0
  5. package/src/button/Button.component.tsx +89 -0
  6. package/src/card/AlertCard.component.tsx +69 -0
  7. package/src/card/Card.component.tsx +25 -0
  8. package/src/card/DashboardCard.component.tsx +44 -0
  9. package/src/card/GalleryCard.component.tsx +50 -0
  10. package/src/card/ProductCard.component.tsx +65 -0
  11. package/src/card/ProfileCard.component.tsx +71 -0
  12. package/src/carousel/Carousel.component.tsx +111 -0
  13. package/src/chip/Chip.component.tsx +39 -0
  14. package/src/index.ts +70 -0
  15. package/src/input/Checkbox.component.tsx +102 -0
  16. package/src/input/Input.component.tsx +334 -0
  17. package/src/input/InputCheckbox.component.tsx +174 -0
  18. package/src/input/InputCurrency.component.tsx +165 -0
  19. package/src/input/InputDate.component.tsx +356 -0
  20. package/src/input/InputDatetime.component.tsx +267 -0
  21. package/src/input/InputDocument.component.tsx +360 -0
  22. package/src/input/InputImage.component.tsx +535 -0
  23. package/src/input/InputNumber.component.tsx +194 -0
  24. package/src/input/InputOtp.component.tsx +169 -0
  25. package/src/input/InputPassword.component.tsx +245 -0
  26. package/src/input/InputRadio.component.tsx +174 -0
  27. package/src/input/InputTime.component.tsx +280 -0
  28. package/src/input/InputValues.component.tsx +71 -0
  29. package/src/input/Radio.component.tsx +98 -0
  30. package/src/input/Select.component.tsx +557 -0
  31. package/src/modal/BottomSheet.component.tsx +246 -0
  32. package/src/modal/FloatingPage.component.tsx +103 -0
  33. package/src/modal/Modal.component.tsx +95 -0
  34. package/src/modal/ModalConfirm.component.tsx +219 -0
  35. package/src/modal/Toast.component.tsx +125 -0
  36. package/src/nav/Bottombar.component.tsx +72 -0
  37. package/src/nav/Footer.component.tsx +177 -0
  38. package/src/nav/Headbar.component.tsx +33 -0
  39. package/src/nav/Navbar.component.tsx +138 -0
  40. package/src/nav/Sidebar.component.tsx +298 -0
  41. package/src/nav/Tabbar.component.tsx +61 -0
  42. package/src/nav/Wizard.component.tsx +80 -0
  43. package/src/supervision/FormSupervision.component.tsx +425 -0
  44. package/src/supervision/TableSupervision.component.tsx +688 -0
  45. package/src/table/ControlBar.component.tsx +501 -0
  46. package/src/table/FilterComponent.tsx +519 -0
  47. package/src/table/Pagination.component.tsx +152 -0
  48. package/src/table/Table.component.tsx +436 -0
  49. package/src/types.d.ts +7 -0
  50. package/src/typography/TypographyArticle.component.tsx +26 -0
  51. package/src/typography/TypographyColumn.component.tsx +20 -0
  52. package/src/typography/TypographyContent.component.tsx +20 -0
  53. package/src/typography/TypographyTips.component.tsx +20 -0
  54. package/src/wrap/Draggable.component.tsx +303 -0
  55. package/src/wrap/Image.component.tsx +10 -0
  56. package/src/wrap/OutsideClick.component.tsx +48 -0
  57. package/src/wrap/ScrollContainer.component.tsx +107 -0
  58. package/src/wrap/ShortcutProvider.tsx +57 -0
  59. package/src/wrap/Swipe.component.tsx +121 -0
@@ -0,0 +1,688 @@
1
+ "use client"
2
+
3
+ import { ReactNode, Suspense, useEffect, useMemo } from "react";
4
+ import { faQuestionCircle } from "@fortawesome/free-regular-svg-icons";
5
+ import { faEdit, faFileExcel, faFilePdf, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
6
+ import { ApiType, cn, conversion, FetchControlType, registry, shortcut, ShortcutHandler, UseResourceIdb, UseResourceProps, useResponsive, useTable } from "@utils";
7
+ import { useToggleContext } from "@contexts";
8
+ import { FloatingPageComponent, FloatingPageProps } from "../modal/FloatingPage.component";
9
+ import { ButtonComponent, ButtonProps } from "../button/Button.component";
10
+ import { TableColumnType, TableComponent, TableProps } from "../table/Table.component";
11
+ import { FormSupervisionComponent, FormType } from "./FormSupervision.component";
12
+ import { ModalConfirmComponent, ModalConfirmProps } from "../modal/ModalConfirm.component";
13
+ import { TypographyColumnComponent } from "../typography/TypographyColumn.component";
14
+ import { ControlBarOptionType } from "../table/ControlBar.component";
15
+ import { BottomSheetComponent } from "../modal/BottomSheet.component";
16
+
17
+ const ExportExcel = registry.get("ExportExcel");
18
+ const ImportExcel = registry.get("ImportExcel");
19
+
20
+
21
+
22
+ export interface TableSupervisionColumnProps {
23
+ selector : string;
24
+ label ?: string;
25
+ width ?: string;
26
+ sortable ?: boolean;
27
+ searchable ?: boolean;
28
+ filterable ?: boolean | {
29
+ type : "text" | "number" | "currency" | "date";
30
+ } | {
31
+ type : "select";
32
+ options : { label: string; value: any }[];
33
+ };
34
+ accessCode ?: string;
35
+ item ?: (data: any) => string | ReactNode;
36
+ tip ?: string | ((data: any) => string);
37
+ exportable ?: boolean | "default" | "optional" | "hidden";
38
+ importable ?: boolean;
39
+ }
40
+
41
+ export interface TableSupervisionFormProps {
42
+ fields : string[] | (FormType & { visibility?: "*" | "create" | "update" })[];
43
+ defaultValue ?: (item: Record<string, any> | null) => Promise<Record<string, any>> | Record<string, any>;
44
+ payload ?: (values: any) => Promise<Record<string, any>> | object;
45
+ modalControl ?: Omit<FloatingPageProps, "show" | "onClose" | "children">;
46
+ contentType ?: "application/json" | "multipart/form-data";
47
+ }
48
+
49
+
50
+ export type TableSupervisionDetailProps = boolean
51
+ | (
52
+ | string
53
+ | { label: string, item: string | ((data: Record<string, any>) => ReactNode), conversion?: keyof typeof conversion }
54
+ | ((data: Record<string, any>) => ReactNode)
55
+ )[]
56
+ | ((data: Record<string, any>) => void)
57
+ | {
58
+ modal?: Omit<FloatingPageProps, "show" | "onClose" | "children">;
59
+ content?: boolean
60
+ | (
61
+ | string
62
+ | { label: string, item: string | ((data: Record<string, any>) => ReactNode), conversion?: keyof typeof conversion }
63
+ | ((data: Record<string, any>) => ReactNode)
64
+ )[]
65
+ | ((data: Record<string, any>) => ReactNode);
66
+ action?: (data: Record<string, any>) => void;
67
+ };
68
+
69
+ export type TableSupervisionProps = {
70
+ fetchControl : UseResourceProps;
71
+ title ?: string;
72
+ id ?: string;
73
+ accessCode ?: number;
74
+ urlParam ?: boolean | { compressed ?: boolean };
75
+ columnControl ?: string[] | TableSupervisionColumnProps[];
76
+ formControl ?: TableSupervisionFormProps;
77
+ detailControl ?: TableSupervisionDetailProps;
78
+ actionControl ?: boolean | (
79
+ | 'EDIT' | 'DELETE' | {
80
+ label : string,
81
+ modal ?: Omit<ModalConfirmProps, "show" | "onClose">,
82
+ button ?: ButtonProps,
83
+ shortcut ?: { key: string, description: string },
84
+ } | ((
85
+ row : Record<string, any>,
86
+ setModal : (type: "EDIT" | "DELETE") => void,
87
+ setDataSelected ?: () => void,
88
+ setShortcut ?: (key: string, handler: ShortcutHandler, description?: string) => void
89
+ ) => ReactNode)
90
+ )[];
91
+ block ?: boolean,
92
+ noIndex ?: boolean;
93
+ actionBulkingControl ?: TableProps["actionBulking"],
94
+ controlBar ?: (ControlBarOptionType | "CREATE" | "IMPORT" | "EXPORT" | "PRINT")[];
95
+ responsiveControl ?: {
96
+ mobile ?: TableProps["responsiveControl"] extends { mobile?: infer M } ? M | boolean : boolean;
97
+ };
98
+ importControl ?: FetchControlType;
99
+ };
100
+
101
+
102
+
103
+ export function TableSupervisionComponent({
104
+ title,
105
+ id,
106
+ fetchControl,
107
+ columnControl,
108
+ formControl,
109
+ detailControl,
110
+ actionControl,
111
+ actionBulkingControl,
112
+ block,
113
+ controlBar,
114
+ noIndex,
115
+ responsiveControl,
116
+ urlParam,
117
+ importControl,
118
+ }: TableSupervisionProps) {
119
+ const { tableKey, tableControl, data, selected, setSelected, checks, setChecks, reset, focus } = useTable(fetchControl, id, title, (urlParam || true))
120
+ const { setToggle, toggle } = useToggleContext()
121
+ const { isSm } = useResponsive();
122
+
123
+ const isDetailControlObject = detailControl && typeof detailControl === "object" && !Array.isArray(detailControl);
124
+ const detailAction = isDetailControlObject
125
+ ? (detailControl as any)?.action
126
+ : (typeof detailControl === "function" ? detailControl : undefined);
127
+ const detailContent = isDetailControlObject
128
+ ? (detailControl as any)?.content
129
+ : (typeof detailControl === "function" ? undefined : detailControl);
130
+ const detailModalProps = isDetailControlObject
131
+ ? (detailControl as any)?.modal
132
+ : undefined;
133
+
134
+ const toggleKey = useMemo(() => conversion.strSnake(tableKey).toUpperCase(), [tableKey])
135
+
136
+ // ============================
137
+ // ## Shortcut register
138
+ // ============================
139
+ useEffect(() => {
140
+ if(!isSm) {
141
+ if (controlBar == undefined || controlBar?.includes("CREATE")) {
142
+ shortcut.register("alt+n", () => {
143
+ setToggle(`MODAL_FORM_${toggleKey}`)
144
+ setSelected(null)
145
+ }, "Tambah Data Baru")
146
+ }
147
+
148
+ if (controlBar?.includes("IMPORT")) {
149
+ shortcut.register("alt+i", () => {
150
+ setToggle(`MODAL_IMPORT_${toggleKey}`)
151
+ }, "Import Data Dari Excel")
152
+ }
153
+
154
+ if (controlBar?.includes("EXPORT")) {
155
+ shortcut.register("alt+e", () => {
156
+ setToggle(`MODAL_EXPORT_${toggleKey}`)
157
+ }, "Export Data Ke Excel")
158
+ }
159
+ }
160
+
161
+ return () => {
162
+ if(!isSm) {
163
+ shortcut.unregister("alt+n")
164
+ shortcut.unregister("alt+i")
165
+ shortcut.unregister("alt+e")
166
+ }
167
+ }
168
+ }, [controlBar, isSm])
169
+
170
+
171
+ useEffect(() => {
172
+ if(actionControl && Array.isArray(actionControl) && selected) {
173
+ actionControl.filter((ac) => typeof ac == "object")?.forEach((ac) => {
174
+ if(typeof ac == "object" && ac?.shortcut) {
175
+ shortcut.register(ac.shortcut.key, () => {
176
+ setToggle(`MODAL_${conversion.strSnake(ac.label).toUpperCase()}_${toggleKey}`)
177
+ }, ac.shortcut.description)
178
+ }
179
+ })
180
+ }
181
+
182
+ return () => {
183
+ if(actionControl && Array.isArray(actionControl)) {
184
+ actionControl.filter((ac) => typeof ac == "object")?.forEach((ac) => {
185
+ if(typeof ac == "object" && ac?.shortcut) {
186
+ shortcut.unregister(ac.shortcut.key)
187
+ }
188
+ })
189
+ }
190
+ }
191
+ }, [actionControl, selected])
192
+
193
+
194
+
195
+ const columns: TableSupervisionColumnProps[] = useMemo(() => {
196
+ return columnControl ? columnControl.map((col) => {
197
+ if(typeof col == "string") {
198
+ return {
199
+ selector : col,
200
+ label : col,
201
+ };
202
+ } else {
203
+ return { ...col };
204
+ }
205
+ })
206
+ : data?.columns || data?.data?.at(0) ? Object.keys(data.data[0]).map((col) => {
207
+ return {
208
+ selector : col,
209
+ label : col,
210
+ };
211
+ })
212
+ : [];
213
+ }, [columnControl, data]);
214
+
215
+
216
+
217
+ const renderTableAction = (
218
+ actions : TableSupervisionProps["actionControl"],
219
+ item ?: Record<string, any>,
220
+ options ?: {size?: ButtonProps['size'], className?: string}
221
+ ) => {
222
+ return (
223
+ <>
224
+ <div className={cn("table-supervision-action-list", options?.className)}>
225
+ {(Array.isArray(actions) ? actions : (actions || actions == undefined) ? ['EDIT', "DELETE"] : [])?.map((action, key) => {
226
+ if(action == "EDIT") {
227
+ return (
228
+ <ButtonComponent
229
+ key={key}
230
+ icon={faEdit}
231
+ label={"Ubah"}
232
+ variant="outline"
233
+ paint="warning"
234
+ size={options?.size || "xs"}
235
+ rounded
236
+ onClick={() => {
237
+ setToggle(`MODAL_FORM_${toggleKey}`);
238
+ item && setSelected?.(item);
239
+ }}
240
+ />
241
+ )
242
+ }
243
+
244
+ if(action == "DELETE") {
245
+ return (
246
+ <ButtonComponent
247
+ key={key}
248
+ icon={faTrash}
249
+ label={"Hapus"}
250
+ variant="outline"
251
+ paint="danger"
252
+ size={options?.size || "xs"}
253
+ rounded
254
+ onClick={() => {
255
+ setToggle(`MODAL_DELETE_${toggleKey}`);
256
+ item && setSelected?.(item);
257
+ }}
258
+ />
259
+ )
260
+ }
261
+
262
+ if (typeof action == "object") {
263
+ return (
264
+ <ButtonComponent
265
+ key={key}
266
+ label={action.label}
267
+ variant="outline"
268
+ paint="primary"
269
+ size={options?.size || "xs"}
270
+ rounded
271
+ onClick={() => {
272
+ setToggle(`MODAL_${conversion.strSnake(action.label).toUpperCase()}_${toggleKey}`);
273
+ item && setSelected?.(item);
274
+ }}
275
+ {...action.button}
276
+ />
277
+ )
278
+ }
279
+
280
+ if (typeof action == "function") {
281
+ return (
282
+ <div key={key}>
283
+ {action(
284
+ item || {},
285
+ (type) => {
286
+ setToggle(`MODAL_${type}_${toggleKey}`)
287
+ item && setSelected?.(item)
288
+ },
289
+ () => item && setSelected?.(item)
290
+ )}
291
+ </div>
292
+ )
293
+ }
294
+ })}
295
+ </div>
296
+ </>
297
+ )
298
+ }
299
+
300
+
301
+
302
+
303
+ // ============================
304
+ // ## Render detail page
305
+ // ============================
306
+ const detailPage = useMemo(() => {
307
+ if (detailContent === false || detailContent === undefined) return null;
308
+ return (
309
+ <div className="table-supervision-detail-container">
310
+ <div className="table-supervision-detail-body">
311
+ {!!selected && (typeof detailContent === "object" && Array.isArray(detailContent) ? detailContent?.map((column, key) => {
312
+ if (typeof column === "string") {
313
+ return (<TypographyColumnComponent
314
+ key={key}
315
+ title={columns?.find((c) => c.selector == column)?.label}
316
+ content={selected[column]}
317
+ />)
318
+ } else if (typeof column === "object" && column !== null) {
319
+ const colObj = column as { label: string, item: string | ((data: Record<string, any>) => ReactNode), conversion?: keyof typeof conversion };
320
+ const rawContent = typeof colObj.item === "string" ? selected[colObj.item] : colObj.item(selected);
321
+ const conversionKey = colObj.conversion;
322
+ const content = conversionKey && conversion[conversionKey] ? (conversion[conversionKey] as any)(rawContent) : rawContent;
323
+ return (<TypographyColumnComponent
324
+ key={key}
325
+ title={colObj.label}
326
+ content={content}
327
+ />)
328
+ } else {
329
+ return column?.(selected)
330
+ }
331
+ }) : typeof detailContent === "function" ? (detailContent as any)(selected) : columns?.map((column, key) => (
332
+ <TypographyColumnComponent
333
+ key={key}
334
+ title={column.label}
335
+ content={selected[column.selector]}
336
+ />
337
+ )))}
338
+ </div>
339
+ </div>
340
+ )
341
+ }, [selected, detailContent, columns]);
342
+
343
+
344
+
345
+
346
+ // ============================
347
+ // ## Form preparation
348
+ // ============================
349
+ const fields = useMemo(() => {
350
+ return formControl?.fields?.length ? formControl?.fields.map((form) => {
351
+ return typeof form === "string" ? {
352
+ label : form,
353
+ name : conversion.strSnake(form),
354
+ type : "text",
355
+ visibility : "*"
356
+ } : {
357
+ ...form,
358
+ visibility : form.visibility || "*"
359
+ }
360
+ }) as FormType[] : []
361
+ }, [formControl]);
362
+
363
+
364
+
365
+ const formPage = useMemo(() => {
366
+ return (
367
+ <FormSupervisionComponent
368
+ fields={fields}
369
+ defaultValue={formControl?.defaultValue}
370
+ payload={formControl?.payload}
371
+ submitControl={fetchControl}
372
+ onSuccess={() => {
373
+ setToggle(`MODAL_FORM_${toggleKey}`, false)
374
+ reset()
375
+ }}
376
+ />
377
+ )
378
+ }, [selected, fields])
379
+
380
+
381
+ const dataTables = useMemo(() => {
382
+ return data?.data?.map((item: Record<string, any>) => {
383
+ return {
384
+ ...item,
385
+ action: renderTableAction(actionControl, item)
386
+ };
387
+ }) || [];
388
+ }, [actionControl, data]);
389
+
390
+
391
+ useEffect(() => {
392
+ if(toggle[`REFRESH_${toggleKey}`] != undefined) reset();
393
+ }, [toggle[`REFRESH_${toggleKey}`]]);
394
+
395
+
396
+ return (
397
+ <>
398
+ <Suspense fallback={<div>Loading...</div>}>
399
+ {title && <h1 className="table-supervision-title">{title}</h1>}
400
+
401
+
402
+ <TableComponent
403
+ id={tableKey}
404
+ controlBar={controlBar?.map((cb) => {
405
+ if (cb == "CREATE") {
406
+ if (isSm) return
407
+ return (
408
+ <div className="control-bar-create-wrapper" key="button-add">
409
+ <ButtonComponent
410
+ icon={faPlus}
411
+ label="Tambah Data"
412
+ size="sm"
413
+ onClick={() => {
414
+ setToggle(`MODAL_FORM_${toggleKey}`)
415
+ setSelected(null)
416
+ }}
417
+ />
418
+ </div>
419
+ )
420
+ }
421
+
422
+ if (cb == "IMPORT") {
423
+ return (
424
+ <div className="control-bar-button-wrapper" key={"import"}>
425
+ <ButtonComponent
426
+ icon={faFileExcel}
427
+ label="Import"
428
+ variant="outline"
429
+ className="table-supervision-control-button"
430
+ onClick={() => setToggle(`MODAL_IMPORT_${toggleKey}`)}
431
+ size="sm"
432
+ />
433
+ </div>
434
+ )
435
+ }
436
+
437
+ if (cb == "EXPORT") {
438
+ return (
439
+ <div className="control-bar-button-wrapper" key={"export-excel"}>
440
+ <ButtonComponent
441
+ icon={faFileExcel}
442
+ label="Export"
443
+ variant="outline"
444
+ className="table-supervision-control-button"
445
+ onClick={() => setToggle(`MODAL_EXPORT_${toggleKey}`)}
446
+ size="sm"
447
+ />
448
+ </div>
449
+ )
450
+ }
451
+
452
+ if (cb == "PRINT") {
453
+ return (
454
+ <div className="control-bar-button-wrapper" key={"export-pdf"}>
455
+ <ButtonComponent
456
+ icon={faFilePdf}
457
+ label="Cetak"
458
+ variant="outline"
459
+ className="table-supervision-control-button"
460
+ onClick={() => setToggle(`MODAL_PRINT_${toggleKey}`)}
461
+ size="sm"
462
+ />
463
+ </div>
464
+ )
465
+ }
466
+
467
+ return cb
468
+ }) || [
469
+ ...(!isSm ? [
470
+ <div className="control-bar-create-wrapper" key="button-add">
471
+ <ButtonComponent
472
+ icon={faPlus}
473
+ label="Tambah Data"
474
+ size="sm"
475
+ onClick={() => {
476
+ setToggle(`MODAL_FORM_${toggleKey}`)
477
+ setSelected(null)
478
+ }}
479
+ />
480
+ </div>
481
+ ] : []),
482
+ "SEARCH",
483
+ ...(columns?.filter((c) => !!(c as { filterable?: any }).filterable)?.length ? ["FILTER"] : []),
484
+ ...(columns?.filter((c) => !!(c as { sortable?: any }).sortable)?.length ? ["SORT"] : []),
485
+ "SELECTABLE", "REFRESH",
486
+ ]}
487
+ columns={columns as TableColumnType[]}
488
+ data={dataTables}
489
+ onRowClick={detailAction ? detailAction : (detailContent !== false && detailContent !== undefined) ? (e) => {
490
+ setToggle(`MODAL_SHOW_${toggleKey}`)
491
+ setSelected(e)
492
+ } : undefined}
493
+ actionBulking={actionBulkingControl}
494
+ checks={checks || []}
495
+ onChangeChecks={(e) => setChecks(e)}
496
+ block={block}
497
+ focus={focus}
498
+ noIndex={noIndex}
499
+ responsiveControl={responsiveControl ? {
500
+ mobile: responsiveControl?.mobile == true ? {
501
+ leftActionControl: (Array.isArray(actionControl) ? actionControl : (actionControl || actionControl == undefined) ? ['EDIT', "DELETE"] : []).includes('EDIT') ? {
502
+ icon: faEdit,
503
+ onAction: (item) => {
504
+ setToggle(`MODAL_FORM_${toggleKey}`);
505
+ item && setSelected?.(item);
506
+ }
507
+ } : undefined,
508
+ rightActionControl: (Array.isArray(actionControl) ? actionControl : (actionControl || actionControl == undefined) ? ['EDIT', "DELETE"] : []).includes('DELETE') ? {
509
+ icon: faTrash,
510
+ onAction: (item) => {
511
+ setToggle(`MODAL_DELETE_${toggleKey}`);
512
+ item && setSelected?.(item);
513
+ }
514
+ } : undefined
515
+ } : responsiveControl?.mobile || undefined,
516
+ } : undefined}
517
+ {...tableControl}
518
+ />
519
+
520
+ <ButtonComponent
521
+ icon={faPlus}
522
+ className="table-supervision-mobile-add-btn"
523
+ size="lg"
524
+ rounded
525
+ onClick={() => {
526
+ setToggle(`MODAL_FORM_${toggleKey}`)
527
+ setSelected(null)
528
+ }}
529
+ />
530
+
531
+
532
+ {(detailContent !== false && detailContent !== undefined) && (isSm ? (
533
+ <BottomSheetComponent
534
+ show={!!toggle[`MODAL_SHOW_${toggleKey}`]}
535
+ onClose={() => setToggle(`MODAL_SHOW_${toggleKey}`, false)}
536
+ className="table-supervision-modal"
537
+ footer={renderTableAction(actionControl, undefined, {className: isSm ? "justify-end p-2 bg-background" : "justify-end", size: isSm ? "sm" : "md"})}
538
+ size="98vh"
539
+ {...(detailModalProps as any)}
540
+ >
541
+ {detailPage}
542
+ </BottomSheetComponent>
543
+ ) : (
544
+ <FloatingPageComponent
545
+ show={!!toggle[`MODAL_SHOW_${toggleKey}`]}
546
+ onClose={() => setToggle(`MODAL_SHOW_${toggleKey}`, false)}
547
+ title="Detail"
548
+ className="table-supervision-modal"
549
+ footer={renderTableAction(actionControl, undefined, {className: isSm ? "justify-end p-2 bg-background" : "justify-end", size: isSm ? "sm" : "md"})}
550
+ {...(detailModalProps as any)}
551
+ >
552
+ {detailPage}
553
+ </FloatingPageComponent>
554
+ ))}
555
+
556
+
557
+ {isSm ? (
558
+ <BottomSheetComponent
559
+ show={!!toggle[`MODAL_FORM_${toggleKey}`]}
560
+ onClose={() => setToggle(`MODAL_FORM_${toggleKey}`, false)}
561
+ className={cn("table-supervision-modal", formControl?.modalControl?.className)}
562
+ size="98vh"
563
+ >
564
+ <div className="table-supervision-form-mobile-container">
565
+ {formPage}
566
+ </div>
567
+ </BottomSheetComponent>
568
+ ) : (
569
+ <FloatingPageComponent
570
+ show={!!toggle[`MODAL_FORM_${toggleKey}`]}
571
+ onClose={() => setToggle(`MODAL_FORM_${toggleKey}`, false)}
572
+ title={!!selected ? "Ubah Data" : "Tambah Data"}
573
+ className={cn("table-supervision-modal", formControl?.modalControl?.className)}
574
+ >
575
+ <div className="table-supervision-form-container">
576
+ {formPage}
577
+ </div>
578
+ </FloatingPageComponent>
579
+ )}
580
+
581
+
582
+ {ExportExcel && (
583
+ <FloatingPageComponent
584
+ show={!!toggle[`MODAL_EXPORT_${toggleKey}`]}
585
+ onClose={() => setToggle(`MODAL_EXPORT_${toggleKey}`, false)}
586
+ title="Export Ke Excel"
587
+ className="table-supervision-large-modal"
588
+ >
589
+ <ExportExcel
590
+ fetchControl={fetchControl as FetchControlType}
591
+ filename={"Export - " + title}
592
+ columnControl={columns?.map((cc: TableSupervisionColumnProps) => ({
593
+ label: cc.label || "",
594
+ selector: cc.selector || "",
595
+ status: cc.exportable === false ? "hidden" : cc.exportable === true ? "default" : typeof cc.exportable === "string" ? cc.exportable : undefined,
596
+ }))}
597
+ />
598
+ </FloatingPageComponent>
599
+ )}
600
+
601
+
602
+ {ImportExcel && (
603
+ <FloatingPageComponent
604
+ show={!!toggle[`MODAL_IMPORT_${toggleKey}`]}
605
+ onClose={() => setToggle(`MODAL_IMPORT_${toggleKey}`, false)}
606
+ title="Import Dari Excel"
607
+ className="table-supervision-large-modal"
608
+ >
609
+ <ImportExcel
610
+ submitControl={importControl}
611
+ fetchControl={
612
+ (fetchControl as ApiType).path ? {
613
+ path: (fetchControl as ApiType).path,
614
+ } : undefined
615
+ }
616
+ columnControl={columns?.filter((cc: TableSupervisionColumnProps) => cc.importable !== false).map((cc: TableSupervisionColumnProps) => ({
617
+ label: cc.label || "",
618
+ selector: cc.selector || "",
619
+ }))}
620
+ />
621
+ </FloatingPageComponent>
622
+ )}
623
+
624
+
625
+ <ModalConfirmComponent
626
+ show={!!toggle[`MODAL_DELETE_${toggleKey}`]}
627
+ onClose={() => setToggle(`MODAL_DELETE_${toggleKey}`, false)}
628
+ icon={faQuestionCircle}
629
+ title={`Menghapus Data?`}
630
+ submitControl={{
631
+ onSubmit: {
632
+ ...((fetchControl as ApiType).path
633
+ ? {path: `${(fetchControl as ApiType).path}/${(selected as { id: number })?.id || ""}`}
634
+ : (fetchControl as ApiType).url ? {url: `${(fetchControl as ApiType).url}/${(selected as { id: number })?.id || ""}`}
635
+ : { idb: { ...(fetchControl as ({ idb: UseResourceIdb }))?.idb, id: (selected as { id: number })?.id || "" }}
636
+ ),
637
+ method: "DELETE",
638
+ },
639
+ onSuccess: () => {
640
+ reset();
641
+ setToggle(`MODAL_DELETE_${toggleKey}`, false);
642
+ },
643
+ }}
644
+ >
645
+ {columns?.at(0)?.selector && selected ? (
646
+ <p className="table-supervision-confirm-text">Yakin menghapus <span className="table-supervision-confirm-item-name">&quot;{selected[columns?.at(0)?.selector || ""]}&quot;</span>?</p>
647
+ ) : (
648
+ <p className="table-supervision-confirm-text">Yakin yang dihapus sudah benar?</p>
649
+ )}
650
+ </ModalConfirmComponent>
651
+
652
+ {actionControl && Array.isArray(actionControl) && actionControl.filter((ac) => typeof ac == "object")?.map((ac, acKey) => {
653
+ const submitControl = ac.modal?.submitControl?.onSubmit as ApiType;
654
+ return (
655
+ <ModalConfirmComponent
656
+ key={acKey}
657
+ show={!!toggle[`MODAL_${conversion.strSnake(ac.label).toUpperCase()}_${toggleKey}`]}
658
+ onClose={() => setToggle(`MODAL_${conversion.strSnake(ac.label).toUpperCase()}_${toggleKey}`, false)}
659
+ icon={faQuestionCircle}
660
+ title={`${ac.label} Data?`}
661
+ submitControl={{
662
+ onSubmit: {
663
+ ...((submitControl as ApiType)?.path
664
+ ? {path: `${(submitControl as ApiType).path}/${(selected as { id: number })?.id || ""}`}
665
+ : (submitControl as ApiType)?.url ? {url: `${(submitControl as ApiType).url}/${(selected as { id: number })?.id || ""}`}
666
+ : { idb: { ...(submitControl as ({ idb: UseResourceIdb }))?.idb, id: (selected as { id: number })?.id || "" }}
667
+ ),
668
+ ...ac.modal?.submitControl?.onSubmit,
669
+ },
670
+ onSuccess: () => {
671
+ reset();
672
+ setToggle(`MODAL_${conversion.strSnake(ac.label).toUpperCase()}_${toggleKey}`, false);
673
+ },
674
+ }}
675
+ {...ac.modal}
676
+ >
677
+ {columns?.at(0)?.selector && selected ? (
678
+ <p className="table-supervision-confirm-text">Yakin melakukan aksi untuk data <span className="table-supervision-confirm-item-name">&quot;{selected[columns?.at(0)?.selector || ""]}&quot;</span>?</p>
679
+ ) : (
680
+ <p className="table-supervision-confirm-text">Yakin aksi yang dilakukan sudah benar?</p>
681
+ )}
682
+ </ModalConfirmComponent>
683
+ )
684
+ })}
685
+ </Suspense>
686
+ </>
687
+ );
688
+ }