@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
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @forjio/auth-ui
|
|
2
|
+
|
|
3
|
+
Shared auth forms for the Forjio family. Login, signup, forgot-
|
|
4
|
+
password, reset-password — all with the same look as every other
|
|
5
|
+
Forjio product, all wired to the standard `@forjio/sdk/auth-handlers`
|
|
6
|
+
backend endpoints (override via `endpoints` prop if your product mounts
|
|
7
|
+
them somewhere else).
|
|
8
|
+
|
|
9
|
+
Sister package to [`@forjio/website-ui`](https://github.com/hachimi-cat/forjio-website-ui)
|
|
10
|
+
+ [`@forjio/portal-ui`](https://github.com/hachimi-cat/forjio-portal-ui).
|
|
11
|
+
Extracted from `saas-plugipay` on 2026-05-19 as the canonical reference
|
|
12
|
+
build per TEMPLATE.md Step 4.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm i @forjio/auth-ui lucide-react
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Peer deps: `react`, `react-dom`, `next` (App Router), `lucide-react`.
|
|
21
|
+
Tailwind is **not** a peer dep but the components use shadcn-style
|
|
22
|
+
utility classes (`bg-primary`, `text-muted-foreground`, etc.) so the
|
|
23
|
+
host product needs a Tailwind config that exposes the shadcn token set.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
// app/(auth)/login/page.tsx
|
|
29
|
+
'use client';
|
|
30
|
+
import { AuthForm } from '@forjio/auth-ui';
|
|
31
|
+
|
|
32
|
+
export default function LoginPage() {
|
|
33
|
+
return <AuthForm mode="login" brand="Kalium" />;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// app/(auth)/signup/page.tsx
|
|
37
|
+
'use client';
|
|
38
|
+
import { AuthForm } from '@forjio/auth-ui';
|
|
39
|
+
|
|
40
|
+
export default function SignupPage() {
|
|
41
|
+
return <AuthForm mode="signup" brand="Kalium" />;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// app/(auth)/forgot-password/page.tsx
|
|
45
|
+
'use client';
|
|
46
|
+
import { ForgotPasswordForm } from '@forjio/auth-ui';
|
|
47
|
+
|
|
48
|
+
export default function ForgotPasswordPage() {
|
|
49
|
+
return <ForgotPasswordForm />;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// app/(auth)/reset-password/page.tsx
|
|
53
|
+
'use client';
|
|
54
|
+
import { ResetPasswordForm } from '@forjio/auth-ui';
|
|
55
|
+
|
|
56
|
+
export default function ResetPasswordPage() {
|
|
57
|
+
return <ResetPasswordForm />;
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Props
|
|
62
|
+
|
|
63
|
+
### `AuthForm`
|
|
64
|
+
|
|
65
|
+
| Prop | Default | Notes |
|
|
66
|
+
|-------------------|----------------------------------|------------------------------------------------------|
|
|
67
|
+
| `mode` | required | `'login'` or `'signup'` |
|
|
68
|
+
| `brand` | required | Brand name shown in copy (e.g. `'Kalium'`) |
|
|
69
|
+
| `endpoints` | family defaults | Override paths if backend mounts differ |
|
|
70
|
+
| `providers` | `null` (fail-open shows both) | `{ google: bool, apple: bool }` — host fetches |
|
|
71
|
+
| `defaultReturnTo` | `/dashboard` | Redirect after success; `?return_to=` overrides |
|
|
72
|
+
|
|
73
|
+
### `ForgotPasswordForm` / `ResetPasswordForm`
|
|
74
|
+
|
|
75
|
+
| Prop | Default | Notes |
|
|
76
|
+
|-------------|------------------|------------------------------------|
|
|
77
|
+
| `endpoints` | family defaults | Override paths if backend differs |
|
|
78
|
+
|
|
79
|
+
## Endpoint defaults
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
{
|
|
83
|
+
login: '/api/v1/auth/login',
|
|
84
|
+
signup: '/api/v1/auth/signup',
|
|
85
|
+
forgotPassword: '/api/v1/auth/password-reset/request',
|
|
86
|
+
resetPassword: '/api/v1/auth/password-reset/complete',
|
|
87
|
+
socialStart: '/api/v1/auth/huudis/start',
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
These match the routes mounted by `@forjio/sdk/auth-handlers`. New
|
|
92
|
+
products should mount the same paths and not override the prop.
|
|
93
|
+
|
|
94
|
+
## Why Tailwind classes (not inline styles)?
|
|
95
|
+
|
|
96
|
+
Auth forms are visually opinionated — inputs, buttons, error states,
|
|
97
|
+
labels — and every Forjio product uses shadcn-flavored Tailwind. Inline
|
|
98
|
+
styles would diverge from the host's design system. The shadcn token
|
|
99
|
+
set (`bg-primary`, `text-muted-foreground`, `border-border`) is stable
|
|
100
|
+
across all 8 active products. Sister `@forjio/portal-ui` uses inline
|
|
101
|
+
styles because its surface is structural chrome where Tailwind would
|
|
102
|
+
collide with the host.
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
UNLICENSED — private Forjio family package.
|
|
@@ -0,0 +1,220 @@
|
|
|
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 AuthForm_exports = {};
|
|
31
|
+
__export(AuthForm_exports, {
|
|
32
|
+
AuthForm: () => AuthForm
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(AuthForm_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 AuthForm({
|
|
42
|
+
mode,
|
|
43
|
+
brand,
|
|
44
|
+
endpoints,
|
|
45
|
+
providers,
|
|
46
|
+
defaultReturnTo = "/dashboard"
|
|
47
|
+
}) {
|
|
48
|
+
const router = (0, import_navigation.useRouter)();
|
|
49
|
+
const params = (0, import_navigation.useSearchParams)();
|
|
50
|
+
const returnTo = params?.get("return_to") || defaultReturnTo;
|
|
51
|
+
const ssoError = params?.get("sso_error");
|
|
52
|
+
const ssoDetail = params?.get("sso_detail");
|
|
53
|
+
const ep = { ...import_types.defaultEndpoints, ...endpoints };
|
|
54
|
+
const [email, setEmail] = (0, import_react.useState)("");
|
|
55
|
+
const [password, setPassword] = (0, import_react.useState)("");
|
|
56
|
+
const [name, setName] = (0, import_react.useState)("");
|
|
57
|
+
const [submitting, setSubmitting] = (0, import_react.useState)(false);
|
|
58
|
+
const [error, setError] = (0, import_react.useState)(
|
|
59
|
+
ssoError ? `Sign-in failed: ${ssoDetail || ssoError}` : null
|
|
60
|
+
);
|
|
61
|
+
async function submit(e) {
|
|
62
|
+
e.preventDefault();
|
|
63
|
+
setError(null);
|
|
64
|
+
setSubmitting(true);
|
|
65
|
+
try {
|
|
66
|
+
const path = mode === "signup" ? ep.signup : ep.login;
|
|
67
|
+
const body = { email, password };
|
|
68
|
+
if (mode === "signup" && name.trim()) body.name = name.trim();
|
|
69
|
+
const res = await fetch(path, {
|
|
70
|
+
method: "POST",
|
|
71
|
+
headers: { "Content-Type": "application/json" },
|
|
72
|
+
credentials: "include",
|
|
73
|
+
body: JSON.stringify(body)
|
|
74
|
+
});
|
|
75
|
+
if (!res.ok) {
|
|
76
|
+
const payload = await res.json().catch(() => null);
|
|
77
|
+
throw new Error(payload?.error?.message ?? `Request failed (${res.status})`);
|
|
78
|
+
}
|
|
79
|
+
router.push(returnTo);
|
|
80
|
+
router.refresh();
|
|
81
|
+
} catch (err) {
|
|
82
|
+
setError(err.message);
|
|
83
|
+
} finally {
|
|
84
|
+
setSubmitting(false);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const otherMode = mode === "login" ? "signup" : "login";
|
|
88
|
+
const otherHref = `/${otherMode}?return_to=${encodeURIComponent(returnTo)}`;
|
|
89
|
+
const socialUrl = (provider) => `${ep.socialStart}?provider=${provider}&return_to=${encodeURIComponent(returnTo)}`;
|
|
90
|
+
const showGoogle = providers?.google !== false;
|
|
91
|
+
const showApple = providers?.apple !== false;
|
|
92
|
+
const hasAnySocial = showGoogle || showApple;
|
|
93
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-4", children: [
|
|
94
|
+
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: [
|
|
95
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.AlertCircle, { className: "h-4 w-4 shrink-0 mt-0.5" }),
|
|
96
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: error })
|
|
97
|
+
] }),
|
|
98
|
+
hasAnySocial && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
99
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid gap-2", children: [
|
|
100
|
+
showGoogle && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
101
|
+
"a",
|
|
102
|
+
{
|
|
103
|
+
href: socialUrl("google"),
|
|
104
|
+
className: "flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent",
|
|
105
|
+
children: [
|
|
106
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(GoogleMark, { className: "h-4 w-4" }),
|
|
107
|
+
"Continue with Google"
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
),
|
|
111
|
+
showApple && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
112
|
+
"a",
|
|
113
|
+
{
|
|
114
|
+
href: socialUrl("apple"),
|
|
115
|
+
className: "flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent",
|
|
116
|
+
children: [
|
|
117
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppleMark, { className: "h-4 w-4" }),
|
|
118
|
+
"Continue with Apple"
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
] }),
|
|
123
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "my-4 flex items-center gap-3 text-[11px] text-muted-foreground", children: [
|
|
124
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1 border-t border-border" }),
|
|
125
|
+
"OR",
|
|
126
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1 border-t border-border" })
|
|
127
|
+
] })
|
|
128
|
+
] }),
|
|
129
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: submit, className: "space-y-3", children: [
|
|
130
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
131
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Email" }),
|
|
132
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
133
|
+
"input",
|
|
134
|
+
{
|
|
135
|
+
type: "email",
|
|
136
|
+
required: true,
|
|
137
|
+
value: email,
|
|
138
|
+
onChange: (e) => setEmail(e.target.value),
|
|
139
|
+
autoComplete: "email",
|
|
140
|
+
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"
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
] }),
|
|
144
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
145
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Password" }),
|
|
146
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
147
|
+
"input",
|
|
148
|
+
{
|
|
149
|
+
type: "password",
|
|
150
|
+
required: true,
|
|
151
|
+
minLength: mode === "signup" ? 10 : void 0,
|
|
152
|
+
value: password,
|
|
153
|
+
onChange: (e) => setPassword(e.target.value),
|
|
154
|
+
autoComplete: mode === "signup" ? "new-password" : "current-password",
|
|
155
|
+
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"
|
|
156
|
+
}
|
|
157
|
+
),
|
|
158
|
+
mode === "signup" && /* @__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." })
|
|
159
|
+
] }),
|
|
160
|
+
mode === "signup" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
161
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: [
|
|
162
|
+
"Your name ",
|
|
163
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-muted-foreground/60", children: "(optional)" })
|
|
164
|
+
] }),
|
|
165
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
166
|
+
"input",
|
|
167
|
+
{
|
|
168
|
+
type: "text",
|
|
169
|
+
value: name,
|
|
170
|
+
onChange: (e) => setName(e.target.value),
|
|
171
|
+
autoComplete: "name",
|
|
172
|
+
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"
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
] }),
|
|
176
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
177
|
+
"button",
|
|
178
|
+
{
|
|
179
|
+
type: "submit",
|
|
180
|
+
disabled: submitting,
|
|
181
|
+
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",
|
|
182
|
+
children: [
|
|
183
|
+
submitting && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Loader2, { className: "h-4 w-4 animate-spin" }),
|
|
184
|
+
submitting ? mode === "signup" ? "Creating\u2026" : "Signing in\u2026" : mode === "signup" ? "Create account" : "Sign in"
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
)
|
|
188
|
+
] }),
|
|
189
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between pt-2 text-xs text-muted-foreground", children: [
|
|
190
|
+
mode === "login" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_link.default, { href: "/forgot-password", className: "hover:text-foreground", children: "Forgot password?" }),
|
|
191
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: mode === "login" ? "" : "ml-auto", children: [
|
|
192
|
+
mode === "login" ? `New to ${brand}?` : "Already have an account?",
|
|
193
|
+
" ",
|
|
194
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_link.default, { href: otherHref, className: "font-medium text-foreground hover:underline", children: mode === "login" ? "Sign up" : "Sign in" })
|
|
195
|
+
] })
|
|
196
|
+
] }),
|
|
197
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "pt-2 text-[11px] leading-relaxed text-muted-foreground/80", children: [
|
|
198
|
+
"Identity is powered by",
|
|
199
|
+
" ",
|
|
200
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: "https://huudis.com", className: "underline hover:text-foreground", children: "Huudis" }),
|
|
201
|
+
". One account for every Forjio product."
|
|
202
|
+
] })
|
|
203
|
+
] });
|
|
204
|
+
}
|
|
205
|
+
function GoogleMark({ className }) {
|
|
206
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className, viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
|
|
207
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21.6 12.227c0-.708-.064-1.39-.182-2.045H12v3.868h5.384a4.603 4.603 0 0 1-1.997 3.018v2.51h3.232c1.891-1.742 2.98-4.307 2.98-7.35Z", fill: "#4285F4" }),
|
|
208
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 22c2.7 0 4.965-.895 6.62-2.422l-3.233-2.51c-.895.6-2.041.955-3.386.955-2.604 0-4.81-1.76-5.596-4.122H3.067v2.59A9.996 9.996 0 0 0 12 22Z", fill: "#34A853" }),
|
|
209
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6.404 13.9a6.016 6.016 0 0 1 0-3.8V7.512H3.067a9.996 9.996 0 0 0 0 8.977L6.404 13.9Z", fill: "#FBBC05" }),
|
|
210
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M12 5.977c1.468 0 2.786.505 3.823 1.497l2.868-2.868C16.96 2.986 14.696 2 12 2 8.118 2 4.76 4.232 3.067 7.51l3.337 2.59C7.19 7.737 9.396 5.977 12 5.977Z", fill: "#EA4335" })
|
|
211
|
+
] });
|
|
212
|
+
}
|
|
213
|
+
function AppleMark({ className }) {
|
|
214
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className, viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M17.564 12.73c-.037-3.16 2.58-4.678 2.698-4.752-1.47-2.146-3.76-2.44-4.576-2.473-1.948-.2-3.8 1.148-4.788 1.148-.993 0-2.513-1.12-4.13-1.091-2.127.03-4.085 1.236-5.174 3.142-2.207 3.82-.562 9.463 1.58 12.56 1.052 1.514 2.306 3.216 3.952 3.155 1.586-.065 2.185-1.026 4.102-1.026 1.917 0 2.455 1.026 4.133.99 1.705-.03 2.785-1.546 3.83-3.066 1.207-1.757 1.702-3.462 1.731-3.55-.038-.018-3.325-1.274-3.358-5.037Zm-3.154-9.24c.878-1.06 1.467-2.542 1.306-4.014-1.26.051-2.79.838-3.695 1.898-.813.937-1.524 2.433-1.333 3.885 1.405.108 2.843-.712 3.722-1.77Z" }) });
|
|
215
|
+
}
|
|
216
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
217
|
+
0 && (module.exports = {
|
|
218
|
+
AuthForm
|
|
219
|
+
});
|
|
220
|
+
//# sourceMappingURL=AuthForm.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/AuthForm.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useRouter, useSearchParams } from 'next/navigation';\nimport Link from 'next/link';\nimport { Loader2, AlertCircle } from 'lucide-react';\nimport { defaultEndpoints, type AuthEndpoints, type SocialProviders } from './types';\n\nexport interface AuthFormProps {\n mode: 'login' | 'signup';\n /** Display name shown in copy (\"New to Plugipay?\", \"Welcome back\"). */\n brand: string;\n /** Override the auth endpoint paths. Default matches Forjio family\n * `@forjio/sdk/auth-handlers` mounts. */\n endpoints?: Partial<AuthEndpoints>;\n /** Which social providers to render. Host fetches the provider\n * status; pass undefined to show all (fail-open). */\n providers?: SocialProviders | null;\n /** Default redirect target after a successful auth. Default\n * `/dashboard`. Search-param `?return_to=` overrides at runtime. */\n defaultReturnTo?: string;\n}\n\nexport function AuthForm({\n mode,\n brand,\n endpoints,\n providers,\n defaultReturnTo = '/dashboard',\n}: AuthFormProps) {\n const router = useRouter();\n const params = useSearchParams();\n const returnTo = params?.get('return_to') || defaultReturnTo;\n const ssoError = params?.get('sso_error');\n const ssoDetail = params?.get('sso_detail');\n const ep: AuthEndpoints = { ...defaultEndpoints, ...endpoints };\n\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [name, setName] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(\n ssoError ? `Sign-in failed: ${ssoDetail || ssoError}` : null,\n );\n\n async function submit(e: React.FormEvent) {\n e.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n const path = mode === 'signup' ? ep.signup : ep.login;\n const body: Record<string, unknown> = { email, password };\n if (mode === 'signup' && name.trim()) body.name = name.trim();\n const res = await fetch(path, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(body),\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 router.push(returnTo);\n router.refresh();\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setSubmitting(false);\n }\n }\n\n const otherMode = mode === 'login' ? 'signup' : 'login';\n const otherHref = `/${otherMode}?return_to=${encodeURIComponent(returnTo)}`;\n const socialUrl = (provider: 'google' | 'apple') =>\n `${ep.socialStart}?provider=${provider}&return_to=${encodeURIComponent(returnTo)}`;\n\n const showGoogle = providers?.google !== false;\n const showApple = providers?.apple !== false;\n const hasAnySocial = showGoogle || showApple;\n\n return (\n <div 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\n {hasAnySocial && (\n <>\n <div className=\"grid gap-2\">\n {showGoogle && (\n <a\n href={socialUrl('google')}\n className=\"flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent\"\n >\n <GoogleMark className=\"h-4 w-4\" />\n Continue with Google\n </a>\n )}\n {showApple && (\n <a\n href={socialUrl('apple')}\n className=\"flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent\"\n >\n <AppleMark className=\"h-4 w-4\" />\n Continue with Apple\n </a>\n )}\n </div>\n\n <div className=\"my-4 flex items-center gap-3 text-[11px] text-muted-foreground\">\n <div className=\"flex-1 border-t border-border\" />\n OR\n <div className=\"flex-1 border-t border-border\" />\n </div>\n </>\n )}\n\n <form onSubmit={submit} className=\"space-y-3\">\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 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 <div>\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">Password</label>\n <input\n type=\"password\"\n required\n minLength={mode === 'signup' ? 10 : undefined}\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n autoComplete={mode === 'signup' ? 'new-password' : 'current-password'}\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 {mode === 'signup' && (\n <p className=\"mt-1 text-[11px] text-muted-foreground\">At least 10 characters, with a letter and a number.</p>\n )}\n </div>\n {mode === 'signup' && (\n <div>\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">\n Your name <span className=\"text-muted-foreground/60\">(optional)</span>\n </label>\n <input\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n autoComplete=\"name\"\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 )}\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\n ? mode === 'signup'\n ? 'Creating…'\n : 'Signing in…'\n : mode === 'signup'\n ? 'Create account'\n : 'Sign in'}\n </button>\n </form>\n\n <div className=\"flex items-center justify-between pt-2 text-xs text-muted-foreground\">\n {mode === 'login' && (\n <Link href=\"/forgot-password\" className=\"hover:text-foreground\">\n Forgot password?\n </Link>\n )}\n <span className={mode === 'login' ? '' : 'ml-auto'}>\n {mode === 'login' ? `New to ${brand}?` : 'Already have an account?'}{' '}\n <Link href={otherHref} className=\"font-medium text-foreground hover:underline\">\n {mode === 'login' ? 'Sign up' : 'Sign in'}\n </Link>\n </span>\n </div>\n <p className=\"pt-2 text-[11px] leading-relaxed text-muted-foreground/80\">\n Identity is powered by{' '}\n <a href=\"https://huudis.com\" className=\"underline hover:text-foreground\">\n Huudis\n </a>\n . One account for every Forjio product.\n </p>\n </div>\n );\n}\n\nfunction GoogleMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M21.6 12.227c0-.708-.064-1.39-.182-2.045H12v3.868h5.384a4.603 4.603 0 0 1-1.997 3.018v2.51h3.232c1.891-1.742 2.98-4.307 2.98-7.35Z\" fill=\"#4285F4\" />\n <path d=\"M12 22c2.7 0 4.965-.895 6.62-2.422l-3.233-2.51c-.895.6-2.041.955-3.386.955-2.604 0-4.81-1.76-5.596-4.122H3.067v2.59A9.996 9.996 0 0 0 12 22Z\" fill=\"#34A853\" />\n <path d=\"M6.404 13.9a6.016 6.016 0 0 1 0-3.8V7.512H3.067a9.996 9.996 0 0 0 0 8.977L6.404 13.9Z\" fill=\"#FBBC05\" />\n <path d=\"M12 5.977c1.468 0 2.786.505 3.823 1.497l2.868-2.868C16.96 2.986 14.696 2 12 2 8.118 2 4.76 4.232 3.067 7.51l3.337 2.59C7.19 7.737 9.396 5.977 12 5.977Z\" fill=\"#EA4335\" />\n </svg>\n );\n}\n\nfunction AppleMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M17.564 12.73c-.037-3.16 2.58-4.678 2.698-4.752-1.47-2.146-3.76-2.44-4.576-2.473-1.948-.2-3.8 1.148-4.788 1.148-.993 0-2.513-1.12-4.13-1.091-2.127.03-4.085 1.236-5.174 3.142-2.207 3.82-.562 9.463 1.58 12.56 1.052 1.514 2.306 3.216 3.952 3.155 1.586-.065 2.185-1.026 4.102-1.026 1.917 0 2.455 1.026 4.133.99 1.705-.03 2.785-1.546 3.83-3.066 1.207-1.757 1.702-3.462 1.731-3.55-.038-.018-3.325-1.274-3.358-5.037Zm-3.154-9.24c.878-1.06 1.467-2.542 1.306-4.014-1.26.051-2.79.838-3.695 1.898-.813.937-1.524 2.433-1.333 3.885 1.405.108 2.843-.712 3.722-1.77Z\" />\n </svg>\n );\n}\n"],"mappings":"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","names":["Link"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { AuthEndpoints, SocialProviders } from './types.cjs';
|
|
3
|
+
|
|
4
|
+
interface AuthFormProps {
|
|
5
|
+
mode: 'login' | 'signup';
|
|
6
|
+
/** Display name shown in copy ("New to Plugipay?", "Welcome back"). */
|
|
7
|
+
brand: string;
|
|
8
|
+
/** Override the auth endpoint paths. Default matches Forjio family
|
|
9
|
+
* `@forjio/sdk/auth-handlers` mounts. */
|
|
10
|
+
endpoints?: Partial<AuthEndpoints>;
|
|
11
|
+
/** Which social providers to render. Host fetches the provider
|
|
12
|
+
* status; pass undefined to show all (fail-open). */
|
|
13
|
+
providers?: SocialProviders | null;
|
|
14
|
+
/** Default redirect target after a successful auth. Default
|
|
15
|
+
* `/dashboard`. Search-param `?return_to=` overrides at runtime. */
|
|
16
|
+
defaultReturnTo?: string;
|
|
17
|
+
}
|
|
18
|
+
declare function AuthForm({ mode, brand, endpoints, providers, defaultReturnTo, }: AuthFormProps): react.JSX.Element;
|
|
19
|
+
|
|
20
|
+
export { AuthForm, type AuthFormProps };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { AuthEndpoints, SocialProviders } from './types.js';
|
|
3
|
+
|
|
4
|
+
interface AuthFormProps {
|
|
5
|
+
mode: 'login' | 'signup';
|
|
6
|
+
/** Display name shown in copy ("New to Plugipay?", "Welcome back"). */
|
|
7
|
+
brand: string;
|
|
8
|
+
/** Override the auth endpoint paths. Default matches Forjio family
|
|
9
|
+
* `@forjio/sdk/auth-handlers` mounts. */
|
|
10
|
+
endpoints?: Partial<AuthEndpoints>;
|
|
11
|
+
/** Which social providers to render. Host fetches the provider
|
|
12
|
+
* status; pass undefined to show all (fail-open). */
|
|
13
|
+
providers?: SocialProviders | null;
|
|
14
|
+
/** Default redirect target after a successful auth. Default
|
|
15
|
+
* `/dashboard`. Search-param `?return_to=` overrides at runtime. */
|
|
16
|
+
defaultReturnTo?: string;
|
|
17
|
+
}
|
|
18
|
+
declare function AuthForm({ mode, brand, endpoints, providers, defaultReturnTo, }: AuthFormProps): react.JSX.Element;
|
|
19
|
+
|
|
20
|
+
export { AuthForm, type AuthFormProps };
|
package/dist/AuthForm.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, 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 } from "lucide-react";
|
|
7
|
+
import { defaultEndpoints } from "./types";
|
|
8
|
+
function AuthForm({
|
|
9
|
+
mode,
|
|
10
|
+
brand,
|
|
11
|
+
endpoints,
|
|
12
|
+
providers,
|
|
13
|
+
defaultReturnTo = "/dashboard"
|
|
14
|
+
}) {
|
|
15
|
+
const router = useRouter();
|
|
16
|
+
const params = useSearchParams();
|
|
17
|
+
const returnTo = params?.get("return_to") || defaultReturnTo;
|
|
18
|
+
const ssoError = params?.get("sso_error");
|
|
19
|
+
const ssoDetail = params?.get("sso_detail");
|
|
20
|
+
const ep = { ...defaultEndpoints, ...endpoints };
|
|
21
|
+
const [email, setEmail] = useState("");
|
|
22
|
+
const [password, setPassword] = useState("");
|
|
23
|
+
const [name, setName] = useState("");
|
|
24
|
+
const [submitting, setSubmitting] = useState(false);
|
|
25
|
+
const [error, setError] = useState(
|
|
26
|
+
ssoError ? `Sign-in failed: ${ssoDetail || ssoError}` : null
|
|
27
|
+
);
|
|
28
|
+
async function submit(e) {
|
|
29
|
+
e.preventDefault();
|
|
30
|
+
setError(null);
|
|
31
|
+
setSubmitting(true);
|
|
32
|
+
try {
|
|
33
|
+
const path = mode === "signup" ? ep.signup : ep.login;
|
|
34
|
+
const body = { email, password };
|
|
35
|
+
if (mode === "signup" && name.trim()) body.name = name.trim();
|
|
36
|
+
const res = await fetch(path, {
|
|
37
|
+
method: "POST",
|
|
38
|
+
headers: { "Content-Type": "application/json" },
|
|
39
|
+
credentials: "include",
|
|
40
|
+
body: JSON.stringify(body)
|
|
41
|
+
});
|
|
42
|
+
if (!res.ok) {
|
|
43
|
+
const payload = await res.json().catch(() => null);
|
|
44
|
+
throw new Error(payload?.error?.message ?? `Request failed (${res.status})`);
|
|
45
|
+
}
|
|
46
|
+
router.push(returnTo);
|
|
47
|
+
router.refresh();
|
|
48
|
+
} catch (err) {
|
|
49
|
+
setError(err.message);
|
|
50
|
+
} finally {
|
|
51
|
+
setSubmitting(false);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const otherMode = mode === "login" ? "signup" : "login";
|
|
55
|
+
const otherHref = `/${otherMode}?return_to=${encodeURIComponent(returnTo)}`;
|
|
56
|
+
const socialUrl = (provider) => `${ep.socialStart}?provider=${provider}&return_to=${encodeURIComponent(returnTo)}`;
|
|
57
|
+
const showGoogle = providers?.google !== false;
|
|
58
|
+
const showApple = providers?.apple !== false;
|
|
59
|
+
const hasAnySocial = showGoogle || showApple;
|
|
60
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
61
|
+
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: [
|
|
62
|
+
/* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4 shrink-0 mt-0.5" }),
|
|
63
|
+
/* @__PURE__ */ jsx("span", { children: error })
|
|
64
|
+
] }),
|
|
65
|
+
hasAnySocial && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
66
|
+
/* @__PURE__ */ jsxs("div", { className: "grid gap-2", children: [
|
|
67
|
+
showGoogle && /* @__PURE__ */ jsxs(
|
|
68
|
+
"a",
|
|
69
|
+
{
|
|
70
|
+
href: socialUrl("google"),
|
|
71
|
+
className: "flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent",
|
|
72
|
+
children: [
|
|
73
|
+
/* @__PURE__ */ jsx(GoogleMark, { className: "h-4 w-4" }),
|
|
74
|
+
"Continue with Google"
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
),
|
|
78
|
+
showApple && /* @__PURE__ */ jsxs(
|
|
79
|
+
"a",
|
|
80
|
+
{
|
|
81
|
+
href: socialUrl("apple"),
|
|
82
|
+
className: "flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent",
|
|
83
|
+
children: [
|
|
84
|
+
/* @__PURE__ */ jsx(AppleMark, { className: "h-4 w-4" }),
|
|
85
|
+
"Continue with Apple"
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
] }),
|
|
90
|
+
/* @__PURE__ */ jsxs("div", { className: "my-4 flex items-center gap-3 text-[11px] text-muted-foreground", children: [
|
|
91
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 border-t border-border" }),
|
|
92
|
+
"OR",
|
|
93
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 border-t border-border" })
|
|
94
|
+
] })
|
|
95
|
+
] }),
|
|
96
|
+
/* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-3", children: [
|
|
97
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
98
|
+
/* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Email" }),
|
|
99
|
+
/* @__PURE__ */ jsx(
|
|
100
|
+
"input",
|
|
101
|
+
{
|
|
102
|
+
type: "email",
|
|
103
|
+
required: true,
|
|
104
|
+
value: email,
|
|
105
|
+
onChange: (e) => setEmail(e.target.value),
|
|
106
|
+
autoComplete: "email",
|
|
107
|
+
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"
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
] }),
|
|
111
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
112
|
+
/* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Password" }),
|
|
113
|
+
/* @__PURE__ */ jsx(
|
|
114
|
+
"input",
|
|
115
|
+
{
|
|
116
|
+
type: "password",
|
|
117
|
+
required: true,
|
|
118
|
+
minLength: mode === "signup" ? 10 : void 0,
|
|
119
|
+
value: password,
|
|
120
|
+
onChange: (e) => setPassword(e.target.value),
|
|
121
|
+
autoComplete: mode === "signup" ? "new-password" : "current-password",
|
|
122
|
+
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"
|
|
123
|
+
}
|
|
124
|
+
),
|
|
125
|
+
mode === "signup" && /* @__PURE__ */ jsx("p", { className: "mt-1 text-[11px] text-muted-foreground", children: "At least 10 characters, with a letter and a number." })
|
|
126
|
+
] }),
|
|
127
|
+
mode === "signup" && /* @__PURE__ */ jsxs("div", { children: [
|
|
128
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: [
|
|
129
|
+
"Your name ",
|
|
130
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/60", children: "(optional)" })
|
|
131
|
+
] }),
|
|
132
|
+
/* @__PURE__ */ jsx(
|
|
133
|
+
"input",
|
|
134
|
+
{
|
|
135
|
+
type: "text",
|
|
136
|
+
value: name,
|
|
137
|
+
onChange: (e) => setName(e.target.value),
|
|
138
|
+
autoComplete: "name",
|
|
139
|
+
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"
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
] }),
|
|
143
|
+
/* @__PURE__ */ jsxs(
|
|
144
|
+
"button",
|
|
145
|
+
{
|
|
146
|
+
type: "submit",
|
|
147
|
+
disabled: submitting,
|
|
148
|
+
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",
|
|
149
|
+
children: [
|
|
150
|
+
submitting && /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }),
|
|
151
|
+
submitting ? mode === "signup" ? "Creating\u2026" : "Signing in\u2026" : mode === "signup" ? "Create account" : "Sign in"
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
] }),
|
|
156
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pt-2 text-xs text-muted-foreground", children: [
|
|
157
|
+
mode === "login" && /* @__PURE__ */ jsx(Link, { href: "/forgot-password", className: "hover:text-foreground", children: "Forgot password?" }),
|
|
158
|
+
/* @__PURE__ */ jsxs("span", { className: mode === "login" ? "" : "ml-auto", children: [
|
|
159
|
+
mode === "login" ? `New to ${brand}?` : "Already have an account?",
|
|
160
|
+
" ",
|
|
161
|
+
/* @__PURE__ */ jsx(Link, { href: otherHref, className: "font-medium text-foreground hover:underline", children: mode === "login" ? "Sign up" : "Sign in" })
|
|
162
|
+
] })
|
|
163
|
+
] }),
|
|
164
|
+
/* @__PURE__ */ jsxs("p", { className: "pt-2 text-[11px] leading-relaxed text-muted-foreground/80", children: [
|
|
165
|
+
"Identity is powered by",
|
|
166
|
+
" ",
|
|
167
|
+
/* @__PURE__ */ jsx("a", { href: "https://huudis.com", className: "underline hover:text-foreground", children: "Huudis" }),
|
|
168
|
+
". One account for every Forjio product."
|
|
169
|
+
] })
|
|
170
|
+
] });
|
|
171
|
+
}
|
|
172
|
+
function GoogleMark({ className }) {
|
|
173
|
+
return /* @__PURE__ */ jsxs("svg", { className, viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [
|
|
174
|
+
/* @__PURE__ */ jsx("path", { d: "M21.6 12.227c0-.708-.064-1.39-.182-2.045H12v3.868h5.384a4.603 4.603 0 0 1-1.997 3.018v2.51h3.232c1.891-1.742 2.98-4.307 2.98-7.35Z", fill: "#4285F4" }),
|
|
175
|
+
/* @__PURE__ */ jsx("path", { d: "M12 22c2.7 0 4.965-.895 6.62-2.422l-3.233-2.51c-.895.6-2.041.955-3.386.955-2.604 0-4.81-1.76-5.596-4.122H3.067v2.59A9.996 9.996 0 0 0 12 22Z", fill: "#34A853" }),
|
|
176
|
+
/* @__PURE__ */ jsx("path", { d: "M6.404 13.9a6.016 6.016 0 0 1 0-3.8V7.512H3.067a9.996 9.996 0 0 0 0 8.977L6.404 13.9Z", fill: "#FBBC05" }),
|
|
177
|
+
/* @__PURE__ */ jsx("path", { d: "M12 5.977c1.468 0 2.786.505 3.823 1.497l2.868-2.868C16.96 2.986 14.696 2 12 2 8.118 2 4.76 4.232 3.067 7.51l3.337 2.59C7.19 7.737 9.396 5.977 12 5.977Z", fill: "#EA4335" })
|
|
178
|
+
] });
|
|
179
|
+
}
|
|
180
|
+
function AppleMark({ className }) {
|
|
181
|
+
return /* @__PURE__ */ jsx("svg", { className, viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M17.564 12.73c-.037-3.16 2.58-4.678 2.698-4.752-1.47-2.146-3.76-2.44-4.576-2.473-1.948-.2-3.8 1.148-4.788 1.148-.993 0-2.513-1.12-4.13-1.091-2.127.03-4.085 1.236-5.174 3.142-2.207 3.82-.562 9.463 1.58 12.56 1.052 1.514 2.306 3.216 3.952 3.155 1.586-.065 2.185-1.026 4.102-1.026 1.917 0 2.455 1.026 4.133.99 1.705-.03 2.785-1.546 3.83-3.066 1.207-1.757 1.702-3.462 1.731-3.55-.038-.018-3.325-1.274-3.358-5.037Zm-3.154-9.24c.878-1.06 1.467-2.542 1.306-4.014-1.26.051-2.79.838-3.695 1.898-.813.937-1.524 2.433-1.333 3.885 1.405.108 2.843-.712 3.722-1.77Z" }) });
|
|
182
|
+
}
|
|
183
|
+
export {
|
|
184
|
+
AuthForm
|
|
185
|
+
};
|
|
186
|
+
//# sourceMappingURL=AuthForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/AuthForm.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useRouter, useSearchParams } from 'next/navigation';\nimport Link from 'next/link';\nimport { Loader2, AlertCircle } from 'lucide-react';\nimport { defaultEndpoints, type AuthEndpoints, type SocialProviders } from './types';\n\nexport interface AuthFormProps {\n mode: 'login' | 'signup';\n /** Display name shown in copy (\"New to Plugipay?\", \"Welcome back\"). */\n brand: string;\n /** Override the auth endpoint paths. Default matches Forjio family\n * `@forjio/sdk/auth-handlers` mounts. */\n endpoints?: Partial<AuthEndpoints>;\n /** Which social providers to render. Host fetches the provider\n * status; pass undefined to show all (fail-open). */\n providers?: SocialProviders | null;\n /** Default redirect target after a successful auth. Default\n * `/dashboard`. Search-param `?return_to=` overrides at runtime. */\n defaultReturnTo?: string;\n}\n\nexport function AuthForm({\n mode,\n brand,\n endpoints,\n providers,\n defaultReturnTo = '/dashboard',\n}: AuthFormProps) {\n const router = useRouter();\n const params = useSearchParams();\n const returnTo = params?.get('return_to') || defaultReturnTo;\n const ssoError = params?.get('sso_error');\n const ssoDetail = params?.get('sso_detail');\n const ep: AuthEndpoints = { ...defaultEndpoints, ...endpoints };\n\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [name, setName] = useState('');\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(\n ssoError ? `Sign-in failed: ${ssoDetail || ssoError}` : null,\n );\n\n async function submit(e: React.FormEvent) {\n e.preventDefault();\n setError(null);\n setSubmitting(true);\n try {\n const path = mode === 'signup' ? ep.signup : ep.login;\n const body: Record<string, unknown> = { email, password };\n if (mode === 'signup' && name.trim()) body.name = name.trim();\n const res = await fetch(path, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(body),\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 router.push(returnTo);\n router.refresh();\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setSubmitting(false);\n }\n }\n\n const otherMode = mode === 'login' ? 'signup' : 'login';\n const otherHref = `/${otherMode}?return_to=${encodeURIComponent(returnTo)}`;\n const socialUrl = (provider: 'google' | 'apple') =>\n `${ep.socialStart}?provider=${provider}&return_to=${encodeURIComponent(returnTo)}`;\n\n const showGoogle = providers?.google !== false;\n const showApple = providers?.apple !== false;\n const hasAnySocial = showGoogle || showApple;\n\n return (\n <div 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\n {hasAnySocial && (\n <>\n <div className=\"grid gap-2\">\n {showGoogle && (\n <a\n href={socialUrl('google')}\n className=\"flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent\"\n >\n <GoogleMark className=\"h-4 w-4\" />\n Continue with Google\n </a>\n )}\n {showApple && (\n <a\n href={socialUrl('apple')}\n className=\"flex w-full items-center justify-center gap-2 rounded-md border border-border bg-background py-2 text-sm font-medium hover:bg-accent\"\n >\n <AppleMark className=\"h-4 w-4\" />\n Continue with Apple\n </a>\n )}\n </div>\n\n <div className=\"my-4 flex items-center gap-3 text-[11px] text-muted-foreground\">\n <div className=\"flex-1 border-t border-border\" />\n OR\n <div className=\"flex-1 border-t border-border\" />\n </div>\n </>\n )}\n\n <form onSubmit={submit} className=\"space-y-3\">\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 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 <div>\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">Password</label>\n <input\n type=\"password\"\n required\n minLength={mode === 'signup' ? 10 : undefined}\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n autoComplete={mode === 'signup' ? 'new-password' : 'current-password'}\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 {mode === 'signup' && (\n <p className=\"mt-1 text-[11px] text-muted-foreground\">At least 10 characters, with a letter and a number.</p>\n )}\n </div>\n {mode === 'signup' && (\n <div>\n <label className=\"mb-1 block text-xs font-medium text-muted-foreground\">\n Your name <span className=\"text-muted-foreground/60\">(optional)</span>\n </label>\n <input\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n autoComplete=\"name\"\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 )}\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\n ? mode === 'signup'\n ? 'Creating…'\n : 'Signing in…'\n : mode === 'signup'\n ? 'Create account'\n : 'Sign in'}\n </button>\n </form>\n\n <div className=\"flex items-center justify-between pt-2 text-xs text-muted-foreground\">\n {mode === 'login' && (\n <Link href=\"/forgot-password\" className=\"hover:text-foreground\">\n Forgot password?\n </Link>\n )}\n <span className={mode === 'login' ? '' : 'ml-auto'}>\n {mode === 'login' ? `New to ${brand}?` : 'Already have an account?'}{' '}\n <Link href={otherHref} className=\"font-medium text-foreground hover:underline\">\n {mode === 'login' ? 'Sign up' : 'Sign in'}\n </Link>\n </span>\n </div>\n <p className=\"pt-2 text-[11px] leading-relaxed text-muted-foreground/80\">\n Identity is powered by{' '}\n <a href=\"https://huudis.com\" className=\"underline hover:text-foreground\">\n Huudis\n </a>\n . One account for every Forjio product.\n </p>\n </div>\n );\n}\n\nfunction GoogleMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M21.6 12.227c0-.708-.064-1.39-.182-2.045H12v3.868h5.384a4.603 4.603 0 0 1-1.997 3.018v2.51h3.232c1.891-1.742 2.98-4.307 2.98-7.35Z\" fill=\"#4285F4\" />\n <path d=\"M12 22c2.7 0 4.965-.895 6.62-2.422l-3.233-2.51c-.895.6-2.041.955-3.386.955-2.604 0-4.81-1.76-5.596-4.122H3.067v2.59A9.996 9.996 0 0 0 12 22Z\" fill=\"#34A853\" />\n <path d=\"M6.404 13.9a6.016 6.016 0 0 1 0-3.8V7.512H3.067a9.996 9.996 0 0 0 0 8.977L6.404 13.9Z\" fill=\"#FBBC05\" />\n <path d=\"M12 5.977c1.468 0 2.786.505 3.823 1.497l2.868-2.868C16.96 2.986 14.696 2 12 2 8.118 2 4.76 4.232 3.067 7.51l3.337 2.59C7.19 7.737 9.396 5.977 12 5.977Z\" fill=\"#EA4335\" />\n </svg>\n );\n}\n\nfunction AppleMark({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M17.564 12.73c-.037-3.16 2.58-4.678 2.698-4.752-1.47-2.146-3.76-2.44-4.576-2.473-1.948-.2-3.8 1.148-4.788 1.148-.993 0-2.513-1.12-4.13-1.091-2.127.03-4.085 1.236-5.174 3.142-2.207 3.82-.562 9.463 1.58 12.56 1.052 1.514 2.306 3.216 3.952 3.155 1.586-.065 2.185-1.026 4.102-1.026 1.917 0 2.455 1.026 4.133.99 1.705-.03 2.785-1.546 3.83-3.066 1.207-1.757 1.702-3.462 1.731-3.55-.038-.018-3.325-1.274-3.358-5.037Zm-3.154-9.24c.878-1.06 1.467-2.542 1.306-4.014-1.26.051-2.79.838-3.695 1.898-.813.937-1.524 2.433-1.333 3.885 1.405.108 2.843-.712 3.722-1.77Z\" />\n </svg>\n );\n}\n"],"mappings":";AAoFQ,SAOA,UANE,KADF;AAlFR,SAAS,gBAAgB;AACzB,SAAS,WAAW,uBAAuB;AAC3C,OAAO,UAAU;AACjB,SAAS,SAAS,mBAAmB;AACrC,SAAS,wBAAkE;AAiBpE,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB;AACpB,GAAkB;AAChB,QAAM,SAAS,UAAU;AACzB,QAAM,SAAS,gBAAgB;AAC/B,QAAM,WAAW,QAAQ,IAAI,WAAW,KAAK;AAC7C,QAAM,WAAW,QAAQ,IAAI,WAAW;AACxC,QAAM,YAAY,QAAQ,IAAI,YAAY;AAC1C,QAAM,KAAoB,EAAE,GAAG,kBAAkB,GAAG,UAAU;AAE9D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAE;AACrC,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,EAAE;AAC3C,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,EAAE;AACnC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,OAAO,QAAQ,IAAI;AAAA,IACxB,WAAW,mBAAmB,aAAa,QAAQ,KAAK;AAAA,EAC1D;AAEA,iBAAe,OAAO,GAAoB;AACxC,MAAE,eAAe;AACjB,aAAS,IAAI;AACb,kBAAc,IAAI;AAClB,QAAI;AACF,YAAM,OAAO,SAAS,WAAW,GAAG,SAAS,GAAG;AAChD,YAAM,OAAgC,EAAE,OAAO,SAAS;AACxD,UAAI,SAAS,YAAY,KAAK,KAAK,EAAG,MAAK,OAAO,KAAK,KAAK;AAC5D,YAAM,MAAM,MAAM,MAAM,MAAM;AAAA,QAC5B,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,aAAa;AAAA,QACb,MAAM,KAAK,UAAU,IAAI;AAAA,MAC3B,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,aAAO,KAAK,QAAQ;AACpB,aAAO,QAAQ;AAAA,IACjB,SAAS,KAAK;AACZ,eAAU,IAAc,OAAO;AAAA,IACjC,UAAE;AACA,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,QAAM,YAAY,SAAS,UAAU,WAAW;AAChD,QAAM,YAAY,IAAI,SAAS,cAAc,mBAAmB,QAAQ,CAAC;AACzE,QAAM,YAAY,CAAC,aACjB,GAAG,GAAG,WAAW,aAAa,QAAQ,cAAc,mBAAmB,QAAQ,CAAC;AAElF,QAAM,aAAa,WAAW,WAAW;AACzC,QAAM,YAAY,WAAW,UAAU;AACvC,QAAM,eAAe,cAAc;AAEnC,SACE,qBAAC,SAAI,WAAU,aACZ;AAAA,aACC,qBAAC,SAAI,WAAU,uHACb;AAAA,0BAAC,eAAY,WAAU,2BAA0B;AAAA,MACjD,oBAAC,UAAM,iBAAM;AAAA,OACf;AAAA,IAGD,gBACC,iCACE;AAAA,2BAAC,SAAI,WAAU,cACZ;AAAA,sBACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAM,UAAU,QAAQ;AAAA,YACxB,WAAU;AAAA,YAEV;AAAA,kCAAC,cAAW,WAAU,WAAU;AAAA,cAAE;AAAA;AAAA;AAAA,QAEpC;AAAA,QAED,aACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAM,UAAU,OAAO;AAAA,YACvB,WAAU;AAAA,YAEV;AAAA,kCAAC,aAAU,WAAU,WAAU;AAAA,cAAE;AAAA;AAAA;AAAA,QAEnC;AAAA,SAEJ;AAAA,MAEA,qBAAC,SAAI,WAAU,kEACb;AAAA,4BAAC,SAAI,WAAU,iCAAgC;AAAA,QAAE;AAAA,QAEjD,oBAAC,SAAI,WAAU,iCAAgC;AAAA,SACjD;AAAA,OACF;AAAA,IAGF,qBAAC,UAAK,UAAU,QAAQ,WAAU,aAChC;AAAA,2BAAC,SACC;AAAA,4BAAC,WAAM,WAAU,wDAAuD,mBAAK;AAAA,QAC7E;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,UAAQ;AAAA,YACR,OAAO;AAAA,YACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,YACxC,cAAa;AAAA,YACb,WAAU;AAAA;AAAA,QACZ;AAAA,SACF;AAAA,MACA,qBAAC,SACC;AAAA,4BAAC,WAAM,WAAU,wDAAuD,sBAAQ;AAAA,QAChF;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,UAAQ;AAAA,YACR,WAAW,SAAS,WAAW,KAAK;AAAA,YACpC,OAAO;AAAA,YACP,UAAU,CAAC,MAAM,YAAY,EAAE,OAAO,KAAK;AAAA,YAC3C,cAAc,SAAS,WAAW,iBAAiB;AAAA,YACnD,WAAU;AAAA;AAAA,QACZ;AAAA,QACC,SAAS,YACR,oBAAC,OAAE,WAAU,0CAAyC,iEAAmD;AAAA,SAE7G;AAAA,MACC,SAAS,YACR,qBAAC,SACC;AAAA,6BAAC,WAAM,WAAU,wDAAuD;AAAA;AAAA,UAC5D,oBAAC,UAAK,WAAU,4BAA2B,wBAAU;AAAA,WACjE;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,OAAO;AAAA,YACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA,YACvC,cAAa;AAAA,YACb,WAAU;AAAA;AAAA,QACZ;AAAA,SACF;AAAA,MAEF;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,UAAU;AAAA,UACV,WAAU;AAAA,UAET;AAAA,0BAAc,oBAAC,WAAQ,WAAU,wBAAuB;AAAA,YACxD,aACG,SAAS,WACP,mBACA,qBACF,SAAS,WACT,mBACA;AAAA;AAAA;AAAA,MACN;AAAA,OACF;AAAA,IAEA,qBAAC,SAAI,WAAU,wEACZ;AAAA,eAAS,WACR,oBAAC,QAAK,MAAK,oBAAmB,WAAU,yBAAwB,8BAEhE;AAAA,MAEF,qBAAC,UAAK,WAAW,SAAS,UAAU,KAAK,WACtC;AAAA,iBAAS,UAAU,UAAU,KAAK,MAAM;AAAA,QAA4B;AAAA,QACrE,oBAAC,QAAK,MAAM,WAAW,WAAU,+CAC9B,mBAAS,UAAU,YAAY,WAClC;AAAA,SACF;AAAA,OACF;AAAA,IACA,qBAAC,OAAE,WAAU,6DAA4D;AAAA;AAAA,MAChD;AAAA,MACvB,oBAAC,OAAE,MAAK,sBAAqB,WAAU,mCAAkC,oBAEzE;AAAA,MAAI;AAAA,OAEN;AAAA,KACF;AAEJ;AAEA,SAAS,WAAW,EAAE,UAAU,GAA2B;AACzD,SACE,qBAAC,SAAI,WAAsB,SAAQ,aAAY,OAAM,8BAA6B,eAAY,QAC5F;AAAA,wBAAC,UAAK,GAAE,sIAAqI,MAAK,WAAU;AAAA,IAC5J,oBAAC,UAAK,GAAE,gJAA+I,MAAK,WAAU;AAAA,IACtK,oBAAC,UAAK,GAAE,yFAAwF,MAAK,WAAU;AAAA,IAC/G,oBAAC,UAAK,GAAE,2JAA0J,MAAK,WAAU;AAAA,KACnL;AAEJ;AAEA,SAAS,UAAU,EAAE,UAAU,GAA2B;AACxD,SACE,oBAAC,SAAI,WAAsB,SAAQ,aAAY,OAAM,8BAA6B,MAAK,gBAAe,eAAY,QAChH,8BAAC,UAAK,GAAE,2iBAA0iB,GACpjB;AAEJ;","names":[]}
|