chore: fix lint errors and reduce warnings across codebase (#234)

* chore: fix lint errors and reduce warnings across codebase

- Fix noExplicitAny catches in backend routes and plugins
- Fix noNestedTernary issues in backend services
- Add keyboard event handlers for useKeyWithClickEvents in frontend
- Disable noImportantStyles rule in biome.json
- Fix formatting errors across all changed files
- Fix test file lint issues

Closes #233

* fix: restore any types in test files for TS compatibility

* fix: revert Auth.tsx dependency array changes that caused infinite re-render

* fix: null-safe user.username access in AppContext dependency array
This commit is contained in:
Daniel Volz
2026-02-17 05:21:47 +01:00
committed by GitHub
parent 08a18fc14a
commit 89d565bc9d
50 changed files with 621 additions and 259 deletions
+12 -2
View File
@@ -51,8 +51,18 @@ export default function AboutModal({ isOpen, onClose }: AboutModalProps) {
if (!isOpen) return null;
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content about-modal" onClick={(e) => e.stopPropagation()}>
<div
className="modal-overlay"
onClick={onClose}
onKeyDown={(e) => {
if (e.key === "Escape") onClose();
}}
>
<div
className="modal-content about-modal"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
>
<button className="modal-close" onClick={onClose}>
×
</button>
-1
View File
@@ -5,7 +5,6 @@ import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { useLocation, useNavigate } from "react-router-dom";
import { useUnsavedChanges } from "../context";
import type { ThemePreference } from "../hooks";
import { useTheme } from "../hooks";
import { useAuth } from "./Auth";
+13 -2
View File
@@ -39,8 +39,19 @@ export function ConfirmModal({
}, [onCancel]);
return (
<div className={`modal-overlay${overlayClassName ? ` ${overlayClassName}` : ""}`} onClick={onCancel}>
<div className="modal-content" onClick={(e) => e.stopPropagation()} style={{ maxWidth: "450px" }}>
<div
className={`modal-overlay${overlayClassName ? ` ${overlayClassName}` : ""}`}
onClick={onCancel}
onKeyDown={(e) => {
if (e.key === "Escape") onCancel();
}}
>
<div
className="modal-content"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
style={{ maxWidth: "450px" }}
>
<button className="modal-close" onClick={onCancel}>
×
</button>
+7 -1
View File
@@ -28,7 +28,13 @@ export function DateInput({ value, placeholder, className, ...rest }: DateInputP
}, []);
return (
<div className={`date-input-wrapper ${className ?? ""}`} onClick={handleClick}>
<div
className={`date-input-wrapper ${className ?? ""}`}
onClick={handleClick}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") handleClick();
}}
>
<span className="date-input-display" aria-hidden="true">
{displayValue || placeholder || ""}
</span>
+7 -1
View File
@@ -29,7 +29,13 @@ export function DateTimeInput({ value, placeholder, className, ...rest }: DateTi
}, []);
return (
<div className={`date-input-wrapper ${className ?? ""}`} onClick={handleClick}>
<div
className={`date-input-wrapper ${className ?? ""}`}
onClick={handleClick}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") handleClick();
}}
>
<span className="date-input-display" aria-hidden="true">
{displayValue || placeholder || ""}
</span>
+13 -2
View File
@@ -13,8 +13,19 @@ export default function ExportModal({ isOpen, onClose, onExport, exporting }: Ex
if (!isOpen) return null;
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()} style={{ maxWidth: "450px" }}>
<div
className="modal-overlay"
onClick={onClose}
onKeyDown={(e) => {
if (e.key === "Escape") onClose();
}}
>
<div
className="modal-content"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
style={{ maxWidth: "450px" }}
>
<button className="modal-close" onClick={onClose}>
×
</button>
+14 -2
View File
@@ -19,12 +19,24 @@ export function Lightbox({ src, alt, onClose }: LightboxProps) {
}
return (
<div className="lightbox-overlay" onClick={handleOverlayClick}>
<div
className="lightbox-overlay"
onClick={handleOverlayClick}
onKeyDown={(e) => {
if (e.key === "Escape") onClose();
}}
>
<div className="lightbox-container">
<button className="lightbox-close" onClick={onClose}>
×
</button>
<img src={src} alt={alt} className="lightbox-image" onClick={(e) => e.stopPropagation()} />
<img
src={src}
alt={alt}
className="lightbox-image"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
/>
</div>
</div>
);
+43 -9
View File
@@ -154,14 +154,24 @@ export function MedDetailModal({
const packageSize = getPackageSize(selectedMed);
const currentStock = medCoverage ? Math.round(medCoverage.medsLeft) : getMedTotal(selectedMed);
const status = medCoverage ? getStockStatus(medCoverage.daysLeft, medCoverage.medsLeft, settings) : null;
const textClass =
status?.className === "danger" ? "danger-text" : status?.className === "warning" ? "warning-text" : "success-text";
const fallbackTextClass = status?.className === "warning" ? "warning-text" : "success-text";
const textClass = status?.className === "danger" ? "danger-text" : fallbackTextClass;
const stock = getBlisterStock(currentStock, selectedMed.pillsPerBlister, selectedMed.looseTablets, packageSize);
const fullForBounds = Math.max(0, parseStockInput(editStockFullInput));
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content med-detail-modal" onClick={(e) => e.stopPropagation()}>
<div
className="modal-overlay"
onClick={onClose}
onKeyDown={(e) => {
if (e.key === "Escape") onClose();
}}
>
<div
className="modal-content med-detail-modal"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
>
<button className="modal-close" onClick={onClose}>
×
</button>
@@ -172,6 +182,11 @@ export function MedDetailModal({
<div
className={`med-detail-avatar-wrapper ${selectedMed.imageUrl ? "clickable" : ""}`}
onClick={() => selectedMed.imageUrl && onOpenImageLightbox()}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
if (selectedMed.imageUrl) onOpenImageLightbox();
}
}}
>
<MedicationAvatar name={selectedMed.name} imageUrl={selectedMed.imageUrl} size="lg" />
{selectedMed.imageUrl && <span className="expand-icon">🔍</span>}
@@ -408,6 +423,9 @@ export function MedDetailModal({
<h3
className="section-header-clickable"
onClick={() => onRefillHistoryExpandedChange(!refillHistoryExpanded)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") onRefillHistoryExpandedChange(!refillHistoryExpanded);
}}
>
{t("refill.history")} ({refillHistory.length})
<span className="expand-arrow">{refillHistoryExpanded ? "▼" : "▶"}</span>
@@ -488,8 +506,16 @@ export function MedDetailModal({
e.stopPropagation();
onCloseRefillModal();
}}
onKeyDown={(e) => {
e.stopPropagation();
if (e.key === "Escape") onCloseRefillModal();
}}
>
<div className="modal-content refill-modal" onClick={(e) => e.stopPropagation()}>
<div
className="modal-content refill-modal"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
>
<button className="modal-close" onClick={onCloseRefillModal}>
×
</button>
@@ -585,8 +611,16 @@ export function MedDetailModal({
e.stopPropagation();
onCloseEditStockModal();
}}
onKeyDown={(e) => {
e.stopPropagation();
if (e.key === "Escape") onCloseEditStockModal();
}}
>
<div className="modal-content edit-stock-modal" onClick={(e) => e.stopPropagation()}>
<div
className="modal-content edit-stock-modal"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
>
<button className="modal-close" onClick={onCloseEditStockModal}>
×
</button>
@@ -602,6 +636,8 @@ export function MedDetailModal({
? editStockPartialBlisterPills
: editStockFullBlisters * selectedMed.pillsPerBlister + editStockPartialBlisterPills;
const difference = newTotal - currentTotal;
const negativeFallback = difference < 0 ? "negative" : "";
const differenceClass = difference > 0 ? "positive" : negativeFallback;
return (
<>
@@ -691,9 +727,7 @@ export function MedDetailModal({
{newTotal} {newTotal === 1 ? t("common.pill") : t("common.pills")}
</span>
</div>
<div
className={`summary-row difference ${difference > 0 ? "positive" : difference < 0 ? "negative" : ""}`}
>
<div className={`summary-row difference ${differenceClass}`}>
<span>{t("editStock.difference")}:</span>
<span>
{difference > 0 ? "+" : ""}
+18 -3
View File
@@ -137,13 +137,28 @@ export function MobileEditModal({
const currentMed = editingId ? meds.find((m) => m.id === editingId) : null;
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content edit-modal" onClick={(e) => e.stopPropagation()}>
<div
className="modal-overlay"
onClick={onClose}
onKeyDown={(e) => {
if (e.key === "Escape") onClose();
}}
>
<div
className="modal-content edit-modal"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
>
<div className="edit-modal-header">
<button type="button" className="ghost small btn-nav" onClick={onClose}>
{t("common.back")}
</button>
<h2>{editingId ? (readOnlyMode ? t("form.viewEntry") : t("form.editEntry")) : t("form.newEntry")}</h2>
<h2>
{(() => {
const editLabel = readOnlyMode ? t("form.viewEntry") : t("form.editEntry");
return editingId ? editLabel : t("form.newEntry");
})()}
</h2>
</div>
<form
className="form-grid mobile-edit-form"
+12 -2
View File
@@ -9,8 +9,18 @@ export default function ProfileModal({ isOpen, onClose }: ProfileModalProps) {
if (!isOpen) return null;
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content profile-modal" onClick={(e) => e.stopPropagation()}>
<div
className="modal-overlay"
onClick={onClose}
onKeyDown={(e) => {
if (e.key === "Escape") onClose();
}}
>
<div
className="modal-content profile-modal"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
>
<button className="modal-close" onClick={onClose}>
×
</button>
+82 -64
View File
@@ -42,8 +42,18 @@ export function ShareDialog({
if (!show) return null;
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content share-dialog-modal" onClick={(e) => e.stopPropagation()}>
<div
className="modal-overlay"
onClick={onClose}
onKeyDown={(e) => {
if (e.key === "Escape") onClose();
}}
>
<div
className="modal-content share-dialog-modal"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
>
<button className="modal-close" onClick={onClose}>
×
</button>
@@ -53,71 +63,79 @@ export function ShareDialog({
<p className="share-dialog-description">{t("share.description")}</p>
</div>
{sharePeople.length === 0 ? (
<div className="share-dialog-empty">
<p>{t("share.noPeople")}</p>
</div>
) : shareLink ? (
<div className="share-dialog-result">
<p className="share-success">{t("share.linkGenerated")}</p>
<div className="share-link-box">
<input
type="text"
value={shareLink}
readOnly
className="share-link-input"
onClick={(e) => (e.target as HTMLInputElement).select()}
/>
<button className="btn-copy" onClick={onCopyShareLink}>
{shareCopied ? "✓" : "📋"}
</button>
</div>
{shareCopied && <span className="share-copied-hint">{t("share.copied")}</span>}
<div className="share-dialog-footer">
<button
className="ghost"
onClick={() => {
onShareLinkChange(null);
onShareCopiedChange(false);
}}
>
{t("share.generateAnother")}
</button>
<button onClick={onClose}>{t("common.close")}</button>
</div>
</div>
) : (
<div className="share-dialog-form">
<div className="form-group">
<label>{t("share.selectPerson")}</label>
<select value={shareSelectedPerson} onChange={(e) => onShareSelectedPersonChange(e.target.value)}>
{sharePeople.map((person) => (
<option key={person} value={person}>
{person}
</option>
))}
</select>
</div>
{(() => {
if (sharePeople.length === 0) {
return (
<div className="share-dialog-empty">
<p>{t("share.noPeople")}</p>
</div>
);
}
if (shareLink) {
return (
<div className="share-dialog-result">
<p className="share-success">{t("share.linkGenerated")}</p>
<div className="share-link-box">
<input
type="text"
value={shareLink}
readOnly
className="share-link-input"
onClick={(e) => (e.target as HTMLInputElement).select()}
/>
<button className="btn-copy" onClick={onCopyShareLink}>
{shareCopied ? "✓" : "📋"}
</button>
</div>
{shareCopied && <span className="share-copied-hint">{t("share.copied")}</span>}
<div className="share-dialog-footer">
<button
className="ghost"
onClick={() => {
onShareLinkChange(null);
onShareCopiedChange(false);
}}
>
{t("share.generateAnother")}
</button>
<button onClick={onClose}>{t("common.close")}</button>
</div>
</div>
);
}
return (
<div className="share-dialog-form">
<div className="form-group">
<label>{t("share.selectPerson")}</label>
<select value={shareSelectedPerson} onChange={(e) => onShareSelectedPersonChange(e.target.value)}>
{sharePeople.map((person) => (
<option key={person} value={person}>
{person}
</option>
))}
</select>
</div>
<div className="form-group">
<label>{t("share.selectPeriod")}</label>
<select value={shareSelectedDays} onChange={(e) => onShareSelectedDaysChange(Number(e.target.value))}>
<option value={30}>{t("dashboard.schedules.1month")}</option>
<option value={90}>{t("dashboard.schedules.3months")}</option>
<option value={180}>{t("dashboard.schedules.6months")}</option>
</select>
</div>
<div className="form-group">
<label>{t("share.selectPeriod")}</label>
<select value={shareSelectedDays} onChange={(e) => onShareSelectedDaysChange(Number(e.target.value))}>
<option value={30}>{t("dashboard.schedules.1month")}</option>
<option value={90}>{t("dashboard.schedules.3months")}</option>
<option value={180}>{t("dashboard.schedules.6months")}</option>
</select>
</div>
<div className="share-dialog-footer">
<button className="ghost" onClick={onClose}>
{t("common.cancel")}
</button>
<button onClick={onGenerateShareLink} disabled={shareGenerating || !shareSelectedPerson}>
{shareGenerating ? t("share.generating") : t("share.generateLink")}
</button>
<div className="share-dialog-footer">
<button className="ghost" onClick={onClose}>
{t("common.cancel")}
</button>
<button onClick={onGenerateShareLink} disabled={shareGenerating || !shareSelectedPerson}>
{shareGenerating ? t("share.generating") : t("share.generateLink")}
</button>
</div>
</div>
</div>
)}
);
})()}
</div>
</div>
);
+59 -14
View File
@@ -209,7 +209,7 @@ export function SharedSchedule() {
// Get dose ID - for per-intake takenBy, the ID already has the person suffix
// This helper is kept for compatibility but since dose.id already includes the suffix, it just returns the id
function getDoseId(doseId: string, _person: string | null): string {
function _getDoseId(doseId: string, _person: string | null): string {
// The dose.id already includes the person suffix if there's a per-intake takenBy
return doseId;
}
@@ -479,7 +479,8 @@ export function SharedSchedule() {
const intake = intakes[blisterIdx];
const intakePerson = intake?.takenBy;
const peopleForThisIntake = intakePerson ? [intakePerson] : med.takenBy?.length > 0 ? med.takenBy : [null];
const fallbackPeople = med.takenBy?.length > 0 ? med.takenBy : [null];
const peopleForThisIntake = intakePerson ? [intakePerson] : fallbackPeople;
let timeBasedConsumed = 0;
let lastAutoConsumedDateMs = 0;
@@ -579,7 +580,8 @@ export function SharedSchedule() {
const status = getStockStatus(coverage.daysLeft, coverage.medsLeft, stockThresholds);
return status.className;
});
return statuses.includes("danger") ? "danger" : statuses.includes("warning") ? "warning" : "success";
const fallbackStatus = statuses.includes("warning") ? "warning" : "success";
return statuses.includes("danger") ? "danger" : fallbackStatus;
}
// Whether to show stock status indicators on the shared schedule
@@ -606,7 +608,7 @@ export function SharedSchedule() {
const missedPastDoseIds = useMemo(() => {
const allPastDoseIds = pastDays.flatMap((d) => d.meds.flatMap((m) => m.doses.map((dose) => dose.id)));
return allPastDoseIds.filter((id) => !isDoseIdDone(id));
}, [pastDays, takenDoses, dismissedDoses, data]);
}, [pastDays, isDoseIdDone]);
if (loading) {
return (
@@ -714,14 +716,19 @@ export function SharedSchedule() {
</div>
</div>
</div>
<p className="shared-schedule-period">
{t("share.period")}:{" "}
{data.scheduleDays === 30
? t("dashboard.schedules.1month")
: data.scheduleDays === 90
? t("dashboard.schedules.3months")
: t("dashboard.schedules.6months")}
</p>
{(() => {
const periodLabel =
data.scheduleDays === 30
? t("dashboard.schedules.1month")
: data.scheduleDays === 90
? t("dashboard.schedules.3months")
: t("dashboard.schedules.6months");
return (
<p className="shared-schedule-period">
{t("share.period")}: {periodLabel}
</p>
);
})()}
</header>
<div className="timeline">
@@ -757,14 +764,18 @@ export function SharedSchedule() {
const isManuallyExpanded = manuallyExpandedDays.has(day.dateStr);
const isCollapsed = !isManuallyExpanded;
const pastMissedClass = allDoseIds.length > 0 ? "past-missed" : "";
return (
<div
key={day.dateStr}
className={`day-block past ${isCollapsed ? "collapsed" : ""} ${allReallyTaken ? "all-taken" : allDoseIds.length > 0 ? "past-missed" : ""}`}
className={`day-block past ${isCollapsed ? "collapsed" : ""} ${allReallyTaken ? "all-taken" : pastMissedClass}`}
>
<div
className="day-divider clickable"
onClick={() => toggleDayCollapse(day.dateStr, true)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") toggleDayCollapse(day.dateStr, true);
}}
title={isCollapsed ? t("common.expand") : t("common.collapse")}
>
<span className="day-collapse-icon">{isCollapsed ? "▶" : "▼"}</span>
@@ -817,6 +828,11 @@ export function SharedSchedule() {
<div
className={med?.imageUrl ? "med-avatar clickable" : ""}
onClick={() => med?.imageUrl && openLightbox(med.imageUrl, med.name)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
if (med?.imageUrl) openLightbox(med.imageUrl, med.name);
}
}}
>
<MedicationAvatar name={item.medName} imageUrl={med?.imageUrl} size="sm" />
</div>
@@ -894,6 +910,9 @@ export function SharedSchedule() {
}, 50);
}
}}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") setShowPastDays(!showPastDays);
}}
>
<span className="past-days-icon">{showPastDays ? "▼" : "▶"}</span>
<span className="past-days-label">
@@ -941,6 +960,9 @@ export function SharedSchedule() {
<div
className="day-divider clickable"
onClick={() => toggleDayCollapse(day.dateStr, isAutoCollapsed)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") toggleDayCollapse(day.dateStr, isAutoCollapsed);
}}
title={isCollapsed ? t("common.expand") : t("common.collapse")}
>
<span className="day-collapse-icon">{isCollapsed ? "▶" : "▼"}</span>
@@ -982,6 +1004,11 @@ export function SharedSchedule() {
<div
className={med?.imageUrl ? "med-avatar clickable" : ""}
onClick={() => med?.imageUrl && openLightbox(med.imageUrl, med.name)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
if (med?.imageUrl) openLightbox(med.imageUrl, med.name);
}
}}
>
<MedicationAvatar name={item.medName} imageUrl={med?.imageUrl} size="sm" />
</div>
@@ -1058,6 +1085,9 @@ export function SharedSchedule() {
<div
className={`future-days-toggle ${showFutureDays ? "expanded" : ""}`}
onClick={() => setShowFutureDays(!showFutureDays)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") setShowFutureDays(!showFutureDays);
}}
>
<span className="future-days-icon">{showFutureDays ? "▼" : "▶"}</span>
<span className="future-days-label">
@@ -1099,6 +1129,9 @@ export function SharedSchedule() {
<div
className="day-divider clickable"
onClick={() => toggleDayCollapse(day.dateStr, isAutoCollapsed)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") toggleDayCollapse(day.dateStr, isAutoCollapsed);
}}
title={isCollapsed ? t("common.expand") : t("common.collapse")}
>
<span className="day-collapse-icon">{isCollapsed ? "▶" : "▼"}</span>
@@ -1139,6 +1172,11 @@ export function SharedSchedule() {
<div
className={med?.imageUrl ? "med-avatar clickable" : ""}
onClick={() => med?.imageUrl && openLightbox(med.imageUrl, med.name)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
if (med?.imageUrl) openLightbox(med.imageUrl, med.name);
}
}}
>
<MedicationAvatar name={item.medName} imageUrl={med?.imageUrl} size="sm" />
</div>
@@ -1215,7 +1253,13 @@ export function SharedSchedule() {
{/* Image Lightbox */}
{lightboxImage && (
<div className="lightbox-overlay" onClick={closeLightbox}>
<div
className="lightbox-overlay"
onClick={closeLightbox}
onKeyDown={(e) => {
if (e.key === "Escape") closeLightbox();
}}
>
<button className="lightbox-close" onClick={closeLightbox}>
×
</button>
@@ -1224,6 +1268,7 @@ export function SharedSchedule() {
alt={lightboxImage.name}
className="lightbox-image"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
/>
</div>
)}
+18 -2
View File
@@ -36,8 +36,18 @@ export function UserFilterModal({
const userMeds = meds.filter((m) => !m.isObsolete && (m.takenBy || []).includes(selectedUser));
return (
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content user-meds-modal" onClick={(e) => e.stopPropagation()}>
<div
className="modal-overlay"
onClick={onClose}
onKeyDown={(e) => {
if (e.key === "Escape") onClose();
}}
>
<div
className="modal-content user-meds-modal"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
>
<button className="modal-close" onClick={onClose}>
×
</button>
@@ -75,6 +85,12 @@ export function UserFilterModal({
onClearUser();
onOpenMedDetail(med);
}}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
onClearUser();
onOpenMedDetail(med);
}
}}
>
<MedicationAvatar name={med.name} imageUrl={med.imageUrl} size="sm" />
<div className="user-med-info">