feat(oxibadge): add month-confirmation flow and fetch records/employees
Check, format and test / build (push) Successful in 10m0s

Adds the screen after onboarding: pick a month/year (ConfirmMonth), show
loading screens while the device is queried, then download that month's
records and resolve each distinct employee's name, printing both to
stdout (debug pretty-print) before landing on the placeholder "done"
screen.

- r701: RecordIterator's boxed stream is now Send, required for it to
  cross into a Tauri command's future.
- oxibadge: load_records/load_employees commands, sharing the existing
  3-retry connect_with_retries helper. load_records uses skip_while/
  take_while on the device's newest-first stream to grab only the
  target month without walking its whole history, then a single
  collect + in-place Vec::reverse() back to chronological order.
- Shared LoadingScreen/Intro components extracted so future screens
  reuse the same look without duplicating markup/CSS.

Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 12:47:36 +02:00
parent 99772b9e4a
commit 28186c7745
14 changed files with 621 additions and 41 deletions
+35 -4
View File
@@ -1,25 +1,56 @@
<script lang="ts">
import { invoke } from '@tauri-apps/api/core';
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { _, locale } from 'svelte-i18n';
import { monthName } from '$lib/month';
import Screen from '$lib/components/Screen.svelte';
import Onboarding from '$lib/components/Onboarding.svelte';
import ConfirmMonth from '$lib/components/ConfirmMonth.svelte';
import LoadingScreen from '$lib/components/LoadingScreen.svelte';
type Step = 'loading' | 'setup' | 'done';
type Step = 'loading' | 'setup' | 'confirm-month' | 'loading-records' | 'loading-employees' | 'done';
let step = $state<Step>('loading');
let selectedYear = $state(0);
let selectedMonth = $state(0);
const period = $derived(`${monthName($locale, selectedMonth)} ${selectedYear}`);
onMount(async () => {
const preferences = await invoke<{ host: string; port: number } | null>('load_preferences');
step = preferences ? 'done' : 'setup';
step = preferences ? 'confirm-month' : 'setup';
});
async function loadMonth(selection: { year: number; month: number }) {
selectedYear = selection.year;
selectedMonth = selection.month;
step = 'loading-records';
try {
const records = await invoke('load_records', selection);
step = 'loading-employees';
await invoke('load_employees', { records });
step = 'done';
} catch (err) {
console.error(err);
step = 'confirm-month';
}
}
</script>
<Screen>
{#if step === 'loading'}
<p>{$_('loading')}</p>
{:else if step === 'setup'}
<Onboarding onconnected={() => (step = 'done')} />
<Onboarding onconnected={() => (step = 'confirm-month')} />
{:else if step === 'confirm-month'}
<ConfirmMonth onstart={loadMonth} />
{:else if step === 'loading-records'}
<LoadingScreen
title={$_('loading_records.title')}
description={$_('loading_records.description', { values: { period } })}
/>
{:else if step === 'loading-employees'}
<LoadingScreen title={$_('loading_employees.title')} description={$_('loading_employees.description')} />
{:else}
<p>{$_('done.message')}</p>
{/if}