@forjio/auth-ui 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 +106 -0
- package/dist/AuthForm.cjs +220 -0
- package/dist/AuthForm.cjs.map +1 -0
- package/dist/AuthForm.d.cts +20 -0
- package/dist/AuthForm.d.ts +20 -0
- package/dist/AuthForm.js +186 -0
- package/dist/AuthForm.js.map +1 -0
- package/dist/ForgotPasswordForm.cjs +123 -0
- package/dist/ForgotPasswordForm.cjs.map +1 -0
- package/dist/ForgotPasswordForm.d.cts +9 -0
- package/dist/ForgotPasswordForm.d.ts +9 -0
- package/dist/ForgotPasswordForm.js +89 -0
- package/dist/ForgotPasswordForm.js.map +1 -0
- package/dist/ResetPasswordForm.cjs +128 -0
- package/dist/ResetPasswordForm.cjs.map +1 -0
- package/dist/ResetPasswordForm.d.cts +9 -0
- package/dist/ResetPasswordForm.d.ts +9 -0
- package/dist/ResetPasswordForm.js +94 -0
- package/dist/ResetPasswordForm.js.map +1 -0
- package/dist/index.cjs +38 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/types.cjs +35 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +23 -0
- package/dist/types.d.ts +23 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var ForgotPasswordForm_exports = {};
|
|
31
|
+
__export(ForgotPasswordForm_exports, {
|
|
32
|
+
ForgotPasswordForm: () => ForgotPasswordForm
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(ForgotPasswordForm_exports);
|
|
35
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
36
|
+
var import_react = require("react");
|
|
37
|
+
var import_link = __toESM(require("next/link"), 1);
|
|
38
|
+
var import_lucide_react = require("lucide-react");
|
|
39
|
+
var import_types = require("./types");
|
|
40
|
+
function ForgotPasswordForm({ endpoints } = {}) {
|
|
41
|
+
const ep = { ...import_types.defaultEndpoints, ...endpoints };
|
|
42
|
+
const [email, setEmail] = (0, import_react.useState)("");
|
|
43
|
+
const [submitting, setSubmitting] = (0, import_react.useState)(false);
|
|
44
|
+
const [sent, setSent] = (0, import_react.useState)(false);
|
|
45
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
46
|
+
async function submit(e) {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
setError(null);
|
|
49
|
+
setSubmitting(true);
|
|
50
|
+
try {
|
|
51
|
+
const res = await fetch(ep.forgotPassword, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: { "Content-Type": "application/json" },
|
|
54
|
+
body: JSON.stringify({ email })
|
|
55
|
+
});
|
|
56
|
+
if (!res.ok) {
|
|
57
|
+
const payload = await res.json().catch(() => null);
|
|
58
|
+
throw new Error(payload?.error?.message ?? `Request failed (${res.status})`);
|
|
59
|
+
}
|
|
60
|
+
setSent(true);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
setError(err.message);
|
|
63
|
+
} finally {
|
|
64
|
+
setSubmitting(false);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (sent) {
|
|
68
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-4", children: [
|
|
69
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-start gap-2 rounded-md border border-primary/30 bg-primary/10 px-3 py-2 text-sm text-foreground", children: [
|
|
70
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.CheckCircle2, { className: "h-4 w-4 shrink-0 mt-0.5 text-primary" }),
|
|
71
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
72
|
+
"If ",
|
|
73
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: email }),
|
|
74
|
+
" has a Huudis account, we\u2019ve sent a reset link. It expires in 1 hour."
|
|
75
|
+
] })
|
|
76
|
+
] }),
|
|
77
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_link.default, { href: "/login", className: "block text-center text-sm font-medium text-foreground hover:underline", children: "Back to sign in" })
|
|
78
|
+
] });
|
|
79
|
+
}
|
|
80
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: submit, className: "space-y-4", children: [
|
|
81
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: [
|
|
82
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.AlertCircle, { className: "h-4 w-4 shrink-0 mt-0.5" }),
|
|
83
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: error })
|
|
84
|
+
] }),
|
|
85
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
86
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Email" }),
|
|
87
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
88
|
+
"input",
|
|
89
|
+
{
|
|
90
|
+
type: "email",
|
|
91
|
+
required: true,
|
|
92
|
+
value: email,
|
|
93
|
+
onChange: (e) => setEmail(e.target.value),
|
|
94
|
+
autoComplete: "email",
|
|
95
|
+
autoFocus: true,
|
|
96
|
+
className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary"
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
] }),
|
|
100
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
101
|
+
"button",
|
|
102
|
+
{
|
|
103
|
+
type: "submit",
|
|
104
|
+
disabled: submitting,
|
|
105
|
+
className: "flex w-full items-center justify-center gap-2 rounded-md bg-primary py-2.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50",
|
|
106
|
+
children: [
|
|
107
|
+
submitting && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader2, { className: "h-4 w-4 animate-spin" }),
|
|
108
|
+
submitting ? "Sending\u2026" : "Send reset link"
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
),
|
|
112
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "text-center text-xs text-muted-foreground", children: [
|
|
113
|
+
"Remembered it?",
|
|
114
|
+
" ",
|
|
115
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_link.default, { href: "/login", className: "font-medium text-foreground hover:underline", children: "Sign in" })
|
|
116
|
+
] })
|
|
117
|
+
] });
|
|
118
|
+
}
|
|
119
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
120
|
+
0 && (module.exports = {
|
|
121
|
+
ForgotPasswordForm
|
|
122
|
+
});
|
|
123
|
+
//# sourceMappingURL=ForgotPasswordForm.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ForgotPasswordForm.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport Link from 'next/link';\nimport { Loader2, AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { defaultEndpoints, type AuthEndpoints } from './types';\n\nexport interface ForgotPasswordFormProps {\n endpoints?: Partial<AuthEndpoints>;\n}\n\nexport function ForgotPasswordForm({ endpoints }: ForgotPasswordFormProps = {}) {\n const ep: AuthEndpoints = { ...defaultEndpoints, ...endpoints };\n const [email, setEmail] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [sent, setSent] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n async function submit(e: React.FormEvent) {\n e.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n const res = await fetch(ep.forgotPassword, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email }),\n });\n if (!res.ok) {\n const payload = (await res.json().catch(() => null)) as { error?: { message?: string } } | null;\n throw new Error(payload?.error?.message ?? `Request failed (${res.status})`);\n }\n setSent(true);\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setSubmitting(false);\n }\n }\n\n if (sent) {\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-start gap-2 rounded-md border border-primary/30 bg-primary/10 px-3 py-2 text-sm text-foreground\">\n <CheckCircle2 className=\"h-4 w-4 shrink-0 mt-0.5 text-primary\" />\n <span>\n If <strong>{email}</strong> has a Huudis account, we’ve sent a reset link. It expires in 1 hour.\n </span>\n </div>\n <Link href=\"/login\" className=\"block text-center text-sm font-medium text-foreground hover:underline\">\n Back to sign in\n </Link>\n </div>\n );\n }\n\n return (\n <form onSubmit={submit} className=\"space-y-4\">\n {error && (\n <div className=\"flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n <AlertCircle className=\"h-4 w-4 shrink-0 mt-0.5\" />\n <span>{error}</span>\n </div>\n )}\n <div>\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">Email</label>\n <input\n type=\"email\"\n required\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n autoComplete=\"email\"\n autoFocus\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n </div>\n <button\n type=\"submit\"\n disabled={submitting}\n className=\"flex w-full items-center justify-center gap-2 rounded-md bg-primary py-2.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50\"\n >\n {submitting && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {submitting ? 'Sending…' : 'Send reset link'}\n </button>\n <div className=\"text-center text-xs text-muted-foreground\">\n Remembered it?{' '}\n <Link href=\"/login\" className=\"font-medium text-foreground hover:underline\">\n Sign in\n </Link>\n </div>\n </form>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA4CU;AA1CV,mBAAyB;AACzB,kBAAiB;AACjB,0BAAmD;AACnD,mBAAqD;AAM9C,SAAS,mBAAmB,EAAE,UAAU,IAA6B,CAAC,GAAG;AAC9E,QAAM,KAAoB,EAAE,GAAG,+BAAkB,GAAG,UAAU;AAC9D,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAS,EAAE;AACrC,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAS,KAAK;AAClD,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAwB,IAAI;AAEtD,iBAAe,OAAO,GAAoB;AACxC,MAAE,eAAe;AACjB,aAAS,IAAI;AACb,kBAAc,IAAI;AAClB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,gBAAgB;AAAA,QACzC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,MAAM,CAAC;AAAA,MAChC,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,UAAW,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAClD,cAAM,IAAI,MAAM,SAAS,OAAO,WAAW,mBAAmB,IAAI,MAAM,GAAG;AAAA,MAC7E;AACA,cAAQ,IAAI;AAAA,IACd,SAAS,KAAK;AACZ,eAAU,IAAc,OAAO;AAAA,IACjC,UAAE;AACA,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,MAAI,MAAM;AACR,WACE,6CAAC,SAAI,WAAU,aACb;AAAA,mDAAC,SAAI,WAAU,8GACb;AAAA,oDAAC,oCAAa,WAAU,wCAAuC;AAAA,QAC/D,6CAAC,UAAK;AAAA;AAAA,UACD,4CAAC,YAAQ,iBAAM;AAAA,UAAS;AAAA,WAC7B;AAAA,SACF;AAAA,MACA,4CAAC,YAAAA,SAAA,EAAK,MAAK,UAAS,WAAU,yEAAwE,6BAEtG;AAAA,OACF;AAAA,EAEJ;AAEA,SACE,6CAAC,UAAK,UAAU,QAAQ,WAAU,aAC/B;AAAA,aACC,6CAAC,SAAI,WAAU,uHACb;AAAA,kDAAC,mCAAY,WAAU,2BAA0B;AAAA,MACjD,4CAAC,UAAM,iBAAM;AAAA,OACf;AAAA,IAEF,6CAAC,SACC;AAAA,kDAAC,WAAM,WAAU,wDAAuD,mBAAK;AAAA,MAC7E;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,UAAQ;AAAA,UACR,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,UACxC,cAAa;AAAA,UACb,WAAS;AAAA,UACT,WAAU;AAAA;AAAA,MACZ;AAAA,OACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,UAAU;AAAA,QACV,WAAU;AAAA,QAET;AAAA,wBAAc,4CAAC,+BAAQ,WAAU,wBAAuB;AAAA,UACxD,aAAa,kBAAa;AAAA;AAAA;AAAA,IAC7B;AAAA,IACA,6CAAC,SAAI,WAAU,6CAA4C;AAAA;AAAA,MAC1C;AAAA,MACf,4CAAC,YAAAA,SAAA,EAAK,MAAK,UAAS,WAAU,+CAA8C,qBAE5E;AAAA,OACF;AAAA,KACF;AAEJ;","names":["Link"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { AuthEndpoints } from './types.cjs';
|
|
3
|
+
|
|
4
|
+
interface ForgotPasswordFormProps {
|
|
5
|
+
endpoints?: Partial<AuthEndpoints>;
|
|
6
|
+
}
|
|
7
|
+
declare function ForgotPasswordForm({ endpoints }?: ForgotPasswordFormProps): react.JSX.Element;
|
|
8
|
+
|
|
9
|
+
export { ForgotPasswordForm, type ForgotPasswordFormProps };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { AuthEndpoints } from './types.js';
|
|
3
|
+
|
|
4
|
+
interface ForgotPasswordFormProps {
|
|
5
|
+
endpoints?: Partial<AuthEndpoints>;
|
|
6
|
+
}
|
|
7
|
+
declare function ForgotPasswordForm({ endpoints }?: ForgotPasswordFormProps): react.JSX.Element;
|
|
8
|
+
|
|
9
|
+
export { ForgotPasswordForm, type ForgotPasswordFormProps };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import Link from "next/link";
|
|
5
|
+
import { Loader2, AlertCircle, CheckCircle2 } from "lucide-react";
|
|
6
|
+
import { defaultEndpoints } from "./types";
|
|
7
|
+
function ForgotPasswordForm({ endpoints } = {}) {
|
|
8
|
+
const ep = { ...defaultEndpoints, ...endpoints };
|
|
9
|
+
const [email, setEmail] = useState("");
|
|
10
|
+
const [submitting, setSubmitting] = useState(false);
|
|
11
|
+
const [sent, setSent] = useState(false);
|
|
12
|
+
const [error, setError] = useState(null);
|
|
13
|
+
async function submit(e) {
|
|
14
|
+
e.preventDefault();
|
|
15
|
+
setError(null);
|
|
16
|
+
setSubmitting(true);
|
|
17
|
+
try {
|
|
18
|
+
const res = await fetch(ep.forgotPassword, {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers: { "Content-Type": "application/json" },
|
|
21
|
+
body: JSON.stringify({ email })
|
|
22
|
+
});
|
|
23
|
+
if (!res.ok) {
|
|
24
|
+
const payload = await res.json().catch(() => null);
|
|
25
|
+
throw new Error(payload?.error?.message ?? `Request failed (${res.status})`);
|
|
26
|
+
}
|
|
27
|
+
setSent(true);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
setError(err.message);
|
|
30
|
+
} finally {
|
|
31
|
+
setSubmitting(false);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (sent) {
|
|
35
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
36
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 rounded-md border border-primary/30 bg-primary/10 px-3 py-2 text-sm text-foreground", children: [
|
|
37
|
+
/* @__PURE__ */ jsx(CheckCircle2, { className: "h-4 w-4 shrink-0 mt-0.5 text-primary" }),
|
|
38
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
39
|
+
"If ",
|
|
40
|
+
/* @__PURE__ */ jsx("strong", { children: email }),
|
|
41
|
+
" has a Huudis account, we\u2019ve sent a reset link. It expires in 1 hour."
|
|
42
|
+
] })
|
|
43
|
+
] }),
|
|
44
|
+
/* @__PURE__ */ jsx(Link, { href: "/login", className: "block text-center text-sm font-medium text-foreground hover:underline", children: "Back to sign in" })
|
|
45
|
+
] });
|
|
46
|
+
}
|
|
47
|
+
return /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-4", children: [
|
|
48
|
+
error && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: [
|
|
49
|
+
/* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4 shrink-0 mt-0.5" }),
|
|
50
|
+
/* @__PURE__ */ jsx("span", { children: error })
|
|
51
|
+
] }),
|
|
52
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
53
|
+
/* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Email" }),
|
|
54
|
+
/* @__PURE__ */ jsx(
|
|
55
|
+
"input",
|
|
56
|
+
{
|
|
57
|
+
type: "email",
|
|
58
|
+
required: true,
|
|
59
|
+
value: email,
|
|
60
|
+
onChange: (e) => setEmail(e.target.value),
|
|
61
|
+
autoComplete: "email",
|
|
62
|
+
autoFocus: true,
|
|
63
|
+
className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary"
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
] }),
|
|
67
|
+
/* @__PURE__ */ jsxs(
|
|
68
|
+
"button",
|
|
69
|
+
{
|
|
70
|
+
type: "submit",
|
|
71
|
+
disabled: submitting,
|
|
72
|
+
className: "flex w-full items-center justify-center gap-2 rounded-md bg-primary py-2.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50",
|
|
73
|
+
children: [
|
|
74
|
+
submitting && /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }),
|
|
75
|
+
submitting ? "Sending\u2026" : "Send reset link"
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
),
|
|
79
|
+
/* @__PURE__ */ jsxs("div", { className: "text-center text-xs text-muted-foreground", children: [
|
|
80
|
+
"Remembered it?",
|
|
81
|
+
" ",
|
|
82
|
+
/* @__PURE__ */ jsx(Link, { href: "/login", className: "font-medium text-foreground hover:underline", children: "Sign in" })
|
|
83
|
+
] })
|
|
84
|
+
] });
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
ForgotPasswordForm
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=ForgotPasswordForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ForgotPasswordForm.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport Link from 'next/link';\nimport { Loader2, AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { defaultEndpoints, type AuthEndpoints } from './types';\n\nexport interface ForgotPasswordFormProps {\n endpoints?: Partial<AuthEndpoints>;\n}\n\nexport function ForgotPasswordForm({ endpoints }: ForgotPasswordFormProps = {}) {\n const ep: AuthEndpoints = { ...defaultEndpoints, ...endpoints };\n const [email, setEmail] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [sent, setSent] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n async function submit(e: React.FormEvent) {\n e.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n const res = await fetch(ep.forgotPassword, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email }),\n });\n if (!res.ok) {\n const payload = (await res.json().catch(() => null)) as { error?: { message?: string } } | null;\n throw new Error(payload?.error?.message ?? `Request failed (${res.status})`);\n }\n setSent(true);\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setSubmitting(false);\n }\n }\n\n if (sent) {\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-start gap-2 rounded-md border border-primary/30 bg-primary/10 px-3 py-2 text-sm text-foreground\">\n <CheckCircle2 className=\"h-4 w-4 shrink-0 mt-0.5 text-primary\" />\n <span>\n If <strong>{email}</strong> has a Huudis account, we’ve sent a reset link. It expires in 1 hour.\n </span>\n </div>\n <Link href=\"/login\" className=\"block text-center text-sm font-medium text-foreground hover:underline\">\n Back to sign in\n </Link>\n </div>\n );\n }\n\n return (\n <form onSubmit={submit} className=\"space-y-4\">\n {error && (\n <div className=\"flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n <AlertCircle className=\"h-4 w-4 shrink-0 mt-0.5\" />\n <span>{error}</span>\n </div>\n )}\n <div>\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">Email</label>\n <input\n type=\"email\"\n required\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n autoComplete=\"email\"\n autoFocus\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n </div>\n <button\n type=\"submit\"\n disabled={submitting}\n className=\"flex w-full items-center justify-center gap-2 rounded-md bg-primary py-2.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50\"\n >\n {submitting && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {submitting ? 'Sending…' : 'Send reset link'}\n </button>\n <div className=\"text-center text-xs text-muted-foreground\">\n Remembered it?{' '}\n <Link href=\"/login\" className=\"font-medium text-foreground hover:underline\">\n Sign in\n </Link>\n </div>\n </form>\n );\n}\n"],"mappings":";AA4CU,cACA,YADA;AA1CV,SAAS,gBAAgB;AACzB,OAAO,UAAU;AACjB,SAAS,SAAS,aAAa,oBAAoB;AACnD,SAAS,wBAA4C;AAM9C,SAAS,mBAAmB,EAAE,UAAU,IAA6B,CAAC,GAAG;AAC9E,QAAM,KAAoB,EAAE,GAAG,kBAAkB,GAAG,UAAU;AAC9D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAE;AACrC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AAEtD,iBAAe,OAAO,GAAoB;AACxC,MAAE,eAAe;AACjB,aAAS,IAAI;AACb,kBAAc,IAAI;AAClB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,gBAAgB;AAAA,QACzC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,MAAM,CAAC;AAAA,MAChC,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,UAAW,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAClD,cAAM,IAAI,MAAM,SAAS,OAAO,WAAW,mBAAmB,IAAI,MAAM,GAAG;AAAA,MAC7E;AACA,cAAQ,IAAI;AAAA,IACd,SAAS,KAAK;AACZ,eAAU,IAAc,OAAO;AAAA,IACjC,UAAE;AACA,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,MAAI,MAAM;AACR,WACE,qBAAC,SAAI,WAAU,aACb;AAAA,2BAAC,SAAI,WAAU,8GACb;AAAA,4BAAC,gBAAa,WAAU,wCAAuC;AAAA,QAC/D,qBAAC,UAAK;AAAA;AAAA,UACD,oBAAC,YAAQ,iBAAM;AAAA,UAAS;AAAA,WAC7B;AAAA,SACF;AAAA,MACA,oBAAC,QAAK,MAAK,UAAS,WAAU,yEAAwE,6BAEtG;AAAA,OACF;AAAA,EAEJ;AAEA,SACE,qBAAC,UAAK,UAAU,QAAQ,WAAU,aAC/B;AAAA,aACC,qBAAC,SAAI,WAAU,uHACb;AAAA,0BAAC,eAAY,WAAU,2BAA0B;AAAA,MACjD,oBAAC,UAAM,iBAAM;AAAA,OACf;AAAA,IAEF,qBAAC,SACC;AAAA,0BAAC,WAAM,WAAU,wDAAuD,mBAAK;AAAA,MAC7E;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,UAAQ;AAAA,UACR,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,UACxC,cAAa;AAAA,UACb,WAAS;AAAA,UACT,WAAU;AAAA;AAAA,MACZ;AAAA,OACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,UAAU;AAAA,QACV,WAAU;AAAA,QAET;AAAA,wBAAc,oBAAC,WAAQ,WAAU,wBAAuB;AAAA,UACxD,aAAa,kBAAa;AAAA;AAAA;AAAA,IAC7B;AAAA,IACA,qBAAC,SAAI,WAAU,6CAA4C;AAAA;AAAA,MAC1C;AAAA,MACf,oBAAC,QAAK,MAAK,UAAS,WAAU,+CAA8C,qBAE5E;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var ResetPasswordForm_exports = {};
|
|
31
|
+
__export(ResetPasswordForm_exports, {
|
|
32
|
+
ResetPasswordForm: () => ResetPasswordForm
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(ResetPasswordForm_exports);
|
|
35
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
36
|
+
var import_react = require("react");
|
|
37
|
+
var import_navigation = require("next/navigation");
|
|
38
|
+
var import_link = __toESM(require("next/link"), 1);
|
|
39
|
+
var import_lucide_react = require("lucide-react");
|
|
40
|
+
var import_types = require("./types");
|
|
41
|
+
function ResetPasswordForm({ endpoints } = {}) {
|
|
42
|
+
const ep = { ...import_types.defaultEndpoints, ...endpoints };
|
|
43
|
+
const router = (0, import_navigation.useRouter)();
|
|
44
|
+
const params = (0, import_navigation.useSearchParams)();
|
|
45
|
+
const token = params?.get("token") ?? "";
|
|
46
|
+
const [password, setPassword] = (0, import_react.useState)("");
|
|
47
|
+
const [submitting, setSubmitting] = (0, import_react.useState)(false);
|
|
48
|
+
const [done, setDone] = (0, import_react.useState)(false);
|
|
49
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
50
|
+
async function submit(e) {
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
setError(null);
|
|
53
|
+
setSubmitting(true);
|
|
54
|
+
try {
|
|
55
|
+
const res = await fetch(ep.resetPassword, {
|
|
56
|
+
method: "POST",
|
|
57
|
+
headers: { "Content-Type": "application/json" },
|
|
58
|
+
body: JSON.stringify({ token, password })
|
|
59
|
+
});
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
const payload = await res.json().catch(() => null);
|
|
62
|
+
throw new Error(payload?.error?.message ?? `Request failed (${res.status})`);
|
|
63
|
+
}
|
|
64
|
+
setDone(true);
|
|
65
|
+
setTimeout(() => router.push("/login"), 1500);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
setError(err.message);
|
|
68
|
+
} finally {
|
|
69
|
+
setSubmitting(false);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!token) {
|
|
73
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "space-y-3", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: [
|
|
74
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.AlertCircle, { className: "h-4 w-4 shrink-0 mt-0.5" }),
|
|
75
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
76
|
+
"Missing or invalid reset link. ",
|
|
77
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_link.default, { href: "/forgot-password", className: "font-medium underline", children: "Request a new one" }),
|
|
78
|
+
"."
|
|
79
|
+
] })
|
|
80
|
+
] }) });
|
|
81
|
+
}
|
|
82
|
+
if (done) {
|
|
83
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-start gap-2 rounded-md border border-primary/30 bg-primary/10 px-3 py-2 text-sm text-foreground", children: [
|
|
84
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.CheckCircle2, { className: "h-4 w-4 shrink-0 mt-0.5 text-primary" }),
|
|
85
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Password updated. Taking you to sign in\u2026" })
|
|
86
|
+
] });
|
|
87
|
+
}
|
|
88
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: submit, className: "space-y-4", children: [
|
|
89
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: [
|
|
90
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.AlertCircle, { className: "h-4 w-4 shrink-0 mt-0.5" }),
|
|
91
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: error })
|
|
92
|
+
] }),
|
|
93
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
94
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "New password" }),
|
|
95
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
96
|
+
"input",
|
|
97
|
+
{
|
|
98
|
+
type: "password",
|
|
99
|
+
required: true,
|
|
100
|
+
minLength: 10,
|
|
101
|
+
value: password,
|
|
102
|
+
onChange: (e) => setPassword(e.target.value),
|
|
103
|
+
autoComplete: "new-password",
|
|
104
|
+
autoFocus: true,
|
|
105
|
+
className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary"
|
|
106
|
+
}
|
|
107
|
+
),
|
|
108
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mt-1 text-[11px] text-muted-foreground", children: "At least 10 characters, with a letter and a number." })
|
|
109
|
+
] }),
|
|
110
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
111
|
+
"button",
|
|
112
|
+
{
|
|
113
|
+
type: "submit",
|
|
114
|
+
disabled: submitting,
|
|
115
|
+
className: "flex w-full items-center justify-center gap-2 rounded-md bg-primary py-2.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50",
|
|
116
|
+
children: [
|
|
117
|
+
submitting && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader2, { className: "h-4 w-4 animate-spin" }),
|
|
118
|
+
submitting ? "Updating\u2026" : "Update password"
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
] });
|
|
123
|
+
}
|
|
124
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
125
|
+
0 && (module.exports = {
|
|
126
|
+
ResetPasswordForm
|
|
127
|
+
});
|
|
128
|
+
//# sourceMappingURL=ResetPasswordForm.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ResetPasswordForm.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useRouter, useSearchParams } from 'next/navigation';\nimport Link from 'next/link';\nimport { Loader2, AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { defaultEndpoints, type AuthEndpoints } from './types';\n\nexport interface ResetPasswordFormProps {\n endpoints?: Partial<AuthEndpoints>;\n}\n\nexport function ResetPasswordForm({ endpoints }: ResetPasswordFormProps = {}) {\n const ep: AuthEndpoints = { ...defaultEndpoints, ...endpoints };\n const router = useRouter();\n const params = useSearchParams();\n const token = params?.get('token') ?? '';\n const [password, setPassword] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [done, setDone] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n async function submit(e: React.FormEvent) {\n e.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n const res = await fetch(ep.resetPassword, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ token, password }),\n });\n if (!res.ok) {\n const payload = (await res.json().catch(() => null)) as { error?: { message?: string } } | null;\n throw new Error(payload?.error?.message ?? `Request failed (${res.status})`);\n }\n setDone(true);\n setTimeout(() => router.push('/login'), 1500);\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setSubmitting(false);\n }\n }\n\n if (!token) {\n return (\n <div className=\"space-y-3\">\n <div className=\"flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n <AlertCircle className=\"h-4 w-4 shrink-0 mt-0.5\" />\n <span>\n Missing or invalid reset link. <Link href=\"/forgot-password\" className=\"font-medium underline\">Request a new one</Link>.\n </span>\n </div>\n </div>\n );\n }\n\n if (done) {\n return (\n <div className=\"flex items-start gap-2 rounded-md border border-primary/30 bg-primary/10 px-3 py-2 text-sm text-foreground\">\n <CheckCircle2 className=\"h-4 w-4 shrink-0 mt-0.5 text-primary\" />\n <span>Password updated. Taking you to sign in…</span>\n </div>\n );\n }\n\n return (\n <form onSubmit={submit} className=\"space-y-4\">\n {error && (\n <div className=\"flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n <AlertCircle className=\"h-4 w-4 shrink-0 mt-0.5\" />\n <span>{error}</span>\n </div>\n )}\n <div>\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">New password</label>\n <input\n type=\"password\"\n required\n minLength={10}\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n autoComplete=\"new-password\"\n autoFocus\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n <p className=\"mt-1 text-[11px] text-muted-foreground\">At least 10 characters, with a letter and a number.</p>\n </div>\n <button\n type=\"submit\"\n disabled={submitting}\n className=\"flex w-full items-center justify-center gap-2 rounded-md bg-primary py-2.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50\"\n >\n {submitting && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {submitting ? 'Updating…' : 'Update password'}\n </button>\n </form>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiDU;AA/CV,mBAAyB;AACzB,wBAA2C;AAC3C,kBAAiB;AACjB,0BAAmD;AACnD,mBAAqD;AAM9C,SAAS,kBAAkB,EAAE,UAAU,IAA4B,CAAC,GAAG;AAC5E,QAAM,KAAoB,EAAE,GAAG,+BAAkB,GAAG,UAAU;AAC9D,QAAM,aAAS,6BAAU;AACzB,QAAM,aAAS,mCAAgB;AAC/B,QAAM,QAAQ,QAAQ,IAAI,OAAO,KAAK;AACtC,QAAM,CAAC,UAAU,WAAW,QAAI,uBAAS,EAAE;AAC3C,QAAM,CAAC,YAAY,aAAa,QAAI,uBAAS,KAAK;AAClD,QAAM,CAAC,MAAM,OAAO,QAAI,uBAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAwB,IAAI;AAEtD,iBAAe,OAAO,GAAoB;AACxC,MAAE,eAAe;AACjB,aAAS,IAAI;AACb,kBAAc,IAAI;AAClB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,eAAe;AAAA,QACxC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,SAAS,CAAC;AAAA,MAC1C,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,UAAW,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAClD,cAAM,IAAI,MAAM,SAAS,OAAO,WAAW,mBAAmB,IAAI,MAAM,GAAG;AAAA,MAC7E;AACA,cAAQ,IAAI;AACZ,iBAAW,MAAM,OAAO,KAAK,QAAQ,GAAG,IAAI;AAAA,IAC9C,SAAS,KAAK;AACZ,eAAU,IAAc,OAAO;AAAA,IACjC,UAAE;AACA,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,MAAI,CAAC,OAAO;AACV,WACE,4CAAC,SAAI,WAAU,aACb,uDAAC,SAAI,WAAU,uHACb;AAAA,kDAAC,mCAAY,WAAU,2BAA0B;AAAA,MACjD,6CAAC,UAAK;AAAA;AAAA,QAC2B,4CAAC,YAAAA,SAAA,EAAK,MAAK,oBAAmB,WAAU,yBAAwB,+BAAiB;AAAA,QAAO;AAAA,SACzH;AAAA,OACF,GACF;AAAA,EAEJ;AAEA,MAAI,MAAM;AACR,WACE,6CAAC,SAAI,WAAU,8GACb;AAAA,kDAAC,oCAAa,WAAU,wCAAuC;AAAA,MAC/D,4CAAC,UAAK,2DAAwC;AAAA,OAChD;AAAA,EAEJ;AAEA,SACE,6CAAC,UAAK,UAAU,QAAQ,WAAU,aAC/B;AAAA,aACC,6CAAC,SAAI,WAAU,uHACb;AAAA,kDAAC,mCAAY,WAAU,2BAA0B;AAAA,MACjD,4CAAC,UAAM,iBAAM;AAAA,OACf;AAAA,IAEF,6CAAC,SACC;AAAA,kDAAC,WAAM,WAAU,wDAAuD,0BAAY;AAAA,MACpF;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,UAAQ;AAAA,UACR,WAAW;AAAA,UACX,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,YAAY,EAAE,OAAO,KAAK;AAAA,UAC3C,cAAa;AAAA,UACb,WAAS;AAAA,UACT,WAAU;AAAA;AAAA,MACZ;AAAA,MACA,4CAAC,OAAE,WAAU,0CAAyC,iEAAmD;AAAA,OAC3G;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,UAAU;AAAA,QACV,WAAU;AAAA,QAET;AAAA,wBAAc,4CAAC,+BAAQ,WAAU,wBAAuB;AAAA,UACxD,aAAa,mBAAc;AAAA;AAAA;AAAA,IAC9B;AAAA,KACF;AAEJ;","names":["Link"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { AuthEndpoints } from './types.cjs';
|
|
3
|
+
|
|
4
|
+
interface ResetPasswordFormProps {
|
|
5
|
+
endpoints?: Partial<AuthEndpoints>;
|
|
6
|
+
}
|
|
7
|
+
declare function ResetPasswordForm({ endpoints }?: ResetPasswordFormProps): react.JSX.Element;
|
|
8
|
+
|
|
9
|
+
export { ResetPasswordForm, type ResetPasswordFormProps };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { AuthEndpoints } from './types.js';
|
|
3
|
+
|
|
4
|
+
interface ResetPasswordFormProps {
|
|
5
|
+
endpoints?: Partial<AuthEndpoints>;
|
|
6
|
+
}
|
|
7
|
+
declare function ResetPasswordForm({ endpoints }?: ResetPasswordFormProps): react.JSX.Element;
|
|
8
|
+
|
|
9
|
+
export { ResetPasswordForm, type ResetPasswordFormProps };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { useRouter, useSearchParams } from "next/navigation";
|
|
5
|
+
import Link from "next/link";
|
|
6
|
+
import { Loader2, AlertCircle, CheckCircle2 } from "lucide-react";
|
|
7
|
+
import { defaultEndpoints } from "./types";
|
|
8
|
+
function ResetPasswordForm({ endpoints } = {}) {
|
|
9
|
+
const ep = { ...defaultEndpoints, ...endpoints };
|
|
10
|
+
const router = useRouter();
|
|
11
|
+
const params = useSearchParams();
|
|
12
|
+
const token = params?.get("token") ?? "";
|
|
13
|
+
const [password, setPassword] = useState("");
|
|
14
|
+
const [submitting, setSubmitting] = useState(false);
|
|
15
|
+
const [done, setDone] = useState(false);
|
|
16
|
+
const [error, setError] = useState(null);
|
|
17
|
+
async function submit(e) {
|
|
18
|
+
e.preventDefault();
|
|
19
|
+
setError(null);
|
|
20
|
+
setSubmitting(true);
|
|
21
|
+
try {
|
|
22
|
+
const res = await fetch(ep.resetPassword, {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: { "Content-Type": "application/json" },
|
|
25
|
+
body: JSON.stringify({ token, password })
|
|
26
|
+
});
|
|
27
|
+
if (!res.ok) {
|
|
28
|
+
const payload = await res.json().catch(() => null);
|
|
29
|
+
throw new Error(payload?.error?.message ?? `Request failed (${res.status})`);
|
|
30
|
+
}
|
|
31
|
+
setDone(true);
|
|
32
|
+
setTimeout(() => router.push("/login"), 1500);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
setError(err.message);
|
|
35
|
+
} finally {
|
|
36
|
+
setSubmitting(false);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!token) {
|
|
40
|
+
return /* @__PURE__ */ jsx("div", { className: "space-y-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: [
|
|
41
|
+
/* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4 shrink-0 mt-0.5" }),
|
|
42
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
43
|
+
"Missing or invalid reset link. ",
|
|
44
|
+
/* @__PURE__ */ jsx(Link, { href: "/forgot-password", className: "font-medium underline", children: "Request a new one" }),
|
|
45
|
+
"."
|
|
46
|
+
] })
|
|
47
|
+
] }) });
|
|
48
|
+
}
|
|
49
|
+
if (done) {
|
|
50
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 rounded-md border border-primary/30 bg-primary/10 px-3 py-2 text-sm text-foreground", children: [
|
|
51
|
+
/* @__PURE__ */ jsx(CheckCircle2, { className: "h-4 w-4 shrink-0 mt-0.5 text-primary" }),
|
|
52
|
+
/* @__PURE__ */ jsx("span", { children: "Password updated. Taking you to sign in\u2026" })
|
|
53
|
+
] });
|
|
54
|
+
}
|
|
55
|
+
return /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-4", children: [
|
|
56
|
+
error && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: [
|
|
57
|
+
/* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4 shrink-0 mt-0.5" }),
|
|
58
|
+
/* @__PURE__ */ jsx("span", { children: error })
|
|
59
|
+
] }),
|
|
60
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
61
|
+
/* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "New password" }),
|
|
62
|
+
/* @__PURE__ */ jsx(
|
|
63
|
+
"input",
|
|
64
|
+
{
|
|
65
|
+
type: "password",
|
|
66
|
+
required: true,
|
|
67
|
+
minLength: 10,
|
|
68
|
+
value: password,
|
|
69
|
+
onChange: (e) => setPassword(e.target.value),
|
|
70
|
+
autoComplete: "new-password",
|
|
71
|
+
autoFocus: true,
|
|
72
|
+
className: "w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary"
|
|
73
|
+
}
|
|
74
|
+
),
|
|
75
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-[11px] text-muted-foreground", children: "At least 10 characters, with a letter and a number." })
|
|
76
|
+
] }),
|
|
77
|
+
/* @__PURE__ */ jsxs(
|
|
78
|
+
"button",
|
|
79
|
+
{
|
|
80
|
+
type: "submit",
|
|
81
|
+
disabled: submitting,
|
|
82
|
+
className: "flex w-full items-center justify-center gap-2 rounded-md bg-primary py-2.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50",
|
|
83
|
+
children: [
|
|
84
|
+
submitting && /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }),
|
|
85
|
+
submitting ? "Updating\u2026" : "Update password"
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
] });
|
|
90
|
+
}
|
|
91
|
+
export {
|
|
92
|
+
ResetPasswordForm
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=ResetPasswordForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ResetPasswordForm.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useRouter, useSearchParams } from 'next/navigation';\nimport Link from 'next/link';\nimport { Loader2, AlertCircle, CheckCircle2 } from 'lucide-react';\nimport { defaultEndpoints, type AuthEndpoints } from './types';\n\nexport interface ResetPasswordFormProps {\n endpoints?: Partial<AuthEndpoints>;\n}\n\nexport function ResetPasswordForm({ endpoints }: ResetPasswordFormProps = {}) {\n const ep: AuthEndpoints = { ...defaultEndpoints, ...endpoints };\n const router = useRouter();\n const params = useSearchParams();\n const token = params?.get('token') ?? '';\n const [password, setPassword] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [done, setDone] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n async function submit(e: React.FormEvent) {\n e.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n const res = await fetch(ep.resetPassword, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ token, password }),\n });\n if (!res.ok) {\n const payload = (await res.json().catch(() => null)) as { error?: { message?: string } } | null;\n throw new Error(payload?.error?.message ?? `Request failed (${res.status})`);\n }\n setDone(true);\n setTimeout(() => router.push('/login'), 1500);\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setSubmitting(false);\n }\n }\n\n if (!token) {\n return (\n <div className=\"space-y-3\">\n <div className=\"flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n <AlertCircle className=\"h-4 w-4 shrink-0 mt-0.5\" />\n <span>\n Missing or invalid reset link. <Link href=\"/forgot-password\" className=\"font-medium underline\">Request a new one</Link>.\n </span>\n </div>\n </div>\n );\n }\n\n if (done) {\n return (\n <div className=\"flex items-start gap-2 rounded-md border border-primary/30 bg-primary/10 px-3 py-2 text-sm text-foreground\">\n <CheckCircle2 className=\"h-4 w-4 shrink-0 mt-0.5 text-primary\" />\n <span>Password updated. Taking you to sign in…</span>\n </div>\n );\n }\n\n return (\n <form onSubmit={submit} className=\"space-y-4\">\n {error && (\n <div className=\"flex items-start gap-2 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n <AlertCircle className=\"h-4 w-4 shrink-0 mt-0.5\" />\n <span>{error}</span>\n </div>\n )}\n <div>\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">New password</label>\n <input\n type=\"password\"\n required\n minLength={10}\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n autoComplete=\"new-password\"\n autoFocus\n className=\"w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n <p className=\"mt-1 text-[11px] text-muted-foreground\">At least 10 characters, with a letter and a number.</p>\n </div>\n <button\n type=\"submit\"\n disabled={submitting}\n className=\"flex w-full items-center justify-center gap-2 rounded-md bg-primary py-2.5 text-sm font-medium text-primary-foreground transition hover:opacity-90 disabled:opacity-50\"\n >\n {submitting && <Loader2 className=\"h-4 w-4 animate-spin\" />}\n {submitting ? 'Updating…' : 'Update password'}\n </button>\n </form>\n );\n}\n"],"mappings":";AAiDU,cACA,YADA;AA/CV,SAAS,gBAAgB;AACzB,SAAS,WAAW,uBAAuB;AAC3C,OAAO,UAAU;AACjB,SAAS,SAAS,aAAa,oBAAoB;AACnD,SAAS,wBAA4C;AAM9C,SAAS,kBAAkB,EAAE,UAAU,IAA4B,CAAC,GAAG;AAC5E,QAAM,KAAoB,EAAE,GAAG,kBAAkB,GAAG,UAAU;AAC9D,QAAM,SAAS,UAAU;AACzB,QAAM,SAAS,gBAAgB;AAC/B,QAAM,QAAQ,QAAQ,IAAI,OAAO,KAAK;AACtC,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,EAAE;AAC3C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AAEtD,iBAAe,OAAO,GAAoB;AACxC,MAAE,eAAe;AACjB,aAAS,IAAI;AACb,kBAAc,IAAI;AAClB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,eAAe;AAAA,QACxC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,SAAS,CAAC;AAAA,MAC1C,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,UAAW,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI;AAClD,cAAM,IAAI,MAAM,SAAS,OAAO,WAAW,mBAAmB,IAAI,MAAM,GAAG;AAAA,MAC7E;AACA,cAAQ,IAAI;AACZ,iBAAW,MAAM,OAAO,KAAK,QAAQ,GAAG,IAAI;AAAA,IAC9C,SAAS,KAAK;AACZ,eAAU,IAAc,OAAO;AAAA,IACjC,UAAE;AACA,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,MAAI,CAAC,OAAO;AACV,WACE,oBAAC,SAAI,WAAU,aACb,+BAAC,SAAI,WAAU,uHACb;AAAA,0BAAC,eAAY,WAAU,2BAA0B;AAAA,MACjD,qBAAC,UAAK;AAAA;AAAA,QAC2B,oBAAC,QAAK,MAAK,oBAAmB,WAAU,yBAAwB,+BAAiB;AAAA,QAAO;AAAA,SACzH;AAAA,OACF,GACF;AAAA,EAEJ;AAEA,MAAI,MAAM;AACR,WACE,qBAAC,SAAI,WAAU,8GACb;AAAA,0BAAC,gBAAa,WAAU,wCAAuC;AAAA,MAC/D,oBAAC,UAAK,2DAAwC;AAAA,OAChD;AAAA,EAEJ;AAEA,SACE,qBAAC,UAAK,UAAU,QAAQ,WAAU,aAC/B;AAAA,aACC,qBAAC,SAAI,WAAU,uHACb;AAAA,0BAAC,eAAY,WAAU,2BAA0B;AAAA,MACjD,oBAAC,UAAM,iBAAM;AAAA,OACf;AAAA,IAEF,qBAAC,SACC;AAAA,0BAAC,WAAM,WAAU,wDAAuD,0BAAY;AAAA,MACpF;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,UAAQ;AAAA,UACR,WAAW;AAAA,UACX,OAAO;AAAA,UACP,UAAU,CAAC,MAAM,YAAY,EAAE,OAAO,KAAK;AAAA,UAC3C,cAAa;AAAA,UACb,WAAS;AAAA,UACT,WAAU;AAAA;AAAA,MACZ;AAAA,MACA,oBAAC,OAAE,WAAU,0CAAyC,iEAAmD;AAAA,OAC3G;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,UAAU;AAAA,QACV,WAAU;AAAA,QAET;AAAA,wBAAc,oBAAC,WAAQ,WAAU,wBAAuB;AAAA,UACxD,aAAa,mBAAc;AAAA;AAAA;AAAA,IAC9B;AAAA,KACF;AAEJ;","names":[]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var index_exports = {};
|
|
20
|
+
__export(index_exports, {
|
|
21
|
+
AuthForm: () => import_AuthForm.AuthForm,
|
|
22
|
+
ForgotPasswordForm: () => import_ForgotPasswordForm.ForgotPasswordForm,
|
|
23
|
+
ResetPasswordForm: () => import_ResetPasswordForm.ResetPasswordForm,
|
|
24
|
+
defaultEndpoints: () => import_types.defaultEndpoints
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
var import_AuthForm = require("./AuthForm");
|
|
28
|
+
var import_ForgotPasswordForm = require("./ForgotPasswordForm");
|
|
29
|
+
var import_ResetPasswordForm = require("./ResetPasswordForm");
|
|
30
|
+
var import_types = require("./types");
|
|
31
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
32
|
+
0 && (module.exports = {
|
|
33
|
+
AuthForm,
|
|
34
|
+
ForgotPasswordForm,
|
|
35
|
+
ResetPasswordForm,
|
|
36
|
+
defaultEndpoints
|
|
37
|
+
});
|
|
38
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { AuthForm } from './AuthForm';\nexport type { AuthFormProps } from './AuthForm';\nexport { ForgotPasswordForm } from './ForgotPasswordForm';\nexport type { ForgotPasswordFormProps } from './ForgotPasswordForm';\nexport { ResetPasswordForm } from './ResetPasswordForm';\nexport type { ResetPasswordFormProps } from './ResetPasswordForm';\nexport type { AuthEndpoints, SocialProviders } from './types';\nexport { defaultEndpoints } from './types';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,sBAAyB;AAEzB,gCAAmC;AAEnC,+BAAkC;AAGlC,mBAAiC;","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AuthForm, AuthFormProps } from './AuthForm.cjs';
|
|
2
|
+
export { ForgotPasswordForm, ForgotPasswordFormProps } from './ForgotPasswordForm.cjs';
|
|
3
|
+
export { ResetPasswordForm, ResetPasswordFormProps } from './ResetPasswordForm.cjs';
|
|
4
|
+
export { AuthEndpoints, SocialProviders, defaultEndpoints } from './types.cjs';
|
|
5
|
+
import 'react';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AuthForm, AuthFormProps } from './AuthForm.js';
|
|
2
|
+
export { ForgotPasswordForm, ForgotPasswordFormProps } from './ForgotPasswordForm.js';
|
|
3
|
+
export { ResetPasswordForm, ResetPasswordFormProps } from './ResetPasswordForm.js';
|
|
4
|
+
export { AuthEndpoints, SocialProviders, defaultEndpoints } from './types.js';
|
|
5
|
+
import 'react';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AuthForm } from "./AuthForm";
|
|
2
|
+
import { ForgotPasswordForm } from "./ForgotPasswordForm";
|
|
3
|
+
import { ResetPasswordForm } from "./ResetPasswordForm";
|
|
4
|
+
import { defaultEndpoints } from "./types";
|
|
5
|
+
export {
|
|
6
|
+
AuthForm,
|
|
7
|
+
ForgotPasswordForm,
|
|
8
|
+
ResetPasswordForm,
|
|
9
|
+
defaultEndpoints
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { AuthForm } from './AuthForm';\nexport type { AuthFormProps } from './AuthForm';\nexport { ForgotPasswordForm } from './ForgotPasswordForm';\nexport type { ForgotPasswordFormProps } from './ForgotPasswordForm';\nexport { ResetPasswordForm } from './ResetPasswordForm';\nexport type { ResetPasswordFormProps } from './ResetPasswordForm';\nexport type { AuthEndpoints, SocialProviders } from './types';\nexport { defaultEndpoints } from './types';\n"],"mappings":"AAAA,SAAS,gBAAgB;AAEzB,SAAS,0BAA0B;AAEnC,SAAS,yBAAyB;AAGlC,SAAS,wBAAwB;","names":[]}
|