Move personal display preferences into My account
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { timeFormat, timeZone, timeZonePersistenceError } from '$ui/stores/time-zone.js';
|
||||
import { availableTimeZones, DEFAULT_TIME_ZONE } from '$lib/time-zone.js';
|
||||
let exampleDate = $state<Date | null>(null);
|
||||
const zones = $derived([...new Set([...availableTimeZones(), $timeZone])].sort());
|
||||
onMount(() => { exampleDate = new Date(); });
|
||||
import { page } from '$app/stores';
|
||||
import { invalidateAll } from '$app/navigation';
|
||||
import Button from '$ui/components/primitives/Button.svelte';
|
||||
@@ -30,7 +36,7 @@
|
||||
</script>
|
||||
|
||||
<svelte:head><title>My account · Electric Cherry</title></svelte:head>
|
||||
<section class="page"><SectionHeader title="My account" subtitle="Manage your password and current session." />
|
||||
<section class="page"><SectionHeader title="My account" subtitle="Manage your password, display preferences, and current session." />
|
||||
{#if user}
|
||||
<section class="panel identity"><div><h2>{user.displayName}</h2><p>@{user.username} · <span class="role">{user.role}</span></p></div><Button disabled={busy || signingOut} loading={signingOut} onclick={logout}>Sign out</Button></section>
|
||||
{#if user.mustChangePassword}<p class="required" role="status">Your account uses a temporary password. Choose a new password before continuing to Electric Cherry.</p>{/if}
|
||||
@@ -45,10 +51,24 @@
|
||||
<div><Button type="submit" variant="primary" loading={busy} disabled={busy || signingOut}>{user.mustChangePassword ? 'Save password and continue' : 'Change password'}</Button></div>
|
||||
</fieldset></form>
|
||||
</section>
|
||||
<section class="panel" aria-labelledby="display-heading">
|
||||
<h2 id="display-heading">Display preferences</h2>
|
||||
<label class="timezone">Timezone
|
||||
<select bind:value={$timeZone} aria-describedby="timezone-help">
|
||||
{#each zones as zone}<option value={zone}>{zone.replaceAll('_', ' ')}{zone === DEFAULT_TIME_ZONE ? ' (default)' : ''}</option>{/each}
|
||||
</select>
|
||||
</label>
|
||||
<p id="timezone-help">Dates and times throughout the app use this timezone, including daylight saving changes. Saved automatically in this browser.</p>
|
||||
{#if exampleDate}<p>Example: {$timeFormat.dateTime(exampleDate)}</p>{/if}
|
||||
{#if $timeZonePersistenceError}<p role="status">Browser storage is unavailable. Your timezone applies for this visit but cannot be saved.</p>{/if}
|
||||
</section>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.timezone { display: grid; gap: 8px; margin-top: 16px; font-size: var(--text-sm); max-width: 420px; }
|
||||
.timezone select { width: 100%; padding: 10px 12px; font: inherit; color: var(--text-primary); background: var(--surface-sunken); border: 1px solid var(--border-control); border-radius: var(--radius-sm); }
|
||||
.timezone ~ p { font-size:var(--text-sm); color:var(--text-secondary); }
|
||||
.page { max-width:760px; display:grid; gap:24px; } .panel { padding:24px; background:var(--surface); border:1px solid var(--border-strong); border-radius:var(--radius-md); } h2 { font-size:var(--text-md); margin:0; } .identity { display:flex; align-items:center; justify-content:space-between; gap:20px; } .identity p { margin:8px 0 0; color:var(--text-secondary); font-size:var(--text-sm); } .role { text-transform:capitalize; }
|
||||
.required { padding:16px; margin:0; color:var(--text-primary); background:var(--surface-raised); border-left:3px solid var(--accent-primary); font-size:var(--text-sm); line-height:1.6; }
|
||||
form { margin-top:24px; max-width:460px; } fieldset { border:0; margin:0; padding:0; display:grid; gap:18px; min-width:0; } label { display:grid; gap:8px; font-size:var(--text-sm); } input { width:100%; padding:12px; color:var(--text-primary); background:var(--surface-sunken); border:1px solid var(--border-control); border-radius:var(--radius-md); font:inherit; } .password-username { position:absolute; width:1px; height:1px; clip-path:inset(50%); overflow:hidden; padding:0; border:0; } .hint { margin:-8px 0 0; color:var(--text-secondary); font-size:var(--text-xs); line-height:1.6; } .error { color:var(--state-danger); font-size:var(--text-sm); } .notice { color:var(--state-online); font-size:var(--text-sm); }
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { timeFormat, timeZone, timeZonePersistenceError } from '$ui/stores/time-zone.js';
|
||||
import { availableTimeZones, DEFAULT_TIME_ZONE } from '$lib/time-zone.js';
|
||||
import { onMount } from 'svelte';
|
||||
import SectionHeader from '$ui/components/primitives/SectionHeader.svelte';
|
||||
import Button from '$ui/components/primitives/Button.svelte';
|
||||
import type { FleetSettings } from '$lib/fleet-settings.js';
|
||||
let exampleDate = $state<Date | null>(null);
|
||||
let settings = $state<FleetSettings | null>(null);
|
||||
let loading = $state(true);
|
||||
let saving = $state(false);
|
||||
let error = $state('');
|
||||
let notice = $state('');
|
||||
const zones = $derived([...new Set([...availableTimeZones(), $timeZone])].sort());
|
||||
onMount(() => { exampleDate = new Date(); void loadSettings(); });
|
||||
onMount(() => { void loadSettings(); });
|
||||
|
||||
async function loadSettings() {
|
||||
loading = true; error = '';
|
||||
@@ -40,7 +36,7 @@
|
||||
|
||||
<svelte:head><title>Settings · Electric Cherry</title></svelte:head>
|
||||
<section class="page">
|
||||
<SectionHeader title="Settings" subtitle="Manage fleet limits, history retention, and display preferences." />
|
||||
<SectionHeader title="Settings" subtitle="Manage shared fleet limits and history retention." />
|
||||
<section class="guide" aria-labelledby="fleet-heading">
|
||||
<h2 id="fleet-heading">Fleet settings</h2>
|
||||
<p>Saved for everyone using this Electric Cherry instance. Changes apply without restarting the app.</p>
|
||||
@@ -74,17 +70,7 @@
|
||||
<Button onclick={loadSettings}>Retry loading</Button>
|
||||
{/if}
|
||||
</section>
|
||||
<section class="guide" aria-labelledby="display-heading">
|
||||
<h2 id="display-heading">Display preferences</h2>
|
||||
<label class="timezone">Timezone
|
||||
<select bind:value={$timeZone} aria-describedby="timezone-help">
|
||||
{#each zones as zone}<option value={zone}>{zone.replaceAll('_', ' ')}{zone === DEFAULT_TIME_ZONE ? ' (default)' : ''}</option>{/each}
|
||||
</select>
|
||||
</label>
|
||||
<p id="timezone-help">Dates and times throughout the app use this timezone, including daylight saving changes. Saved automatically in this browser.</p>
|
||||
{#if exampleDate}<p>Example: {$timeFormat.dateTime(exampleDate)}</p>{/if}
|
||||
{#if $timeZonePersistenceError}<p role="status">Browser storage is unavailable. Your timezone applies for this visit but cannot be saved.</p>{/if}
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
<style>
|
||||
@@ -92,8 +78,6 @@
|
||||
.guide { padding: 20px; border: 1px solid var(--border-strong); border-radius: var(--radius-md); background: var(--surface); }
|
||||
h2 { font-size: var(--text-md); margin: 0; }
|
||||
p { font-size: var(--text-sm); color: var(--text-secondary); line-height: 1.65; }
|
||||
.timezone { display: grid; gap: 8px; margin-top: 16px; font-size: var(--text-sm); max-width: 420px; }
|
||||
.timezone select { width: 100%; padding: 10px 12px; font: inherit; color: var(--text-primary); background: var(--surface-sunken); border: 1px solid var(--border-control); border-radius: var(--radius-sm); }
|
||||
fieldset { border: 0; padding: 0; margin: 20px 0 0; min-width: 0; }
|
||||
fieldset label { display: block; margin: 20px 0 8px; font-size: var(--text-sm); }
|
||||
fieldset input { width: 160px; padding: 10px 12px; font: inherit; color: var(--text-primary); background: var(--surface-sunken); border: 1px solid var(--border-control); border-radius: var(--radius-sm); }
|
||||
|
||||
Reference in New Issue
Block a user