fix: prevent duplicate consultations in backend, health calendar shows medication dots

This commit is contained in:
MingNian
2026-05-22 10:59:46 +08:00
parent 4a525124c5
commit 8caa374699
2 changed files with 64 additions and 39 deletions

View File

@@ -1,60 +1,87 @@
import { useState, useMemo } from 'react';
import { useState, useMemo, useEffect } from 'react';
import { PageHeader } from '@/components/layout/PageHeader';
import { Card } from '@/components/common/Card';
import type { CalendarDay } from '@/types';
import { api } from '@/services/api-client';
import dayjs from 'dayjs';
import styles from './HealthCalendarPage.module.css';
const MARKER_COLORS: Record<string, string> = {
medication_taken: '#10B981',
medication_missed: '#EF4444',
follow_up: '#F59E0B',
measurement: '#2563EB',
};
interface MedRecord { medicationId: string; timeSlot: string; takenAt?: string | null; isTaken: boolean; }
interface HealthRecord { id: string; type: string; recordedAt: string; }
interface Medication { id: string; drugName: string; timeSlots: string[]; status: string; startDate: string; endDate?: string | null; }
export function HealthCalendarPage() {
const [currentDate, setCurrentDate] = useState(dayjs());
const [medRecords, setMedRecords] = useState<MedRecord[]>([]);
const [medications, setMedications] = useState<Medication[]>([]);
useEffect(() => {
// Fetch all active medications and their records
api.get<Medication[]>('/api/medications').then((res) => {
const active = res.data.filter((m) => m.status === 'active');
setMedications(active);
// Fetch records for each active medication
Promise.all(active.map((m) =>
api.get<MedRecord[]>(`/api/medications/${m.id}/records`)
.then((r) => r.data)
.catch(() => [] as MedRecord[])
)).then((all) => setMedRecords(all.flat()));
});
}, []);
const calendarDays = useMemo(() => {
const startOfMonth = currentDate.startOf('month');
const endOfMonth = currentDate.endOf('month');
const startDay = startOfMonth.day();
const days: CalendarDay[] = [];
const today = dayjs().format('YYYY-MM-DD');
// Build a map: date -> { taken, missed }
const dateMap: Record<string, { taken: number; missed: number }> = {};
medRecords.forEach((r) => {
const d = r.takenAt?.split('T')[0];
if (!d) return;
if (!dateMap[d]) dateMap[d] = { taken: 0, missed: 0 };
if (r.isTaken) dateMap[d].taken++;
else dateMap[d].missed++;
});
// Pad previous month
for (let i = startDay - 1; i >= 0; i--) {
const d = startOfMonth.subtract(i + 1, 'day');
days.push({
date: d.format('YYYY-MM-DD'),
year: d.year(),
month: d.month() + 1,
day: d.date(),
isCurrentMonth: false,
isToday: d.format('YYYY-MM-DD') === today,
markers: [],
});
days.push({ date: d.format('YYYY-MM-DD'), year: d.year(), month: d.month() + 1, day: d.date(), isCurrentMonth: false, isToday: d.format('YYYY-MM-DD') === today, markers: [] });
}
for (let d = startOfMonth; d.isBefore(endOfMonth) || d.isSame(endOfMonth, 'day'); d = d.add(1, 'day')) {
const dateStr = d.format('YYYY-MM-DD');
const markers: CalendarDay['markers'] = [];
const dm = dateMap[dateStr];
// Calendar markers would be populated from real API data
if (dm) {
if (dm.taken > 0) {
markers.push({ type: 'medication_taken', color: '#10B981', count: dm.taken });
}
if (dm.missed > 0) {
markers.push({ type: 'medication_missed', color: '#EF4444', count: dm.missed });
}
} else {
// Check if any medication should have been taken on this date
const dateInRange = medications.some((m) => {
if (m.status !== 'active') return false;
const sd = m.startDate;
const ed = m.endDate || '9999-12-31';
return dateStr >= sd && dateStr <= ed;
});
if (dateInRange && medications.length > 0) {
markers.push({ type: 'medication_missed', color: '#FFA500', count: 0 });
}
}
days.push({
date: dateStr,
year: d.year(),
month: d.month() + 1,
day: d.date(),
isCurrentMonth: true,
isToday: dateStr === today,
markers,
});
days.push({ date: dateStr, year: d.year(), month: d.month() + 1, day: d.date(), isCurrentMonth: true, isToday: dateStr === today, markers });
}
return days;
}, [currentDate]);
}, [currentDate, medRecords, medications]);
const weeks: CalendarDay[][] = [];
for (let i = 0; i < calendarDays.length; i += 7) {
@@ -79,18 +106,11 @@ export function HealthCalendarPage() {
{weeks.map((week, wi) => (
<div key={wi} className={styles.week}>
{week.map((day) => (
<div
key={day.date}
className={`${styles.day} ${!day.isCurrentMonth ? styles.outside : ''} ${day.isToday ? styles.today : ''}`}
>
<div key={day.date} className={`${styles.day} ${!day.isCurrentMonth ? styles.outside : ''} ${day.isToday ? styles.today : ''}`}>
<span className={styles.dayNum}>{day.day}</span>
<div className={styles.markers}>
{day.markers.slice(0, 3).map((m, i) => (
<span
key={i}
className={styles.dot}
style={{ background: m.color }}
/>
<span key={i} className={styles.dot} style={{ background: m.color }} />
))}
</div>
</div>
@@ -101,10 +121,9 @@ export function HealthCalendarPage() {
<Card className={styles.legend}>
<div className={styles.legendTitle}></div>
<div className={styles.legendItems}>
<span className={styles.legendItem}><span className={styles.dot} style={{ background: '#2563EB' }} /> </span>
<span className={styles.legendItem}><span className={styles.dot} style={{ background: '#10B981' }} /> </span>
<span className={styles.legendItem}><span className={styles.dot} style={{ background: '#EF4444' }} /> </span>
<span className={styles.legendItem}><span className={styles.dot} style={{ background: '#F59E0B' }} /> </span>
<span className={styles.legendItem}><span className={styles.dot} style={{ background: '#FFA500' }} /> </span>
</div>
</Card>
</div>