@imksh/create 1.0.28 → 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/javascript/package.json +2 -1
- package/templates/layers/node/javascript/src/controllers/push.controller.js +35 -14
- package/templates/layers/node/javascript/src/models/pushSubscription.model.js +5 -0
- package/templates/layers/node/javascript/src/utils/pushHelper.js +59 -12
- package/templates/layers/node/typescript/package-lock.json +3795 -0
- package/templates/layers/node/typescript/package.json +3 -2
- package/templates/layers/node/typescript/src/controllers/push.controller.ts +35 -14
- package/templates/layers/node/typescript/src/models/pushSubscription.model.ts +6 -0
- package/templates/layers/node/typescript/src/services/auth.service.ts +3 -3
- package/templates/layers/node/typescript/src/utils/pushHelper.ts +66 -12
- package/templates/layers/node-prisma/javascript/package.json +6 -3
- package/templates/layers/node-prisma/javascript/prisma/schema.prisma +20 -9
- package/templates/layers/node-prisma/javascript/prisma.config.js +12 -0
- package/templates/layers/node-prisma/javascript/src/config/{db.js → prisma.js} +9 -1
- package/templates/layers/node-prisma/javascript/src/controllers/push.controller.js +58 -19
- package/templates/layers/node-prisma/javascript/src/utils/pushHelper.js +62 -14
- package/templates/layers/node-prisma/typescript/package-lock.json +4808 -0
- package/templates/layers/node-prisma/typescript/package.json +8 -4
- package/templates/layers/node-prisma/typescript/prisma/schema.prisma +20 -9
- package/templates/layers/node-prisma/typescript/prisma.config.ts +12 -0
- package/templates/layers/node-prisma/typescript/src/config/{db.ts → prisma.ts} +9 -1
- package/templates/layers/node-prisma/typescript/src/controllers/push.controller.ts +67 -22
- package/templates/layers/node-prisma/typescript/src/index.ts +1 -1
- package/templates/layers/node-prisma/typescript/src/middleware/auth.middleware.ts +15 -5
- package/templates/layers/node-prisma/typescript/src/services/auth.service.ts +134 -128
- package/templates/layers/node-prisma/typescript/src/utils/pushHelper.ts +69 -14
- 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/components/ui/BiometricLock.tsx +2 -1
- package/templates/layers/react-native/base/src/config/appConfig.ts +44 -0
- 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,13 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { useEffect } 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
|
const sizes = {
|
|
6
8
|
sm: "max-w-sm",
|
|
7
9
|
md: "max-w-md",
|
|
8
10
|
lg: "max-w-lg",
|
|
9
|
-
xl: "max-w-
|
|
10
|
-
"2xl": "max-w-
|
|
11
|
+
xl: "max-w-xl",
|
|
12
|
+
"2xl": "max-w-2xl",
|
|
13
|
+
"3xl": "max-w-3xl",
|
|
14
|
+
"4xl": "max-w-4xl",
|
|
15
|
+
"5xl": "max-w-5xl",
|
|
16
|
+
"6xl": "max-w-6xl",
|
|
17
|
+
"7xl": "max-w-7xl",
|
|
11
18
|
full: "max-w-7xl",
|
|
12
19
|
};
|
|
13
20
|
|
|
@@ -16,12 +23,27 @@ const Modal = ({
|
|
|
16
23
|
onClose,
|
|
17
24
|
title,
|
|
18
25
|
children,
|
|
19
|
-
size = "
|
|
26
|
+
size = "4xl",
|
|
20
27
|
closeOnBackdrop = true,
|
|
21
28
|
closeOnEsc = true,
|
|
22
29
|
showCloseButton = true,
|
|
30
|
+
hideHeader = false,
|
|
31
|
+
className = "",
|
|
32
|
+
bodyClass = "p-2 sm:p-4",
|
|
23
33
|
footer,
|
|
34
|
+
height,
|
|
24
35
|
}) => {
|
|
36
|
+
const setModalOpen = useUiStore((state) => state.setModalOpen);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (open) {
|
|
40
|
+
setModalOpen(true);
|
|
41
|
+
return () => {
|
|
42
|
+
setModalOpen(false);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}, [open, setModalOpen]);
|
|
46
|
+
|
|
25
47
|
useEffect(() => {
|
|
26
48
|
if (!open || !closeOnEsc) return;
|
|
27
49
|
|
|
@@ -33,8 +55,9 @@ const Modal = ({
|
|
|
33
55
|
|
|
34
56
|
document.addEventListener("keydown", handleKeyDown);
|
|
35
57
|
|
|
36
|
-
return () =>
|
|
58
|
+
return () => {
|
|
37
59
|
document.removeEventListener("keydown", handleKeyDown);
|
|
60
|
+
};
|
|
38
61
|
}, [open, closeOnEsc, onClose]);
|
|
39
62
|
|
|
40
63
|
if (!open) return null;
|
|
@@ -51,37 +74,45 @@ const Modal = ({
|
|
|
51
74
|
<div
|
|
52
75
|
className={`
|
|
53
76
|
relative
|
|
77
|
+
flex flex-col overflow-hidden max-h-[90vh]
|
|
54
78
|
w-full
|
|
55
79
|
${sizes[size]}
|
|
56
80
|
rounded-2xl
|
|
57
81
|
bg-base-100
|
|
58
|
-
shadow-
|
|
82
|
+
shadow-md
|
|
59
83
|
border border-base-300
|
|
60
84
|
animate-in zoom-in-95 fade-in duration-200
|
|
85
|
+
${className}
|
|
61
86
|
`}
|
|
87
|
+
style={height ? { height } : undefined}
|
|
62
88
|
>
|
|
63
89
|
{/* Header */}
|
|
64
|
-
|
|
65
|
-
<
|
|
90
|
+
{!hideHeader && (title || showCloseButton) && (
|
|
91
|
+
<div className="flex items-center justify-between border-b border-base-300 px-6 py-4 shrink-0">
|
|
92
|
+
{title && <h2 className="text-lg font-bold">{title}</h2>}
|
|
66
93
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
94
|
+
{showCloseButton && (
|
|
95
|
+
<button
|
|
96
|
+
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"
|
|
97
|
+
onClick={onClose}
|
|
98
|
+
>
|
|
99
|
+
<IoClose size={20} className="" />
|
|
100
|
+
</button>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
)}
|
|
76
104
|
|
|
77
105
|
{/* Body */}
|
|
78
|
-
<div
|
|
106
|
+
<div
|
|
107
|
+
className={`flex-1 min-h-0 overflow-y-auto ${bodyClass}`}
|
|
108
|
+
data-lenis-prevent="true"
|
|
109
|
+
>
|
|
79
110
|
{children}
|
|
80
111
|
</div>
|
|
81
112
|
|
|
82
113
|
{/* Footer */}
|
|
83
114
|
{footer && (
|
|
84
|
-
<div className="border-t border-base-300 px-6 py-4">
|
|
115
|
+
<div className="border-t border-base-300 px-6 py-4 shrink-0">
|
|
85
116
|
{footer}
|
|
86
117
|
</div>
|
|
87
118
|
)}
|
|
@@ -91,4 +122,4 @@ const Modal = ({
|
|
|
91
122
|
);
|
|
92
123
|
};
|
|
93
124
|
|
|
94
|
-
export default Modal;
|
|
125
|
+
export default Modal;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { X } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
export const Input = React.forwardRef(
|
|
6
|
+
(
|
|
7
|
+
{
|
|
8
|
+
icon,
|
|
9
|
+
wrapperClassName = "",
|
|
10
|
+
onClear,
|
|
11
|
+
className = "",
|
|
12
|
+
value,
|
|
13
|
+
size,
|
|
14
|
+
...props
|
|
15
|
+
},
|
|
16
|
+
ref,
|
|
17
|
+
) => {
|
|
18
|
+
const sizeClass = size ? `input-${size}` : "";
|
|
19
|
+
return (
|
|
20
|
+
<div className={`relative ${wrapperClassName}`}>
|
|
21
|
+
{icon && (
|
|
22
|
+
<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">
|
|
23
|
+
{icon}
|
|
24
|
+
</div>
|
|
25
|
+
)}
|
|
26
|
+
<input
|
|
27
|
+
ref={ref}
|
|
28
|
+
value={value}
|
|
29
|
+
className={`input input-bordered w-full ${sizeClass} ${icon ? "pl-10" : ""} ${
|
|
30
|
+
onClear && value ? "pr-10" : ""
|
|
31
|
+
} ${className}`}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
|
|
35
|
+
{onClear && value && (
|
|
36
|
+
<button
|
|
37
|
+
type="button"
|
|
38
|
+
onClick={onClear}
|
|
39
|
+
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"
|
|
40
|
+
aria-label="Clear input"
|
|
41
|
+
>
|
|
42
|
+
<X size={16} />
|
|
43
|
+
</button>
|
|
44
|
+
)}
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
Input.displayName = "Input";
|
|
@@ -1,26 +1,36 @@
|
|
|
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();
|
|
17
|
-
const
|
|
21
|
+
const router = useRouter();
|
|
22
|
+
const isMobile = width < 1024;
|
|
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
|
-
{/* Overlay with AnimatePresence */}
|
|
24
34
|
<AnimatePresence>
|
|
25
35
|
{isMobile && open && (
|
|
26
36
|
<motion.div
|
|
@@ -34,7 +44,6 @@ const Sidebar = () => {
|
|
|
34
44
|
)}
|
|
35
45
|
</AnimatePresence>
|
|
36
46
|
|
|
37
|
-
{/* Sidebar with AnimatePresence */}
|
|
38
47
|
<AnimatePresence>
|
|
39
48
|
{showSidebar && (
|
|
40
49
|
<motion.aside
|
|
@@ -47,18 +56,14 @@ const Sidebar = () => {
|
|
|
47
56
|
h-screen bg-base-200 border-r border-base-300
|
|
48
57
|
flex flex-col overflow-hidden
|
|
49
58
|
transition-[width] duration-300
|
|
50
|
-
${collapsed ? "w-
|
|
59
|
+
${collapsed ? "w-18" : "w-[75%] md:w-[16%] md:max-w-60"}
|
|
51
60
|
`}
|
|
52
61
|
>
|
|
53
62
|
{/* Header */}
|
|
54
63
|
<div className="h-16 border-b border-base-300 flex items-center justify-between px-5">
|
|
55
64
|
<div className="flex items-center gap-3">
|
|
56
65
|
{appConfig.app.logo ? (
|
|
57
|
-
<img
|
|
58
|
-
src={appConfig.app.logo}
|
|
59
|
-
alt=""
|
|
60
|
-
className="w-8 h-8 rounded-lg"
|
|
61
|
-
/>
|
|
66
|
+
<img src={appConfig.app.logo} alt="" className="w-8 h-8 rounded-lg" />
|
|
62
67
|
) : (
|
|
63
68
|
<div className="flex items-center">
|
|
64
69
|
<div className="bg-primary w-8 h-8 rounded-lg flex items-center justify-center text-primary-content font-bold">
|
|
@@ -85,65 +90,72 @@ const Sidebar = () => {
|
|
|
85
90
|
<nav className="flex-1 overflow-y-auto p-2.5 space-y-1" data-lenis-prevent>
|
|
86
91
|
{navigation.map((item) => {
|
|
87
92
|
const Icon = item.icon;
|
|
88
|
-
const isActive =
|
|
93
|
+
const isActive =
|
|
94
|
+
item.path === "/" ? pathname === "/" : pathname.startsWith(item.path);
|
|
89
95
|
|
|
90
96
|
return (
|
|
91
97
|
<Link
|
|
92
98
|
key={item.path}
|
|
93
99
|
href={item.path}
|
|
94
100
|
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
101
|
title={collapsed ? item.title : ""}
|
|
102
|
+
className={`flex items-center gap-3 rounded-box px-4 py-3 h-12 transition-colors
|
|
103
|
+
${isActive ? "bg-primary text-primary-content" : "hover:bg-base-300"}
|
|
104
|
+
${collapsed ? "justify-center px-0" : ""}`}
|
|
103
105
|
>
|
|
104
|
-
<Icon
|
|
105
|
-
size={18}
|
|
106
|
-
className={`shrink-0 ${collapsed && "mr-auto"}`}
|
|
107
|
-
/>
|
|
108
|
-
|
|
106
|
+
<Icon size={18} className={`shrink-0 ${collapsed ? "mr-auto" : ""}`} />
|
|
109
107
|
{!collapsed && (
|
|
110
|
-
<span className="font-medium
|
|
111
|
-
{item.title}
|
|
112
|
-
</span>
|
|
108
|
+
<span className="font-medium whitespace-nowrap">{item.title}</span>
|
|
113
109
|
)}
|
|
114
110
|
</Link>
|
|
115
111
|
);
|
|
116
112
|
})}
|
|
117
113
|
</nav>
|
|
118
114
|
|
|
119
|
-
{/* Footer
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
115
|
+
{/* Footer */}
|
|
116
|
+
<div
|
|
117
|
+
className={`p-3 border-t border-base-300/50 shrink-0 flex gap-2 ${
|
|
118
|
+
collapsed ? "flex-col items-center justify-center" : "flex-row items-center justify-between"
|
|
119
|
+
}`}
|
|
124
120
|
>
|
|
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
121
|
<button
|
|
136
|
-
onClick={
|
|
137
|
-
className={`
|
|
138
|
-
title=
|
|
122
|
+
onClick={handleLogout}
|
|
123
|
+
className={`btn btn-sm btn-circle btn-soft btn-error ${collapsed ? "hidden" : "flex"}`}
|
|
124
|
+
title="Logout"
|
|
139
125
|
>
|
|
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>}
|
|
126
|
+
<CiLogout size={18} />
|
|
146
127
|
</button>
|
|
128
|
+
|
|
129
|
+
<div className={`flex items-center gap-2 ${collapsed ? "flex-col" : ""}`}>
|
|
130
|
+
<button
|
|
131
|
+
onClick={toggleTheme}
|
|
132
|
+
className="flex items-center gap-3 hover:opacity-80 transition-opacity"
|
|
133
|
+
title="Toggle Theme"
|
|
134
|
+
>
|
|
135
|
+
<div
|
|
136
|
+
className={`btn btn-sm btn-circle btn-primary btn-soft pointer-events-none ${collapsed ? "hidden" : "flex"}`}
|
|
137
|
+
>
|
|
138
|
+
{theme === "dark" ? (
|
|
139
|
+
<Sun size={16} className="shrink-0" />
|
|
140
|
+
) : (
|
|
141
|
+
<Moon size={16} className="shrink-0" />
|
|
142
|
+
)}
|
|
143
|
+
</div>
|
|
144
|
+
{!collapsed && (
|
|
145
|
+
<span className="md:hidden font-medium text-sm text-base-content/80">
|
|
146
|
+
{theme === "dark" ? "Dark Mode" : "Light Mode"}
|
|
147
|
+
</span>
|
|
148
|
+
)}
|
|
149
|
+
</button>
|
|
150
|
+
|
|
151
|
+
<button
|
|
152
|
+
onClick={toggleCollapsed}
|
|
153
|
+
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"
|
|
154
|
+
title={collapsed ? "Expand Sidebar" : "Collapse Sidebar"}
|
|
155
|
+
>
|
|
156
|
+
{collapsed ? <GoSidebarExpand size={18} /> : <GoSidebarCollapse size={18} />}
|
|
157
|
+
</button>
|
|
158
|
+
</div>
|
|
147
159
|
</div>
|
|
148
160
|
</motion.aside>
|
|
149
161
|
)}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
1
3
|
import { FiMenu } from "react-icons/fi";
|
|
2
4
|
import { useUiStore } from "../../store/useUiStore";
|
|
5
|
+
import { Breadcrumbs } from "../common/Breadcrumbs";
|
|
3
6
|
|
|
4
7
|
const SidebarHeader = ({ title = "Dashboard", actions = null }) => {
|
|
5
|
-
const { toggleSidebar } = useUiStore();
|
|
8
|
+
const { toggleSidebar, breadcrumbs } = useUiStore();
|
|
6
9
|
|
|
7
10
|
return (
|
|
8
|
-
<header className="sticky top-0 z-30 h-16 bg-base-200 backdrop-blur border-b border-base-
|
|
11
|
+
<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">
|
|
9
12
|
<div className="flex items-center gap-3">
|
|
10
13
|
<button
|
|
11
14
|
onClick={toggleSidebar}
|
|
@@ -14,12 +17,14 @@ const SidebarHeader = ({ title = "Dashboard", actions = null }) => {
|
|
|
14
17
|
<FiMenu size={22} />
|
|
15
18
|
</button>
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
{breadcrumbs && breadcrumbs.length > 0 ? (
|
|
21
|
+
<Breadcrumbs items={breadcrumbs} />
|
|
22
|
+
) : (
|
|
23
|
+
<h1 className="text-xl font-bold">{title}</h1>
|
|
24
|
+
)}
|
|
18
25
|
</div>
|
|
19
26
|
|
|
20
|
-
<div className="flex items-center gap-2">
|
|
21
|
-
{actions}
|
|
22
|
-
</div>
|
|
27
|
+
<div className="flex items-center gap-2">{actions}</div>
|
|
23
28
|
</header>
|
|
24
29
|
);
|
|
25
30
|
};
|
|
@@ -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({
|
|
@@ -6,6 +6,8 @@ export const useUiStore = create((set, get) => ({
|
|
|
6
6
|
isMobileMenuOpened: false,
|
|
7
7
|
headerTitle: "Dashboard",
|
|
8
8
|
headerActions: null,
|
|
9
|
+
breadcrumbs: null,
|
|
10
|
+
setBreadcrumbs: (items) => set({ breadcrumbs: items }),
|
|
9
11
|
setHeaderTitle: (title) => set({ headerTitle: title }),
|
|
10
12
|
setHeaderActions: (actions) => set({ headerActions: actions }),
|
|
11
13
|
toggleCollapsed: () => set((state) => ({ collapsed: !state.collapsed })),
|
|
@@ -17,7 +19,10 @@ export const useUiStore = create((set, get) => ({
|
|
|
17
19
|
set((state) => ({ isMobileMenuOpened: !state.isMobileMenuOpened }));
|
|
18
20
|
},
|
|
19
21
|
|
|
20
|
-
theme:
|
|
22
|
+
theme:
|
|
23
|
+
typeof window !== "undefined"
|
|
24
|
+
? localStorage.getItem("theme") || "light"
|
|
25
|
+
: "light",
|
|
21
26
|
|
|
22
27
|
setTheme: (theme) => {
|
|
23
28
|
if (typeof document !== "undefined") {
|
|
@@ -26,20 +31,17 @@ export const useUiStore = create((set, get) => ({
|
|
|
26
31
|
if (typeof window !== "undefined") {
|
|
27
32
|
localStorage.setItem("theme", theme);
|
|
28
33
|
}
|
|
29
|
-
|
|
30
34
|
set({ theme });
|
|
31
35
|
},
|
|
32
36
|
|
|
33
37
|
toggleTheme: () => {
|
|
34
38
|
const nextTheme = get().theme === "light" ? "dark" : "light";
|
|
35
|
-
|
|
36
39
|
if (typeof document !== "undefined") {
|
|
37
40
|
document.documentElement.setAttribute("data-theme", nextTheme);
|
|
38
41
|
}
|
|
39
42
|
if (typeof window !== "undefined") {
|
|
40
43
|
localStorage.setItem("theme", nextTheme);
|
|
41
44
|
}
|
|
42
|
-
|
|
43
45
|
set({ theme: nextTheme });
|
|
44
46
|
},
|
|
45
47
|
|
|
@@ -57,4 +59,7 @@ export const useUiStore = create((set, get) => ({
|
|
|
57
59
|
set({
|
|
58
60
|
open: false,
|
|
59
61
|
}),
|
|
62
|
+
|
|
63
|
+
modalOpen: false,
|
|
64
|
+
setModalOpen: (open) => set({ modalOpen: open }),
|
|
60
65
|
}));
|
|
@@ -1,25 +1,74 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import Link from "next/link";
|
|
4
|
-
import {
|
|
4
|
+
import { useRouter } from "next/navigation";
|
|
5
|
+
import { ArrowLeft, ChevronRight } from "lucide-react";
|
|
5
6
|
|
|
6
|
-
export
|
|
7
|
+
export interface BreadcrumbItem {
|
|
8
|
+
label: React.ReactNode;
|
|
9
|
+
path?: string;
|
|
10
|
+
subtitle?: string;
|
|
11
|
+
onClick?: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface BreadcrumbsProps {
|
|
15
|
+
items: BreadcrumbItem[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const Breadcrumbs = ({ items }: BreadcrumbsProps) => {
|
|
19
|
+
const router = useRouter();
|
|
7
20
|
return (
|
|
8
21
|
<div className="flex items-center gap-1.5 text-sm font-bold text-base-content/50 flex-wrap">
|
|
9
|
-
|
|
22
|
+
<button
|
|
23
|
+
className="btn btn-primary btn-soft btn-square btn-sm mr-1 hidden md:flex"
|
|
24
|
+
onClick={() => router.back()}
|
|
25
|
+
>
|
|
26
|
+
<ArrowLeft size={18} />
|
|
27
|
+
</button>
|
|
28
|
+
{items.map((item, idx) => {
|
|
10
29
|
const isLast = idx === items.length - 1;
|
|
11
30
|
return (
|
|
12
31
|
<React.Fragment key={idx}>
|
|
13
32
|
{item.path && !isLast ? (
|
|
14
|
-
<Link
|
|
33
|
+
<Link
|
|
34
|
+
href={item.path}
|
|
35
|
+
onClick={item.onClick}
|
|
36
|
+
className="hover:text-primary transition-colors hidden sm:flex items-center gap-1"
|
|
37
|
+
>
|
|
15
38
|
{item.label}
|
|
16
39
|
</Link>
|
|
17
|
-
) : (
|
|
18
|
-
<
|
|
40
|
+
) : item.onClick && !isLast ? (
|
|
41
|
+
<button
|
|
42
|
+
type="button"
|
|
43
|
+
onClick={item.onClick}
|
|
44
|
+
className="hover:text-primary transition-colors hidden sm:flex items-center gap-1"
|
|
45
|
+
>
|
|
19
46
|
{item.label}
|
|
20
|
-
</
|
|
47
|
+
</button>
|
|
48
|
+
) : (
|
|
49
|
+
<div className="flex flex-col">
|
|
50
|
+
<span
|
|
51
|
+
className={
|
|
52
|
+
isLast
|
|
53
|
+
? "text-lg md:text-xl font-bold leading-tight text-base-content tracking-tight truncate max-w-[200px] md:max-w-none"
|
|
54
|
+
: ""
|
|
55
|
+
}
|
|
56
|
+
>
|
|
57
|
+
{item.label}
|
|
58
|
+
</span>
|
|
59
|
+
{item.subtitle && (
|
|
60
|
+
<span className="text-xs text-base-content/60 font-normal truncate">
|
|
61
|
+
{item.subtitle}
|
|
62
|
+
</span>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
)}
|
|
66
|
+
{!isLast && (
|
|
67
|
+
<ChevronRight
|
|
68
|
+
size={14}
|
|
69
|
+
className="text-base-content/30 shrink-0 hidden sm:block"
|
|
70
|
+
/>
|
|
21
71
|
)}
|
|
22
|
-
{!isLast && <ChevronRight size={14} className="text-base-content/30 shrink-0" />}
|
|
23
72
|
</React.Fragment>
|
|
24
73
|
);
|
|
25
74
|
})}
|