@skalfa/skalfa-app 1.0.2 → 1.0.5

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 (99) 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 +39 -24
  13. package/components/index.ts +1 -3
  14. package/package.json +8 -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 +3 -0
  20. package/blueprints/starter.blueprint.json +0 -103
  21. package/components/base.components/accordion/Accordion.component.tsx +0 -82
  22. package/components/base.components/breadcrumb/Breadcrumb.component.tsx +0 -80
  23. package/components/base.components/button/Button.component.tsx +0 -91
  24. package/components/base.components/button/IconButton.component.tsx +0 -88
  25. package/components/base.components/button/button.decorate.ts +0 -82
  26. package/components/base.components/card/AlertCard.component.tsx +0 -69
  27. package/components/base.components/card/Card.component.tsx +0 -25
  28. package/components/base.components/card/DashboardCard.component.tsx +0 -44
  29. package/components/base.components/card/GalleryCard.component.tsx +0 -50
  30. package/components/base.components/card/ProductCard.component.tsx +0 -65
  31. package/components/base.components/card/ProfileCard.component.tsx +0 -71
  32. package/components/base.components/carousel/Carousel.component.tsx +0 -113
  33. package/components/base.components/chip/Chip.component.tsx +0 -39
  34. package/components/base.components/document/DocumentViewer.component.tsx +0 -164
  35. package/components/base.components/document/ExportExcel.component.tsx +0 -340
  36. package/components/base.components/document/ImportExcel.component.tsx +0 -315
  37. package/components/base.components/document/PrintTable.component.tsx +0 -204
  38. package/components/base.components/document/RenderPDF.component.tsx +0 -416
  39. package/components/base.components/index.ts +0 -85
  40. package/components/base.components/input/Checkbox.component.tsx +0 -109
  41. package/components/base.components/input/Input.component.tsx +0 -332
  42. package/components/base.components/input/InputCheckbox.component.tsx +0 -174
  43. package/components/base.components/input/InputCurrency.component.tsx +0 -163
  44. package/components/base.components/input/InputDate.component.tsx +0 -352
  45. package/components/base.components/input/InputDatetime.component.tsx +0 -260
  46. package/components/base.components/input/InputDocument.component.tsx +0 -352
  47. package/components/base.components/input/InputImage.component.tsx +0 -533
  48. package/components/base.components/input/InputMap.component.tsx +0 -318
  49. package/components/base.components/input/InputNumber.component.tsx +0 -192
  50. package/components/base.components/input/InputOtp.component.tsx +0 -169
  51. package/components/base.components/input/InputPassword.component.tsx +0 -236
  52. package/components/base.components/input/InputRadio.component.tsx +0 -175
  53. package/components/base.components/input/InputTime.component.tsx +0 -276
  54. package/components/base.components/input/InputValues.component.tsx +0 -68
  55. package/components/base.components/input/Radio.component.tsx +0 -102
  56. package/components/base.components/input/Select.component.tsx +0 -541
  57. package/components/base.components/modal/BottomSheet.component.tsx +0 -246
  58. package/components/base.components/modal/FloatingPage.component.tsx +0 -104
  59. package/components/base.components/modal/Modal.component.tsx +0 -96
  60. package/components/base.components/modal/ModalConfirm.component.tsx +0 -218
  61. package/components/base.components/modal/Toast.component.tsx +0 -126
  62. package/components/base.components/nav/Bottombar.component.tsx +0 -116
  63. package/components/base.components/nav/Footer.component.tsx +0 -144
  64. package/components/base.components/nav/Headbar.component.tsx +0 -104
  65. package/components/base.components/nav/Navbar.component.tsx +0 -100
  66. package/components/base.components/nav/Sidebar.component.tsx +0 -301
  67. package/components/base.components/nav/Tabbar.component.tsx +0 -60
  68. package/components/base.components/nav/Wizard.component.tsx +0 -73
  69. package/components/base.components/supervision/FormSupervision.component.tsx +0 -434
  70. package/components/base.components/supervision/TableSupervision.component.tsx +0 -697
  71. package/components/base.components/table/ControlBar.component.tsx +0 -497
  72. package/components/base.components/table/FilterComponent.tsx +0 -518
  73. package/components/base.components/table/Pagination.component.tsx +0 -159
  74. package/components/base.components/table/Table.component.tsx +0 -469
  75. package/components/base.components/typography/TypographyArticle.component.tsx +0 -26
  76. package/components/base.components/typography/TypographyColumn.component.tsx +0 -20
  77. package/components/base.components/typography/TypographyContent.component.tsx +0 -20
  78. package/components/base.components/typography/TypographyTips.component.tsx +0 -20
  79. package/components/base.components/wrap/Draggable.component.tsx +0 -303
  80. package/components/base.components/wrap/IDBProvider.tsx +0 -12
  81. package/components/base.components/wrap/Image.component.tsx +0 -10
  82. package/components/base.components/wrap/OutsideClick.component.tsx +0 -48
  83. package/components/base.components/wrap/ScrollContainer.component.tsx +0 -104
  84. package/components/base.components/wrap/ShortcutProvider.tsx +0 -57
  85. package/components/base.components/wrap/Swipe.component.tsx +0 -93
  86. package/components/construct.components/example.tsx +0 -1
  87. package/components/construct.components/index.ts +0 -5
  88. package/components/structure.components/example.tsx +0 -1
  89. package/components/structure.components/index.ts +0 -5
  90. package/langs/index.ts +0 -1
  91. package/langs/validation.langs.ts +0 -17
  92. package/schema/idb/app.schema.ts +0 -9
  93. package/schema/index.ts +0 -5
  94. package/utils/commands/barrels.ts +0 -28
  95. package/utils/commands/blueprint.ts +0 -421
  96. package/utils/commands/light.ts +0 -21
  97. package/utils/commands/logger.ts +0 -42
  98. package/utils/commands/stubs/table-blueprint.stub +0 -13
  99. package/utils/commands/use-pdf.ts +0 -29
@@ -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
- }