Compare commits

...

2 Commits

Author SHA1 Message Date
Daniel Volz 093aa419af chore: release v1.0.2 2025-12-29 23:55:00 +01:00
Daniel Volz 8132da3c3d feat: update package versions to 1.0.1, improve dose tracking timestamp logic, and enhance release script for signed tags 2025-12-29 23:54:29 +01:00
7 changed files with 48 additions and 55 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "medassist-ng-backend", "name": "medassist-ng-backend",
"version": "0.1.0", "version": "1.0.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "medassist-ng-backend", "name": "medassist-ng-backend",
"version": "0.1.0", "version": "1.0.1",
"dependencies": { "dependencies": {
"@fastify/cookie": "^10.0.1", "@fastify/cookie": "^10.0.1",
"@fastify/cors": "^10.0.1", "@fastify/cors": "^10.0.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "medassist-ng-backend", "name": "medassist-ng-backend",
"version": "1.0.1", "version": "1.0.2",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
+1 -1
View File
@@ -109,6 +109,6 @@ export const doseTracking = sqliteTable("dose_tracking", {
id: integer("id").primaryKey({ autoIncrement: true }), id: integer("id").primaryKey({ autoIncrement: true }),
userId: integer("user_id").notNull().references(() => users.id, { onDelete: "cascade" }), userId: integer("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
doseId: text("dose_id", { length: 255 }).notNull(), // e.g. "med-5-1-86400000-1735200000000" doseId: text("dose_id", { length: 255 }).notNull(), // e.g. "med-5-1-86400000-1735200000000"
takenAt: integer("taken_at", { mode: "timestamp" }).notNull().default(sql`CURRENT_TIMESTAMP`), takenAt: integer("taken_at", { mode: "timestamp" }).notNull().default(sql`(strftime('%s','now'))`),
markedBy: text("marked_by", { length: 100 }), // null = user, "Daniel" = via share link markedBy: text("marked_by", { length: 100 }), // null = user, "Daniel" = via share link
}); });
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "medassist-ng-frontend", "name": "medassist-ng-frontend",
"version": "0.1.0", "version": "1.0.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "medassist-ng-frontend", "name": "medassist-ng-frontend",
"version": "0.1.0", "version": "1.0.1",
"dependencies": { "dependencies": {
"i18next": "^24.2.2", "i18next": "^24.2.2",
"i18next-browser-languagedetector": "^8.0.4", "i18next-browser-languagedetector": "^8.0.4",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "medassist-ng-frontend", "name": "medassist-ng-frontend",
"private": true, "private": true,
"version": "1.0.1", "version": "1.0.2",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
+39 -46
View File
@@ -3625,31 +3625,32 @@ function SharedSchedule() {
fetchData(); fetchData();
}, [token, t]); }, [token, t]);
// Build schedule from medications // Build schedule from medications - matches buildSchedulePreview logic exactly
const schedule = useMemo(() => { const schedule = useMemo(() => {
if (!data) return []; if (!data) return [];
const todayStart = new Date(); // Use same logic as buildSchedulePreview in main app
todayStart.setHours(0, 0, 0, 0); const now = new Date();
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()); // Midnight today
const todayStartTime = todayStart.getTime(); const todayStartTime = todayStart.getTime();
// Calculate end time: today midnight + scheduleDays days // Use 180 days horizon like main app (scheduleDays only limits futureDays display)
const endDate = new Date(todayStart); const end = new Date();
endDate.setDate(endDate.getDate() + data.scheduleDays); end.setDate(end.getDate() + 180);
const endTime = endDate.getTime(); const endTime = end.getTime();
const doses: { id: string; when: number; medName: string; usage: number; timeStr: string; isPast: boolean; takenBy: string[] }[] = []; const doses: { id: string; when: number; medName: string; usage: number; timeStr: string; isPast: boolean; takenBy: string[]; dateStr: string }[] = [];
for (const med of data.medications) { for (const med of data.medications) {
med.blisters.forEach((blister, blisterIdx) => { med.blisters.forEach((blister, blisterIdx) => {
const startDate = new Date(blister.start); const startDate = new Date(blister.start);
const intervalMs = blister.every * 24 * 60 * 60 * 1000; if (Number.isNaN(startDate.getTime())) return;
let t = startDate.getTime();
// Use the same iteration method as buildSchedulePreview (setDate instead of adding ms)
// Start from the very first dose (blister start) // This ensures identical timestamps even across DST changes
while (t <= endTime) { for (let d = new Date(startDate); d <= end; d.setDate(d.getDate() + blister.every)) {
const d = new Date(t); const t = d.getTime();
const isPast = t < todayStartTime; const isPast = d < todayStart;
// Generate dose ID matching Dashboard format: ${med.id}-${blisterIdx}-${whenMs} // Generate dose ID matching Dashboard format: ${med.id}-${blisterIdx}-${whenMs}
const doseId = `${med.id}-${blisterIdx}-${t}`; const doseId = `${med.id}-${blisterIdx}-${t}`;
doses.push({ doses.push({
@@ -3660,48 +3661,40 @@ function SharedSchedule() {
isPast, isPast,
takenBy: med.takenBy || [], takenBy: med.takenBy || [],
timeStr: d.toLocaleTimeString(i18n.language, { hour: "2-digit", minute: "2-digit" }), timeStr: d.toLocaleTimeString(i18n.language, { hour: "2-digit", minute: "2-digit" }),
dateStr: d.toLocaleDateString(i18n.language, { weekday: "short", day: "2-digit", month: "short" }),
}); });
t += intervalMs;
} }
}); });
} }
doses.sort((a, b) => a.when - b.when); doses.sort((a, b) => a.when - b.when);
// Group by date // Group by date - matches groupedSchedule logic in main app
const grouped: { dateStr: string; date: Date; isPast: boolean; meds: { medName: string; total: number; lastWhen: number; doses: typeof doses }[] }[] = []; type DoseInfo = typeof doses[number];
const byDate = new Map<string, typeof doses>(); const days = new Map<string, { dateStr: string; date: Date; isPast: boolean; meds: Map<string, { medName: string; total: number; doses: DoseInfo[]; lastWhen: number }> }>();
for (const dose of doses) { for (const dose of doses.slice(0, 2000)) {
const dateKey = new Date(dose.when).toLocaleDateString(i18n.language, { const day = days.get(dose.dateStr) ?? { dateStr: dose.dateStr, date: new Date(dose.when), isPast: dose.isPast, meds: new Map() };
weekday: "long", const medEntry = day.meds.get(dose.medName) ?? { medName: dose.medName, total: 0, doses: [], lastWhen: dose.when };
day: "2-digit", medEntry.total += dose.usage;
month: "short", medEntry.doses.push(dose);
}); medEntry.lastWhen = Math.max(medEntry.lastWhen, dose.when);
if (!byDate.has(dateKey)) byDate.set(dateKey, []); day.meds.set(dose.medName, medEntry);
byDate.get(dateKey)!.push(dose); days.set(dose.dateStr, day);
} }
for (const [dateStr, dayDoses] of byDate) { return Array.from(days.values()).map((d) => ({
const byMed = new Map<string, typeof doses>(); dateStr: d.dateStr,
for (const dose of dayDoses) { date: d.date,
if (!byMed.has(dose.medName)) byMed.set(dose.medName, []); isPast: d.isPast,
byMed.get(dose.medName)!.push(dose); meds: Array.from(d.meds.values())
} }));
const meds = Array.from(byMed.entries()).map(([medName, medDoses]) => ({
medName,
total: medDoses.reduce((sum, d) => sum + d.usage, 0),
lastWhen: Math.max(...medDoses.map(d => d.when)),
doses: medDoses,
}));
grouped.push({ dateStr, date: new Date(dayDoses[0].when), isPast: dayDoses[0].isPast, meds });
}
return grouped;
}, [data, i18n.language]); }, [data, i18n.language]);
// Split into past and future - matches main app logic
const pastDays = useMemo(() => schedule.filter(d => d.isPast), [schedule]); const pastDays = useMemo(() => schedule.filter(d => d.isPast), [schedule]);
const futureDays = useMemo(() => schedule.filter(d => !d.isPast), [schedule]); // Limit future days by scheduleDays setting (same as main app)
const futureDays = useMemo(() => schedule.filter(d => !d.isPast).slice(0, data?.scheduleDays ?? 30), [schedule, data?.scheduleDays]);
// Calculate coverage for stock status colors (matches main app logic) // Calculate coverage for stock status colors (matches main app logic)
// This needs to account for taken doses and calculate depletion time // This needs to account for taken doses and calculate depletion time
+2 -2
View File
@@ -95,8 +95,8 @@ if git rev-parse "v${NEW_VERSION}" >/dev/null 2>&1; then
fi fi
# Create and push tag # Create and push tag
echo -e "${BLUE}Creating tag v${NEW_VERSION}...${NC}" echo -e "${BLUE}Creating signed tag v${NEW_VERSION}...${NC}"
git tag -a "v${NEW_VERSION}" -m "Release v${NEW_VERSION}" git tag -s "v${NEW_VERSION}" -m "Release v${NEW_VERSION}"
# Push # Push
echo -e "${BLUE}Pushing to origin (GitHub)...${NC}" echo -e "${BLUE}Pushing to origin (GitHub)...${NC}"