Files
eden/src/routes/characters/+page.svelte
T

72 lines
4.8 KiB
Svelte

<script lang="ts">
import LiveCharacters from '$lib/components/LiveCharacters.svelte';
let { data } = $props();
const datasetLabel = (dataset: string) => dataset.replaceAll('_', ' ');
const assetsScope = 'esi-assets.read_assets.v1';
const structuresScope = 'esi-universe.read_structures.v1';
const locationState = (character: any) => character.authorizationStatus !== 'active' || !character.scopes.includes(assetsScope)
? 'reduced' : character.scopes.includes(structuresScope) ? 'granted' : 'missing';
</script>
<p class="eyebrow">APPLICATION-USER AGGREGATION</p><h1>Characters</h1>
<p class="muted">Connect each character explicitly. One primary character is the only signed-out login; linked characters are data-only until you switch the primary while authenticated.</p>
{#if data.mode !== 'demo' && data.identityEnabled}
<form method="POST" action="/auth/eve">
<input type="hidden" name="capability" value="asset_search" />
<button>Connect EVE Character</button>
</form>
{/if}
{#if data.error}<p class="error" role="alert">{data.error}</p>{/if}
{#if data.userId && data.convexUrl && data.mode !== 'demo'}<LiveCharacters />{/if}
{#each data.characters as character (character.id)}
<section class="panel">
<div class="row spread"><h2>{character.name}</h2><span class="muted">{character.loginRole === 'data_only' ? 'DATA-ONLY' : 'PRIMARY'} · {character.authorizationStatus ?? 'active'}</span></div>
<small>Character {character.id} · grant r{character.grantRevision ?? 1} · credential v{character.credentialVersion ?? 1} · epoch {character.authorizationEpoch ?? 1}</small>
<p class="muted">Assets: {character.assets.status} · synchronized: {character.assets.syncedAt ? new Date(character.assets.syncedAt).toISOString() : 'Not yet'}<br />Granted scopes: {character.scopes.join(', ') || 'None'}</p>
{#if character.datasets}
<p class="muted">Datasets: {character.datasets.map((dataset) => `${datasetLabel(dataset.dataset)}=${dataset.status}`).join(' · ')}</p>
{/if}
{#if character.assets.error}<p class="warning">{character.assets.error}</p>{/if}
<p class="muted">Inventory locations: <strong>{data.locationEnabled ? locationState(character) : 'unavailable'}</strong>{#if data.locationEnabled && locationState(character) === 'missing'} · Structure consent is required for private location evidence.{/if}</p>
{#if data.mode !== 'demo' && data.identityEnabled}
<div class="row">
{#if character.loginRole === 'data_only' && character.authorizationStatus === 'active'}
<form method="POST" action="/characters/primary"><input type="hidden" name="characterId" value={character.id} /><button class="quiet">Set as primary</button></form>
{/if}
<form method="POST" action="/auth/eve">
<input type="hidden" name="capability" value="asset_search" />
<input type="hidden" name="intent" value="reconnect" />
<input type="hidden" name="targetCharacterId" value={character.id} />
<button class="quiet">{character.authorizationStatus === 'active' ? 'Refresh authorization' : 'Reconnect'}</button>
</form>
{#if character.authorizationStatus === 'active' && data.locationEnabled && character.scopes.includes(assetsScope) && !character.scopes.includes(structuresScope)}
<form method="POST" action="/auth/eve">
<input type="hidden" name="capability" value="inventory_locations" />
<input type="hidden" name="intent" value="upgrade_dataset" />
<input type="hidden" name="targetCharacterId" value={character.id} />
<button class="quiet">Authorize private locations</button>
</form>
{/if}
{#if character.authorizationStatus === 'active' && data.walletEnabled}
<form method="POST" action="/auth/eve">
<input type="hidden" name="capability" value="wallet_balance" />
<input type="hidden" name="intent" value="upgrade_dataset" />
<input type="hidden" name="targetCharacterId" value={character.id} />
<button class="quiet">Authorize wallet</button>
</form>
{/if}
{#if character.loginRole === 'data_only' && character.authorizationStatus !== 'revoked'}
<details>
<summary>Disconnect character</summary>
<p class="muted">This immediately blocks local datasets, discards the stored credential, and cancels pending local work. Reconnecting requires EVE authorization again.</p>
<form method="POST" action="/characters/invalidate">
<input type="hidden" name="characterId" value={character.id} />
<button class="quiet">Confirm disconnect</button>
</form>
</details>
{/if}
</div>
{/if}
</section>
{:else}<section class="panel"><p>No connected characters. Your first verified EVE login creates the primary application character.</p></section>{/each}
<p><a href="/assets">Open asset report →</a></p>