@skalfa/skalfa-app 1.0.3 → 1.0.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 (90) hide show
  1. package/.env.example +8 -16
  2. package/app/auth/edit/page.tsx +1 -1
  3. package/app/auth/login/page.tsx +1 -1
  4. package/app/auth/me/page.tsx +1 -1
  5. package/app/auth/register/page.tsx +1 -1
  6. package/app/auth/verify/page.tsx +1 -1
  7. package/app/dashboard/layout.tsx +2 -2
  8. package/app/dashboard/page.tsx +1 -1
  9. package/app/index.ts +1 -0
  10. package/app/layout.tsx +2 -4
  11. package/app/page.tsx +2 -2
  12. package/bun.lock +7 -2
  13. package/components/index.ts +1 -3
  14. package/package.json +10 -7
  15. package/styles/components.css +1392 -0
  16. package/styles/globals.css +40 -175
  17. package/styles/utilities.css +37 -0
  18. package/tsconfig.json +4 -2
  19. package/utils/commands/skalfa.ts +1 -1
  20. package/components/base.components/accordion/Accordion.component.tsx +0 -82
  21. package/components/base.components/breadcrumb/Breadcrumb.component.tsx +0 -80
  22. package/components/base.components/button/Button.component.tsx +0 -91
  23. package/components/base.components/button/IconButton.component.tsx +0 -88
  24. package/components/base.components/button/button.decorate.ts +0 -82
  25. package/components/base.components/card/AlertCard.component.tsx +0 -69
  26. package/components/base.components/card/Card.component.tsx +0 -25
  27. package/components/base.components/card/DashboardCard.component.tsx +0 -44
  28. package/components/base.components/card/GalleryCard.component.tsx +0 -50
  29. package/components/base.components/card/ProductCard.component.tsx +0 -65
  30. package/components/base.components/card/ProfileCard.component.tsx +0 -71
  31. package/components/base.components/carousel/Carousel.component.tsx +0 -113
  32. package/components/base.components/chip/Chip.component.tsx +0 -39
  33. package/components/base.components/document/DocumentViewer.component.tsx +0 -164
  34. package/components/base.components/document/ExportExcel.component.tsx +0 -340
  35. package/components/base.components/document/ImportExcel.component.tsx +0 -315
  36. package/components/base.components/document/PrintTable.component.tsx +0 -204
  37. package/components/base.components/document/RenderPDF.component.tsx +0 -416
  38. package/components/base.components/index.ts +0 -85
  39. package/components/base.components/input/Checkbox.component.tsx +0 -109
  40. package/components/base.components/input/Input.component.tsx +0 -332
  41. package/components/base.components/input/InputCheckbox.component.tsx +0 -174
  42. package/components/base.components/input/InputCurrency.component.tsx +0 -163
  43. package/components/base.components/input/InputDate.component.tsx +0 -352
  44. package/components/base.components/input/InputDatetime.component.tsx +0 -260
  45. package/components/base.components/input/InputDocument.component.tsx +0 -352
  46. package/components/base.components/input/InputImage.component.tsx +0 -533
  47. package/components/base.components/input/InputMap.component.tsx +0 -318
  48. package/components/base.components/input/InputNumber.component.tsx +0 -192
  49. package/components/base.components/input/InputOtp.component.tsx +0 -169
  50. package/components/base.components/input/InputPassword.component.tsx +0 -236
  51. package/components/base.components/input/InputRadio.component.tsx +0 -175
  52. package/components/base.components/input/InputTime.component.tsx +0 -276
  53. package/components/base.components/input/InputValues.component.tsx +0 -68
  54. package/components/base.components/input/Radio.component.tsx +0 -102
  55. package/components/base.components/input/Select.component.tsx +0 -541
  56. package/components/base.components/modal/BottomSheet.component.tsx +0 -246
  57. package/components/base.components/modal/FloatingPage.component.tsx +0 -104
  58. package/components/base.components/modal/Modal.component.tsx +0 -96
  59. package/components/base.components/modal/ModalConfirm.component.tsx +0 -218
  60. package/components/base.components/modal/Toast.component.tsx +0 -126
  61. package/components/base.components/nav/Bottombar.component.tsx +0 -116
  62. package/components/base.components/nav/Footer.component.tsx +0 -144
  63. package/components/base.components/nav/Headbar.component.tsx +0 -104
  64. package/components/base.components/nav/Navbar.component.tsx +0 -100
  65. package/components/base.components/nav/Sidebar.component.tsx +0 -301
  66. package/components/base.components/nav/Tabbar.component.tsx +0 -60
  67. package/components/base.components/nav/Wizard.component.tsx +0 -73
  68. package/components/base.components/supervision/FormSupervision.component.tsx +0 -434
  69. package/components/base.components/supervision/TableSupervision.component.tsx +0 -697
  70. package/components/base.components/table/ControlBar.component.tsx +0 -497
  71. package/components/base.components/table/FilterComponent.tsx +0 -518
  72. package/components/base.components/table/Pagination.component.tsx +0 -159
  73. package/components/base.components/table/Table.component.tsx +0 -469
  74. package/components/base.components/typography/TypographyArticle.component.tsx +0 -26
  75. package/components/base.components/typography/TypographyColumn.component.tsx +0 -20
  76. package/components/base.components/typography/TypographyContent.component.tsx +0 -20
  77. package/components/base.components/typography/TypographyTips.component.tsx +0 -20
  78. package/components/base.components/wrap/Draggable.component.tsx +0 -303
  79. package/components/base.components/wrap/IDBProvider.tsx +0 -12
  80. package/components/base.components/wrap/Image.component.tsx +0 -10
  81. package/components/base.components/wrap/OutsideClick.component.tsx +0 -48
  82. package/components/base.components/wrap/ScrollContainer.component.tsx +0 -104
  83. package/components/base.components/wrap/ShortcutProvider.tsx +0 -57
  84. package/components/base.components/wrap/Swipe.component.tsx +0 -93
  85. package/components/construct.components/example.tsx +0 -1
  86. package/components/construct.components/index.ts +0 -5
  87. package/components/structure.components/example.tsx +0 -1
  88. package/components/structure.components/index.ts +0 -5
  89. package/schema/idb/app.schema.ts +0 -9
  90. package/schema/index.ts +0 -5
