@imksh/create 1.0.30 → 1.0.31
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.
- package/package.json +1 -1
- package/src/utils/askTemplate.js +2 -1
- package/templates/layers/electron/base/electron/main.js +262 -0
- package/templates/layers/electron/base/electron/preload.js +37 -0
- package/templates/layers/electron/base/electron-builder.json5 +68 -0
- package/templates/layers/electron/javascript/_gitignore +38 -0
- package/templates/layers/electron/javascript/package.json +49 -0
- package/templates/layers/electron/javascript/src/components/layout/SidebarLayout.jsx +43 -0
- package/templates/layers/electron/javascript/src/components/ui/TitleBar.jsx +130 -0
- package/templates/layers/electron/javascript/src/config/appConfig.js +51 -0
- package/templates/layers/electron/javascript/src/hooks/useElectron.js +68 -0
- package/templates/layers/electron/javascript/src/main.jsx +17 -0
- package/templates/layers/electron/javascript/src/store/useUiStore.js +59 -0
- package/templates/layers/electron/javascript/vite.config.js +29 -0
- package/templates/layers/electron/typescript/_gitignore +38 -0
- package/templates/layers/electron/typescript/electron/main.ts +213 -0
- package/templates/layers/electron/typescript/electron/preload.ts +52 -0
- package/templates/layers/electron/typescript/package.json +52 -0
- package/templates/layers/electron/typescript/src/components/layout/SidebarLayout.tsx +42 -0
- package/templates/layers/electron/typescript/src/components/ui/TitleBar.tsx +120 -0
- package/templates/layers/electron/typescript/src/config/appConfig.ts +52 -0
- package/templates/layers/electron/typescript/src/hooks/useElectron.ts +68 -0
- package/templates/layers/electron/typescript/src/main.tsx +17 -0
- package/templates/layers/electron/typescript/src/store/useUiStore.ts +97 -0
- package/templates/layers/electron/typescript/src/vite-env.d.ts +13 -0
- package/templates/layers/electron/typescript/tsconfig.json +25 -0
- package/templates/layers/electron/typescript/tsconfig.node.json +11 -0
- package/templates/layers/electron/typescript/vite.config.ts +29 -0
- package/templates/layers/next/javascript/src/components/common/Breadcrumbs.jsx +44 -6
- package/templates/layers/next/javascript/src/components/common/Loading.jsx +1 -0
- package/templates/layers/next/javascript/src/components/common/LottieIcon.jsx +1 -0
- package/templates/layers/next/javascript/src/components/common/Pagination.jsx +170 -0
- package/templates/layers/next/javascript/src/components/common/SidebarModal.jsx +180 -0
- package/templates/layers/next/javascript/src/components/common/StatsCard.jsx +168 -0
- package/templates/layers/next/javascript/src/components/common/Table.jsx +191 -41
- package/templates/layers/next/javascript/src/components/common/Tabs.jsx +187 -0
- package/templates/layers/next/javascript/src/components/common/ToggleSwitch.jsx +4 -3
- package/templates/layers/next/javascript/src/components/layout/ProtectedRoute.jsx +2 -1
- package/templates/layers/next/javascript/src/components/layout/PublicLayout.jsx +3 -6
- package/templates/layers/next/javascript/src/components/layout/SidebarLayout.jsx +4 -17
- package/templates/layers/next/javascript/src/components/modals/ConfirmModal.jsx +4 -12
- package/templates/layers/next/javascript/src/components/modals/Modal.jsx +50 -19
- package/templates/layers/next/javascript/src/components/ui/Input.jsx +50 -0
- package/templates/layers/next/{typescript/src/components/ui/sidebar.tsx → javascript/src/components/ui/Sidebar.jsx} +65 -53
- package/templates/layers/next/javascript/src/components/ui/SidebarHeader.jsx +11 -6
- package/templates/layers/next/javascript/src/config/appConfig.js +4 -4
- package/templates/layers/next/javascript/src/hooks/useWindowSize.js +2 -3
- package/templates/layers/next/javascript/src/store/useUiStore.js +9 -4
- package/templates/layers/next/typescript/src/components/common/Breadcrumbs.tsx +57 -8
- package/templates/layers/next/typescript/src/components/common/Loading.tsx +1 -0
- package/templates/layers/next/typescript/src/components/common/Pagination.tsx +186 -0
- package/templates/layers/next/typescript/src/components/common/SidebarModal.tsx +200 -0
- package/templates/layers/next/typescript/src/components/common/StatsCard.tsx +232 -0
- package/templates/layers/next/typescript/src/components/common/Table.tsx +201 -35
- package/templates/layers/next/typescript/src/components/common/Tabs.tsx +190 -0
- package/templates/layers/next/typescript/src/components/common/ToggleSwitch.tsx +12 -8
- package/templates/layers/next/typescript/src/components/layout/PublicLayout.tsx +8 -6
- package/templates/layers/next/typescript/src/components/layout/SidebarLayout.tsx +8 -17
- package/templates/layers/next/typescript/src/components/modals/ConfirmModal.tsx +1 -0
- package/templates/layers/next/typescript/src/components/modals/Modal.tsx +67 -21
- package/templates/layers/next/typescript/src/components/ui/Button.tsx +1 -0
- package/templates/layers/next/typescript/src/components/ui/Input.tsx +40 -60
- package/templates/layers/next/{javascript/src/components/ui/sidebar.jsx → typescript/src/components/ui/Sidebar.tsx} +77 -44
- package/templates/layers/next/typescript/src/components/ui/SidebarHeader.tsx +16 -10
- package/templates/layers/next/typescript/src/config/appConfig.ts +4 -4
- package/templates/layers/next/typescript/src/hooks/useWindowSize.ts +2 -3
- package/templates/layers/next/typescript/src/store/useUiStore.ts +14 -4
- package/templates/layers/node/typescript/package-lock.json +2 -2
- package/templates/layers/node/typescript/package.json +1 -1
- package/templates/layers/prisma/typescript/prisma/schema.prisma +35 -10
- package/templates/layers/react/javascript/src/components/common/Breadcrumbs.jsx +62 -58
- package/templates/layers/react/javascript/src/components/common/Pagination.jsx +164 -76
- package/templates/layers/react/javascript/src/components/common/SidebarModal.jsx +179 -0
- package/templates/layers/react/javascript/src/components/common/StatsCard.jsx +167 -0
- package/templates/layers/react/javascript/src/components/common/Table.jsx +248 -132
- package/templates/layers/react/javascript/src/components/common/Tabs.jsx +164 -112
- package/templates/layers/react/javascript/src/components/common/ToggleSwitch.jsx +3 -3
- package/templates/layers/react/javascript/src/components/layout/SidebarLayout.jsx +4 -12
- package/templates/layers/react/javascript/src/components/modals/Modal.jsx +53 -18
- package/templates/layers/react/javascript/src/components/ui/Input.jsx +49 -0
- package/templates/layers/react/javascript/src/components/ui/Sidebar.jsx +215 -0
- package/templates/layers/react/javascript/src/components/ui/SidebarHeader.jsx +25 -30
- package/templates/layers/react/javascript/src/config/appConfig.js +3 -3
- package/templates/layers/react/javascript/src/hooks/useWindowSize.js +2 -2
- package/templates/layers/react/typescript/src/components/common/Breadcrumbs.tsx +32 -6
- package/templates/layers/react/typescript/src/components/common/Pagination.tsx +151 -57
- package/templates/layers/react/typescript/src/components/common/SidebarModal.tsx +199 -0
- package/templates/layers/react/typescript/src/components/common/StatsCard.tsx +231 -0
- package/templates/layers/react/typescript/src/components/common/Table.tsx +197 -34
- package/templates/layers/react/typescript/src/components/common/Tabs.tsx +56 -12
- package/templates/layers/react/typescript/src/components/common/ToggleSwitch.tsx +15 -3
- package/templates/layers/react/typescript/src/components/layout/SidebarLayout.tsx +3 -6
- package/templates/layers/react/typescript/src/components/modals/Modal.tsx +151 -0
- package/templates/layers/react/typescript/src/components/ui/Input.tsx +58 -0
- package/templates/layers/react/typescript/src/components/ui/Sidebar.tsx +116 -53
- package/templates/layers/react/typescript/src/components/ui/SidebarHeader.tsx +5 -4
- package/templates/layers/react/typescript/src/config/appConfig.ts +3 -3
- package/templates/layers/react/typescript/src/hooks/useWindowSize.ts +2 -3
- package/templates/layers/react-native/base/src/config/appConfig.ts +1 -1
- package/templates/presets/electron/preset.json +9 -0
- package/templates/presets/electron-ts/preset.json +9 -0
- package/templates/layers/react/javascript/src/components/ui/sidebar.jsx +0 -169
|
@@ -1,25 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { useEffect, ReactNode } from "react";
|
|
2
3
|
import { createPortal } from "react-dom";
|
|
3
4
|
import { IoClose } from "react-icons/io5";
|
|
5
|
+
import { useUiStore } from "../../store/useUiStore";
|
|
4
6
|
|
|
5
7
|
export interface ModalProps {
|
|
6
8
|
open: boolean;
|
|
7
9
|
onClose: () => void;
|
|
8
|
-
title
|
|
10
|
+
title?: string;
|
|
9
11
|
children: ReactNode;
|
|
10
|
-
size?:
|
|
12
|
+
size?:
|
|
13
|
+
| "sm"
|
|
14
|
+
| "md"
|
|
15
|
+
| "lg"
|
|
16
|
+
| "xl"
|
|
17
|
+
| "2xl"
|
|
18
|
+
| "3xl"
|
|
19
|
+
| "4xl"
|
|
20
|
+
| "5xl"
|
|
21
|
+
| "6xl"
|
|
22
|
+
| "7xl"
|
|
23
|
+
| "full";
|
|
11
24
|
closeOnBackdrop?: boolean;
|
|
12
25
|
closeOnEsc?: boolean;
|
|
13
26
|
showCloseButton?: boolean;
|
|
27
|
+
hideHeader?: boolean;
|
|
28
|
+
className?: string;
|
|
29
|
+
bodyClass?: string;
|
|
14
30
|
footer?: ReactNode;
|
|
31
|
+
height?: string | number;
|
|
15
32
|
}
|
|
16
33
|
|
|
17
34
|
const sizes = {
|
|
18
35
|
sm: "max-w-sm",
|
|
19
36
|
md: "max-w-md",
|
|
20
37
|
lg: "max-w-lg",
|
|
21
|
-
xl: "max-w-
|
|
22
|
-
"2xl": "max-w-
|
|
38
|
+
xl: "max-w-xl",
|
|
39
|
+
"2xl": "max-w-2xl",
|
|
40
|
+
"3xl": "max-w-3xl",
|
|
41
|
+
"4xl": "max-w-4xl",
|
|
42
|
+
"5xl": "max-w-5xl",
|
|
43
|
+
"6xl": "max-w-6xl",
|
|
44
|
+
"7xl": "max-w-7xl",
|
|
23
45
|
full: "max-w-7xl",
|
|
24
46
|
};
|
|
25
47
|
|
|
@@ -28,12 +50,27 @@ const Modal = ({
|
|
|
28
50
|
onClose,
|
|
29
51
|
title,
|
|
30
52
|
children,
|
|
31
|
-
size = "
|
|
53
|
+
size = "4xl",
|
|
32
54
|
closeOnBackdrop = true,
|
|
33
55
|
closeOnEsc = true,
|
|
34
56
|
showCloseButton = true,
|
|
57
|
+
hideHeader = false,
|
|
58
|
+
className = "",
|
|
59
|
+
bodyClass = "p-2 sm:p-4",
|
|
35
60
|
footer,
|
|
61
|
+
height,
|
|
36
62
|
}: ModalProps) => {
|
|
63
|
+
const setModalOpen = useUiStore((state) => state.setModalOpen);
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (open) {
|
|
67
|
+
setModalOpen(true);
|
|
68
|
+
return () => {
|
|
69
|
+
setModalOpen(false);
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}, [open, setModalOpen]);
|
|
73
|
+
|
|
37
74
|
useEffect(() => {
|
|
38
75
|
if (!open || !closeOnEsc) return;
|
|
39
76
|
|
|
@@ -45,8 +82,9 @@ const Modal = ({
|
|
|
45
82
|
|
|
46
83
|
document.addEventListener("keydown", handleKeyDown);
|
|
47
84
|
|
|
48
|
-
return () =>
|
|
85
|
+
return () => {
|
|
49
86
|
document.removeEventListener("keydown", handleKeyDown);
|
|
87
|
+
};
|
|
50
88
|
}, [open, closeOnEsc, onClose]);
|
|
51
89
|
|
|
52
90
|
if (!open) return null;
|
|
@@ -63,37 +101,45 @@ const Modal = ({
|
|
|
63
101
|
<div
|
|
64
102
|
className={`
|
|
65
103
|
relative
|
|
104
|
+
flex flex-col overflow-hidden max-h-[90vh]
|
|
66
105
|
w-full
|
|
67
106
|
${sizes[size]}
|
|
68
107
|
rounded-2xl
|
|
69
108
|
bg-base-100
|
|
70
|
-
shadow-
|
|
109
|
+
shadow-md
|
|
71
110
|
border border-base-300
|
|
72
111
|
animate-in zoom-in-95 fade-in duration-200
|
|
112
|
+
${className}
|
|
73
113
|
`}
|
|
114
|
+
style={height ? { height } : undefined}
|
|
74
115
|
>
|
|
75
116
|
{/* Header */}
|
|
76
|
-
|
|
77
|
-
<
|
|
117
|
+
{!hideHeader && (title || showCloseButton) && (
|
|
118
|
+
<div className="flex items-center justify-between border-b border-base-300 px-6 py-4 shrink-0">
|
|
119
|
+
{title && <h2 className="text-lg font-bold">{title}</h2>}
|
|
78
120
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
121
|
+
{showCloseButton && (
|
|
122
|
+
<button
|
|
123
|
+
className="btn btn-circle btn-sm btn-error btn-soft text-error/70 hover:text-error hover:bg-error/20 hover:shadow-sm hover:rotate-180 transition-transform duration-200"
|
|
124
|
+
onClick={onClose}
|
|
125
|
+
>
|
|
126
|
+
<IoClose size={20} className="" />
|
|
127
|
+
</button>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
)}
|
|
88
131
|
|
|
89
132
|
{/* Body */}
|
|
90
|
-
<div
|
|
133
|
+
<div
|
|
134
|
+
className={`flex-1 min-h-0 overflow-y-auto ${bodyClass}`}
|
|
135
|
+
data-lenis-prevent="true"
|
|
136
|
+
>
|
|
91
137
|
{children}
|
|
92
138
|
</div>
|
|
93
139
|
|
|
94
140
|
{/* Footer */}
|
|
95
141
|
{footer && (
|
|
96
|
-
<div className="border-t border-base-300 px-6 py-4">
|
|
142
|
+
<div className="border-t border-base-300 px-6 py-4 shrink-0">
|
|
97
143
|
{footer}
|
|
98
144
|
</div>
|
|
99
145
|
)}
|
|
@@ -103,4 +149,4 @@ const Modal = ({
|
|
|
103
149
|
);
|
|
104
150
|
};
|
|
105
151
|
|
|
106
|
-
export default Modal;
|
|
152
|
+
export default Modal;
|
|
@@ -1,79 +1,59 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import React, { InputHTMLAttributes, ReactNode } from "react";
|
|
3
|
+
import { X } from "lucide-react";
|
|
2
4
|
|
|
3
|
-
export interface InputProps extends
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
export interface InputProps extends Omit<
|
|
6
|
+
InputHTMLAttributes<HTMLInputElement>,
|
|
7
|
+
"size"
|
|
8
|
+
> {
|
|
9
|
+
icon?: ReactNode;
|
|
10
|
+
wrapperClassName?: string;
|
|
11
|
+
onClear?: () => void;
|
|
12
|
+
size?: "xs" | "sm" | "md" | "lg";
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
13
16
|
(
|
|
14
17
|
{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
leftIcon,
|
|
19
|
-
rightIcon,
|
|
20
|
-
fullWidth = true,
|
|
18
|
+
icon,
|
|
19
|
+
wrapperClassName = "",
|
|
20
|
+
onClear,
|
|
21
21
|
className = "",
|
|
22
|
-
|
|
22
|
+
value,
|
|
23
|
+
size,
|
|
23
24
|
...props
|
|
24
25
|
},
|
|
25
|
-
ref
|
|
26
|
+
ref,
|
|
26
27
|
) => {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
const inputClasses = [
|
|
30
|
-
"input input-bordered",
|
|
31
|
-
error ? "input-error" : "",
|
|
32
|
-
leftIcon ? "pl-10" : "",
|
|
33
|
-
rightIcon ? "pr-10" : "",
|
|
34
|
-
fullWidth ? "w-full" : "",
|
|
35
|
-
className,
|
|
36
|
-
]
|
|
37
|
-
.filter(Boolean)
|
|
38
|
-
.join(" ");
|
|
39
|
-
|
|
28
|
+
const sizeClass = size ? `input-${size}` : "";
|
|
40
29
|
return (
|
|
41
|
-
<div className={`
|
|
42
|
-
{
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
</
|
|
30
|
+
<div className={`relative ${wrapperClassName}`}>
|
|
31
|
+
{icon && (
|
|
32
|
+
<div className="absolute left-3 top-1/2 -translate-y-1/2 text-base-content/50 pointer-events-none flex items-center justify-center z-10">
|
|
33
|
+
{icon}
|
|
34
|
+
</div>
|
|
46
35
|
)}
|
|
47
|
-
<
|
|
48
|
-
{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
</div>
|
|
65
|
-
{(error || helperText) && (
|
|
66
|
-
<label className="label">
|
|
67
|
-
<span className={`label-text-alt ${error ? "text-error" : "text-base-content/70"}`}>
|
|
68
|
-
{error || helperText}
|
|
69
|
-
</span>
|
|
70
|
-
</label>
|
|
36
|
+
<input
|
|
37
|
+
ref={ref}
|
|
38
|
+
value={value}
|
|
39
|
+
className={`input input-bordered w-full ${sizeClass} ${icon ? "pl-10" : ""} ${
|
|
40
|
+
onClear && value ? "pr-10" : ""
|
|
41
|
+
} ${className}`}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
{onClear && value && (
|
|
45
|
+
<button
|
|
46
|
+
type="button"
|
|
47
|
+
onClick={onClear}
|
|
48
|
+
className="absolute right-3 top-1/2 -translate-y-1/2 text-base-content/40 hover:text-base-content transition-colors flex items-center justify-center"
|
|
49
|
+
aria-label="Clear input"
|
|
50
|
+
>
|
|
51
|
+
<X size={16} />
|
|
52
|
+
</button>
|
|
71
53
|
)}
|
|
72
54
|
</div>
|
|
73
55
|
);
|
|
74
|
-
}
|
|
56
|
+
},
|
|
75
57
|
);
|
|
76
58
|
|
|
77
59
|
Input.displayName = "Input";
|
|
78
|
-
|
|
79
|
-
export default Input;
|
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import Link from "next/link";
|
|
3
|
-
import { usePathname } from "next/navigation";
|
|
3
|
+
import { usePathname, useRouter } from "next/navigation";
|
|
4
4
|
import { IoClose } from "react-icons/io5";
|
|
5
5
|
import { useUiStore } from "../../store/useUiStore";
|
|
6
6
|
import { navigation } from "../../config/navigation";
|
|
7
|
-
import {
|
|
7
|
+
import { Moon, Sun } from "lucide-react";
|
|
8
8
|
import appConfig from "../../config/appConfig";
|
|
9
9
|
import { motion, AnimatePresence } from "framer-motion";
|
|
10
10
|
import useWindowSize from "../../hooks/useWindowSize";
|
|
11
|
+
import { useAuthStore } from "../../store/useAuthStore";
|
|
12
|
+
import { GoSidebarCollapse, GoSidebarExpand } from "react-icons/go";
|
|
13
|
+
import { CiLogout } from "react-icons/ci";
|
|
11
14
|
|
|
12
15
|
const Sidebar = () => {
|
|
13
|
-
const { open, close, collapsed, toggleCollapsed } =
|
|
14
|
-
|
|
16
|
+
const { open, close, collapsed, toggleCollapsed, theme, toggleTheme } =
|
|
17
|
+
useUiStore();
|
|
18
|
+
const { logout, user } = useAuthStore();
|
|
15
19
|
const { width } = useWindowSize();
|
|
16
20
|
const pathname = usePathname();
|
|
21
|
+
const router = useRouter();
|
|
17
22
|
const isMobile = width < 1024; // lg breakpoint
|
|
18
23
|
|
|
19
24
|
const showSidebar = !isMobile || open;
|
|
20
25
|
|
|
26
|
+
const handleLogout = async () => {
|
|
27
|
+
await logout();
|
|
28
|
+
close();
|
|
29
|
+
router.replace("/login");
|
|
30
|
+
};
|
|
31
|
+
|
|
21
32
|
return (
|
|
22
33
|
<>
|
|
23
34
|
{/* Overlay with AnimatePresence */}
|
|
@@ -47,7 +58,7 @@ const Sidebar = () => {
|
|
|
47
58
|
h-screen bg-base-200 border-r border-base-300
|
|
48
59
|
flex flex-col overflow-hidden
|
|
49
60
|
transition-[width] duration-300
|
|
50
|
-
${collapsed ? "w-
|
|
61
|
+
${collapsed ? "w-18" : "w-[75%] md:w-[16%] md:max-w-60"}
|
|
51
62
|
`}
|
|
52
63
|
>
|
|
53
64
|
{/* Header */}
|
|
@@ -82,32 +93,30 @@ const Sidebar = () => {
|
|
|
82
93
|
</div>
|
|
83
94
|
|
|
84
95
|
{/* Navigation */}
|
|
85
|
-
<nav
|
|
96
|
+
<nav
|
|
97
|
+
className="flex-1 overflow-y-auto p-2.5 space-y-1"
|
|
98
|
+
data-lenis-prevent
|
|
99
|
+
>
|
|
86
100
|
{navigation.map((item) => {
|
|
87
101
|
const Icon = item.icon;
|
|
88
|
-
const isActive =
|
|
102
|
+
const isActive =
|
|
103
|
+
item.path === "/"
|
|
104
|
+
? pathname === "/"
|
|
105
|
+
: pathname.startsWith(item.path);
|
|
89
106
|
|
|
90
107
|
return (
|
|
91
108
|
<Link
|
|
92
109
|
key={item.path}
|
|
93
110
|
href={item.path}
|
|
94
111
|
onClick={isMobile ? close : undefined}
|
|
95
|
-
className={`flex items-center gap-3 rounded-box px-4 py-3 transition-all h-12
|
|
96
|
-
${
|
|
97
|
-
isActive
|
|
98
|
-
? "bg-primary text-primary-content"
|
|
99
|
-
: "hover:bg-base-300"
|
|
100
|
-
} ${collapsed ? "justify-center px-0" : ""}`
|
|
101
|
-
}
|
|
102
112
|
title={collapsed ? item.title : ""}
|
|
113
|
+
className={`flex items-center gap-3 rounded-box px-4 py-3 h-12 transition-colors
|
|
114
|
+
${isActive ? "bg-primary text-primary-content" : "hover:bg-base-300"}
|
|
115
|
+
${collapsed ? "justify-center px-0" : ""}`}
|
|
103
116
|
>
|
|
104
|
-
<Icon
|
|
105
|
-
size={18}
|
|
106
|
-
className={`shrink-0 ${collapsed && "mr-auto"}`}
|
|
107
|
-
/>
|
|
108
|
-
|
|
117
|
+
<Icon size={18} className={`shrink-0 ${collapsed ? "mr-auto" : ""}`} />
|
|
109
118
|
{!collapsed && (
|
|
110
|
-
<span className="font-medium
|
|
119
|
+
<span className="font-medium whitespace-nowrap">
|
|
111
120
|
{item.title}
|
|
112
121
|
</span>
|
|
113
122
|
)}
|
|
@@ -116,34 +125,58 @@ const Sidebar = () => {
|
|
|
116
125
|
})}
|
|
117
126
|
</nav>
|
|
118
127
|
|
|
119
|
-
{/* Footer with
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
128
|
+
{/* Footer with Actions */}
|
|
129
|
+
<div
|
|
130
|
+
className={`p-3 border-t border-base-300/50 shrink-0 flex gap-2 ${
|
|
131
|
+
collapsed
|
|
132
|
+
? "flex-col items-center justify-center"
|
|
133
|
+
: "flex-row items-center justify-between"
|
|
134
|
+
}`}
|
|
124
135
|
>
|
|
125
|
-
{!collapsed && <span>{theme === "dark" ? "Dark" : "Light"}</span>}
|
|
126
|
-
<span className="btn btn-xs btn-ghost btn-circle ">
|
|
127
|
-
{theme === "dark" ? (
|
|
128
|
-
<Sun size={16} className="shrink-0 " />
|
|
129
|
-
) : (
|
|
130
|
-
<Moon size={16} className="shrink-0" />
|
|
131
|
-
)}
|
|
132
|
-
</span>
|
|
133
|
-
</button>
|
|
134
|
-
<div className="p-3 border-t border-base-300/50 shrink-0 flex flex-col gap-2">
|
|
135
136
|
<button
|
|
136
|
-
onClick={
|
|
137
|
-
className={`
|
|
138
|
-
title=
|
|
137
|
+
onClick={handleLogout}
|
|
138
|
+
className={`btn btn-sm btn-circle btn-soft btn-error ${collapsed ? "hidden" : "flex"}`}
|
|
139
|
+
title="Logout"
|
|
139
140
|
>
|
|
140
|
-
{
|
|
141
|
-
<ChevronRight size={18} className="shrink-0 mr-auto" />
|
|
142
|
-
) : (
|
|
143
|
-
<ChevronLeft size={18} className="shrink-0" />
|
|
144
|
-
)}
|
|
145
|
-
{!collapsed && <span className="truncate">Collapse</span>}
|
|
141
|
+
<CiLogout size={18} />
|
|
146
142
|
</button>
|
|
143
|
+
|
|
144
|
+
<div
|
|
145
|
+
className={`flex items-center gap-2 ${collapsed ? "flex-col" : ""}`}
|
|
146
|
+
>
|
|
147
|
+
<button
|
|
148
|
+
onClick={toggleTheme}
|
|
149
|
+
className="flex items-center gap-3 hover:opacity-80 transition-opacity"
|
|
150
|
+
title="Toggle Theme"
|
|
151
|
+
>
|
|
152
|
+
<div
|
|
153
|
+
className={`btn btn-sm btn-circle btn-primary btn-soft pointer-events-none ${collapsed ? "hidden" : "flex"}`}
|
|
154
|
+
>
|
|
155
|
+
{theme === "dark" ? (
|
|
156
|
+
<Sun size={16} className="shrink-0" />
|
|
157
|
+
) : (
|
|
158
|
+
<Moon size={16} className="shrink-0" />
|
|
159
|
+
)}
|
|
160
|
+
</div>
|
|
161
|
+
{!collapsed && (
|
|
162
|
+
<span className="md:hidden font-medium text-sm text-base-content/80">
|
|
163
|
+
{theme === "dark" ? "Dark Mode" : "Light Mode"}
|
|
164
|
+
</span>
|
|
165
|
+
)}
|
|
166
|
+
</button>
|
|
167
|
+
|
|
168
|
+
<button
|
|
169
|
+
onClick={toggleCollapsed}
|
|
170
|
+
className="btn btn-sm btn-circle btn-outline border-none text-base-content/70 hover:text-base-content hover:bg-base-300/60 hidden md:flex"
|
|
171
|
+
title={collapsed ? "Expand Sidebar" : "Collapse Sidebar"}
|
|
172
|
+
>
|
|
173
|
+
{collapsed ? (
|
|
174
|
+
<GoSidebarExpand size={18} />
|
|
175
|
+
) : (
|
|
176
|
+
<GoSidebarCollapse size={18} />
|
|
177
|
+
)}
|
|
178
|
+
</button>
|
|
179
|
+
</div>
|
|
147
180
|
</div>
|
|
148
181
|
</motion.aside>
|
|
149
182
|
)}
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import React from "react";
|
|
2
3
|
import { FiMenu } from "react-icons/fi";
|
|
3
4
|
import { useUiStore } from "../../store/useUiStore";
|
|
5
|
+
import { Breadcrumbs } from "../common/Breadcrumbs";
|
|
6
|
+
|
|
7
|
+
interface SidebarHeaderProps {
|
|
8
|
+
title?: string;
|
|
9
|
+
actions?: React.ReactNode;
|
|
10
|
+
}
|
|
4
11
|
|
|
5
12
|
const SidebarHeader = ({
|
|
6
13
|
title = "Dashboard",
|
|
7
14
|
actions = null,
|
|
8
|
-
}: {
|
|
9
|
-
|
|
10
|
-
actions?: React.ReactNode;
|
|
11
|
-
}) => {
|
|
12
|
-
const { toggleSidebar } = useUiStore();
|
|
15
|
+
}: SidebarHeaderProps) => {
|
|
16
|
+
const { toggleSidebar, breadcrumbs } = useUiStore();
|
|
13
17
|
|
|
14
18
|
return (
|
|
15
|
-
<header className="sticky top-0 z-30 h-16 bg-base-200 backdrop-blur border-b border-base-
|
|
19
|
+
<header className="sticky top-0 z-30 h-16 bg-base-200 backdrop-blur border-b border-base-200 flex items-center justify-between px-4 lg:px-6">
|
|
16
20
|
<div className="flex items-center gap-3">
|
|
17
21
|
<button
|
|
18
22
|
onClick={toggleSidebar}
|
|
@@ -21,12 +25,14 @@ const SidebarHeader = ({
|
|
|
21
25
|
<FiMenu size={22} />
|
|
22
26
|
</button>
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
{breadcrumbs && breadcrumbs.length > 0 ? (
|
|
29
|
+
<Breadcrumbs items={breadcrumbs} />
|
|
30
|
+
) : (
|
|
31
|
+
<h1 className="text-xl font-bold">{title}</h1>
|
|
32
|
+
)}
|
|
25
33
|
</div>
|
|
26
34
|
|
|
27
|
-
<div className="flex items-center gap-2">
|
|
28
|
-
{actions}
|
|
29
|
-
</div>
|
|
35
|
+
<div className="flex items-center gap-2">{actions}</div>
|
|
30
36
|
</header>
|
|
31
37
|
);
|
|
32
38
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
const appConfig = {
|
|
2
2
|
app: {
|
|
3
|
-
name: "
|
|
3
|
+
name: "My App",
|
|
4
4
|
logo: "./logo.png",
|
|
5
|
-
shortName: "
|
|
6
|
-
description: "A
|
|
5
|
+
shortName: "App",
|
|
6
|
+
description: "A Next.js application built with @imksh/create",
|
|
7
7
|
version: "1.0.0",
|
|
8
|
-
author: "
|
|
8
|
+
author: "",
|
|
9
9
|
},
|
|
10
10
|
|
|
11
11
|
api: {
|
|
@@ -2,11 +2,10 @@ import { useState, useEffect } from "react";
|
|
|
2
2
|
|
|
3
3
|
const useWindowSize = () => {
|
|
4
4
|
const [size, setSize] = useState({
|
|
5
|
-
width: window.innerWidth,
|
|
6
|
-
height: window.innerHeight,
|
|
5
|
+
width: typeof window !== "undefined" ? window.innerWidth : 1024,
|
|
6
|
+
height: typeof window !== "undefined" ? window.innerHeight : 768,
|
|
7
7
|
});
|
|
8
8
|
|
|
9
|
-
|
|
10
9
|
useEffect(() => {
|
|
11
10
|
const handleResize = () => {
|
|
12
11
|
setSize({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { create } from "zustand";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
|
+
import type { BreadcrumbItem } from "../components/common/Breadcrumbs";
|
|
3
4
|
|
|
4
5
|
export interface UiState {
|
|
5
6
|
open: boolean;
|
|
@@ -7,6 +8,8 @@ export interface UiState {
|
|
|
7
8
|
isMobileMenuOpened: boolean;
|
|
8
9
|
headerTitle: string;
|
|
9
10
|
headerActions: ReactNode;
|
|
11
|
+
breadcrumbs: BreadcrumbItem[] | null;
|
|
12
|
+
setBreadcrumbs: (items: BreadcrumbItem[] | null) => void;
|
|
10
13
|
setHeaderTitle: (title: string) => void;
|
|
11
14
|
setHeaderActions: (actions: ReactNode) => void;
|
|
12
15
|
toggleCollapsed: () => void;
|
|
@@ -18,6 +21,8 @@ export interface UiState {
|
|
|
18
21
|
toggleSidebar: () => void;
|
|
19
22
|
openSidebar: () => void;
|
|
20
23
|
close: () => void;
|
|
24
|
+
modalOpen: boolean;
|
|
25
|
+
setModalOpen: (open: boolean) => void;
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
export const useUiStore = create<UiState>((set, get) => ({
|
|
@@ -26,6 +31,8 @@ export const useUiStore = create<UiState>((set, get) => ({
|
|
|
26
31
|
isMobileMenuOpened: false,
|
|
27
32
|
headerTitle: "Dashboard",
|
|
28
33
|
headerActions: null,
|
|
34
|
+
breadcrumbs: null,
|
|
35
|
+
setBreadcrumbs: (items) => set({ breadcrumbs: items }),
|
|
29
36
|
setHeaderTitle: (title: string) => set({ headerTitle: title }),
|
|
30
37
|
setHeaderActions: (actions: ReactNode) => set({ headerActions: actions }),
|
|
31
38
|
toggleCollapsed: () => set((state) => ({ collapsed: !state.collapsed })),
|
|
@@ -37,7 +44,10 @@ export const useUiStore = create<UiState>((set, get) => ({
|
|
|
37
44
|
set((state) => ({ isMobileMenuOpened: !state.isMobileMenuOpened }));
|
|
38
45
|
},
|
|
39
46
|
|
|
40
|
-
theme:
|
|
47
|
+
theme:
|
|
48
|
+
typeof window !== "undefined"
|
|
49
|
+
? localStorage.getItem("theme") || "light"
|
|
50
|
+
: "light",
|
|
41
51
|
|
|
42
52
|
setTheme: (theme) => {
|
|
43
53
|
if (typeof document !== "undefined") {
|
|
@@ -46,20 +56,17 @@ export const useUiStore = create<UiState>((set, get) => ({
|
|
|
46
56
|
if (typeof window !== "undefined") {
|
|
47
57
|
localStorage.setItem("theme", theme);
|
|
48
58
|
}
|
|
49
|
-
|
|
50
59
|
set({ theme });
|
|
51
60
|
},
|
|
52
61
|
|
|
53
62
|
toggleTheme: () => {
|
|
54
63
|
const nextTheme = get().theme === "light" ? "dark" : "light";
|
|
55
|
-
|
|
56
64
|
if (typeof document !== "undefined") {
|
|
57
65
|
document.documentElement.setAttribute("data-theme", nextTheme);
|
|
58
66
|
}
|
|
59
67
|
if (typeof window !== "undefined") {
|
|
60
68
|
localStorage.setItem("theme", nextTheme);
|
|
61
69
|
}
|
|
62
|
-
|
|
63
70
|
set({ theme: nextTheme });
|
|
64
71
|
},
|
|
65
72
|
|
|
@@ -77,4 +84,7 @@ export const useUiStore = create<UiState>((set, get) => ({
|
|
|
77
84
|
set({
|
|
78
85
|
open: false,
|
|
79
86
|
}),
|
|
87
|
+
|
|
88
|
+
modalOpen: false,
|
|
89
|
+
setModalOpen: (open) => set({ modalOpen: open }),
|
|
80
90
|
}));
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
2
|
+
"name": "node-ts-template",
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
|
-
"name": "
|
|
8
|
+
"name": "node-ts-template",
|
|
9
9
|
"version": "1.0.0",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"bcrypt": "^5.1.1",
|