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.
- package/README.md +50 -42
- package/dist/cli/add.js +122 -56
- package/dist/cli/create.d.ts +2 -0
- package/dist/cli/create.js +271 -95
- package/dist/cli/doctor.js +1 -0
- package/dist/cli/list.d.ts +1 -1
- package/dist/cli/list.js +6 -4
- package/dist/index.js +234 -191
- package/dist/lib/constants.d.ts +4 -0
- package/dist/lib/constants.js +4 -0
- package/dist/lib/discovery/module-discovery.d.ts +4 -0
- package/dist/lib/discovery/module-discovery.js +56 -0
- package/dist/lib/generation/code-generator.d.ts +11 -2
- package/dist/lib/generation/code-generator.js +42 -3
- package/dist/lib/generation/generator-utils.js +3 -1
- package/dist/lib/pm/package-manager.js +16 -13
- package/dist/lib/ui/logger.js +3 -2
- package/dist/lib/utils/path-resolver.d.ts +2 -0
- package/dist/lib/utils/path-resolver.js +8 -0
- package/dist/meta.json +8312 -0
- package/modules/auth/better-auth/files/{shared → express}/config/env.ts +48 -52
- package/modules/auth/better-auth/files/express/middlewares/authorize.ts +20 -1
- package/modules/auth/better-auth/files/express/modules/auth.controller.ts +349 -0
- package/modules/auth/better-auth/files/express/modules/{auth/auth.route.ts → auth.route.ts} +12 -7
- package/modules/auth/better-auth/files/express/modules/auth.service.ts +664 -0
- package/modules/auth/better-auth/files/express/modules/{auth/auth.type.ts → auth.type.ts} +22 -9
- package/modules/auth/better-auth/files/{shared/mongoose/auth/constants.ts → express/mongo-modules/auth.constants.ts} +0 -1
- package/modules/auth/better-auth/files/{shared/mongoose/auth/helper.ts → express/mongo-modules/auth.helper.ts} +11 -1
- package/modules/auth/better-auth/files/express/types/express.d.ts +11 -0
- package/modules/auth/better-auth/files/nextjs/api-route.ts +74 -0
- package/modules/auth/better-auth/files/nextjs/dashboard/pages/(user)/page.tsx +6 -0
- package/modules/auth/better-auth/files/nextjs/dashboard/pages/admin/page.tsx +6 -0
- package/modules/auth/better-auth/files/nextjs/dashboard/pages/layout.tsx +48 -0
- package/modules/auth/better-auth/files/nextjs/dashboard/pages/my-profile/page.tsx +5 -0
- package/modules/auth/better-auth/files/nextjs/features/services/auth.service.ts +102 -0
- package/modules/auth/better-auth/files/nextjs/layout/layout.tsx +13 -0
- package/modules/auth/better-auth/files/nextjs/lib/axios/http.ts +158 -0
- package/modules/auth/better-auth/files/nextjs/lib/env.ts +35 -0
- package/modules/auth/better-auth/files/nextjs/lib/utils/auth.ts +75 -0
- package/modules/auth/better-auth/files/nextjs/lib/utils/cookie.ts +29 -0
- package/modules/auth/better-auth/files/nextjs/lib/utils/jwt.ts +28 -0
- package/modules/auth/better-auth/files/nextjs/lib/utils/token.ts +49 -0
- package/modules/auth/better-auth/files/nextjs/pages/forgot-password/page.tsx +5 -0
- package/modules/auth/better-auth/files/nextjs/pages/layout.tsx +11 -0
- package/modules/auth/better-auth/files/nextjs/pages/login/page.tsx +9 -0
- package/modules/auth/better-auth/files/nextjs/pages/register/page.tsx +5 -0
- package/modules/auth/better-auth/files/nextjs/pages/reset-password/page.tsx +10 -0
- package/modules/auth/better-auth/files/nextjs/pages/verify-email/page.tsx +10 -0
- package/modules/auth/better-auth/files/nextjs/proxy.ts +157 -22
- package/modules/auth/better-auth/files/nextjs/theme/providers/theme-provider.tsx +11 -0
- package/modules/auth/better-auth/files/nextjs/types/api.types.ts +18 -0
- package/modules/auth/better-auth/files/react/components/protected-route.tsx +39 -0
- package/modules/auth/better-auth/files/react/components/route-guards.tsx +13 -0
- package/modules/auth/better-auth/files/react/dashboard/admin/pages/overview.tsx +3 -0
- package/modules/auth/better-auth/files/react/dashboard/pages/overview.tsx +3 -0
- package/modules/auth/better-auth/files/react/features/pages/forgot-password.tsx +5 -0
- package/modules/auth/better-auth/files/react/features/pages/login.tsx +5 -0
- package/modules/auth/better-auth/files/react/features/pages/my-profile.tsx +5 -0
- package/modules/auth/better-auth/files/react/features/pages/oauth-callback.tsx +59 -0
- package/modules/auth/better-auth/files/react/features/pages/register.tsx +5 -0
- package/modules/auth/better-auth/files/react/features/pages/reset-password.tsx +10 -0
- package/modules/auth/better-auth/files/react/features/pages/verify-email.tsx +10 -0
- package/modules/auth/better-auth/files/react/layout/dashboard-layout.tsx +54 -0
- package/modules/auth/better-auth/files/react/lib/axios/http.ts +68 -0
- package/modules/auth/better-auth/files/react/lib/env.ts +25 -0
- package/modules/auth/better-auth/files/react/router.tsx +73 -0
- package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider-context.ts +13 -0
- package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider.tsx +51 -0
- package/modules/auth/better-auth/files/react/theme/hooks/use-theme.ts +8 -0
- package/modules/auth/better-auth/files/shared/features/components/change-password-dialog.tsx +113 -0
- package/modules/auth/better-auth/files/shared/features/components/forgot-password-form.tsx +84 -0
- package/modules/auth/better-auth/files/shared/features/components/login-form.tsx +134 -0
- package/modules/auth/better-auth/files/shared/features/components/my-profile.tsx +147 -0
- package/modules/auth/better-auth/files/shared/features/components/profile-form.tsx +205 -0
- package/modules/auth/better-auth/files/shared/features/components/register-form.tsx +100 -0
- package/modules/auth/better-auth/files/shared/features/components/reset-password-form.tsx +111 -0
- package/modules/auth/better-auth/files/shared/features/components/social-login-buttons.tsx +47 -0
- package/modules/auth/better-auth/files/shared/features/components/user-profile-menu.tsx +106 -0
- package/modules/auth/better-auth/files/shared/features/components/verify-email-form.tsx +110 -0
- package/modules/auth/better-auth/files/shared/features/queries/auth.mutations.tsx +312 -0
- package/modules/auth/better-auth/files/shared/features/queries/auth.querie.ts +19 -0
- package/modules/auth/better-auth/files/shared/features/services/auth.api.ts +81 -0
- package/modules/auth/better-auth/files/shared/features/types/auth.type.ts +47 -0
- package/modules/auth/better-auth/files/shared/features/validators/change-password.validator.ts +18 -0
- package/modules/auth/better-auth/files/shared/features/validators/forgot.validator.ts +7 -0
- package/modules/auth/better-auth/files/shared/features/validators/login.validator.ts +14 -0
- package/modules/auth/better-auth/files/shared/features/validators/profile.validator.ts +8 -0
- package/modules/auth/better-auth/files/shared/features/validators/register.validator.ts +9 -0
- package/modules/auth/better-auth/files/shared/features/validators/reset.validator.ts +9 -0
- package/modules/auth/better-auth/files/shared/features/validators/verify.validator.ts +8 -0
- package/modules/auth/better-auth/files/shared/lib/auth-client.ts +2 -1
- package/modules/auth/better-auth/files/shared/lib/auth.ts +10 -29
- package/modules/auth/better-auth/files/shared/lib/constant/dashboard.ts +90 -0
- package/modules/auth/better-auth/files/shared/prisma/enums.prisma +0 -1
- package/modules/auth/better-auth/files/shared/theme/mode-toggle.tsx +30 -0
- package/modules/auth/better-auth/files/shared/ui/shadcn/components/dashboard/dashboard-header.tsx +94 -0
- package/modules/auth/better-auth/files/shared/ui/shadcn/components/dashboard/dashboard-sidebar.tsx +255 -0
- package/modules/auth/better-auth/files/shared/ui/shadcn/components/footer.tsx +35 -0
- package/modules/auth/better-auth/files/shared/ui/shadcn/components/navbar.tsx +145 -0
- package/modules/auth/better-auth/files/shared/ui/shadcn/form-field/input-field.tsx +440 -0
- package/modules/auth/better-auth/files/shared/utils/email.ts +20 -18
- package/modules/auth/better-auth/generator.json +174 -53
- package/modules/auth/better-auth/module.json +2 -2
- package/modules/components/files/shared/hooks/use-file-upload.ts +412 -0
- package/modules/components/files/shared/lib/utils/url-helpers.ts +110 -0
- package/modules/components/files/shared/shadcn/dashboard/data-table-column-selector.tsx +52 -0
- package/modules/components/files/shared/shadcn/dashboard/data-table-footer.tsx +156 -0
- package/modules/components/files/shared/shadcn/dashboard/data-table.tsx +405 -0
- package/modules/components/files/shared/shadcn/global/form-field/input-field.tsx +440 -0
- package/modules/components/files/shared/shadcn/global/form-field/media-uploader-field.tsx +745 -0
- package/modules/components/files/shared/shadcn/global/form-field/multi-select-field.tsx +207 -0
- package/modules/components/files/shared/shadcn/global/form-field/select-field.tsx +247 -0
- package/modules/components/files/shared/shadcn/global/form-field/textarea-field.tsx +277 -0
- package/modules/components/files/shared/shadcn/global/form-field/tiptap-editor-field.tsx +35 -0
- package/modules/components/files/shared/shadcn/global/no-results.tsx +41 -0
- package/modules/components/files/shared/shadcn/tiptap-editor/editor-menu-bar.tsx +217 -0
- package/modules/components/files/shared/shadcn/tiptap-editor/tiptap-editor.tsx +104 -0
- package/modules/components/files/shared/url/load-more.tsx +93 -0
- package/modules/components/files/shared/url/search-bar.tsx +131 -0
- package/modules/components/files/shared/url/sort-select.tsx +118 -0
- package/modules/components/files/shared/url/url-tabs.tsx +77 -0
- package/modules/components/generator.json +109 -0
- package/modules/components/module.json +11 -0
- package/modules/database/mongoose/generator.json +3 -14
- package/modules/database/mongoose/module.json +2 -2
- package/modules/database/prisma/generator.json +6 -12
- package/modules/database/prisma/module.json +2 -2
- package/modules/storage/cloudinary/files/express/config/env.ts +65 -0
- package/modules/storage/cloudinary/files/express/config/media.ts +103 -0
- package/modules/storage/cloudinary/files/express/modules/media/media.controller.ts +59 -0
- package/modules/storage/cloudinary/files/express/modules/media/media.route.ts +29 -0
- package/modules/storage/cloudinary/files/express/modules/media/media.service.ts +113 -0
- package/modules/storage/cloudinary/files/express/modules/media/media.type.ts +32 -0
- package/modules/storage/cloudinary/generator.json +34 -0
- package/modules/storage/cloudinary/module.json +11 -0
- package/modules/ui/shadcn/generator.json +21 -0
- package/modules/ui/shadcn/module.json +11 -0
- package/package.json +24 -26
- package/templates/express/README.md +11 -16
- package/templates/express/src/config/env.ts +7 -5
- package/templates/nextjs/README.md +13 -18
- package/templates/nextjs/app/favicon.ico +0 -0
- package/templates/nextjs/app/layout.tsx +6 -4
- package/templates/nextjs/components/providers/query-provider.tsx +3 -0
- package/templates/nextjs/env.example +3 -1
- package/templates/nextjs/lib/axios/http.ts +23 -0
- package/templates/nextjs/lib/env.ts +7 -5
- package/templates/nextjs/package.json +2 -1
- package/templates/nextjs/template.json +1 -2
- package/templates/react/README.md +9 -14
- package/templates/react/index.html +1 -1
- package/templates/react/package.json +1 -1
- package/templates/react/src/assets/favicon.ico +0 -0
- package/templates/react/src/components/providers/query-provider.tsx +38 -0
- package/templates/react/src/{shared/components → components}/seo.tsx +4 -8
- package/templates/react/src/lib/axios/http.ts +24 -0
- package/templates/react/src/main.tsx +8 -11
- package/templates/react/src/{features/about/pages → pages}/about.tsx +1 -1
- package/templates/react/src/{features/home/pages → pages}/home.tsx +1 -1
- package/templates/react/src/router.tsx +6 -6
- package/templates/react/src/vite-env.d.ts +2 -1
- package/templates/react/template.json +0 -1
- package/templates/react/tsconfig.app.json +6 -0
- package/templates/react/tsconfig.json +7 -1
- package/templates/react/vite.config.ts +12 -0
- package/modules/auth/authjs/files/nextjs/api/auth/[...nextauth]/route.ts +0 -3
- package/modules/auth/authjs/files/nextjs/proxy.ts +0 -1
- package/modules/auth/authjs/files/shared/lib/auth.ts +0 -119
- package/modules/auth/authjs/files/shared/prisma/schema.prisma +0 -61
- package/modules/auth/authjs/generator.json +0 -64
- package/modules/auth/authjs/module.json +0 -13
- package/modules/auth/better-auth/files/express/modules/auth/auth.controller.ts +0 -264
- package/modules/auth/better-auth/files/express/modules/auth/auth.service.ts +0 -537
- package/modules/auth/better-auth/files/express/templates/google-redirect.ejs +0 -24
- package/modules/auth/better-auth/files/nextjs/api/auth/[...all]/route.ts +0 -4
- package/modules/auth/better-auth/files/nextjs/lib/auth/auth-guards.ts +0 -41
- package/modules/auth/better-auth/files/nextjs/templates/email-otp.tsx +0 -74
- package/templates/express/node_modules/.bin/acorn +0 -17
- package/templates/express/node_modules/.bin/eslint +0 -17
- package/templates/express/node_modules/.bin/tsc +0 -17
- package/templates/express/node_modules/.bin/tsserver +0 -17
- package/templates/express/node_modules/.bin/tsx +0 -17
- package/templates/nextjs/lib/api/http.ts +0 -40
- package/templates/nextjs/next-env.d.ts +0 -6
- package/templates/react/dist/assets/index-D4AHT4dU.js +0 -193
- package/templates/react/dist/assets/index-rpwj5ZOX.css +0 -1
- package/templates/react/dist/index.html +0 -14
- package/templates/react/dist/vite.svg +0 -1
- package/templates/react/public/vite.svg +0 -1
- package/templates/react/src/app/layouts/dashboard-layout.tsx +0 -8
- package/templates/react/src/app/layouts/public-layout.tsx +0 -5
- package/templates/react/src/app/providers.tsx +0 -20
- package/templates/react/src/app/router.tsx +0 -21
- package/templates/react/src/assets/react.svg +0 -1
- package/templates/react/src/shared/api/http.ts +0 -39
- package/templates/react/src/shared/components/loading.tsx +0 -8
- package/templates/react/src/shared/lib/query-client.ts +0 -12
- package/templates/react/src/utils/storage.ts +0 -35
- package/templates/react/src/utils/utils.ts +0 -3
- /package/templates/nextjs/app/{page.tsx → (public)/(root)/page.tsx} +0 -0
- /package/templates/react/src/{shared/components → components}/error-boundary.tsx +0 -0
- /package/templates/react/src/{shared/components → components}/layout.tsx +0 -0
- /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;
|
package/modules/auth/better-auth/files/react/theme/components/providers/theme-provider-context.ts
ADDED
|
@@ -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
|
+
}
|