@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.
Files changed (127) 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/javascript/package.json +2 -1
  69. package/templates/layers/node/javascript/src/controllers/push.controller.js +35 -14
  70. package/templates/layers/node/javascript/src/models/pushSubscription.model.js +5 -0
  71. package/templates/layers/node/javascript/src/utils/pushHelper.js +59 -12
  72. package/templates/layers/node/typescript/package-lock.json +3795 -0
  73. package/templates/layers/node/typescript/package.json +3 -2
  74. package/templates/layers/node/typescript/src/controllers/push.controller.ts +35 -14
  75. package/templates/layers/node/typescript/src/models/pushSubscription.model.ts +6 -0
  76. package/templates/layers/node/typescript/src/services/auth.service.ts +3 -3
  77. package/templates/layers/node/typescript/src/utils/pushHelper.ts +66 -12
  78. package/templates/layers/node-prisma/javascript/package.json +6 -3
  79. package/templates/layers/node-prisma/javascript/prisma/schema.prisma +20 -9
  80. package/templates/layers/node-prisma/javascript/prisma.config.js +12 -0
  81. package/templates/layers/node-prisma/javascript/src/config/{db.js → prisma.js} +9 -1
  82. package/templates/layers/node-prisma/javascript/src/controllers/push.controller.js +58 -19
  83. package/templates/layers/node-prisma/javascript/src/utils/pushHelper.js +62 -14
  84. package/templates/layers/node-prisma/typescript/package-lock.json +4808 -0
  85. package/templates/layers/node-prisma/typescript/package.json +8 -4
  86. package/templates/layers/node-prisma/typescript/prisma/schema.prisma +20 -9
  87. package/templates/layers/node-prisma/typescript/prisma.config.ts +12 -0
  88. package/templates/layers/node-prisma/typescript/src/config/{db.ts → prisma.ts} +9 -1
  89. package/templates/layers/node-prisma/typescript/src/controllers/push.controller.ts +67 -22
  90. package/templates/layers/node-prisma/typescript/src/index.ts +1 -1
  91. package/templates/layers/node-prisma/typescript/src/middleware/auth.middleware.ts +15 -5
  92. package/templates/layers/node-prisma/typescript/src/services/auth.service.ts +134 -128
  93. package/templates/layers/node-prisma/typescript/src/utils/pushHelper.ts +69 -14
  94. package/templates/layers/prisma/typescript/prisma/schema.prisma +35 -10
  95. package/templates/layers/react/javascript/src/components/common/Breadcrumbs.jsx +62 -58
  96. package/templates/layers/react/javascript/src/components/common/Pagination.jsx +164 -76
  97. package/templates/layers/react/javascript/src/components/common/SidebarModal.jsx +179 -0
  98. package/templates/layers/react/javascript/src/components/common/StatsCard.jsx +167 -0
  99. package/templates/layers/react/javascript/src/components/common/Table.jsx +248 -132
  100. package/templates/layers/react/javascript/src/components/common/Tabs.jsx +164 -112
  101. package/templates/layers/react/javascript/src/components/common/ToggleSwitch.jsx +3 -3
  102. package/templates/layers/react/javascript/src/components/layout/SidebarLayout.jsx +4 -12
  103. package/templates/layers/react/javascript/src/components/modals/Modal.jsx +53 -18
  104. package/templates/layers/react/javascript/src/components/ui/Input.jsx +49 -0
  105. package/templates/layers/react/javascript/src/components/ui/Sidebar.jsx +215 -0
  106. package/templates/layers/react/javascript/src/components/ui/SidebarHeader.jsx +25 -30
  107. package/templates/layers/react/javascript/src/config/appConfig.js +3 -3
  108. package/templates/layers/react/javascript/src/hooks/useWindowSize.js +2 -2
  109. package/templates/layers/react/typescript/src/components/common/Breadcrumbs.tsx +32 -6
  110. package/templates/layers/react/typescript/src/components/common/Pagination.tsx +151 -57
  111. package/templates/layers/react/typescript/src/components/common/SidebarModal.tsx +199 -0
  112. package/templates/layers/react/typescript/src/components/common/StatsCard.tsx +231 -0
  113. package/templates/layers/react/typescript/src/components/common/Table.tsx +197 -34
  114. package/templates/layers/react/typescript/src/components/common/Tabs.tsx +56 -12
  115. package/templates/layers/react/typescript/src/components/common/ToggleSwitch.tsx +15 -3
  116. package/templates/layers/react/typescript/src/components/layout/SidebarLayout.tsx +3 -6
  117. package/templates/layers/react/typescript/src/components/modals/Modal.tsx +151 -0
  118. package/templates/layers/react/typescript/src/components/ui/Input.tsx +58 -0
  119. package/templates/layers/react/typescript/src/components/ui/Sidebar.tsx +116 -53
  120. package/templates/layers/react/typescript/src/components/ui/SidebarHeader.tsx +5 -4
  121. package/templates/layers/react/typescript/src/config/appConfig.ts +3 -3
  122. package/templates/layers/react/typescript/src/hooks/useWindowSize.ts +2 -3
  123. package/templates/layers/react-native/base/src/components/ui/BiometricLock.tsx +2 -1
  124. package/templates/layers/react-native/base/src/config/appConfig.ts +44 -0
  125. package/templates/presets/electron/preset.json +9 -0
  126. package/templates/presets/electron-ts/preset.json +9 -0
  127. package/templates/layers/react/javascript/src/components/ui/sidebar.jsx +0 -169
