Files
pardis/src/routes/Resume.svelte
2026-09-19 19:58:13 +02:00

172 lines
4.8 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script>
import { dayState, reopenToday } from '../lib/day.svelte.js';
import { buildResume, resumeToText } from '../lib/resume.js';
import { go } from '../lib/router.svelte.js';
import { settings } from '../lib/settings.svelte.js';
import { fmtDayLabel, fmtDuration, weekday } from '../lib/time.js';
let copied = $state(false);
let selectedDate = $state('');
let reopening = $state(false);
let error = $state('');
const selectedDay = $derived(dayState.closedDays.find((d) => d.date === selectedDate) ?? dayState.lastClosed);
const resume = $derived(
buildResume(
selectedDay,
dayState.historyEntries.filter((e) => e.date === selectedDay?.date),
settings.roundingMinutes,
),
);
const text = $derived(resumeToText(resume));
/** Shown when today is a work day whose start time has not arrived yet. */
const startsAt = $derived(
settings.workdays.includes(weekday(dayState.today)) ? settings.dayStart : null,
);
const closedToday = $derived(selectedDay?.date === dayState.today && !dayState.day);
async function reopen() {
if (reopening) return;
reopening = true;
error = '';
try { await reopenToday(); go('today'); }
catch (cause) { error = `Reprise impossible : ${cause.message}`; }
finally { reopening = false; }
}
async function copy() {
try {
await navigator.clipboard.writeText(text);
} catch {
// Clipboard API needs a secure context; fall back to a selectable dialog.
window.prompt('Copier le récapitulatif :', text);
}
copied = true;
setTimeout(() => (copied = false), 2500);
}
</script>
<div class="screen">
{#if error}<p role="alert">{error}</p>{/if}
{#if dayState.closedDays.length > 1}
<label class="stack">
<span>Journée à consulter</span>
<select value={selectedDay?.date} onchange={(event) => { selectedDate = event.currentTarget.value; copied = false; }}>
{#each dayState.closedDays as day (day.date)}
<option value={day.date}>{fmtDayLabel(day.date)} {day.date.slice(0, 4)}</option>
{/each}
</select>
</label>
{/if}
{#if !resume}
<div class="card center stack">
<h2>Aucune journée enregistrée</h2>
<p class="dim">
Le récapitulatif de la dernière journée s'affichera ici, prêt à recopier.
</p>
</div>
{:else}
<div class="row-between">
<h1>{resume.label}</h1>
</div>
<div class="card stack">
{#each resume.clients as client (client.name)}
<div class="client">
<div class="row-between head">
<h2>{client.name}</h2>
<span class="tnum">{fmtDuration(client.totalMs)}</span>
</div>
<ul>
{#each client.tasks as task (task.name)}
<li class="row-between">
<span>{task.name}</span>
<span class="tnum">{fmtDuration(task.ms)}</span>
</li>
{/each}
</ul>
</div>
{/each}
{#if resume.lunch}
<hr />
<div class="row-between">
<span
>Déjeuner : {resume.lunch.type === 'restaurant'
? `restaurant (${resume.lunch.restaurant || 'non précisé'})`
: 'panier'}</span
>
<span class="tnum">{fmtDuration(resume.lunch.ms)}</span>
</div>
{/if}
<hr />
<div class="row-between total">
<strong>Total travaillé</strong>
<strong class="tnum">{fmtDuration(resume.workedMs)}</strong>
</div>
{#if resume.omitted}
<p class="dim small">
{resume.omitted} poste{resume.omitted > 1 ? 's' : ''} de moins de {settings.roundingMinutes}
min non affiché{resume.omitted > 1 ? 's' : ''}.
</p>
{/if}
</div>
<button type="button" class="btn-primary" onclick={copy}>
{copied ? '✓ Copié' : 'Copier le récapitulatif'}
</button>
{/if}
{#if closedToday}
<button
type="button"
disabled={reopening}
onclick={reopen}>Reprendre la journée</button
>
<p class="dim small">Annule la clôture et continue la dernière tâche depuis l’arrêt.</p>
{:else if !dayState.day && !dayState.todayRecord && startsAt}
<p class="dim center small">
La journée démarrera automatiquement à {startsAt}.
</p>
{:else if !dayState.day && !dayState.todayRecord}
<p class="dim center small">Aujourd'hui n'est pas un jour travaillé.</p>
{/if}
</div>
<style>
.client + .client {
border-top: 2px solid var(--line);
padding-top: var(--gap);
}
.head {
margin-bottom: 6px;
}
ul {
list-style: none;
margin: 0;
padding: 0 0 0 var(--pad);
display: flex;
flex-direction: column;
gap: 4px;
}
li {
font-size: 0.95rem;
}
.total {
font-size: 1.15rem;
}
.small {
font-size: 0.85rem;
}
</style>