@dravyn/auth-ui 0.1.1
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/dist/chunk-35G4WYX3.mjs +154 -0
- package/dist/chunk-35G4WYX3.mjs.map +1 -0
- package/dist/chunk-6BBGRN4E.mjs +1 -0
- package/dist/chunk-6BBGRN4E.mjs.map +1 -0
- package/dist/chunk-FAI3ERB3.mjs +209 -0
- package/dist/chunk-FAI3ERB3.mjs.map +1 -0
- package/dist/chunk-FVXZZTKL.mjs +152 -0
- package/dist/chunk-FVXZZTKL.mjs.map +1 -0
- package/dist/chunk-Z2ICX4UY.mjs +132 -0
- package/dist/chunk-Z2ICX4UY.mjs.map +1 -0
- package/dist/index.css +235 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +678 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +22 -0
- package/dist/index.mjs.map +1 -0
- package/dist/screens/ForgotResetScreen.css +235 -0
- package/dist/screens/ForgotResetScreen.css.map +1 -0
- package/dist/screens/ForgotResetScreen.d.mts +17 -0
- package/dist/screens/ForgotResetScreen.d.ts +17 -0
- package/dist/screens/ForgotResetScreen.js +245 -0
- package/dist/screens/ForgotResetScreen.js.map +1 -0
- package/dist/screens/ForgotResetScreen.mjs +11 -0
- package/dist/screens/ForgotResetScreen.mjs.map +1 -0
- package/dist/screens/LoginScreen.css +235 -0
- package/dist/screens/LoginScreen.css.map +1 -0
- package/dist/screens/LoginScreen.d.mts +36 -0
- package/dist/screens/LoginScreen.d.ts +36 -0
- package/dist/screens/LoginScreen.js +167 -0
- package/dist/screens/LoginScreen.js.map +1 -0
- package/dist/screens/LoginScreen.mjs +9 -0
- package/dist/screens/LoginScreen.mjs.map +1 -0
- package/dist/screens/OTPScreen.css +235 -0
- package/dist/screens/OTPScreen.css.map +1 -0
- package/dist/screens/OTPScreen.d.mts +16 -0
- package/dist/screens/OTPScreen.d.ts +16 -0
- package/dist/screens/OTPScreen.js +187 -0
- package/dist/screens/OTPScreen.js.map +1 -0
- package/dist/screens/OTPScreen.mjs +9 -0
- package/dist/screens/OTPScreen.mjs.map +1 -0
- package/dist/screens/RegisterScreen.css +235 -0
- package/dist/screens/RegisterScreen.css.map +1 -0
- package/dist/screens/RegisterScreen.d.mts +21 -0
- package/dist/screens/RegisterScreen.d.ts +21 -0
- package/dist/screens/RegisterScreen.js +189 -0
- package/dist/screens/RegisterScreen.js.map +1 -0
- package/dist/screens/RegisterScreen.mjs +9 -0
- package/dist/screens/RegisterScreen.mjs.map +1 -0
- package/package.json +39 -0
- package/src/screens/auth.css +260 -0
|
@@ -0,0 +1,189 @@
|
|
|
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
|
+
|
|
31
|
+
// src/screens/RegisterScreen.tsx
|
|
32
|
+
var RegisterScreen_exports = {};
|
|
33
|
+
__export(RegisterScreen_exports, {
|
|
34
|
+
RegisterScreen: () => RegisterScreen
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(RegisterScreen_exports);
|
|
37
|
+
var import_react = __toESM(require("react"));
|
|
38
|
+
var import_ui = require("@dravyn/ui");
|
|
39
|
+
var import_ui2 = require("@dravyn/ui");
|
|
40
|
+
var import_ui3 = require("@dravyn/ui");
|
|
41
|
+
var RegisterScreen = ({
|
|
42
|
+
redirectTo = "/auth/verify",
|
|
43
|
+
onSuccess,
|
|
44
|
+
logo,
|
|
45
|
+
brandName = "Dravyn",
|
|
46
|
+
apiBase = process.env.NEXT_PUBLIC_AUTH_API_URL ?? "http://localhost:3001",
|
|
47
|
+
extraFields,
|
|
48
|
+
requireOTP = true
|
|
49
|
+
}) => {
|
|
50
|
+
const [form, setForm] = import_react.default.useState({
|
|
51
|
+
firstName: "",
|
|
52
|
+
lastName: "",
|
|
53
|
+
email: "",
|
|
54
|
+
password: "",
|
|
55
|
+
confirm: ""
|
|
56
|
+
});
|
|
57
|
+
const [showPass, setShowPass] = import_react.default.useState(false);
|
|
58
|
+
const [loading, setLoading] = import_react.default.useState(false);
|
|
59
|
+
const [error, setError] = import_react.default.useState("");
|
|
60
|
+
const [fieldErrs, setFieldErrs] = import_react.default.useState({});
|
|
61
|
+
const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value }));
|
|
62
|
+
const validate = () => {
|
|
63
|
+
const errs = {};
|
|
64
|
+
if (!form.firstName.trim()) errs.firstName = "First name is required.";
|
|
65
|
+
if (!form.lastName.trim()) errs.lastName = "Last name is required.";
|
|
66
|
+
if (!form.email.trim()) errs.email = "Email is required.";
|
|
67
|
+
else if (!/\S+@\S+\.\S+/.test(form.email)) errs.email = "Enter a valid email.";
|
|
68
|
+
if (!form.password) errs.password = "Password is required.";
|
|
69
|
+
else if (form.password.length < 8) errs.password = "Password must be at least 8 characters.";
|
|
70
|
+
if (form.confirm !== form.password) errs.confirm = "Passwords do not match.";
|
|
71
|
+
return errs;
|
|
72
|
+
};
|
|
73
|
+
const handleSubmit = async (e) => {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
const errs = validate();
|
|
76
|
+
if (Object.keys(errs).length) {
|
|
77
|
+
setFieldErrs(errs);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
setFieldErrs({});
|
|
81
|
+
setLoading(true);
|
|
82
|
+
setError("");
|
|
83
|
+
try {
|
|
84
|
+
const res = await fetch(`${apiBase}/auth/register`, {
|
|
85
|
+
method: "POST",
|
|
86
|
+
headers: { "Content-Type": "application/json" },
|
|
87
|
+
body: JSON.stringify({
|
|
88
|
+
firstName: form.firstName.trim(),
|
|
89
|
+
lastName: form.lastName.trim(),
|
|
90
|
+
email: form.email.trim().toLowerCase(),
|
|
91
|
+
password: form.password
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
const data = await res.json();
|
|
95
|
+
if (!res.ok) {
|
|
96
|
+
setError(data.message ?? "Registration failed.");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (requireOTP) {
|
|
100
|
+
sessionStorage.setItem("dravyn_pending_email", form.email.trim().toLowerCase());
|
|
101
|
+
window.location.href = redirectTo;
|
|
102
|
+
} else {
|
|
103
|
+
localStorage.setItem("dravyn_token", data.accessToken);
|
|
104
|
+
localStorage.setItem("dravyn_user", JSON.stringify(data.user));
|
|
105
|
+
if (onSuccess) onSuccess(data.accessToken, data.user);
|
|
106
|
+
else window.location.href = redirectTo;
|
|
107
|
+
}
|
|
108
|
+
} catch {
|
|
109
|
+
setError("Unable to connect. Check your internet connection.");
|
|
110
|
+
} finally {
|
|
111
|
+
setLoading(false);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const EyeIcon = () => /* @__PURE__ */ import_react.default.createElement("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "3" }));
|
|
115
|
+
const EyeOffIcon = () => /* @__PURE__ */ import_react.default.createElement("svg", { width: "15", height: "15", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "2", x2: "22", y1: "2", y2: "22" }));
|
|
116
|
+
return /* @__PURE__ */ import_react.default.createElement("div", { className: "dauth-root" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dauth-card" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dauth-brand" }, logo ?? /* @__PURE__ */ import_react.default.createElement("div", { className: "dauth-logo-default" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "dauth-logo-dot" }), brandName)), /* @__PURE__ */ import_react.default.createElement("h1", { className: "dauth-title" }, "Create your account"), /* @__PURE__ */ import_react.default.createElement("p", { className: "dauth-sub" }, "Join ", brandName, " \u2014 it's free"), error && /* @__PURE__ */ import_react.default.createElement("div", { style: { marginBottom: 16 } }, /* @__PURE__ */ import_react.default.createElement(import_ui3.Alert, { variant: "danger" }, error)), /* @__PURE__ */ import_react.default.createElement("form", { onSubmit: handleSubmit, noValidate: true }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dauth-fields" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "dauth-row" }, /* @__PURE__ */ import_react.default.createElement(
|
|
117
|
+
import_ui2.Input,
|
|
118
|
+
{
|
|
119
|
+
label: "First name",
|
|
120
|
+
placeholder: "Jeremiah",
|
|
121
|
+
value: form.firstName,
|
|
122
|
+
onChange: set("firstName"),
|
|
123
|
+
error: fieldErrs.firstName,
|
|
124
|
+
required: true
|
|
125
|
+
}
|
|
126
|
+
), /* @__PURE__ */ import_react.default.createElement(
|
|
127
|
+
import_ui2.Input,
|
|
128
|
+
{
|
|
129
|
+
label: "Last name",
|
|
130
|
+
placeholder: "Adeniyi",
|
|
131
|
+
value: form.lastName,
|
|
132
|
+
onChange: set("lastName"),
|
|
133
|
+
error: fieldErrs.lastName,
|
|
134
|
+
required: true
|
|
135
|
+
}
|
|
136
|
+
)), /* @__PURE__ */ import_react.default.createElement(
|
|
137
|
+
import_ui2.Input,
|
|
138
|
+
{
|
|
139
|
+
label: "Email address",
|
|
140
|
+
type: "email",
|
|
141
|
+
placeholder: "you@example.com",
|
|
142
|
+
value: form.email,
|
|
143
|
+
onChange: set("email"),
|
|
144
|
+
error: fieldErrs.email,
|
|
145
|
+
autoComplete: "email",
|
|
146
|
+
required: true
|
|
147
|
+
}
|
|
148
|
+
), /* @__PURE__ */ import_react.default.createElement(
|
|
149
|
+
import_ui2.Input,
|
|
150
|
+
{
|
|
151
|
+
label: "Password",
|
|
152
|
+
type: showPass ? "text" : "password",
|
|
153
|
+
placeholder: "Min. 8 characters",
|
|
154
|
+
value: form.password,
|
|
155
|
+
onChange: set("password"),
|
|
156
|
+
error: fieldErrs.password,
|
|
157
|
+
hint: !fieldErrs.password ? "At least 8 characters." : void 0,
|
|
158
|
+
rightIcon: /* @__PURE__ */ import_react.default.createElement(
|
|
159
|
+
"button",
|
|
160
|
+
{
|
|
161
|
+
type: "button",
|
|
162
|
+
className: "dauth-eye",
|
|
163
|
+
onClick: () => setShowPass((v) => !v),
|
|
164
|
+
"aria-label": showPass ? "Hide" : "Show"
|
|
165
|
+
},
|
|
166
|
+
showPass ? /* @__PURE__ */ import_react.default.createElement(EyeOffIcon, null) : /* @__PURE__ */ import_react.default.createElement(EyeIcon, null)
|
|
167
|
+
),
|
|
168
|
+
autoComplete: "new-password",
|
|
169
|
+
required: true
|
|
170
|
+
}
|
|
171
|
+
), /* @__PURE__ */ import_react.default.createElement(
|
|
172
|
+
import_ui2.Input,
|
|
173
|
+
{
|
|
174
|
+
label: "Confirm password",
|
|
175
|
+
type: showPass ? "text" : "password",
|
|
176
|
+
placeholder: "Re-enter your password",
|
|
177
|
+
value: form.confirm,
|
|
178
|
+
onChange: set("confirm"),
|
|
179
|
+
error: fieldErrs.confirm,
|
|
180
|
+
autoComplete: "new-password",
|
|
181
|
+
required: true
|
|
182
|
+
}
|
|
183
|
+
), extraFields), /* @__PURE__ */ import_react.default.createElement(import_ui.Button, { type: "submit", variant: "primary", fullWidth: true, loading, style: { marginTop: 8 } }, "Create account")), /* @__PURE__ */ import_react.default.createElement("p", { className: "dauth-footer-text", style: { marginTop: 20 } }, "Already have an account?", " ", /* @__PURE__ */ import_react.default.createElement("a", { href: "/auth/login", className: "dauth-link" }, "Sign in")), /* @__PURE__ */ import_react.default.createElement("p", { className: "dauth-terms" }, "By creating an account you agree to our", " ", /* @__PURE__ */ import_react.default.createElement("a", { href: "/terms", className: "dauth-link" }, "Terms of Service"), " ", "and", " ", /* @__PURE__ */ import_react.default.createElement("a", { href: "/privacy", className: "dauth-link" }, "Privacy Policy"), ".")));
|
|
184
|
+
};
|
|
185
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
186
|
+
0 && (module.exports = {
|
|
187
|
+
RegisterScreen
|
|
188
|
+
});
|
|
189
|
+
//# sourceMappingURL=RegisterScreen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/screens/RegisterScreen.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\nimport { Button } from '@dravyn/ui';\nimport { Input } from '@dravyn/ui';\nimport { Alert } from '@dravyn/ui';\nimport './auth.css';\n\nexport interface RegisterScreenProps {\n redirectTo?: string;\n onSuccess?: (token: string, user: AuthUser) => void;\n logo?: React.ReactNode;\n brandName?: string;\n apiBase?: string;\n /** Extra fields to collect — rendered after the core fields */\n extraFields?: React.ReactNode;\n /** If true, redirect to OTP screen after register instead of auto-login */\n requireOTP?: boolean;\n}\n\ninterface AuthUser {\n id: string; email: string; name: string;\n}\n\nexport const RegisterScreen: React.FC<RegisterScreenProps> = ({\n redirectTo = '/auth/verify',\n onSuccess,\n logo,\n brandName = 'Dravyn',\n apiBase = process.env.NEXT_PUBLIC_AUTH_API_URL ?? 'http://localhost:3001',\n extraFields,\n requireOTP = true,\n}) => {\n const [form, setForm] = React.useState({\n firstName: '', lastName: '', email: '', password: '', confirm: '',\n });\n const [showPass, setShowPass] = React.useState(false);\n const [loading, setLoading] = React.useState(false);\n const [error, setError] = React.useState('');\n const [fieldErrs, setFieldErrs] = React.useState<Record<string, string>>({});\n\n const set = (k: keyof typeof form) => (e: React.ChangeEvent<HTMLInputElement>) =>\n setForm(f => ({ ...f, [k]: e.target.value }));\n\n const validate = () => {\n const errs: Record<string, string> = {};\n if (!form.firstName.trim()) errs.firstName = 'First name is required.';\n if (!form.lastName.trim()) errs.lastName = 'Last name is required.';\n if (!form.email.trim()) errs.email = 'Email is required.';\n else if (!/\\S+@\\S+\\.\\S+/.test(form.email)) errs.email = 'Enter a valid email.';\n if (!form.password) errs.password = 'Password is required.';\n else if (form.password.length < 8) errs.password = 'Password must be at least 8 characters.';\n if (form.confirm !== form.password) errs.confirm = 'Passwords do not match.';\n return errs;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n const errs = validate();\n if (Object.keys(errs).length) { setFieldErrs(errs); return; }\n setFieldErrs({});\n setLoading(true);\n setError('');\n\n try {\n const res = await fetch(`${apiBase}/auth/register`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n firstName: form.firstName.trim(),\n lastName: form.lastName.trim(),\n email: form.email.trim().toLowerCase(),\n password: form.password,\n }),\n });\n const data = await res.json();\n if (!res.ok) { setError(data.message ?? 'Registration failed.'); return; }\n\n if (requireOTP) {\n // Store email for OTP screen to use\n sessionStorage.setItem('dravyn_pending_email', form.email.trim().toLowerCase());\n window.location.href = redirectTo;\n } else {\n localStorage.setItem('dravyn_token', data.accessToken);\n localStorage.setItem('dravyn_user', JSON.stringify(data.user));\n if (onSuccess) onSuccess(data.accessToken, data.user);\n else window.location.href = redirectTo;\n }\n } catch {\n setError('Unable to connect. Check your internet connection.');\n } finally {\n setLoading(false);\n }\n };\n\n const EyeIcon = () => <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></svg>;\n const EyeOffIcon = () => <svg width=\"15\" height=\"15\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M9.88 9.88a3 3 0 1 0 4.24 4.24\"/><path d=\"M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68\"/><path d=\"M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61\"/><line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\"/></svg>;\n\n return (\n <div className=\"dauth-root\">\n <div className=\"dauth-card\">\n <div className=\"dauth-brand\">\n {logo ?? <div className=\"dauth-logo-default\"><span className=\"dauth-logo-dot\" />{brandName}</div>}\n </div>\n\n <h1 className=\"dauth-title\">Create your account</h1>\n <p className=\"dauth-sub\">Join {brandName} — it's free</p>\n\n {error && <div style={{ marginBottom: 16 }}><Alert variant=\"danger\">{error}</Alert></div>}\n\n <form onSubmit={handleSubmit} noValidate>\n <div className=\"dauth-fields\">\n <div className=\"dauth-row\">\n <Input\n label=\"First name\"\n placeholder=\"Jeremiah\"\n value={form.firstName}\n onChange={set('firstName')}\n error={fieldErrs.firstName}\n required\n />\n <Input\n label=\"Last name\"\n placeholder=\"Adeniyi\"\n value={form.lastName}\n onChange={set('lastName')}\n error={fieldErrs.lastName}\n required\n />\n </div>\n\n <Input\n label=\"Email address\"\n type=\"email\"\n placeholder=\"you@example.com\"\n value={form.email}\n onChange={set('email')}\n error={fieldErrs.email}\n autoComplete=\"email\"\n required\n />\n\n <Input\n label=\"Password\"\n type={showPass ? 'text' : 'password'}\n placeholder=\"Min. 8 characters\"\n value={form.password}\n onChange={set('password')}\n error={fieldErrs.password}\n hint={!fieldErrs.password ? 'At least 8 characters.' : undefined}\n rightIcon={\n <button type=\"button\" className=\"dauth-eye\"\n onClick={() => setShowPass(v => !v)}\n aria-label={showPass ? 'Hide' : 'Show'}>\n {showPass ? <EyeOffIcon /> : <EyeIcon />}\n </button>\n }\n autoComplete=\"new-password\"\n required\n />\n\n <Input\n label=\"Confirm password\"\n type={showPass ? 'text' : 'password'}\n placeholder=\"Re-enter your password\"\n value={form.confirm}\n onChange={set('confirm')}\n error={fieldErrs.confirm}\n autoComplete=\"new-password\"\n required\n />\n\n {/* Slot for extra fields (university, department, etc.) */}\n {extraFields}\n </div>\n\n <Button type=\"submit\" variant=\"primary\" fullWidth loading={loading} style={{ marginTop: 8 }}>\n Create account\n </Button>\n </form>\n\n <p className=\"dauth-footer-text\" style={{ marginTop: 20 }}>\n Already have an account?{' '}\n <a href=\"/auth/login\" className=\"dauth-link\">Sign in</a>\n </p>\n\n <p className=\"dauth-terms\">\n By creating an account you agree to our{' '}\n <a href=\"/terms\" className=\"dauth-link\">Terms of Service</a>{' '}\n and{' '}\n <a href=\"/privacy\" className=\"dauth-link\">Privacy Policy</a>.\n </p>\n </div>\n </div>\n );\n};\n"],"mappings":"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","names":["import_ui","React"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dravyn/auth-ui",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Dravyn Auth UI — Pre-built authentication screens for React/Next.js",
|
|
5
|
+
"author": "Durotoye Jeremiah Adeniyi <team@dravyn.it.com>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./auth.css": "./src/screens/auth.css"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src/screens/auth.css"
|
|
21
|
+
],
|
|
22
|
+
"sideEffects": [
|
|
23
|
+
"*.css"
|
|
24
|
+
],
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react": ">=17.0.0",
|
|
27
|
+
"react-dom": ">=17.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"typescript": "^5.0.0",
|
|
31
|
+
"@types/react": "^18.0.0",
|
|
32
|
+
"@types/node": "^20.0.0",
|
|
33
|
+
"tsup": "^8.0.0"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsup",
|
|
37
|
+
"dev": "tsup --watch"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Dravyn Auth UI — Styles
|
|
3
|
+
Consumes @dravyn/ui CSS custom properties.
|
|
4
|
+
Import tokens first: import 'dravyn-ui/tokens'
|
|
5
|
+
Then import this file once: import '@dravyn/auth-ui/auth.css'
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/* ── Root / centred layout ──────────────────────────────────────────────── */
|
|
9
|
+
.dauth-root {
|
|
10
|
+
min-height: 100vh;
|
|
11
|
+
display: flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
justify-content: center;
|
|
14
|
+
background: var(--dui-bg, #0d0d0d);
|
|
15
|
+
padding: 24px 16px;
|
|
16
|
+
font-family: var(--dui-font-sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* ── Card ───────────────────────────────────────────────────────────────── */
|
|
20
|
+
.dauth-card {
|
|
21
|
+
width: 100%;
|
|
22
|
+
max-width: 420px;
|
|
23
|
+
background: var(--dui-bg-raised, #161616);
|
|
24
|
+
border: 0.5px solid var(--dui-border-strong, rgba(255,255,255,0.16));
|
|
25
|
+
border-radius: 16px;
|
|
26
|
+
padding: 36px 32px 32px;
|
|
27
|
+
animation: dauth-in 220ms cubic-bezier(0.16, 1, 0.3, 1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@keyframes dauth-in {
|
|
31
|
+
from { opacity: 0; transform: translateY(10px) scale(0.98); }
|
|
32
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* ── Brand / logo ───────────────────────────────────────────────────────── */
|
|
36
|
+
.dauth-brand {
|
|
37
|
+
margin-bottom: 24px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.dauth-logo-default {
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: 7px;
|
|
44
|
+
font-size: 15px;
|
|
45
|
+
font-weight: 700;
|
|
46
|
+
color: var(--dui-text, #f0f0f0);
|
|
47
|
+
letter-spacing: -0.01em;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.dauth-logo-dot {
|
|
51
|
+
width: 8px;
|
|
52
|
+
height: 8px;
|
|
53
|
+
border-radius: 50%;
|
|
54
|
+
background: var(--dui-teal-400, #4ecdc4);
|
|
55
|
+
flex-shrink: 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* ── Headings ────────────────────────────────────────────────────────────── */
|
|
59
|
+
.dauth-title {
|
|
60
|
+
font-size: 22px;
|
|
61
|
+
font-weight: 700;
|
|
62
|
+
color: var(--dui-text, #f0f0f0);
|
|
63
|
+
letter-spacing: -0.02em;
|
|
64
|
+
line-height: 1.2;
|
|
65
|
+
margin: 0 0 6px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.dauth-sub {
|
|
69
|
+
font-size: 13.5px;
|
|
70
|
+
color: var(--dui-text-muted, #888);
|
|
71
|
+
line-height: 1.55;
|
|
72
|
+
margin: 0 0 22px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* ── Form fields ─────────────────────────────────────────────────────────── */
|
|
76
|
+
.dauth-fields {
|
|
77
|
+
display: flex;
|
|
78
|
+
flex-direction: column;
|
|
79
|
+
gap: 14px;
|
|
80
|
+
margin-bottom: 14px;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.dauth-row {
|
|
84
|
+
display: grid;
|
|
85
|
+
grid-template-columns: 1fr 1fr;
|
|
86
|
+
gap: 10px;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* ── Password wrap (holds input + forgot link) ───────────────────────────── */
|
|
90
|
+
.dauth-password-wrap {
|
|
91
|
+
display: flex;
|
|
92
|
+
flex-direction: column;
|
|
93
|
+
gap: 6px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.dauth-forgot {
|
|
97
|
+
font-size: 12px;
|
|
98
|
+
color: var(--dui-text-muted, #888);
|
|
99
|
+
text-decoration: none;
|
|
100
|
+
align-self: flex-end;
|
|
101
|
+
transition: color 0.15s;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.dauth-forgot:hover { color: var(--dui-teal-400, #4ecdc4); }
|
|
105
|
+
|
|
106
|
+
/* ── Show/hide password button ───────────────────────────────────────────── */
|
|
107
|
+
.dauth-eye {
|
|
108
|
+
background: none;
|
|
109
|
+
border: none;
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
color: var(--dui-text-hint, #555);
|
|
112
|
+
display: inline-flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
padding: 0;
|
|
115
|
+
transition: color 0.15s;
|
|
116
|
+
}
|
|
117
|
+
.dauth-eye:hover { color: var(--dui-text-muted, #888); }
|
|
118
|
+
|
|
119
|
+
/* ── Divider ─────────────────────────────────────────────────────────────── */
|
|
120
|
+
.dauth-divider {
|
|
121
|
+
display: flex;
|
|
122
|
+
align-items: center;
|
|
123
|
+
gap: 10px;
|
|
124
|
+
margin: 18px 0;
|
|
125
|
+
color: var(--dui-text-hint, #555);
|
|
126
|
+
font-size: 12px;
|
|
127
|
+
}
|
|
128
|
+
.dauth-divider::before,
|
|
129
|
+
.dauth-divider::after {
|
|
130
|
+
content: '';
|
|
131
|
+
flex: 1;
|
|
132
|
+
height: 0.5px;
|
|
133
|
+
background: var(--dui-border, rgba(255,255,255,0.07));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* ── Google OAuth button ─────────────────────────────────────────────────── */
|
|
137
|
+
.dauth-google-btn {
|
|
138
|
+
width: 100%;
|
|
139
|
+
display: flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
justify-content: center;
|
|
142
|
+
gap: 10px;
|
|
143
|
+
background: transparent;
|
|
144
|
+
border: 0.5px solid var(--dui-border-strong, rgba(255,255,255,0.16));
|
|
145
|
+
border-radius: 8px;
|
|
146
|
+
padding: 10px 18px;
|
|
147
|
+
font-family: var(--dui-font-sans);
|
|
148
|
+
font-size: 13px;
|
|
149
|
+
font-weight: 500;
|
|
150
|
+
color: var(--dui-text, #f0f0f0);
|
|
151
|
+
cursor: pointer;
|
|
152
|
+
transition: background 0.15s, border-color 0.15s;
|
|
153
|
+
}
|
|
154
|
+
.dauth-google-btn:hover:not(:disabled) {
|
|
155
|
+
background: var(--dui-bg-overlay, #1e1e1e);
|
|
156
|
+
border-color: rgba(255,255,255,0.24);
|
|
157
|
+
}
|
|
158
|
+
.dauth-google-btn:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
159
|
+
|
|
160
|
+
/* ── OTP boxes ───────────────────────────────────────────────────────────── */
|
|
161
|
+
.dauth-otp-icon {
|
|
162
|
+
font-size: 28px;
|
|
163
|
+
margin-bottom: 12px;
|
|
164
|
+
color: var(--dui-teal-400, #4ecdc4);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.dauth-otp-boxes {
|
|
168
|
+
display: flex;
|
|
169
|
+
gap: 8px;
|
|
170
|
+
justify-content: center;
|
|
171
|
+
margin: 20px 0 8px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.dauth-otp-box {
|
|
175
|
+
width: 46px;
|
|
176
|
+
height: 54px;
|
|
177
|
+
background: var(--dui-bg, #0d0d0d);
|
|
178
|
+
border: 0.5px solid var(--dui-border-strong, rgba(255,255,255,0.16));
|
|
179
|
+
border-radius: 8px;
|
|
180
|
+
color: var(--dui-text, #f0f0f0);
|
|
181
|
+
font-size: 22px;
|
|
182
|
+
font-weight: 700;
|
|
183
|
+
font-family: var(--dui-font-sans);
|
|
184
|
+
text-align: center;
|
|
185
|
+
outline: none;
|
|
186
|
+
transition: border-color 0.15s, box-shadow 0.15s;
|
|
187
|
+
caret-color: var(--dui-teal-400, #4ecdc4);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.dauth-otp-box:focus {
|
|
191
|
+
border-color: var(--dui-teal-400, #4ecdc4);
|
|
192
|
+
box-shadow: 0 0 0 3px rgba(78, 205, 196, 0.12);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.dauth-otp-box--filled {
|
|
196
|
+
border-color: var(--dui-teal-400, #4ecdc4);
|
|
197
|
+
background: rgba(78, 205, 196, 0.04);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* ── Resend row ──────────────────────────────────────────────────────────── */
|
|
201
|
+
.dauth-resend {
|
|
202
|
+
text-align: center;
|
|
203
|
+
font-size: 13px;
|
|
204
|
+
color: var(--dui-text-muted, #888);
|
|
205
|
+
margin: 14px 0 4px;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* ── Links ───────────────────────────────────────────────────────────────── */
|
|
209
|
+
.dauth-link {
|
|
210
|
+
color: var(--dui-teal-400, #4ecdc4);
|
|
211
|
+
text-decoration: none;
|
|
212
|
+
font-weight: 500;
|
|
213
|
+
background: none;
|
|
214
|
+
border: none;
|
|
215
|
+
cursor: pointer;
|
|
216
|
+
font-size: inherit;
|
|
217
|
+
font-family: inherit;
|
|
218
|
+
padding: 0;
|
|
219
|
+
transition: opacity 0.15s;
|
|
220
|
+
}
|
|
221
|
+
.dauth-link:hover { opacity: 0.8; }
|
|
222
|
+
|
|
223
|
+
/* ── Footer text + terms ─────────────────────────────────────────────────── */
|
|
224
|
+
.dauth-footer-text {
|
|
225
|
+
text-align: center;
|
|
226
|
+
font-size: 13px;
|
|
227
|
+
color: var(--dui-text-muted, #888);
|
|
228
|
+
margin-top: 18px;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.dauth-terms {
|
|
232
|
+
text-align: center;
|
|
233
|
+
font-size: 11.5px;
|
|
234
|
+
color: var(--dui-text-hint, #555);
|
|
235
|
+
margin-top: 12px;
|
|
236
|
+
line-height: 1.6;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/* ── CSS variable aliases (readable names inside auth components) ─────────── */
|
|
240
|
+
:root {
|
|
241
|
+
--dauth-text: var(--dui-text, #f0f0f0);
|
|
242
|
+
--dauth-muted: var(--dui-text-muted, #888888);
|
|
243
|
+
--dauth-teal: var(--dui-teal-400, #4ecdc4);
|
|
244
|
+
--dauth-green: #22c55e;
|
|
245
|
+
--dauth-label: var(--dui-text-muted, #888888);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* ── Label helper (used in ResetPasswordScreen) ───────────────────────────── */
|
|
249
|
+
.dauth-label {
|
|
250
|
+
font-size: 12px;
|
|
251
|
+
font-weight: 500;
|
|
252
|
+
color: var(--dauth-label);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* ── Responsive ──────────────────────────────────────────────────────────── */
|
|
256
|
+
@media (max-width: 480px) {
|
|
257
|
+
.dauth-card { padding: 28px 20px 24px; }
|
|
258
|
+
.dauth-row { grid-template-columns: 1fr; }
|
|
259
|
+
.dauth-otp-box { width: 40px; height: 48px; font-size: 20px; }
|
|
260
|
+
}
|