mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
chore(release): v1.4.27
v1.4.27 — Mobile capability and maintainer-finding cleanup. ResponsiveSheet primitive (bottom-sheet on narrow viewports, centred dialog on desktop) applied to BD, Weight, Glucose, Mood, Activity forms. Maintainer-finding cleanups across Coach, Insights, Reports, Privacy. GeoLite2 offline geocoding with resilience layer (graceful fallback when EULA not yet propagated). E2E gap-fill coverage. 450-line release notes attached to the GitHub Release.
This commit is contained in:
1 parent
d98e85cd03
commit
4f09c3f862
314 files changed
+26801
-4419
No files matched your search
@@ -136,6 +136,11 @@ export const GET = apiHandler(async (request: NextRequest) => {
|
||||
action: true,
|
||||
ipAddress: true,
|
||||
location: true,
|
||||
// v1.4.27 B3 — surface the carrier chip data alongside the
|
||||
// existing location string. Both nullable so older rows render
|
||||
// with the same dash that `location` falls back to.
|
||||
asn: true,
|
||||
carrier: true,
|
||||
details: true,
|
||||
createdAt: true,
|
||||
user: {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { apiHandler, requireAdmin } from "@/lib/api-handler";
|
||||
import { annotate, getEvent } from "@/lib/logging/context";
|
||||
import { apiSuccess } from "@/lib/api-response";
|
||||
import { parseScheduleRecurrence } from "@/lib/medication-schedule";
|
||||
import { dispatchNotification } from "@/lib/notifications/dispatcher";
|
||||
import { dispatchLocalisedNotification } from "@/lib/notifications/dispatch-localised";
|
||||
import { getUserTodayBounds, getDayOfWeekInTz } from "@/lib/timezone";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
@@ -138,20 +138,36 @@ export const POST = apiHandler(async () => {
|
||||
const timeWindow = `${schedule.windowStart}–${schedule.windowEnd}`;
|
||||
|
||||
try {
|
||||
// v1.4.27 F21 — each notification renders in the affected
|
||||
// user's `User.locale` (the recipient is the user whose dose
|
||||
// is overdue, not the admin running the check).
|
||||
// `dispatchLocalisedNotification` resolves the locale from
|
||||
// the userId inside the helper.
|
||||
if (status === "missed") {
|
||||
await dispatchNotification({
|
||||
eventType: "MEDICATION_REMINDER",
|
||||
await dispatchLocalisedNotification({
|
||||
userId: med.user.id,
|
||||
title: `Verpasst: ${med.name}`,
|
||||
message: `<b>${med.name}</b> (${doseInfo}, ${timeWindow}) wurde als verpasst markiert.`,
|
||||
eventType: "MEDICATION_REMINDER",
|
||||
titleKey: "notifications.admin.reminderCheckMissedTitle",
|
||||
messageKey: "notifications.admin.reminderCheckMissedBody",
|
||||
params: {
|
||||
medication: med.name,
|
||||
dose: doseInfo,
|
||||
window: timeWindow,
|
||||
},
|
||||
metadata: { medicationId: med.id },
|
||||
});
|
||||
} else {
|
||||
await dispatchNotification({
|
||||
eventType: "MEDICATION_REMINDER",
|
||||
await dispatchLocalisedNotification({
|
||||
userId: med.user.id,
|
||||
title: `Erinnerung: ${med.name}`,
|
||||
message: `Erinnerung: <b>${med.name}</b> (${doseInfo}, ${timeWindow}) wurde noch nicht eingenommen. Seit ${minutesPastEnd} Min überfällig.`,
|
||||
eventType: "MEDICATION_REMINDER",
|
||||
titleKey: "notifications.admin.reminderCheckOverdueTitle",
|
||||
messageKey: "notifications.admin.reminderCheckOverdueBody",
|
||||
params: {
|
||||
medication: med.name,
|
||||
dose: doseInfo,
|
||||
window: timeWindow,
|
||||
minutes: minutesPastEnd,
|
||||
},
|
||||
metadata: { medicationId: med.id },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import type {
|
||||
ChannelType,
|
||||
NotificationPayload,
|
||||
} from "@/lib/notifications/types";
|
||||
import { defaultLocale, type Locale } from "@/lib/i18n/config";
|
||||
import { getServerTranslator } from "@/lib/i18n/server-translator";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
@@ -59,6 +61,11 @@ async function ensureTelegramChannel(userId: string): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
function isLocale(value: string | null | undefined): value is Locale {
|
||||
return value === "de" || value === "en" || value === "fr" ||
|
||||
value === "es" || value === "it" || value === "pl";
|
||||
}
|
||||
|
||||
export const POST = apiHandler(async () => {
|
||||
const { user } = await requireAdmin();
|
||||
annotate({ action: { name: "admin.notifications.test" } });
|
||||
@@ -84,12 +91,26 @@ export const POST = apiHandler(async () => {
|
||||
});
|
||||
}
|
||||
|
||||
// v1.4.27 F21 — resolve the admin's persisted locale so the test
|
||||
// notification arrives in their preferred language. The test route
|
||||
// talks to the senders directly (not the dispatcher) because it
|
||||
// needs per-channel success / error feedback for the UI, so we
|
||||
// compose the payload locally rather than via
|
||||
// `dispatchLocalisedNotification`.
|
||||
const adminLocaleRow = await prisma.user.findUnique({
|
||||
where: { id: user.id },
|
||||
select: { locale: true },
|
||||
});
|
||||
const adminLocale: Locale = isLocale(adminLocaleRow?.locale)
|
||||
? adminLocaleRow.locale
|
||||
: defaultLocale;
|
||||
const t = getServerTranslator(adminLocale).t;
|
||||
|
||||
const payload: NotificationPayload = {
|
||||
eventType: "SYSTEM_ALERT",
|
||||
userId: user.id,
|
||||
title: "Test-Notification",
|
||||
message:
|
||||
"<b>HealthLog Test:</b> If you see this message, your notifications are working!",
|
||||
title: t("notifications.admin.testNotificationTitle"),
|
||||
message: t("notifications.admin.testNotificationBody"),
|
||||
};
|
||||
|
||||
const results: Array<{
|
||||
|
||||
Reference in new issue
Block a user