From 67db106fba627ff723615ea0cf22b07cbfd6b0d5 Mon Sep 17 00:00:00 2001 From: JB Date: Sat, 25 Jul 2026 20:55:13 -0400 Subject: [PATCH] Rename admin routes to ISAC --- docs/DESIGN_SYSTEM.md | 2 +- docs/architecture.md | 6 +++- scripts/seed-test-notifications.mjs | 4 +-- src/hooks.server.ts | 6 +++- src/lib/components/AppShell.svelte | 4 +-- src/routes/{admin => isac}/+layout.server.ts | 0 src/routes/{admin => isac}/+layout.svelte | 32 +++++++++---------- src/routes/{admin => isac}/+page.server.ts | 10 +++--- .../attributes/+page.server.ts | 0 .../{admin => isac}/attributes/+page.svelte | 0 .../{admin => isac}/audit/+page.server.ts | 0 src/routes/{admin => isac}/audit/+page.svelte | 0 .../avatar-oversight/+page.server.ts | 0 .../avatar-oversight/+page.svelte | 0 .../{admin => isac}/brands/+page.server.ts | 0 .../{admin => isac}/brands/+page.svelte | 0 .../contributor-guidelines/+page.server.ts | 6 ++-- .../contributor-guidelines/+page.svelte | 0 .../data-moderation/+page.server.ts | 8 ++--- .../data-moderation/+page.svelte | 0 .../{admin => isac}/gear/+page.server.ts | 0 src/routes/{admin => isac}/gear/+page.svelte | 0 .../{admin => isac}/metrics/+page.server.ts | 0 .../{admin => isac}/metrics/+page.svelte | 0 .../{admin => isac}/protocols/+page.server.ts | 0 .../{admin => isac}/protocols/+page.svelte | 0 .../{admin => isac}/roles/+page.server.ts | 0 src/routes/{admin => isac}/roles/+page.svelte | 0 .../skillchips/+page.server.ts | 0 .../{admin => isac}/skillchips/+page.svelte | 0 .../{admin => isac}/talents/+page.server.ts | 0 .../{admin => isac}/talents/+page.svelte | 2 +- .../{admin => isac}/users/+page.server.ts | 0 src/routes/{admin => isac}/users/+page.svelte | 2 +- .../users/[id]/+page.server.ts | 0 .../{admin => isac}/users/[id]/+page.svelte | 2 +- .../{admin => isac}/weapons/+page.server.ts | 0 .../{admin => isac}/weapons/+page.svelte | 0 38 files changed, 46 insertions(+), 38 deletions(-) rename src/routes/{admin => isac}/+layout.server.ts (100%) rename src/routes/{admin => isac}/+layout.svelte (75%) rename src/routes/{admin => isac}/+page.server.ts (64%) rename src/routes/{admin => isac}/attributes/+page.server.ts (100%) rename src/routes/{admin => isac}/attributes/+page.svelte (100%) rename src/routes/{admin => isac}/audit/+page.server.ts (100%) rename src/routes/{admin => isac}/audit/+page.svelte (100%) rename src/routes/{admin => isac}/avatar-oversight/+page.server.ts (100%) rename src/routes/{admin => isac}/avatar-oversight/+page.svelte (100%) rename src/routes/{admin => isac}/brands/+page.server.ts (100%) rename src/routes/{admin => isac}/brands/+page.svelte (100%) rename src/routes/{admin => isac}/contributor-guidelines/+page.server.ts (94%) rename src/routes/{admin => isac}/contributor-guidelines/+page.svelte (100%) rename src/routes/{admin => isac}/data-moderation/+page.server.ts (96%) rename src/routes/{admin => isac}/data-moderation/+page.svelte (100%) rename src/routes/{admin => isac}/gear/+page.server.ts (100%) rename src/routes/{admin => isac}/gear/+page.svelte (100%) rename src/routes/{admin => isac}/metrics/+page.server.ts (100%) rename src/routes/{admin => isac}/metrics/+page.svelte (100%) rename src/routes/{admin => isac}/protocols/+page.server.ts (100%) rename src/routes/{admin => isac}/protocols/+page.svelte (100%) rename src/routes/{admin => isac}/roles/+page.server.ts (100%) rename src/routes/{admin => isac}/roles/+page.svelte (100%) rename src/routes/{admin => isac}/skillchips/+page.server.ts (100%) rename src/routes/{admin => isac}/skillchips/+page.svelte (100%) rename src/routes/{admin => isac}/talents/+page.server.ts (100%) rename src/routes/{admin => isac}/talents/+page.svelte (98%) rename src/routes/{admin => isac}/users/+page.server.ts (100%) rename src/routes/{admin => isac}/users/+page.svelte (93%) rename src/routes/{admin => isac}/users/[id]/+page.server.ts (100%) rename src/routes/{admin => isac}/users/[id]/+page.svelte (99%) rename src/routes/{admin => isac}/weapons/+page.server.ts (100%) rename src/routes/{admin => isac}/weapons/+page.svelte (100%) diff --git a/docs/DESIGN_SYSTEM.md b/docs/DESIGN_SYSTEM.md index 8754ce8..614fa5a 100644 --- a/docs/DESIGN_SYSTEM.md +++ b/docs/DESIGN_SYSTEM.md @@ -435,7 +435,7 @@ Do not communicate state by color alone; include a label, icon, or pattern. ## Adding a new admin module -1. Add the route beneath `src/routes/admin`. +1. Add the route beneath `src/routes/isac`. 2. Register it in the admin layout navigation. 3. Start with the module toolbar and KPI row. 4. Reuse dashboard editor and table classes. diff --git a/docs/architecture.md b/docs/architecture.md index 55a8926..68ab9a8 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -181,7 +181,11 @@ specialized workflow rather than inferred database writes. # System metrics -The read-only `/admin/metrics` route requires `metrics.view` and collects a +ISAC uses `/isac` as its canonical route namespace. The server permanently +redirects legacy `/admin` URLs to their matching `/isac` location so old +notifications and external bookmarks remain usable. + +The read-only `/isac/metrics` route requires `metrics.view` and collects a point-in-time process-local health snapshot. Node and operating-system APIs provide process CPU and memory, event-loop utilization and delay, host memory, load average, core count, platform, and uptime. Embedded Turso pragmas provide diff --git a/scripts/seed-test-notifications.mjs b/scripts/seed-test-notifications.mjs index c2227df..ae45a07 100644 --- a/scripts/seed-test-notifications.mjs +++ b/scripts/seed-test-notifications.mjs @@ -12,8 +12,8 @@ if (!user) throw new Error(`No user matched ${targetName}.`); const now = Date.now(); const samples = [ ['isac', 'ISAC AVATAR OVERSIGHT', '[TEST] Custom avatar removed', 'Your custom avatar did not meet SHD.build image requirements and was removed. Oversight reason: The image contained unrelated promotional text.', '/account#avatar', 1], - ['contributions', 'ISAC DATA OVERSIGHT', '[TEST] Contribution approved', 'Quick Draw brand-set data was approved and deployed to the live builder registry.', '/admin/brands', 1], - ['contributions', 'ISAC DATA OVERSIGHT', '[TEST] Contribution needs revision', 'A talent update was not approved. Oversight reason: Tier II effect value does not match the supplied game evidence.', '/admin/talents', 1], + ['contributions', 'ISAC DATA OVERSIGHT', '[TEST] Contribution approved', 'Quick Draw brand-set data was approved and deployed to the live builder registry.', '/isac/brands', 1], + ['contributions', 'ISAC DATA OVERSIGHT', '[TEST] Contribution needs revision', 'A talent update was not approved. Oversight reason: Tier II effect value does not match the supplied game evidence.', '/isac/talents', 1], ['comments', 'SHD COMMUNITY', '[TEST] New comment on your build', 'An agent commented on “First Wave Vector Critical Build”: This setup worked really well in Legendary missions.', '/builds', 0] ]; for (const [index, sample] of samples.entries()) { diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 9a90c7e..4e17f56 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -4,8 +4,12 @@ import { getSessionUser } from '$lib/server/auth/session'; import { DATA_MODULES, queueDataProposal, shouldModerateDataMutation, type DataModule } from '$lib/server/data-moderation'; export const handle: Handle = async ({ event, resolve }) => { + if (event.url.pathname === '/admin' || event.url.pathname.startsWith('/admin/')) { + const location = `/isac${event.url.pathname.slice('/admin'.length)}${event.url.search}`; + return new Response(null, { status: 308, headers: { location } }); + } if (event.request.method === 'POST') { - const match = event.url.pathname.match(/^\/admin\/([^/]+)$/); + const match = event.url.pathname.match(/^\/isac\/([^/]+)$/); const module = match?.[1] as DataModule | undefined; const action = event.url.search.startsWith('?/') ? event.url.search.slice(2) : ''; if (module && DATA_MODULES.includes(module) && action) { diff --git a/src/lib/components/AppShell.svelte b/src/lib/components/AppShell.svelte index 7059809..58dd061 100644 --- a/src/lib/components/AppShell.svelte +++ b/src/lib/components/AppShell.svelte @@ -90,7 +90,7 @@ My Profile {/if} {#if isacAccess} - ISAC + ISAC {/if} {#if user} @@ -120,7 +120,7 @@ {:else} 04Sign In› {/if} - {#if isacAccess}06ISAC›{/if} + {#if isacAccess}06ISAC›{/if} {#if user}
AGENT ONLINE{user.displayName}
{#snippet children(pending)}{/snippet} diff --git a/src/routes/admin/+layout.server.ts b/src/routes/isac/+layout.server.ts similarity index 100% rename from src/routes/admin/+layout.server.ts rename to src/routes/isac/+layout.server.ts diff --git a/src/routes/admin/+layout.svelte b/src/routes/isac/+layout.svelte similarity index 75% rename from src/routes/admin/+layout.svelte rename to src/routes/isac/+layout.svelte index 5078ceb..2346007 100644 --- a/src/routes/admin/+layout.svelte +++ b/src/routes/isac/+layout.svelte @@ -4,30 +4,30 @@ const can = (permission: string) => data.permissions.includes('*') || data.permissions.includes(permission); const canSeeData = $derived(['data.view', 'data.manage', 'data.propose', 'data.approve'].some(can)); const contributorEnabled = $derived(Boolean(data.contributorMode?.enabled)); - const guidelinesPage = $derived(page.url.pathname === '/admin/contributor-guidelines'); + const guidelinesPage = $derived(page.url.pathname === '/isac/contributor-guidelines'); const dataReadOnly = $derived(can('data.view') && !can('data.manage') && (!can('data.propose') || !contributorEnabled) && !guidelinesPage); const dataMode = $derived(can('*') ? 'ADMIN' : can('data.approve') ? 'OVERSIGHT' : can('data.manage') ? 'DATA MANAGER' : contributorEnabled ? 'CONTRIBUTOR' : 'READ-ONLY'); const dataModeClass = $derived(dataMode.toLowerCase().replace(' ', '-')); let readOnlyBlocked = $state(false); const dataSections = [ - { href: '/admin/attributes', label: 'Attributes', code: 'ATTR', module: 'attributes' }, - { href: '/admin/talents', label: 'Talents', code: 'TLNT', module: 'talents' }, - { href: '/admin/brands', label: 'Brand sets', code: 'BRND', module: 'brands' }, - { href: '/admin/gear', label: 'Gear', code: 'GEAR', module: 'gear' }, - { href: '/admin/weapons', label: 'Weapons', code: 'WPN', module: 'weapons' }, - { href: '/admin/protocols', label: 'OS Protocols', code: 'OSPR', module: 'protocols' }, - { href: '/admin/skillchips', label: 'Skill Chips', code: 'CHIP', module: 'skillchips' } + { href: '/isac/attributes', label: 'Attributes', code: 'ATTR', module: 'attributes' }, + { href: '/isac/talents', label: 'Talents', code: 'TLNT', module: 'talents' }, + { href: '/isac/brands', label: 'Brand sets', code: 'BRND', module: 'brands' }, + { href: '/isac/gear', label: 'Gear', code: 'GEAR', module: 'gear' }, + { href: '/isac/weapons', label: 'Weapons', code: 'WPN', module: 'weapons' }, + { href: '/isac/protocols', label: 'OS Protocols', code: 'OSPR', module: 'protocols' }, + { href: '/isac/skillchips', label: 'Skill Chips', code: 'CHIP', module: 'skillchips' } ]; const userSections = [ - { href: '/admin/users', label: 'User operations', code: 'USER', permission: 'users.view' }, - { href: '/admin/roles', label: 'Roles', code: 'ROLE', permission: 'users.roles' } + { href: '/isac/users', label: 'User operations', code: 'USER', permission: 'users.view' }, + { href: '/isac/roles', label: 'Roles', code: 'ROLE', permission: 'users.roles' } ]; const visibleUserSections = $derived(userSections.filter((section) => can(section.permission))); const oversightSections = [ - { href: '/admin/data-moderation', label: 'Data Oversight', code: 'OVST', permission: 'data.approve' }, - { href: '/admin/avatar-oversight', label: 'Avatar Oversight', code: 'AVTR', permission: 'avatars.review' }, - { href: '/admin/audit', label: 'Audit history', code: 'AUDT', permission: 'audit.view' }, - { href: '/admin/metrics', label: 'System metrics', code: 'MTRX', permission: 'metrics.view' } + { href: '/isac/data-moderation', label: 'Data Oversight', code: 'OVST', permission: 'data.approve' }, + { href: '/isac/avatar-oversight', label: 'Avatar Oversight', code: 'AVTR', permission: 'avatars.review' }, + { href: '/isac/audit', label: 'Audit history', code: 'AUDT', permission: 'audit.view' }, + { href: '/isac/metrics', label: 'System metrics', code: 'MTRX', permission: 'metrics.view' } ]; const visibleOversightSections = $derived(oversightSections.filter((section) => can(section.permission))); const selectNumericValue = (event: FocusEvent) => { @@ -66,8 +66,8 @@ } }}> {#if page.url.searchParams.has('proposal')}
CHANGE QUEUEDYour suggestion is awaiting data moderation and has not changed the live data set.
{/if} - {#if dataReadOnly}
READ-ONLY DATA ACCESSYou can inspect registries without submitting changes.
{#if can('data.propose')}ENABLE CONTRIBUTOR MODE →{/if}
- {:else if contributorEnabled && !guidelinesPage && can('data.propose') && !can('data.manage')}
CONTRIBUTOR MODESuggestions enter moderation and do not immediately change live data.
{/if} + {#if dataReadOnly}
READ-ONLY DATA ACCESSYou can inspect registries without submitting changes.
{#if can('data.propose')}ENABLE CONTRIBUTOR MODE →{/if}
+ {:else if contributorEnabled && !guidelinesPage && can('data.propose') && !can('data.manage')}
CONTRIBUTOR MODESuggestions enter moderation and do not immediately change live data.
{/if} {#if readOnlyBlocked}{/if} {@render children()} diff --git a/src/routes/admin/+page.server.ts b/src/routes/isac/+page.server.ts similarity index 64% rename from src/routes/admin/+page.server.ts rename to src/routes/isac/+page.server.ts index 69856d3..3a47789 100644 --- a/src/routes/admin/+page.server.ts +++ b/src/routes/isac/+page.server.ts @@ -5,10 +5,10 @@ import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ cookies }) => { const permissions = await getPermissions(await getSessionUser(cookies)); - if (permissions.includes('*') || permissions.includes('users.view')) redirect(303, '/admin/users'); - if (permissions.includes('users.roles')) redirect(303, '/admin/roles'); - if (permissions.includes('*') || ['data.view','data.manage','data.propose','data.approve'].some((permission) => permissions.includes(permission))) redirect(303, '/admin/attributes'); - if (permissions.includes('audit.view')) redirect(303, '/admin/audit'); - if (permissions.includes('metrics.view')) redirect(303, '/admin/metrics'); + if (permissions.includes('*') || permissions.includes('users.view')) redirect(303, '/isac/users'); + if (permissions.includes('users.roles')) redirect(303, '/isac/roles'); + if (permissions.includes('*') || ['data.view','data.manage','data.propose','data.approve'].some((permission) => permissions.includes(permission))) redirect(303, '/isac/attributes'); + if (permissions.includes('audit.view')) redirect(303, '/isac/audit'); + if (permissions.includes('metrics.view')) redirect(303, '/isac/metrics'); redirect(303, '/account'); }; diff --git a/src/routes/admin/attributes/+page.server.ts b/src/routes/isac/attributes/+page.server.ts similarity index 100% rename from src/routes/admin/attributes/+page.server.ts rename to src/routes/isac/attributes/+page.server.ts diff --git a/src/routes/admin/attributes/+page.svelte b/src/routes/isac/attributes/+page.svelte similarity index 100% rename from src/routes/admin/attributes/+page.svelte rename to src/routes/isac/attributes/+page.svelte diff --git a/src/routes/admin/audit/+page.server.ts b/src/routes/isac/audit/+page.server.ts similarity index 100% rename from src/routes/admin/audit/+page.server.ts rename to src/routes/isac/audit/+page.server.ts diff --git a/src/routes/admin/audit/+page.svelte b/src/routes/isac/audit/+page.svelte similarity index 100% rename from src/routes/admin/audit/+page.svelte rename to src/routes/isac/audit/+page.svelte diff --git a/src/routes/admin/avatar-oversight/+page.server.ts b/src/routes/isac/avatar-oversight/+page.server.ts similarity index 100% rename from src/routes/admin/avatar-oversight/+page.server.ts rename to src/routes/isac/avatar-oversight/+page.server.ts diff --git a/src/routes/admin/avatar-oversight/+page.svelte b/src/routes/isac/avatar-oversight/+page.svelte similarity index 100% rename from src/routes/admin/avatar-oversight/+page.svelte rename to src/routes/isac/avatar-oversight/+page.svelte diff --git a/src/routes/admin/brands/+page.server.ts b/src/routes/isac/brands/+page.server.ts similarity index 100% rename from src/routes/admin/brands/+page.server.ts rename to src/routes/isac/brands/+page.server.ts diff --git a/src/routes/admin/brands/+page.svelte b/src/routes/isac/brands/+page.svelte similarity index 100% rename from src/routes/admin/brands/+page.svelte rename to src/routes/isac/brands/+page.svelte diff --git a/src/routes/admin/contributor-guidelines/+page.server.ts b/src/routes/isac/contributor-guidelines/+page.server.ts similarity index 94% rename from src/routes/admin/contributor-guidelines/+page.server.ts rename to src/routes/isac/contributor-guidelines/+page.server.ts index 9b946e7..93f63da 100644 --- a/src/routes/admin/contributor-guidelines/+page.server.ts +++ b/src/routes/isac/contributor-guidelines/+page.server.ts @@ -8,8 +8,8 @@ import { recordIsacAudit } from '$lib/server/audit'; import type { Actions, PageServerLoad } from './$types'; const safeReturn = (value: FormDataEntryValue | null) => { - const path = String(value ?? '/admin/attributes'); - return path.startsWith('/admin/') && !path.startsWith('//') ? path : '/admin/attributes'; + const path = String(value ?? '/isac/attributes'); + return path.startsWith('/isac/') && !path.startsWith('//') ? path : '/isac/attributes'; }; export const load: PageServerLoad = async ({ cookies, url }) => { @@ -17,7 +17,7 @@ export const load: PageServerLoad = async ({ cookies, url }) => { if (!user) redirect(303, '/sign-in'); const permissions = await getPermissions(user); const eligible = permissions.includes('*') || permissions.includes('data.propose'); - if (!eligible) redirect(303, '/admin/attributes'); + if (!eligible) redirect(303, '/isac/attributes'); return { contributorMode: await getContributorMode(user.id), returnTo: safeReturn(url.searchParams.get('returnTo')) diff --git a/src/routes/admin/contributor-guidelines/+page.svelte b/src/routes/isac/contributor-guidelines/+page.svelte similarity index 100% rename from src/routes/admin/contributor-guidelines/+page.svelte rename to src/routes/isac/contributor-guidelines/+page.svelte diff --git a/src/routes/admin/data-moderation/+page.server.ts b/src/routes/isac/data-moderation/+page.server.ts similarity index 96% rename from src/routes/admin/data-moderation/+page.server.ts rename to src/routes/isac/data-moderation/+page.server.ts index 45b3e14..06fa1cc 100644 --- a/src/routes/admin/data-moderation/+page.server.ts +++ b/src/routes/isac/data-moderation/+page.server.ts @@ -102,7 +102,7 @@ export const actions: Actions = { const names = unresolved.map((item) => `${item.summary} (${item.status})`).join(', '); return fail(409, { moderationError: `Approve this proposal's dependencies first: ${names}.` }); } - const target = new URL(`/admin/${proposal.module}?/${proposal.action}`, url.origin); + const target = new URL(`/isac/${proposal.module}?/${proposal.action}`, url.origin); const proposalForm = deserializeProposal(String(proposal.payload_json)); if (proposal.draft_entity_id && !proposal.entity_id) proposalForm.set('_proposedEntityId', String(proposal.draft_entity_id)); const response = await fetch(target, { method: 'POST', body: proposalForm, headers: { origin: url.origin } }); @@ -111,12 +111,12 @@ export const actions: Actions = { const details = (await response.text()).slice(0, 1000); await (await database.prepare("UPDATE data_change_proposals SET status='failed',reviewed_by=?,review_note=?,reviewed_at=? WHERE id=?")).run(reviewer.id, details || note || 'Canonical validation failed.', now, id); await recordIsacAudit({ actorId: reviewer.id, category: 'data_moderation', action: 'proposal.failed', entityId: id, before: { status: 'pending' }, after: { status: 'failed', responseStatus: response.status } }); - await createNotification({ userId: String(proposal.proposed_by), category: 'contributions', source: 'ISAC DATA OVERSIGHT', title: 'Contribution could not be deployed', body: `${proposal.summary} did not pass current validation. Oversight evidence is available in your notification inbox.`, link: `/admin/${proposal.module}`, mandatory: true }); + await createNotification({ userId: String(proposal.proposed_by), category: 'contributions', source: 'ISAC DATA OVERSIGHT', title: 'Contribution could not be deployed', body: `${proposal.summary} did not pass current validation. Oversight evidence is available in your notification inbox.`, link: `/isac/${proposal.module}`, mandatory: true }); return fail(409, { moderationError: 'The proposal did not pass the current canonical validation and was marked failed.' }); } await (await database.prepare("UPDATE data_change_proposals SET status='approved',applied_entity_id=COALESCE(entity_id,draft_entity_id),reviewed_by=?,review_note=?,reviewed_at=?,applied_at=? WHERE id=?")).run(reviewer.id, note || null, now, now, id); await recordIsacAudit({ actorId: reviewer.id, category: 'data_moderation', action: 'proposal.approved', entityId: id, before: { status: 'pending' }, after: { status: 'approved', module: proposal.module, action: proposal.action, proposedBy: proposal.proposed_by, appliedEntityId: proposal.entity_id ?? proposal.draft_entity_id } }); - await createNotification({ userId: String(proposal.proposed_by), category: 'contributions', source: 'ISAC DATA OVERSIGHT', title: 'Contribution approved', body: `${proposal.summary} was approved and deployed to the live builder data set.`, link: `/admin/${proposal.module}`, mandatory: true }); + await createNotification({ userId: String(proposal.proposed_by), category: 'contributions', source: 'ISAC DATA OVERSIGHT', title: 'Contribution approved', body: `${proposal.summary} was approved and deployed to the live builder data set.`, link: `/isac/${proposal.module}`, mandatory: true }); return { moderationSaved: true, moderationToken: randomUUID() }; }, reject: async ({ cookies, request }) => { @@ -131,7 +131,7 @@ export const actions: Actions = { const now = new Date().toISOString(); await (await database.prepare("UPDATE data_change_proposals SET status='rejected',reviewed_by=?,review_note=?,reviewed_at=? WHERE id=?")).run(reviewer.id, note, now, id); await recordIsacAudit({ actorId: reviewer.id, category: 'data_moderation', action: 'proposal.rejected', entityId: id, before: { status: 'pending' }, after: { status: 'rejected', note, module: proposal.module, action: proposal.action, proposedBy: proposal.proposed_by } }); - await createNotification({ userId: String(proposal.proposed_by), category: 'contributions', source: 'ISAC DATA OVERSIGHT', title: 'Contribution needs revision', body: `${String(proposal.action).replace(/([A-Z])/g, ' $1').trim()} was not approved. Oversight reason: ${note}`, link: `/admin/${proposal.module}`, mandatory: true }); + await createNotification({ userId: String(proposal.proposed_by), category: 'contributions', source: 'ISAC DATA OVERSIGHT', title: 'Contribution needs revision', body: `${String(proposal.action).replace(/([A-Z])/g, ' $1').trim()} was not approved. Oversight reason: ${note}`, link: `/isac/${proposal.module}`, mandatory: true }); return { moderationSaved: true, moderationToken: randomUUID() }; } }; diff --git a/src/routes/admin/data-moderation/+page.svelte b/src/routes/isac/data-moderation/+page.svelte similarity index 100% rename from src/routes/admin/data-moderation/+page.svelte rename to src/routes/isac/data-moderation/+page.svelte diff --git a/src/routes/admin/gear/+page.server.ts b/src/routes/isac/gear/+page.server.ts similarity index 100% rename from src/routes/admin/gear/+page.server.ts rename to src/routes/isac/gear/+page.server.ts diff --git a/src/routes/admin/gear/+page.svelte b/src/routes/isac/gear/+page.svelte similarity index 100% rename from src/routes/admin/gear/+page.svelte rename to src/routes/isac/gear/+page.svelte diff --git a/src/routes/admin/metrics/+page.server.ts b/src/routes/isac/metrics/+page.server.ts similarity index 100% rename from src/routes/admin/metrics/+page.server.ts rename to src/routes/isac/metrics/+page.server.ts diff --git a/src/routes/admin/metrics/+page.svelte b/src/routes/isac/metrics/+page.svelte similarity index 100% rename from src/routes/admin/metrics/+page.svelte rename to src/routes/isac/metrics/+page.svelte diff --git a/src/routes/admin/protocols/+page.server.ts b/src/routes/isac/protocols/+page.server.ts similarity index 100% rename from src/routes/admin/protocols/+page.server.ts rename to src/routes/isac/protocols/+page.server.ts diff --git a/src/routes/admin/protocols/+page.svelte b/src/routes/isac/protocols/+page.svelte similarity index 100% rename from src/routes/admin/protocols/+page.svelte rename to src/routes/isac/protocols/+page.svelte diff --git a/src/routes/admin/roles/+page.server.ts b/src/routes/isac/roles/+page.server.ts similarity index 100% rename from src/routes/admin/roles/+page.server.ts rename to src/routes/isac/roles/+page.server.ts diff --git a/src/routes/admin/roles/+page.svelte b/src/routes/isac/roles/+page.svelte similarity index 100% rename from src/routes/admin/roles/+page.svelte rename to src/routes/isac/roles/+page.svelte diff --git a/src/routes/admin/skillchips/+page.server.ts b/src/routes/isac/skillchips/+page.server.ts similarity index 100% rename from src/routes/admin/skillchips/+page.server.ts rename to src/routes/isac/skillchips/+page.server.ts diff --git a/src/routes/admin/skillchips/+page.svelte b/src/routes/isac/skillchips/+page.svelte similarity index 100% rename from src/routes/admin/skillchips/+page.svelte rename to src/routes/isac/skillchips/+page.svelte diff --git a/src/routes/admin/talents/+page.server.ts b/src/routes/isac/talents/+page.server.ts similarity index 100% rename from src/routes/admin/talents/+page.server.ts rename to src/routes/isac/talents/+page.server.ts diff --git a/src/routes/admin/talents/+page.svelte b/src/routes/isac/talents/+page.svelte similarity index 98% rename from src/routes/admin/talents/+page.svelte rename to src/routes/isac/talents/+page.svelte index 4539423..687cff8 100644 --- a/src/routes/admin/talents/+page.svelte +++ b/src/routes/isac/talents/+page.svelte @@ -98,4 +98,4 @@ {/if} -
TALENTCATEGORYTIER I EFFECTTIER II EFFECTSTATE
{#each data.talents as item}
{#if item.iconUrl}{:else}◇{/if}
{item.name}{item.category === 'Exotic Weapon' ? `OWNED BY ${item.ownerWeaponName ?? 'UNASSIGNED WEAPON'}` : 'Tiered talent'}{#if cooldownLine(item.cooldown_seconds)}{cooldownLine(item.cooldown_seconds)}{/if}
{item.category}

{tierDescription(item.tiers, 'I')}

{tierDescription(item.tiers, 'II')}

ACTIVE{#if item.category === 'Exotic Weapon'}{#if item.ownerWeaponId}EDIT WEAPON{:else}{/if}{:else}{/if}
{/each}
+
TALENTCATEGORYTIER I EFFECTTIER II EFFECTSTATE
{#each data.talents as item}
{#if item.iconUrl}{:else}◇{/if}
{item.name}{item.category === 'Exotic Weapon' ? `OWNED BY ${item.ownerWeaponName ?? 'UNASSIGNED WEAPON'}` : 'Tiered talent'}{#if cooldownLine(item.cooldown_seconds)}{cooldownLine(item.cooldown_seconds)}{/if}
{item.category}

{tierDescription(item.tiers, 'I')}

{tierDescription(item.tiers, 'II')}

ACTIVE{#if item.category === 'Exotic Weapon'}{#if item.ownerWeaponId}EDIT WEAPON{:else}{/if}{:else}{/if}
{/each}
diff --git a/src/routes/admin/users/+page.server.ts b/src/routes/isac/users/+page.server.ts similarity index 100% rename from src/routes/admin/users/+page.server.ts rename to src/routes/isac/users/+page.server.ts diff --git a/src/routes/admin/users/+page.svelte b/src/routes/isac/users/+page.svelte similarity index 93% rename from src/routes/admin/users/+page.svelte rename to src/routes/isac/users/+page.svelte index 0d5b088..65d0410 100644 --- a/src/routes/admin/users/+page.svelte +++ b/src/routes/isac/users/+page.svelte @@ -10,6 +10,6 @@
USERACCESSROLESLAST LOGINLAST ACTIVECONTENT
- {#each data.users as user}
{user.display_name}LOGIN: {user.username ?? 'NOT CONFIGURED'}
{user.accessState}{user.inactive && user.accessState === 'ACTIVE' ? ' · INACTIVE' : ''}
{#each String(user.assigned_roles ?? user.role ?? 'User').split(' · ') as role}{role.toUpperCase()}{/each}
{user.last_login_provider?.toUpperCase() ?? 'NONE'}{date(user.last_login_at)}{date(user.last_active_at)}{user.build_count} BUILDS · {user.comment_count} COMMENTSVIEW
+ {#each data.users as user}
{user.display_name}LOGIN: {user.username ?? 'NOT CONFIGURED'}
{user.accessState}{user.inactive && user.accessState === 'ACTIVE' ? ' · INACTIVE' : ''}
{#each String(user.assigned_roles ?? user.role ?? 'User').split(' · ') as role}{role.toUpperCase()}{/each}
{user.last_login_provider?.toUpperCase() ?? 'NONE'}{date(user.last_login_at)}{date(user.last_active_at)}{user.build_count} BUILDS · {user.comment_count} COMMENTSVIEW
{:else}
NO USERS FOUNDAdjust the username search and try again.
{/each}
diff --git a/src/routes/admin/users/[id]/+page.server.ts b/src/routes/isac/users/[id]/+page.server.ts similarity index 100% rename from src/routes/admin/users/[id]/+page.server.ts rename to src/routes/isac/users/[id]/+page.server.ts diff --git a/src/routes/admin/users/[id]/+page.svelte b/src/routes/isac/users/[id]/+page.svelte similarity index 99% rename from src/routes/admin/users/[id]/+page.svelte rename to src/routes/isac/users/[id]/+page.svelte index 86c8dc1..1e363c7 100644 --- a/src/routes/admin/users/[id]/+page.svelte +++ b/src/routes/isac/users/[id]/+page.svelte @@ -29,7 +29,7 @@ const accountState = () => data.profile.deleted_at ? 'DELETED' : data.profile.blocked_at ? 'BLOCKED' : data.profile.banned_until && new Date(data.profile.banned_until) > new Date() ? 'BANNED' : 'ACTIVE'; {data.profile.display_name} — ISAC user operations -

AGENT DOSSIER

{data.profile.display_name}

Private login: {data.profile.username ?? 'Not configured'} · Created {date(data.profile.created_at)} · {data.authenticationMethods.total} sign-in method{data.authenticationMethods.total === 1 ? '' : 's'}

← USER REGISTRY
+

AGENT DOSSIER

{data.profile.display_name}

Private login: {data.profile.username ?? 'Not configured'} · Created {date(data.profile.created_at)} · {data.authenticationMethods.total} sign-in method{data.authenticationMethods.total === 1 ? '' : 's'}

← USER REGISTRY
ACCESS STATE{accountState()}{data.profile.moderation_reason ?? 'No restrictions'}
LAST ACTIVE{date(data.profile.last_active_at)}Site activity
BUILDS{data.historyPagination.build.total}Authored records
COMMENTS{data.historyPagination.comment.total}Posted records
diff --git a/src/routes/admin/weapons/+page.server.ts b/src/routes/isac/weapons/+page.server.ts similarity index 100% rename from src/routes/admin/weapons/+page.server.ts rename to src/routes/isac/weapons/+page.server.ts diff --git a/src/routes/admin/weapons/+page.svelte b/src/routes/isac/weapons/+page.svelte similarity index 100% rename from src/routes/admin/weapons/+page.svelte rename to src/routes/isac/weapons/+page.svelte