1097 lines
53 KiB
HTML
1097 lines
53 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>derez.ai — My Portal</title>
|
|
<link rel="stylesheet" href="styles.css" />
|
|
</head>
|
|
<body>
|
|
<!-- ═══════════════════════════════════════════════════════════
|
|
AUTH CARD (shown when not logged in)
|
|
════════════════════════════════════════════════════════════════ -->
|
|
<div class="auth-card" id="auth-card">
|
|
<!-- Logo shown on narrow screens (single-col mode) -->
|
|
<div class="auth-logo auth-logo-top">
|
|
<div class="auth-logo-icon">
|
|
<svg
|
|
width="28"
|
|
height="28"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="1.8"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<rect x="3" y="8" width="18" height="13" rx="2" />
|
|
<path d="M9 12h.01M15 12h.01" />
|
|
<path d="M9 16h6" />
|
|
<path d="M12 8V5" />
|
|
<circle
|
|
cx="12"
|
|
cy="4"
|
|
r="1.2"
|
|
fill="currentColor"
|
|
stroke="none"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<h1 class="auth-title">derez.ai</h1>
|
|
<p class="auth-sub">Your AI agents, managed simply.</p>
|
|
</div>
|
|
|
|
<!-- Sign In / Sign Up toggle (used on narrow screens) -->
|
|
<div class="auth-switch">
|
|
<button
|
|
class="auth-switch-btn active"
|
|
data-tab="signin"
|
|
onclick="switchAuthTab('signin')"
|
|
>
|
|
Sign In
|
|
</button>
|
|
<button
|
|
class="auth-switch-btn"
|
|
data-tab="signup"
|
|
onclick="switchAuthTab('signup')"
|
|
>
|
|
Hire Agent
|
|
</button>
|
|
</div>
|
|
|
|
<div class="auth-forms-wrap">
|
|
<!-- ── Sign In pane ── -->
|
|
<div class="auth-pane">
|
|
<!-- Logo shown in dual-col mode, above Sign In -->
|
|
<div class="auth-logo auth-logo-pane">
|
|
<div class="auth-logo-icon">
|
|
<svg
|
|
width="28"
|
|
height="28"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="1.8"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<rect
|
|
x="3"
|
|
y="8"
|
|
width="18"
|
|
height="13"
|
|
rx="2"
|
|
/>
|
|
<path d="M9 12h.01M15 12h.01" />
|
|
<path d="M9 16h6" />
|
|
<path d="M12 8V5" />
|
|
<circle
|
|
cx="12"
|
|
cy="4"
|
|
r="1.2"
|
|
fill="currentColor"
|
|
stroke="none"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<h1 class="auth-title">derez.ai</h1>
|
|
<p class="auth-sub">
|
|
Your AI agents, managed simply.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="auth-pane-title">Sign In</div>
|
|
<form
|
|
id="signin-form"
|
|
class="auth-form"
|
|
onsubmit="
|
|
event.preventDefault();
|
|
doSignIn();
|
|
"
|
|
>
|
|
<div class="form-group">
|
|
<label for="signin-email">Email address</label>
|
|
<input
|
|
type="email"
|
|
id="signin-email"
|
|
placeholder="you@example.com"
|
|
autocomplete="email"
|
|
required
|
|
/>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="signin-password">Password</label>
|
|
<input
|
|
type="password"
|
|
id="signin-password"
|
|
placeholder="Your password"
|
|
autocomplete="current-password"
|
|
required
|
|
/>
|
|
</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 — link expires
|
|
in 15 minutes.</span
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<button
|
|
type="submit"
|
|
id="signin-btn"
|
|
class="btn btn-primary btn-block"
|
|
>
|
|
Sign In
|
|
</button>
|
|
</form>
|
|
</div>
|
|
<!-- /auth-pane signin -->
|
|
<div class="auth-forms-divider"></div>
|
|
<!-- ── Sign Up pane ── -->
|
|
<div class="auth-pane">
|
|
<div class="auth-pane-title">Hire Agent</div>
|
|
<div
|
|
id="signup-form"
|
|
class="hire-cta"
|
|
style="display: none"
|
|
>
|
|
<a
|
|
href="https://derez.ai/#pricing"
|
|
target="_blank"
|
|
rel="noopener"
|
|
class="btn btn-primary btn-block"
|
|
>
|
|
Hire Agent
|
|
</a>
|
|
<a
|
|
href="https://derez.ai/#pricing"
|
|
target="_blank"
|
|
rel="noopener"
|
|
class="hire-link"
|
|
>
|
|
View plans & pricing →
|
|
</a>
|
|
<p class="hire-note">
|
|
New clients — a reset link has already been sent to
|
|
your email.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<!-- /auth-pane signup -->
|
|
</div>
|
|
<!-- /auth-forms-wrap -->
|
|
</div>
|
|
<!-- /auth-card -->
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════
|
|
APP SHELL (shown when logged in)
|
|
════════════════════════════════════════════════════════════════ -->
|
|
<div class="app-shell" id="app-shell">
|
|
<!-- ── Header ───────────────────────────────────────────── -->
|
|
<header class="app-header">
|
|
<div class="app-brand">
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<rect x="3" y="8" width="18" height="13" rx="2" />
|
|
<path d="M9 12h.01M15 12h.01" />
|
|
<path d="M9 16h6" />
|
|
<path d="M12 8V5" />
|
|
<circle
|
|
cx="12"
|
|
cy="4"
|
|
r="1.2"
|
|
fill="currentColor"
|
|
stroke="none"
|
|
/>
|
|
</svg>
|
|
derez.ai
|
|
</div>
|
|
<!-- ── Header comment (centre) ───────────────────── -->
|
|
<div class="header-comment" id="header-comment-wrap">
|
|
<span
|
|
id="header-comment-display"
|
|
class="comment-empty"
|
|
title="Click to edit"
|
|
onclick="editComment()"
|
|
>Name your Agent</span
|
|
>
|
|
<div id="header-comment-edit" style="display: none">
|
|
<input
|
|
id="header-comment-input"
|
|
type="text"
|
|
maxlength="140"
|
|
placeholder="Add a comment…"
|
|
onkeydown="handleCommentKey(event)"
|
|
/>
|
|
<button
|
|
id="comment-save-btn"
|
|
class="btn btn-ghost btn-sm"
|
|
onclick="saveComment()"
|
|
>
|
|
Save
|
|
</button>
|
|
<button
|
|
class="btn btn-ghost btn-sm"
|
|
onclick="cancelComment()"
|
|
>
|
|
✕
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="app-user" id="user-panel-wrap">
|
|
<span
|
|
class="user-pill"
|
|
onclick="toggleUserPanel()"
|
|
title="Account settings"
|
|
>
|
|
<span class="pulse-dot"></span>
|
|
<span id="user-email-label">—</span>
|
|
</span>
|
|
|
|
<button class="btn btn-ghost btn-sm" onclick="doSignOut()">
|
|
<svg
|
|
width="13"
|
|
height="13"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2.5"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
|
|
<polyline points="16 17 21 12 16 7" />
|
|
<line x1="21" y1="12" x2="9" y2="12" />
|
|
</svg>
|
|
Sign Out
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- ── Agent tabs ────────────────────────────────────────── -->
|
|
<div class="agent-tabs-bar" id="agent-tabs-bar">
|
|
<div class="tabs-loading">
|
|
<div class="spinner"></div>
|
|
Loading agents…
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── Body ──────────────────────────────────────────────── -->
|
|
<div class="app-body">
|
|
<!-- Demo mode overlay -->
|
|
<div id="demo-notice" class="demo-notice">
|
|
<span>
|
|
<svg
|
|
width="14"
|
|
height="14"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<rect x="3" y="11" width="18" height="11" rx="2" />
|
|
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
|
|
</svg>
|
|
Sign in to manage your agents
|
|
</span>
|
|
</div>
|
|
|
|
<!-- No agents state -->
|
|
<div id="no-agents" class="empty-center" style="display: none">
|
|
<svg
|
|
width="52"
|
|
height="52"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="1.4"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<rect x="3" y="8" width="18" height="13" rx="2" />
|
|
<path d="M9 12h.01M15 12h.01" />
|
|
<path d="M9 16h6" />
|
|
<path d="M12 8V5" />
|
|
<circle
|
|
cx="12"
|
|
cy="4"
|
|
r="1.2"
|
|
fill="currentColor"
|
|
stroke="none"
|
|
/>
|
|
</svg>
|
|
<p>No agents found for your account.</p>
|
|
<p
|
|
class="text-muted"
|
|
style="font-size: 12px; margin-top: 2px"
|
|
>
|
|
Sign out and use Sign Up to get your first agent.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Agent panel (one per agent, shown for active tab) -->
|
|
<div id="agent-panel" style="display: none">
|
|
<div class="content-grid">
|
|
<!-- ── LEFT column ─────────────────────────── -->
|
|
<div class="content-col">
|
|
<!-- Server Actions card -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<span class="card-title">
|
|
<svg
|
|
width="14"
|
|
height="14"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<polygon
|
|
points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"
|
|
/>
|
|
</svg>
|
|
Agent Actions
|
|
</span>
|
|
<div style="display: flex; gap: 8px">
|
|
<button
|
|
id="wizard-toggle-btn"
|
|
class="btn btn-ghost btn-sm"
|
|
onclick="openWizard()"
|
|
>
|
|
Integrations
|
|
</button>
|
|
<button
|
|
id="restart-btn"
|
|
class="btn btn-ghost btn-sm"
|
|
onclick="doRestart()"
|
|
>
|
|
Restart Agent
|
|
</button>
|
|
<button
|
|
id="open-agent-btn"
|
|
class="btn btn-primary btn-sm"
|
|
onclick="openAgent()"
|
|
>
|
|
Open Agent
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div id="server-info-body">
|
|
<div class="loading-row">
|
|
<div class="spinner"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Instance Password card -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<span class="card-title">
|
|
<svg
|
|
width="14"
|
|
height="14"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<rect
|
|
x="3"
|
|
y="11"
|
|
width="18"
|
|
height="11"
|
|
rx="2"
|
|
/>
|
|
<path
|
|
d="M7 11V7a5 5 0 0 1 10 0v4"
|
|
/>
|
|
</svg>
|
|
Your Hermes & Root Password
|
|
</span>
|
|
</div>
|
|
<div class="pw-wrap">
|
|
<div class="form-group">
|
|
<label>New Password</label>
|
|
<div class="pw-input-wrap">
|
|
<input
|
|
type="password"
|
|
id="pw-new-input"
|
|
placeholder="Enter new password"
|
|
autocomplete="new-password"
|
|
oninput="
|
|
togglePwSave(this.value)
|
|
"
|
|
onkeydown="
|
|
if (event.key === 'Enter')
|
|
savePassword();
|
|
"
|
|
/>
|
|
<button
|
|
type="button"
|
|
id="pw-save-btn"
|
|
class="pw-save-btn"
|
|
onclick="savePassword()"
|
|
title="Save password"
|
|
style="display: none"
|
|
>
|
|
<svg
|
|
width="14"
|
|
height="14"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2.5"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<polyline
|
|
points="20 6 9 17 4 12"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<p class="ssh-hint">
|
|
Changing this updates root access on your
|
|
server immediately.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- API Keys card -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<span class="card-title">
|
|
<span
|
|
id="keys-status-dot"
|
|
class="contract-dot"
|
|
style="visibility: hidden"
|
|
></span>
|
|
API Keys
|
|
</span>
|
|
</div>
|
|
<div id="keys-body">
|
|
<div class="loading-row">
|
|
<div class="spinner"></div>
|
|
</div>
|
|
</div>
|
|
<p class="ssh-hint">
|
|
You can add your own API key directly in
|
|
your Agent.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<!-- /content-col left -->
|
|
|
|
<!-- ── RIGHT column ────────────────────────── -->
|
|
<div class="content-col">
|
|
<!-- Integrations Wizard card (hidden when WIZZARD=false) -->
|
|
<div
|
|
id="wizard-card"
|
|
class="card"
|
|
style="display: none"
|
|
>
|
|
<div class="card-header">
|
|
<span class="card-title">
|
|
<svg
|
|
width="14"
|
|
height="14"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<polygon
|
|
points="12 2 2 7 12 12 22 7 12 2"
|
|
/>
|
|
<polyline
|
|
points="2 17 12 22 22 17"
|
|
/>
|
|
<polyline
|
|
points="2 12 12 17 22 12"
|
|
/>
|
|
</svg>
|
|
<span id="wizard-card-title"
|
|
>Select Skills</span
|
|
>
|
|
</span>
|
|
</div>
|
|
<div id="wizard-body"></div>
|
|
<div class="wizard-footer">
|
|
<button
|
|
class="btn btn-ghost btn-sm"
|
|
id="wizard-back-btn"
|
|
style="display: none"
|
|
onclick="wizardBack()"
|
|
>
|
|
Back
|
|
</button>
|
|
<button
|
|
class="btn btn-primary btn-sm"
|
|
id="wizard-next-btn"
|
|
onclick="wizardNext()"
|
|
>
|
|
Next →
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Backups card -->
|
|
<div class="card card-full">
|
|
<div class="card-header">
|
|
<span class="card-title">
|
|
<svg
|
|
width="14"
|
|
height="14"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<path
|
|
d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"
|
|
/>
|
|
<polyline points="17 8 12 3 7 8" />
|
|
<line
|
|
x1="12"
|
|
y1="3"
|
|
x2="12"
|
|
y2="15"
|
|
/>
|
|
</svg>
|
|
Backups
|
|
</span>
|
|
<button
|
|
id="make-backup-btn"
|
|
class="btn btn-ghost btn-sm"
|
|
style="
|
|
font-size: 18px;
|
|
line-height: 1;
|
|
padding: 2px 10px;
|
|
"
|
|
onclick="makeBackup()"
|
|
title="Make Backup"
|
|
>
|
|
+
|
|
</button>
|
|
</div>
|
|
<div id="backups-body">
|
|
<div class="loading-row">
|
|
<div class="spinner"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Contract card -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<span class="card-title">
|
|
<span
|
|
id="contract-status-dot"
|
|
class="contract-dot"
|
|
style="visibility: hidden"
|
|
></span>
|
|
Contract
|
|
</span>
|
|
</div>
|
|
<div id="contract-body">
|
|
<div class="loading-row">
|
|
<div class="spinner"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- /content-col right -->
|
|
</div>
|
|
<!-- /content-grid -->
|
|
</div>
|
|
<!-- /agent-panel -->
|
|
</div>
|
|
<!-- /app-body -->
|
|
|
|
<!-- ── Talk to Us ──────────────────────────────────── -->
|
|
<div class="talk-wrap" id="talk-wrap">
|
|
<button
|
|
class="talk-toggle"
|
|
id="talk-toggle-btn"
|
|
onclick="toggleTalk()"
|
|
>
|
|
<svg
|
|
width="14"
|
|
height="14"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<path
|
|
d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"
|
|
/>
|
|
</svg>
|
|
Talk to Us
|
|
<svg
|
|
id="talk-chevron"
|
|
width="13"
|
|
height="13"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2.5"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
style="margin-left: auto; transition: transform 0.25s"
|
|
>
|
|
<polyline points="18 15 12 9 6 15" />
|
|
</svg>
|
|
</button>
|
|
<div class="talk-panel" id="talk-panel">
|
|
<!-- Left: support chat -->
|
|
<div class="talk-col talk-chat-col">
|
|
<div class="talk-col-title">Support Chat</div>
|
|
<div class="chat-msgs" id="chat-msgs"></div>
|
|
<div class="chat-input-row">
|
|
<input
|
|
id="chat-input"
|
|
type="text"
|
|
placeholder="Type a message…"
|
|
onkeydown="handleChatKey(event)"
|
|
/>
|
|
<button
|
|
id="chat-send-btn"
|
|
class="btn btn-primary btn-sm"
|
|
onclick="sendChat()"
|
|
>
|
|
Send
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- Right: refer a friend -->
|
|
<div class="talk-col talk-refer-col">
|
|
<div class="talk-col-title">Refer a Friend</div>
|
|
<p class="refer-desc">
|
|
When your friend signs up mentioning your email
|
|
(<strong id="refer-own-email"></strong>), you earn
|
|
<strong>$5 USD</strong> credit on your selected
|
|
Agent.
|
|
</p>
|
|
<div class="refer-form">
|
|
<div class="refer-input-row">
|
|
<input
|
|
id="name"
|
|
type="text"
|
|
placeholder="Your friend's name"
|
|
/>
|
|
</div>
|
|
<div class="refer-input-row">
|
|
<input
|
|
id="refer-email"
|
|
type="email"
|
|
placeholder="friend@example.com"
|
|
/>
|
|
<button
|
|
id="refer-send-btn"
|
|
class="btn btn-primary btn-sm"
|
|
onclick="sendReferral()"
|
|
>
|
|
Invite
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- /talk-wrap -->
|
|
</div>
|
|
<!-- /app-shell -->
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════
|
|
ACCOUNT SETTINGS MODAL
|
|
════════════════════════════════════════════════════════════════ -->
|
|
<div
|
|
id="account-backdrop"
|
|
class="modal-backdrop"
|
|
onclick="if (event.target === this) closeUserPanel();"
|
|
>
|
|
<div class="modal account-modal">
|
|
<div class="modal-header">
|
|
<span class="modal-title">Account Settings</span>
|
|
<button
|
|
type="button"
|
|
class="modal-close-btn"
|
|
onclick="closeUserPanel()"
|
|
>
|
|
✕
|
|
</button>
|
|
</div>
|
|
<div class="account-modal-body">
|
|
<!-- ── Left: settings ───────────────────────── -->
|
|
<div class="account-col account-col--settings">
|
|
<div class="acct-section">
|
|
<div class="acct-field-label">Email</div>
|
|
<div class="acct-field-val" id="acct-email-display">
|
|
—
|
|
</div>
|
|
</div>
|
|
<div class="acct-divider"></div>
|
|
<div class="acct-section">
|
|
<div class="form-group">
|
|
<label>Change Password</label>
|
|
<div class="pw-input-wrap">
|
|
<input
|
|
type="password"
|
|
id="acct-pw-input"
|
|
placeholder="New password"
|
|
autocomplete="new-password"
|
|
oninput="
|
|
toggleAccountPwSave(this.value)
|
|
"
|
|
onkeydown="
|
|
if (event.key === 'Enter')
|
|
saveAccountPassword();
|
|
"
|
|
/>
|
|
<button
|
|
type="button"
|
|
id="acct-pw-save-btn"
|
|
class="pw-save-btn"
|
|
onclick="saveAccountPassword()"
|
|
title="Save password"
|
|
style="display: none"
|
|
>
|
|
<svg
|
|
width="14"
|
|
height="14"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2.5"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<polyline points="20 6 9 17 4 12" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<p class="ssh-hint" style="margin-bottom: 0">
|
|
Min. 8 characters.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="acct-divider"></div>
|
|
<div class="acct-section">
|
|
<div class="form-group">
|
|
<label>Invoice Email</label>
|
|
<div class="pw-input-wrap">
|
|
<input
|
|
type="email"
|
|
id="invoice-email-input"
|
|
placeholder="invoices@example.com"
|
|
autocomplete="email"
|
|
oninput="
|
|
toggleInvoiceEmailSave(this.value)
|
|
"
|
|
onkeydown="
|
|
if (event.key === 'Enter')
|
|
saveInvoiceEmail();
|
|
"
|
|
/>
|
|
<button
|
|
type="button"
|
|
id="invoice-email-save-btn"
|
|
class="pw-save-btn"
|
|
onclick="saveInvoiceEmail()"
|
|
title="Save invoice email"
|
|
style="display: none"
|
|
>
|
|
<svg
|
|
width="14"
|
|
height="14"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2.5"
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
>
|
|
<polyline points="20 6 9 17 4 12" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="acct-divider"></div>
|
|
<div class="acct-section">
|
|
<div class="acct-field-label">
|
|
Manage Subscriptions
|
|
</div>
|
|
<div
|
|
class="acct-affiliate-row"
|
|
style="justify-content: flex-end"
|
|
>
|
|
<button
|
|
type="button"
|
|
id="portal-btn"
|
|
class="btn btn-ghost btn-sm"
|
|
onclick="openBillingPortal()"
|
|
>
|
|
Portal →
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="acct-divider"></div>
|
|
<div class="acct-section">
|
|
<div class="acct-field-label">Affiliate Code</div>
|
|
<div class="acct-affiliate-row">
|
|
<span
|
|
class="acct-affiliate-code"
|
|
id="acct-affiliate-code"
|
|
>—</span
|
|
>
|
|
<button
|
|
type="button"
|
|
id="acct-affiliate-copy-btn"
|
|
class="btn btn-ghost btn-sm"
|
|
onclick="copyAffiliateCode()"
|
|
style="display: none"
|
|
>
|
|
Copy Affiliate Link
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- ── Vertical divider ──────────────────────── -->
|
|
<div class="account-col-divider"></div>
|
|
<!-- ── Right: invoices ──────────────────────── -->
|
|
<div class="account-col account-col--invoices">
|
|
<div class="acct-invoices-toolbar">
|
|
<div class="acct-invoices-title">Invoices</div>
|
|
<div class="acct-invoices-actions">
|
|
<input
|
|
type="text"
|
|
id="invoice-filter"
|
|
class="invoice-filter-input"
|
|
placeholder="Filter invoices…"
|
|
oninput="applyInvoiceFilter()"
|
|
/>
|
|
<button
|
|
type="button"
|
|
class="btn btn-ghost btn-sm"
|
|
onclick="exportInvoicesCSV()"
|
|
>
|
|
Export CSV
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div id="invoices-body"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════
|
|
BUY CREDITS MODAL
|
|
════════════════════════════════════════════════════════════════ -->
|
|
<div id="credits-backdrop" class="modal-backdrop">
|
|
<div class="modal" style="max-width: 460px">
|
|
<div class="modal-header">
|
|
<span class="modal-title">Buy Credits</span>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p class="credits-key-label">
|
|
Credits will be assigned to key:
|
|
<span id="credits-key-name"></span>
|
|
</p>
|
|
<div class="credits-options">
|
|
<label class="credits-option">
|
|
<input
|
|
type="radio"
|
|
name="credits-amount"
|
|
value="11.00"
|
|
/>
|
|
<span class="credits-option-label"
|
|
>€ 11.00</span
|
|
>
|
|
</label>
|
|
<label class="credits-option">
|
|
<input
|
|
type="radio"
|
|
name="credits-amount"
|
|
value="22.00"
|
|
/>
|
|
<span class="credits-option-label"
|
|
>€ 22.00</span
|
|
>
|
|
</label>
|
|
<label class="credits-option">
|
|
<input
|
|
type="radio"
|
|
name="credits-amount"
|
|
value="55.00"
|
|
/>
|
|
<span class="credits-option-label"
|
|
>€ 55.00</span
|
|
>
|
|
</label>
|
|
<label class="credits-option">
|
|
<input
|
|
type="radio"
|
|
name="credits-amount"
|
|
value="100.00"
|
|
/>
|
|
<span class="credits-option-label"
|
|
>€ 100.00</span
|
|
>
|
|
</label>
|
|
</div>
|
|
<p class="credits-fee-note">
|
|
A 10% handling fee is added on top of the OpenRouter
|
|
credits value.
|
|
</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-ghost" onclick="buyCreditsClose()">
|
|
Cancel
|
|
</button>
|
|
<button
|
|
id="credits-buy-btn"
|
|
class="btn btn-primary"
|
|
onclick="buyCreditsSubmit()"
|
|
>
|
|
Buy Credits
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════
|
|
CONFIRM DIALOG
|
|
════════════════════════════════════════════════════════════════ -->
|
|
<div id="confirm-backdrop" class="modal-backdrop">
|
|
<div class="modal">
|
|
<div class="modal-header">
|
|
<span class="modal-title" id="confirm-title">Confirm</span>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p id="confirm-message"></p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-ghost" onclick="confirmClose(false)">
|
|
Cancel
|
|
</button>
|
|
<button
|
|
id="confirm-ok-btn"
|
|
class="btn btn-danger"
|
|
onclick="confirmClose(true)"
|
|
>
|
|
Confirm
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════
|
|
TOAST CONTAINER
|
|
════════════════════════════════════════════════════════════════ -->
|
|
<div id="toast-container"></div>
|
|
|
|
<script src="app.js"></script>
|
|
</body>
|
|
</html>
|