stackkit 0.3.4 → 0.3.6

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 (203) hide show
  1. package/README.md +50 -42
  2. package/dist/cli/add.js +122 -56
  3. package/dist/cli/create.d.ts +2 -0
  4. package/dist/cli/create.js +271 -95
  5. package/dist/cli/doctor.js +1 -0
  6. package/dist/cli/list.d.ts +1 -1
  7. package/dist/cli/list.js +6 -4
  8. package/dist/index.js +234 -191
  9. package/dist/lib/constants.d.ts +4 -0
  10. package/dist/lib/constants.js +4 -0
  11. package/dist/lib/discovery/module-discovery.d.ts +4 -0
  12. package/dist/lib/discovery/module-discovery.js +56 -0
  13. package/dist/lib/generation/code-generator.d.ts +11 -2
  14. package/dist/lib/generation/code-generator.js +42 -3
  15. package/dist/lib/generation/generator-utils.js +3 -1
  16. package/dist/lib/pm/package-manager.js +16 -13
  17. package/dist/lib/ui/logger.js +3 -2
  18. package/dist/lib/utils/path-resolver.d.ts +2 -0
  19. package/dist/lib/utils/path-resolver.js +8 -0
  20. package/dist/meta.json +8312 -0
  21. package/modules/auth/better-auth/files/{shared → express}/config/env.ts +48 -52
  22. package/modules/auth/better-auth/files/express/middlewares/authorize.ts +20 -1
  23. package/modules/auth/better-auth/files/express/modules/auth.controller.ts +349 -0
  24. package/modules/auth/better-auth/files/express/modules/{auth/auth.route.ts → auth.route.ts} +12 -7
  25. package/modules/auth/better-auth/files/express/modules/auth.service.ts +664 -0
  26. package/modules/auth/better-auth/files/express/modules/{auth/auth.type.ts → auth.type.ts} +22 -9
  27. package/modules/auth/better-auth/files/{shared/mongoose/auth/constants.ts → express/mongo-modules/auth.constants.ts} +0 -1
  28. package/modules/auth/better-auth/files/{shared/mongoose/auth/helper.ts → express/mongo-modules/auth.helper.ts} +11 -1
  29. package/modules/auth/better-auth/files/express/types/express.d.ts +11 -0
  30. package/modules/auth/better-auth/files/nextjs/api-route.ts +74 -0
  31. package/modules/auth/better-auth/files/nextjs/dashboard/pages/(user)/page.tsx +6 -0
  32. package/modules/auth/better-auth/files/nextjs/dashboard/pages/admin/page.tsx +6 -0
  33. package/modules/auth/better-auth/files/nextjs/dashboard/pages/layout.tsx +48 -0
  34. package/modules/auth/better-auth/files/nextjs/dashboard/pages/my-profile/page.tsx +5 -0
  35. package/modules/auth/better-auth/files/nextjs/features/services/auth.service.ts +102 -0
  36. package/modules/auth/better-auth/files/nextjs/layout/layout.tsx +13 -0
  37. package/modules/auth/better-auth/files/nextjs/lib/axios/http.ts +158 -0
  38. package/modules/auth/better-auth/files/nextjs/lib/env.ts +35 -0
  39. package/modules/auth/better-auth/files/nextjs/lib/utils/auth.ts +75 -0
  40. package/modules/auth/better-auth/files/nextjs/lib/utils/cookie.ts +29 -0
  41. package/modules/auth/better-auth/files/nextjs/lib/utils/jwt.ts +28 -0
  42. package/modules/auth/better-auth/files/nextjs/lib/utils/token.ts +49 -0
  43. package/modules/auth/better-auth/files/nextjs/pages/forgot-password/page.tsx +5 -0
  44. package/modules/auth/better-auth/files/nextjs/pages/layout.tsx +11 -0
  45. package/modules/auth/better-auth/files/nextjs/pages/login/page.tsx +9 -0
  46. package/modules/auth/better-auth/files/nextjs/pages/register/page.tsx +5 -0
  47. package/modules/auth/better-auth/files/nextjs/pages/reset-password/page.tsx +10 -0
  48. package/modules/auth/better-auth/files/nextjs/pages/verify-email/page.tsx +10 -0
  49. package/modules/auth/better-auth/files/nextjs/proxy.ts +157 -22
  50. package/modules/auth/better-auth/files/nextjs/theme/providers/theme-provider.tsx +11 -0
  51. package/modules/auth/better-auth/files/nextjs/types/api.types.ts +18 -0
  52. package/modules/auth/better-auth/files/react/components/protected-route.tsx +39 -0
  53. package/modules/auth/better-auth/files/react/components/route-guards.tsx +13 -0
  54. package/modules/auth/better-auth/files/react/dashboard/admin/pages/overview.tsx +3 -0
  55. package/modules/auth/better-auth/files/react/dashboard/pages/overview.tsx +3 -0
  56. package/modules/auth/better-auth/files/react/features/pages/forgot-password.tsx +5 -0
  57. package/modules/auth/better-auth/files/react/features/pages/login.tsx +5 -0
  58. package/modules/auth/better-auth/files/react/features/pages/my-profile.tsx +5 -0
  59. package/modules/auth/better-auth/files/react/features/pages/oauth-callback.tsx +59 -0
  60. package/modules/auth/better-auth/files/react/features/pages/register.tsx +5 -0
  61. package/modules/auth/better-auth/files/react/features/pages/reset-password.tsx +10 -0
  62. package/modules/auth/better-auth/files/react/features/pages/verify-email.tsx +10 -0
  63. package/modules/auth/better-auth/files/react/layout/dashboard-layout.tsx +54 -0
  64. package/modules/auth/better-auth/files/react/lib/axios/http.ts +68 -0
  65. package/modules/auth/better-auth/files/react/lib/env.ts +25 -0
  66. package/modules/auth/better-auth/files/react/router.tsx +73 -0
  67. package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider-context.ts +13 -0
  68. package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider.tsx +51 -0
  69. package/modules/auth/better-auth/files/react/theme/hooks/use-theme.ts +8 -0
  70. package/modules/auth/better-auth/files/shared/features/components/change-password-dialog.tsx +113 -0
  71. package/modules/auth/better-auth/files/shared/features/components/forgot-password-form.tsx +84 -0
  72. package/modules/auth/better-auth/files/shared/features/components/login-form.tsx +134 -0
  73. package/modules/auth/better-auth/files/shared/features/components/my-profile.tsx +147 -0
  74. package/modules/auth/better-auth/files/shared/features/components/profile-form.tsx +205 -0
  75. package/modules/auth/better-auth/files/shared/features/components/register-form.tsx +100 -0
  76. package/modules/auth/better-auth/files/shared/features/components/reset-password-form.tsx +111 -0
  77. package/modules/auth/better-auth/files/shared/features/components/social-login-buttons.tsx +47 -0
  78. package/modules/auth/better-auth/files/shared/features/components/user-profile-menu.tsx +106 -0
  79. package/modules/auth/better-auth/files/shared/features/components/verify-email-form.tsx +110 -0
  80. package/modules/auth/better-auth/files/shared/features/queries/auth.mutations.tsx +312 -0
  81. package/modules/auth/better-auth/files/shared/features/queries/auth.querie.ts +19 -0
  82. package/modules/auth/better-auth/files/shared/features/services/auth.api.ts +81 -0
  83. package/modules/auth/better-auth/files/shared/features/types/auth.type.ts +47 -0
  84. package/modules/auth/better-auth/files/shared/features/validators/change-password.validator.ts +18 -0
  85. package/modules/auth/better-auth/files/shared/features/validators/forgot.validator.ts +7 -0
  86. package/modules/auth/better-auth/files/shared/features/validators/login.validator.ts +14 -0
  87. package/modules/auth/better-auth/files/shared/features/validators/profile.validator.ts +8 -0
  88. package/modules/auth/better-auth/files/shared/features/validators/register.validator.ts +9 -0
  89. package/modules/auth/better-auth/files/shared/features/validators/reset.validator.ts +9 -0
  90. package/modules/auth/better-auth/files/shared/features/validators/verify.validator.ts +8 -0
  91. package/modules/auth/better-auth/files/shared/lib/auth-client.ts +2 -1
  92. package/modules/auth/better-auth/files/shared/lib/auth.ts +10 -29
  93. package/modules/auth/better-auth/files/shared/lib/constant/dashboard.ts +90 -0
  94. package/modules/auth/better-auth/files/shared/prisma/enums.prisma +0 -1
  95. package/modules/auth/better-auth/files/shared/theme/mode-toggle.tsx +30 -0
  96. package/modules/auth/better-auth/files/shared/ui/shadcn/components/dashboard/dashboard-header.tsx +94 -0
  97. package/modules/auth/better-auth/files/shared/ui/shadcn/components/dashboard/dashboard-sidebar.tsx +255 -0
  98. package/modules/auth/better-auth/files/shared/ui/shadcn/components/footer.tsx +35 -0
  99. package/modules/auth/better-auth/files/shared/ui/shadcn/components/navbar.tsx +145 -0
  100. package/modules/auth/better-auth/files/shared/ui/shadcn/form-field/input-field.tsx +440 -0
  101. package/modules/auth/better-auth/files/shared/utils/email.ts +20 -18
  102. package/modules/auth/better-auth/generator.json +174 -53
  103. package/modules/auth/better-auth/module.json +2 -2
  104. package/modules/components/files/shared/hooks/use-file-upload.ts +412 -0
  105. package/modules/components/files/shared/lib/utils/url-helpers.ts +110 -0
  106. package/modules/components/files/shared/shadcn/dashboard/data-table-column-selector.tsx +52 -0
  107. package/modules/components/files/shared/shadcn/dashboard/data-table-footer.tsx +156 -0
  108. package/modules/components/files/shared/shadcn/dashboard/data-table.tsx +405 -0
  109. package/modules/components/files/shared/shadcn/global/form-field/input-field.tsx +440 -0
  110. package/modules/components/files/shared/shadcn/global/form-field/media-uploader-field.tsx +745 -0
  111. package/modules/components/files/shared/shadcn/global/form-field/multi-select-field.tsx +207 -0
  112. package/modules/components/files/shared/shadcn/global/form-field/select-field.tsx +247 -0
  113. package/modules/components/files/shared/shadcn/global/form-field/textarea-field.tsx +277 -0
  114. package/modules/components/files/shared/shadcn/global/form-field/tiptap-editor-field.tsx +35 -0
  115. package/modules/components/files/shared/shadcn/global/no-results.tsx +41 -0
  116. package/modules/components/files/shared/shadcn/tiptap-editor/editor-menu-bar.tsx +217 -0
  117. package/modules/components/files/shared/shadcn/tiptap-editor/tiptap-editor.tsx +104 -0
  118. package/modules/components/files/shared/url/load-more.tsx +93 -0
  119. package/modules/components/files/shared/url/search-bar.tsx +131 -0
  120. package/modules/components/files/shared/url/sort-select.tsx +118 -0
  121. package/modules/components/files/shared/url/url-tabs.tsx +77 -0
  122. package/modules/components/generator.json +109 -0
  123. package/modules/components/module.json +11 -0
  124. package/modules/database/mongoose/generator.json +3 -14
  125. package/modules/database/mongoose/module.json +2 -2
  126. package/modules/database/prisma/generator.json +6 -12
  127. package/modules/database/prisma/module.json +2 -2
  128. package/modules/storage/cloudinary/files/express/config/env.ts +65 -0
  129. package/modules/storage/cloudinary/files/express/config/media.ts +103 -0
  130. package/modules/storage/cloudinary/files/express/modules/media/media.controller.ts +59 -0
  131. package/modules/storage/cloudinary/files/express/modules/media/media.route.ts +29 -0
  132. package/modules/storage/cloudinary/files/express/modules/media/media.service.ts +113 -0
  133. package/modules/storage/cloudinary/files/express/modules/media/media.type.ts +32 -0
  134. package/modules/storage/cloudinary/generator.json +34 -0
  135. package/modules/storage/cloudinary/module.json +11 -0
  136. package/modules/ui/shadcn/generator.json +21 -0
  137. package/modules/ui/shadcn/module.json +11 -0
  138. package/package.json +24 -26
  139. package/templates/express/README.md +11 -16
  140. package/templates/express/src/config/env.ts +7 -5
  141. package/templates/nextjs/README.md +13 -18
  142. package/templates/nextjs/app/favicon.ico +0 -0
  143. package/templates/nextjs/app/layout.tsx +6 -4
  144. package/templates/nextjs/components/providers/query-provider.tsx +3 -0
  145. package/templates/nextjs/env.example +3 -1
  146. package/templates/nextjs/lib/axios/http.ts +23 -0
  147. package/templates/nextjs/lib/env.ts +7 -5
  148. package/templates/nextjs/package.json +2 -1
  149. package/templates/nextjs/template.json +1 -2
  150. package/templates/react/README.md +9 -14
  151. package/templates/react/index.html +1 -1
  152. package/templates/react/package.json +1 -1
  153. package/templates/react/src/assets/favicon.ico +0 -0
  154. package/templates/react/src/components/providers/query-provider.tsx +38 -0
  155. package/templates/react/src/{shared/components → components}/seo.tsx +4 -8
  156. package/templates/react/src/lib/axios/http.ts +24 -0
  157. package/templates/react/src/main.tsx +8 -11
  158. package/templates/react/src/{features/about/pages → pages}/about.tsx +1 -1
  159. package/templates/react/src/{features/home/pages → pages}/home.tsx +1 -1
  160. package/templates/react/src/router.tsx +6 -6
  161. package/templates/react/src/vite-env.d.ts +2 -1
  162. package/templates/react/template.json +0 -1
  163. package/templates/react/tsconfig.app.json +6 -0
  164. package/templates/react/tsconfig.json +7 -1
  165. package/templates/react/vite.config.ts +12 -0
  166. package/modules/auth/authjs/files/nextjs/api/auth/[...nextauth]/route.ts +0 -3
  167. package/modules/auth/authjs/files/nextjs/proxy.ts +0 -1
  168. package/modules/auth/authjs/files/shared/lib/auth.ts +0 -119
  169. package/modules/auth/authjs/files/shared/prisma/schema.prisma +0 -61
  170. package/modules/auth/authjs/generator.json +0 -64
  171. package/modules/auth/authjs/module.json +0 -13
  172. package/modules/auth/better-auth/files/express/modules/auth/auth.controller.ts +0 -264
  173. package/modules/auth/better-auth/files/express/modules/auth/auth.service.ts +0 -537
  174. package/modules/auth/better-auth/files/express/templates/google-redirect.ejs +0 -24
  175. package/modules/auth/better-auth/files/nextjs/api/auth/[...all]/route.ts +0 -4
  176. package/modules/auth/better-auth/files/nextjs/lib/auth/auth-guards.ts +0 -41
  177. package/modules/auth/better-auth/files/nextjs/templates/email-otp.tsx +0 -74
  178. package/templates/express/node_modules/.bin/acorn +0 -17
  179. package/templates/express/node_modules/.bin/eslint +0 -17
  180. package/templates/express/node_modules/.bin/tsc +0 -17
  181. package/templates/express/node_modules/.bin/tsserver +0 -17
  182. package/templates/express/node_modules/.bin/tsx +0 -17
  183. package/templates/nextjs/lib/api/http.ts +0 -40
  184. package/templates/nextjs/next-env.d.ts +0 -6
  185. package/templates/react/dist/assets/index-D4AHT4dU.js +0 -193
  186. package/templates/react/dist/assets/index-rpwj5ZOX.css +0 -1
  187. package/templates/react/dist/index.html +0 -14
  188. package/templates/react/dist/vite.svg +0 -1
  189. package/templates/react/public/vite.svg +0 -1
  190. package/templates/react/src/app/layouts/dashboard-layout.tsx +0 -8
  191. package/templates/react/src/app/layouts/public-layout.tsx +0 -5
  192. package/templates/react/src/app/providers.tsx +0 -20
  193. package/templates/react/src/app/router.tsx +0 -21
  194. package/templates/react/src/assets/react.svg +0 -1
  195. package/templates/react/src/shared/api/http.ts +0 -39
  196. package/templates/react/src/shared/components/loading.tsx +0 -8
  197. package/templates/react/src/shared/lib/query-client.ts +0 -12
  198. package/templates/react/src/utils/storage.ts +0 -35
  199. package/templates/react/src/utils/utils.ts +0 -3
  200. /package/templates/nextjs/app/{page.tsx → (public)/(root)/page.tsx} +0 -0
  201. /package/templates/react/src/{shared/components → components}/error-boundary.tsx +0 -0
  202. /package/templates/react/src/{shared/components → components}/layout.tsx +0 -0
  203. /package/templates/react/src/{shared/pages → pages}/not-found.tsx +0 -0
