feat: rework the UI
This commit is contained in:
parent
139d7b40b1
commit
7674dc956a
4 changed files with 115 additions and 27 deletions
35
package-lock.json
generated
35
package-lock.json
generated
|
@ -43,7 +43,6 @@
|
||||||
"version": "5.10.1",
|
"version": "5.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@fullcalendar/common/-/common-5.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/@fullcalendar/common/-/common-5.10.1.tgz",
|
||||||
"integrity": "sha512-EumKIJcQTvQdTs75/9dmeREFgjcRVWzqHJS1Xvlz5mNsmB+w9EINCHETRjChtAQg1WD/lTQyVj4sHsKO7vCMSw==",
|
"integrity": "sha512-EumKIJcQTvQdTs75/9dmeREFgjcRVWzqHJS1Xvlz5mNsmB+w9EINCHETRjChtAQg1WD/lTQyVj4sHsKO7vCMSw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
|
@ -80,6 +79,15 @@
|
||||||
"tslib": "^2.1.0"
|
"tslib": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@fullcalendar/rrule": {
|
||||||
|
"version": "5.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fullcalendar/rrule/-/rrule-5.10.1.tgz",
|
||||||
|
"integrity": "sha512-K5TO8298eVkZiJ70hZrAvAAP81aTMiymgPW1nnaOkflI4bvEDJlkGdFe1MqgE07oTBZca7VU7/33ePiSTs0Pcg==",
|
||||||
|
"requires": {
|
||||||
|
"@fullcalendar/common": "~5.10.1",
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@fullcalendar/timegrid": {
|
"@fullcalendar/timegrid": {
|
||||||
"version": "5.10.1",
|
"version": "5.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-5.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-5.10.1.tgz",
|
||||||
|
@ -1339,6 +1347,12 @@
|
||||||
"yallist": "^4.0.0"
|
"yallist": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"luxon": {
|
||||||
|
"version": "1.28.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz",
|
||||||
|
"integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"magic-string": {
|
"magic-string": {
|
||||||
"version": "0.25.7",
|
"version": "0.25.7",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
|
||||||
|
@ -2209,6 +2223,22 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"rrule": {
|
||||||
|
"version": "2.6.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/rrule/-/rrule-2.6.8.tgz",
|
||||||
|
"integrity": "sha512-cUaXuUPrz9d1wdyzHsBfT1hptKlGgABeCINFXFvulEPqh9Np9BnF3C3lrv9uO54IIr8VDb58tsSF3LhsW+4VRw==",
|
||||||
|
"requires": {
|
||||||
|
"luxon": "^1.21.3",
|
||||||
|
"tslib": "^1.10.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "1.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||||
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"sade": {
|
"sade": {
|
||||||
"version": "1.7.4",
|
"version": "1.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/sade/-/sade-1.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/sade/-/sade-1.7.4.tgz",
|
||||||
|
@ -2582,8 +2612,7 @@
|
||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
|
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"undici": {
|
"undici": {
|
||||||
"version": "4.14.1",
|
"version": "4.14.1",
|
||||||
|
|
|
@ -27,8 +27,10 @@
|
||||||
"svelte-fullcalendar": "^1.1.1"
|
"svelte-fullcalendar": "^1.1.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fullcalendar/rrule": "^5.10.1",
|
||||||
"@nextcloud/cdav-library": "^1.0.0",
|
"@nextcloud/cdav-library": "^1.0.0",
|
||||||
"ical.js": "^1.5.0",
|
"ical.js": "^1.5.0",
|
||||||
|
"rrule": "^2.6.8",
|
||||||
"sirv-cli": "^1.0.0"
|
"sirv-cli": "^1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,26 @@
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import FullCalendar from 'svelte-fullcalendar';
|
import FullCalendar from 'svelte-fullcalendar';
|
||||||
import timeGridPlugin from '@fullcalendar/timegrid';
|
import timeGridPlugin from '@fullcalendar/timegrid';
|
||||||
|
import rrulePlugin from '@fullcalendar/rrule';
|
||||||
|
import dayGridPlugin from '@fullcalendar/daygrid';
|
||||||
import frLocale from '@fullcalendar/core/locales/fr';
|
import frLocale from '@fullcalendar/core/locales/fr';
|
||||||
import { refreshEvents } from './calendar';
|
import { refreshEvents, calendarTree } from './calendar';
|
||||||
|
|
||||||
let options = {
|
let options = {
|
||||||
initialView: 'timeGridWeek',
|
initialView: 'timeGridWeek',
|
||||||
plugins: [timeGridPlugin],
|
plugins: [timeGridPlugin, dayGridPlugin, rrulePlugin],
|
||||||
locale: frLocale,
|
locale: frLocale,
|
||||||
|
height: "100%",
|
||||||
|
schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives',
|
||||||
|
nowIndicator: true,
|
||||||
|
now: new Date(),
|
||||||
events: []
|
events: []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let selectedCalendars;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const evts = await refreshEvents();
|
const evts = await refreshEvents(selectedCalendars);
|
||||||
options.events = evts.flat();
|
options.events = evts.flat();
|
||||||
options = { ...options };
|
options = { ...options };
|
||||||
});
|
});
|
||||||
|
@ -21,15 +29,15 @@
|
||||||
$: console.log(options);
|
$: console.log(options);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div style="height:100vh">
|
<div class="app-container">
|
||||||
|
<h1 class="title">Calendrier de la vie étudiante à l'ENS</h1>
|
||||||
|
<div style="height: 100%;">
|
||||||
|
<!-- <FilterBar {calendarTree} {selectedCalendars}> -->
|
||||||
<FullCalendar {options} />
|
<FullCalendar {options} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
|
@ -37,11 +45,14 @@
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
.title {
|
||||||
color: #ff3e00;
|
text-align: center;
|
||||||
text-transform: uppercase;
|
}
|
||||||
font-size: 4em;
|
|
||||||
font-weight: 100;
|
.app-container {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
@media (min-width: 640px) {
|
||||||
|
|
|
@ -1,9 +1,24 @@
|
||||||
import ICAL from 'ical.js';
|
import ICAL from 'ical.js';
|
||||||
|
|
||||||
const calendarIds = [
|
const calendars = {
|
||||||
"5WrcagPPARQ3BD87",
|
"5WrcagPPARQ3BD87": {
|
||||||
"TFEAKjAgNFQZpNjo"
|
name: "Club réseau",
|
||||||
];
|
color: null
|
||||||
|
},
|
||||||
|
"TFEAKjAgNFQZpNjo": {
|
||||||
|
name: "hackENS",
|
||||||
|
color: null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const calendarTree = {
|
||||||
|
"COF": [
|
||||||
|
"Club réseau",
|
||||||
|
"hackENS"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const calendarIds = Object.keys(calendars);
|
||||||
|
|
||||||
function mkCalendarUrl(id) {
|
function mkCalendarUrl(id) {
|
||||||
return `/cal/${id}/?export&accept=jcal`;
|
return `/cal/${id}/?export&accept=jcal`;
|
||||||
|
@ -14,8 +29,10 @@ function fetchCalendar(id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Calendar {
|
class Calendar {
|
||||||
constructor (calendar) {
|
constructor (id, calendar) {
|
||||||
this.calName = calendar[1][3][3];
|
const metadata = calendars[id];
|
||||||
|
this.name = metadata.name;
|
||||||
|
this.color = metadata.color || calendar[1][4][3];
|
||||||
this.events = calendar[2].filter(item => item[0] === 'vevent').map(item => this._parse_vevent(item[1]));
|
this.events = calendar[2].filter(item => item[0] === 'vevent').map(item => this._parse_vevent(item[1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,11 +45,39 @@ class Calendar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fcEventFromjCalEvent(cal) {
|
||||||
|
return function (evt) {
|
||||||
|
const start = new Date(evt.dtstart);
|
||||||
|
const end = new Date(evt.dtend);
|
||||||
|
const fcEvent = {
|
||||||
|
title: `${cal.name}: ${evt.summary}`,
|
||||||
|
start,
|
||||||
|
color: cal.color,
|
||||||
|
duration: end - start // in ms
|
||||||
|
};
|
||||||
|
|
||||||
|
if (evt.description) {
|
||||||
|
fcEvent.description = evt.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt.rrule) {
|
||||||
|
const { freq, byday } = evt.rrule;
|
||||||
|
fcEvent.rrule = {
|
||||||
|
freq,
|
||||||
|
byweekday: byday,
|
||||||
|
dtstart: evt.dtstart,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return fcEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function mkEventsFromCalendar(id) {
|
function mkEventsFromCalendar(id) {
|
||||||
return fetchCalendar(id).then(calendar => {
|
return fetchCalendar(id).then(calendar => {
|
||||||
if (calendar[0] !== 'vcalendar') return;
|
if (calendar[0] !== 'vcalendar') return;
|
||||||
const cal = new Calendar(calendar)
|
const cal = new Calendar(id, calendar)
|
||||||
return cal.events.map(evt => ({ title: evt.summary, start: new Date(evt.dtstart), end: new Date(evt.dtend) }))
|
return cal.events.map(fcEventFromjCalEvent(cal))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +93,7 @@ export function mkEvent(title, start, duration, ...rest) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function refreshEvents() {
|
// TODO: move to FullCalendar custom fetcher to control start&end.
|
||||||
return Promise.all(calendarIds.map(mkEventsFromCalendar))
|
export function refreshEvents(selectedCalendars) {
|
||||||
|
return Promise.all(calendarIds.filter(id => selectedCalendars ? selectedCalendars.includes(id) : true).map(mkEventsFromCalendar))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue