-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Selected Room
-
-
-
Meeting link inserted!
-
-
-
-
-
-
-
-
-
-
diff --git a/server/index.js b/server/index.js
index a58db1f..f539724 100644
--- a/server/index.js
+++ b/server/index.js
@@ -59,15 +59,10 @@ async function start() {
app.use('/api/federation', calendarRoutes);
app.get('/.well-known/redlight', wellKnownHandler);
- // Serve Outlook Add-in static files (before SPA catch-all)
- app.use('/outlook-addin', express.static(path.join(__dirname, '..', 'outlook-addin')));
-
// Serve static files in production
if (process.env.NODE_ENV === 'production') {
app.use(express.static(path.join(__dirname, '..', 'dist')));
app.get('*', (req, res) => {
- // Don't serve SPA for outlook-addin paths
- if (req.path.startsWith('/outlook-addin')) return res.status(404).end();
res.sendFile(path.join(__dirname, '..', 'dist', 'index.html'));
});
}
diff --git a/src/i18n/de.json b/src/i18n/de.json
index afb6d1b..2b7f21c 100644
--- a/src/i18n/de.json
+++ b/src/i18n/de.json
@@ -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",
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 93ac9d6..84231a0 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -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",
diff --git a/src/pages/Calendar.jsx b/src/pages/Calendar.jsx
index bfeff3f..704422a 100644
--- a/src/pages/Calendar.jsx
+++ b/src/pages/Calendar.jsx
@@ -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 */}