@@ -1,697 +0,0 @@
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, ButtonComponent, IconButtonComponent, TableColumnType, TableComponent, FormSupervisionComponent, FormType, ModalConfirmComponent, TypographyColumnComponent, ButtonProps, ModalConfirmProps, TableProps, ControlBarOptionType, BottomSheetComponent, SwipeActionType } from "@components";
9
-
10
- const ExportExcel = registry.get("ExportExcel");
11
- const ImportExcel = registry.get("ImportExcel");
12
-
13
-
14
-
15
- export interface TableSupervisionColumnProps {
16
- selector : string;
17
- label ?: string;
18
- width ?: string;
19
- sortable ?: boolean;
20
- searchable ?: boolean;
21
- filterable ?: boolean | {
22
- type : "text" | "number" | "currency" | "date";
23
- } | {
24
- type : "select";
25
- options : { label: string; value: any }[];
26
- };
27
- accessCode ?: string;
28
- item ?: (data: any) => string | ReactNode;
29
- tip ?: string | ((data: any) => string);
30
- exportable ?: boolean | "default" | "optional" | "hidden";
31
- importable ?: boolean;
32
- };
33
-
34
- export interface TableSupervisionFormProps {
35
- fields : string[] | (FormType & { visibility?: "*" | "create" | "update" })[];
36
- defaultValue ?: (item: Record<string, any> | null) => Promise<Record<string, any>> | Record<string, any>;
37
- payload ?: (values: any) => Promise<Record<string, any>> | object;
38
- modalControl ?: Omit<FloatingPageProps, "show" | "onClose" | "children">;
39
- contentType ?: "application/json" | "multipart/form-data";
40
- };
41
-
42
-
43
- export type TableSupervisionProps = {
44
- fetchControl : UseResourceProps;
45
- title ?: string;
46
- id ?: string;
47
- accessCode ?: number;
48
- urlParam ?: boolean | { compressed ?: boolean }
49
- onRowClick ?: (data: Record<string, any>) => void;
50
- columnControl ?: string[] | TableSupervisionColumnProps[];
51
- formControl ?: TableSupervisionFormProps;
52
- detailControl ?: boolean
53
- | (
54
- | string
55
- | { label: string, item: string | ((data: Record<string, any>) => ReactNode) }
56
- | ((data: Record<string, any>) => ReactNode)
57
- )[]
58
- | ((data: Record<string, any>) => ReactNode);
59
- actionControl ?: boolean | (
60
- | 'EDIT' | 'DELETE' | {
61
- label : string,
62
- modal ?: Omit<ModalConfirmProps, "show" | "onClose">,
63
- button ?: ButtonProps,
64
- shortcut ?: { key: string, description: string },
65
- } | ((
66
- row : Record<string, any>,
67
- setModal : (type: "EDIT" | "DELETE") => void,
68
- setDataSelected ?: () => void,
69
- setShortcut ?: (key: string, handler: ShortcutHandler, description?: string) => void
70
- ) => ReactNode)
71
- )[];
72
- block ?: boolean,
73
- noIndex ?: boolean;
74
- actionBulkingControl ?: TableProps["actionBulking"],
75
- controlBar ?: (ControlBarOptionType | "CREATE" | "IMPORT" | "EXPORT" | "PRINT")[];
76
- responsiveControl ?: {
77
- mobile ?: boolean | {
78
- item ?: (item: Record<string, any>, key: number) => ReactNode,
79
- leftActionControl ?: Omit<SwipeActionType, "onAction"> & { onAction?: (item: Record<string, any>, key?: number) => void },
80
- rightActionControl ?: Omit<SwipeActionType, "onAction"> & { onAction?: (item: Record<string, any>, key?: number) => void },
81
- }
82
- };
83
- importControl ?: FetchControlType;
84
- };
85
-
86
-
87
-
88
- export function TableSupervisionComponent({
89
- title,
90
- id,
91
- fetchControl,
92
- columnControl,
93
- formControl,
94
- onRowClick,
95
- detailControl,
96
- actionControl,
97
- actionBulkingControl,
98
- block,
99
- controlBar,
100
- noIndex,
101
- responsiveControl,
102
- urlParam,
103
- importControl,
104
- }: TableSupervisionProps) {
105
- const { tableKey, tableControl, data, selected, setSelected, checks, setChecks, reset, focus, setFocus } = useTable(fetchControl, id, title, (urlParam || true))
106
- const { setToggle, toggle } = useToggleContext()
107
- const { isSm } = useResponsive();
108
-
109
- const toggleKey = useMemo(() => conversion.strSnake(tableKey).toUpperCase(), [tableKey])
110
-
111
-
112
- useEffect(() => {
113
- if(data?.data?.length && !toggle[`MODAL_DELETE_${toggleKey}`] && !toggle[`MODAL_DELETE_${toggleKey}`] && !toggle[`MODAL_SHOW_${toggleKey}`]) {
114
- shortcut.register("arrowdown", () => {
115
- const max = data?.data?.length - 1;
116
- setFocus(focus == null ? 0 : focus >= max ? max : (focus + 1))
117
- }, "Pilih data kebawah")
118
-
119
- shortcut.register("arrowup", () => {
120
- setFocus(focus == null ? 0 : focus <= 0 ? 0 : (focus - 1))
121
- }, "Pilih data keatas")
122
-
123
- if(focus != null) {
124
- shortcut.register("delete", () => {
125
- setSelected(data?.data?.at(focus))
126
- setToggle(`MODAL_DELETE_${toggleKey}`)
127
- }, "Delete data yang dipilih")
128
-
129
- shortcut.register(" ", () => {
130
- setSelected(data?.data?.at(focus))
131
- setToggle(`MODAL_FORM_${toggleKey}`)
132
- }, "Edit data yang dipilih")
133
-
134
- shortcut.register("enter", () => {
135
- setSelected(data?.data?.at(focus))
136
- setToggle(`MODAL_SHOW_${toggleKey}`)
137
- }, "Detail data yang dipilih")
138
-
139
- shortcut.register("escape", () => {
140
- setFocus(null)
141
- }, "Kembali")
142
- }
143
- }
144
-
145
- return () => {
146
- shortcut.unregister("arrowdown")
147
- shortcut.unregister("arrowup")
148
- shortcut.unregister("delete")
149
- shortcut.unregister(" ")
150
- shortcut.unregister("enter")
151
- shortcut.unregister("escape")
152
- }
153
- }, [data?.data, actionControl, focus, toggle[`MODAL_DELETE_${toggleKey}`], toggle[`MODAL_DELETE_${toggleKey}`], toggle[`MODAL_SHOW_${toggleKey}`]])
154
-
155
-
156
- // ============================
157
- // ## Column preparation
158
- // ============================
159
- const columns = useMemo(() => {
160
- return columnControl?.length ? columnControl.map((col) => {
161
- if (typeof col === "string") {
162
- return {
163
- selector : col,
164
- label : col,
165
- };
166
- } else {
167
- return { ...col };
168
- }
169
- })
170
- : data?.columns || data?.data?.at(0) ? Object.keys(data.data[0]).map((col) => {
171
- return {
172
- selector : col,
173
- label : col,
174
- };
175
- })
176
- : [];
177
- }, [columnControl, data]);
178
-
179
-
180
-
181
- const renderTableAction = (
182
- actions : TableSupervisionProps["actionControl"],
183
- item ?: Record<string, any>,
184
- options ?: {size?: ButtonProps['size'], className?: string}
185
- ) => {
186
- return (
187
- <>
188
- <div className={cn("flex items-center gap-2", options?.className)}>
189
- {(Array.isArray(actions) ? actions : (actions || actions == undefined) ? ['EDIT', "DELETE"] : [])?.map((action, key) => {
190
- if(action == "EDIT") {
191
- return (
192
- <ButtonComponent
193
- key={key}
194
- icon={faEdit}
195
- label={"Ubah"}
196
- variant="outline"
197
- paint="warning"
198
- size={options?.size || "xs"}
199
- rounded
200
- onClick={() => {
201
- setToggle(`MODAL_FORM_${toggleKey}`);
202
- item && setSelected?.(item);
203
- }}
204
- />
205
- )
206
- }
207
-
208
- if(action == "DELETE") {
209
- return (
210
- <ButtonComponent
211
- key={key}
212
- icon={faTrash}
213
- label={"Hapus"}
214
- variant="outline"
215
- paint="danger"
216
- size={options?.size || "xs"}
217
- rounded
218
- onClick={() => {
219
- setToggle(`MODAL_DELETE_${toggleKey}`);
220
- item && setSelected?.(item);
221
- }}
222
- />
223
- )
224
- }
225
-
226
- if(typeof action == "object") {
227
- <ButtonComponent
228
- key={`action-object-${key}`}
229
- label={action?.button?.label || action?.label}
230
- variant={action?.button?.variant || "outline"}
231
- paint={action?.button?.paint || "primary"}
232
- size={action?.button?.size || options?.size || "xs"}
233
- rounded={action?.button?.rounded || true}
234
- onClick={() => {
235
- if (action?.button?.onClick) {
236
- action?.button?.onClick(item)
237
- } else {
238
- setToggle(`MODAL_${conversion.strSnake(action?.label).toUpperCase()}_${toggleKey}`);
239
- item && setSelected?.(item);
240
- }
241
- }}
242
- {...action.button}
243
- />
244
- }
245
-
246
- if(typeof action == "function") {
247
- return (
248
- <span key={`action-fn-${key}`}>
249
- {action(item || {}, (type: "EDIT" | "DELETE") => {
250
- if(type == "EDIT") {
251
- setToggle(`MODAL_FORM_${toggleKey}`);
252
- item && setSelected?.(item);
253
- }
254
-
255
- if (type == "DELETE") {
256
- setToggle(`MODAL_DELETE_${toggleKey}`);
257
- item && setSelected?.(item);
258
- }
259
- })}
260
- </span>
261
- )
262
- }
263
-
264
- return <span key={`action-default-${key}`}></span>;
265
- })}
266
- </div>
267
- </>
268
- )
269
- }
270
-
271
-
272
- // ============================
273
- // ## Data table preparation
274
- // ============================
275
- const dataTables = useMemo(() => {
276
- return data?.data?.map((row: object) => {
277
- return {
278
- ...row,
279
- action: renderTableAction(actionControl, row),
280
- };
281
- });
282
- }, [actionControl, data]);
283
-
284
-
285
- // ============================
286
- // ## Render detail page
287
- // ============================
288
- const detailPage = useMemo(() => {
289
- return (
290
- <div className="p-4">
291
- <div className={cn(
292
- "flex flex-col gap-y-4",
293
- )}>
294
- {!!selected && (typeof detailControl === "object" && detailControl?.length ? detailControl?.map((column, key) => {
295
- if (typeof column === "string") {
296
- return (<TypographyColumnComponent
297
- key={key}
298
- title={columns?.find((c) => c.selector == column)?.label}
299
- content={selected[column]}
300
- />)
301
- } else if (typeof column === "object") {
302
- return (<TypographyColumnComponent
303
- key={key}
304
- title={column?.label}
305
- content={typeof column?.item === "string" ? selected[column?.item] : column?.item(selected)}
306
- />)
307
- } else {
308
- return column?.(selected)
309
- }
310
- }) : typeof detailControl == "function" ? detailControl(selected) : columns?.map((column, key) => (
311
- <TypographyColumnComponent
312
- key={key}
313
- title={column.label}
314
- content={selected[column.selector]}
315
- />
316
- )))}
317
- </div>
318
- </div>
319
- )
320
- }, [selected, detailControl]);
321
-
322
-
323
-
324
-
325
- // ============================
326
- // ## Form preparation
327
- // ============================
328
- const fields = useMemo(() => {
329
- return formControl?.fields?.length ? formControl?.fields.map((form) => {
330
- return typeof form === "string" ? {
331
- col : 12,
332
- type : "text",
333
- construction : {
334
- name : form,
335
- label : form,
336
- },
337
- } : { ...form };
338
- }) : data?.forms || data?.columns || columnControl?.map((col) => {
339
- return {
340
- col : 12,
341
- type : "text",
342
- construction : {
343
- name : typeof col == "string" ? col : col?.selector,
344
- label : typeof col == "string" ? col : col?.label,
345
- placeholder : `Masukkan ${ typeof col == "string" ? col : col?.label}...`,
346
- },
347
- };
348
- }) || (data?.data?.at(0) ? Object.keys(data.data[0]).map((col) => {
349
- return {
350
- col : 12,
351
- type : "text",
352
- construction : {
353
- name : col,
354
- label : col,
355
- placeholder : `Masukkan ${col}...`,
356
- },
357
- };
358
- })
359
- : []);
360
- }, [formControl, data]);
361
-
362
-
363
-
364
- // ============================
365
- // ## Render form page
366
- // ============================
367
- const formPage = useMemo(async () => {
368
- return (
369
- <FormSupervisionComponent
370
- submitControl={(fetchControl as ApiType).path ? {
371
- path: `${(fetchControl as ApiType).path}/${(selected as { id: number })?.id || "" }`,
372
- method: !(selected as { id: number })?.id ? "POST" : "PUT",
373
- } : (fetchControl as ApiType).url ? {
374
- url: `${(fetchControl as ApiType).url}/${(selected as { id: number })?.id || ""}`,
375
- method: !(selected as { id: number })?.id ? "POST" : "PUT",
376
- } : { idb: (fetchControl as ({ idb: UseResourceIdb }))?.idb }
377
- }
378
- fields={fields as FormType[]}
379
- defaultValue={formControl?.defaultValue ? await formControl?.defaultValue(selected || null) : selected}
380
- payload={formControl?.payload}
381
- onSuccess={() => {
382
- reset();
383
- setToggle(`MODAL_FORM_${toggleKey}`, false);
384
- }}
385
- />
386
- )
387
- }, [selected, fetchControl, formControl]);
388
-
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="text-lg lg:text-xl font-bold mb-2 lg:mb-4">{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="pl-1.5 pr-3 mr-2 border-r" 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="px-1.5 rounded-md relative" key={"import"}>
425
- <ButtonComponent
426
- icon={faFileExcel}
427
- label="Import"
428
- variant="outline"
429
- className="!text-foreground"
430
- onClick={() => setToggle(`MODAL_IMPORT_${toggleKey}`)}
431
- size="sm"
432
- />
433
- </div>
434
- )
435
- }
436
-
437
- if (cb == "EXPORT") {
438
- return (
439
- <div className="px-1.5 rounded-md relative" key={"export-excel"}>
440
- <ButtonComponent
441
- icon={faFileExcel}
442
- label="Export"
443
- variant="outline"
444
- className="!text-foreground"
445
- onClick={() => setToggle(`MODAL_EXPORT_${toggleKey}`)}
446
- size="sm"
447
- />
448
- </div>
449
- )
450
- }
451
-
452
- if (cb == "PRINT") {
453
- return (
454
- <div className="px-1.5 rounded-md relative" key={"export-pdf"}>
455
- <ButtonComponent
456
- icon={faFilePdf}
457
- label="Cetak"
458
- variant="outline"
459
- className="!text-foreground"
460
- onClick={() => setToggle(`MODAL_PRINT_${toggleKey}`)}
461
- size="sm"
462
- />
463
- </div>
464
- )
465
- }
466
-
467
- return cb
468
- }) || [
469
- ...(!isSm ? [
470
- <div className="pl-1.5 pr-3 mr-2 border-r" 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={onRowClick ? onRowClick : detailControl != false ? (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
- <IconButtonComponent
521
- icon={faPlus}
522
- className="fixed bottom-2 left-2 w-12 h-12 md:hidden"
523
- size="lg"
524
- rounded
525
- onClick={() => {
526
- setToggle(`MODAL_FORM_${toggleKey}`)
527
- setSelected(null)
528
- }}
529
- />
530
-
531
-
532
- {isSm ? (
533
- <BottomSheetComponent
534
- show={!!toggle[`MODAL_SHOW_${toggleKey}`]}
535
- onClose={() => setToggle(`MODAL_SHOW_${toggleKey}`, false)}
536
- className="bg-background"
537
- footer={renderTableAction(actionControl, undefined, {className: isSm ? "justify-end p-2 bg-background" : "justify-end", size: isSm ? "sm" : "md"})}
538
- size="98vh"
539
- >
540
- {detailPage}
541
- </BottomSheetComponent>
542
- ) : (
543
- <FloatingPageComponent
544
- show={!!toggle[`MODAL_SHOW_${toggleKey}`]}
545
- onClose={() => setToggle(`MODAL_SHOW_${toggleKey}`, false)}
546
- title="Detail"
547
- className="bg-background"
548
- footer={renderTableAction(actionControl, undefined, {className: isSm ? "justify-end p-2 bg-background" : "justify-end", size: isSm ? "sm" : "md"})}
549
- >
550
- {detailPage}
551
- </FloatingPageComponent>
552
- )}
553
-
554
-
555
- {isSm ? (
556
- <BottomSheetComponent
557
- show={!!toggle[`MODAL_FORM_${toggleKey}`]}
558
- onClose={() => setToggle(`MODAL_FORM_${toggleKey}`, false)}
559
- className={cn("bg-background", formControl?.modalControl?.className)}
560
- size="98vh"
561
- >
562
- <div className="p-4 h-[110vh]">
563
- {formPage}
564
- </div>
565
- </BottomSheetComponent>
566
- ) : (
567
- <FloatingPageComponent
568
- show={!!toggle[`MODAL_FORM_${toggleKey}`]}
569
- onClose={() => setToggle(`MODAL_FORM_${toggleKey}`, false)}
570
- title={!!selected ? "Ubah Data" : "Tambah Data"}
571
- className={cn("bg-background", formControl?.modalControl?.className)}
572
- >
573
- <div className="p-4">
574
- {formPage}
575
- </div>
576
- </FloatingPageComponent>
577
- )}
578
-
579
-
580
- {ExportExcel && (
581
- <FloatingPageComponent
582
- show={!!toggle[`MODAL_EXPORT_${toggleKey}`]}
583
- onClose={() => setToggle(`MODAL_EXPORT_${toggleKey}`, false)}
584
- title="Export Ke Excel"
585
- className="bg-background md:w-[1200px] max-w-[1200px]"
586
- >
587
- <ExportExcel
588
- fetchControl={fetchControl as FetchControlType}
589
- filename={"Export - " + title}
590
- columnControl={columns?.map((cc: TableSupervisionColumnProps) => ({
591
- label: cc.label || "",
592
- selector: cc.selector || "",
593
- status: cc.exportable === false ? "hidden" : cc.exportable === true ? "default" : typeof cc.exportable === "string" ? cc.exportable : undefined,
594
- }))}
595
- />
596
- </FloatingPageComponent>
597
- )}
598
-
599
-
600
- {ImportExcel && (
601
- <FloatingPageComponent
602
- show={!!toggle[`MODAL_IMPORT_${toggleKey}`]}
603
- onClose={() => setToggle(`MODAL_IMPORT_${toggleKey}`, false)}
604
- title="Import Dari Excel"
605
- className="bg-background md:w-[1200px] max-w-[1200px]"
606
- >
607
- <ImportExcel
608
- submitControl={importControl}
609
- fetchControl={
610
- (fetchControl as ApiType).path ? {
611
- path: (fetchControl as ApiType).path,
612
- } : undefined
613
- }
614
- columnControl={columns?.filter((cc: TableSupervisionColumnProps) => cc.importable !== false).map((cc: TableSupervisionColumnProps) => ({
615
- label: cc.label || "",
616
- selector: cc.selector || "",
617
- }))}
618
- />
619
- </FloatingPageComponent>
620
- )}
621
-
622
-
623
- {/* <FloatingPageComponent
624
- show={!!toggle[`MODAL_PRINT_${toggleKey}`]}
625
- onClose={() => setToggle(`MODAL_PRINT_${toggleKey}`, false)}
626
- title="Print PDF"
627
- className="bg-background md:w-[1200px] max-w-[1200px]"
628
- >
629
- <PrintTable
630
- fetchControl={fetchControl}
631
- columnControl={columns?.map((cc) => ({
632
- label: cc.label || "",
633
- selector: cc.selector || "",
634
- }))}
635
- title={"Print - " + title}
636
- />
637
- </FloatingPageComponent> */}
638
-
639
-
640
- <ModalConfirmComponent
641
- show={!!toggle[`MODAL_DELETE_${toggleKey}`]}
642
- onClose={() => setToggle(`MODAL_DELETE_${toggleKey}`, false)}
643
- icon={faQuestionCircle}
644
- title={`Menghapus Data?`}
645
- submitControl={{
646
- onSubmit: {
647
- ...((fetchControl as ApiType).path
648
- ? {path: `${(fetchControl as ApiType).path}/${(selected as { id: number })?.id || ""}`}
649
- : (fetchControl as ApiType).url ? {url: `${(fetchControl as ApiType).url}/${(selected as { id: number })?.id || ""}`}
650
- : { idb: { ...(fetchControl as ({ idb: UseResourceIdb }))?.idb, id: (selected as { id: number })?.id || "" }}
651
- ),
652
- method: "DELETE",
653
- },
654
- onSuccess: () => {
655
- reset();
656
- setToggle(`MODAL_DELETE_${toggleKey}`, false);
657
- },
658
- }}
659
- >
660
- {columns?.at(0)?.selector && selected ? (
661
- <p className="px-2 pb-2 text-sm text-center">Yakin menghapus <span className="font-semibold">&quot;{selected[columns?.at(0)?.selector || ""]}&quot;</span>?</p>
662
- ) : (
663
- <p className="px-2 pb-2 text-sm text-center">Yakin yang dihapus sudah benar?</p>
664
- )}
665
- </ModalConfirmComponent>
666
-
667
- {actionControl && Array.isArray(actionControl) && actionControl.filter((ac) => typeof ac == "object")?.map((ac, acKey) => {
668
- const submitControl = ac.modal?.submitControl?.onSubmit as ApiType;
669
- return (
670
- <ModalConfirmComponent
671
- key={acKey}
672
- show={!!toggle[`MODAL_${conversion.strSnake(ac.label).toUpperCase()}_${toggleKey}`]}
673
- onClose={() => setToggle(`MODAL_${conversion.strSnake(ac.label).toUpperCase()}_${toggleKey}`, false)}
674
- icon={ac?.modal?.icon || faQuestionCircle}
675
- title={ac?.modal?.title || ac.label}
676
- submitControl={{
677
- onSubmit: {
678
- ...(submitControl?.path
679
- ? {path: `${submitControl?.path}/${(selected as { id: number })?.id || ""}`}
680
- : {url: `${submitControl?.url}/${(selected as { id: number })?.id || ""}`}
681
- ),
682
- method: submitControl?.method || "POST",
683
- },
684
- onSuccess: () => {
685
- reset();
686
- setToggle(`MODAL_${conversion.strSnake(ac.label).toUpperCase()}_${conversion.strSnake(tableKey).toUpperCase()}`, false);
687
- setSelected(null)
688
- ac.modal?.submitControl?.onSuccess?.()
689
- },
690
- }}
691
- >{ac.modal?.children}</ModalConfirmComponent>
692
- )
693
- })}
694
- </Suspense>
695
- </>
696
- );
697
- }