@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.
Files changed (102) hide show
  1. package/package.json +1 -1
  2. package/src/utils/askTemplate.js +2 -1
  3. package/templates/layers/electron/base/electron/main.js +262 -0
  4. package/templates/layers/electron/base/electron/preload.js +37 -0
  5. package/templates/layers/electron/base/electron-builder.json5 +68 -0
  6. package/templates/layers/electron/javascript/_gitignore +38 -0
  7. package/templates/layers/electron/javascript/package.json +49 -0
  8. package/templates/layers/electron/javascript/src/components/layout/SidebarLayout.jsx +43 -0
  9. package/templates/layers/electron/javascript/src/components/ui/TitleBar.jsx +130 -0
  10. package/templates/layers/electron/javascript/src/config/appConfig.js +51 -0
  11. package/templates/layers/electron/javascript/src/hooks/useElectron.js +68 -0
  12. package/templates/layers/electron/javascript/src/main.jsx +17 -0
  13. package/templates/layers/electron/javascript/src/store/useUiStore.js +59 -0
  14. package/templates/layers/electron/javascript/vite.config.js +29 -0
  15. package/templates/layers/electron/typescript/_gitignore +38 -0
  16. package/templates/layers/electron/typescript/electron/main.ts +213 -0
  17. package/templates/layers/electron/typescript/electron/preload.ts +52 -0
  18. package/templates/layers/electron/typescript/package.json +52 -0
  19. package/templates/layers/electron/typescript/src/components/layout/SidebarLayout.tsx +42 -0
  20. package/templates/layers/electron/typescript/src/components/ui/TitleBar.tsx +120 -0
  21. package/templates/layers/electron/typescript/src/config/appConfig.ts +52 -0
  22. package/templates/layers/electron/typescript/src/hooks/useElectron.ts +68 -0
  23. package/templates/layers/electron/typescript/src/main.tsx +17 -0
  24. package/templates/layers/electron/typescript/src/store/useUiStore.ts +97 -0
  25. package/templates/layers/electron/typescript/src/vite-env.d.ts +13 -0
  26. package/templates/layers/electron/typescript/tsconfig.json +25 -0
  27. package/templates/layers/electron/typescript/tsconfig.node.json +11 -0
  28. package/templates/layers/electron/typescript/vite.config.ts +29 -0
  29. package/templates/layers/next/javascript/src/components/common/Breadcrumbs.jsx +44 -6
  30. package/templates/layers/next/javascript/src/components/common/Loading.jsx +1 -0
  31. package/templates/layers/next/javascript/src/components/common/LottieIcon.jsx +1 -0
  32. package/templates/layers/next/javascript/src/components/common/Pagination.jsx +170 -0
  33. package/templates/layers/next/javascript/src/components/common/SidebarModal.jsx +180 -0
  34. package/templates/layers/next/javascript/src/components/common/StatsCard.jsx +168 -0
  35. package/templates/layers/next/javascript/src/components/common/Table.jsx +191 -41
  36. package/templates/layers/next/javascript/src/components/common/Tabs.jsx +187 -0
  37. package/templates/layers/next/javascript/src/components/common/ToggleSwitch.jsx +4 -3
  38. package/templates/layers/next/javascript/src/components/layout/ProtectedRoute.jsx +2 -1
  39. package/templates/layers/next/javascript/src/components/layout/PublicLayout.jsx +3 -6
  40. package/templates/layers/next/javascript/src/components/layout/SidebarLayout.jsx +4 -17
  41. package/templates/layers/next/javascript/src/components/modals/ConfirmModal.jsx +4 -12
  42. package/templates/layers/next/javascript/src/components/modals/Modal.jsx +50 -19
  43. package/templates/layers/next/javascript/src/components/ui/Input.jsx +50 -0
  44. package/templates/layers/next/{typescript/src/components/ui/sidebar.tsx → javascript/src/components/ui/Sidebar.jsx} +65 -53
  45. package/templates/layers/next/javascript/src/components/ui/SidebarHeader.jsx +11 -6
  46. package/templates/layers/next/javascript/src/config/appConfig.js +4 -4
  47. package/templates/layers/next/javascript/src/hooks/useWindowSize.js +2 -3
  48. package/templates/layers/next/javascript/src/store/useUiStore.js +9 -4
  49. package/templates/layers/next/typescript/src/components/common/Breadcrumbs.tsx +57 -8
  50. package/templates/layers/next/typescript/src/components/common/Loading.tsx +1 -0
  51. package/templates/layers/next/typescript/src/components/common/Pagination.tsx +186 -0
  52. package/templates/layers/next/typescript/src/components/common/SidebarModal.tsx +200 -0
  53. package/templates/layers/next/typescript/src/components/common/StatsCard.tsx +232 -0
  54. package/templates/layers/next/typescript/src/components/common/Table.tsx +201 -35
  55. package/templates/layers/next/typescript/src/components/common/Tabs.tsx +190 -0
  56. package/templates/layers/next/typescript/src/components/common/ToggleSwitch.tsx +12 -8
  57. package/templates/layers/next/typescript/src/components/layout/PublicLayout.tsx +8 -6
  58. package/templates/layers/next/typescript/src/components/layout/SidebarLayout.tsx +8 -17
  59. package/templates/layers/next/typescript/src/components/modals/ConfirmModal.tsx +1 -0
  60. package/templates/layers/next/typescript/src/components/modals/Modal.tsx +67 -21
  61. package/templates/layers/next/typescript/src/components/ui/Button.tsx +1 -0
  62. package/templates/layers/next/typescript/src/components/ui/Input.tsx +40 -60
  63. package/templates/layers/next/{javascript/src/components/ui/sidebar.jsx → typescript/src/components/ui/Sidebar.tsx} +77 -44
  64. package/templates/layers/next/typescript/src/components/ui/SidebarHeader.tsx +16 -10
  65. package/templates/layers/next/typescript/src/config/appConfig.ts +4 -4
  66. package/templates/layers/next/typescript/src/hooks/useWindowSize.ts +2 -3
  67. package/templates/layers/next/typescript/src/store/useUiStore.ts +14 -4
  68. package/templates/layers/node/typescript/package-lock.json +2 -2
  69. package/templates/layers/node/typescript/package.json +1 -1
  70. package/templates/layers/prisma/typescript/prisma/schema.prisma +35 -10
  71. package/templates/layers/react/javascript/src/components/common/Breadcrumbs.jsx +62 -58
  72. package/templates/layers/react/javascript/src/components/common/Pagination.jsx +164 -76
  73. package/templates/layers/react/javascript/src/components/common/SidebarModal.jsx +179 -0
  74. package/templates/layers/react/javascript/src/components/common/StatsCard.jsx +167 -0
  75. package/templates/layers/react/javascript/src/components/common/Table.jsx +248 -132
  76. package/templates/layers/react/javascript/src/components/common/Tabs.jsx +164 -112
  77. package/templates/layers/react/javascript/src/components/common/ToggleSwitch.jsx +3 -3
  78. package/templates/layers/react/javascript/src/components/layout/SidebarLayout.jsx +4 -12
  79. package/templates/layers/react/javascript/src/components/modals/Modal.jsx +53 -18
  80. package/templates/layers/react/javascript/src/components/ui/Input.jsx +49 -0
  81. package/templates/layers/react/javascript/src/components/ui/Sidebar.jsx +215 -0
  82. package/templates/layers/react/javascript/src/components/ui/SidebarHeader.jsx +25 -30
  83. package/templates/layers/react/javascript/src/config/appConfig.js +3 -3
  84. package/templates/layers/react/javascript/src/hooks/useWindowSize.js +2 -2
  85. package/templates/layers/react/typescript/src/components/common/Breadcrumbs.tsx +32 -6
  86. package/templates/layers/react/typescript/src/components/common/Pagination.tsx +151 -57
  87. package/templates/layers/react/typescript/src/components/common/SidebarModal.tsx +199 -0
  88. package/templates/layers/react/typescript/src/components/common/StatsCard.tsx +231 -0
  89. package/templates/layers/react/typescript/src/components/common/Table.tsx +197 -34
  90. package/templates/layers/react/typescript/src/components/common/Tabs.tsx +56 -12
  91. package/templates/layers/react/typescript/src/components/common/ToggleSwitch.tsx +15 -3
  92. package/templates/layers/react/typescript/src/components/layout/SidebarLayout.tsx +3 -6
  93. package/templates/layers/react/typescript/src/components/modals/Modal.tsx +151 -0
  94. package/templates/layers/react/typescript/src/components/ui/Input.tsx +58 -0
  95. package/templates/layers/react/typescript/src/components/ui/Sidebar.tsx +116 -53
  96. package/templates/layers/react/typescript/src/components/ui/SidebarHeader.tsx +5 -4
  97. package/templates/layers/react/typescript/src/config/appConfig.ts +3 -3
  98. package/templates/layers/react/typescript/src/hooks/useWindowSize.ts +2 -3
  99. package/templates/layers/react-native/base/src/config/appConfig.ts +1 -1
  100. package/templates/presets/electron/preset.json +9 -0
  101. package/templates/presets/electron-ts/preset.json +9 -0
  102. 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: string;
