From f0aeca41667ec36b0b464866ff025ea6e14e5cf1 Mon Sep 17 00:00:00 2001 From: Nicholas McKee Date: Tue, 24 Oct 2023 18:24:23 -0400 Subject: [PATCH 1/5] Added calendar export --- src/lib/utils.ts | 75 ++++++++++++++++++++++++++ src/pages/Competition/Person/index.tsx | 11 +++- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index b6a9212..114ecdf 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -7,6 +7,7 @@ import { formatMultiResult, } from '@wca/helpers'; import { format, parseISO } from 'date-fns'; +import * as ics from 'ics' export const byName = (a: { name: string }, b: { name: string }) => a.name.localeCompare(b.name); export const byDate = (a: T & { startTime: string }, b: T & { startTime: string }) => @@ -148,3 +149,77 @@ export const renderResultByEventId = ( return formatCentiseconds(result as number); }; + +const AssignmentCodeDescription = { + 'staff-scrambler': 'Scrambling for:', + 'staff-runner': 'Runner for:', + 'staff-judge': 'Judging for:', + 'staff-dataentry': 'Data Entry for:', + 'staff-announcer': 'Announcing for:', + 'staff-delegate': 'Delegating for:', + 'competitor': 'Competing in:' +}; + +export const generateIcs = (assignments, fileName) => { + if (!assignments) { + return; + } + + console.log(assignments); + + let events: { title: string; description: string; location: string; start: ics.DateArray; end: ics.DateArray }[] = []; + + assignments.forEach(item => { + const titleFormatted = `${AssignmentCodeDescription[item.assignmentCode]} for ${item.activity.name}`; + const startDate = new Date(item.activity.startTime); + const endDate = new Date(item.activity.endTime); + + const startDateArray: ics.DateArray = [ + startDate.getFullYear(), + startDate.getMonth() + 1, // Months are 1-based in ics format + startDate.getDate(), + startDate.getHours(), + startDate.getMinutes(), + ]; + + const endDateArray: ics.DateArray = [ + endDate.getFullYear(), + endDate.getMonth() + 1, + endDate.getDate(), + endDate.getHours(), + endDate.getMinutes(), + ];endDate + + const icalEvent = { + title: titleFormatted, + description: item.activity.name, + location: item.activity.parent.room.name, + start: startDateArray, + end: endDateArray, + }; + + events.push(icalEvent); + }); + + const { error, value } = ics.createEvents(events); + + if (error) { + console.log(error); + return; + } + + if (!value) { + return; + } + + const blob = new Blob([value], { type: 'text/calendar' }); + const a = document.createElement('a'); + a.href = URL.createObjectURL(blob); + a.download = `fileName`; + a.style.display = 'none'; + + document.body.appendChild(a); + a.click(); + + document.body.removeChild(a); + } \ No newline at end of file diff --git a/src/pages/Competition/Person/index.tsx b/src/pages/Competition/Person/index.tsx index 5cff093..f7debe5 100644 --- a/src/pages/Competition/Person/index.tsx +++ b/src/pages/Competition/Person/index.tsx @@ -7,7 +7,7 @@ import { Link, useParams } from 'react-router-dom'; import { useWCIF } from '../WCIFProvider'; import { ActivityWithRoomOrParent, parseActivityCode, rooms } from '../../../lib/activities'; import AssignmentLabel from '../../../components/AssignmentLabel/AssignmentLabel'; -import { formatDate, formatToParts, roundTime } from '../../../lib/utils'; +import { formatDate, formatToParts, roundTime, generateIcs } from '../../../lib/utils'; import DisclaimerText from '../../../components/DisclaimerText'; import { shortEventNameById } from '../../../lib/events'; import classNames from 'classnames'; @@ -445,6 +445,15 @@ export default function PersonPage() { ) : (
No Assignments
)} +
+ +
); } From e9e814b215f6b72a62712e97c1bf568c8fd5ea14 Mon Sep 17 00:00:00 2001 From: Nicholas McKee Date: Tue, 24 Oct 2023 18:27:56 -0400 Subject: [PATCH 2/5] run prettier --- package.json | 1 + src/lib/utils.ts | 27 +++++++++++++++++--------- src/pages/Competition/Person/index.tsx | 15 +++++++------- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index d71b790..c872934 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "clsx": "^1.1.1", "country-flag-icons": "^1.5.4", "date-fns": "^2.30.0", + "ics": "^3.5.0", "lodash.flatmap": "^4.5.0", "lodash.flatten": "^4.4.0", "react": "^18.2.0", diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 114ecdf..71c62ba 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -7,7 +7,7 @@ import { formatMultiResult, } from '@wca/helpers'; import { format, parseISO } from 'date-fns'; -import * as ics from 'ics' +import * as ics from 'ics'; export const byName = (a: { name: string }, b: { name: string }) => a.name.localeCompare(b.name); export const byDate = (a: T & { startTime: string }, b: T & { startTime: string }) => @@ -157,20 +157,28 @@ const AssignmentCodeDescription = { 'staff-dataentry': 'Data Entry for:', 'staff-announcer': 'Announcing for:', 'staff-delegate': 'Delegating for:', - 'competitor': 'Competing in:' + competitor: 'Competing in:', }; -export const generateIcs = (assignments, fileName) => { +export const generateIcs = (assignments, fileName) => { if (!assignments) { return; } console.log(assignments); - let events: { title: string; description: string; location: string; start: ics.DateArray; end: ics.DateArray }[] = []; - - assignments.forEach(item => { - const titleFormatted = `${AssignmentCodeDescription[item.assignmentCode]} for ${item.activity.name}`; + let events: { + title: string; + description: string; + location: string; + start: ics.DateArray; + end: ics.DateArray; + }[] = []; + + assignments.forEach((item) => { + const titleFormatted = `${AssignmentCodeDescription[item.assignmentCode]} for ${ + item.activity.name + }`; const startDate = new Date(item.activity.startTime); const endDate = new Date(item.activity.endTime); @@ -188,7 +196,8 @@ export const generateIcs = (assignments, fileName) => { endDate.getDate(), endDate.getHours(), endDate.getMinutes(), - ];endDate + ]; + endDate; const icalEvent = { title: titleFormatted, @@ -222,4 +231,4 @@ export const generateIcs = (assignments, fileName) => { a.click(); document.body.removeChild(a); - } \ No newline at end of file +}; diff --git a/src/pages/Competition/Person/index.tsx b/src/pages/Competition/Person/index.tsx index f7debe5..0294c61 100644 --- a/src/pages/Competition/Person/index.tsx +++ b/src/pages/Competition/Person/index.tsx @@ -446,14 +446,13 @@ export default function PersonPage() {
No Assignments
)}
- -
+ + ); } From 14377ae14d2bd3d5771fd2140da1582b44e2ba5c Mon Sep 17 00:00:00 2001 From: Nicholas McKee Date: Tue, 24 Oct 2023 19:01:40 -0400 Subject: [PATCH 3/5] More fixes and added geolocation --- src/lib/utils.ts | 27 +++++++++++++------------- src/pages/Competition/Person/index.tsx | 4 +++- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 71c62ba..eeea5f5 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -160,13 +160,12 @@ const AssignmentCodeDescription = { competitor: 'Competing in:', }; -export const generateIcs = (assignments, fileName) => { +export const generateIcs = (assignments, wcif, fileName: string) => { if (!assignments) { + //Check if assignments is empty return; } - console.log(assignments); - let events: { title: string; description: string; @@ -176,7 +175,7 @@ export const generateIcs = (assignments, fileName) => { }[] = []; assignments.forEach((item) => { - const titleFormatted = `${AssignmentCodeDescription[item.assignmentCode]} for ${ + const titleFormatted = `${AssignmentCodeDescription[item.assignmentCode]} ${ item.activity.name }`; const startDate = new Date(item.activity.startTime); @@ -184,7 +183,7 @@ export const generateIcs = (assignments, fileName) => { const startDateArray: ics.DateArray = [ startDate.getFullYear(), - startDate.getMonth() + 1, // Months are 1-based in ics format + startDate.getMonth() + 1, // Months are 1-indexed in ics format startDate.getDate(), startDate.getHours(), startDate.getMinutes(), @@ -197,12 +196,17 @@ export const generateIcs = (assignments, fileName) => { endDate.getHours(), endDate.getMinutes(), ]; - endDate; + + const location = { + lat: wcif.schedule.venues[0].latitudeMicrodegrees / 100, + lon: wcif.schedule.venues[0].longitudeMicrodegrees / 100, + }; const icalEvent = { title: titleFormatted, description: item.activity.name, location: item.activity.parent.room.name, + ...(wcif.schedule.venues.length > 1 ? {} : { geo: location }), start: startDateArray, end: endDateArray, }; @@ -212,19 +216,14 @@ export const generateIcs = (assignments, fileName) => { const { error, value } = ics.createEvents(events); - if (error) { - console.log(error); - return; - } - - if (!value) { - return; + if (error || !value) { + throw new Error('Failed to create ICS events'); } const blob = new Blob([value], { type: 'text/calendar' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); - a.download = `fileName`; + a.download = fileName; a.style.display = 'none'; document.body.appendChild(a); diff --git a/src/pages/Competition/Person/index.tsx b/src/pages/Competition/Person/index.tsx index 0294c61..8053097 100644 --- a/src/pages/Competition/Person/index.tsx +++ b/src/pages/Competition/Person/index.tsx @@ -447,7 +447,9 @@ export default function PersonPage() { )}
+
+ ) : ( -
No Assignments
+
+
No Assignments
+
)} -
- -
); } From 5739ae527d4270570545336abc3f286b103b9284 Mon Sep 17 00:00:00 2001 From: Nicholas McKee Date: Mon, 30 Oct 2023 13:34:31 -0400 Subject: [PATCH 5/5] Code cleanup --- src/lib/utils.ts | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 3008be2..3a319df 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -160,12 +160,18 @@ const AssignmentCodeDescription = { competitor: 'Competing in:', }; -export const generateIcs = (assignments, wcif, fileName: string) => { - if (!assignments) { - //Check if assignments is empty - return; - } +const createDateArray = (date: Date) => { + const dateArray: ics.DateArray = [ + date.getFullYear(), + date.getMonth() + 1, // Months are 1-indexed in ics format + date.getDate(), + date.getHours(), + date.getMinutes(), + ]; + return dateArray; +}; +export const generateIcs = (assignments, wcif, fileName: string) => { let events: { title: string; description: string; @@ -189,21 +195,8 @@ export const generateIcs = (assignments, wcif, fileName: string) => { }, ]; - const startDateArray: ics.DateArray = [ - startDate.getFullYear(), - startDate.getMonth() + 1, // Months are 1-indexed in ics format - startDate.getDate(), - startDate.getHours(), - startDate.getMinutes(), - ]; - - const endDateArray: ics.DateArray = [ - endDate.getFullYear(), - endDate.getMonth() + 1, - endDate.getDate(), - endDate.getHours(), - endDate.getMinutes(), - ]; + const startDateArray = createDateArray(startDate); + const endDateArray = createDateArray(endDate); const location = { lat: wcif.schedule.venues[0].latitudeMicrodegrees / 100,