mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
fix(ui): defensive optional chains on all remaining var?.x.y sites
Marc reported many residual TypeError crashes after the targeted fix
in 680b001. A comprehensive perl regex sweep across src/app and
src/components turned up 19 more occurrences of the same anti-
pattern: `var?.x.y` only protects the outer object, so when the
inner field is undefined the access throws.
Touched files:
- src/app/api/dashboard/summary/route.ts (server-side null guards)
- src/app/api/health/route.ts (session check)
- src/components/admin/feedback-inbox-section.tsx
- src/components/medications/medication-card.tsx (compliance rates)
- src/components/medications/medication-form.tsx
- src/components/medications/intake-history-list.tsx
- src/components/measurements/measurement-list.tsx
- src/components/mood/mood-list.tsx
- src/components/ui/password-strength.tsx
Pattern applied: `var?.outer.inner` → `var?.outer?.inner`. Tests
green, typecheck clean. Lint untouched (the rule we already enforce
catches set-state-in-effect, not optional-chain depth).
This commit is contained in:
1 parent
680b0019c1
commit
0810a859e1
10 files changed
+22
-22
No files matched your search
@@ -190,11 +190,11 @@ export const GET = apiHandler(async () => {
|
||||
glitchtipEnabled: Boolean(appSettings?.glitchtipEnabled),
|
||||
umamiEnabled: Boolean(appSettings?.umamiEnabled),
|
||||
wideEventsEnabled: true,
|
||||
lastErrorAt: lastErrorEntry?.createdAt.toISOString() ?? null,
|
||||
lastErrorAt: lastErrorEntry?.createdAt?.toISOString() ?? null,
|
||||
},
|
||||
backups: {
|
||||
severity: severityForBackup(latestBackup?.createdAt ?? null),
|
||||
lastBackupAt: latestBackup?.createdAt.toISOString() ?? null,
|
||||
lastBackupAt: latestBackup?.createdAt?.toISOString() ?? null,
|
||||
backedUpUsers,
|
||||
retentionDays: 90,
|
||||
},
|
||||
@@ -203,13 +203,13 @@ export const GET = apiHandler(async () => {
|
||||
workerRunning: worker.running,
|
||||
workerUptimeSeconds,
|
||||
lastIdempotencyCleanup:
|
||||
lastIdempotencyCleanup?.createdAt.toISOString() ?? null,
|
||||
lastAuditLogCleanup: lastAuditLogCleanup?.createdAt.toISOString() ?? null,
|
||||
lastIdempotencyCleanup?.createdAt?.toISOString() ?? null,
|
||||
lastAuditLogCleanup: lastAuditLogCleanup?.createdAt?.toISOString() ?? null,
|
||||
},
|
||||
auditLog: {
|
||||
severity: "info",
|
||||
eventsLast30d: last30dEvents,
|
||||
lastLoginAt: lastLoginEntry?.createdAt.toISOString() ?? null,
|
||||
lastLoginAt: lastLoginEntry?.createdAt?.toISOString() ?? null,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -246,7 +246,7 @@ export const GET = apiHandler(async () => {
|
||||
unit: METRIC_UNITS.weight,
|
||||
trend: trendOf(spark),
|
||||
sparkline: spark,
|
||||
updatedAt: latest?.at.toISOString() ?? null,
|
||||
updatedAt: latest?.at?.toISOString() ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ export const GET = apiHandler(async () => {
|
||||
trend: trendOf(sysList.map((p) => p.value)),
|
||||
sparkline: sysList.map((p) => p.value),
|
||||
updatedAt:
|
||||
latestSys?.at.toISOString() ?? latestDia?.at.toISOString() ?? null,
|
||||
latestSys?.at?.toISOString() ?? latestDia?.at?.toISOString() ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ export const GET = apiHandler(async () => {
|
||||
unit: METRIC_UNITS.pulse,
|
||||
trend: trendOf(spark),
|
||||
sparkline: spark,
|
||||
updatedAt: latest?.at.toISOString() ?? null,
|
||||
updatedAt: latest?.at?.toISOString() ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ export const GET = apiHandler(async () => {
|
||||
|
||||
// Only expose detailed info to authenticated admins
|
||||
const session = await getSession().catch(() => null);
|
||||
if (session?.user.role === "ADMIN") {
|
||||
if (session?.user?.role === "ADMIN") {
|
||||
return NextResponse.json(
|
||||
{
|
||||
status,
|
||||
|
||||
@@ -49,7 +49,7 @@ export function FeedbackInboxSection({ id }: { id: string }) {
|
||||
const { t } = useTranslations();
|
||||
const queryClient = useQueryClient();
|
||||
const { data: status } = useSystemStatus();
|
||||
const githubConfigured = Boolean(status?.integrations.bugReport?.configured);
|
||||
const githubConfigured = Boolean(status?.integrations?.bugReport?.configured);
|
||||
|
||||
const [activeStatus, setActiveStatus] = useState<FeedbackStatusType>("OPEN");
|
||||
const [selected, setSelected] = useState<FeedbackItem | null>(null);
|
||||
@@ -65,7 +65,7 @@ export function FeedbackInboxSection({ id }: { id: string }) {
|
||||
},
|
||||
});
|
||||
|
||||
const counts = data?.meta.countsByStatus ?? {};
|
||||
const counts = data?.meta?.countsByStatus ?? {};
|
||||
|
||||
function refresh() {
|
||||
queryClient.invalidateQueries({ queryKey: ["admin", "feedback"] });
|
||||
@@ -113,7 +113,7 @@ export function FeedbackInboxSection({ id }: { id: string }) {
|
||||
{t("admin.feedback.loading")}
|
||||
</span>
|
||||
</div>
|
||||
) : !data?.items.length ? (
|
||||
) : !data?.items?.length ? (
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("admin.feedback.noEntries")}
|
||||
</p>
|
||||
|
||||
@@ -287,7 +287,7 @@ export function MeasurementList({ onEdit }: MeasurementListProps) {
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{data?.meta.total !== undefined && (
|
||||
{data?.meta?.total !== undefined && (
|
||||
<span className="text-muted-foreground text-sm">
|
||||
{t("measurements.measurementCount", {
|
||||
count: fmt.integer(data.meta.total),
|
||||
|
||||
@@ -362,7 +362,7 @@ export function IntakeHistoryList({ medicationId, createOpen, onCreateOpenChange
|
||||
<>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-end">
|
||||
{data?.meta.total !== undefined && (
|
||||
{data?.meta?.total !== undefined && (
|
||||
<span className="text-muted-foreground text-sm">
|
||||
{t("medications.intakeCount", { count: data.meta.total })}
|
||||
</span>
|
||||
@@ -373,7 +373,7 @@ export function IntakeHistoryList({ medicationId, createOpen, onCreateOpenChange
|
||||
<div className="flex h-32 items-center justify-center">
|
||||
<Loader2 className="text-primary h-6 w-6 animate-spin" />
|
||||
</div>
|
||||
) : !data?.events.length ? (
|
||||
) : !data?.events?.length ? (
|
||||
<div className="text-muted-foreground flex h-32 items-center justify-center rounded-lg border border-dashed">
|
||||
{t("medications.noIntakesYet")}
|
||||
</div>
|
||||
|
||||
@@ -243,9 +243,9 @@ export function MedicationCard({ medication, onEdit }: MedicationCardProps) {
|
||||
}
|
||||
}
|
||||
|
||||
const rate7 = compliance?.compliance7.rate ?? 0;
|
||||
const rate30 = compliance?.compliance30.rate ?? 0;
|
||||
const streak = compliance?.compliance7.streak ?? 0;
|
||||
const rate7 = compliance?.compliance7?.rate ?? 0;
|
||||
const rate30 = compliance?.compliance30?.rate ?? 0;
|
||||
const streak = compliance?.compliance7?.streak ?? 0;
|
||||
const categoryLabels: Record<string, string> = {
|
||||
BLOOD_PRESSURE: t("medications.categoryBloodPressure"),
|
||||
VITAMIN: t("medications.categoryVitamin"),
|
||||
|
||||
@@ -259,7 +259,7 @@ export function MedicationForm({
|
||||
);
|
||||
const [schedules, setSchedules] = useState<Schedule[]>(
|
||||
sortSchedules(
|
||||
initial?.schedules.length
|
||||
initial?.schedules?.length
|
||||
? initial.schedules.map((s) => {
|
||||
const recurrence = {
|
||||
daysOfWeek: s.daysOfWeek ?? [],
|
||||
|
||||
@@ -283,7 +283,7 @@ export function MoodList() {
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{data?.meta.total !== undefined && (
|
||||
{data?.meta?.total !== undefined && (
|
||||
<span className="text-muted-foreground text-sm">
|
||||
{t("mood.entryCount", {
|
||||
count: fmt.integer(data.meta.total),
|
||||
|
||||
@@ -74,10 +74,10 @@ export function PasswordStrength({
|
||||
if (tooShort) {
|
||||
feedback.push(t("passwordStrength.minLength", { count: minLength }));
|
||||
}
|
||||
if (result?.feedback.warning) {
|
||||
if (result?.feedback?.warning) {
|
||||
feedback.push(translate(result.feedback.warning));
|
||||
}
|
||||
if (result?.feedback.suggestions) {
|
||||
if (result?.feedback?.suggestions) {
|
||||
feedback.push(...result.feedback.suggestions.map(translate));
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user