feat(outlook-addin): remove add-in files and update server to prevent serving static files
All checks were successful
Build & Push Docker Image / build (push) Successful in 5m59s

feat(i18n): add translations for "Add to Outlook" and "Add to Google Calendar"
feat(calendar): implement functionality to generate Outlook and Google Calendar event links
This commit is contained in:
2026-03-02 11:49:01 +01:00
parent 9275c20d19
commit 2a8ded5211
13 changed files with 58 additions and 739 deletions

View File

@@ -445,6 +445,8 @@
"sat": "Sa",
"sun": "So",
"downloadICS": "ICS herunterladen",
"addToOutlook": "Zu Outlook hinzufügen",
"addToGoogleCalendar": "Zu Google Kalender",
"icsDownloaded": "ICS-Datei heruntergeladen",
"icsFailed": "ICS-Datei konnte nicht heruntergeladen werden",
"share": "Teilen",

View File

@@ -445,6 +445,8 @@
"sat": "Sat",
"sun": "Sun",
"downloadICS": "Download ICS",
"addToOutlook": "Add to Outlook",
"addToGoogleCalendar": "Google Calendar",
"icsDownloaded": "ICS file downloaded",
"icsFailed": "Could not download ICS file",
"share": "Share",

View File

@@ -1,7 +1,7 @@
import { useState, useEffect, useMemo } from 'react';
import {
ChevronLeft, ChevronRight, Plus, Calendar as CalendarIcon, Clock, Video,
Loader2, Download, Share2, Globe, Trash2, Edit, X, UserPlus, Send,
Loader2, Download, Share2, Globe, Trash2, Edit, X, UserPlus, Send, ExternalLink,
} from 'lucide-react';
import api from '../services/api';
import { useAuth } from '../contexts/AuthContext';
@@ -229,6 +229,41 @@ export default function Calendar() {
}
};
const buildOutlookUrl = (ev) => {
const start = new Date(ev.start_time);
const end = new Date(ev.end_time);
const fmt = (d) => d.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '');
const baseUrl = window.location.origin;
const location = ev.room_uid ? `${baseUrl}/join/${ev.room_uid}` : '';
const body = [ev.description || '', location ? `\n\nMeeting: ${location}` : ''].join('');
const params = new URLSearchParams({
rru: 'addevent',
subject: ev.title,
startdt: start.toISOString(),
enddt: end.toISOString(),
body: body.trim(),
location,
allday: 'false',
path: '/calendar/action/compose',
});
return `https://outlook.live.com/calendar/0/action/compose?${params.toString()}`;
};
const buildGoogleCalUrl = (ev) => {
const fmt = (d) => new Date(d).toISOString().replace(/[-:]/g, '').replace(/\.\d{3}/, '');
const baseUrl = window.location.origin;
const location = ev.room_uid ? `${baseUrl}/join/${ev.room_uid}` : '';
const details = [ev.description || '', location ? `\nMeeting: ${location}` : ''].join('');
const params = new URLSearchParams({
action: 'TEMPLATE',
text: ev.title,
dates: `${fmt(ev.start_time)}/${fmt(ev.end_time)}`,
details: details.trim(),
location,
});
return `https://calendar.google.com/calendar/render?${params.toString()}`;
};
// Share functions
const openShareModal = async (ev) => {
setShowShare(ev);
@@ -601,6 +636,24 @@ export default function Calendar() {
{/* Actions */}
<div className="flex flex-wrap items-center gap-2 pt-4 border-t border-th-border">
<a
href={buildOutlookUrl(showDetail)}
target="_blank"
rel="noopener noreferrer"
className="btn-ghost text-xs py-1.5 px-3 inline-flex items-center gap-1.5 no-underline"
>
<ExternalLink size={14} />
{t('calendar.addToOutlook')}
</a>
<a
href={buildGoogleCalUrl(showDetail)}
target="_blank"
rel="noopener noreferrer"
className="btn-ghost text-xs py-1.5 px-3 inline-flex items-center gap-1.5 no-underline"
>
<ExternalLink size={14} />
{t('calendar.addToGoogleCalendar')}
</a>
<button onClick={() => handleDownloadICS(showDetail)} className="btn-ghost text-xs py-1.5 px-3">
<Download size={14} />
{t('calendar.downloadICS')}