- TemplateManager with LEGO-style building block composition (5 block types) - BlindStructureEditor with full level fields, mixed game, reorder, add/delete - StructureWizard generates structures from player count, chips, duration params - More page with navigable menu to all sub-pages (admin-gated operators section) - Templates page with DataTable list, create/edit/duplicate/delete actions - Structures page with DataTable list, wizard integration, and editor - Settings page with venue config, currency, receipts, theme toggle (Mocha/Latte) - Audit log page with filterable DataTable, detail panel, and undo capability Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
202 lines
4.9 KiB
Svelte
202 lines
4.9 KiB
Svelte
<script lang="ts">
|
|
/**
|
|
* More tab page.
|
|
*
|
|
* Navigation list to sub-pages: templates, blind structures, chip sets,
|
|
* payout structures, buy-in configs, venue settings, operators,
|
|
* audit log, and about/version.
|
|
*/
|
|
|
|
import { auth } from '$lib/stores/auth.svelte';
|
|
import { goto } from '$app/navigation';
|
|
|
|
interface MenuItem {
|
|
label: string;
|
|
description: string;
|
|
href: string;
|
|
icon: string;
|
|
adminOnly?: boolean;
|
|
}
|
|
|
|
const menuItems: MenuItem[] = [
|
|
{ label: 'Tournament Templates', description: 'LEGO-style template builder', href: '/more/templates', icon: '\u{1F4CB}' },
|
|
{ label: 'Blind Structures', description: 'Level timing and blinds', href: '/more/structures', icon: '\u{23F1}' },
|
|
{ label: 'Chip Sets', description: 'Denomination configurations', href: '/more/templates', icon: '\u{1FA99}' },
|
|
{ label: 'Payout Structures', description: 'Prize distribution brackets', href: '/more/templates', icon: '\u{1F4B0}' },
|
|
{ label: 'Buy-in Configs', description: 'Entry fees and rake', href: '/more/templates', icon: '\u{1F3AB}' },
|
|
{ label: 'Venue Settings', description: 'Currency, receipts, theme', href: '/more/settings', icon: '\u{2699}' },
|
|
{ label: 'Operators', description: 'Manage floor staff', href: '/more/settings', icon: '\u{1F464}', adminOnly: true },
|
|
{ label: 'Audit Log', description: 'Action history and undo', href: '/more/audit', icon: '\u{1F4DC}' },
|
|
{ label: 'About / Version', description: 'Felt v1.0 - Phase 1', href: '/more/settings', icon: '\u{2139}' }
|
|
];
|
|
|
|
let visibleItems = $derived(
|
|
menuItems.filter((item) => !item.adminOnly || auth.isAdmin)
|
|
);
|
|
|
|
function handleLogout(): void {
|
|
auth.logout();
|
|
goto('/login');
|
|
}
|
|
</script>
|
|
|
|
<div class="page-content">
|
|
<h2>More</h2>
|
|
<p class="text-secondary">Settings, templates, and administration.</p>
|
|
|
|
<!-- Current operator info -->
|
|
<div class="operator-card">
|
|
<div class="operator-info">
|
|
<span class="operator-name">{auth.operator?.name ?? 'Unknown'}</span>
|
|
<span class="operator-role">{auth.operator?.role ?? 'Unknown'}</span>
|
|
</div>
|
|
<button class="logout-btn touch-target" onclick={handleLogout}>
|
|
Sign Out
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Menu list -->
|
|
<nav class="menu-list" aria-label="More options">
|
|
{#each visibleItems as item}
|
|
<a href={item.href} class="menu-item touch-target">
|
|
<span class="menu-icon" aria-hidden="true">{item.icon}</span>
|
|
<div class="menu-text">
|
|
<span class="menu-label">{item.label}</span>
|
|
<span class="menu-desc">{item.description}</span>
|
|
</div>
|
|
<span class="menu-arrow" aria-hidden="true">›</span>
|
|
</a>
|
|
{/each}
|
|
</nav>
|
|
</div>
|
|
|
|
<style>
|
|
.page-content {
|
|
padding: var(--space-4);
|
|
}
|
|
|
|
h2 {
|
|
font-size: var(--text-2xl);
|
|
font-weight: 700;
|
|
color: var(--color-text);
|
|
margin-bottom: var(--space-1);
|
|
}
|
|
|
|
.text-secondary {
|
|
color: var(--color-text-secondary);
|
|
font-size: var(--text-sm);
|
|
margin-bottom: var(--space-6);
|
|
}
|
|
|
|
/* Operator card */
|
|
.operator-card {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: var(--space-4);
|
|
background-color: var(--color-surface);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-lg);
|
|
margin-bottom: var(--space-6);
|
|
}
|
|
|
|
.operator-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
}
|
|
|
|
.operator-name {
|
|
font-size: var(--text-base);
|
|
font-weight: 600;
|
|
color: var(--color-text);
|
|
}
|
|
|
|
.operator-role {
|
|
font-size: var(--text-xs);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
color: var(--color-text-muted);
|
|
}
|
|
|
|
.logout-btn {
|
|
padding: var(--space-2) var(--space-3);
|
|
font-size: var(--text-sm);
|
|
font-weight: 600;
|
|
color: var(--color-error);
|
|
background: none;
|
|
border: 1px solid var(--color-error);
|
|
border-radius: var(--radius-md);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.logout-btn:hover {
|
|
background-color: var(--color-error);
|
|
color: white;
|
|
}
|
|
|
|
/* Menu list */
|
|
.menu-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
background-color: var(--color-surface);
|
|
border-radius: var(--radius-lg);
|
|
border: 1px solid var(--color-border);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.menu-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-3);
|
|
padding: var(--space-3) var(--space-4);
|
|
min-height: var(--touch-target);
|
|
text-decoration: none;
|
|
color: var(--color-text);
|
|
border-bottom: 1px solid var(--color-border);
|
|
transition: background-color var(--transition-fast);
|
|
}
|
|
|
|
.menu-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.menu-item:hover {
|
|
background-color: var(--color-surface-hover);
|
|
}
|
|
|
|
.menu-item:active {
|
|
background-color: var(--color-surface-active);
|
|
}
|
|
|
|
.menu-icon {
|
|
font-size: var(--text-xl);
|
|
width: 32px;
|
|
text-align: center;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.menu-text {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1px;
|
|
}
|
|
|
|
.menu-label {
|
|
font-size: var(--text-base);
|
|
font-weight: 500;
|
|
color: var(--color-text);
|
|
}
|
|
|
|
.menu-desc {
|
|
font-size: var(--text-xs);
|
|
color: var(--color-text-muted);
|
|
}
|
|
|
|
.menu-arrow {
|
|
font-size: var(--text-xl);
|
|
color: var(--color-text-muted);
|
|
flex-shrink: 0;
|
|
}
|
|
</style>
|