@skalfa/skalfa-component 1.0.10 → 1.0.12

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.
@@ -69,15 +69,15 @@ const defaultColumns: FooterColumnProps[] = [
69
69
  ];
70
70
 
71
71
  export function FooterComponent({
72
- brandTitle = "Next Light v.3",
73
- brandSubtitle = "The Magic Starter Template",
74
- address = { label: "Soekarno Hatta No 27 C, Ponorogo, Jawa Timur, Indonesia", path: "https://maps.app.goo.gl/TY2QDjFPm3RfwjUq6" },
75
- phone = { label: "+62 888888888888", path: "https://wa.me/6281456140392" },
76
- email = { label: "example@gmail.com", path: "mailto:sejedigital@gmail.com" },
77
- supportHours = "24 / 7 Online Suport | Senin - Sabtu ( 09.00 s/d 17.00 )",
78
- socials = defaultSocials,
79
- columns = defaultColumns,
80
- copyrightLink = { label: "sejedigital.com 2020 - 2025", path: "https://sejedigital.com/" },
72
+ brandTitle = "Next Light v.3",
73
+ brandSubtitle = "The Magic Starter Template",
74
+ address = { label: "Soekarno Hatta No 27 C, Ponorogo, Jawa Timur, Indonesia", path: "https://maps.app.goo.gl/TY2QDjFPm3RfwjUq6" },
75
+ phone = { label: "+62 888888888888", path: "https://wa.me/6281456140392" },
76
+ email = { label: "example@gmail.com", path: "mailto:sejedigital@gmail.com" },
77
+ supportHours = "24 / 7 Online Suport | Senin - Sabtu ( 09.00 s/d 17.00 )",
78
+ socials = defaultSocials,
79
+ columns = defaultColumns,
80
+ copyrightLink = { label: "sejedigital.com 2020 - 2025", path: "https://sejedigital.com/" },
81
81
  }: FooterProps) {
82
82
  return (
83
83
  <>
@@ -37,12 +37,12 @@ const defaultSpecialLink = {
37
37
  } as const;
38
38
 
39
39
  export function NavbarComponent({
40
- logoTitle = "Next Light v.3",
41
- logoSubtitle = "The Magic Starter Template",
42
- logoPath = "/",
43
- specialLink = defaultSpecialLink,
44
- items = defaultItems,
45
- isLoggedIn = false,
40
+ logoTitle = "Next Light v.3",
41
+ logoSubtitle = "The Magic Starter Template",
42
+ logoPath = "/",
43
+ specialLink = defaultSpecialLink,
44
+ items = defaultItems,
45
+ isLoggedIn = false,
46
46
  onLogin,
47
47
  onRegister,
48
48
  onProfile,
@@ -154,7 +154,7 @@ export function SidebarComponent({
154
154
  </div>
155
155
 
156
156
  <nav className={cn(
157
- "flex flex-col flex-1 overflow-hidden",
157
+ "flex flex-col flex-1 overflow-y-auto overflow-x-hidden",
158
158
  toggle[`SIDEBAR${id ? "" : "_" + id?.toUpperCase()}`] ? "w-full" : "w-0 lg:w-full",
159
159
  )}>
160
160
  {items?.map((menu_head, menu_head_key) => {
@@ -41,9 +41,7 @@ export function TabbarComponent({
41
41
  key={i}
42
42
  className={cn(
43
43
  "tabbar-item",
44
- isItemActive
45
- ? "tabbar-item-active"
46
- : "tabbar-item-inactive",
44
+ isItemActive ? "tabbar-item-active" : "tabbar-item-inactive",
47
45
  pcn<CT>(className, "item"),
48
46
  pcn<CT>(className, "active"),
49
47
  )}
@@ -301,7 +301,7 @@ export function FormSupervisionComponent({
301
301
  <div>
302
302
  <ButtonComponent
303
303
  icon={"solid/plus"}
304
- label={`Tambah ${label || mapName}`}
304
+ label={`${l.base.add ? l.base.add() : "Add"} ${label || mapName}`}
305
305
  variant="outline"
306
306
  size="sm"
307
307
  onClick={addGroup}
@@ -332,6 +332,10 @@ export function FormSupervisionComponent({
332
332
  );
333
333
  };
334
334
 
335
+ useEffect(() => {
336
+ setModal(false)
337
+ }, [values]);
338
+
335
339
  return (
336
340
  <>
337
341
  {title && <h4 className={cn("text-lg font-semibold mb-4", pcn<CT>(className, "title"))}>{title}</h4>}
@@ -341,7 +345,7 @@ export function FormSupervisionComponent({
341
345
  <div className="w-12 h-12 rounded-full bg-blue-50 flex items-center justify-center">
342
346
  <Icon icon={"solid/check"} className="text-primary text-2xl" />
343
347
  </div>
344
- <p className="text-primary text-lg font-semibold mt-4">{successMessage || "Berhasil disimpan!"}</p>
348
+ <p className="text-primary text-lg font-semibold mt-4">{successMessage || l.base?.success ? l.base.success() : "Success!"}</p>
345
349
  </div>
346
350
  ) : (
347
351
  <form id={id} className={cn("flex flex-col h-full pb-8")} onSubmit={submit}>
@@ -386,7 +390,7 @@ export function FormSupervisionComponent({
386
390
  <Icon icon={"solid/exclamation-triangle"} className="text-danger text-lg" />
387
391
  </div>
388
392
  </div>
389
- <p className="text-danger text-sm font-semibold">{"Terjadi Masalah, Coba ulangi lagi!"}</p>
393
+ <p className="text-danger text-sm font-semibold">{l.base.formFailed ? l.base.formFailed() : "Failed to save data, please check your data and internet connection then try again!"}</p>
390
394
  </div>
391
395
  )}
392
396
  </div>
@@ -396,7 +400,7 @@ export function FormSupervisionComponent({
396
400
  <ModalConfirmComponent
397
401
  show={confirm.show}
398
402
  onClose={() => confirm.onClose()}
399
- title={l.base?.confirmTitle ? l.base.confirmTitle() : "Yakin"}
403
+ title={l.base?.formConfirm ? l.base.formConfirm() : "Are you sure all data is correct?"}
400
404
  submitControl={{ onSubmit: () => confirm?.onConfirm(), paint: "primary" }}
401
405
  />
402
406
  </>
@@ -112,31 +112,31 @@ export function TableSupervisionComponent({
112
112
  shortcut.register("arrowdown", () => {
113
113
  const max = data?.data?.length - 1;
114
114
  setFocus(focus == null ? 0 : focus >= max ? max : (focus + 1))
115
- }, "Pilih data kebawah")
115
+ }, l.base.shortcutDown ? l.base.shortcutDown() : "")
116
116
 
117
117
  shortcut.register("arrowup", () => {
118
118
  setFocus(focus == null ? 0 : focus <= 0 ? 0 : (focus - 1))
119
- }, "Pilih data keatas")
119
+ }, l.base.shortcutUp ? l.base.shortcutUp() : "")
120
120
 
121
121
  if(focus != null) {
122
122
  shortcut.register("delete", () => {
123
123
  setSelected(data?.data?.at(focus))
124
124
  setToggle(`MODAL_DELETE_${toggleKey}`)
125
- }, "Delete data yang dipilih")
125
+ }, l.base.shortcutDelete ? l.base.shortcutDelete() : "")
126
126
 
127
127
  shortcut.register(" ", () => {
128
128
  setSelected(data?.data?.at(focus))
129
129
  setToggle(`MODAL_FORM_${toggleKey}`)
130
- }, "Edit data yang dipilih")
130
+ }, l.base.shortcutEdit ? l.base.shortcutEdit() : "")
131
131
 
132
132
  shortcut.register("enter", () => {
133
133
  setSelected(data?.data?.at(focus))
134
134
  setToggle(`MODAL_SHOW_${toggleKey}`)
135
- }, "Detail data yang dipilih")
135
+ }, l.base.shortcutDetail ? l.base.shortcutDetail() : "")
136
136
 
137
137
  shortcut.register("escape", () => {
138
138
  setFocus(null)
139
- }, "Kembali")
139
+ }, l.base.shortcutBack ? l.base.shortcutBack() : "")
140
140
  }
141
141
  }
142
142
 
@@ -339,9 +339,9 @@ export function TableSupervisionComponent({
339
339
  col : 12,
340
340
  type : "text",
341
341
  construction : {
342
- name : typeof col == "string" ? col : col?.selector,
343
- label : typeof col == "string" ? col : col?.label,
344
- placeholder : `Masukkan ${ typeof col == "string" ? col : col?.label}...`,
342
+ name : typeof col == "string" ? col : col?.selector,
343
+ label : typeof col == "string" ? col : col?.label,
344
+ placeholder : l.base.inputPlaceholder ? l.base.inputPlaceholder(`${ typeof col == "string" ? col : col?.label}`) : `Enter ${ typeof col == "string" ? col : col?.label}...`,
345
345
  },
346
346
  };
347
347
  }) || (data?.data?.at(0) ? Object.keys(data.data[0]).map((col) => {
@@ -351,7 +351,7 @@ export function TableSupervisionComponent({
351
351
  construction : {
352
352
  name : col,
353
353
  label : col,
354
- placeholder : `Masukkan ${col}...`,
354
+ placeholder : l.base.inputPlaceholder ? l.base.inputPlaceholder(col) : `Enter ${col}...`,
355
355
  },
356
356
  };
357
357
  })
@@ -367,12 +367,12 @@ export function TableSupervisionComponent({
367
367
  return (
368
368
  <FormSupervisionComponent
369
369
  submitControl={(fetchControl as ApiType).path ? {
370
- path: `${(fetchControl as ApiType).path}/${(selected as { id: number })?.id || "" }`,
371
- method: !(selected as { id: number })?.id ? "POST" : "PUT",
372
- } : (fetchControl as ApiType).url ? {
373
- url: `${(fetchControl as ApiType).url}/${(selected as { id: number })?.id || ""}`,
374
- method: !(selected as { id: number })?.id ? "POST" : "PUT",
375
- } : { idb: (fetchControl as ({ idb: UseResourceIdb }))?.idb }
370
+ path : `${(fetchControl as ApiType).path}/${(selected as { id: number })?.id || "" }`,
371
+ method : !(selected as { id: number })?.id ? "POST" : "PUT",
372
+ } : (fetchControl as ApiType).url ? {
373
+ url : `${(fetchControl as ApiType).url}/${(selected as { id: number })?.id || ""}`,
374
+ method : !(selected as { id: number })?.id ? "POST" : "PUT",
375
+ } : { idb: (fetchControl as ({ idb: UseResourceIdb }))?.idb }
376
376
  }
377
377
  fields={fields?.filter((f: any) => f.visibility ? !(selected as { id: number })?.id ? ["*", "create"]?.includes(f.visibility) : ["*", "update"]?.includes(f.visibility) : true) as FormType[]}
378
378
  defaultValue={formControl?.defaultValue ? await formControl?.defaultValue(selected || null) : selected}
@@ -401,11 +401,12 @@ export function TableSupervisionComponent({
401
401
  controlBar={controlBar?.map((cb) => {
402
402
  if (cb == "CREATE") {
403
403
  if (isSm) return
404
+
404
405
  return (
405
406
  <div className="pl-1.5 pr-3 mr-2 border-r" key="button-add">
406
407
  <ButtonComponent
407
408
  icon={"solid/plus"}
408
- label="Tambah Data"
409
+ label={l.base.add ? l.base.add() : "Add"}
409
410
  size="sm"
410
411
  onClick={() => {
411
412
  setToggle(`MODAL_FORM_${toggleKey}`)
@@ -421,7 +422,6 @@ export function TableSupervisionComponent({
421
422
  <div className="px-1.5 rounded-md relative" key={"import"}>
422
423
  <ButtonComponent
423
424
  icon={"solid/file-excel"}
424
- label="Import"
425
425
  variant="outline"
426
426
  className="!text-foreground"
427
427
  onClick={() => setToggle(`MODAL_IMPORT_${toggleKey}`)}
@@ -436,7 +436,6 @@ export function TableSupervisionComponent({
436
436
  <div className="px-1.5 rounded-md relative" key={"export-excel"}>
437
437
  <ButtonComponent
438
438
  icon={"solid/file-excel"}
439
- label="Export"
440
439
  variant="outline"
441
440
  className="!text-foreground"
442
441
  onClick={() => setToggle(`MODAL_EXPORT_${toggleKey}`)}
@@ -451,7 +450,7 @@ export function TableSupervisionComponent({
451
450
  <div className="px-1.5 rounded-md relative" key={"export-pdf"}>
452
451
  <ButtonComponent
453
452
  icon={"solid/file-pdf"}
454
- label="Cetak"
453
+ label={l.base.print ? l.base.print() : "Print"}
455
454
  variant="outline"
456
455
  className="!text-foreground"
457
456
  onClick={() => setToggle(`MODAL_PRINT_${toggleKey}`)}
@@ -467,7 +466,7 @@ export function TableSupervisionComponent({
467
466
  <div className="pl-1.5 pr-3 mr-2 border-r" key="button-add">
468
467
  <ButtonComponent
469
468
  icon={"solid/plus"}
470
- label="Tambah Data"
469
+ label={l.base.add ? l.base.add() : "Add"}
471
470
  size="sm"
472
471
  onClick={() => {
473
472
  setToggle(`MODAL_FORM_${toggleKey}`)
@@ -532,7 +531,7 @@ export function TableSupervisionComponent({
532
531
  <FloatingPageComponent
533
532
  show={!!toggle[`MODAL_SHOW_${toggleKey}`]}
534
533
  onClose={() => setToggle(`MODAL_SHOW_${toggleKey}`, false)}
535
- title="Detail"
534
+ title={l.base.detailTitle ? l.base.detailTitle() : "Detail"}
536
535
  className="bg-background"
537
536
  footer={renderTableAction(actionControl, undefined, {className: isSm ? "justify-end p-2 bg-background" : "justify-end", size: isSm ? "sm" : "md"})}
538
537
  >
@@ -543,7 +542,7 @@ export function TableSupervisionComponent({
543
542
  <FloatingPageComponent
544
543
  show={!!toggle[`MODAL_FORM_${toggleKey}`]}
545
544
  onClose={() => setToggle(`MODAL_FORM_${toggleKey}`, false)}
546
- title={!!selected ? "Ubah Data" : "Tambah Data"}
545
+ title={!!selected ? (l.base.editTitle ? l.base.editTitle() : "Edit") : (l.base.addTitle ? l.base.addTitle() : "Add")}
547
546
  className={cn("bg-white", formControl?.modalControl?.className)}
548
547
  >
549
548
  <div className="p-4">
@@ -555,12 +554,12 @@ export function TableSupervisionComponent({
555
554
  <FloatingPageComponent
556
555
  show={!!toggle[`MODAL_EXPORT_${toggleKey}`]}
557
556
  onClose={() => setToggle(`MODAL_EXPORT_${toggleKey}`, false)}
558
- title="Export Ke Excel"
557
+ title={l.base.exportTitle ? l.base.exportTitle() : "Export"}
559
558
  className="bg-white md:w-[1200px] max-w-[1200px]"
560
559
  >
561
560
  <ExportExcel
562
561
  fetchControl={fetchControl}
563
- filename={"Export - " + title}
562
+ filename={"export"}
564
563
  columnControl={columns?.map((cc) => ({
565
564
  label: cc.label || "",
566
565
  selector: cc.selector || "",
@@ -572,7 +571,7 @@ export function TableSupervisionComponent({
572
571
  <FloatingPageComponent
573
572
  show={!!toggle[`MODAL_IMPORT_${toggleKey}`]}
574
573
  onClose={() => setToggle(`MODAL_IMPORT_${toggleKey}`, false)}
575
- title="Import Dari Excel"
574
+ title={l.base.importTitle ? l.base.importTitle() : "Import"}
576
575
  className="bg-white md:w-[1200px] max-w-[1200px]"
577
576
  >
578
577
  <ImportExcel
@@ -605,7 +604,7 @@ export function TableSupervisionComponent({
605
604
  <ModalConfirmComponent
606
605
  show={!!toggle[`MODAL_DELETE_${toggleKey}`]}
607
606
  onClose={() => setToggle(`MODAL_DELETE_${toggleKey}`, false)}
608
- title={<>Menghapus Data <span className="font-semibold">&quot;{selected?.[columns?.at(0)?.selector || ""]}&quot;</span> ?</>}
607
+ title={l.base.deleteTitle ? l.base.deleteTitle({ data: selected?.[columns?.at(0)?.selector || ""] }) : `Delete ${selected?.[columns?.at(0)?.selector || ""]}?`}
609
608
  submitControl={{
610
609
  onSubmit: {
611
610
  ...((fetchControl as ApiType).path
@@ -1,5 +1,5 @@
1
1
  "use client"
2
- import { Icon, type IconName } from "@skalfa/skalfa-icon";
2
+ import { Icon } from "@skalfa/skalfa-icon";
3
3
 
4
4
 
5
5
  import { ReactNode, useEffect, useRef } from 'react'
@@ -12,6 +12,7 @@ import { OutsideClickComponent } from "../wrap/OutsideClick.component";
12
12
  import { ButtonComponent } from "../button/Button.component";
13
13
  import { BottomSheetComponent } from "../modal/BottomSheet.component";
14
14
  import { FilterComponent, FilterColumnOption } from "./FilterComponent";
15
+ import { useLang } from "@skalfa/skalfa-lang";
15
16
 
16
17
  export type ControlBarOptionType = "SEARCH" | "SEARCHABLE" | "FILTER" | "SELECTABLE" | "REFRESH" | ReactNode;
17
18
 
@@ -56,6 +57,7 @@ export function ControlBarComponent({
56
57
  filter,
57
58
  onRefresh
58
59
  }: ControlBarProps) {
60
+ const l = useLang();
59
61
  const {toggle, setToggle} = useToggleContext()
60
62
  const { isSm } = useResponsive();
61
63
 
@@ -65,31 +67,31 @@ export function ControlBarComponent({
65
67
  if (options?.includes("SEARCH")) {
66
68
  shortcut.register("ctrl+s", () => {
67
69
  searchRef.current?.focus()
68
- }, "Cari")
70
+ }, l.base.shortcutSearch ? l.base.shortcutSearch() : "Search")
69
71
  }
70
72
 
71
73
  if (options?.includes("FILTER")) {
72
74
  shortcut.register("ctrl+f", () => {
73
75
  setToggle("FILTER")
74
- }, "Filter")
76
+ }, l.base.shortcutFilter ? l.base.shortcutFilter() : "Filter")
75
77
  }
76
78
 
77
79
  if (options?.includes("SORT")) {
78
80
  shortcut.register("ctrl+o", () => {
79
81
  setToggle("SORT")
80
- }, "Urutkan")
82
+ }, l.base.shortcutSort ? l.base.shortcutSort() : "Sort")
81
83
  }
82
84
 
83
85
  if (options?.includes("SELECTABLE")) {
84
86
  shortcut.register("ctrl+l", () => {
85
87
  setToggle("SELECTABLE")
86
- }, "Kolom Ditampilkan")
88
+ }, l.base.shortcutSelectable ? l.base.shortcutSelectable() : "Select")
87
89
  }
88
90
 
89
91
  if (options?.includes("REFRESH")) {
90
92
  shortcut.register("ctrl+shift+r", () => {
91
93
  onRefresh?.()
92
- }, "Refresh Tabel")
94
+ }, l.base.shortcutRefresh ? l.base.shortcutRefresh() : "Refresh")
93
95
  }
94
96
 
95
97
  return () => {
@@ -134,7 +136,7 @@ export function ControlBarComponent({
134
136
  <div className="control-bar-create-wrapper" key="button-add">
135
137
  <ButtonComponent
136
138
  icon="solid/plus"
137
- label="Tambah Data"
139
+ label={l.base.add ? l.base.add() : "Add"}
138
140
  size="sm"
139
141
  onClick={() => setToggle(`MODAL_FORM_${conversion.strSnake(id).toUpperCase()}`)}
140
142
  />
@@ -155,7 +157,7 @@ export function ControlBarComponent({
155
157
  <InputComponent
156
158
  ref={searchRef}
157
159
  name="search"
158
- placeholder="Cari disini..."
160
+ placeholder={l.base.searchPlaceholder ? l.base.searchPlaceholder() : "Search here..."}
159
161
  rightIcon="solid/magnifying-glass"
160
162
  value={search}
161
163
  onChange={(e) => onSearch?.(e)}
@@ -213,7 +215,7 @@ export function ControlBarComponent({
213
215
  !toggle.SELECTABLE && "control-bar-dropdown-hidden"
214
216
  )}
215
217
  >
216
- <p className='control-bar-dropdown-title'>Kolom Ditampilkan</p>
218
+ <p className='control-bar-dropdown-title'>{l.base.selectable ? l.base.selectable() : "Show Column"}</p>
217
219
  <InputCheckboxComponent
218
220
  vertical
219
221
  name="show_column"
@@ -256,7 +258,7 @@ export function ControlBarComponent({
256
258
  !toggle.SORT && "control-bar-dropdown-hidden"
257
259
  )}
258
260
  >
259
- <p className='control-bar-dropdown-title'>Urut Berdasarkan</p>
261
+ <p className='control-bar-dropdown-title'>{l.base.sort ? l.base.sort() : "Sort By"}</p>
260
262
  <div className='control-bar-sort-list'>
261
263
  {sortableOptions?.map((option, key) => {
262
264
  const sortBy = sort?.find((s) => s.split(" ")?.at(0) == option?.selector)?.split(" ")?.at(1) || "";
@@ -376,7 +378,7 @@ export function ControlBarComponent({
376
378
  <div key={key}>
377
379
  <InputComponent
378
380
  name="search"
379
- placeholder="Cari disini..."
381
+ placeholder={l.base.searchPlaceholder ? l.base.searchPlaceholder() : "Search here..."}
380
382
  rightIcon="solid/magnifying-glass"
381
383
  value={search}
382
384
  onChange={(e) => onSearch?.(e)}
@@ -418,7 +420,7 @@ export function ControlBarComponent({
418
420
  if (option == "SELECTABLE") {
419
421
  return (
420
422
  <div key={key}>
421
- <p className='control-bar-mobile-title'>Kolom Ditampilkan</p>
423
+ <p className='control-bar-mobile-title'>{l.base.selectable ? l.base.selectable() : "Show Column"}</p>
422
424
  <InputCheckboxComponent
423
425
  vertical
424
426
  name="show_column"
@@ -445,7 +447,7 @@ export function ControlBarComponent({
445
447
  if (option == "SORT") {
446
448
  return sortableOptions?.length ? (
447
449
  <div key={key}>
448
- <p className='control-bar-mobile-title'>Urut Berdasarkan</p>
450
+ <p className='control-bar-mobile-title'>{l.base.sort ? l.base.sort() : "Sort By"}</p>
449
451
  <div className='flex flex-col'>
450
452
  {sortableOptions?.map((option, key) => {
451
453
  const sortBy = sort?.find((s) => s.split(" ")?.at(0) == option?.selector)?.split(" ")?.at(1) || "";
@@ -11,6 +11,7 @@ import { InputNumberComponent } from "../input/InputNumber.component";
11
11
  import { ModalComponent } from "../modal/Modal.component";
12
12
  import { SelectComponent } from "../input/Select.component";
13
13
  import { useToggleContext } from "@contexts";
14
+ import { useLang } from "@skalfa/skalfa-lang";
14
15
 
15
16
  type CT = "base" | "title";
16
17
 
@@ -65,6 +66,8 @@ export function FilterComponent({
65
66
  onMinimize,
66
67
  className = "",
67
68
  }: AdvancedFilterProps) {
69
+ const l = useLang();
70
+
68
71
  const { isSm } = useResponsive();
69
72
  const { toggle, setToggle } = useToggleContext()
70
73
  const [filters, setFilters] = useState<ApiFilterType[]>([]);
@@ -116,7 +119,7 @@ export function FilterComponent({
116
119
  <>
117
120
  <div className={cn("filter-panel", pcn<CT>(className, "base"))}>
118
121
  <div className="filter-panel-header">
119
- <p className={cn(pcn<CT>(className, "title"))}>Filter</p>
122
+ <p className={cn(pcn<CT>(className, "title"))}>{l.base.filter ? l.base.filter() : "Filter"}</p>
120
123
  <div className="filter-panel-controls">
121
124
  <div className="filter-panel-bookmarks-wrapper">
122
125
  <ButtonComponent
@@ -150,7 +153,7 @@ export function FilterComponent({
150
153
 
151
154
  <ButtonComponent
152
155
  icon="solid/rotate"
153
- label="Bersihkan"
156
+ label={l.base.clear ? l.base.clear() : "Filter"}
154
157
  variant="outline"
155
158
  paint="primary"
156
159
  className="filter-panel-bookmark-btn icon::text-slate-400"
@@ -181,7 +184,7 @@ export function FilterComponent({
181
184
  <SelectComponent
182
185
  name={`filter_logic_${i}`}
183
186
  options={LOGIC_OPTIONS}
184
- placeholder="Dan/Atau"
187
+ placeholder={l.base.filterLogic ? l.base.filterLogic() : "And/Or"}
185
188
  value={f.logic}
186
189
  onChange={(e) => handleChange(i, "logic", e as "and" | "or")}
187
190
  className="filter-input-field"
@@ -196,7 +199,7 @@ export function FilterComponent({
196
199
  label: c.label,
197
200
  value: c.selector,
198
201
  }))}
199
- placeholder="Kolom"
202
+ placeholder={l.base.column ? l.base.column() : "Column"}
200
203
  value={f.column}
201
204
  onChange={(e) => handleChange(i, "column", e)}
202
205
  className="filter-input-field"
@@ -207,7 +210,7 @@ export function FilterComponent({
207
210
  <SelectComponent
208
211
  name={`filter_operator_${i}`}
209
212
  options={FILTER_OPERATORS}
210
- placeholder="Operator"
213
+ placeholder={l.base.filterOperator ? l.base.filterOperator() : "Operation"}
211
214
  value={f.type}
212
215
  onChange={(e) => handleChange(i, "type", e)}
213
216
  className="filter-input-field"
@@ -254,7 +257,7 @@ export function FilterComponent({
254
257
  })}
255
258
 
256
259
  <ButtonComponent
257
- label="Tambahkan"
260
+ label={l.base.add ? l.base.add() : "Add"}
258
261
  icon="solid/plus"
259
262
  variant="outline"
260
263
  paint="primary"
@@ -267,7 +270,7 @@ export function FilterComponent({
267
270
  <ModalComponent
268
271
  show={!!toggle["MODAL_BOOKMARK_LIST"]}
269
272
  onClose={() => setToggle("MODAL_BOOKMARK_LIST", false)}
270
- title="Bookmark Filter"
273
+ title={l.base.filterBookmarks ? l.base.filterBookmarks() : "Filter Bookmark"}
271
274
  >
272
275
  <div className="filter-bookmark-list-container">
273
276
  {bookmarks?.length ? bookmarks?.map((b, key) => (
@@ -296,7 +299,7 @@ export function FilterComponent({
296
299
  </div>
297
300
  </div>
298
301
  )) : (
299
- <div className="filter-bookmark-empty-text"> -- Tidak ada Bookmark --</div>
302
+ <div className="filter-bookmark-empty-text"> -- {l.base.filterBookmarkEmpty ? l.base.filterBookmarkEmpty() : "No Bookmark"} --</div>
300
303
  )}
301
304
  </div>
302
305
  </ModalComponent>
@@ -304,11 +307,11 @@ export function FilterComponent({
304
307
  <ModalComponent
305
308
  show={!!toggle["MODAL_BOOKMARK"]}
306
309
  onClose={() => setToggle("MODAL_BOOKMARK", false)}
307
- title="Bookmark Filter"
310
+ title={l.base.filterBookmark ? l.base.filterBookmark() : "Filter Bookmark"}
308
311
  footer={
309
312
  <div className="flex justify-end">
310
313
  <ButtonComponent
311
- label="Terapkan"
314
+ label={l.base.apply ? l.base.apply() : "Apply"}
312
315
  onClick={() => {
313
316
  let updated: FilterBookmark[] = [];
314
317
 
@@ -339,12 +342,12 @@ export function FilterComponent({
339
342
  <div className="filter-bookmark-form-container">
340
343
  <SelectComponent
341
344
  name={`bookmark_id`}
342
- placeholder="Pilih bookmark filter..."
345
+ placeholder={l.base.filterBookmarkPlaceholder ? l.base.filterBookmarkPlaceholder() : "Pilih bookmark filter..."}
343
346
  value={(toggle["MODAL_BOOKMARK"] as { bookmark_id: string })?.bookmark_id ?? ""}
344
347
  onChange={e => setToggle("MODAL_BOOKMARK", {...(toggle["MODAL_BOOKMARK"] as object), bookmark_id: e})}
345
348
  options={[
346
349
  {
347
- label: "-- BOOKMARK BARU --",
350
+ label: `-- ${l.base.filterBookmarkNew ? l.base.filterBookmarkNew() : "New Filter Bookmark"} --`,
348
351
  value: "new",
349
352
  },
350
353
  ...bookmarks.map((b) => ({
@@ -357,7 +360,7 @@ export function FilterComponent({
357
360
  {(toggle["MODAL_BOOKMARK"] as { bookmark_id: string })?.bookmark_id == "new" && (
358
361
  <InputComponent
359
362
  name="bookmark_name"
360
- placeholder="Masukkan nama bookmark..."
363
+ placeholder={l.base.filterBookmarkNewPlaceholder ? l.base.filterBookmarkNewPlaceholder() : "Masukkan nama bookmark..."}
361
364
  value={(toggle["MODAL_BOOKMARK"] as { bookmark_name: string })?.bookmark_name ?? ""}
362
365
  onChange={e => setToggle("MODAL_BOOKMARK", {...(toggle["MODAL_BOOKMARK"] as object), bookmark_name: e})}
363
366
  className="filter-bookmark-form-input"
@@ -392,6 +395,8 @@ export function InputFilterValueComponent({
392
395
  between,
393
396
  multiple,
394
397
  }: FilterInputProps) {
398
+ const l = useLang();
399
+
395
400
  const resolvedType = type || "text";
396
401
 
397
402
  if (!between) {
@@ -470,7 +475,7 @@ export function InputFilterValueComponent({
470
475
  name={`${name}_${pos}`}
471
476
  value={Number(currentValue) || 0}
472
477
  onChange={handle}
473
- placeholder={pos === "from" ? "Dari" : "Sampai"}
478
+ placeholder={pos === "from" ? l.base.filterFrom ? l.base.filterFrom() : "Dari" : l.base.filterTo ? l.base.filterTo() : "Sampai"}
474
479
  className={className}
475
480
  />
476
481
  );
@@ -480,7 +485,7 @@ export function InputFilterValueComponent({
480
485
  name={`${name}_${pos}`}
481
486
  value={Number(currentValue) || 0}
482
487
  onChange={handle}
483
- placeholder={pos === "from" ? "Dari" : "Sampai"}
488
+ placeholder={pos === "from" ? l.base.filterFrom ? l.base.filterFrom() : "Dari" : l.base.filterTo ? l.base.filterTo() : "Sampai"}
484
489
  className={className}
485
490
  />
486
491
  );
@@ -490,7 +495,7 @@ export function InputFilterValueComponent({
490
495
  name={`${name}_${pos}`}
491
496
  value={currentValue as string || ""}
492
497
  onChange={handle}
493
- placeholder={pos === "from" ? "Dari" : "Sampai"}
498
+ placeholder={pos === "from" ? l.base.filterFrom ? l.base.filterFrom() : "Dari" : l.base.filterTo ? l.base.filterTo() : "Sampai"}
494
499
  className={className}
495
500
  />
496
501
  );
@@ -500,7 +505,7 @@ export function InputFilterValueComponent({
500
505
  name={`${name}_${pos}`}
501
506
  value={currentValue || ""}
502
507
  onChange={(e: any) => handle(e.target.value)}
503
- placeholder={pos === "from" ? "Dari" : "Sampai"}
508
+ placeholder={pos === "from" ? l.base.filterFrom ? l.base.filterFrom() : "Dari" : l.base.filterTo ? l.base.filterTo() : "Sampai"}
504
509
  className={className}
505
510
  />
506
511
  );
@@ -10,6 +10,7 @@ import { ScrollContainerComponent } from "../wrap/ScrollContainer.component";
10
10
  import { FilterColumnOption } from "./FilterComponent";
11
11
  import { CheckboxComponent } from "../input/Checkbox.component";
12
12
  import { SwipeComponent, SwipeActionType } from "../wrap/Swipe.component";
13
+ import { useLang } from "@skalfa/skalfa-lang";
13
14
 
14
15
  type CT = "controller-bar" | "head-column" | "column" | "row" | "floating-action" | "base";
15
16
 
@@ -102,6 +103,8 @@ export function TableComponent({
102
103
 
103
104
  className = "",
104
105
  }: TableProps) {
106
+ const l = useLang();
107
+
105
108
  const [displayColumns, setDisplayColumns] = useState<string[]>([]);
106
109
  const [showFloatingAction, setShowFloatingAction] = useState(false);
107
110
  const [floatingActionActive, setFloatingActionActive] = useState<false | number>(false);
@@ -312,14 +315,14 @@ export function TableComponent({
312
315
  <div className="w-max min-w-full">
313
316
  <div className="table-loading-container">
314
317
  <h1 className="table-loading-text">
315
- Memuat data...
318
+ {l.base.tableLoading ? l.base.tableLoading() : ""}
316
319
  </h1>
317
320
  </div>
318
321
  </div>
319
322
  ) : !data || !data.length ? (
320
323
  <div className="table-empty-container">
321
324
  <h1 className="table-empty-text">
322
- Belum Ada Data
325
+ {l.base.tableEmpty ? l.base.tableEmpty() : "Data empty"}
323
326
  </h1>
324
327
  </div>
325
328
  ) : (
@@ -413,7 +416,7 @@ export function TableComponent({
413
416
 
414
417
  {!!actionBulking && !!checks?.length && (
415
418
  <div className="table-bulk-actions-bar">
416
- <div className="table-bulk-actions-title">{checks?.length} Data Terpilih</div>
419
+ <div className="table-bulk-actions-title">{checks?.length} {l.base.tableSelected ? l.base.tableSelected() : ""}</div>
417
420
  <div className="table-bulk-actions-buttons">
418
421
  {actionBulking?.(checks)}
419
422
  </div>
@@ -4,8 +4,10 @@ import { useEffect, Fragment } from "react";
4
4
  import { shortcut } from "@utils"
5
5
  import { useToggleContext } from "@contexts"
6
6
  import { ModalComponent } from "../modal/Modal.component";
7
+ import { useLang } from "@skalfa/skalfa-lang";
7
8
 
8
9
  export function ShortcutProvider() {
10
+ const l = useLang();
9
11
  const { setToggle, toggle } = useToggleContext();
10
12
  const shortcuts = shortcut.list()
11
13
 
@@ -16,7 +18,7 @@ export function ShortcutProvider() {
16
18
  useEffect(() => {
17
19
  shortcut.register("ctrl+/", () => {
18
20
  setToggle("MODAL_SHORTCUT_HELP")
19
- }, "List Shortcut")
21
+ }, l.base.shortcutList ? l.base.shortcutList() : "")
20
22
  }, [])
21
23
 
22
24
  function formatShortcutKey(key: string) {
@@ -39,7 +41,7 @@ export function ShortcutProvider() {
39
41
  <ModalComponent
40
42
  show={!!toggle["MODAL_SHORTCUT_HELP"]}
41
43
  onClose={() => setToggle("MODAL_SHORTCUT_HELP")}
42
- title="Shortcut"
44
+ title={l.base.shortcut ? l.base.shortcut() : ""}
43
45
  >
44
46
  <div className="shortcut-help-grid">
45
47
  {shortcuts.map(({ key, description }) => (