From b97a4579fe2f73b53d80a25b74d48e417050b04c Mon Sep 17 00:00:00 2001 From: Ronni Skansing Date: Sat, 6 Jun 2026 11:54:13 +0200 Subject: [PATCH] improve calendar invite builder Signed-off-by: Ronni Skansing --- frontend/src/routes/tools/+page.svelte | 168 ++++++++++++++++++++++--- 1 file changed, 150 insertions(+), 18 deletions(-) diff --git a/frontend/src/routes/tools/+page.svelte b/frontend/src/routes/tools/+page.svelte index dc5ff87..8445aa8 100644 --- a/frontend/src/routes/tools/+page.svelte +++ b/frontend/src/routes/tools/+page.svelte @@ -460,13 +460,21 @@ let icsDate = ''; let icsTime = '09:00'; let icsDuration = '30'; - let icsTimezone = 'floating'; + let icsTimezone = 'UTC'; let icsAddReminder = false; let icsReminder = '15'; // the uid stays stable while editing so the same invite is not seen as a new // event on every keystroke. regenerate it explicitly with the button. let icsUID = ''; let icsResult = ''; + let icsClass = 'PUBLIC'; + let icsSequence = '0'; + let icsAttendeePartstat = 'NEEDS-ACTION'; + let icsMsTeamsUrl = ''; + let icsMsSuppressRsvp = true; + let icsMsBusyStatus = ''; + let icsMsDisallowCounter = true; + let icsGoogleConference = ''; const durationOptions = [ { value: '15', label: '15 minutes' }, @@ -488,16 +496,13 @@ { value: '1440', label: '1 day before' } ]; - // floating shows the entered time as is in the recipient calendar, UTC pins it to - // an absolute instant, a named zone carries the wall time plus a VTIMEZONE block. const timezoneOptions = [ - { value: 'floating', label: 'Recipient local time' }, { value: 'UTC', label: 'UTC' }, { value: 'America/Los_Angeles', label: 'Los Angeles (Pacific)' }, { value: 'America/Chicago', label: 'Chicago (Central)' }, { value: 'America/New_York', label: 'New York (Eastern)' }, { value: 'Europe/London', label: 'London' }, - { value: 'Europe/Paris', label: 'Paris / Berlin / Madrid' }, + { value: 'Europe/Paris', label: 'Paris / Berlin / Copenhagen / Madrid' }, { value: 'Europe/Athens', label: 'Athens / Helsinki' }, { value: 'Asia/Dubai', label: 'Dubai' }, { value: 'Asia/Kolkata', label: 'India' }, @@ -607,10 +612,7 @@ let dtStart; let dtEnd; let vtimezone = []; - if (icsTimezone === 'floating') { - dtStart = `DTSTART:${toWallStamp(base)}`; - dtEnd = `DTEND:${toWallStamp(end)}`; - } else if (icsTimezone === 'UTC') { + if (icsTimezone === 'UTC') { dtStart = `DTSTART:${toWallStamp(base)}Z`; dtEnd = `DTEND:${toWallStamp(end)}Z`; } else { @@ -642,8 +644,9 @@ `DTSTAMP:${toICSStamp(new Date())}`, dtStart, dtEnd, - 'SEQUENCE:0', + `SEQUENCE:${icsSequence || '0'}`, 'STATUS:CONFIRMED', + `CLASS:${icsClass}`, `SUMMARY:${escapeICSText(icsSummary)}` ]; if (icsDescription.trim()) { @@ -660,9 +663,24 @@ } if (icsAttendee.trim()) { lines.push( - `ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:${icsAttendee.trim()}` + `ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=${icsAttendeePartstat};RSVP=TRUE:mailto:${icsAttendee.trim()}` ); } + if (icsMsTeamsUrl.trim()) { + lines.push(`X-MICROSOFT-SKYPETEAMSMEETINGURL:${icsMsTeamsUrl.trim()}`); + } + if (icsMsSuppressRsvp) { + lines.push('X-MICROSOFT-ISRESPONSEREQUESTED:FALSE'); + } + if (icsMsBusyStatus) { + lines.push(`X-MICROSOFT-CDO-BUSYSTATUS:${icsMsBusyStatus}`); + } + if (icsMsDisallowCounter) { + lines.push('X-MICROSOFT-DISALLOW-COUNTER:TRUE'); + } + if (icsGoogleConference.trim()) { + lines.push(`X-GOOGLE-CONFERENCE:${icsGoogleConference.trim()}`); + } if (icsAddReminder) { const reminderMinutes = parseInt(icsReminder, 10) || 15; lines.push( @@ -706,6 +724,7 @@ icsOrganizerName, icsOrganizerEmail, icsAttendee, + icsAttendeePartstat, icsLocation, icsDescription, icsDate, @@ -715,6 +734,13 @@ icsAddReminder, icsReminder, icsUID, + icsClass, + icsSequence, + icsMsTeamsUrl, + icsMsSuppressRsvp, + icsMsBusyStatus, + icsMsDisallowCounter, + icsGoogleConference, buildICS(); onMount(() => { @@ -890,14 +916,10 @@ class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-sm dark:shadow-gray-900/50 border border-gray-100 dark:border-gray-700 h-[420px] lg:h-[858px] flex flex-col transition-colors duration-200 lg:col-span-2 lg:row-span-2" >

Calendar Invitation Builder

-

- Builds an Outlook ready meeting request (.ics). Save it as an email attachment with - embedded content enabled so template variables resolve per recipient. -

@@ -920,7 +942,7 @@ Attendee @@ -981,7 +1003,7 @@ {/each} - +
@@ -1007,6 +1029,116 @@ {/if} + +
+

+ Event +

+
+ + +
+ +
+ +
+

+ Microsoft / Outlook +

+ + Teams meeting URL + +
+ + Suppress RSVP replies + + + Disallow counter proposals + +
+ +
+ +
+

+ Google +

+ + Conference URL + +