diff --git a/src/common.ts b/src/common.ts new file mode 100644 index 0000000..efbc740 --- /dev/null +++ b/src/common.ts @@ -0,0 +1,84 @@ +import { Calendar, CalendarOptions } from '@fullcalendar/core'; +import iCalendarPlugin from '@fullcalendar/icalendar'; +import resourceTimeGridPlugin from '@fullcalendar/resource-timegrid'; + +import { unique_colors } from 'unique-colors'; + +const calendars: { [key: string]: string } = { + computer_lab: '6mmjp85e4732ru6skf1dda54ls@group.calendar.google.com', + electronics: '1g8atbdschshrg2inf162rcqt4@group.calendar.google.com', + wood_shop: '4unv3ia1n9mc9u31n2n5lv8nd8@group.calendar.google.com', + fiber_arts_studio: '7gbndciog37ge0hd8ug33ml70k@group.calendar.google.com', + jewelry_studio: 'l0dl2jq3vhbi9f4lfmaf5negc0@group.calendar.google.com', + metal_shop: 'a4p97kiiafatqdr52c3a0cpre0@group.calendar.google.com', + room_reservations: 'f4ro53uklj2u6pr0se7ucskm6g@group.calendar.google.com', +}; + +const colors: string[] = unique_colors(Object.keys(calendars).length); + +export const common_calendarOptions: CalendarOptions = { + schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives', + plugins: [iCalendarPlugin, resourceTimeGridPlugin], + allDaySlot: false, + nowIndicator: true, + headerToolbar: { start: '', center: 'title', end: '' }, + initialView: 'resourceTimeGrid', + height: 'auto', + slotMinTime: '8:00', + slotMaxTime: '22:00', + businessHours: { + daysOfWeek: [0, 1, 2, 3, 4, 5, 6], + startTime: '10:00', + endTime: '21:00', + }, + slotLabelFormat: { + hour: 'numeric', + minute: '2-digit', + hour12: false, + }, + eventTimeFormat: { + hour: 'numeric', + minute: '2-digit', + hour12: false, + }, +}; + +export function main( + calendarOptions: CalendarOptions, + allTools: boolean = false +) { + const calendarEl = document.getElementById('calendar'); + const calendar = new Calendar(calendarEl!, calendarOptions); + Object.values(calendars).forEach((id, idx) => + calendar.addEventSource({ + url: '/calendar/ical/' + id + '/public/basic.ics', + format: 'ics', + color: colors[idx], + eventDataTransform: (eventData) => { + // clear the url to prevent clicking on the event + delete eventData.url; + + const match = eventData?.title?.match(/([^\/]*) \| ([^-]*) - (.*)/); + if (match) { + const [, member, shop, tool] = match; + eventData.title = `${member}`; + eventData.resourceId = tool; + if (allTools) { + calendar.addResource({ id: tool, title: tool }, false); + } + } + return eventData; + }, + }) + ); + + calendar.render(); + + function refresh() { + calendar.refetchEvents(); + calendar.today(); + } + + // refresh data every five minutes + window.setInterval(refresh, 5 * 60 * 1000); +} diff --git a/src/index.ts b/src/index.ts index 7f69ac5..3d55323 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,107 +1,5 @@ -import 'core-js/stable/url'; -import 'core-js/stable/function'; - -import Intl from 'intl'; -import 'intl/locale-data/jsonp/en.js'; -window.Intl = Intl; - -import { Calendar, CalendarOptions } from '@fullcalendar/core'; -import iCalendarPlugin from '@fullcalendar/icalendar'; -import resourceTimeGridPlugin from '@fullcalendar/resource-timegrid'; - -import { unique_colors } from 'unique-colors'; +import { common_calendarOptions, main } from './common'; import './index.html'; -import './index.css'; -const calendars: { [key: string]: string } = { - computer_lab: '6mmjp85e4732ru6skf1dda54ls@group.calendar.google.com', - electronics: '1g8atbdschshrg2inf162rcqt4@group.calendar.google.com', - wood_shop: '4unv3ia1n9mc9u31n2n5lv8nd8@group.calendar.google.com', - fiber_arts_studio: '7gbndciog37ge0hd8ug33ml70k@group.calendar.google.com', - jewelry_studio: 'l0dl2jq3vhbi9f4lfmaf5negc0@group.calendar.google.com', - metal_shop: 'a4p97kiiafatqdr52c3a0cpre0@group.calendar.google.com', - room_reservations: 'f4ro53uklj2u6pr0se7ucskm6g@group.calendar.google.com', -}; - -const colors: string[] = unique_colors(Object.keys(calendars).length); - -const urlParams = new URLSearchParams(window.location.search); -const toolFilter: string[] | undefined = urlParams.get('tool')?.split(';'); - -const calendarOptions: CalendarOptions = { - schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives', - plugins: [iCalendarPlugin, resourceTimeGridPlugin], - allDaySlot: false, - nowIndicator: true, - headerToolbar: { start: '', center: 'title', end: '' }, - initialView: 'resourceTimeGrid', - height: 'auto', - slotMinTime: '8:00', - slotMaxTime: '22:00', - businessHours: { - daysOfWeek: [0, 1, 2, 3, 4, 5, 6], - startTime: '10:00', - endTime: '21:00', - }, - slotLabelFormat: { - hour: 'numeric', - minute: '2-digit', - hour12: false, - }, - eventTimeFormat: { - hour: 'numeric', - minute: '2-digit', - hour12: false, - }, - resources: toolFilter - ? toolFilter.map((tool) => { - return { - id: tool, - title: tool, - }; - }) - : [], -}; - -function main() { - const calendarEl = document.getElementById('calendar'); - const calendar = new Calendar(calendarEl!, calendarOptions); - Object.values(calendars).forEach((id, idx) => - calendar.addEventSource({ - url: '/calendar/ical/' + id + '/public/basic.ics', - format: 'ics', - color: colors[idx], - eventDataTransform: (eventData) => { - // clear the url to prevent clicking on the event - delete eventData.url; - - const match = eventData?.title?.match(/([^\/]*) \| ([^-]*) - (.*)/); - if (match) { - const [, member, shop, tool] = match; - eventData.title = `${member}`; - eventData.resourceId = tool; - if (!toolFilter) { - calendar.addResource({ id: tool, title: tool }, false); - } - } - return eventData; - }, - }) - ); - - calendar.render(); - - function refresh() { - calendar.refetchEvents(); - calendar.today(); - } - - // refresh data every five minutes - window.setInterval(refresh, 5 * 60 * 1000); -} - -document.body.addEventListener('touchmove', (e) => e.preventDefault(), { - passive: false, -}); -main(); +main(common_calendarOptions); diff --git a/src/index.css b/src/ios-fixes.css similarity index 100% rename from src/index.css rename to src/ios-fixes.css diff --git a/src/wall-display.html b/src/wall-display.html new file mode 100644 index 0000000..2a10aa9 --- /dev/null +++ b/src/wall-display.html @@ -0,0 +1,13 @@ + + + + + + + Tool Reservations + + +
+ + + diff --git a/src/wall-display.ts b/src/wall-display.ts new file mode 100644 index 0000000..2aea3fc --- /dev/null +++ b/src/wall-display.ts @@ -0,0 +1,34 @@ +import 'core-js/stable/url'; +import 'core-js/stable/function'; + +import Intl from 'intl'; +import 'intl/locale-data/jsonp/en.js'; +window.Intl = Intl; + +import { CalendarOptions } from '@fullcalendar/core'; + +import { common_calendarOptions, main } from './common'; + +import './wall-display.html'; +import './ios-fixes.css'; + +document.body.addEventListener('touchmove', (e) => e.preventDefault(), { + passive: false, +}); + +const urlParams = new URLSearchParams(window.location.search); +const toolFilter: string[] | undefined = urlParams.get('tool')?.split(';'); + +const calendarOptions: CalendarOptions = { + ...common_calendarOptions, + resources: toolFilter + ? toolFilter.map((tool) => { + return { + id: tool, + title: tool, + }; + }) + : [], +}; + +main(calendarOptions, !toolFilter); diff --git a/webpack.config.js b/webpack.config.js index e54a281..ad12664 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,8 +1,7 @@ const path = require('path'); -module.exports = { +const common = { mode: 'development', - entry: './src/index.ts', devServer: { proxy: { '/calendar': { @@ -11,25 +10,14 @@ module.exports = { }, }, }, - devtool: 'source-map', module: { rules: [ - { - test: /\.m?js$/, - exclude: /node_modules/, - loader: 'babel-loader', - }, - { - test: /\.tsx?$/, - exclude: /node_modules/, - use: ['babel-loader', 'ts-loader'], - }, { test: /\.css$/i, use: ['style-loader', 'css-loader'], }, { - test: /\/index.html$/i, + test: /\.html$/i, use: { loader: 'file-loader', options: { @@ -39,11 +27,54 @@ module.exports = { }, ], }, + devtool: 'source-map', resolve: { extensions: ['.tsx', '.ts', '.js'], }, - output: { - path: path.resolve(__dirname, 'dist'), - filename: 'bundle.js', - }, }; + +module.exports = [ + { + ...common, + name: 'default', + entry: './src/index.ts', + module: { + rules: [ + { + test: /\.tsx?$/, + exclude: /node_modules/, + use: ['ts-loader'], + }, + ...common.module.rules, + ], + }, + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'bundle.js', + }, + }, + { + ...common, + name: 'wall-display', + entry: './src/wall-display.ts', + module: { + rules: [ + { + test: /\.m?js$/, + exclude: /node_modules/, + loader: 'babel-loader', + }, + { + test: /\.tsx?$/, + exclude: /node_modules/, + use: ['babel-loader', 'ts-loader'], + }, + ...common.module.rules, + ], + }, + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'bundle-wall-display.js', + }, + }, +];