diff --git a/README.md b/README.md index 6d3b883..9cdd4e9 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ env = GRIST_ROOT_URL=https://grist.tiker.net env = GRIST_API_KEY_FILE=/home/grist-av/.grist-api-key env = GRIST_DOC_ID=rLJPGJ9RLJ4TRVx4AxT2tW env = SECRET_KEY=CHANGE_ME +env = CAL_TIMEZONES=America/Chicago,local,UTC # Optional. Only effective if both are provided. env = NOTIFY_FROM=andreask@illinois.edu diff --git a/availability/app.py b/availability/app.py index 00aa86a..6b9b942 100644 --- a/availability/app.py +++ b/availability/app.py @@ -279,6 +279,9 @@ def render_calendar( if slots is None: slots = [] + timezones = [tzname.strip() + for tzname in os.environ.get("CAL_TIMEZONES", "local,UTC").split(",")] + initial_date = None last_date = None events = [] @@ -322,8 +325,8 @@ def render_calendar( if has_spans: for span in spans: events.append({ - "start": span.start.isoformat(), - "end": span.end.isoformat(), + "start": span.start.timestamp() * 1000, + "end": span.end.timestamp() * 1000, "editable": True, "extendedProps": { "type": "span", @@ -350,7 +353,8 @@ def render_calendar( js_url=url_for("static", filename="availability.js"), has_slots=has_slots, allow_maybe=av_request.allow_maybe, - has_spans=has_spans) + has_spans=has_spans, + timezones=timezones) def send_notify(av_request: AvailabilityRequest, text_response: str, diff --git a/availability/static/availability.js b/availability/static/availability.js index 3355436..044c8eb 100644 --- a/availability/static/availability.js +++ b/availability/static/availability.js @@ -53,22 +53,29 @@ function calSelect(info) { styleEvent(ev); } +function fullcalDateToISO(date, calendar) { + // eslint-disable-next-line no-unused-vars + return FullCalendar.Luxon3.toLuxonDateTime(date, calendar).toISO(); +} + function onSubmit() { const slots = []; const spans = []; - document.calendarInstance.getEvents().forEach((ev) => { + const cal = document.calendarInstance; + + cal.getEvents().forEach((ev) => { if (ev.extendedProps.type === 'slot') { slots.push({ rspan_id: ev.extendedProps.rspan_id, - start: ev.start, - end: ev.end, + start: fullcalDateToISO(ev.start, cal), + end: fullcalDateToISO(ev.end, cal), available: ev.extendedProps.available, }); } else if (ev.extendedProps.type === 'span') { spans.push({ - start: ev.start, - end: ev.end, + start: fullcalDateToISO(ev.start, cal), + end: fullcalDateToISO(ev.end, cal), maybe: ev.extendedProps.maybe, }); } @@ -84,7 +91,7 @@ function onSubmit() { } // eslint-disable-next-line no-unused-vars -function initialize(initialDate, nDays, events, hasSpans) { +function initialize(initialDate, nDays, events, hasSpans, timezones) { document.addEventListener( 'DOMContentLoaded', () => { @@ -92,6 +99,7 @@ function initialize(initialDate, nDays, events, hasSpans) { // eslint-disable-next-line no-undef const calendar = new FullCalendar.Calendar(calendarEl, { // plugins: [timeGridPlugin], + timeZone: timezones[0], initialView: 'timeGridNDay', headerToolbar: { start: false, @@ -122,6 +130,25 @@ function initialize(initialDate, nDays, events, hasSpans) { document.calendarInstance = calendar; document.getElementById('submitButton').addEventListener('click', onSubmit); + document.getElementById('calPreviousButton').addEventListener('click', () => { + calendar.prev(); + }); + document.getElementById('calNextButton').addEventListener('click', () => { + calendar.next(); + }); + + const tzSelect = document.getElementById('timezone'); + for (let i = 0; i < timezones.length; i += 1) { + const opt = document.createElement('option'); + const tzName = timezones[i]; + opt.value = tzName; + opt.innerHTML = tzName; + tzSelect.appendChild(opt); + } + + tzSelect.addEventListener('change', () => { + calendar.setOption('timeZone', tzSelect.value); + }); }, ); } diff --git a/availability/templates/base.html b/availability/templates/base.html index 88e4492..bd8fa56 100644 --- a/availability/templates/base.html +++ b/availability/templates/base.html @@ -5,6 +5,7 @@ + {% block in_head %} {% endblock %} diff --git a/availability/templates/index.html b/availability/templates/index.html index bd785ba..7f808e3 100644 --- a/availability/templates/index.html +++ b/availability/templates/index.html @@ -1,14 +1,17 @@ {% extends "base.html" %} {% block in_head %} - + + + {% endblock %} @@ -37,8 +40,6 @@
Instructions
  • For time slots shown in blue, please click to indicate your availability (green) or unavailability (red).
  • {% endif %} -
  • The calendar is shown in the local time zone known to your browser. - So please fill out the poll using your own local times.
  • If using this on a touch device: To create a time range: hold on a time until a highlight appears, then drag. To edit a time range: hold on that time range until selected, then drag @@ -49,17 +50,34 @@
    Instructions
    Poll for {{ av_request.name }}
    -
    - + {% if av_request.message %}
    {{ av_request.message }}
    {% endif %}
    +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    -
    {% endblock %} diff --git a/dev.sh b/dev.sh index fd31375..9c2d915 100755 --- a/dev.sh +++ b/dev.sh @@ -8,6 +8,8 @@ export GRIST_DOC_ID="s7VzXiAHXbwgivucYprb6z" export NOTIFY_FROM="inform@tiker.net" export NOTIFY_TO="inform@tiker.net" +export CAL_TIMEZONES="America/Chicago,local,UTC" + export SECRET_KEY="CHANGE_ME" poetry run flask --app=availability.app run --debug