@@ -1,134 +1,186 @@
1
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
1
  import React from "react";
3
2
  import { ChevronDown } from "lucide-react";
4
3
  import { motion } from "framer-motion";
5
- const Tabs = ({
4
+
5
+ export const Tabs = ({
6
6
  tabs,
7
7
  activeTab,
8
8
  onChange,
9
- variant = "underline",
10
- className = "",
9
+ variant = "box",
10
+ className = "bg-base-200 w-fit",
11
11
  dropdown = false,
12
+ disabled = false,
13
+ size = "base",
12
14
  }) => {
13
15
  const [isOpen, setIsOpen] = React.useState(false);
16
+
17
+ const getTabSizeClass = () => {
18
+ switch (size) {
19
+ case "xs":
20
+ return "text-xs px-2.5 h-7 min-h-7";
21
+ case "sm":
22
+ return "text-sm px-3 h-8 min-h-8";
23
+ case "base":
24
+ return "text-base px-4 h-10 min-h-10";
25
+ case "lg":
26
+ return "text-lg px-6 h-12 min-h-12";
27
+ case "xl":
28
+ return "text-xl px-6 h-14 min-h-14";
29
+ case "2xl":
30
+ return "text-2xl px-8 h-16 min-h-16";
31
+ default:
32
+ return "text-base px-4 h-10 min-h-10";
33
+ }
34
+ };
35
+
36
+ const getBtnSizeClass = () => {
37
+ switch (size) {
38
+ case "xs":
39
+ return "btn-xs text-xs";
40
+ case "sm":
41
+ return "btn-sm text-sm";
42
+ case "base":
43
+ return "text-base";
44
+ case "lg":
45
+ return "btn-lg text-lg";
46
+ case "xl":
47
+ return "btn-lg text-xl";
48
+ case "2xl":
49
+ return "btn-lg text-2xl";
50
+ default:
51
+ return "text-base";
52
+ }
53
+ };
54
+
55
+ const tabSizeClass = getTabSizeClass();
56
+ const btnSizeClass = getBtnSizeClass();
57
+
58
+ // Close dropdown if user clicks outside
14
59
  React.useEffect(() => {
15
60
  if (!dropdown || !isOpen) return;
16
61
  const closeMenu = () => setIsOpen(false);
17
62
  document.addEventListener("click", closeMenu);
18
63
  return () => document.removeEventListener("click", closeMenu);
19
64
  }, [dropdown, isOpen]);
65
+
20
66
  const renderDropdown = () => {
21
67
  if (!dropdown) return null;
22
68
  const activeItem = tabs.find((t) => t.id === activeTab);
23
- return /* @__PURE__ */ jsxs("div", {
24
- className: "relative md:hidden w-full max-w-xs z-[50]",
25
- onClick: (e) => e.stopPropagation(),
26
- children: [
27
- /* @__PURE__ */ jsxs("button", {
28
- onClick: () => setIsOpen(!isOpen),
29
- className:
30
- "btn btn-sm bg-base-200 border-base-300 border flex w-full items-center justify-between px-3 text-base-content ",
31
- children: [
32
- /* @__PURE__ */ jsxs("div", {
33
- className: "flex items-center gap-1.5",
34
- children: [
35
- activeItem?.icon,
36
- /* @__PURE__ */ jsx("span", {
37
- className: "font-semibold",
38
- children: activeItem?.label,
39
- }),
40
- ],
41
- }),
42
- /* @__PURE__ */ jsx(ChevronDown, {
43
- size: 14,
44
- className: `opacity-50 transition-transform ${isOpen ? "rotate-180" : ""}`,
45
- }),
46
- ],
47
- }),
48
- isOpen &&
49
- /* @__PURE__ */ jsx("div", {
50
- className:
51
- "absolute top-full left-0 right-0 mt-1 bg-base-100 border border-base-200 shadow rounded-lg py-1 animate-in fade-in slide-in-from-top-2",
52
- children: tabs.map((tab) =>
53
- /* @__PURE__ */ jsxs(
54
- "button",
55
- {
56
- onClick: () => {
57
- onChange(tab.id);
58
- setIsOpen(false);
59
- },
60
- className: `w-full flex items-center gap-2 px-3 py-2 text-sm transition-colors ${activeTab === tab.id ? "bg-primary/10 text-primary font-semibold" : "hover:bg-base-200 text-base-content/80"}`,
61
- children: [tab.icon, tab.label],
62
- },
63
- tab.id,
64
- ),
65
- ),
66
- }),
67
- ],
68
- });
69
+ return (
70
+ <div
71
+ className="relative md:hidden w-full max-w-xs z-[50]"
72
+ onClick={(e) => e.stopPropagation()}
73
+ >
74
+ {/* Trigger Button */}
75
+ <button
76
+ type="button"
77
+ onClick={() => setIsOpen(!isOpen)}
78
+ disabled={disabled}
79
+ className={`btn ${btnSizeClass} bg-base-200 border-base-300 border flex w-full items-center justify-between px-3 text-base-content ${disabled ? "opacity-50 cursor-not-allowed" : ""}`}
80
+ >
81
+ <div className="flex items-center gap-1.5">
82
+ {activeItem?.icon}
83
+ <span className="font-semibold">{activeItem?.label}</span>
84
+ </div>
85
+ <ChevronDown
86
+ size={14}
87
+ className={`opacity-50 transition-transform ${isOpen ? "rotate-180" : ""}`}
88
+ />
89
+ </button>
90
+
91
+ {/* Dropdown Menu */}
92
+ {isOpen && (
93
+ <div className="absolute top-full left-0 right-0 mt-1 bg-base-100 border border-base-200 shadow rounded-lg py-1 animate-in fade-in slide-in-from-top-2">
94
+ {tabs.map((tab) => (
95
+ <button
96
+ key={tab.id}
97
+ onClick={() => {
98
+ onChange(tab.id);
99
+ setIsOpen(false);
100
+ }}
101
+ type="button"
102
+ className={`w-full flex items-center gap-2 px-3 py-2 transition-colors ${size === "base" ? "text-sm" : `text-${size}`} ${
103
+ activeTab === tab.id
104
+ ? "bg-primary/10 text-primary font-semibold"
105
+ : "hover:bg-base-200 text-base-content/80"
106
+ }`}
107
+ >
108
+ {tab.icon}
109
+ {tab.label}
110
+ </button>
111
+ ))}
112
+ </div>
113
+ )}
114
+ </div>
115
+ );
69
116
  };
117
+
70
118
  if (variant === "box") {
71
- return /* @__PURE__ */ jsxs(Fragment, {
72
- children: [
73
- renderDropdown(),
74
- /* @__PURE__ */ jsx("div", {
75
- className: `tabs tabs-boxed bg-base-200 p-1 flex-nowrap overflow-x-auto hide-scrollbar shrink-0 rounded-lg ${dropdown ? "hidden md:flex" : "flex"} ${className}`,
76
- children: tabs.map((tab) =>
77
- /* @__PURE__ */ jsxs(
78
- "button",
79
- {
80
- onClick: () => onChange(tab.id),
81
- className: `tab whitespace-nowrap flex items-center rounded-lg gap-2 relative z-0 ${activeTab === tab.id ? "text-primary-content" : "text-base-content/70 hover:text-base-content"}`,
82
- children: [
83
- activeTab === tab.id &&
84
- /* @__PURE__ */ jsx(motion.div, {
85
- layoutId: "activeTabBox",
86
- className: "absolute inset-0 bg-primary rounded-lg -z-10",
87
- transition: { type: "spring", duration: 0, bounce: 0.2 },
88
- }),
89
- tab.icon,
90
- tab.label,
91
- ],
92
- },
93
- tab.id,
94
- ),
95
- ),
96
- }),
97
- ],
98
- });
119
+ return (
120
+ <>
121
+ {renderDropdown()}
122
+ <div
123
+ className={`tabs tabs-boxed p-1 flex-nowrap overflow-x-auto hide-scrollbar shrink-0 rounded-lg ${dropdown ? "hidden md:flex" : "flex"} ${className}`}
124
+ >
125
+ {tabs.map((tab) => (
126
+ <button
127
+ type="button"
128
+ key={tab.id}
129
+ onClick={() => onChange(tab.id)}
130
+ disabled={disabled}
131
+ className={`tab ${tabSizeClass} whitespace-nowrap flex items-center rounded-lg gap-2 relative z-0 ${
132
+ activeTab === tab.id
133
+ ? "text-primary-content"
134
+ : "text-base-content/70 hover:text-base-content "
135
+ } ${disabled ? "opacity-50 cursor-not-allowed" : ""}`}
136
+ >
137
+ {activeTab === tab.id && (
138
+ <motion.div
139
+ layoutId="activeTabBox"
140
+ className="absolute inset-0 bg-primary rounded-lg -z-10"
141
+ transition={{ type: "spring", duration: 0, bounce: 0.2 }}
142
+ />
143
+ )}
144
+ {tab.icon}
145
+ {tab.label}
146
+ </button>
147
+ ))}
148
+ </div>
149
+ </>
150
+ );
99
151
  }
100
- return /* @__PURE__ */ jsxs(Fragment, {
101
- children: [
102
- renderDropdown(),
103
- /* @__PURE__ */ jsx("div", {
104
- className: `tabs tabs-bordered ${dropdown ? "hidden md:flex" : "flex"} ${className}`,
105
- children: tabs.map((tab) =>
106
- /* @__PURE__ */ jsxs(
107
- "button",
108
- {
109
- onClick: () => onChange(tab.id),
110
- className: `tab whitespace-nowrap relative !border-transparent ${activeTab === tab.id ? "font-semibold text-primary" : "text-base-content/70 hover:text-base-content"}`,
111
- children: [
112
- activeTab === tab.id &&
113
- /* @__PURE__ */ jsx(motion.div, {
114
- layoutId: "activeTabUnderline",
115
- className:
116
- "absolute bottom-0 left-0 right-0 h-0.5 bg-primary",
117
- transition: { type: "spring", duration: 0.5, bounce: 0.2 },
118
- }),
119
- tab.icon &&
120
- /* @__PURE__ */ jsx("span", {
121
- className: "mr-2",
122
- children: tab.icon,
123
- }),
124
- tab.label,
125
- ],
126
- },
127
- tab.id,
128
- ),
129
- ),
130
- }),
131
- ],
132
- });
152
+
153
+ // default: underline
154
+ return (
155
+ <>
156
+ {renderDropdown()}
157
+ <div
158
+ className={`tabs tabs-bordered ${dropdown ? "hidden md:flex" : "flex"} ${className}`}
159
+ >
160
+ {tabs.map((tab) => (
161
+ <button
162
+ type="button"
163
+ key={tab.id}
164
+ onClick={() => onChange(tab.id)}
165
+ disabled={disabled}
166
+ className={`tab ${tabSizeClass} whitespace-nowrap relative !border-transparent ${
167
+ activeTab === tab.id
168
+ ? "font-semibold text-primary"
169
+ : "text-base-content/70 hover:text-base-content"
170
+ } ${disabled ? "opacity-50 cursor-not-allowed" : ""}`}
171
+ >
172
+ {activeTab === tab.id && (
173
+ <motion.div
174
+ layoutId="activeTabUnderline"
175
+ className="absolute bottom-0 left-0 right-0 h-0.5 bg-primary"
176
+ transition={{ type: "spring", duration: 0.5, bounce: 0.2 }}
177
+ />
178
+ )}
179
+ {tab.icon && <span className="mr-2">{tab.icon}</span>}
180
+ {tab.label}
181
+ </button>
182
+ ))}
183
+ </div>
184
+ </>
185
+ );
133
186
  };
134
- export { Tabs };
@@ -1,10 +1,10 @@
1
1
  import React from "react";
2
2
  import { motion } from "motion/react";
3
3
 
4
- const ToggleSwitch = ({ checked, onChange, label }) => {
4
+ const ToggleSwitch = ({ checked, onChange, label, className }) => {
5
5
  return (
6
6
  <div
7
- className="flex items-center gap-3 cursor-pointer select-none w-fit group"
7
+ className={`flex items-center gap-3 cursor-pointer select-none w-fit group ${className}`}
8
8
  onClick={() => onChange(!checked)}
9
9
  >
10
10
  <span className="text-sm font-bold">{label}</span>
@@ -16,7 +16,7 @@ const ToggleSwitch = ({ checked, onChange, label }) => {
16
16
  }`}
17
17
  >
18
18
  <motion.div
19
- className={`w-4 h-4 rounded-full shadow-sm ${
19
+ className={`w-4 h-4 rounded-full shadow ${
20
20
  checked ? "bg-white" : "bg-base-content/40"
21
21
  }`}
22
22
  layout
@@ -1,20 +1,15 @@
1
- import { useEffect } from "react";
2
1
  import { Outlet, useLocation } from "react-router-dom";
3
2
  import SidebarHeader from "../ui/SidebarHeader";
4
- import Sidebar from "../ui/sidebar";
3
+ import Sidebar from "../ui/Sidebar";
5
4
  import { useUiStore } from "../../store/useUiStore";
6
- import appConfig from "../../config/appConfig";
7
5
 
8
6
  const SidebarLayout = () => {
9
7
  const { headerTitle, headerActions, setHeaderTitle, setHeaderActions } =
10
8
  useUiStore();
11
9
  const { pathname } = useLocation();
12
10
 
13
- // Reset header title and actions whenever the route shifts
14
- useEffect(() => {
15
- setHeaderTitle(appConfig.app.name);
16
- setHeaderActions(null);
17
- }, [pathname, setHeaderTitle, setHeaderActions]);
11
+ // Layout no longer resets title on every route change.
12
+ // Individual pages are responsible for setting their own title via useUiStore.
18
13
 
19
14
  return (
20
15
  <div className="flex h-screen overflow-hidden">
@@ -23,10 +18,7 @@ const SidebarLayout = () => {
23
18
  <div className="flex flex-1 flex-col overflow-hidden">
24
19
  <SidebarHeader title={headerTitle} actions={headerActions} />
25
20
 
26
- <main
27
- className="flex-1 h-full overflow-y-auto px-2 py-6 md:p-6 max-w-7xl"
28
- data-lenis-prevent
29
- >
21
+ <main className="flex-1 h-full overflow-y-auto " data-lenis-prevent>
30
22
  <Outlet />
31
23
  </main>
32
24
  </div>
@@ -1,13 +1,19 @@
1
1
  import { useEffect } from "react";
2
2
  import { createPortal } from "react-dom";
3
3
  import { IoClose } from "react-icons/io5";
4
+ import { useUiStore } from "../../store/useUiStore";
4
5
 
5
6
  const sizes = {
6
7
  sm: "max-w-sm",
7
8
  md: "max-w-md",
8
9
  lg: "max-w-lg",
9
- xl: "max-w-2xl",
10
- "2xl": "max-w-4xl",
10
+ xl: "max-w-xl",
11
+ "2xl": "max-w-2xl",
12
+ "3xl": "max-w-3xl",
13
+ "4xl": "max-w-4xl",
14
+ "5xl": "max-w-5xl",
15
+ "6xl": "max-w-6xl",
16
+ "7xl": "max-w-7xl",
11
17
  full: "max-w-7xl",
12
18
  };
13
19
 
@@ -16,12 +22,27 @@ const Modal = ({
16
22
  onClose,
17
23
  title,
18
24
  children,
19
- size = "md",
25
+ size = "4xl",
20
26
  closeOnBackdrop = true,
21
27
  closeOnEsc = true,
22
28
  showCloseButton = true,
29
+ hideHeader = false,
30
+ className = "",
31
+ bodyClass = "p-2 sm:p-4",
23
32
  footer,
33
+ height,
24
34
  }) => {
35
+ const setModalOpen = useUiStore((state) => state.setModalOpen);
36
+
37
+ useEffect(() => {
38
+ if (open) {
39
+ setModalOpen(true);
40
+ return () => {
41
+ setModalOpen(false);
42
+ };
43
+ }
44
+ }, [open, setModalOpen]);
45
+
25
46
  useEffect(() => {
26
47
  if (!open || !closeOnEsc) return;
27
48
 
@@ -33,7 +54,9 @@ const Modal = ({
33
54
 
34
55
  document.addEventListener("keydown", handleKeyDown);
35
56
 
36
- return () => document.removeEventListener("keydown", handleKeyDown);
57
+ return () => {
58
+ document.removeEventListener("keydown", handleKeyDown);
59
+ };
37
60
  }, [open, closeOnEsc, onClose]);
38
61
 
39
62
  if (!open) return null;
@@ -50,35 +73,47 @@ const Modal = ({
50
73
  <div
51
74
  className={`
52
75
  relative
76
+ flex flex-col overflow-hidden max-h-[90vh]
53
77
  w-full
54
78
  ${sizes[size]}
55
79
  rounded-2xl
56
80
  bg-base-100
57
- shadow-2xl
81
+ shadow-md
58
82
  border border-base-300
59
83
  animate-in zoom-in-95 fade-in duration-200
84
+ ${className}
60
85
  `}
86
+ style={height ? { height } : undefined}
61
87
  >
62
88
  {/* Header */}
63
- <div className="flex items-center justify-between border-b border-base-300 px-6 py-4">
64
- <h2 className="text-lg font-bold">{title}</h2>
89
+ {!hideHeader && (title || showCloseButton) && (
90
+ <div className="flex items-center justify-between border-b border-base-300 px-6 py-4 shrink-0">
91
+ {title && <h2 className="text-lg font-bold">{title}</h2>}
65
92
 
66
- {showCloseButton && (
67
- <button
68
- className="btn btn-circle btn-sm btn-ghost"
69
- onClick={onClose}
70
- >
71
- <IoClose size={20} />
72
- </button>
73
- )}
74
- </div>
93
+ {showCloseButton && (
94
+ <button
95
+ 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"
96
+ onClick={onClose}
97
+ >
98
+ <IoClose size={20} className="" />
99
+ </button>
100
+ )}
101
+ </div>
102
+ )}
75
103
 
76
104
  {/* Body */}
77
- <div className="max-h-[70vh] overflow-y-auto p-6">{children}</div>
105
+ <div
106
+ className={`flex-1 min-h-0 overflow-y-auto ${bodyClass}`}
107
+ data-lenis-prevent="true"
108
+ >
109
+ {children}
110
+ </div>
78
111
 
79
112
  {/* Footer */}
80
113
  {footer && (
81
- <div className="border-t border-base-300 px-6 py-4">{footer}</div>
114
+ <div className="border-t border-base-300 px-6 py-4 shrink-0">
115
+ {footer}
116
+ </div>
82
117
  )}
83
118
  </div>
84
119
  </div>,
@@ -0,0 +1,49 @@
1
+ import React from "react";
2
+ import { X } from "lucide-react";
3
+
4
+ export const Input = React.forwardRef(
5
+ (
6
+ {
7
+ icon,
8
+ wrapperClassName = "",
9
+ onClear,
10
+ className = "",
11
+ value,
12
+ size,
13
+ ...props
14
+ },
15
+ ref,
16
+ ) => {
17
+ const sizeClass = size ? `input-${size}` : "";
18
+ return (
19
+ <div className={`relative ${wrapperClassName}`}>
20
+ {icon && (
21
+ <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">
22
+ {icon}
23
+ </div>
24
+ )}
25
+ <input
26
+ ref={ref}
27
+ value={value}
28
+ className={`input input-bordered w-full ${sizeClass} ${icon ? "pl-10" : ""} ${
29
+ onClear && value ? "pr-10" : ""
30
+ } ${className}`}
31
+ {...props}
32
+ />
33
+
34
+ {onClear && value && (
35
+ <button
36
+ type="button"
37
+ onClick={onClear}
38
+ 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"
39
+ aria-label="Clear input"
40
+ >
41
+ <X size={16} />
42
+ </button>
43
+ )}
44
+ </div>
45
+ );
46
+ },
47
+ );
48
+
49
+ Input.displayName = "Input";