feat: access control UI in Settings + sidebar drag-and-drop reordering
Deploy Dashboard (Dev) / deploy-dev (push) Successful in 1m13s

- Add Access Control card in Settings (admin-only): role defaults display, user overrides CRUD
- Add sidebar drag-and-drop reordering with per-user persistence in rbac.json
- Backend: GET/PUT /api/auth/preferences endpoints, sidebar order helpers in rbac.py
- Frontend: HTML5 drag API with grip handles, smart order merge, debounced save
- Preserve sidebar_order when updating page overrides
This commit is contained in:
Gan, Jimmy
2026-03-01 14:48:35 +08:00
parent 9992105b49
commit a8debcfb4b
6 changed files with 334 additions and 20 deletions
+17 -2
View File
@@ -11,7 +11,7 @@
import Security from "./routes/Security.svelte";
import Login from "./routes/Login.svelte";
import { onMount } from "svelte";
import { getToken, checkAuth, setToken, setRefreshToken, currentUser, setCurrentUser } from "./lib/api.js";
import { getToken, checkAuth, setToken, setRefreshToken, currentUser, setCurrentUser, getPreferences, savePreferences } from "./lib/api.js";
let page = $state("dashboard");
let dark = $state(false);
@@ -20,6 +20,8 @@
let loading = $state(true);
let userRole = $state("");
let allowedPages = $state([]);
let sidebarOrder = $state(null);
let orderSaveTimer = null;
async function fetchUserInfo() {
try {
@@ -27,11 +29,24 @@
setCurrentUser(data);
userRole = data.role || "admin";
allowedPages = data.pages || "*";
// Fetch sidebar preferences
try {
const prefs = await getPreferences();
sidebarOrder = prefs.sidebar_order || null;
} catch {}
} catch (e) {
console.error("Failed to fetch user info:", e);
}
}
function handleOrderChange(newOrder) {
sidebarOrder = newOrder;
clearTimeout(orderSaveTimer);
orderSaveTimer = setTimeout(() => {
savePreferences({ sidebar_order: newOrder }).catch(() => {});
}, 500);
}
function hasPageAccess(pageId) {
if (allowedPages === "*") return true;
return Array.isArray(allowedPages) && allowedPages.includes(pageId);
@@ -109,7 +124,7 @@
<Login />
{:else}
<div class="flex h-screen overflow-hidden bg-surface-50 {dark ? 'dark bg-surface-900' : ''}">
<Sidebar bind:page {dark} {toggleTheme} {logout} bind:sidebarOpen {allowedPages} {userRole} />
<Sidebar bind:page {dark} {toggleTheme} {logout} bind:sidebarOpen {allowedPages} {userRole} {sidebarOrder} onOrderChange={handleOrderChange} />
<main class="flex-1 overflow-auto">
<div class="max-w-[1400px] mx-auto p-6 lg:p-8">
<!-- Mobile hamburger -->