Polish results, FPP, and auth UI

This commit is contained in:
sas.fajri
2026-04-13 15:06:42 +07:00
parent 6e059c8115
commit a6f9b60362
2 changed files with 104 additions and 9 deletions

View File

@@ -687,7 +687,7 @@ function renderResultsTable(results, selectedResultId) {
(result) => `
<tr>
<td><strong>${escapeHtml(result.patient)}</strong><br /><span class="muted">${escapeHtml(result.summary)}</span></td>
<td><a href="/results/${result.id}">${escapeHtml(result.id)}</a></td>
<td><a href="/results/${result.id}" hx-get="/fragments/results/detail/${result.id}" hx-target="#result-detail-fragment" hx-swap="innerHTML">${escapeHtml(result.id)}</a></td>
<td>${escapeHtml(result.test)}</td>
<td>${statusBadge(result.status)}</td>
<td>${escapeHtml(result.date)}</td>
@@ -710,6 +710,8 @@ function renderResultsTable(results, selectedResultId) {
</div>
${statusBadge(result.status)}
</div>
<div style="height:12px"></div>
<a class="btn btn-secondary" href="/results/${result.id}" hx-get="/fragments/results/detail/${result.id}" hx-target="#result-detail-fragment" hx-swap="innerHTML">Open detail</a>
<p class="muted" style="margin-top:10px">${escapeHtml(result.summary)}</p>
</article>
`,
@@ -717,7 +719,7 @@ function renderResultsTable(results, selectedResultId) {
.join("")}
</div>
</section>
<aside class="panel">
<aside class="panel" id="result-detail-fragment">
${panelHeader("Selected result", "Use the right pane for quick context without losing list position.", `<a class="btn btn-secondary" href="/results/${selected.id}">Open detail</a>`)}
<div class="stack">
<div class="card">
@@ -766,7 +768,20 @@ function renderFpp(groups) {
${panelHeader("FPP catalog", "Filter blocks and list cards on mobile, richer panel on desktop.")}
<div class="pill-row">
${["All", ...mockFppGroups.map((item) => item.group)]
.map((item) => `<a class="pill ${groups.filter === item ? "active" : ""}" href="/fragments/fpp/list?group=${encodeURIComponent(item)}">${escapeHtml(item)}</a>`)
.map(
(item) => `
<button
class="pill ${groups.filter === item ? "active" : ""}"
type="button"
hx-get="/fragments/fpp/list?group=${encodeURIComponent(item)}"
hx-target="#fpp-fragment"
hx-swap="innerHTML"
hx-push-url="true"
>
${escapeHtml(item)}
</button>
`,
)
.join("")}
</div>
</section>
@@ -916,12 +931,21 @@ function loginPage({ error = "" } = {}) {
<section class="auth-card">
<div class="auth-visual">
<div class="badge">DocLink rebuild · responsive shell</div>
<h1>Clinical workflow that works on desktop and mobile.</h1>
<p>The old app behavior is preserved in a cleaner layout with dashboard, orders, results, FPP, and settings routes.</p>
<h1>Clinical workflow that stays fast on desktop and mobile.</h1>
<p>The rebuilt shell keeps the old app's workflows intact, but removes the cramped mobile-only feel.</p>
<div class="auth-highlight">
<strong>Built for doctors who move between screens</strong>
<span>Orders, results, FPP, and account actions stay one click away.</span>
</div>
<div class="auth-points">
<div class="auth-point"><strong>Route structure</strong><div>Login, dashboard, order flow, result flow, and account pages.</div></div>
<div class="auth-point"><strong>Responsive shell</strong><div>Persistent sidebar on desktop, bottom nav on mobile.</div></div>
<div class="auth-point"><strong>Clean interaction model</strong><div>HTMX fragments plus server-rendered templates.</div></div>
<div class="auth-point"><strong>HTMX fragments</strong><div>Detail panels update without leaving the page.</div></div>
<div class="auth-point"><strong>API adapter</strong><div>Login, orders, results, and save flows proxy upstream.</div></div>
</div>
<div class="auth-mini-grid">
<div class="auth-mini-card"><strong>5 routes</strong><span>Public + shell</span></div>
<div class="auth-mini-card"><strong>3 flows</strong><span>Most used actions</span></div>
<div class="auth-mini-card"><strong>1 session</strong><span>Cookie-backed auth</span></div>
</div>
</div>
<div class="auth-form">
@@ -940,6 +964,7 @@ function loginPage({ error = "" } = {}) {
<div class="pill-row">
<a class="pill" href="/splash">Open splash</a>
<a class="pill" href="/problem-login">Problem login</a>
<span class="pill">Demo fallback ready</span>
</div>
</div>
</section>
@@ -958,12 +983,20 @@ function splashPage() {
<div class="auth-visual">
<div class="badge">Starting DocLink</div>
<h1>Loading workspace...</h1>
<p>Checking session and routing into the right entry point.</p>
<p>Checking session state, then routing into the right clinical entry point.</p>
<div class="auth-highlight">
<strong>Fast redirect</strong>
<span>Users land on dashboard, login, or the error screen without extra clicks.</span>
</div>
</div>
<div class="auth-form">
<div class="empty-state">
<strong>Redirecting</strong>
<p>We will move you to the correct route in a moment.</p>
<div class="pill-row" style="justify-content:center; margin-top:14px">
<span class="pill">Session check</span>
<span class="pill">Route resolve</span>
</div>
</div>
</div>
</section>
@@ -982,7 +1015,11 @@ function problemLoginPage() {
<div class="auth-visual">
<div class="badge">Login problem</div>
<h1>Access needs attention.</h1>
<p>Use this page when auth state is broken or a session expires.</p>
<p>Use this page when auth state is broken, expired, or the upstream login rejects credentials.</p>
<div class="auth-highlight">
<strong>What to do</strong>
<span>Re-enter credentials or wait for the upstream service to recover.</span>
</div>
</div>
<div class="auth-form">
<div class="empty-state">
@@ -990,6 +1027,10 @@ function problemLoginPage() {
<p>The session was cleared. Go back to login and sign in again.</p>
<div style="height:16px"></div>
<a class="btn btn-primary" href="/login">Back to login</a>
<div class="pill-row" style="justify-content:center; margin-top:14px">
<span class="pill">Auth reset</span>
<span class="pill">Retry login</span>
</div>
</div>
</div>
</section>

View File

@@ -734,6 +734,52 @@ tr:last-child td {
gap: 18px;
}
.auth-highlight {
display: grid;
gap: 6px;
margin-top: 18px;
padding: 16px 18px;
border-radius: 18px;
color: #effaf8;
background: rgba(255, 255, 255, 0.12);
border: 1px solid rgba(255, 255, 255, 0.14);
}
.auth-highlight strong {
font-size: 1rem;
}
.auth-highlight span {
line-height: 1.55;
opacity: 0.94;
}
.auth-mini-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
margin-top: 18px;
}
.auth-mini-card {
padding: 14px 15px;
border-radius: 18px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.12);
}
.auth-mini-card strong {
display: block;
font-size: 1.05rem;
}
.auth-mini-card span {
display: block;
margin-top: 4px;
opacity: 0.86;
font-size: 0.88rem;
}
.auth-form h2 {
margin: 0;
font-size: 1.9rem;
@@ -823,6 +869,10 @@ tr:last-child td {
box-shadow: var(--shadow-lg);
}
.modal-card .panel-header {
margin-bottom: 18px;
}
.desktop-only {
display: block;
}
@@ -908,6 +958,10 @@ tr:last-child td {
max-height: 100vh;
border-radius: 0;
}
.auth-mini-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 620px) {