password reset

This commit is contained in:
oliver
2026-06-08 09:37:14 -03:00
parent 53803e9d12
commit ccc3f92d04
3 changed files with 224 additions and 0 deletions
+40
View File
@@ -34,6 +34,8 @@ const AUTH_URL =
"https://n8n.derez.ai/webhook/e256310a-6627-45ba-a221-599751943fe6";
const ACCOUNT_PASSWORD_URL =
"https://n8n.derez.ai/webhook/4644f196-a31c-4d1a-b76e-03e9afe39302";
const PW_RESET_URL =
"https://n8n.derez.ai/webhook/c2ce0eba-eb26-405d-8a90-8d982ec30698";
/* ─── State ─────────────────────────────────────────────────── */
let currentEmail = null;
@@ -129,6 +131,8 @@ function switchAuthTab(tab) {
document.getElementById("signin-error").style.display = "none";
document.getElementById("signup-error").style.display = "none";
document.getElementById("signup-success").style.display = "none";
const _rpw = document.getElementById("reset-pw-wrap");
if (_rpw) _rpw.style.display = "none";
}
/* ═══════════════════════════════════════════════════════════════
@@ -182,6 +186,7 @@ async function doSignIn() {
loadAgents();
} catch (err) {
showAuthError("signin-error", err.message);
showResetOption();
} finally {
btnReset(btn);
}
@@ -1824,6 +1829,41 @@ function showAuthError(id, msg) {
function hideAuthError(id) {
document.getElementById(id).style.display = "none";
if (id === "signin-error") {
const wrap = document.getElementById("reset-pw-wrap");
if (wrap) wrap.style.display = "none";
}
}
function showResetOption() {
const wrap = document.getElementById("reset-pw-wrap");
if (!wrap) return;
// Return to idle state in case it was previously used
document.getElementById("reset-pw-idle").style.display = "flex";
document.getElementById("reset-pw-sent").style.display = "none";
wrap.style.display = "block";
}
async function sendPasswordReset() {
const email = document.getElementById("signin-email").value.trim();
if (!email) {
toast("Enter your email address above first.", "warning");
return;
}
const btn = document.getElementById("reset-pw-btn");
btnLoad(btn, "Sending…");
try {
await fetch(PW_RESET_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
});
} catch (_) {
// Always show sent — never reveal whether the address exists
} finally {
document.getElementById("reset-pw-idle").style.display = "none";
document.getElementById("reset-pw-sent").style.display = "flex";
}
}
/* ═══════════════════════════════════════════════════════════════
+81
View File
@@ -131,6 +131,87 @@
/>
</div>
<div id="signin-error" class="auth-error"></div>
<!-- Password reset — shown only after a failed sign-in -->
<div
id="reset-pw-wrap"
class="reset-pw-wrap"
style="display: none"
>
<div id="reset-pw-idle" class="reset-pw-idle">
<span class="reset-pw-hint"
>Forgot your password?</span
>
<button
id="reset-pw-btn"
class="reset-pw-btn"
onclick="sendPasswordReset()"
>
<svg
width="13"
height="13"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect
x="2"
y="4"
width="20"
height="16"
rx="2"
/>
<polyline points="22,4 12,13 2,4" />
</svg>
Send reset email
</button>
</div>
<div
id="reset-pw-sent"
class="reset-pw-sent"
style="display: none"
>
<svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect
x="2"
y="4"
width="20"
height="16"
rx="2"
/>
<polyline points="22,4 12,13 2,4" />
<polyline
points="2,20 9,13"
style="opacity: 0.5"
/>
<polyline
points="22,20 15,13"
style="opacity: 0.5"
/>
</svg>
<div class="reset-pw-sent-text">
<span class="reset-pw-sent-title"
>Reset link sent</span
>
<span class="reset-pw-sent-sub"
>Check your inbox &mdash; link expires
in 15&nbsp;minutes.</span
>
</div>
</div>
</div>
<button
type="submit"
id="signin-btn"
+103
View File
@@ -2095,3 +2095,106 @@ body.auth-layout #signin-btn {
.wizard-review-textarea:focus {
border-color: var(--accent);
}
/* ─── Password reset section ───────────────────────────────────── */
@keyframes resetSlideIn {
from {
opacity: 0;
transform: translateY(-8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.reset-pw-wrap {
border: 1px solid rgba(0, 245, 255, 0.22);
border-left: 3px solid var(--accent);
border-radius: var(--radius);
background: rgba(0, 245, 255, 0.03);
overflow: hidden;
animation: resetSlideIn 0.25s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.reset-pw-idle {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
padding: 10px 14px;
flex-wrap: wrap;
}
.reset-pw-hint {
font-size: 12px;
color: var(--text-muted);
letter-spacing: 0.1px;
}
.reset-pw-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 12px;
border: 1px solid var(--accent);
border-radius: 6px;
background: transparent;
color: var(--accent);
font-family: inherit;
font-size: 12px;
font-weight: 500;
cursor: pointer;
white-space: nowrap;
letter-spacing: 0.1px;
box-shadow: 0 0 8px rgba(0, 245, 255, 0.2);
transition:
background 0.15s,
box-shadow 0.15s,
color 0.15s;
}
.reset-pw-btn:hover {
background: rgba(0, 245, 255, 0.08);
box-shadow: 0 0 18px rgba(0, 245, 255, 0.45);
color: #fff;
}
.reset-pw-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
box-shadow: none;
}
.reset-pw-sent {
display: flex;
align-items: center;
gap: 14px;
padding: 13px 14px;
animation: resetSlideIn 0.3s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.reset-pw-sent > svg {
flex-shrink: 0;
color: var(--success);
filter: drop-shadow(0 0 6px rgba(0, 255, 136, 0.55));
}
.reset-pw-sent-text {
display: flex;
flex-direction: column;
gap: 3px;
}
.reset-pw-sent-title {
font-size: 13px;
font-weight: 600;
color: var(--success);
text-shadow: 0 0 10px rgba(0, 255, 136, 0.4);
}
.reset-pw-sent-sub {
font-size: 11px;
color: var(--text-muted);
line-height: 1.45;
}