Files
r701/oxibadge/src/lib/components/Onboarding.svelte
T
nicolabelluti 25127bee96
Check, format and test / build (push) Successful in 10m27s
feat(oxibadge): cache employee names locally and let readers be a list
Employee names are now cached locally (employees.toml, keyed by reader
index then employee id) instead of being re-fetched from the time
clock on every month: load_employees only asks the device for ids not
already in the cache. When a month has employees the cache doesn't
know yet, a new screen (NewEmployees.svelte) lets the user name them
before continuing to the review screen; the chosen names are local to
Oxibadge only, never written back to the device. The same cache can be
managed afterwards from a drawer reachable from the time clock card in
Settings ("Gestisci dipendenti"), reusing the rename-in-row pattern
from the original mockup.

Settings.readers is a list again (was flattened to a single `reader`
object) so multiple time clocks can be supported later; only the first
one is used today.

Also:
- buildEmployees() now derives the employee list from the month's
  records instead of from every cached name ever seen, so the review
  screen no longer shows employees unrelated to the selected month.
- Renaming an employee from the review screen's pencil button now
  actually persists to the cache (the callback existed but was never
  wired up).
- Translated to English a number of Rust/TS/Svelte comments that had
  been written in Italian; only the i18n locale files should contain
  Italian.
- Minor dedup: extracted PillButton.svelte (was duplicated inline in
  ReaderCard), promoted the repeated .mono CSS rule to app.css, moved
  initials() out of the review feature into $lib/text.ts.

Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-08 20:04:37 +02:00

102 lines
2.7 KiB
Svelte

<script lang="ts">
import { invoke } from '@tauri-apps/api/core';
import { _ } from 'svelte-i18n';
import { error, setError, clearError } from '$lib/error';
import type { Settings } from '$lib/settings';
import Column from './Column.svelte';
import Intro from './Intro.svelte';
import Field from './Field.svelte';
import Button from './Button.svelte';
import ErrorBanner from './ErrorBanner.svelte';
import Hint from './Hint.svelte';
let { onconnected }: { onconnected: (cfg: { host: string; port: number }) => void } = $props();
let host = $state('');
let port = $state(5005);
let testing = $state(false);
const trimmedHost = $derived(host.trim());
const empty = $derived(trimmedHost === '');
const disabled = $derived(empty || testing);
const btnLabel = $derived(
testing ? $_('onboarding.button.testing') : $error ? $_('onboarding.button.retry') : $_('onboarding.button.idle')
);
async function testAndSave() {
if (disabled) return;
testing = true;
clearError();
try {
await invoke('test_connection', { host: trimmedHost, port });
const settings = await invoke<Settings>('load_settings');
await invoke('save_settings', { settings: { ...settings, readers: [{ host: trimmedHost, port }] } });
onconnected({ host: trimmedHost, port });
} catch (err) {
setError(err);
} finally {
testing = false;
}
}
</script>
<Column>
<Intro title={$_('onboarding.title')} subtitle={$_('onboarding.subtitle')} />
<form
class="form"
onsubmit={(event) => {
event.preventDefault();
testAndSave();
}}
>
<div class="row">
<Field
id="ox-host"
label={$_('onboarding.host_label')}
bind:value={host}
placeholder="192.168.1.100"
oninput={clearError}
>
{#snippet icon()}
<svg width="18" height="18" viewBox="0 0 24 24" aria-hidden="true">
<rect x="3" y="4" width="18" height="12" rx="2" /><path d="M8 20h8M12 16v4" />
</svg>
{/snippet}
</Field>
<Field
id="ox-port"
label={$_('onboarding.port_label')}
bind:value={port}
type="number"
placeholder="5005"
oninput={clearError}
/>
</div>
<Button type="submit" label={btnLabel} faded={empty} busy={testing} {disabled} />
{#if $error}
<ErrorBanner title={$_('errors.title')} message={$error} />
{/if}
<Hint text={$_('onboarding.hint')} />
</form>
</Column>
<style>
.form {
display: flex;
flex-direction: column;
gap: 20px;
}
.row {
display: grid;
grid-template-columns: minmax(0, 1fr) 108px;
gap: 12px;
align-items: start;
}
</style>