10
+ title?: string;
9
11
  children: ReactNode;
10
- size?: "sm" | "md" | "lg" | "xl" | "2xl" | "full";
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-2xl",
22
- "2xl": "max-w-4xl",
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 = "md",
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-2xl
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
- <div className="flex items-center justify-between border-b border-base-300 px-6 py-4">
77
- <h2 className="text-lg font-bold">{title}</h2>
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
- {showCloseButton && (
80
- <button
81
- className="btn btn-circle btn-sm btn-ghost"
82
- onClick={onClose}
83
- >
84
- <IoClose size={20} />
85
- </button>
86
- )}
87
- </div>
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 className="max-h-[70vh] overflow-y-auto p-6">
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,3 +1,4 @@
1
+ "use client";
1
2
  import React, { ButtonHTMLAttributes } from "react";
2
3
  import { FiLoader } from "react-icons/fi";
3
4
 
@@ -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 InputHTMLAttributes<HTMLInputElement> {
4
- label?: string;
5
- error?: string;
6
- helperText?: string;
7
- leftIcon?: ReactNode;
8
- rightIcon?: ReactNode;
9
- fullWidth?: boolean;
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
- label,
16
- error,
17
- helperText,
18
- leftIcon,
19
- rightIcon,
20
- fullWidth = true,
18
+ icon,
19
+ wrapperClassName = "",
20
+ onClear,
21
21
  className = "",
22
- id,
22
+ value,
23
+ size,
23
24
  ...props
24
25
  },
