diff --git a/package-lock.json b/package-lock.json index be2c8be..b4397d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@fullcalendar/adaptive": "^5.10.1", "@fullcalendar/bootstrap5": "^5.10.2", "@fullcalendar/list": "^5.10.1", + "@fullcalendar/resource-timeline": "^5.10.1", "@fullcalendar/rrule": "^5.10.1", "@nextcloud/cdav-library": "^1.0.0", "bootstrap": "^5.1.3", @@ -27,6 +28,7 @@ "@fullcalendar/daygrid": "^5.10.1", "@fullcalendar/timegrid": "^5.10.1", "@rollup/plugin-commonjs": "^17.0.0", + "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^11.0.0", "dav": "^1.8.0", "postcss": "^8.3.11", @@ -166,6 +168,29 @@ "tslib": "^2.1.0" } }, + "node_modules/@fullcalendar/resource-common": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource-common/-/resource-common-5.10.1.tgz", + "integrity": "sha512-20JR8cucAeJEXSbWVSj9USwsPGKb3dVQr8CBiXuHPbD0OLK93j7jhKjBlp/pRldtcJW9mIXC8ENQvw/aNZJ9Cw==", + "dependencies": { + "@fullcalendar/common": "~5.10.1", + "@fullcalendar/premium-common": "~5.10.1", + "tslib": "^2.1.0" + } + }, + "node_modules/@fullcalendar/resource-timeline": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource-timeline/-/resource-timeline-5.10.1.tgz", + "integrity": "sha512-gsqjr6Z+LQcNbQlHgaTtg/kF8l6yDRtIuMQbhFlTy71RJI//x2mHhLXgV40FJeEE+srp48xJPd89+rIhlyJ5Tw==", + "dependencies": { + "@fullcalendar/common": "~5.10.1", + "@fullcalendar/premium-common": "~5.10.1", + "@fullcalendar/resource-common": "~5.10.1", + "@fullcalendar/scrollgrid": "~5.10.1", + "@fullcalendar/timeline": "~5.10.1", + "tslib": "^2.1.0" + } + }, "node_modules/@fullcalendar/rrule": { "version": "5.10.1", "resolved": "https://registry.npmjs.org/@fullcalendar/rrule/-/rrule-5.10.1.tgz", @@ -178,6 +203,16 @@ "rrule": "^2.6.0" } }, + "node_modules/@fullcalendar/scrollgrid": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@fullcalendar/scrollgrid/-/scrollgrid-5.10.1.tgz", + "integrity": "sha512-Hj6gzj2/sUUnozIMC0GBK60/ZTeDchp/Gc2S4F+05W6V4BoUXbIwav+EdAerfNFOv7EXWa9vM9Lq3A53iVWFVg==", + "dependencies": { + "@fullcalendar/common": "~5.10.1", + "@fullcalendar/premium-common": "~5.10.1", + "tslib": "^2.1.0" + } + }, "node_modules/@fullcalendar/timegrid": { "version": "5.10.1", "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-5.10.1.tgz", @@ -189,6 +224,17 @@ "tslib": "^2.1.0" } }, + "node_modules/@fullcalendar/timeline": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@fullcalendar/timeline/-/timeline-5.10.1.tgz", + "integrity": "sha512-pFMhK4nsCvpsA63GPJQtT1RSS2OLlT9a2+fvsf2oQregBLcottJSlCjIsIuKP7hpQLimaSdLr2kNjh5hs8jKlw==", + "dependencies": { + "@fullcalendar/common": "~5.10.1", + "@fullcalendar/premium-common": "~5.10.1", + "@fullcalendar/scrollgrid": "~5.10.1", + "tslib": "^2.1.0" + } + }, "node_modules/@hapi/hoek": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", @@ -283,6 +329,18 @@ "rollup": "^2.30.0" } }, + "node_modules/@rollup/plugin-json": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz", + "integrity": "sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.0.8" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, "node_modules/@rollup/plugin-node-resolve": { "version": "11.2.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", @@ -4124,6 +4182,29 @@ "tslib": "^2.1.0" } }, + "@fullcalendar/resource-common": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource-common/-/resource-common-5.10.1.tgz", + "integrity": "sha512-20JR8cucAeJEXSbWVSj9USwsPGKb3dVQr8CBiXuHPbD0OLK93j7jhKjBlp/pRldtcJW9mIXC8ENQvw/aNZJ9Cw==", + "requires": { + "@fullcalendar/common": "~5.10.1", + "@fullcalendar/premium-common": "~5.10.1", + "tslib": "^2.1.0" + } + }, + "@fullcalendar/resource-timeline": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@fullcalendar/resource-timeline/-/resource-timeline-5.10.1.tgz", + "integrity": "sha512-gsqjr6Z+LQcNbQlHgaTtg/kF8l6yDRtIuMQbhFlTy71RJI//x2mHhLXgV40FJeEE+srp48xJPd89+rIhlyJ5Tw==", + "requires": { + "@fullcalendar/common": "~5.10.1", + "@fullcalendar/premium-common": "~5.10.1", + "@fullcalendar/resource-common": "~5.10.1", + "@fullcalendar/scrollgrid": "~5.10.1", + "@fullcalendar/timeline": "~5.10.1", + "tslib": "^2.1.0" + } + }, "@fullcalendar/rrule": { "version": "5.10.1", "resolved": "https://registry.npmjs.org/@fullcalendar/rrule/-/rrule-5.10.1.tgz", @@ -4133,6 +4214,16 @@ "tslib": "^2.1.0" } }, + "@fullcalendar/scrollgrid": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@fullcalendar/scrollgrid/-/scrollgrid-5.10.1.tgz", + "integrity": "sha512-Hj6gzj2/sUUnozIMC0GBK60/ZTeDchp/Gc2S4F+05W6V4BoUXbIwav+EdAerfNFOv7EXWa9vM9Lq3A53iVWFVg==", + "requires": { + "@fullcalendar/common": "~5.10.1", + "@fullcalendar/premium-common": "~5.10.1", + "tslib": "^2.1.0" + } + }, "@fullcalendar/timegrid": { "version": "5.10.1", "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-5.10.1.tgz", @@ -4144,6 +4235,17 @@ "tslib": "^2.1.0" } }, + "@fullcalendar/timeline": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/@fullcalendar/timeline/-/timeline-5.10.1.tgz", + "integrity": "sha512-pFMhK4nsCvpsA63GPJQtT1RSS2OLlT9a2+fvsf2oQregBLcottJSlCjIsIuKP7hpQLimaSdLr2kNjh5hs8jKlw==", + "requires": { + "@fullcalendar/common": "~5.10.1", + "@fullcalendar/premium-common": "~5.10.1", + "@fullcalendar/scrollgrid": "~5.10.1", + "tslib": "^2.1.0" + } + }, "@hapi/hoek": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", @@ -4216,6 +4318,15 @@ "resolve": "^1.17.0" } }, + "@rollup/plugin-json": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-4.1.0.tgz", + "integrity": "sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.0.8" + } + }, "@rollup/plugin-node-resolve": { "version": "11.2.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", diff --git a/package.json b/package.json index 9c46cd2..843dd97 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@fullcalendar/daygrid": "^5.10.1", "@fullcalendar/timegrid": "^5.10.1", "@rollup/plugin-commonjs": "^17.0.0", + "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^11.0.0", "dav": "^1.8.0", "postcss": "^8.3.11", @@ -33,6 +34,7 @@ "@fullcalendar/adaptive": "^5.10.1", "@fullcalendar/bootstrap5": "^5.10.2", "@fullcalendar/list": "^5.10.1", + "@fullcalendar/resource-timeline": "^5.10.1", "@fullcalendar/rrule": "^5.10.1", "@nextcloud/cdav-library": "^1.0.0", "bootstrap": "^5.1.3", diff --git a/rollup.config.js b/rollup.config.js index 53c1a1e..6bc4c2e 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -7,6 +7,7 @@ import css from 'rollup-plugin-css-only' import postcss from 'rollup-plugin-postcss' import dev from 'rollup-plugin-dev' import copy from 'rollup-plugin-copy' +import json from '@rollup/plugin-json'; const production = !process.env.ROLLUP_WATCH @@ -48,6 +49,8 @@ export default { } }), + json(), + // Copy font files copy({ targets: [ diff --git a/src/App.svelte b/src/App.svelte index 359568f..0cff600 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -7,6 +7,7 @@ import rrulePlugin from '@fullcalendar/rrule'; import dayGridPlugin from '@fullcalendar/daygrid'; import listPlugin from '@fullcalendar/list'; + import resourceTimelinePlugin from '@fullcalendar/resource-timeline'; import frLocale from '@fullcalendar/core/locales/fr'; import EventModal from './EventModal.svelte'; import FilterBar from './FilterBar.svelte'; @@ -18,6 +19,8 @@ import 'bootstrap-icons/font/bootstrap-icons.css'; import bootstrap5Plugin from '@fullcalendar/bootstrap5'; + import ENSLocations from './static-ens-locations.json'; + const event = writable(null); let openModal = false; @@ -36,12 +39,12 @@ ? { left: 'title', center: 'prev,today,next', - right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek' + right: 'resourceTimelineDay dayGridMonth,timeGridWeek,timeGridDay,listWeek' } : { left: 'prev,next today', center: 'title', - right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek' + right: 'resourceTimelineDay dayGridMonth,timeGridWeek,timeGridDay,listWeek' }; let calendar; @@ -53,12 +56,24 @@ dayGridPlugin, rrulePlugin, listPlugin, + resourceTimelinePlugin, adaptivePlugin, bootstrap5Plugin ], locale: frLocale, allDayContent: '', headerToolbar: headers, + buttonText: { resourceTimelineDay: 'Salles' }, + scrollTime: "08:00:00", + resourceGroupField: 'building', + resourceAreaWidth: '27%', + resources: Object.entries(ENSLocations).flatMap(([building, rooms]) => + rooms.map(room => ({ + id: `${building}-${room}`, + building, + title: room + })) + ), height: '100%', schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives', nowIndicator: true, @@ -76,7 +91,8 @@ eventSources: [], themeSystem: 'bootstrap5', nextDayThreshold: '05:00:00', - progressiveEventRendering: true + progressiveEventRendering: true, + expandRows: true }); const flatten = d => { diff --git a/src/calendar.js b/src/calendar.js index 8b0aca2..c386ad9 100644 --- a/src/calendar.js +++ b/src/calendar.js @@ -1,3 +1,5 @@ +import STATIC_LOCATIONS from './static-ens-locations.json' + // https://stackoverflow.com/a/35970186 function invertColor(hex) { if (hex.indexOf('#') === 0) { @@ -92,7 +94,7 @@ const calendars = { color: null, initial: false }, - 'Ekjb4kDqMMqwJXZF': { + Ekjb4kDqMMqwJXZF: { cloud: clouds.ELEVES_ENS, name: 'Rentrée des départements', short_name: 'Dpt', @@ -104,20 +106,20 @@ const calendars = { short_name: 'Conf', color: null }, - 'PnRXqeq4SsSC33FM': { + PnRXqeq4SsSC33FM: { cloud: clouds.ELEVES_ENS, name: 'Visites de bibliothèques', short_name: 'Bibli', initial: false, color: null }, - 'NWPtiEiz62LTtjo2': { + NWPtiEiz62LTtjo2: { cloud: clouds.ELEVES_ENS, name: 'Amphis de rentrée', short_name: 'Prés. de rentrée', color: null }, - 'JiRt58aJXay9kfyk': { + JiRt58aJXay9kfyk: { cloud: clouds.ELEVES_ENS, name: 'Réunions de rentrée des Masters', short_name: 'Masters', @@ -171,7 +173,7 @@ export const calendarTree = { 'Réunions de rentrée des Masters': {}, 'Activités pour les étudiants internationaux': {} }, - 'Divers': {} + Divers: {} } export function getSubCalendars(name, tree = calendarTree) { @@ -231,6 +233,26 @@ class Calendar { } } +function findLocationId(location) { + const adhocMap = { + 'Amphi Jourdan': 'Amphithéâtre Jourdan', + 'R2-21 (Jourdan)': 'R2-21', + 'Salle Jean Ibanes (Jourdan, R1-07)': 'Salle Jean Ibanes (R1-07)', + 'Salle Madeleine Rebérioux (Jourdan, R2-02)': 'Salle Madeleine Rebérioux (R2-02)', + 'Salle Marcel Roncayolo (Jourdan, R2-05)': 'Salle Marcel Roncayolo (R2-05)' + }; + + const correctedLocation = adhocMap[location] || location; + + const result = Object.entries(STATIC_LOCATIONS).find(([building, rooms]) => + rooms.includes(correctedLocation) + ) + + if (result === undefined) return undefined + const [building, _] = result + return `${building}-${correctedLocation}` +} + function fcEventFromjCalEvent(cal) { return function (evt) { const start = new Date(evt.dtstart) @@ -249,6 +271,10 @@ function fcEventFromjCalEvent(cal) { fcEvent.description = evt.description fcEvent.location = evt.location || cal.default_location + if (fcEvent.location) { + fcEvent.resourceId = findLocationId(fcEvent.location) + } + if (evt.status) { fcEvent.status = evt.status fcEvent.classNames = [`st-${evt.status.toLowerCase()}`] diff --git a/src/static-ens-locations.json b/src/static-ens-locations.json new file mode 100644 index 0000000..ea6f529 --- /dev/null +++ b/src/static-ens-locations.json @@ -0,0 +1,34 @@ +{ + "45 rue d'Ulm": [ + "Amphithéâtre Galois", + "Bibliothèque Lettres", + "Salle Histoire", + "Salle Cavaillès", + "Salle Dussane", + "Salle des Actes", + "Salle des Résistants", + "Salle Cavaillès", + "Salle Cartan", + "Salle Noether", + "Salle Bourbaki", + "Cour aux Ernests", + "Cour du NIR", + "Cour Pasteur", + "Pôt", + "Petit pôt", + "Canopée", + "K-Fêt", + "Cave d'hackENS", + "Gymnase" + ], + "24 rue Lhomond": ["Salle CONF IV"], + "29 rue d'Ulm": ["Bibliothèque des sciences expérimentales", "Salle Jaurès"], + "Jourdan": [ + "Bibliothèque de Jourdan", + "Salle Marcel Roncayolo (R2-05)", + "Salle Madeleine Rebérioux (R2-02)", + "Amphithéâtre Jourdan", + "R2-21", + "Salle Jean Ibanes" + ] +}