@@ -0,0 +1,73 @@
1
+ import { createBrowserRouter } from "react-router";
2
+ import { ErrorBoundary } from "./components/error-boundary";
3
+ import Layout from "./components/layout";
4
+ import { AdminRoute, AuthenticatedRoute, UserRoute } from "./components/route-guards";
5
+ import ForgotPasswordPage from "./features/auth/pages/forgot-password";
6
+ import LoginPage from "./features/auth/pages/login";
7
+ import MyProfilePage from "./features/auth/pages/my-profile";
8
+ import OAuthCallbackPage from "./features/auth/pages/oauth-callback";
9
+ import RegisterPage from "./features/auth/pages/register";
10
+ import ResetPasswordPage from "./features/auth/pages/reset-password";
11
+ import VerifyEmailPage from "./features/auth/pages/verify-email";
12
+ import AdminOverview from "./features/dashboard/admin/pages/overview";
13
+ import DashboardOverview from "./features/dashboard/pages/overview";
14
+ import DashboardLayout from "./layout/dashboard-layout";
15
+ import About from "./pages/about";
16
+ import Home from "./pages/home";
17
+ import NotFound from "./pages/not-found";
18
+
19
+ export const router = createBrowserRouter([
20
+ {
21
+ path: "/",
22
+ Component: Layout,
23
+ errorElement: <ErrorBoundary />,
24
+ children: [
25
+ { index: true, Component: Home },
26
+ { path: "about", Component: About },
27
+ ],
28
+ },
29
+ {
30
+ Component: DashboardLayout,
31
+ children: [
32
+ { path: "login", Component: LoginPage },
33
+ { path: "register", Component: RegisterPage },
34
+ { path: "forgot-password", Component: ForgotPasswordPage },
35
+ { path: "reset-password", Component: ResetPasswordPage },
36
+ { path: "verify-email", Component: VerifyEmailPage },
37
+ ],
38
+ },
39
+ {
40
+ Component: UserRoute,
41
+ children: [
42
+ {
43
+ path: "dashboard",
44
+ Component: DashboardLayout,
45
+ children: [{ index: true, Component: DashboardOverview }],
46
+ },
47
+ ],
48
+ },
49
+ {
50
+ Component: AdminRoute,
51
+ children: [
52
+ {
53
+ path: "dashboard/admin",
54
+ Component: DashboardLayout,
55
+ children: [{ index: true, Component: AdminOverview }],
56
+ },
57
+ ],
58
+ },
59
+ {
60
+ Component: AuthenticatedRoute,
61
+ children: [
62
+ {
63
+ path: "dashboard",
64
+ Component: DashboardLayout,
65
+ children: [{ path: "my-profile", Component: MyProfilePage }],
66
+ },
67
+ ],
68
+ },
69
+ { path: "api/auth/callback/:provider", Component: OAuthCallbackPage },
70
+ { path: "*", Component: NotFound },
71
+ ]);
72
+
73
+ export default router;
@@ -0,0 +1,13 @@
1
+ import { createContext } from "react";
2
+
3
+ export type Theme = "dark" | "light" | "system";
4
+
5
+ export type ThemeProviderState = {
6
+ theme: Theme;
7
+ setTheme: (theme: Theme) => void;
8
+ };
9
+
10
+ export const ThemeProviderContext = createContext<ThemeProviderState>({
11
+ theme: "system",
12
+ setTheme: () => null,
13
+ });
@@ -0,0 +1,51 @@
1
+ import { useEffect, useState } from "react";
2
+ import type { Theme } from "./theme-provider-context";
3
+ import { ThemeProviderContext } from "./theme-provider-context";
4
+
5
+ type ThemeProviderProps = {
6
+ children: React.ReactNode;
7
+ defaultTheme?: Theme;
8
+ storageKey?: string;
9
+ };
10
+
11
+ export function ThemeProvider({
12
+ children,
13
+ defaultTheme = "system",
14
+ storageKey = "vite-ui-theme",
15
+ ...props
16
+ }: ThemeProviderProps) {
17
+ const [theme, setTheme] = useState<Theme>(
18
+ () => (localStorage.getItem(storageKey) as Theme) || defaultTheme,
19
+ );
20
+
21
+ useEffect(() => {
22
+ const root = window.document.documentElement;
23
+
24
+ root.classList.remove("light", "dark");
25
+
26
+ if (theme === "system") {
27
+ const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
28
+ ? "dark"
29
+ : "light";
30
+
31
+ root.classList.add(systemTheme);
32
+ return;
33
+ }
34
+
35
+ root.classList.add(theme);
36
+ }, [theme]);
37
+
38
+ const value = {
39
+ theme,
40
+ setTheme: (theme: Theme) => {
41
+ localStorage.setItem(storageKey, theme);
42
+ setTheme(theme);
43
+ },
44
+ };
45
+
46
+ return (
47
+ <ThemeProviderContext.Provider {...props} value={value}>
48
+ {children}
49
+ </ThemeProviderContext.Provider>
50
+ );
51
+ }
@@ -0,0 +1,8 @@
1
+ import { useContext } from "react";
2
+ import { ThemeProviderContext } from "../components/providers/theme-provider-context";
3
+
4
+ export const useTheme = () => {
5
+ const context = useContext(ThemeProviderContext);
6
+ if (context === undefined) throw new Error("useTheme must be used within a ThemeProvider");
7
+ return context;
8
+ };
@@ -0,0 +1,113 @@
1
+ "use client";
2
+
3
+ import InputField from "@/components/global/form-field/input-field";
4
+ import { Button } from "@/components/ui/button";
5
+ import {
6
+ Dialog,
7
+ DialogContent,
8
+ DialogDescription,
9
+ DialogFooter,
10
+ DialogHeader,
11
+ DialogTitle,
12
+ } from "@/components/ui/dialog";
13
+ import { useChangePasswordMutation } from "@/features/auth/queries/auth.mutations";
14
+ import {
15
+ changePasswordZodSchema,
16
+ type IChangePasswordPayload,
17
+ } from "@/features/auth/validators/change-password.validator";
18
+ import { zodResolver } from "@hookform/resolvers/zod";
19
+ import { Loader2 } from "lucide-react";
20
+ import { FormProvider, useForm } from "react-hook-form";
21
+
22
+ interface ChangePasswordDialogProps {
23
+ open: boolean;
24
+ onOpenChange: (open: boolean) => void;
25
+ }
26
+
27
+ export default function ChangePasswordDialog({
28
+ open,
29
+ onOpenChange,
30
+ }: ChangePasswordDialogProps) {
31
+ const mutation = useChangePasswordMutation();
32
+
33
+ const form = useForm<IChangePasswordPayload>({
34
+ mode: "onTouched",
35
+ resolver: zodResolver(changePasswordZodSchema),
36
+ defaultValues: {
37
+ currentPassword: "",
38
+ newPassword: "",
39
+ confirmPassword: "",
40
+ },
41
+ });
42
+
43
+ async function onSubmit(values: IChangePasswordPayload) {
44
+ try {
45
+ await mutation.mutateAsync({
46
+ currentPassword: values.currentPassword,
47
+ newPassword: values.newPassword,
48
+ });
49
+ form.reset();
50
+ onOpenChange(false);
51
+ } catch {
52
+ // Error handling is done in the mutation's onError callback
53
+ }
54
+ }
55
+
56
+ return (
57
+ <Dialog open={open} onOpenChange={onOpenChange}>
58
+ <DialogContent className="sm:max-w-106.25">
59
+ <DialogHeader>
60
+ <DialogTitle>Change Password</DialogTitle>
61
+ <DialogDescription>
62
+ Enter your current password and choose a new one. Password must be
63
+ at least 8 characters.
64
+ </DialogDescription>
65
+ </DialogHeader>
66
+ <FormProvider {...form}>
67
+ <form onSubmit={form.handleSubmit(onSubmit)}>
68
+ <div className="grid gap-4 py-4">
69
+ <InputField
70
+ name="currentPassword"
71
+ label="Current Password"
72
+ type="password"
73
+ required
74
+ disabled={mutation.isPending}
75
+ />
76
+ <InputField
77
+ name="newPassword"
78
+ label="New Password"
79
+ type="password"
80
+ required
81
+ disabled={mutation.isPending}
82
+ hint="At least 8 characters"
83
+ />
84
+ <InputField
85
+ name="confirmPassword"
86
+ label="Confirm New Password"
87
+ type="password"
88
+ required
89
+ disabled={mutation.isPending}
90
+ />
91
+ </div>
92
+ <DialogFooter>
93
+ <Button
94
+ type="button"
95
+ variant="outline"
96
+ onClick={() => onOpenChange(false)}
97
+ disabled={mutation.isPending}
98
+ >
99
+ Cancel
100
+ </Button>
101
+ <Button type="submit" disabled={mutation.isPending}>
102
+ {mutation.isPending && (
103
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
104
+ )}
105
+ Change Password
106
+ </Button>
107
+ </DialogFooter>
108
+ </form>
109
+ </FormProvider>
110
+ </DialogContent>
111
+ </Dialog>
112
+ );
113
+ }
@@ -0,0 +1,84 @@
1
+ {{#if framework == "nextjs"}}
2
+ "use client";
3
+ {{/if}}
4
+
5
+ import InputField from "@/components/global/form-field/input-field";
6
+ import { Button } from "@/components/ui/button";
7
+ import {
8
+ Card,
9
+ CardContent,
10
+ CardDescription,
11
+ CardHeader,
12
+ CardTitle,
13
+ } from "@/components/ui/card";
14
+ import { useForgotPasswordMutation } from "@/features/auth/queries/auth.mutations";
15
+ import { forgotZodSchema } from "@/features/auth/validators/forgot.validator";
16
+ import { zodResolver } from "@hookform/resolvers/zod";
17
+ {{#if framework == "nextjs"}}
18
+ import Link from "next/link";
19
+ {{else}}
20
+ import { Link } from "react-router";
21
+ {{/if}}
22
+ import { FormProvider, useForm } from "react-hook-form";
23
+
24
+ type ForgotValues = {
25
+ email: string;
26
+ };
27
+
28
+ export default function ForgotPasswordForm() {
29
+ const mutation = useForgotPasswordMutation();
30
+
31
+ const form = useForm<ForgotValues>({
32
+ mode: "onTouched",
33
+ resolver: zodResolver(forgotZodSchema),
34
+ defaultValues: { email: "" },
35
+ });
36
+
37
+ async function onSubmit(values: ForgotValues) {
38
+ try {
39
+ await mutation.mutateAsync(values);
40
+ } catch {
41
+ // Error handling is done in the mutation's onError callback
42
+ }
43
+ }
44
+
45
+ return (
46
+ <Card className="w-full max-w-md">
47
+ <CardHeader>
48
+ <CardTitle>Forgot password</CardTitle>
49
+ <CardDescription>
50
+ Enter your email to receive a password reset OTP
51
+ </CardDescription>
52
+ </CardHeader>
53
+ <CardContent className="p-4">
54
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
55
+ <FormProvider {...form}>
56
+ <InputField
57
+ name="email"
58
+ label="Email"
59
+ type="email"
60
+ placeholder="you@example.com"
61
+ />
62
+
63
+ <div className="flex items-center justify-between mt-2">
64
+ <div className="text-sm flex flex-col gap-1">
65
+ {{#if framework == "nextjs"}}
66
+ <Link href="/login" className="text-muted-foreground underline">
67
+ Back to sign in
68
+ </Link>
69
+ {{else}}
70
+ <Link to="/login" className="text-muted-foreground underline">
71
+ Back to sign in
72
+ </Link>
73
+ {{/if}}
74
+ </div>
75
+ <Button type="submit" disabled={form.formState.isSubmitting}>
76
+ {form.formState.isSubmitting ? "Sending..." : "Send reset OTP"}
77
+ </Button>
78
+ </div>
79
+ </FormProvider>
80
+ </form>
81
+ </CardContent>
82
+ </Card>
83
+ );
84
+ }
@@ -0,0 +1,134 @@
1
+ {{#if framework == "nextjs"}}
2
+ "use client";
3
+ {{/if}}
4
+
5
+ import InputField from "@/components/global/form-field/input-field";
6
+ import { Button } from "@/components/ui/button";
7
+ import {
8
+ Card,
9
+ CardContent,
10
+ CardDescription,
11
+ CardFooter,
12
+ CardHeader,
13
+ CardTitle,
14
+ } from "@/components/ui/card";
15
+ import { FieldGroup } from "@/components/ui/field";
16
+ import { useLoginMutation } from "@/features/auth/queries/auth.mutations";
17
+ import type { ILoginPayload } from "@/features/auth/validators/login.validator";
18
+ import { loginZodSchema } from "@/features/auth/validators/login.validator";
19
+ import { zodResolver } from "@hookform/resolvers/zod";
20
+ {{#if framework == "nextjs"}}
21
+ import Link from "next/link";
22
+ {{else}}
23
+ import { Link, useSearchParams } from "react-router";
24
+ {{/if}}
25
+ import { FormProvider, useForm } from "react-hook-form";
26
+ import SocialLoginButtons from "./social-login-buttons";
27
+
28
+ {{#if framework == "nextjs"}}
29
+ export default function LoginForm({
30
+ searchParams,
31
+ }: {
32
+ searchParams?: { redirect?: string };
33
+ }) {
34
+ const mutation = useLoginMutation();
35
+ const redirectPath = searchParams?.redirect || "";
36
+ {{else}}
37
+ export default function LoginForm() {
38
+ const mutation = useLoginMutation();
39
+ const [searchParams] = useSearchParams();
40
+ const redirectPath = searchParams.get("redirect") || "";
41
+ {{/if}}
42
+
43
+ const form = useForm<ILoginPayload & { redirectPath?: string }>({
44
+ mode: "onTouched",
45
+ resolver: zodResolver(loginZodSchema),
46
+ defaultValues: { email: "", password: "", redirectPath },
47
+ });
48
+
49
+ async function onSubmit(values: ILoginPayload & { redirectPath?: string }) {
50
+ try {
51
+ const data = await mutation.mutateAsync({
52
+ email: values.email,
53
+ password: values.password,
54
+ redirectPath: values.redirectPath,
55
+ });
56
+
57
+ console.log("Login using login form", data);
58
+ } catch {
59
+ // Error handling is done in the mutation's onError callback
60
+ }
61
+ }
62
+
63
+ return (
64
+ <Card className="w-full max-w-md">
65
+ <CardHeader>
66
+ <CardTitle>Sign in to your account</CardTitle>
67
+ <CardDescription>
68
+ Enter your email and password to sign in
69
+ </CardDescription>
70
+ </CardHeader>
71
+ <CardContent className="p-4">
72
+ <form
73
+ id="login-form"
74
+ onSubmit={form.handleSubmit(onSubmit)}
75
+ className="space-y-4"
76
+ >
77
+ <FormProvider {...form}>
78
+ <FieldGroup>
79
+ <InputField
80
+ className="grid gap-3"
81
+ name="email"
82
+ label="Email"
83
+ placeholder="m@example.com"
84
+ type="email"
85
+ />
86
+ <InputField
87
+ className="grid gap-3"
88
+ name="password"
89
+ label="Password"
90
+ type="password"
91
+ />
92
+
93
+ <div className="flex justify-between">
94
+ <div className="text-sm flex flex-col gap-1">
95
+ {{#if framework == "nextjs"}}
96
+ <Link href="/register" className="text-muted-foreground underline">
97
+ Don't have an account? Create one
98
+ </Link>
99
+ {{else}}
100
+ <Link to="/register" className="text-muted-foreground underline">
101
+ Don't have an account? Create one
102
+ </Link>
103
+ {{/if}}
104
+ </div>
105
+ {{#if framework == "nextjs"}}
106
+ <Link href="/forgot-password" className="text-muted-foreground underline flex items-end text-sm">
107
+ Forgot password?
108
+ </Link>
109
+ {{else}}
110
+ <Link to="/forgot-password" className="text-muted-foreground underline flex items-end text-sm">
111
+ Forgot password?
112
+ </Link>
113
+ {{/if}}
114
+ </div>
115
+ </FieldGroup>
116
+
117
+ <CardFooter className="flex flex-col items-center gap-4">
118
+ <Button
119
+ type="submit"
120
+ size="lg"
121
+ form="login-form"
122
+ disabled={form.formState.isSubmitting}
123
+ className="w-full"
124
+ >
125
+ {form.formState.isSubmitting ? "Signing in..." : "Sign in"}
126
+ </Button>
127
+ <SocialLoginButtons />
128
+ </CardFooter>
129
+ </FormProvider>
130
+ </form>
131
+ </CardContent>
132
+ </Card>
133
+ );
134
+ }
@@ -0,0 +1,147 @@
1
+ "use client";
2
+
3
+ import { Skeleton } from "@/components/ui/skeleton";
4
+ import { useMeQuery } from "@/features/auth/queries/auth.querie";
5
+ import ProfileForm from "./profile-form";
6
+
7
+ export default function MyProfile() {
8
+ const { data: user, isLoading } = useMeQuery();
9
+
10
+ if (isLoading || !user) {
11
+ return (
12
+ <div className="flex flex-col gap-6 p-4 md:p-6 lg:p-8">
13
+ <div className="flex flex-col gap-2">
14
+ <Skeleton className="h-9 w-32" />
15
+ <Skeleton className="h-4 w-72" />
16
+ </div>
17
+ <div className="grid gap-6 lg:grid-cols-3">
18
+ <div className="space-y-6 lg:col-span-2">
19
+ <div className="space-y-6 rounded-lg border bg-card p-6">
20
+ <div className="flex items-center gap-6">
21
+ <Skeleton className="h-24 w-24 rounded-full" />
22
+ <div className="space-y-2">
23
+ <Skeleton className="h-4 w-28" />
24
+ <Skeleton className="h-3 w-40" />
25
+ </div>
26
+ </div>
27
+ <div className="space-y-2">
28
+ <Skeleton className="h-4 w-20" />
29
+ <Skeleton className="h-10 w-full" />
30
+ </div>
31
+ <div className="space-y-2">
32
+ <Skeleton className="h-4 w-28" />
33
+ <Skeleton className="h-10 w-full" />
34
+ </div>
35
+ <Skeleton className="h-px w-full" />
36
+ <div className="space-y-3">
37
+ <Skeleton className="h-5 w-20" />
38
+ <Skeleton className="h-4 w-56" />
39
+ <Skeleton className="h-16 w-full rounded-lg" />
40
+ </div>
41
+ <div className="flex justify-end gap-3 border-t pt-6">
42
+ <Skeleton className="h-9 w-20" />
43
+ <Skeleton className="h-9 w-28" />
44
+ </div>
45
+ </div>
46
+ </div>
47
+ <div className="space-y-4">
48
+ <div className="rounded-lg border bg-card p-6">
49
+ <Skeleton className="mb-4 h-5 w-32" />
50
+ <div className="space-y-3">
51
+ <Skeleton className="h-4 w-full" />
52
+ <Skeleton className="h-4 w-full" />
53
+ <Skeleton className="h-4 w-full" />
54
+ </div>
55
+ </div>
56
+ <div className="rounded-lg border bg-card p-6">
57
+ <Skeleton className="mb-4 h-5 w-28" />
58
+ <div className="space-y-3">
59
+ <Skeleton className="h-4 w-full" />
60
+ <Skeleton className="h-4 w-full" />
61
+ <Skeleton className="h-4 w-full" />
62
+ </div>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ );
68
+ }
69
+
70
+ return (
71
+ <div className="flex flex-col gap-6 p-4 md:p-6 lg:p-8">
72
+ <div className="flex flex-col gap-2">
73
+ <h1 className="text-3xl font-bold tracking-tight">Profile</h1>
74
+ <p className="text-muted-foreground">
75
+ Manage your account information and preferences
76
+ </p>
77
+ </div>
78
+
79
+ <div className="grid gap-6 lg:grid-cols-3">
80
+ <div className="space-y-6 lg:col-span-2">
81
+ <ProfileForm user={user} />
82
+ </div>
83
+
84
+ <div className="space-y-4">
85
+ <div className="rounded-lg border bg-card p-6">
86
+ <h3 className="mb-4 text-lg font-semibold">Account Status</h3>
87
+ <div className="space-y-3 text-sm">
88
+ <div className="flex items-center justify-between">
89
+ <span className="text-muted-foreground">Status</span>
90
+ <span className="flex items-center gap-2">
91
+ <span className="h-2 w-2 rounded-full bg-emerald-500" />
92
+ Active
93
+ </span>
94
+ </div>
95
+ <div className="flex items-center justify-between">
96
+ <span className="text-muted-foreground">Role</span>
97
+ <span className="capitalize">{user.role || "User"}</span>
98
+ </div>
99
+ <div className="flex items-center justify-between">
100
+ <span className="text-muted-foreground">User ID</span>
101
+ <span className="truncate font-mono text-xs">
102
+ {user.id.slice(0, 8)}...{user.id.slice(-4)}
103
+ </span>
104
+ </div>
105
+ </div>
106
+ </div>
107
+
108
+ <div className="rounded-lg border bg-card p-6">
109
+ <h3 className="mb-4 text-lg font-semibold">Account Info</h3>
110
+ <div className="space-y-3 text-sm">
111
+ <div className="flex items-center justify-between">
112
+ <span className="text-muted-foreground">Member Since</span>
113
+ <span>
114
+ {user.createdAt
115
+ ? new Date(user.createdAt).toLocaleDateString("en-US", {
116
+ month: "short",
117
+ day: "numeric",
118
+ year: "numeric",
119
+ })
120
+ : "N/A"}
121
+ </span>
122
+ </div>
123
+ <div className="flex items-center justify-between">
124
+ <span className="text-muted-foreground">Email Verified</span>
125
+ <span className="flex items-center gap-1.5">
126
+ <span className="h-1.5 w-1.5 rounded-full bg-emerald-500" />
127
+ Verified
128
+ </span>
129
+ </div>
130
+ <div className="flex items-center justify-between">
131
+ <span className="text-muted-foreground">Last Login</span>
132
+ <span>Today</span>
133
+ </div>
134
+ </div>
135
+ </div>
136
+
137
+ <div className="rounded-lg border border-muted bg-muted/20 p-4">
138
+ <p className="text-xs text-muted-foreground">
139
+ 💡 <span className="font-medium">Tip:</span> Keep your profile
140
+ information up to date for better security and communication.
141
+ </p>
142
+ </div>
143
+ </div>
144
+ </div>
145
+ </div>
146
+ );
147
+ }