improve pages

This commit is contained in:
nate 2026-04-10 06:05:50 +04:00
parent b1ad8f513b
commit 9339c67ba7
5 changed files with 31 additions and 24 deletions

View File

@ -680,7 +680,7 @@ export const dashboard = new Elysia()
})
// ── Status pages ──────────────────────────────────────────────────
.get("/dashboard/status-pages", async ({ cookie, headers }) => {
.get("/dashboard/pages", async ({ cookie, headers }) => {
const resolved = await getAccountId(cookie, headers);
if (!resolved?.accountId) return redirect("/dashboard");
const pages = await sql`
@ -688,25 +688,25 @@ export const dashboard = new Elysia()
FROM status_pages WHERE account_id = ${resolved.accountId}
ORDER BY created_at DESC
`;
return html("status-pages", { nav: "status-pages", pages });
return html("status-pages", { nav: "pages", pages });
})
.get("/dashboard/status-pages/new", async ({ cookie, headers }) => {
.get("/dashboard/pages/new", async ({ cookie, headers }) => {
const resolved = await getAccountId(cookie, headers);
if (!resolved?.accountId) return redirect("/dashboard");
const allMonitors = await sql`
SELECT id, name FROM monitors WHERE account_id = ${resolved.accountId} ORDER BY created_at DESC
`;
return html("status-page-edit", { nav: "status-pages", isNew: true, page: null, allMonitors });
return html("status-page-edit", { nav: "pages", isNew: true, page: null, allMonitors });
})
.get("/dashboard/status-pages/:id", async ({ cookie, headers, params }) => {
.get("/dashboard/pages/:id", async ({ cookie, headers, params }) => {
const resolved = await getAccountId(cookie, headers);
if (!resolved?.accountId) return redirect("/dashboard");
const [page] = await sql`
SELECT * FROM status_pages WHERE id = ${params.id} AND account_id = ${resolved.accountId}
`;
if (!page) return redirect("/dashboard/status-pages");
if (!page) return redirect("/dashboard/pages");
const [monitors, groups, allMonitors] = await Promise.all([
sql`SELECT monitor_id, display_name, group_id FROM status_page_monitors WHERE status_page_id = ${params.id} ORDER BY position ASC`,
sql`SELECT id, name, position FROM status_page_groups WHERE status_page_id = ${params.id} ORDER BY position ASC`,
@ -714,10 +714,10 @@ export const dashboard = new Elysia()
]);
page.monitors = monitors;
page.groups = groups;
return html("status-page-edit", { nav: "status-pages", isNew: false, page, allMonitors });
return html("status-page-edit", { nav: "pages", isNew: false, page, allMonitors });
})
.post("/dashboard/status-pages/new", async ({ cookie, headers, body }) => {
.post("/dashboard/pages/new", async ({ cookie, headers, body }) => {
const resolved = await getAccountId(cookie, headers);
if (!resolved?.accountId) return redirect("/dashboard");
const b = body as any;
@ -752,10 +752,10 @@ export const dashboard = new Elysia()
}),
});
} catch {}
return redirect("/dashboard/status-pages");
return redirect("/dashboard/pages");
})
.post("/dashboard/status-pages/:id/edit", async ({ cookie, headers, params, body }) => {
.post("/dashboard/pages/:id/edit", async ({ cookie, headers, params, body }) => {
const resolved = await getAccountId(cookie, headers);
if (!resolved?.accountId) return redirect("/dashboard");
const b = body as any;
@ -796,10 +796,10 @@ export const dashboard = new Elysia()
body: JSON.stringify(payload),
});
} catch {}
return redirect("/dashboard/status-pages");
return redirect("/dashboard/pages");
})
.post("/dashboard/status-pages/:id/delete", async ({ cookie, headers, params }) => {
.post("/dashboard/pages/:id/delete", async ({ cookie, headers, params }) => {
const resolved = await getAccountId(cookie, headers);
if (!resolved?.accountId) return redirect("/dashboard");
try {
@ -810,7 +810,7 @@ export const dashboard = new Elysia()
headers: { "Authorization": `Bearer ${key}` },
});
} catch {}
return redirect("/dashboard/status-pages");
return redirect("/dashboard/pages");
})
// ── Incidents ─────────────────────────────────────────────────────

View File

@ -68,7 +68,7 @@
<div>
<label class="block text-sm text-gray-400 mb-1.5">Show on status pages</label>
<% if (allPages.length === 0) { %>
<p class="text-xs text-gray-600">No status pages yet. <a href="/dashboard/status-pages/new" class="text-blue-400 hover:text-blue-300">Create one</a>.</p>
<p class="text-xs text-gray-600">No status pages yet. <a href="/dashboard/pages/new" class="text-blue-400 hover:text-blue-300">Create one</a>.</p>
<% } else { %>
<div class="flex flex-wrap gap-2">
<% allPages.forEach(function(p) { %>

View File

@ -2,7 +2,7 @@
<a href="/dashboard/home" class="text-xl font-bold tracking-tight group">Ping<span class="text-blue-400 transition-all group-hover:drop-shadow-[0_0_8px_rgba(59,130,246,0.4)]">QL</span></a>
<div class="flex items-center gap-5 text-sm text-gray-500">
<a href="/dashboard/home" class="<%= it.nav === 'monitors' ? 'text-gray-200 relative after:absolute after:bottom-[-18px] after:left-0 after:right-0 after:h-[2px] after:bg-blue-500 after:rounded-full' : 'hover:text-gray-300' %> transition-colors">Monitors</a>
<a href="/dashboard/status-pages" class="<%= it.nav === 'status-pages' ? 'text-gray-200 relative after:absolute after:bottom-[-18px] after:left-0 after:right-0 after:h-[2px] after:bg-blue-500 after:rounded-full' : 'hover:text-gray-300' %> transition-colors">Pages</a>
<a href="/dashboard/pages" class="<%= it.nav === 'pages' ? 'text-gray-200 relative after:absolute after:bottom-[-18px] after:left-0 after:right-0 after:h-[2px] after:bg-blue-500 after:rounded-full' : 'hover:text-gray-300' %> transition-colors">Pages</a>
<a href="/dashboard/incidents" class="<%= it.nav === 'incidents' ? 'text-gray-200 relative after:absolute after:bottom-[-18px] after:left-0 after:right-0 after:h-[2px] after:bg-blue-500 after:rounded-full' : 'hover:text-gray-300' %> transition-colors">Incidents</a>
<a href="/dashboard/notifications" class="<%= it.nav === 'notifications' ? 'text-gray-200 relative after:absolute after:bottom-[-18px] after:left-0 after:right-0 after:h-[2px] after:bg-blue-500 after:rounded-full' : 'hover:text-gray-300' %> transition-colors">Notifications</a>
<a href="/dashboard/settings" class="<%= it.nav === 'settings' ? 'text-gray-200 relative after:absolute after:bottom-[-18px] after:left-0 after:right-0 after:h-[2px] after:bg-blue-500 after:rounded-full' : 'hover:text-gray-300' %> transition-colors">Settings</a>

View File

@ -1,5 +1,5 @@
<%~ include('./partials/head', { title: it.isNew ? 'New status page' : 'Edit status page' }) %>
<%~ include('./partials/nav', { nav: 'status-pages' }) %>
<%~ include('./partials/nav', { nav: 'pages' }) %>
<%
const p = it.page || {};
@ -31,17 +31,24 @@
<main class="max-w-3xl mx-auto px-8 py-10">
<div class="mb-6">
<a href="/dashboard/status-pages" class="text-sm text-gray-500 hover:text-gray-300 transition-colors">&larr; Back to status pages</a>
<a href="/dashboard/pages" class="text-sm text-gray-500 hover:text-gray-300 transition-colors">&larr; Back to status pages</a>
<h1 class="text-xl font-semibold text-white mt-2"><%= it.isNew ? 'New status page' : 'Edit status page' %></h1>
</div>
<form method="POST" action="<%= it.isNew ? '/dashboard/status-pages/new' : '/dashboard/status-pages/' + p.id + '/edit' %>" class="space-y-6">
<form method="POST" action="<%= it.isNew ? '/dashboard/pages/new' : '/dashboard/pages/' + p.id + '/edit' %>" class="space-y-6">
<div>
<label class="block text-sm text-gray-400 mb-1.5">Slug</label>
<input name="slug" type="text" required value="<%= p.slug || '' %>" placeholder="my-app" pattern="^[a-z0-9][a-z0-9-]*$"
<input id="slug-input" name="slug" type="text" required value="<%= p.slug || '' %>" placeholder="my-app" pattern="^[a-z0-9][a-z0-9-]*$"
class="w-full bg-surface-solid border border-border-subtle rounded-lg px-4 py-2.5 text-gray-100 placeholder-gray-600 focus:outline-none focus:border-blue-500 font-mono text-sm">
<p class="text-xs text-gray-600 mt-1">Public URL: <span class="text-blue-400 font-mono">pages.pingql.com/<your-slug></span></p>
<p class="text-xs text-gray-600 mt-1">Public URL: <a id="slug-url" href="https://pages.pingql.com/<%= p.slug || '' %>" target="_blank" class="text-blue-400 hover:text-blue-300 font-mono">pages.pingql.com/<span id="slug-preview"><%= p.slug || '' %></span></a></p>
<script>
document.getElementById('slug-input').addEventListener('input', function() {
var v = this.value.trim();
document.getElementById('slug-preview').textContent = v || '';
document.getElementById('slug-url').href = 'https://pages.pingql.com/' + encodeURIComponent(v);
});
</script>
</div>
<div>

View File

@ -1,11 +1,11 @@
<%~ include('./partials/head', { title: 'Status pages' }) %>
<%~ include('./partials/nav', { nav: 'status-pages' }) %>
<%~ include('./partials/nav', { nav: 'pages' }) %>
<main class="max-w-3xl mx-auto px-8 py-10 space-y-8">
<div class="flex items-center justify-between">
<h1 class="text-xl font-semibold text-white">Status pages</h1>
<a href="/dashboard/status-pages/new" class="btn-primary inline-flex items-center gap-2 px-4 py-2 text-sm">+ New page</a>
<a href="/dashboard/pages/new" class="btn-primary inline-flex items-center gap-2 px-4 py-2 text-sm">+ New page</a>
</div>
<p class="text-sm text-gray-500 leading-relaxed">
@ -33,8 +33,8 @@
<% if (p.description) { %><p class="text-xs text-gray-500 mt-1"><%= p.description %></p><% } %>
</div>
<div class="flex items-center gap-2 shrink-0">
<a href="/dashboard/status-pages/<%= p.id %>" class="px-3 py-1.5 rounded-lg border border-border-subtle text-gray-400 hover:text-gray-200 text-xs transition-colors">Edit</a>
<form action="/dashboard/status-pages/<%= p.id %>/delete" method="POST" class="inline" onsubmit="return confirm('Delete status page \'<%= p.title %>\'? This cannot be undone.')">
<a href="/dashboard/pages/<%= p.id %>" class="px-3 py-1.5 rounded-lg border border-border-subtle text-gray-400 hover:text-gray-200 text-xs transition-colors">Edit</a>
<form action="/dashboard/pages/<%= p.id %>/delete" method="POST" class="inline" onsubmit="return confirm('Delete status page \'<%= p.title %>\'? This cannot be undone.')">
<button type="submit" class="px-3 py-1.5 rounded-lg border border-red-900/30 text-red-400 hover:bg-red-900/20 hover:border-red-800/40 text-xs transition-colors">Delete</button>
</form>
</div>