@skalfa/skalfa-app 1.0.0

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 (112) hide show
  1. package/.env.example +44 -0
  2. package/README.md +28 -0
  3. package/app/auth/edit/page.tsx +65 -0
  4. package/app/auth/login/page.tsx +63 -0
  5. package/app/auth/me/page.tsx +58 -0
  6. package/app/auth/register/page.tsx +69 -0
  7. package/app/auth/verify/page.tsx +53 -0
  8. package/app/dashboard/layout.tsx +47 -0
  9. package/app/dashboard/page.tsx +9 -0
  10. package/app/dashboard/user/page.tsx +77 -0
  11. package/app/index.ts +14 -0
  12. package/app/layout.tsx +38 -0
  13. package/app/page.tsx +13 -0
  14. package/barrels.json +6 -0
  15. package/blueprints/starter.blueprint.json +103 -0
  16. package/components/base.components/accordion/Accordion.component.tsx +82 -0
  17. package/components/base.components/breadcrumb/Breadcrumb.component.tsx +80 -0
  18. package/components/base.components/button/Button.component.tsx +91 -0
  19. package/components/base.components/button/IconButton.component.tsx +88 -0
  20. package/components/base.components/button/button.decorate.ts +82 -0
  21. package/components/base.components/card/AlertCard.component.tsx +69 -0
  22. package/components/base.components/card/Card.component.tsx +25 -0
  23. package/components/base.components/card/DashboardCard.component.tsx +44 -0
  24. package/components/base.components/card/GalleryCard.component.tsx +50 -0
  25. package/components/base.components/card/ProductCard.component.tsx +65 -0
  26. package/components/base.components/card/ProfileCard.component.tsx +71 -0
  27. package/components/base.components/carousel/Carousel.component.tsx +113 -0
  28. package/components/base.components/chip/Chip.component.tsx +39 -0
  29. package/components/base.components/document/DocumentViewer.component.tsx +164 -0
  30. package/components/base.components/document/ExportExcel.component.tsx +340 -0
  31. package/components/base.components/document/ImportExcel.component.tsx +315 -0
  32. package/components/base.components/document/PrintTable.component.tsx +204 -0
  33. package/components/base.components/document/RenderPDF.component.tsx +416 -0
  34. package/components/base.components/index.ts +85 -0
  35. package/components/base.components/input/Checkbox.component.tsx +109 -0
  36. package/components/base.components/input/Input.component.tsx +332 -0
  37. package/components/base.components/input/InputCheckbox.component.tsx +174 -0
  38. package/components/base.components/input/InputCurrency.component.tsx +163 -0
  39. package/components/base.components/input/InputDate.component.tsx +352 -0
  40. package/components/base.components/input/InputDatetime.component.tsx +260 -0
  41. package/components/base.components/input/InputDocument.component.tsx +352 -0
  42. package/components/base.components/input/InputImage.component.tsx +533 -0
  43. package/components/base.components/input/InputMap.component.tsx +318 -0
  44. package/components/base.components/input/InputNumber.component.tsx +192 -0
  45. package/components/base.components/input/InputOtp.component.tsx +169 -0
  46. package/components/base.components/input/InputPassword.component.tsx +236 -0
  47. package/components/base.components/input/InputRadio.component.tsx +175 -0
  48. package/components/base.components/input/InputTime.component.tsx +276 -0
  49. package/components/base.components/input/InputValues.component.tsx +68 -0
  50. package/components/base.components/input/Radio.component.tsx +102 -0
  51. package/components/base.components/input/Select.component.tsx +541 -0
  52. package/components/base.components/modal/BottomSheet.component.tsx +246 -0
  53. package/components/base.components/modal/FloatingPage.component.tsx +104 -0
  54. package/components/base.components/modal/Modal.component.tsx +96 -0
  55. package/components/base.components/modal/ModalConfirm.component.tsx +218 -0
  56. package/components/base.components/modal/Toast.component.tsx +126 -0
  57. package/components/base.components/nav/Bottombar.component.tsx +116 -0
  58. package/components/base.components/nav/Footer.component.tsx +144 -0
  59. package/components/base.components/nav/Headbar.component.tsx +104 -0
  60. package/components/base.components/nav/Navbar.component.tsx +100 -0
  61. package/components/base.components/nav/Sidebar.component.tsx +301 -0
  62. package/components/base.components/nav/Tabbar.component.tsx +60 -0
  63. package/components/base.components/nav/Wizard.component.tsx +73 -0
  64. package/components/base.components/supervision/FormSupervision.component.tsx +434 -0
  65. package/components/base.components/supervision/TableSupervision.component.tsx +697 -0
  66. package/components/base.components/table/ControlBar.component.tsx +497 -0
  67. package/components/base.components/table/FilterComponent.tsx +518 -0
  68. package/components/base.components/table/Pagination.component.tsx +159 -0
  69. package/components/base.components/table/Table.component.tsx +469 -0
  70. package/components/base.components/typography/TypographyArticle.component.tsx +26 -0
  71. package/components/base.components/typography/TypographyColumn.component.tsx +20 -0
  72. package/components/base.components/typography/TypographyContent.component.tsx +20 -0
  73. package/components/base.components/typography/TypographyTips.component.tsx +20 -0
  74. package/components/base.components/wrap/Draggable.component.tsx +303 -0
  75. package/components/base.components/wrap/IDBProvider.tsx +12 -0
  76. package/components/base.components/wrap/Image.component.tsx +10 -0
  77. package/components/base.components/wrap/OutsideClick.component.tsx +48 -0
  78. package/components/base.components/wrap/ScrollContainer.component.tsx +104 -0
  79. package/components/base.components/wrap/ShortcutProvider.tsx +57 -0
  80. package/components/base.components/wrap/Swipe.component.tsx +93 -0
  81. package/components/construct.components/example.tsx +1 -0
  82. package/components/construct.components/index.ts +5 -0
  83. package/components/index.ts +3 -0
  84. package/components/structure.components/example.tsx +1 -0
  85. package/components/structure.components/index.ts +5 -0
  86. package/contexts/AppProvider.tsx +12 -0
  87. package/contexts/Auth.context.tsx +64 -0
  88. package/contexts/Toggle.context.tsx +44 -0
  89. package/contexts/index.ts +7 -0
  90. package/eslint.config.mjs +34 -0
  91. package/langs/index.ts +1 -0
  92. package/langs/validation.langs.ts +17 -0
  93. package/next.config.ts +17 -0
  94. package/package.json +43 -0
  95. package/postcss.config.mjs +12 -0
  96. package/public/204.svg +19 -0
  97. package/public/500.svg +39 -0
  98. package/public/images/avatar.jpg +0 -0
  99. package/public/images/example.png +0 -0
  100. package/schema/idb/app.schema.ts +9 -0
  101. package/schema/index.ts +5 -0
  102. package/styles/globals.css +231 -0
  103. package/styles/tailwind.safelist +69 -0
  104. package/tailwind.config.ts +10 -0
  105. package/tsconfig.json +35 -0
  106. package/utils/commands/barrels.ts +28 -0
  107. package/utils/commands/blueprint.ts +421 -0
  108. package/utils/commands/light.ts +21 -0
  109. package/utils/commands/logger.ts +42 -0
  110. package/utils/commands/stubs/table-blueprint.stub +13 -0
  111. package/utils/commands/use-pdf.ts +29 -0
  112. package/utils/index.ts +3 -0
