feat: switch to phosphor icons for a cleaner dashboard UI

This commit is contained in:
ramvignesh-b
2026-05-11 11:21:01 +05:30
parent e00ddd2653
commit 17459ee493
3 changed files with 104 additions and 65 deletions
+3
View File
@@ -5,6 +5,7 @@
"": {
"name": "auth-server",
"dependencies": {
"@phosphor-icons/core": "^2.1.1",
"hono": "^4.12.18",
"ioredis": "^5.10.1",
"zod": "^4.4.3",
@@ -39,6 +40,8 @@
"@ioredis/commands": ["@ioredis/commands@1.5.1", "", {}, "sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw=="],
"@phosphor-icons/core": ["@phosphor-icons/core@2.1.1", "", {}, "sha512-v4ARvrip4qBCImOE5rmPUylOEK4iiED9ZyKjcvzuezqMaiRASCHKcRIuvvxL/twvLpkfnEODCOJp5dM4eZilxQ=="],
"@types/node": ["@types/node@22.19.18", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-9v00a+dn2yWVsYDEunWC4g/TcRKVq3r8N5FuZp7u0SGrPvdN9c2yXI9bBuf5Fl0hNCb+QTIePTn5pJs2pwBOQQ=="],
"ansi-escapes": ["ansi-escapes@7.3.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="],
+1
View File
@@ -10,6 +10,7 @@
"prepare": "husky"
},
"dependencies": {
"@phosphor-icons/core": "^2.1.1",
"hono": "^4.12.18",
"ioredis": "^5.10.1",
"zod": "^4.4.3"
+100 -65
View File
@@ -8,95 +8,125 @@
<link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<link href="https://cdn.jsdelivr.net/npm/daisyui@5/themes.css" rel="stylesheet" type="text/css" />
<script src="https://unpkg.com/@phosphor-icons/web@2.1.1"></script>
<style>
.font-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
.ph { font-size: 1.25rem; vertical-align: middle; }
</style>
</head>
<body class="bg-base-200 min-h-screen">
<div class="navbar bg-base-100 shadow-lg px-8">
<body class="bg-base-200/50 min-h-screen font-sans antialiased text-base-content">
<div class="navbar bg-base-100 shadow-sm px-4 md:px-8 border-b border-base-300">
<div class="flex-1">
<a class="btn btn-ghost normal-case text-xl font-bold">Auth Server</a>
</div>
<div class="flex-none gap-4">
<div class="form-control">
<div class="input-group">
<input type="password" id="apiKey" placeholder="API Key"
class="input input-bordered w-32 md:w-auto" />
<div class="flex items-center gap-2">
<div class="w-8 h-8 bg-primary rounded-lg flex items-center justify-center text-primary-content font-bold text-lg">
<i class="ph ph-shield-check"></i>
</div>
<a class="text-xl font-bold tracking-tight">Auth Server <span class="text-xs font-normal opacity-50 ml-1">v1.0</span></a>
</div>
</div>
<div class="flex-none">
<div class="join shadow-sm border border-base-300">
<div class="join-item bg-base-200 px-3 flex items-center border-r border-base-300">
<i class="ph ph-key opacity-70"></i>
</div>
<input type="password" id="apiKey" placeholder="Master API Key"
class="input join-item input-sm focus:outline-none w-32 md:w-64" />
</div>
</div>
</div>
<div class="container mx-auto p-4 md:p-8">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div class="card bg-base-100 shadow-xl col-span-1">
<div class="card-body">
<h2 class="card-title text-primary">Add/Update Provider</h2>
<div class="container mx-auto p-4 md:p-8 max-w-7xl">
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6 md:gap-8">
<div class="card bg-base-100 shadow-xl border border-base-300 lg:col-span-4 self-start">
<div class="card-body p-6">
<div class="flex items-center gap-2 mb-4">
<div class="w-2 h-6 bg-primary rounded-full"></div>
<h2 class="card-title text-xl font-bold">Configure Provider</h2>
</div>
<form id="configForm" class="space-y-4">
<div class="form-control">
<label class="label"><span class="label-text font-semibold">Provider Name</span></label>
<label class="label py-1"><span class="label-text font-semibold opacity-70">Provider ID</span></label>
<input type="text" id="providerName" placeholder="e.g. trakt" required
class="input input-bordered" />
class="input input-bordered focus:input-primary" />
</div>
<div class="divider text-xs opacity-50 my-2 uppercase tracking-widest">Credentials</div>
<div class="form-control">
<label class="label py-1"><span class="label-text font-semibold opacity-70">Client ID</span></label>
<input type="text" id="clientId" placeholder="OAuth client id" required class="input input-bordered focus:input-primary" />
</div>
<div class="form-control">
<label class="label"><span class="label-text font-semibold">Client ID</span></label>
<input type="text" id="clientId" required class="input input-bordered" />
<label class="label py-1"><span class="label-text font-semibold opacity-70">Client Secret</span></label>
<input type="password" id="clientSecret" placeholder="OAuth client secret" required class="input input-bordered focus:input-primary" />
</div>
<div class="divider text-xs opacity-50 my-2 uppercase tracking-widest">Endpoints</div>
<div class="form-control">
<label class="label py-1"><span class="label-text font-semibold opacity-70">Auth URL</span></label>
<input type="url" id="authUrl" placeholder="https://trakt.tv/oauth/authorize" required
class="input input-bordered focus:input-primary" />
</div>
<div class="form-control">
<label class="label"><span class="label-text font-semibold">Client Secret</span></label>
<input type="password" id="clientSecret" required class="input input-bordered" />
<label class="label py-1"><span class="label-text font-semibold opacity-70">Token URL</span></label>
<input type="url" id="tokenUrl" placeholder="https://api.trakt.tv/oauth/token" required
class="input input-bordered focus:input-primary" />
</div>
<div class="form-control">
<label class="label"><span class="label-text font-semibold">Auth URL</span></label>
<input type="url" id="authUrl" placeholder="https://..." required
class="input input-bordered" />
<label class="label py-1"><span class="label-text font-semibold opacity-70">Redirect URI</span></label>
<input type="url" id="redirectUri" placeholder="http://localhost:3000/auth/trakt/callback"
required class="input input-bordered focus:input-primary" />
</div>
<div class="form-control">
<label class="label"><span class="label-text font-semibold">Token URL</span></label>
<input type="url" id="tokenUrl" placeholder="https://..." required
class="input input-bordered" />
<label class="label py-1"><span class="label-text font-semibold opacity-70">Scope</span></label>
<input type="text" id="scope" placeholder="public" class="input input-bordered focus:input-primary" />
</div>
<div class="form-control">
<label class="label"><span class="label-text font-semibold">Redirect URI</span></label>
<input type="url" id="redirectUri" placeholder="http://localhost:3000/auth/.../callback"
required class="input input-bordered" />
</div>
<div class="form-control">
<label class="label"><span class="label-text font-semibold">Scope</span></label>
<input type="text" id="scope" placeholder="public" class="input input-bordered" />
</div>
<div class="card-actions justify-end mt-4">
<button type="submit" class="btn btn-primary w-full">Save Configuration</button>
<div class="card-actions pt-4">
<button type="submit" class="btn btn-primary w-full shadow-md">
<i class="ph ph-plus-bold"></i>
Save Configuration
</button>
</div>
</form>
</div>
</div>
<div class="card bg-base-100 shadow-xl col-span-1 lg:col-span-2">
<div class="card-body">
<div class="flex justify-between items-center mb-6">
<h2 class="card-title text-primary">Configured Providers</h2>
<button type="button" onclick="fetchProviders()" class="btn btn-sm btn-outline">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-4 h-4 mr-1" role="img" aria-label="Refresh icon">
<title>Refresh</title>
<path stroke-linecap="round" stroke-linejoin="round"
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99" />
</svg>
<div class="card bg-base-100 shadow-xl border border-base-300 lg:col-span-8">
<div class="card-body p-0">
<div class="p-6 pb-0 flex justify-between items-center">
<div class="flex items-center gap-2">
<div class="w-2 h-6 bg-secondary rounded-full"></div>
<h2 class="card-title text-xl font-bold">Registry</h2>
</div>
<button type="button" onclick="fetchProviders()" class="btn btn-sm btn-ghost border-base-300">
<i class="ph ph-arrows-clockwise mr-1"></i>
Refresh
</button>
</div>
<div class="divider my-2 opacity-10"></div>
<div class="overflow-x-auto">
<table class="table table-zebra w-full">
<table class="table w-full">
<thead>
<tr>
<th>Provider</th>
<th>Client ID</th>
<th>Actions</th>
<tr class="bg-base-200/50">
<th class="rounded-none font-bold uppercase tracking-wider text-xs opacity-60">Provider</th>
<th class="font-bold uppercase tracking-wider text-xs opacity-60">Client ID</th>
<th class="rounded-none text-right font-bold uppercase tracking-wider text-xs opacity-60">Actions</th>
</tr>
</thead>
<tbody id="providerTableBody">
<tr>
<td colspan="3" class="text-center italic opacity-50">Enter API Key to load
providers...</td>
<td colspan="3" class="text-center py-12">
<div class="flex flex-col items-center gap-2 opacity-40">
<i class="ph ph-database text-5xl mb-2"></i>
<p class="font-medium">Enter Master API Key to load registry</p>
</div>
</td>
</tr>
</tbody>
</table>
@@ -106,8 +136,8 @@
</div>
</div>
<div class="toast toast-end" id="notificationToast" style="display: none;">
<div class="alert alert-success">
<div class="toast toast-end" id="notificationToast" style="display: none; z-index: 100;">
<div class="alert shadow-lg border border-base-300">
<span id="notificationMessage">Message sent successfully.</span>
</div>
</div>
@@ -153,18 +183,19 @@
const entries = Object.entries(providers);
if (entries.length === 0) {
providerTableBody.innerHTML = '<tr><td colspan="3" class="text-center">No providers configured yet.</td></tr>';
providerTableBody.innerHTML = '<tr><td colspan="3" class="text-center py-8 opacity-50">No providers configured yet.</td></tr>';
return;
}
for (const [name, config] of entries) {
const row = document.createElement('tr');
row.className = "hover";
row.innerHTML = `
<td class="font-bold">${name}</td>
<td><code class="text-xs bg-base-200 p-1 rounded">${config.clientId}</code></td>
<td>
<div class="flex gap-2">
<a href="/auth/${name}/login" target="_blank" class="btn btn-xs btn-accent">Connect</a>
<td class="font-bold text-base-content/80">${name}</td>
<td><code class="text-xs bg-base-300 px-2 py-1 rounded-md font-mono">${config.clientId}</code></td>
<td class="text-right">
<div class="flex gap-2 justify-end">
<a href="/auth/${name}/login" target="_blank" class="btn btn-xs btn-primary">Connect</a>
<button type="button" onclick="editProvider('${name}')" class="btn btn-xs btn-outline">Edit</button>
</div>
</td>
@@ -173,11 +204,12 @@
};
} catch (error) {
console.error('Error fetching providers:', error);
providerTableBody.innerHTML = `<tr><td colspan="3" class="text-center text-error">Failed to load: ${error.message}</td></tr>`;
providerTableBody.innerHTML = `<tr><td colspan="3" class="text-center text-error py-4">Failed to load: ${error.message}</td></tr>`;
}
}
function _editProvider(name) {
// biome-ignore lint/correctness/noUnusedVariables: used in dynamically generated HTML
function editProvider(name) {
const config = providerData[name];
if (!config) return;
@@ -190,6 +222,9 @@
document.getElementById('scope').value = config.scope;
document.getElementById('configForm').scrollIntoView({ behavior: 'smooth' });
// Highlight the form field
document.getElementById('providerName').focus();
}
configForm.addEventListener('submit', async (e) => {