feat: add export to calendar functionality for medication schedules
This commit is contained in:
@@ -1445,6 +1445,11 @@ export default function App() {
|
|||||||
<button className="ghost" onClick={() => { setSelectedMed(null); setShowImageLightbox(false); }}>
|
<button className="ghost" onClick={() => { setSelectedMed(null); setShowImageLightbox(false); }}>
|
||||||
Close
|
Close
|
||||||
</button>
|
</button>
|
||||||
|
{selectedMed.slices.length > 0 && (
|
||||||
|
<button className="ghost" onClick={() => generateICS(selectedMed)} title="Export schedule to calendar">
|
||||||
|
📅 Export to Calendar
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
<button className="ghost" onClick={() => { setSelectedMed(null); setShowImageLightbox(false); navigate("/medications"); startEdit(selectedMed); }}>
|
<button className="ghost" onClick={() => { setSelectedMed(null); setShowImageLightbox(false); navigate("/medications"); startEdit(selectedMed); }}>
|
||||||
Edit Medication
|
Edit Medication
|
||||||
</button>
|
</button>
|
||||||
@@ -1557,6 +1562,63 @@ function formatDateTime(value: string) {
|
|||||||
return d.toLocaleString([], { weekday: "short", day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" });
|
return d.toLocaleString([], { weekday: "short", day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateICS(med: Medication) {
|
||||||
|
const formatICSDate = (date: Date) => {
|
||||||
|
return date.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '');
|
||||||
|
};
|
||||||
|
|
||||||
|
const events = med.slices.map((slice, idx) => {
|
||||||
|
const start = new Date(slice.start);
|
||||||
|
const end = new Date(start.getTime() + 15 * 60 * 1000); // 15 min duration
|
||||||
|
const interval = slice.every;
|
||||||
|
|
||||||
|
const pillInfo = `${slice.usage} pill${slice.usage !== 1 ? 's' : ''}${med.pillWeightMg ? ` (${slice.usage * med.pillWeightMg} mg)` : ''}`;
|
||||||
|
const summary = `💊 ${med.name} - ${pillInfo}`;
|
||||||
|
const description = [
|
||||||
|
`Medication: ${med.name}`,
|
||||||
|
med.genericName ? `Generic: ${med.genericName}` : '',
|
||||||
|
med.takenBy ? `For: ${med.takenBy}` : '',
|
||||||
|
`Dosage: ${pillInfo}`,
|
||||||
|
`Frequency: every ${interval} day${interval !== 1 ? 's' : ''}`,
|
||||||
|
med.notes ? `Notes: ${med.notes}` : '',
|
||||||
|
].filter(Boolean).join('\\n');
|
||||||
|
|
||||||
|
return `BEGIN:VEVENT
|
||||||
|
UID:medassist-${med.id}-${idx}@medassist
|
||||||
|
DTSTAMP:${formatICSDate(new Date())}
|
||||||
|
DTSTART:${formatICSDate(start)}
|
||||||
|
DTEND:${formatICSDate(end)}
|
||||||
|
RRULE:FREQ=DAILY;INTERVAL=${interval}
|
||||||
|
SUMMARY:${summary}
|
||||||
|
DESCRIPTION:${description}
|
||||||
|
BEGIN:VALARM
|
||||||
|
TRIGGER:-PT5M
|
||||||
|
ACTION:DISPLAY
|
||||||
|
DESCRIPTION:Time to take ${med.name}
|
||||||
|
END:VALARM
|
||||||
|
END:VEVENT`;
|
||||||
|
}).join('\n');
|
||||||
|
|
||||||
|
const ics = `BEGIN:VCALENDAR
|
||||||
|
VERSION:2.0
|
||||||
|
PRODID:-//MedAssist//Medication Schedule//EN
|
||||||
|
CALSCALE:GREGORIAN
|
||||||
|
METHOD:PUBLISH
|
||||||
|
X-WR-CALNAME:${med.name} Schedule
|
||||||
|
${events}
|
||||||
|
END:VCALENDAR`;
|
||||||
|
|
||||||
|
const blob = new Blob([ics], { type: 'text/calendar;charset=utf-8' });
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = url;
|
||||||
|
link.download = `${med.name.replace(/[^a-zA-Z0-9]/g, '_')}_schedule.ics`;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
function buildSchedulePreview(meds: Medication[]) {
|
function buildSchedulePreview(meds: Medication[]) {
|
||||||
const events: Array<{ id: string; medName: string; timeStr: string; dateStr: string; usage: number; when: number }> = [];
|
const events: Array<{ id: string; medName: string; timeStr: string; dateStr: string; usage: number; when: number }> = [];
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|||||||
Reference in New Issue
Block a user