72 lines
4.8 KiB
Svelte
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>
|