mirror of
https://github.com/BetterCorp/BetterFrame.git
synced 2026-05-27 02:56:33 +00:00
feat: global Settings page for AbleSign + Cloud Cam account config
- New /admin/settings page with AbleSign account setup (API key) and link to Cloud Cams config - Settings nav item in sidebar (gear icon, before Account) - Removed AbleSign Config from AbleSign dropdown (now in Settings) - AbleSign account delete redirects to Settings - Cloud Cams nav item kept for its own CRUD page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f0088836e9
commit
d6a52df27a
4 changed files with 78 additions and 3 deletions
|
|
@ -217,7 +217,7 @@ export function registerAbleSignRoutes(app: H3, deps: AdminDeps): void {
|
||||||
app.post("/admin/ablesign/:id/delete", async (event) => {
|
app.post("/admin/ablesign/:id/delete", async (event) => {
|
||||||
const id = getRouterParam(event, "id") ?? "";
|
const id = getRouterParam(event, "id") ?? "";
|
||||||
await deps.repo.deleteAbleSignAccount(id);
|
await deps.repo.deleteAbleSignAccount(id);
|
||||||
return new Response(null, { status: 302, headers: { location: "/admin/ablesign" } });
|
return new Response(null, { status: 302, headers: { location: "/admin/settings" } });
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/admin/ablesign/screens/:sid/delete", async (event) => {
|
app.post("/admin/ablesign/screens/:sid/delete", async (event) => {
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import {
|
||||||
renderKioskLabels,
|
renderKioskLabels,
|
||||||
renderDisplayLayouts,
|
renderDisplayLayouts,
|
||||||
renderDefaultLayoutSelect,
|
renderDefaultLayoutSelect,
|
||||||
|
SettingsPage,
|
||||||
} from "../../web-templates/admin-pages.js";
|
} from "../../web-templates/admin-pages.js";
|
||||||
import { discover as onvifDiscover, getEventProperties as onvifGetEventProperties } from "../../shared/onvif.js";
|
import { discover as onvifDiscover, getEventProperties as onvifGetEventProperties } from "../../shared/onvif.js";
|
||||||
import { generateBundle } from "../../shared/bundle.js";
|
import { generateBundle } from "../../shared/bundle.js";
|
||||||
|
|
@ -2219,6 +2220,14 @@ export function registerAdminRoutes(app: H3, deps: AdminDeps): void {
|
||||||
return new Response(null, { status: 302, headers: { location: `/admin/kiosks/${id}` } });
|
return new Response(null, { status: 302, headers: { location: `/admin/kiosks/${id}` } });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ---- Settings page ----------------------------------------------------------
|
||||||
|
|
||||||
|
app.get("/admin/settings", async () => {
|
||||||
|
const cloudAccounts = await deps.repo.listCloudAccounts();
|
||||||
|
const ablesignAccounts = await deps.repo.listAbleSignAccounts();
|
||||||
|
return htmlPage(SettingsPage({ cloudAccounts, ablesignAccounts }));
|
||||||
|
});
|
||||||
|
|
||||||
// ---- Tenant switcher fragment (htmx) ----------------------------------------
|
// ---- Tenant switcher fragment (htmx) ----------------------------------------
|
||||||
app.get("/admin/_tenant_switcher", async (event) => {
|
app.get("/admin/_tenant_switcher", async (event) => {
|
||||||
const tenants = await deps.repo.listTenants();
|
const tenants = await deps.repo.listTenants();
|
||||||
|
|
|
||||||
|
|
@ -4380,6 +4380,73 @@ export function TenantEditPage(props: TenantEditPageProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- Settings Page ----------------------------------------------------------
|
||||||
|
|
||||||
|
interface SettingsPageProps {
|
||||||
|
cloudAccounts: any[];
|
||||||
|
ablesignAccounts: any[];
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SettingsPage(props: SettingsPageProps) {
|
||||||
|
return (
|
||||||
|
<Layout title="Settings" activeNav="settings">
|
||||||
|
<h1 style="font-size:1.5rem; margin:0 0 1.5rem">Settings</h1>
|
||||||
|
|
||||||
|
{props.error ? <div class="alert alert-error" style="margin-bottom:1rem">{props.error}</div> : ""}
|
||||||
|
|
||||||
|
<div class="card" style="margin-bottom:1.5rem">
|
||||||
|
<h2 style="font-size:1.1rem; margin:0 0 1rem">AbleSign Account</h2>
|
||||||
|
{props.ablesignAccounts.length > 0 ? (
|
||||||
|
<div class="table-wrap">
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>Name</th><th>Screens</th><th>Last Sync</th><th>Actions</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
{props.ablesignAccounts.map((a: any) => (
|
||||||
|
<tr>
|
||||||
|
<td>{a.name}</td>
|
||||||
|
<td>{String(a.screen_count ?? 0)}</td>
|
||||||
|
<td style="font-size:0.85rem">{a.last_sync_at ? formatTime(a.last_sync_at) : "Never"}</td>
|
||||||
|
<td>
|
||||||
|
<form method="POST" action={`/admin/ablesign/${String(a.id)}/delete`} style="display:inline">
|
||||||
|
<button type="submit" class="btn btn-sm btn-ghost" style="color:#c00">Remove</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<form method="POST" action="/admin/ablesign/add" style="display:flex; gap:0.5rem; flex-wrap:wrap; align-items:end">
|
||||||
|
<label style="font-size:0.85rem">
|
||||||
|
{"Name"}<br/>
|
||||||
|
<input type="text" name="name" required style="width:10rem" placeholder="My AbleSign" />
|
||||||
|
</label>
|
||||||
|
<label style="font-size:0.85rem">
|
||||||
|
{"API Key"}<br/>
|
||||||
|
<input type="password" name="api_key" required style="width:14rem" placeholder="ak_..." />
|
||||||
|
</label>
|
||||||
|
<label style="font-size:0.85rem">
|
||||||
|
{"Workspace ID"}<br/>
|
||||||
|
<input type="text" name="workspace_id" style="width:6rem" />
|
||||||
|
</label>
|
||||||
|
<button type="submit" class="btn btn-sm">Connect</button>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card" style="margin-bottom:1.5rem">
|
||||||
|
<h2 style="font-size:1.1rem; margin:0 0 1rem">Cloud Camera Accounts</h2>
|
||||||
|
<p style="font-size:0.85rem; color:#999">
|
||||||
|
{"Manage cloud camera integrations at "}
|
||||||
|
<a href="/admin/cloud-accounts">Cloud Cams</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ---- AbleSign Pages ---------------------------------------------------------
|
// ---- AbleSign Pages ---------------------------------------------------------
|
||||||
|
|
||||||
interface AbleSignPageProps {
|
interface AbleSignPageProps {
|
||||||
|
|
|
||||||
|
|
@ -71,18 +71,17 @@ function Sidebar(props: { activeNav?: string }) {
|
||||||
<NavItem href="/admin/kiosks" label="Kiosks" icon="◈" active={a === "kiosks"} />
|
<NavItem href="/admin/kiosks" label="Kiosks" icon="◈" active={a === "kiosks"} />
|
||||||
<NavItem href="/admin/firmware" label="Firmware" icon="▲" active={a === "firmware"} />
|
<NavItem href="/admin/firmware" label="Firmware" icon="▲" active={a === "firmware"} />
|
||||||
<NavItem href="/admin/os-updates" label="OS Updates" icon="●" active={a === "os-updates"} />
|
<NavItem href="/admin/os-updates" label="OS Updates" icon="●" active={a === "os-updates"} />
|
||||||
<NavItem href="/admin/cloud-accounts" label="Cloud Cams" icon="☁" active={a === "cloud"} />
|
|
||||||
<NavGroup label="AbleSign" icon="▶" active={a?.startsWith("ablesign")}>
|
<NavGroup label="AbleSign" icon="▶" active={a?.startsWith("ablesign")}>
|
||||||
<NavItem href="/admin/ablesign/screens" label="Screens" icon=" " active={a === "ablesign-screens"} />
|
<NavItem href="/admin/ablesign/screens" label="Screens" icon=" " active={a === "ablesign-screens"} />
|
||||||
<NavItem href="/admin/ablesign/content" label="Content" icon=" " active={a === "ablesign-content"} />
|
<NavItem href="/admin/ablesign/content" label="Content" icon=" " active={a === "ablesign-content"} />
|
||||||
<NavItem href="/admin/ablesign/playlists" label="Playlists" icon=" " active={a === "ablesign-playlists"} />
|
<NavItem href="/admin/ablesign/playlists" label="Playlists" icon=" " active={a === "ablesign-playlists"} />
|
||||||
<NavItem href="/admin/ablesign" label="Config" icon=" " active={a === "ablesign"} />
|
|
||||||
</NavGroup>
|
</NavGroup>
|
||||||
<NavItem href="/admin/labels" label="Labels" icon="◆" active={a === "labels"} />
|
<NavItem href="/admin/labels" label="Labels" icon="◆" active={a === "labels"} />
|
||||||
<NavItem href="/admin/audit" label="Audit" icon="◎" active={a === "audit"} />
|
<NavItem href="/admin/audit" label="Audit" icon="◎" active={a === "audit"} />
|
||||||
<NavItem href="/admin/backup" label="Backup" icon="☼" active={a === "backup"} />
|
<NavItem href="/admin/backup" label="Backup" icon="☼" active={a === "backup"} />
|
||||||
<NavItem href="/admin/tenants" label="Tenants" icon="☷" active={a === "tenants"} />
|
<NavItem href="/admin/tenants" label="Tenants" icon="☷" active={a === "tenants"} />
|
||||||
<hr />
|
<hr />
|
||||||
|
<NavItem href="/admin/settings" label="Settings" icon="⚙" active={a === "settings"} />
|
||||||
<NavItem href="/admin/account" label="Account" icon="●" active={a === "account"} />
|
<NavItem href="/admin/account" label="Account" icon="●" active={a === "account"} />
|
||||||
<NavItem href="/admin/nodered" label="Node-RED" icon="→" active={a === "nodered"} />
|
<NavItem href="/admin/nodered" label="Node-RED" icon="→" active={a === "nodered"} />
|
||||||
<div class="tenant-switcher" {...{"hx-get": "/admin/_tenant_switcher", "hx-trigger": "load", "hx-swap": "innerHTML"}}></div>
|
<div class="tenant-switcher" {...{"hx-get": "/admin/_tenant_switcher", "hx-trigger": "load", "hx-swap": "innerHTML"}}></div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue