@explita/cloud-auth-client 0.0.1 → 0.1.0
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 +25 -2
- package/dist/components/change-password.js +13 -11
- package/dist/components/icons/lock.d.ts +4 -0
- package/dist/components/icons/lock.js +15 -0
- package/dist/components/icons/logout.d.ts +4 -0
- package/dist/components/icons/logout.js +11 -0
- package/dist/components/icons/setting.d.ts +4 -0
- package/dist/components/icons/setting.js +12 -0
- package/dist/components/login-form.js +3 -4
- package/dist/components/optional-otp.js +2 -2
- package/dist/components/reset-password.js +15 -16
- package/dist/components/settings.js +4 -1
- package/dist/components/signup-form.d.ts +3 -3
- package/dist/components/signup-form.js +20 -21
- package/dist/components/toggle-2fa.js +3 -3
- package/dist/components/toggle-account-status.js +18 -11
- package/dist/components/ui/dialog.js +1 -1
- package/dist/components/user-card.js +22 -9
- package/dist/contexts/auth-provider.js +23 -18
- package/dist/hooks/use-token-refresher.js +11 -11
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -1
- package/dist/lib/api-client.js +39 -24
- package/dist/lib/api-server.js +5 -4
- package/dist/lib/constants.d.ts +10 -0
- package/dist/lib/constants.js +13 -0
- package/dist/lib/error.js +1 -1
- package/dist/lib/refresh-helper.d.ts +0 -6
- package/dist/lib/refresh-helper.js +12 -22
- package/dist/lib/utils.d.ts +3 -1
- package/dist/lib/utils.js +19 -14
- package/dist/server/index.d.ts +3 -2
- package/dist/server/index.js +5 -2
- package/dist/server/{cookie.js → next-cookie-override.js} +3 -3
- package/dist/server/reset-password.js +7 -0
- package/dist/server/role.d.ts +2 -4
- package/dist/server/role.js +13 -1
- package/dist/server/server-session.js +5 -0
- package/dist/server/server-token.js +5 -5
- package/dist/server/toggle-2fa.js +4 -0
- package/dist/server/{users-accounts.d.ts → user.d.ts} +3 -5
- package/dist/server/{users-accounts.js → user.js} +16 -4
- package/dist/styles.css +7 -1
- package/dist/types.d.ts +21 -87
- package/package.json +9 -9
- /package/dist/components/{x-icon.d.ts → icons/x-icon.d.ts} +0 -0
- /package/dist/components/{x-icon.js → icons/x-icon.js} +0 -0
- /package/dist/server/{cookie.d.ts → next-cookie-override.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -78,7 +78,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
|
|
78
78
|
loginPath: "/login", //default is /login
|
|
79
79
|
signupPath: "/signup", //default is /signup
|
|
80
80
|
resetPasswordPath: "/reset-password", //default is /reset-password
|
|
81
|
-
cookieOverride: cookieOverrideHandler, //included in the server-side functions and only needed if your app
|
|
81
|
+
cookieOverride: cookieOverrideHandler, //included in the server-side functions and only needed if your app needs to use anything from @explita/cloud-auth-client/server, e.g: getServerSession() - only in Next.js
|
|
82
82
|
disableLoading: true, //default is false
|
|
83
83
|
excludedPaths: [], //array of strings of relative paths, all the paths mentioned above are already excluded.
|
|
84
84
|
}}
|
|
@@ -165,7 +165,7 @@ LoggedOut; //check if user is logged out
|
|
|
165
165
|
```typescript
|
|
166
166
|
"use server";
|
|
167
167
|
|
|
168
|
-
import {
|
|
168
|
+
import type {
|
|
169
169
|
NewRole,
|
|
170
170
|
ResetPasswordWithToken,
|
|
171
171
|
ResetPasswordWithUserId,
|
|
@@ -227,6 +227,29 @@ export async function assignPermissions(roleId: string, permissions: string[]) {
|
|
|
227
227
|
|
|
228
228
|
---
|
|
229
229
|
|
|
230
|
+
### Using Node/Express Backend
|
|
231
|
+
|
|
232
|
+
If you're using a Node.js/Express backend and need to authenticate requests between your frontend and backend, use [cloud-auth-express](https://www.npmjs.com/package/@explita/cloud-auth-express) to verify tokens server-side.
|
|
233
|
+
|
|
234
|
+
Make sure your frontend sends the token along in each request:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { getToken } from "@explita/cloud-auth-client";
|
|
238
|
+
|
|
239
|
+
const response = await fetch("/api/your-route", {
|
|
240
|
+
method: "POST",
|
|
241
|
+
headers: {
|
|
242
|
+
Authorization: `Bearer ${getToken()}`,
|
|
243
|
+
},
|
|
244
|
+
body: JSON.stringify(data),
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
> ⚠️ **Note:** This section only applies if you're doing separate frontend/backend communication.
|
|
249
|
+
> If you're building a fullstack app (e.g. with Next.js using Server Actions or API routes), you don't need to handle this manually — it's already managed internally by cloud-auth-client.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
230
253
|
### Getting started
|
|
231
254
|
|
|
232
255
|
Sign up for an account at [Explita Cloud](https://cloud.explita.ng), create a project, add auth service and get your API key from the dashboard and add it to your environment variables.
|
|
@@ -44,7 +44,6 @@ const message_1 = require("./message");
|
|
|
44
44
|
const must_login_1 = require("./must-login");
|
|
45
45
|
const utils_1 = require("../lib/utils");
|
|
46
46
|
function ChangePassword({ user, open, onOpenChange, onSubmit, onSuccess, }) {
|
|
47
|
-
var _a, _b;
|
|
48
47
|
const { user: loggedInUser } = (0, auth_provider_1.useAuth)();
|
|
49
48
|
const [state, handleSubmit, isPending] = (0, react_1.useActionState)(async function (prevState, formData) {
|
|
50
49
|
if (!onSubmit || typeof onSubmit !== "function") {
|
|
@@ -55,11 +54,11 @@ function ChangePassword({ user, open, onOpenChange, onSubmit, onSuccess, }) {
|
|
|
55
54
|
}
|
|
56
55
|
try {
|
|
57
56
|
const result = await onSubmit({
|
|
58
|
-
userId:
|
|
57
|
+
userId: user?.id || loggedInUser.id,
|
|
59
58
|
password: formData.get("password"),
|
|
60
59
|
confirmPassword: formData.get("confirmPassword"),
|
|
61
60
|
});
|
|
62
|
-
onSuccess
|
|
61
|
+
onSuccess?.();
|
|
63
62
|
return result;
|
|
64
63
|
}
|
|
65
64
|
catch (error) {
|
|
@@ -70,7 +69,10 @@ function ChangePassword({ user, open, onOpenChange, onSubmit, onSuccess, }) {
|
|
|
70
69
|
onOpenChange(state);
|
|
71
70
|
(0, utils_1.unstuckPointerEvents)();
|
|
72
71
|
} },
|
|
73
|
-
react_1.default.createElement(dialog_1.DialogContent, null, !loggedInUser ? (react_1.default.createElement(must_login_1.MustLogin, { onOpenChange:
|
|
72
|
+
react_1.default.createElement(dialog_1.DialogContent, null, !loggedInUser ? (react_1.default.createElement(must_login_1.MustLogin, { onOpenChange: (state) => {
|
|
73
|
+
onOpenChange(state);
|
|
74
|
+
(0, utils_1.unstuckPointerEvents)();
|
|
75
|
+
} })) : (react_1.default.createElement("form", { action: handleSubmit },
|
|
74
76
|
react_1.default.createElement(dialog_1.DialogHeader, null,
|
|
75
77
|
!user || loggedInUser.id === user.id ? (react_1.default.createElement(dialog_1.DialogTitle, null, "Change your password")) : (react_1.default.createElement(dialog_1.DialogTitle, null,
|
|
76
78
|
"Change password for ",
|
|
@@ -78,22 +80,22 @@ function ChangePassword({ user, open, onOpenChange, onSubmit, onSuccess, }) {
|
|
|
78
80
|
" ",
|
|
79
81
|
user.lastName)),
|
|
80
82
|
react_1.default.createElement(dialog_1.DialogDescription, null, "Please enter the new password"),
|
|
81
|
-
user && loggedInUser.id !==
|
|
83
|
+
user && loggedInUser.id !== user?.id && (react_1.default.createElement(dialog_1.DialogDescription, { className: "ecpauth:mt-2 ecpauth:text-base" },
|
|
82
84
|
"Username: ",
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
user?.username || user?.email))),
|
|
86
|
+
state?.status === "success" ? (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:mt-2 ecpauth:font-semibold ecpauth:text-base", variant: "success" })) : (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:mt-2" })),
|
|
85
87
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:flex-col ecpauth:gap-6 ecpauth:my-5" },
|
|
86
88
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
87
89
|
react_1.default.createElement(label_1.Label, { htmlFor: "password" }, "New Password"),
|
|
88
90
|
react_1.default.createElement(input_1.Input, { id: "password", type: "password", name: "password" }),
|
|
89
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
90
|
-
?
|
|
91
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
92
|
+
? state?.errors?.password
|
|
91
93
|
: "" })),
|
|
92
94
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
93
95
|
react_1.default.createElement(label_1.Label, { htmlFor: "confirmPassword" }, "Confirm New Password"),
|
|
94
96
|
react_1.default.createElement(input_1.Input, { id: "confirmPassword", type: "password", name: "confirmPassword" }),
|
|
95
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
96
|
-
?
|
|
97
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
98
|
+
? state?.errors?.confirmPassword
|
|
97
99
|
: "" }))),
|
|
98
100
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:justify-end ecpauth:gap-2" },
|
|
99
101
|
react_1.default.createElement(button_1.Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false) }, "Cancel"),
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.LockIcon = LockIcon;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
function LockIcon({ size = 24 }) {
|
|
9
|
+
return (react_1.default.createElement("svg", { stroke: "currentColor", fill: "none", strokeWidth: "2", viewBox: "0 0 24 24", strokeLinecap: "round", strokeLinejoin: "round", height: `${size}px`, width: `${size}px`, xmlns: "http://www.w3.org/2000/svg" },
|
|
10
|
+
react_1.default.createElement("path", { d: "M15 21h-8a2 2 0 0 1 -2 -2v-6a2 2 0 0 1 2 -2h10c.265 0 .518 .052 .75 .145" }),
|
|
11
|
+
react_1.default.createElement("path", { d: "M11 16a1 1 0 1 0 2 0a1 1 0 0 0 -2 0" }),
|
|
12
|
+
react_1.default.createElement("path", { d: "M8 11v-4a4 4 0 1 1 8 0v4" }),
|
|
13
|
+
react_1.default.createElement("path", { d: "M19 22v.01" }),
|
|
14
|
+
react_1.default.createElement("path", { d: "M19 19a2.003 2.003 0 0 0 .914 -3.782a1.98 1.98 0 0 0 -2.414 .483" })));
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.LogoutIcon = LogoutIcon;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
function LogoutIcon({ size = 24 }) {
|
|
9
|
+
return (react_1.default.createElement("svg", { stroke: "currentColor", fill: "currentColor", strokeWidth: "0", viewBox: "0 0 20 20", "aria-hidden": "true", height: `${size}px`, width: `${size}px`, xmlns: "http://www.w3.org/2000/svg" },
|
|
10
|
+
react_1.default.createElement("path", { fillRule: "evenodd", d: "M3 3a1 1 0 00-1 1v12a1 1 0 102 0V4a1 1 0 00-1-1zm10.293 9.293a1 1 0 001.414 1.414l3-3a1 1 0 000-1.414l-3-3a1 1 0 10-1.414 1.414L14.586 9H7a1 1 0 100 2h7.586l-1.293 1.293z", clipRule: "evenodd" })));
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SettingIcon = SettingIcon;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
function SettingIcon({ size = 24 }) {
|
|
9
|
+
return (react_1.default.createElement("svg", { stroke: "currentColor", fill: "currentColor", strokeWidth: "0", viewBox: "0 0 24 24", height: `${size}px`, width: `${size}px`, xmlns: "http://www.w3.org/2000/svg" },
|
|
10
|
+
react_1.default.createElement("path", { fill: "none", d: "M0 0h24v24H0z" }),
|
|
11
|
+
react_1.default.createElement("path", { d: "M17.41 6.59 15 5.5l2.41-1.09L18.5 2l1.09 2.41L22 5.5l-2.41 1.09L18.5 9l-1.09-2.41zm3.87 6.13L20.5 11l-.78 1.72-1.72.78 1.72.78.78 1.72.78-1.72L23 13.5l-1.72-.78zm-5.04 1.65 1.94 1.47-2.5 4.33-2.24-.94c-.2.13-.42.26-.64.37l-.3 2.4h-5l-.3-2.41c-.22-.11-.43-.23-.64-.37l-2.24.94-2.5-4.33 1.94-1.47c-.01-.11-.01-.24-.01-.36s0-.25.01-.37l-1.94-1.47 2.5-4.33 2.24.94c.2-.13.42-.26.64-.37L7.5 6h5l.3 2.41c.22.11.43.23.64.37l2.24-.94 2.5 4.33-1.94 1.47c.01.12.01.24.01.37s0 .24-.01.36zM13 14c0-1.66-1.34-3-3-3s-3 1.34-3 3 1.34 3 3 3 3-1.34 3-3z" })));
|
|
12
|
+
}
|
|
@@ -45,7 +45,6 @@ const auth_provider_1 = require("../contexts/auth-provider");
|
|
|
45
45
|
const loader_1 = require("./loader");
|
|
46
46
|
const message_1 = require("./message");
|
|
47
47
|
function Login({ className, ...props }) {
|
|
48
|
-
var _a, _b;
|
|
49
48
|
const { login, error, computedRouteContext, isAuthenticated } = (0, auth_provider_1.useAuth)();
|
|
50
49
|
const [authToken, setAuthToken] = react_1.default.useState(null);
|
|
51
50
|
const { resetPasswordUrl, signupUrl, loginUrl } = computedRouteContext;
|
|
@@ -86,14 +85,14 @@ function Login({ className, ...props }) {
|
|
|
86
85
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:flex-col ecpauth:gap-6" },
|
|
87
86
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
88
87
|
react_1.default.createElement(label_1.Label, { htmlFor: "email" }, "Email/Username"),
|
|
89
|
-
react_1.default.createElement(input_1.Input, { id: "email", type: "text", placeholder: "email@example.com", name: "email", defaultValue: state
|
|
90
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
88
|
+
react_1.default.createElement(input_1.Input, { id: "email", type: "text", placeholder: "email@example.com", name: "email", defaultValue: state?.email }),
|
|
89
|
+
react_1.default.createElement(message_1.Message, { message: error?.errors?.email })),
|
|
91
90
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
92
91
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:items-center" },
|
|
93
92
|
react_1.default.createElement(label_1.Label, { htmlFor: "password" }, "Password"),
|
|
94
93
|
resetPasswordUrl && (react_1.default.createElement("a", { href: resetPasswordUrl, className: "ecpauth:ml-auto ecpauth:inline-block ecpauth:text-sm ecpauth:underline-offset-4 ecpauth:hover:underline" }, "Forgot your password?"))),
|
|
95
94
|
react_1.default.createElement(input_1.Input, { id: "password", type: "password", name: "password" }),
|
|
96
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
95
|
+
react_1.default.createElement(message_1.Message, { message: error?.errors?.password })),
|
|
97
96
|
react_1.default.createElement(button_1.Button, { type: "submit", className: "ecpauth:w-full ecpauth:cursor-pointer", disabled: isPending }, isPending ? "Logging in..." : "Login")),
|
|
98
97
|
signupUrl && (react_1.default.createElement("div", { className: "ecpauth:mt-4 ecpauth:text-center ecpauth:text-sm" },
|
|
99
98
|
"Don't have an account?",
|
|
@@ -55,10 +55,10 @@ function OptionalOTPInner({ user, onVerified, onCanceled }) {
|
|
|
55
55
|
return null; // or a fallback UI
|
|
56
56
|
}
|
|
57
57
|
const otp = useOTP({
|
|
58
|
-
referenceId: user
|
|
58
|
+
referenceId: user?.id,
|
|
59
59
|
onVerified,
|
|
60
60
|
onCanceled,
|
|
61
|
-
sentTo: user
|
|
61
|
+
sentTo: user?.email,
|
|
62
62
|
channel: "email",
|
|
63
63
|
});
|
|
64
64
|
(0, react_1.useEffect)(() => {
|
|
@@ -44,7 +44,6 @@ const label_1 = require("./ui/label");
|
|
|
44
44
|
const message_1 = require("./message");
|
|
45
45
|
const auth_provider_1 = require("../contexts/auth-provider");
|
|
46
46
|
function ResetPassword({ className, onChangePassword, ...props }) {
|
|
47
|
-
var _a;
|
|
48
47
|
const { sendPasswordResetRequest, computedRouteContext } = (0, auth_provider_1.useAuth)();
|
|
49
48
|
const [token, setToken] = (0, react_1.useState)(null);
|
|
50
49
|
const { loginUrl } = computedRouteContext;
|
|
@@ -75,33 +74,33 @@ function ResetPassword({ className, onChangePassword, ...props }) {
|
|
|
75
74
|
console.log(error);
|
|
76
75
|
}
|
|
77
76
|
}, undefined);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}, [state
|
|
77
|
+
// useEffect(() => {
|
|
78
|
+
// let timer: NodeJS.Timeout;
|
|
79
|
+
// if (state?.status === "success") {
|
|
80
|
+
// if (loginUrl) {
|
|
81
|
+
// timer = setTimeout(() => {
|
|
82
|
+
// window.location.href = loginUrl;
|
|
83
|
+
// }, 2000);
|
|
84
|
+
// }
|
|
85
|
+
// }
|
|
86
|
+
// return () => {
|
|
87
|
+
// clearTimeout(timer);
|
|
88
|
+
// };
|
|
89
|
+
// }, [state?.status]);
|
|
91
90
|
return (react_1.default.createElement("div", { className: (0, utils_1.cn)("ecpauth:flex ecpauth:flex-col ecpauth:gap-6 ecpauth:w-full ecpauth:max-w-sm", className), ...props },
|
|
92
91
|
react_1.default.createElement(card_1.Card, null,
|
|
93
92
|
react_1.default.createElement(card_1.CardHeader, null,
|
|
94
93
|
react_1.default.createElement(card_1.CardTitle, { className: "ecpauth:text-2xl" }, "Reset Password"),
|
|
95
94
|
react_1.default.createElement(card_1.CardDescription, null, "Provide your details to reset your password.")),
|
|
96
95
|
react_1.default.createElement(card_1.CardContent, null,
|
|
97
|
-
|
|
96
|
+
state?.status === "success" ? (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:mb-4 ecpauth:font-semibold ecpauth:text-base", variant: "success" })) : (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:mb-4" })),
|
|
98
97
|
!token ? (react_1.default.createElement("form", { action: handleSubmit },
|
|
99
98
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:flex-col ecpauth:gap-6" },
|
|
100
99
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
101
100
|
react_1.default.createElement(label_1.Label, { htmlFor: "email" }, "Email"),
|
|
102
101
|
react_1.default.createElement(input_1.Input, { id: "email", type: "email", placeholder: "email@example.com", name: "email", defaultValue:
|
|
103
102
|
//@ts-ignore
|
|
104
|
-
|
|
103
|
+
state?.form?.email })),
|
|
105
104
|
react_1.default.createElement(button_1.Button, { type: "submit", className: "ecpauth:w-full ecpauth:cursor-pointer", disabled: isPending }, "Send Reset Password Link")))) : (react_1.default.createElement("form", { action: handleSubmit },
|
|
106
105
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:flex-col ecpauth:gap-6" },
|
|
107
106
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
@@ -16,7 +16,10 @@ function Settings({ open, onOpenChange, onToggle2FA }) {
|
|
|
16
16
|
onOpenChange(state);
|
|
17
17
|
(0, utils_1.unstuckPointerEvents)();
|
|
18
18
|
} },
|
|
19
|
-
react_1.default.createElement(dialog_1.DialogContent, null, !user ? (react_1.default.createElement(must_login_1.MustLogin, { onOpenChange:
|
|
19
|
+
react_1.default.createElement(dialog_1.DialogContent, null, !user ? (react_1.default.createElement(must_login_1.MustLogin, { onOpenChange: (state) => {
|
|
20
|
+
onOpenChange(state);
|
|
21
|
+
(0, utils_1.unstuckPointerEvents)();
|
|
22
|
+
} })) : (react_1.default.createElement("div", { className: "ecpauth:relative" },
|
|
20
23
|
react_1.default.createElement(dialog_1.DialogHeader, { className: "ecpauth:border-b ecpauth:border-gray-200 ecpauth:dark:border-gray-800 ecpauth:pb-2" },
|
|
21
24
|
react_1.default.createElement(dialog_1.DialogTitle, { className: "ecpauth:flex ecpauth:items-center ecpauth:justify-between" },
|
|
22
25
|
react_1.default.createElement("span", { className: "ecpauth:text-stone-800 ecpauth:dark:text-stone-50" }, "Account Settings")),
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { CreateUser, FormResponse, User } from "../types";
|
|
3
3
|
type Props = {
|
|
4
4
|
className?: string;
|
|
5
|
-
onSubmit: (data:
|
|
6
|
-
groupId?: string;
|
|
5
|
+
onSubmit: (data: CreateUser) => Promise<FormResponse<User>>;
|
|
6
|
+
groupId?: string | null;
|
|
7
7
|
metaData?: Record<string, string>;
|
|
8
8
|
acceptUsername?: boolean;
|
|
9
9
|
};
|
|
@@ -44,7 +44,6 @@ const label_1 = require("./ui/label");
|
|
|
44
44
|
const message_1 = require("./message");
|
|
45
45
|
const auth_provider_1 = require("../contexts/auth-provider");
|
|
46
46
|
function Signup({ className, onSubmit, groupId, metaData, acceptUsername = true, ...props }) {
|
|
47
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
48
47
|
const { computedRouteContext } = (0, auth_provider_1.useAuth)();
|
|
49
48
|
const { loginUrl } = computedRouteContext;
|
|
50
49
|
const [state, handleSubmit, isPending] = (0, react_1.useActionState)(async function (prevState, formData) {
|
|
@@ -76,59 +75,59 @@ function Signup({ className, onSubmit, groupId, metaData, acceptUsername = true,
|
|
|
76
75
|
}, undefined);
|
|
77
76
|
(0, react_1.useEffect)(() => {
|
|
78
77
|
let timer;
|
|
79
|
-
if (
|
|
78
|
+
if (state?.status === "success" && loginUrl) {
|
|
80
79
|
timer = setTimeout(() => {
|
|
81
|
-
window.location.href = `${loginUrl}?authToken=${state
|
|
80
|
+
window.location.href = `${loginUrl}?authToken=${state?.authToken}`;
|
|
82
81
|
}, 2000);
|
|
83
82
|
}
|
|
84
83
|
return () => {
|
|
85
84
|
clearTimeout(timer);
|
|
86
85
|
};
|
|
87
|
-
}, [state
|
|
86
|
+
}, [state?.status, loginUrl]);
|
|
88
87
|
return (react_1.default.createElement("div", { className: (0, utils_1.cn)("ecpauth:flex ecpauth:flex-col ecpauth:gap-6 ecpauth:w-full ecpauth:max-w-sm", className), ...props },
|
|
89
88
|
react_1.default.createElement(card_1.Card, null,
|
|
90
89
|
react_1.default.createElement(card_1.CardHeader, null,
|
|
91
90
|
react_1.default.createElement(card_1.CardTitle, { className: "ecpauth:text-2xl" }, "Sign Up"),
|
|
92
91
|
react_1.default.createElement(card_1.CardDescription, null, "Provide your details to sign up from your account.")),
|
|
93
92
|
react_1.default.createElement(card_1.CardContent, null,
|
|
94
|
-
|
|
93
|
+
state?.status === "success" ? (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:mb-4 ecpauth:font-semibold ecpauth:text-lg", variant: "success" })) : (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:mb-4" })),
|
|
95
94
|
react_1.default.createElement("form", { action: handleSubmit },
|
|
96
95
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:flex-col ecpauth:gap-6" },
|
|
97
96
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
98
97
|
react_1.default.createElement(label_1.Label, { htmlFor: "firstName" }, "First Name"),
|
|
99
|
-
react_1.default.createElement(input_1.Input, { id: "firstName", type: "text", placeholder: "First Name", name: "firstName", defaultValue:
|
|
100
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
101
|
-
?
|
|
98
|
+
react_1.default.createElement(input_1.Input, { id: "firstName", type: "text", placeholder: "First Name", name: "firstName", defaultValue: state?.form?.firstName }),
|
|
99
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
100
|
+
? state?.errors?.firstName
|
|
102
101
|
: "" })),
|
|
103
102
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
104
103
|
react_1.default.createElement(label_1.Label, { htmlFor: "lastName" }, "Last Name"),
|
|
105
|
-
react_1.default.createElement(input_1.Input, { id: "lastName", type: "text", placeholder: "Last Name", name: "lastName", defaultValue:
|
|
106
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
107
|
-
?
|
|
104
|
+
react_1.default.createElement(input_1.Input, { id: "lastName", type: "text", placeholder: "Last Name", name: "lastName", defaultValue: state?.form?.lastName }),
|
|
105
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
106
|
+
? state?.errors?.lastName
|
|
108
107
|
: "" })),
|
|
109
108
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
110
109
|
react_1.default.createElement(label_1.Label, { htmlFor: "email" }, "Email"),
|
|
111
|
-
react_1.default.createElement(input_1.Input, { id: "email", type: "email", placeholder: "email@example.com", name: "email", defaultValue:
|
|
112
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
113
|
-
?
|
|
110
|
+
react_1.default.createElement(input_1.Input, { id: "email", type: "email", placeholder: "email@example.com", name: "email", defaultValue: state?.form?.email }),
|
|
111
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
112
|
+
? state?.errors?.email
|
|
114
113
|
: "" })),
|
|
115
114
|
acceptUsername && (react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
116
115
|
react_1.default.createElement(label_1.Label, { htmlFor: "username" }, "Username"),
|
|
117
|
-
react_1.default.createElement(input_1.Input, { id: "username", type: "text", placeholder: "Username", name: "username", defaultValue:
|
|
118
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
119
|
-
?
|
|
116
|
+
react_1.default.createElement(input_1.Input, { id: "username", type: "text", placeholder: "Username", name: "username", defaultValue: state?.form?.username }),
|
|
117
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
118
|
+
? state?.errors?.username
|
|
120
119
|
: "" }))),
|
|
121
120
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
122
121
|
react_1.default.createElement(label_1.Label, { htmlFor: "password" }, "Password"),
|
|
123
122
|
react_1.default.createElement(input_1.Input, { id: "password", type: "password", name: "password" }),
|
|
124
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
125
|
-
?
|
|
123
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
124
|
+
? state?.errors?.password
|
|
126
125
|
: "" })),
|
|
127
126
|
react_1.default.createElement("div", { className: "ecpauth:grid ecpauth:gap-2" },
|
|
128
127
|
react_1.default.createElement(label_1.Label, { htmlFor: "confirmPassword" }, "Confirm Password"),
|
|
129
128
|
react_1.default.createElement(input_1.Input, { id: "confirmPassword", type: "password", name: "confirmPassword" }),
|
|
130
|
-
react_1.default.createElement(message_1.Message, { message:
|
|
131
|
-
?
|
|
129
|
+
react_1.default.createElement(message_1.Message, { message: state?.status === "validation-error"
|
|
130
|
+
? state?.errors?.confirmPassword
|
|
132
131
|
: "" })),
|
|
133
132
|
react_1.default.createElement(button_1.Button, { type: "submit", className: "ecpauth:w-full ecpauth:cursor-pointer", disabled: isPending }, isPending ? "Signing up..." : "Sign Up")),
|
|
134
133
|
loginUrl && (react_1.default.createElement("div", { className: "ecpauth:mt-4 ecpauth:text-center ecpauth:text-sm" },
|
|
@@ -64,15 +64,15 @@ function Toggle2FA({ onSubmit }) {
|
|
|
64
64
|
return (react_1.default.createElement("div", { className: "ecpauth:text-stone-800 ecpauth:dark:text-stone-50" },
|
|
65
65
|
isPending && react_1.default.createElement(loader_1.Loader, null),
|
|
66
66
|
react_1.default.createElement("p", { className: "ecpauth:font-semibold ecpauth:text-lg ecpauth:mb-2" }, "Toggle Two Factor Authentication"),
|
|
67
|
-
|
|
67
|
+
state?.status === "success" ? (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:my-2 ecpauth:font-semibold ecpauth:text-base", variant: "success" })) : (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:my-2" })),
|
|
68
68
|
react_1.default.createElement("p", null,
|
|
69
69
|
"This action will enable or disable 2FA on your account. If enabled, you\u2019ll be asked for a code sent to ",
|
|
70
|
-
react_1.default.createElement("strong", null, user
|
|
70
|
+
react_1.default.createElement("strong", null, user?.email),
|
|
71
71
|
" during login."),
|
|
72
72
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:items-center ecpauth:justify-between ecpauth:py-3 ecpauth:my-3 ecpauth:border-y ecpauth:border-gray-200 ecpauth:dark:border-gray-800" },
|
|
73
73
|
react_1.default.createElement("p", { className: "ecpauth:flex-1 ecpauth:mb-0" }, "You can enable or disable your Two Factor Authentication"),
|
|
74
74
|
react_1.default.createElement("div", { className: "ecpauth:w-40 ecpauth:flex ecpauth:justify-end ecpauth:pe-3" },
|
|
75
|
-
react_1.default.createElement(switch_1.Switch, { checked: !!
|
|
75
|
+
react_1.default.createElement(switch_1.Switch, { checked: !!user?.with2fa, onCheckedChange: () => startTransition(handleSubmit), disabled: !canUseOtp }))),
|
|
76
76
|
!canUseOtp && (react_1.default.createElement("p", { className: "ecpauth:text-red-500 ecpauth:my-2 ecpauth:text-sm" },
|
|
77
77
|
"2FA is not available. Please install",
|
|
78
78
|
" ",
|
|
@@ -40,6 +40,7 @@ const button_1 = require("./ui/button");
|
|
|
40
40
|
const auth_provider_1 = require("../contexts/auth-provider");
|
|
41
41
|
const message_1 = require("./message");
|
|
42
42
|
const must_login_1 = require("./must-login");
|
|
43
|
+
const utils_1 = require("../lib/utils");
|
|
43
44
|
function ToggleAccountStatus({ user, open, onOpenChange, onSuccess, onSubmit, }) {
|
|
44
45
|
const { user: loggedInUser } = (0, auth_provider_1.useAuth)();
|
|
45
46
|
const [state, handleSubmit, isPending] = (0, react_1.useActionState)(async function () {
|
|
@@ -57,35 +58,41 @@ function ToggleAccountStatus({ user, open, onOpenChange, onSuccess, onSubmit, })
|
|
|
57
58
|
}
|
|
58
59
|
try {
|
|
59
60
|
const result = await onSubmit(user.id);
|
|
60
|
-
onSuccess
|
|
61
|
+
onSuccess?.();
|
|
61
62
|
return result;
|
|
62
63
|
}
|
|
63
64
|
catch (error) {
|
|
64
65
|
console.log(error);
|
|
65
66
|
}
|
|
66
67
|
}, undefined);
|
|
67
|
-
return (react_1.default.createElement(dialog_1.Dialog, { open: open, onOpenChange:
|
|
68
|
-
|
|
68
|
+
return (react_1.default.createElement(dialog_1.Dialog, { open: open, onOpenChange: (state) => {
|
|
69
|
+
onOpenChange(state);
|
|
70
|
+
(0, utils_1.unstuckPointerEvents)();
|
|
71
|
+
} },
|
|
72
|
+
react_1.default.createElement(dialog_1.DialogContent, null, !loggedInUser ? (react_1.default.createElement(must_login_1.MustLogin, { onOpenChange: (state) => {
|
|
73
|
+
onOpenChange(state);
|
|
74
|
+
(0, utils_1.unstuckPointerEvents)();
|
|
75
|
+
} })) : (react_1.default.createElement("form", { action: handleSubmit },
|
|
69
76
|
react_1.default.createElement(dialog_1.DialogHeader, null,
|
|
70
77
|
react_1.default.createElement(dialog_1.DialogTitle, null,
|
|
71
|
-
"Toggle account status (",
|
|
72
|
-
user
|
|
73
|
-
" ",
|
|
74
|
-
user
|
|
78
|
+
"Toggle account status (",
|
|
79
|
+
user?.firstName,
|
|
80
|
+
" ",
|
|
81
|
+
user?.lastName,
|
|
75
82
|
")"),
|
|
76
|
-
|
|
83
|
+
state?.status === "success" ? (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:my-2 ecpauth:font-semibold ecpauth:text-base", variant: "success" })) : (react_1.default.createElement(message_1.Message, { message: state?.message, className: "ecpauth:my-2" }))),
|
|
77
84
|
react_1.default.createElement(dialog_1.DialogDescription, { className: "ecpauth:flex ecpauth:flex-col ecpauth:gap-2 ecpauth:text-gray-700 ecpauth:dark:text-gray-50 ecpauth:mt-4" },
|
|
78
85
|
react_1.default.createElement("span", null,
|
|
79
86
|
"This action will",
|
|
80
87
|
" ",
|
|
81
|
-
react_1.default.createElement("strong", null,
|
|
88
|
+
react_1.default.createElement("strong", null, user?.isActive ? "suspend" : "unsuspend"),
|
|
82
89
|
" the user."),
|
|
83
90
|
react_1.default.createElement("span", null,
|
|
84
91
|
"You can always",
|
|
85
92
|
" ",
|
|
86
|
-
react_1.default.createElement("strong", null,
|
|
93
|
+
react_1.default.createElement("strong", null, user?.isActive ? "unsuspend" : "suspend"),
|
|
87
94
|
" the user if you change your mind.")),
|
|
88
95
|
react_1.default.createElement(dialog_1.DialogFooter, { className: "ecpauth:flex ecpauth:justify-center ecpauth:gap-2 ecpauth:mt-4" },
|
|
89
96
|
react_1.default.createElement(button_1.Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false) }, "Cancel"),
|
|
90
|
-
react_1.default.createElement(button_1.Button, { disabled: isPending, type: "submit" },
|
|
97
|
+
react_1.default.createElement(button_1.Button, { disabled: isPending, type: "submit" }, user?.isActive ? "Suspend" : "Unsuspend")))))));
|
|
91
98
|
}
|
|
@@ -47,7 +47,7 @@ exports.DialogTrigger = DialogTrigger;
|
|
|
47
47
|
const React = __importStar(require("react"));
|
|
48
48
|
const DialogPrimitive = __importStar(require("@radix-ui/react-dialog"));
|
|
49
49
|
const utils_1 = require("../../lib/utils");
|
|
50
|
-
const x_icon_1 = require("../x-icon");
|
|
50
|
+
const x_icon_1 = require("../icons/x-icon");
|
|
51
51
|
function Dialog({ ...props }) {
|
|
52
52
|
return React.createElement(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
|
|
53
53
|
}
|
|
@@ -39,8 +39,11 @@ const dropdown_menu_1 = require("./ui/dropdown-menu");
|
|
|
39
39
|
const change_password_1 = require("./change-password");
|
|
40
40
|
const auth_provider_1 = require("../contexts/auth-provider");
|
|
41
41
|
const settings_1 = require("./settings");
|
|
42
|
+
const setting_1 = require("./icons/setting");
|
|
43
|
+
const lock_1 = require("./icons/lock");
|
|
44
|
+
const logout_1 = require("./icons/logout");
|
|
42
45
|
function UserCard({ align = "start", onChangePassword, onToggle2FA, trigger, }) {
|
|
43
|
-
const { logout } = (0, auth_provider_1.useAuth)();
|
|
46
|
+
const { logout, user } = (0, auth_provider_1.useAuth)();
|
|
44
47
|
const [passwordOpen, setPasswordOpen] = (0, react_1.useState)(false);
|
|
45
48
|
const [settingsOpen, setSettingsOpen] = (0, react_1.useState)(false);
|
|
46
49
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
@@ -49,19 +52,29 @@ function UserCard({ align = "start", onChangePassword, onToggle2FA, trigger, })
|
|
|
49
52
|
react_1.default.createElement(dropdown_menu_1.DropdownMenu, null,
|
|
50
53
|
trigger ? (react_1.default.createElement(dropdown_menu_1.DropdownMenuTrigger, null, trigger)) : (react_1.default.createElement(Trigger, null)),
|
|
51
54
|
react_1.default.createElement(dropdown_menu_1.DropdownMenuContent, { className: "ecpauth:w-56 ecpauth:border-gray-200 ecpauth:dark:border-gray-800", align: align },
|
|
52
|
-
react_1.default.createElement(
|
|
53
|
-
|
|
55
|
+
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:flex-col ecpauth:items-center ecpauth:justify-center ecpauth:py-4" },
|
|
56
|
+
react_1.default.createElement("h2", { className: "ecpauth:text-lg ecpauth:font-semibold" }, `${user?.fullName}`),
|
|
57
|
+
react_1.default.createElement("p", { className: "ecpauth:text-sm ecpauth:text-muted-foreground" }, user?.role?.label ?? "-Global-")),
|
|
54
58
|
react_1.default.createElement(dropdown_menu_1.DropdownMenuSeparator, null),
|
|
55
|
-
react_1.default.createElement(dropdown_menu_1.DropdownMenuItem, { onClick: () =>
|
|
59
|
+
react_1.default.createElement(dropdown_menu_1.DropdownMenuItem, { onClick: () => setSettingsOpen(true) },
|
|
60
|
+
react_1.default.createElement(setting_1.SettingIcon, null),
|
|
61
|
+
"Settings"),
|
|
62
|
+
react_1.default.createElement(dropdown_menu_1.DropdownMenuItem, { onClick: () => setPasswordOpen(true) },
|
|
63
|
+
react_1.default.createElement(lock_1.LockIcon, null),
|
|
64
|
+
"Change Password"),
|
|
65
|
+
react_1.default.createElement(dropdown_menu_1.DropdownMenuSeparator, null),
|
|
66
|
+
react_1.default.createElement(dropdown_menu_1.DropdownMenuItem, { onClick: () => logout() },
|
|
67
|
+
react_1.default.createElement(logout_1.LogoutIcon, null),
|
|
68
|
+
"Logout")))));
|
|
56
69
|
}
|
|
57
70
|
function Trigger() {
|
|
58
71
|
const { user, authLoading } = (0, auth_provider_1.useAuth)();
|
|
59
72
|
return (react_1.default.createElement(dropdown_menu_1.DropdownMenuTrigger, { className: "ecpauth:flex ecpauth:items-center ecpauth:gap-2 ecpauth:p-1 ecpauth:pe-4 ecpauth:rounded-lg ecpauth:border ecpauth:border-gray-200 ecpauth:dark:border-gray-800 ecpauth:cursor-pointer ecpauth:bg-white ecpauth:hover:bg-gray-100 ecpauth:dark:bg-gray-800 ecpauth:dark:hover:bg-gray-800/80 ecpauth:dark:hover:text-white ecpauth:outline-none ecpauth:focus:outline-none" }, authLoading ? ("Please wait...") : (react_1.default.createElement(react_1.default.Fragment, null,
|
|
60
73
|
react_1.default.createElement("div", { className: "ecpauth:size-12 ecpauth:rounded-full ecpauth:bg-gray-200" }),
|
|
61
74
|
react_1.default.createElement("div", { className: "ecpauth:flex ecpauth:flex-col ecpauth:text-left" },
|
|
62
|
-
react_1.default.createElement("span", { className: "ecpauth:font-semibold ecpauth:text-stone-800 ecpauth:dark:text-stone-50" },
|
|
63
|
-
user
|
|
64
|
-
" ",
|
|
65
|
-
user
|
|
66
|
-
react_1.default.createElement("span", { className: "ecpauth:text-sm ecpauth:text-gray-500" }, user
|
|
75
|
+
react_1.default.createElement("span", { className: "ecpauth:font-semibold ecpauth:text-stone-800 ecpauth:dark:text-stone-50" },
|
|
76
|
+
user?.firstName,
|
|
77
|
+
" ",
|
|
78
|
+
user?.lastName),
|
|
79
|
+
react_1.default.createElement("span", { className: "ecpauth:text-sm ecpauth:text-gray-500" }, user?.email))))));
|
|
67
80
|
}
|