feat: add checkbox to include consumption from today until planner start date (#98)

- Add 'Include consumption from today until start date' checkbox to planner
- When checked, usage calculation starts from today instead of max(today, startDate)
- Persist checkbox state in localStorage per user
- Add i18n translations (EN + DE)
- Update planner tests to use dynamic future dates
This commit is contained in:
Daniel Volz
2026-02-06 22:01:01 +01:00
committed by GitHub
parent 01deea1fa0
commit 5818dcc00d
6 changed files with 215 additions and 43 deletions
+1
View File
@@ -176,6 +176,7 @@
"badge": "Vorrat planen",
"from": "Von",
"until": "Bis",
"includeUntilStart": "Verbrauch von heute bis Startdatum einrechnen",
"calculate": "Berechnen",
"calculating": "Wird berechnet...",
"sendEmail": "📧 Per E-Mail senden",
+1
View File
@@ -176,6 +176,7 @@
"badge": "Plan your supply",
"from": "From",
"until": "Until",
"includeUntilStart": "Include consumption from today until start date",
"calculate": "Calculate",
"calculating": "Calculating...",
"sendEmail": "📧 Send via Email",
+21 -1
View File
@@ -41,6 +41,7 @@ export function PlannerPage() {
start: toInputValue(todayIso()),
end: toInputValue(plusDaysIso(3)),
});
const [includeUntilStart, setIncludeUntilStart] = useState(false);
const [sendingPlannerEmail, setSendingPlannerEmail] = useState(false);
const [plannerEmailResult, setPlannerEmailResult] = useState<{ success: boolean; message: string } | null>(null);
@@ -49,6 +50,7 @@ export function PlannerPage() {
if (typeof window !== "undefined" && user?.id) {
const savedRows = localStorage.getItem(userStorageKey(user.id, "plannerRows"));
const savedRange = localStorage.getItem(userStorageKey(user.id, "plannerRange"));
const savedIncludeUntilStart = localStorage.getItem(userStorageKey(user.id, "plannerIncludeUntilStart"));
if (savedRows) {
try {
@@ -69,16 +71,23 @@ export function PlannerPage() {
} else {
setRange({ start: toInputValue(todayIso()), end: toInputValue(plusDaysIso(3)) });
}
if (savedIncludeUntilStart) {
setIncludeUntilStart(savedIncludeUntilStart === "true");
} else {
setIncludeUntilStart(false);
}
} else {
setPlannerRows([]);
setRange({ start: toInputValue(todayIso()), end: toInputValue(plusDaysIso(3)) });
setIncludeUntilStart(false);
}
}, [user?.id]);
async function runPlanner(e: React.FormEvent) {
e.preventDefault();
setPlannerLoading(true);
const body = { startDate: toIsoString(range.start), endDate: toIsoString(range.end) };
const body = { startDate: toIsoString(range.start), endDate: toIsoString(range.end), includeUntilStart };
const rows = (await fetch("/api/medications/usage", {
method: "POST",
headers: { "Content-Type": "application/json" },
@@ -93,15 +102,18 @@ export function PlannerPage() {
if (user?.id) {
localStorage.setItem(userStorageKey(user.id, "plannerRange"), JSON.stringify(range));
localStorage.setItem(userStorageKey(user.id, "plannerRows"), JSON.stringify(rows));
localStorage.setItem(userStorageKey(user.id, "plannerIncludeUntilStart"), String(includeUntilStart));
}
}
function resetRange() {
setRange({ start: toInputValue(todayIso()), end: toInputValue(plusDaysIso(3)) });
setIncludeUntilStart(false);
setPlannerRows([]);
if (user?.id) {
localStorage.removeItem(userStorageKey(user.id, "plannerRange"));
localStorage.removeItem(userStorageKey(user.id, "plannerRows"));
localStorage.removeItem(userStorageKey(user.id, "plannerIncludeUntilStart"));
}
}
@@ -159,6 +171,14 @@ export function PlannerPage() {
onChange={(e) => setRange({ ...range, end: e.target.value })}
/>
</label>
<label className="planner-checkbox">
<input
type="checkbox"
checked={includeUntilStart}
onChange={(e) => setIncludeUntilStart(e.target.checked)}
/>
{t("planner.includeUntilStart")}
</label>
<div className="planner-actions">
<button type="button" className="ghost" onClick={resetRange}>
{t("common.reset")}
+19
View File
@@ -2019,6 +2019,25 @@ textarea.auto-resize {
text-transform: uppercase;
letter-spacing: 0.04em;
}
.planner-checkbox {
grid-column: 1 / -1;
display: flex;
flex-direction: row;
align-items: center;
gap: 0.75rem;
color: var(--text-secondary);
font-size: 0.875rem;
font-weight: 400;
text-transform: none;
letter-spacing: normal;
cursor: pointer;
}
.planner-checkbox input[type="checkbox"] {
width: 1.125rem;
height: 1.125rem;
cursor: pointer;
accent-color: var(--accent-primary);
}
.planner-actions {
grid-column: 1 / -1;
display: flex;