fix: prevent duplicate consultations in backend, health calendar shows medication dots
This commit is contained in:
@@ -28,6 +28,12 @@ public class ConsultationService(AppDbContext db)
|
||||
|
||||
public async Task<Consultation> StartAsync(Guid patientId, Guid doctorId, string subject)
|
||||
{
|
||||
// Reuse existing active consultation between this patient and doctor
|
||||
var existing = await db.Consultations
|
||||
.FirstOrDefaultAsync(c => c.PatientId == patientId && c.DoctorId == doctorId && c.Status == "active");
|
||||
if (existing != null)
|
||||
return existing;
|
||||
|
||||
var consultation = new Consultation
|
||||
{
|
||||
PatientId = patientId,
|
||||
|
||||
@@ -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
|
||||
|
||||
days.push({
|
||||
date: dateStr,
|
||||
year: d.year(),
|
||||
month: d.month() + 1,
|
||||
day: d.date(),
|
||||
isCurrentMonth: true,
|
||||
isToday: dateStr === today,
|
||||
markers,
|
||||
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 });
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user