25
- ref
26
+ ref,
26
27
  ) => {
27
- const inputId = id || (label ? label.toLowerCase().replace(/\s+/g, "-") : undefined);
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={`form-control ${fullWidth ? "w-full" : ""}`}>
42
- {label && (
43
- <label htmlFor={inputId} className="label">
44
- <span className="label-text font-medium">{label}</span>
45
- </label>
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
- <div className="relative flex items-center">
48
- {leftIcon && (
49
- <div className="absolute left-3 text-base-content/50 pointer-events-none">
50
- {leftIcon}
51
- </div>
52
- )}
53
- <input
54
- id={inputId}
55
- ref={ref}
56
- className={inputClasses}
57
- {...props}
58
- />
59
- {rightIcon && (
60
- <div className="absolute right-3 text-base-content/50 pointer-events-none">
61
- {rightIcon}
62
- </div>
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 { ChevronLeft, ChevronRight, Moon, Sun } from "lucide-react";
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 } = useUiStore();
14
- const { theme, toggleTheme } = useUiStore();
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-20" : "w-[75%] md:w-[16%] md:max-w-60"}
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 className="flex-1 overflow-y-auto p-2.5 space-y-1" data-lenis-prevent>
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 = pathname === item.path;
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 transition-all duration-300">
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 Collapse Button */}
120
- <button
121
- onClick={toggleTheme}
122
- className={`w-full flex items-center gap-3 px-3 py-2.5 md:px-4 text-sm font-semibold transition-all duration-150 text-base-content/60 hover:text-base-content hover:bg-base-300/60 justify-between rounded-none ${collapsed ? "justify-center" : ""}`}
123
- title="Toggle Theme"
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={toggleCollapsed}
137
- className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-semibold transition-all duration-150 text-base-content/60 hover:text-base-content hover:bg-base-300/60 ${collapsed ? "justify-center" : ""}`}
138
- title={collapsed ? "Expand Sidebar" : "Collapse Sidebar"}
137
+ onClick={handleLogout}
138
+ className={`btn btn-sm btn-circle btn-soft btn-error ${collapsed ? "hidden" : "flex"}`}
139
+ title="Logout"
139
140
  >
140
- {collapsed ? (
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
- title?: string;
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-300 flex items-center justify-between px-4 lg:px-6">
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
- <h1 className="text-xl font-bold">{title}</h1>
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: "React App",
3
+ name: "My App",
4
4
  logo: "./logo.png",
5
- shortName: "React",
6
- description: "A React application built with @imksh/create",
5
+ shortName: "App",
6
+ description: "A Next.js application built with @imksh/create",
7
7
  version: "1.0.0",
8
- author: "Karan Sharma",
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: typeof window !== "undefined" ? localStorage.getItem("theme") || "light" : "light",
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": "/Users/karan/workspace/Projects/boilerplate/test-app-node-ts",
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": "/Users/karan/workspace/Projects/boilerplate/test-app-node-ts",
8
+ "name": "node-ts-template",
9
9
  "version": "1.0.0",
10
10
  "dependencies": {
11
11
  "bcrypt": "^5.1.1",
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "/Users/karan/workspace/Projects/boilerplate/test-app-node-ts",
2
+ "name": "node-ts-template",
3
3
  "version": "1.0.0",
4
4
  "main": "src/index.ts",
5
5
  "type": "module",