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:
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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")}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user