update: refactor dashboard and login pages
This commit is contained in:
@@ -182,79 +182,90 @@ function renderLoginPage({ returnTo = "/app", error = null }) {
|
||||
<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-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">
|
||||
<form method="POST" action="/auth/dev-login" class="space-y-4">
|
||||
<input type="hidden" name="returnTo" value="${escapeHtml(returnTo)}" />
|
||||
<label class="form-control w-full">
|
||||
<span class="label-text text-slate-200 mb-1">Name</span>
|
||||
<input name="name" required minlength="2" maxlength="80" class="input input-bordered w-full bg-slate-950" placeholder="Matiss" />
|
||||
</label>
|
||||
<label class="form-control w-full">
|
||||
<span class="label-text text-slate-200 mb-1">Email</span>
|
||||
<input name="email" type="email" required class="input input-bordered w-full bg-slate-950" placeholder="you@domain.com" />
|
||||
</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>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Username</span>
|
||||
</label>
|
||||
<input name="userId" required minlength="2" maxlength="40" class="input input-bordered w-full" placeholder="matiss" />
|
||||
</div>
|
||||
<button class="btn btn-primary w-full">Continue</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
? `<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="space-y-2">${jobs
|
||||
.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>`)
|
||||
.join("")}</div>`;
|
||||
? `<div class="text-center py-10 text-base-content/60 bg-base-200 rounded-box">
|
||||
<p>No generated audiobooks yet.</p>
|
||||
<p class="text-sm mt-1">Use the simulation form below or call the bot on X.</p>
|
||||
</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({
|
||||
title: "Dashboard | XArtAudio",
|
||||
content: `
|
||||
${nav({ authenticated: true, userId })}
|
||||
${flashMarkup}
|
||||
<section class="grid gap-4 md:grid-cols-3 mb-5">
|
||||
<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 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 bg-slate-900/70 border border-slate-700 rounded-xl"><div class="stat-title">Spent</div><div class="stat-value">${summary.totalCreditsSpent}</div></div>
|
||||
<section class="stats stats-vertical lg:stats-horizontal shadow w-full bg-base-200 mb-8">
|
||||
<div class="stat">
|
||||
<div class="stat-title">Credits</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 class="grid gap-4 md:grid-cols-2">
|
||||
<article class="card bg-slate-900/80 border border-slate-700">
|
||||
<div class="card-body p-4">
|
||||
<h2 class="font-bold">Top up credits</h2>
|
||||
<p class="text-xs text-slate-400">Dev shortcut for MVP. Production uses Polar webhook.</p>
|
||||
<form method="POST" action="/app/actions/topup" class="flex gap-2">
|
||||
<input name="amount" type="number" min="1" max="500" value="10" class="input input-bordered w-full bg-slate-950" />
|
||||
<button class="btn btn-primary">Add</button>
|
||||
<div class="grid gap-6 md:grid-cols-2 mb-8">
|
||||
<div class="card bg-base-100 shadow-xl border border-base-200">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Top up credits</h2>
|
||||
<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="join w-full">
|
||||
<input name="amount" type="number" min="1" max="500" value="10" class="input input-bordered join-item w-full" />
|
||||
<button class="btn btn-primary join-item">Add</button>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<article class="card bg-slate-900/80 border border-slate-700">
|
||||
<div class="card-body p-4">
|
||||
<h2 class="font-bold">Simulate mention</h2>
|
||||
<p class="text-xs text-slate-400">Simulates a webhook mention event and article conversion.</p>
|
||||
<form method="POST" action="/app/actions/simulate-mention" class="space-y-2">
|
||||
<input name="title" required class="input input-bordered w-full bg-slate-950" placeholder="Article title" />
|
||||
<textarea name="body" required rows="4" class="textarea textarea-bordered w-full bg-slate-950" placeholder="Paste article text..."></textarea>
|
||||
<div class="card bg-base-100 shadow-xl border border-base-200">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Simulate mention</h2>
|
||||
<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="flex flex-col gap-3">
|
||||
<input name="title" required class="input input-bordered w-full" placeholder="Article title" />
|
||||
<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>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="mt-5">
|
||||
<h2 class="font-bold mb-2">Recent audiobooks</h2>
|
||||
<section>
|
||||
<h2 class="text-2xl font-bold mb-4">Recent audiobooks</h2>
|
||||
${jobsMarkup}
|
||||
</section>
|
||||
`,
|
||||
|
||||
Reference in New Issue
Block a user