update: refactor dashboard and login pages

This commit is contained in:
Codex
2026-02-18 14:49:51 +00:00
parent 2b9f4e8b3f
commit c92032eb72

View File

@@ -182,79 +182,90 @@ function renderLoginPage({ returnTo = "/app", error = null }) {
<label class="form-control w-full"> <label class="form-control w-full">
<span class="label-text text-slate-200 mb-1">Password</span> <span class="label-text text-slate-200 mb-1">Password</span>
<input name="password" type="password" required minlength="8" maxlength="128" class="input input-bordered w-full bg-slate-950" placeholder="••••••••" /> <input name="password" type="password" required minlength="8" maxlength="128" class="input input-bordered w-full bg-slate-950" placeholder="••••••••" />
</label> <form method="POST" action="/auth/dev-login" class="space-y-4">
<button class="btn btn-primary w-full">Sign in with email</button>
</form>
<div class="divider text-xs uppercase text-slate-400">Create account</div>
<form method="POST" action="/auth/email/sign-up" class="space-y-3">
<input type="hidden" name="returnTo" value="${escapeHtml(returnTo)}" /> <input type="hidden" name="returnTo" value="${escapeHtml(returnTo)}" />
<label class="form-control w-full"> <div class="form-control">
<span class="label-text text-slate-200 mb-1">Name</span> <label class="label">
<input name="name" required minlength="2" maxlength="80" class="input input-bordered w-full bg-slate-950" placeholder="Matiss" /> <span class="label-text">Username</span>
</label> </label>
<label class="form-control w-full"> <input name="userId" required minlength="2" maxlength="40" class="input input-bordered w-full" placeholder="matiss" />
<span class="label-text text-slate-200 mb-1">Email</span> </div>
<input name="email" type="email" required class="input input-bordered w-full bg-slate-950" placeholder="you@domain.com" /> <button class="btn btn-primary w-full">Continue</button>
</label>
<label class="form-control w-full">
<span class="label-text text-slate-200 mb-1">Password</span>
<input name="password" type="password" required minlength="8" maxlength="128" class="input input-bordered w-full bg-slate-950" placeholder="••••••••" />
</label>
<button class="btn btn-outline w-full">Create account</button>
</form> </form>
</div> </div>
</section> </div>
`, `,
}); });
} }
function renderAppPage({ userId, summary, jobs, flash = null }) { function renderAppPage({ userId, summary, jobs, flash = null }) {
const flashMarkup = flash ? `<div class="alert alert-info mb-4">${escapeHtml(flash)}</div>` : ""; const flashMarkup = flash ? `<div role="alert" class="alert alert-info mb-4"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg><span>${escapeHtml(flash)}</span></div>` : "";
const jobsMarkup = jobs.length === 0 const jobsMarkup = jobs.length === 0
? `<div class="text-sm text-slate-300">No generated audiobooks yet. Use the simulation form below or call the bot on X.</div>` ? `<div class="text-center py-10 text-base-content/60 bg-base-200 rounded-box">
: `<div class="space-y-2">${jobs <p>No generated audiobooks yet.</p>
.map((job) => `<a class="card bg-slate-950/70 border border-slate-700 hover:border-cyan-400 transition-colors" href="/audio/${job.assetId}"><div class="card-body p-3"><h3 class="font-semibold">${escapeHtml(job.article.title)}</h3><p class="text-xs text-slate-400">credits: ${job.creditsCharged} • status: ${escapeHtml(job.status)}</p></div></a>`) <p class="text-sm mt-1">Use the simulation form below or call the bot on X.</p>
.join("")}</div>`; </div>`
: `<div class="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
${jobs.map((job) => `
<a class="card bg-base-100 shadow-md hover:shadow-xl transition-all border border-base-200" href="/audio/${job.assetId}">
<div class="card-body p-4">
<h3 class="font-bold text-lg truncate" title="${escapeHtml(job.article.title)}">${escapeHtml(job.article.title)}</h3>
<div class="flex justify-between items-center mt-2">
<span class="badge badge-sm badge-ghost">credits: ${job.creditsCharged}</span>
<span class="text-xs uppercase font-mono opacity-70">${escapeHtml(job.status)}</span>
</div>
</div>
</a>`).join("")}
</div>`;
return shell({ return shell({
title: "Dashboard | XArtAudio", title: "Dashboard | XArtAudio",
content: ` content: `
${nav({ authenticated: true, userId })} ${nav({ authenticated: true, userId })}
${flashMarkup} ${flashMarkup}
<section class="grid gap-4 md:grid-cols-3 mb-5"> <section class="stats stats-vertical lg:stats-horizontal shadow w-full bg-base-200 mb-8">
<div class="stat bg-slate-900/70 border border-slate-700 rounded-xl"><div class="stat-title">Credits</div><div class="stat-value">${summary.balance}</div></div> <div class="stat">
<div class="stat bg-slate-900/70 border border-slate-700 rounded-xl"><div class="stat-title">Audiobooks</div><div class="stat-value">${summary.totalJobs}</div></div> <div class="stat-title">Credits</div>
<div class="stat bg-slate-900/70 border border-slate-700 rounded-xl"><div class="stat-title">Spent</div><div class="stat-value">${summary.totalCreditsSpent}</div></div> <div class="stat-value text-primary">${summary.balance}</div>
</div>
<div class="stat">
<div class="stat-title">Audiobooks</div>
<div class="stat-value text-secondary">${summary.totalJobs}</div>
</div>
<div class="stat">
<div class="stat-title">Spent</div>
<div class="stat-value">${summary.totalCreditsSpent}</div>
</div>
</section> </section>
<section class="grid gap-4 md:grid-cols-2"> <div class="grid gap-6 md:grid-cols-2 mb-8">
<article class="card bg-slate-900/80 border border-slate-700"> <div class="card bg-base-100 shadow-xl border border-base-200">
<div class="card-body p-4"> <div class="card-body">
<h2 class="font-bold">Top up credits</h2> <h2 class="card-title">Top up credits</h2>
<p class="text-xs text-slate-400">Dev shortcut for MVP. Production uses Polar webhook.</p> <p class="text-xs text-base-content/60 mb-2">Dev shortcut for MVP. Production uses Polar webhook.</p>
<form method="POST" action="/app/actions/topup" class="flex gap-2"> <form method="POST" action="/app/actions/topup" class="join w-full">
<input name="amount" type="number" min="1" max="500" value="10" class="input input-bordered w-full bg-slate-950" /> <input name="amount" type="number" min="1" max="500" value="10" class="input input-bordered join-item w-full" />
<button class="btn btn-primary">Add</button> <button class="btn btn-primary join-item">Add</button>
</form> </form>
</div> </div>
</article> </div>
<article class="card bg-slate-900/80 border border-slate-700"> <div class="card bg-base-100 shadow-xl border border-base-200">
<div class="card-body p-4"> <div class="card-body">
<h2 class="font-bold">Simulate mention</h2> <h2 class="card-title">Simulate mention</h2>
<p class="text-xs text-slate-400">Simulates a webhook mention event and article conversion.</p> <p class="text-xs text-base-content/60 mb-2">Simulates a webhook mention event.</p>
<form method="POST" action="/app/actions/simulate-mention" class="space-y-2"> <form method="POST" action="/app/actions/simulate-mention" class="flex flex-col gap-3">
<input name="title" required class="input input-bordered w-full bg-slate-950" placeholder="Article title" /> <input name="title" required class="input input-bordered w-full" placeholder="Article title" />
<textarea name="body" required rows="4" class="textarea textarea-bordered w-full bg-slate-950" placeholder="Paste article text..."></textarea> <textarea name="body" required rows="3" class="textarea textarea-bordered w-full" placeholder="Paste article text..."></textarea>
<button class="btn btn-accent w-full">Generate audiobook</button> <button class="btn btn-accent w-full">Generate audiobook</button>
</form> </form>
</div> </div>
</article> </div>
</section> </div>
<section class="mt-5"> <section>
<h2 class="font-bold mb-2">Recent audiobooks</h2> <h2 class="text-2xl font-bold mb-4">Recent audiobooks</h2>
${jobsMarkup} ${jobsMarkup}
</section> </section>
`, `,