@@ -0,0 +1,301 @@
1
+ "use client"
2
+
3
+ import { Fragment, ReactNode, useEffect, useState } from "react";
4
+ import { usePathname } from "next/navigation";
5
+ import Link from "next/link";
6
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
7
+ import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons";
8
+ import { cn, pcn } from "@utils";
9
+
10
+
11
+
12
+ type CT = "backdrop" | "base" | "head-item" | "item" | "child-item";
13
+
14
+ export interface SidebarItemProps {
15
+ label : string;
16
+ key ?: number;
17
+ leftContent ?: any;
18
+ rightContent ?: any;
19
+ path ?: string;
20
+ items ?: SidebarItemProps[];
21
+ className ?: string;
22
+ };
23
+
24
+ export interface SidebarHeadItemProps {
25
+ label : string;
26
+ collapse ?: boolean;
27
+ items ?: SidebarItemProps[];
28
+ className ?: string;
29
+ };
30
+
31
+ export interface sidebarProps {
32
+ head ?: any;
33
+ items ?: SidebarHeadItemProps[];
34
+ basePath ?: string;
35
+ show ?: boolean;
36
+ toggle ?: boolean;
37
+ onToggleChange ?: () => void;
38
+ children ?: any;
39
+ hasAccess ?: number[];
40
+ onChange ?: () => void;
41
+
42
+ /** Use custom class with: "backdrop::", "head-item::", "item::", "child-item::". */
43
+ className?: string;
44
+ };
45
+
46
+ interface sidebarWrapperProps {
47
+ path ?: string;
48
+ onClick ?: () => void;
49
+ children ?: any;
50
+ }
51
+
52
+
53
+
54
+
55
+ function SidebarWrapper({
56
+ path,
57
+ children,
58
+ onClick,
59
+ } : sidebarWrapperProps) {
60
+ if (path) {
61
+ return <Link href={path}>{children}</Link>;
62
+ } else {
63
+ return <div onClick={() => onClick?.()}>{children}</div>;
64
+ }
65
+ }
66
+
67
+
68
+
69
+ export function SidebarComponent({
70
+ head,
71
+ items,
72
+ basePath,
73
+ toggle,
74
+ onToggleChange,
75
+ // onChange,
76
+ // hasAccess,
77
+ className = "",
78
+ } : sidebarProps) {
79
+ const pathName = usePathname();
80
+
81
+ const [shows, setShows] = useState<string[]>([]);
82
+
83
+ const setShow = (key: string) => {
84
+ setShows((prevShows) =>
85
+ prevShows?.find((pk) => pk === key)
86
+ ? prevShows.filter((pk) => pk !== key)
87
+ : [...prevShows, key],
88
+ );
89
+ };
90
+
91
+ const checkShow = (key: string): boolean => {
92
+ if (shows?.includes(key)) {
93
+ return true;
94
+ }
95
+
96
+ return false;
97
+ };
98
+
99
+ const cekActive = (path: string) => {
100
+ const activePath =
101
+ pathName?.split("?")[0]?.replace(`${basePath || ""}`, "") || "/";
102
+
103
+ const currentPath = `${path ? `${path}` : ""}`;
104
+
105
+ const isPrefix = (longer: string, shorter: string): boolean => {
106
+ return (
107
+ longer.startsWith(shorter) &&
108
+ (longer === shorter || longer[shorter.length] === "/")
109
+ );
110
+ };
111
+
112
+ return (
113
+ isPrefix(activePath, currentPath) || isPrefix(currentPath, activePath)
114
+ );
115
+ };
116
+
117
+ useEffect(() => {
118
+ items?.map((head, head_key) => {
119
+ head?.items?.map((menu, key) => {
120
+ if (menu?.path && cekActive(menu?.path || "")) {
121
+ setShow(`${head_key}`);
122
+ }
123
+ menu?.items?.map((child) => {
124
+ if (child?.path && cekActive(child?.path || "")) {
125
+ setShow(`${head_key}`);
126
+ setShow(`${head_key}.${key}`);
127
+ }
128
+ });
129
+ });
130
+ });
131
+ }, []);
132
+
133
+ const styles = {
134
+ backdrop: cn(
135
+ "absolute top-0 left-0 w-full h-full bg-background bg-opacity-50 blur-md z-20 md:hidden",
136
+ toggle ? "scale-100 md:scale-0" : "scale-0",
137
+ pcn<CT>(className, "backdrop"),
138
+ ),
139
+ base: cn(
140
+ "flex flex-col fixed w-full sm:w-64 h-screen px-2 py-4 overflow-y-auto bg-white z-20 rounded-r-[8px] border-r scroll-sm",
141
+ toggle ? "scale-x-100 md:scale-x-0" : "scale-x-0 md:scale-x-100",
142
+ pcn<CT>(className, "base"),
143
+ ),
144
+ headItem: "flex justify-between items-center text-light-foreground font-semibold py-2 text-xs uppercase",
145
+ item: "flex items-center justify-between px-2 py-2 gap-2 transition-colors duration-300 transform hover:text-primary cursor-pointer transition-none",
146
+ childItem:"flex items-center justify-between px-2 py-2 gap-2 transition-colors duration-300 transform hover:text-primary cursor-pointer transition-none border-l-2",
147
+ };
148
+
149
+ return (
150
+ <>
151
+ <div className={styles.backdrop} onClick={() => onToggleChange?.()}></div>
152
+ <aside className={styles.base}>
153
+ {head}
154
+ <nav className="flex flex-col flex-1 mt-3">
155
+ {items?.map((menu_head, menu_head_key) => {
156
+ return (
157
+ <Fragment key={menu_head_key}>
158
+ <div className="px-2 pt-2">
159
+ <div
160
+ className={cn(
161
+ styles.headItem,
162
+ menu_head?.collapse && "cursor-pointer",
163
+ pcn<CT>(className, "head-item"),
164
+ menu_head?.className,
165
+ )}
166
+ onClick={() => setShow(String(menu_head_key))}
167
+ >
168
+ {menu_head?.label}
169
+ {menu_head.collapse && (
170
+ <FontAwesomeIcon
171
+ icon={faChevronDown}
172
+ className={cn(
173
+ "text-xs",
174
+ checkShow(String(menu_head_key)) && "rotate-180",
175
+ )}
176
+ />
177
+ )}
178
+ </div>
179
+
180
+ {(!menu_head?.collapse || checkShow(String(menu_head_key))) &&
181
+ menu_head?.items?.map((menu, menu_key) => {
182
+ return (
183
+ <Fragment key={`${menu_head_key}.${menu_key}`}>
184
+ <SidebarWrapper
185
+ path={
186
+ menu?.path ? `${basePath || ""}${menu?.path}` : ""
187
+ }
188
+ onClick={() =>
189
+ setShow(`${menu_head_key}.${menu_key}`)
190
+ }
191
+ >
192
+ <div
193
+ className={cn(
194
+ styles.item,
195
+ menu?.path &&
196
+ cekActive(menu?.path || "") &&
197
+ "text-primary border-l-2 border-primary pl-4",
198
+ pcn<CT>(className, "item"),
199
+ menu?.className,
200
+ )}
201
+ >
202
+ <div className="flex gap-2 items-center">
203
+ {menu?.leftContent}
204
+ <span className="text-sm font-medium">
205
+ {menu?.label}
206
+ </span>
207
+ </div>
208
+ <div className="flex gap-2 items-center">
209
+ {menu?.rightContent}
210
+
211
+ {menu?.items?.length && (
212
+ <FontAwesomeIcon
213
+ icon={faChevronUp}
214
+ className={`text-sm ${
215
+ checkShow(
216
+ `${menu_head_key}.${menu_key}`,
217
+ ) || "rotate-180"
218
+ }`}
219
+ />
220
+ )}
221
+ </div>
222
+ </div>
223
+ </SidebarWrapper>
224
+ <div className="px-4">
225
+ <div className="flex flex-col">
226
+ {menu?.items?.length &&
227
+ checkShow(`${menu_head_key}.${menu_key}`) &&
228
+ menu?.items?.map((child, menu_child_key) => {
229
+ return (
230
+ <Fragment
231
+ key={`${menu_head_key}.${menu_key}.${menu_child_key}`}
232
+ >
233
+ <SidebarWrapper
234
+ path={
235
+ child?.path
236
+ ? `${basePath || ""}${child?.path}`
237
+ : ""
238
+ }
239
+ onClick={() =>
240
+ setShow(
241
+ `${menu_head_key}.${menu_key}.${menu_child_key}`,
242
+ )
243
+ }
244
+ >
245
+ <div
246
+ className={cn(
247
+ styles.childItem,
248
+ child?.path &&
249
+ cekActive(child?.path || "") &&
250
+ "text-primary border-primary pl-4",
251
+ pcn<CT>(className, "child-item"),
252
+ child?.className,
253
+ )}
254
+ >
255
+ <div className="flex gap-2 items-center">
256
+ {child?.leftContent}
257
+ <span className="text-sm font-medium">
258
+ {child?.label}
259
+ </span>
260
+ </div>
261
+ <div className="flex gap-2 items-center">
262
+ {child?.rightContent}
263
+
264
+ {child?.items?.length && (
265
+ <FontAwesomeIcon
266
+ icon={faChevronUp}
267
+ className={`block text-sm ${
268
+ checkShow(
269
+ `${menu_head_key}.${menu_key}`,
270
+ ) || "rotate-180"
271
+ }`}
272
+ />
273
+ )}
274
+ </div>
275
+ </div>
276
+ </SidebarWrapper>
277
+ </Fragment>
278
+ );
279
+ })}
280
+ </div>
281
+ </div>
282
+ </Fragment>
283
+ );
284
+ })}
285
+ </div>
286
+ </Fragment>
287
+ );
288
+ })}
289
+ </nav>
290
+ </aside>
291
+ </>
292
+ );
293
+ }
294
+
295
+ export function SidebarContentComponent({ children }: { children: ReactNode }) {
296
+ return (
297
+ <main className="w-full md:ml-[256px] md:w-[calc(100vw-256px)] min-h-screen overflow-x-hidden">
298
+ {children}
299
+ </main>
300
+ );
301
+ }
@@ -0,0 +1,60 @@
1
+ import { cn, pcn } from "@utils";
2
+
3
+
4
+
5
+ type CT = "item" | "active" | "base";
6
+
7
+ export interface TabbarItemProps {
8
+ label : string;
9
+ value : string | number;
10
+ };
11
+
12
+ export interface TabbarProps {
13
+ items : string[] | TabbarItemProps[];
14
+ onChange ?: (item: string | number) => void;
15
+ active ?: string | number;
16
+ className ?: string;
17
+ };
18
+
19
+
20
+
21
+ export function TabbarComponent({
22
+ items,
23
+ onChange,
24
+ active,
25
+
26
+ /** Use custom class with: "item::", "active::". */
27
+ className = "",
28
+ }: TabbarProps) {
29
+ return (
30
+ <>
31
+ <div
32
+ className={cn(
33
+ "grid grid-flow-col grid-cols-auto rounded-[6px] border",
34
+ pcn<CT>(className, "base"),
35
+ )}
36
+ >
37
+ {items?.map((item, i) => {
38
+ return (
39
+ <div
40
+ key={i}
41
+ className={cn(
42
+ "text-center px-2 py-2 rounded-64px",
43
+ active == (typeof item != "string" ? item?.value : item)
44
+ ? "bg-white/60 text-primary font-semibold"
45
+ : "hover:bg-white/60 cursor-pointer",
46
+ pcn<CT>(className, "item"),
47
+ pcn<CT>(className, "active"),
48
+ )}
49
+ onClick={() =>
50
+ onChange?.(typeof item != "string" ? item?.value : item)
51
+ }
52
+ >
53
+ {typeof item != "string" ? item?.label : item}
54
+ </div>
55
+ );
56
+ })}
57
+ </div>
58
+ </>
59
+ );
60
+ }
@@ -0,0 +1,73 @@
1
+ import { ReactNode } from "react";
2
+
3
+
4
+
5
+ export interface WizardProps {
6
+ items : {label: string, circle_content: ReactNode}[];
7
+ active : number;
8
+ };
9
+
10
+
11
+
12
+ export function WizardComponent({
13
+ items,
14
+ active
15
+ }: WizardProps) {
16
+ return (
17
+ <div>
18
+ <div className="w-full py-4">
19
+ <div className="flex">
20
+ {items.map((item, key) => {
21
+ return (
22
+ <div
23
+ key={key}
24
+ style={{ width: `calc(100% * 1 / ${items.length})` }}
25
+ >
26
+ <div className="relative mb-2">
27
+ {key > 0 && (
28
+ <div
29
+ className="absolute flex align-center items-center align-middle content-center"
30
+ style={{
31
+ width: "calc(100% - 2.5rem - 1rem)",
32
+ top: "50%",
33
+ transform: "translate(-50%, -50%)",
34
+ }}
35
+ >
36
+ <div className="w-full bg-light-primary rounded items-center align-middle align-center flex-1">
37
+ <div
38
+ className={`${
39
+ key == active + 1
40
+ ? "bg-gradient-to-r from-primary to-light-primary to-70%"
41
+ : key <= active + 1
42
+ ? "bg-primary"
43
+ : ""
44
+ } py-1 rounded w-full`}
45
+ ></div>
46
+ </div>
47
+ </div>
48
+ )}
49
+
50
+ <div
51
+ className={`${
52
+ key <= active
53
+ ? "bg-primary text-background"
54
+ : "bg-light-primary text-white"
55
+ } w-10 h-10 mx-auto rounded-full text-lg font-bold flex items-center`}
56
+ >
57
+ <span className="text-center w-full">
58
+ {item.circle_content}
59
+ </span>
60
+ </div>
61
+ </div>
62
+
63
+ <div className="text-xs text-center md:text-sm 2xl:text-base font-medium">
64
+ {item.label}
65
+ </div>
66
+ </div>
67
+ );
68
+ })}
69
+ </div>
70
+ </div>
71
+ </div>
72
+ );
73
+ }