This commit is contained in:
2026-05-31 20:19:44 +12:00
parent 2f2466ecac
commit 84792c0947
59 changed files with 5412 additions and 898 deletions
+37 -15
View File
@@ -1,9 +1,13 @@
<script lang="ts">
import { clientSession, hasModuleAccess } from '$lib/session';
import type { MixCalculatorSession } from '$lib/types';
import { featureFlags } from '$lib/features';
import MixCalculatorEditor from '$lib/components/mix-calculator/MixCalculatorEditor.svelte';
import type { MixCalculatorOptions, MixCalculatorSession } from '$lib/types';
let { data }: { data: { sessions: MixCalculatorSession[] } } = $props();
let { data }: { data: { sessions?: MixCalculatorSession[]; options?: MixCalculatorOptions } } =
$props();
const sessions = $derived(data.sessions ?? []);
const canEdit = $derived(hasModuleAccess($clientSession, 'mix_calculator', 'edit'));
function formatDate(value: string) {
@@ -18,6 +22,9 @@
}
</script>
{#if !featureFlags.mixCalculatorSessionHistory}
<MixCalculatorEditor options={data.options} />
{:else}
{#if canEdit}
<section class="page-actions">
<a class="primary-button" href="/mix-calculator/new">New mix session</a>
@@ -27,17 +34,17 @@
<section class="metric-row">
<article class="metric-card">
<span>Saved Sessions</span>
<strong>{data.sessions.length}</strong>
<strong>{sessions.length}</strong>
<p>Visible under your access scope</p>
</article>
<article class="metric-card">
<span>Total Planned Kg</span>
<strong>{formatNumber(data.sessions.reduce((sum, session) => sum + session.total_kg, 0), 2)}</strong>
<strong>{formatNumber(sessions.reduce((sum, session) => sum + session.total_kg, 0), 2)}</strong>
<p>Across the visible history</p>
</article>
<article class="metric-card">
<span>Sessions With Warnings</span>
<strong>{data.sessions.filter((session) => session.warnings.length).length}</strong>
<strong>{sessions.filter((session) => session.warnings.length).length}</strong>
<p>Fractional bag outputs need review</p>
</article>
</section>
@@ -50,7 +57,7 @@
</div>
</div>
{#if data.sessions.length}
{#if sessions.length}
<div class="table-wrap">
<table>
<thead>
@@ -65,7 +72,7 @@
</tr>
</thead>
<tbody>
{#each data.sessions as session}
{#each sessions as session}
<tr>
<td data-label="Session">
<strong>{session.session_number}</strong>
@@ -102,6 +109,7 @@
</div>
{/if}
</section>
{/if}
<style>
h2,
@@ -140,10 +148,24 @@
align-items: center;
justify-content: center;
padding: 0.78rem 0.96rem;
border-radius: 0.9rem;
border-radius: 0.6rem;
background: var(--color-brand);
color: #fff;
font-weight: 600;
transition: background-color 160ms ease;
}
.primary-button:hover {
background: #126a33;
}
.primary-button:active {
background: #0f5a2b;
}
.primary-button:focus-visible {
outline: 3px solid color-mix(in srgb, var(--color-brand) 45%, transparent);
outline-offset: 2px;
}
.metric-row {
@@ -155,7 +177,7 @@
.metric-card,
.table-card {
border: 1px solid var(--line);
border-radius: 1.3rem;
border-radius: 0.8rem;
background: var(--panel);
box-shadow: var(--shadow);
}
@@ -222,12 +244,12 @@
tbody td:first-child {
border-left: 1px solid var(--line);
border-radius: 1rem 0 0 1rem;
border-radius: 0.65rem 0 0 0.65rem;
}
tbody td:last-child {
border-right: 1px solid var(--line);
border-radius: 0 1rem 1rem 0;
border-radius: 0 0.65rem 0.65rem 0;
}
.row-actions {
@@ -245,8 +267,8 @@
margin-left: 0.55rem;
padding: 0.25rem 0.5rem;
border-radius: 999px;
background: #fff6e6;
color: #8b5b1e;
background: #fdf6e9;
color: #8a5a00;
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
@@ -257,7 +279,7 @@
display: grid;
gap: 0.2rem;
padding: 1rem;
border-radius: 1rem;
border-radius: 0.65rem;
background: var(--panel-soft);
}
@@ -299,7 +321,7 @@
tbody tr {
padding: 0.3rem;
border: 1px solid var(--line);
border-radius: 1rem;
border-radius: 0.65rem;
background: var(--panel-soft);
}
+28 -9
View File
@@ -2,17 +2,35 @@ import { redirect } from '@sveltejs/kit';
import { api } from '$lib/api';
import { featureFlags } from '$lib/features';
import { getStoredClientSession, hasModuleAccess, hasStoredClientSession } from '$lib/session';
import { canOpenMixCalculator, getWorkspaceHomeHref } from '$lib/workspace-access';
import { canCreateMixSession, canOpenMixCalculator, getWorkspaceHomeHref } from '$lib/workspace-access';
export async function load({ fetch }) {
// Single-page mode (session history disabled): this route IS the calculator.
if (!featureFlags.mixCalculatorSessionHistory) {
throw redirect(307, '/mix-calculator/new');
if (!hasStoredClientSession()) {
return { options: { clients: [], products: [] } };
}
const session = getStoredClientSession();
if (!canCreateMixSession(session)) {
throw redirect(307, getWorkspaceHomeHref(session));
}
try {
return {
options:
hasModuleAccess(session, 'mix_calculator', 'edit') || session?.role === 'internal'
? await api.mixCalculatorOptions(fetch)
: { clients: [], products: [] }
};
} catch {
return { options: { clients: [], products: [] } };
}
}
// History mode: list saved sessions.
if (!hasStoredClientSession()) {
return {
sessions: []
};
return { sessions: [] };
}
const session = getStoredClientSession();
@@ -22,11 +40,12 @@ export async function load({ fetch }) {
try {
return {
sessions: hasModuleAccess(session, 'mix_calculator') || session?.role === 'internal' ? await api.mixCalculatorSessions(fetch) : []
sessions:
hasModuleAccess(session, 'mix_calculator') || session?.role === 'internal'
? await api.mixCalculatorSessions(fetch)
: []
};
} catch {
return {
sessions: []
};
return { sessions: [] };
}
}
@@ -15,7 +15,7 @@
{#if featureFlags.mixCalculatorSessionHistory}
<a class="secondary-button" href="/mix-calculator">Back to session history</a>
{:else}
<a class="secondary-button" href="/mix-calculator/new">New mix session</a>
<a class="secondary-button" href="/mix-calculator">New mix session</a>
{/if}
</section>
{/if}
@@ -38,7 +38,7 @@
max-width: 42rem;
padding: 1.25rem;
border: 1px solid var(--line);
border-radius: 1.25rem;
border-radius: 0.8rem;
background: var(--panel);
box-shadow: var(--shadow);
}
@@ -58,7 +58,7 @@
margin-top: 1rem;
padding: 0.78rem 0.92rem;
border: 1px solid var(--line-strong);
border-radius: 0.88rem;
border-radius: 0.6rem;
background: #fff;
color: #304038;
font-weight: 600;
@@ -15,7 +15,7 @@
{#if featureFlags.mixCalculatorSessionHistory}
<a class="secondary-button" href="/mix-calculator">Back to session history</a>
{:else}
<a class="secondary-button" href="/mix-calculator/new">New mix session</a>
<a class="secondary-button" href="/mix-calculator">New mix session</a>
{/if}
</section>
{/if}