+ Record the pulse on at least two days to see the graph. +
+ diff --git a/frontend/src/components/PushCard.vue b/frontend/src/components/PushCard.vue new file mode 100644 index 0000000..f50535c --- /dev/null +++ b/frontend/src/components/PushCard.vue @@ -0,0 +1,42 @@ + + + +{{ push.status.value }}
+ ++ A reminder is sent at each dose's reminder time, and only if it isn't already ticked + off. Set those times on the medication in the admin. +
+ +Osiris needs a connection to load. Try again once you're back online.
", + { headers: { "Content-Type": "text/html; charset=utf-8" } }, + ), + ); + } + return Promise.resolve(Response.error()); +}); + +interface PushPayload { + title?: string; + body?: string; + tag?: string; + url?: string; +} + +self.addEventListener("push", (event) => { + let payload: PushPayload = {}; + try { + payload = event.data ? (event.data.json() as PushPayload) : {}; + } catch { + payload = { title: "Osiris", body: event.data ? event.data.text() : "" }; + } + + const options: NotificationOptions & { renotify?: boolean } = { + body: payload.body || "", + icon: "/static/icons/icon-192.png", + badge: "/static/icons/icon-192.png", + // Same tag replaces the previous notification instead of stacking. + tag: payload.tag || "osiris", + renotify: true, + requireInteraction: true, // A dose reminder should wait, not fade away. + data: { url: payload.url || "/admin/today/" }, + }; + event.waitUntil(self.registration.showNotification(payload.title || "Osiris", options)); +}); + +self.addEventListener("notificationclick", (event) => { + event.notification.close(); + const data = event.notification.data as { url?: string } | undefined; + const url = data?.url || "/admin/today/"; + + // Focus the app if it is already open rather than opening a second window. + event.waitUntil( + self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((windows) => { + for (const client of windows) { + if (client.url.includes(url) && "focus" in client) return client.focus(); + } + return self.clients.openWindow(url); + }), + ); +}); diff --git a/frontend/src/views/RecapView.vue b/frontend/src/views/RecapView.vue new file mode 100644 index 0000000..51ac49a --- /dev/null +++ b/frontend/src/views/RecapView.vue @@ -0,0 +1,133 @@ + + + +| Day | +Given | +Pulse | +
|---|---|---|
|
+ |
+ + ✅ + {{ row.taken }}/{{ row.expected }} + | ++ {{ row.bpm }} + — + | +
+ ✅ means every active dose was ticked off that day; otherwise the count given out of + expected. Tap a day to fill it in. +
+{{ note.body }}
++ No notes yet — write one on the Today page. +
++ Last updated {{ new Date(globalNote.updated_at).toLocaleString("en-GB") }} +
+ +Osiris needs a connection to load. Try again once you're back online.
", - { headers: { "Content-Type": "text/html; charset=utf-8" } } - ) - ); - }) - ); -}); diff --git a/templates/tracker/app.html b/templates/tracker/app.html new file mode 100644 index 0000000..999d62f --- /dev/null +++ b/templates/tracker/app.html @@ -0,0 +1,24 @@ +{% load django_vite static %} + + + + +| Day | -Given | -Pulse | -
|---|---|---|
| - {{ row.date|date:"D j M" }} - {% if forloop.first %}today{% endif %} - | -- {% if row.complete %} - ✅ - {% else %} - {{ row.taken }}/{{ row.expected }} - {% endif %} - | -{% if row.bpm %}{{ row.bpm }}{% else %}—{% endif %} | -
- ✅ means every active dose was ticked off that day; otherwise the count given - out of expected. Tap a day to fill it in. -
-Record the pulse on at least two days to see the graph.
- {% endif %} -{{ note.body }}
-No notes yet — write one on the Today page.
- {% endfor %} -Checking…
-- A reminder is sent at each dose's reminder time, and only if it isn't already ticked off. - Set those times on the medication in the admin. -
-
- Recap: doses, pulse & notes →
- Manage medication & history →
-