🪻 distributed transcription service thistle.dunkirk.sh

feat: use the query param to indicate tab in admin ui

dunkirk.sh f68f4adb 9b70d9be

verified
Changed files
+42 -12
src
pages
+10 -1
CRUSH.md
···
**Admin UI features:**
- Statistics cards (total users, total/failed transcriptions)
-
- Tabbed interface (Transcriptions / Users)
- Status badges for transcription states
- Delete buttons for transcriptions with confirmation
- Role dropdown for changing user roles
···
- User avatars and info display
- Timestamp formatting
- Admin badge on user listings
**Implementation notes:**
- `role` column in users table ('user' or 'admin', default 'user')
···
**Admin UI features:**
- Statistics cards (total users, total/failed transcriptions)
+
- Tabbed interface (Pending Recordings / Transcriptions / Users / Classes)
- Status badges for transcription states
- Delete buttons for transcriptions with confirmation
- Role dropdown for changing user roles
···
- User avatars and info display
- Timestamp formatting
- Admin badge on user listings
+
- Query parameter support for direct tab navigation (`?tab=<tabname>`)
+
+
**Admin tab navigation:**
+
- `/admin` - Opens to default "pending" tab
+
- `/admin?tab=pending` - Pending recordings tab
+
- `/admin?tab=transcriptions` - All transcriptions tab
+
- `/admin?tab=users` - Users management tab
+
- `/admin?tab=classes` - Classes management tab
+
- URL updates when switching tabs (browser history support)
**Implementation notes:**
- `role` column in users table ('user' or 'admin', default 'user')
+32 -11
src/pages/admin.html
···
}
// Tab switching
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', () => {
-
const tabName = tab.dataset.tab;
-
-
document.querySelectorAll('.tab').forEach(t => {
-
t.classList.remove('active');
-
});
-
document.querySelectorAll('.tab-content').forEach(c => {
-
c.classList.remove('active');
-
});
-
-
tab.classList.add('active');
-
document.getElementById(`${tabName}-tab`).classList.add('active');
});
});
// Initialize
loadStats();
···
}
// Tab switching
+
function switchTab(tabName) {
+
document.querySelectorAll('.tab').forEach(t => {
+
t.classList.remove('active');
+
});
+
document.querySelectorAll('.tab-content').forEach(c => {
+
c.classList.remove('active');
+
});
+
+
const tabButton = document.querySelector(`[data-tab="${tabName}"]`);
+
const tabContent = document.getElementById(`${tabName}-tab`);
+
+
if (tabButton && tabContent) {
+
tabButton.classList.add('active');
+
tabContent.classList.add('active');
+
+
// Update URL without reloading
+
const url = new URL(window.location.href);
+
url.searchParams.set('tab', tabName);
+
window.history.pushState({}, '', url);
+
}
+
}
+
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', () => {
+
switchTab(tab.dataset.tab);
});
});
+
+
// Check for tab query parameter on load
+
const params = new URLSearchParams(window.location.search);
+
const initialTab = params.get('tab');
+
const validTabs = ['pending', 'transcriptions', 'users', 'classes'];
+
+
if (initialTab && validTabs.includes(initialTab)) {
+
switchTab(initialTab);
+
}
// Initialize
loadStats();