Migrate Event Badges to V3 and implement Core Management pages

- Completed V3 migration for Event Badge CRUD operations
- Implemented User module V3 logic and editable fields
- Created management routes for Accounts, Sites, Users, and Lookups
- Updated Site Domain logic to use 'fqdn' and show 'access_key'
- Modernized Core Dashboard with navigation cards
- Restored Dexie User table definition
This commit is contained in:
Scott Idem
2026-01-06 13:38:47 -05:00
parent 1b318eeb8b
commit 00e80af3a1
29 changed files with 3800 additions and 218 deletions

View File

@@ -0,0 +1,113 @@
<script lang="ts">
import { onMount } from 'svelte';
import { load_ae_obj_li__user, create_ae_obj__user } from '$lib/ae_core/ae_core__user';
import { ae_api, ae_loc } from '$lib/stores/ae_stores';
import { goto } from '$app/navigation';
import { Plus, Search, User, ShieldCheck, Mail } from 'lucide-svelte';
let user_li: any[] = $state([]);
let loading = $state(true);
let qry_str = $state('');
let qry_enabled = $state('all');
async function load_users() {
loading = true;
user_li = await load_ae_obj_li__user({
api_cfg: $ae_api,
qry_str: qry_str || null,
enabled: qry_enabled as any,
log_lvl: 1
});
loading = false;
}
onMount(() => {
if (!$ae_loc.manager_access) {
goto('/core');
return;
}
load_users();
});
async function handle_add_user() {
const username = prompt('Enter new username:');
if (!username) return;
const email = prompt('Enter email address:');
await create_ae_obj__user({
api_cfg: $ae_api,
data_kv: { username, email, enable: true },
log_lvl: 1
});
load_users();
}
</script>
<div class="container mx-auto p-4 space-y-4">
<header class="flex justify-between items-center">
<div class="flex items-center gap-2">
<ShieldCheck size={24} />
<h1 class="h2">User Management</h1>
</div>
<button class="btn variant-filled-primary" onclick={handle_add_user}>
<Plus size={16} class="mr-2" /> Add User
</button>
</header>
<div class="card p-4 variant-soft flex flex-wrap gap-4 items-end">
<label class="label flex-1">
<span>Search Username or Email</span>
<div class="input-group input-group-divider grid-cols-[auto_1fr_auto]">
<div class="input-group-shim"><Search size={16} /></div>
<input type="search" bind:value={qry_str} placeholder="Search..." onkeydown={(e) => e.key === 'Enter' && load_users()} />
<button class="variant-filled-secondary" onclick={load_users}>Go</button>
</div>
</label>
<label class="label">
<span>Status</span>
<select class="select w-32" bind:value={qry_enabled} onchange={load_users}>
<option value="all">All</option>
<option value="enabled">Enabled</option>
<option value="not_enabled">Disabled</option>
</select>
</label>
</div>
{#if loading}
<div class="placeholder animate-pulse w-full h-64"></div>
{:else if user_li.length === 0}
<p class="text-center py-12 opacity-60">No users found.</p>
{:else}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{#each user_li as user}
<div class="card p-4 space-y-3 variant-soft flex flex-col justify-between">
<div class="space-y-2">
<header class="flex justify-between items-start">
<div class="flex items-center gap-2">
<div class="avatar variant-filled-surface w-10 h-10 flex items-center justify-center rounded-full">
<User size={20} />
</div>
<div>
<p class="font-bold">{user.username}</p>
<p class="text-xs opacity-60">{user.name || '--'}</p>
</div>
</div>
{#if user.super}
<span class="badge variant-filled-error">Super</span>
{:else if user.manager}
<span class="badge variant-filled-warning">Manager</span>
{/if}
</header>
<div class="flex items-center gap-2 text-sm opacity-80">
<Mail size={14} />
<span>{user.email || '--'}</span>
</div>
</div>
<button class="btn btn-sm variant-filled-primary w-full" onclick={() => goto(`/core/users/${user.user_id_random}`)}>
Edit User
</button>
</div>
{/each}
</div>
{/if}
</div>