diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..58a6839 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,10 @@ +module.exports = { + plugins: ['@babel/plugin-transform-runtime'], + presets: [ + ['@babel/preset-env', { + useBuiltIns: 'usage', + corejs: 3, + }, + ], + ], +}; diff --git a/package.json b/package.json index 249cf5a..63f5106 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,34 @@ { + "browserslist": [ + "defaults", + "ios 8.4" + ], "scripts": { - "start": "npm run dev", - "dev": "vite", - "build": "vue-tsc --noEmit && vite build", - "serve": "vite preview" + "start": "npm run serve", + "build": "webpack build", + "serve": "webpack serve" }, "devDependencies": { + "@babel/core": "^7.16.0", + "@babel/plugin-transform-runtime": "^7.16.0", + "@babel/preset-env": "^7.16.0", "@types/intl": "^1.2.0", - "@vitejs/plugin-legacy": "^1.6.2", - "@vitejs/plugin-vue": "^1.9.4", - "sass": "^1.23.1", + "babel-loader": "^8.2.3", + "css-loader": "^6.5.1", + "style-loader": "^3.3.1", + "ts-loader": "^9.2.6", "typescript": "^4.2.3", - "vite": "^2.6.13", - "vue-tsc": "^0.29.4" + "webpack": "^5.64.0", + "webpack-cli": "^4.9.1", + "webpack-dev-server": "^4.4.0" }, "dependencies": { "@fullcalendar/core": "^5.10.0", "@fullcalendar/icalendar": "^5.10.1", "@fullcalendar/timegrid": "^5.6.0", - "@fullcalendar/vue3": "^5.10.0", + "core-js": "^3.19.1", "ical.js": "github:ekreative/ical.js#var-ical", "intl": "^1.2.5", - "unique-colors": "^1.0.1", - "vue": "^3.2.20" + "unique-colors": "^1.0.1" } } diff --git a/index.html b/public/index.html similarity index 64% rename from index.html rename to public/index.html index d620beb..26303d3 100644 --- a/index.html +++ b/public/index.html @@ -5,7 +5,7 @@ Tool Reservations -
- +
+ diff --git a/src/App.vue b/src/App.vue deleted file mode 100644 index 59b0818..0000000 --- a/src/App.vue +++ /dev/null @@ -1,94 +0,0 @@ - - - - - diff --git a/src/index.ts b/src/index.ts index 4f612eb..239cfc8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,90 @@ -import 'core-js/modules/es.object.entries'; +// TODO: could probably be a dynamic import +import intl from 'intl'; +import 'intl/locale-data/jsonp/en.js'; -import * as Vue from 'vue'; +if (!window.Intl) { + // No `Intl`, so use and load the polyfill. + window.Intl = intl; +} -import App from './App.vue'; +import { EventInput, Calendar, CalendarOptions } from '@fullcalendar/core'; +import dayGridPlugin from '@fullcalendar/daygrid'; +import timeGridPlugin from '@fullcalendar/timegrid'; +import iCalendarPlugin from '@fullcalendar/icalendar'; -const app = Vue.createApp(App).mount('#app'); +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); + +const urlParams = new URLSearchParams(window.location.search); +const toolFilter: string | null = urlParams.get('tool'); + +function eventDataTransform(eventData: EventInput): EventInput | false { + // 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 = `${tool} - ${member}`; + if (toolFilter === null || tool.includes(toolFilter)) { + return eventData; + } + } + return false; +} + +const calendarOptions: CalendarOptions = { + plugins: [timeGridPlugin, dayGridPlugin, iCalendarPlugin], + allDaySlot: false, + nowIndicator: true, + initialView: 'timeGridWeek', + height: 'auto', + slotMinTime: '8:00', + slotMaxTime: '22:00', + businessHours: { + daysOfWeek: [0, 1, 2, 3, 4, 5, 6], + startTime: '10:00', + endTime: '21:00', + }, + eventSources: Object.values(calendars).map((id, idx) => { + return { + url: '/calendar/ical/' + id + '/public/basic.ics', + format: 'ics', + color: colors[idx], + eventDataTransform: eventDataTransform, + }; + }), + slotLabelFormat: { + hour: 'numeric', + minute: '2-digit', + hour12: false, + }, + eventTimeFormat: { + hour: 'numeric', + minute: '2-digit', + hour12: false, + }, +}; + +const calendarEl = document.getElementById('calendar'); +const calendar = new Calendar(calendarEl!, calendarOptions); + +calendar.render(); + +function refresh() { + calendar.refetchEvents(); +} + +// refresh data every five minutes +window.setInterval(refresh, 5 * 60 * 1000); diff --git a/vite.config.ts b/vite.config.ts deleted file mode 100644 index 7e7ade5..0000000 --- a/vite.config.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { defineConfig } from 'vite'; -import vue from '@vitejs/plugin-vue'; -import legacy from '@vitejs/plugin-legacy' - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [ - vue(), - legacy({ - targets: ['defaults', 'not IE 11', "ios 8.4"] - }), - ], - server: { - proxy: { - '/calendar': { - target: 'https://calendar.google.com', - changeOrigin: true, - }, - }, - }, -}); diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..a319737 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,48 @@ +const path = require('path'); + +module.exports = { + mode: 'development', + entry: './src/index.ts', + devServer: { + proxy: { + '/calendar': { + target: 'https://calendar.google.com', + changeOrigin: true, + }, + }, + }, + devtool: "eval-cheap-source-map", + module: { + rules: [ + { + test: /\.m?js$/, + exclude: /node_modules/, + loader: 'babel-loader', + }, + { + test: /\.tsx?$/, + exclude: /node_modules/, + use: [ + 'babel-loader', + { + loader: 'ts-loader', + options: { + appendTsSuffixTo: [/\.vue$/], + }, + } + ], + }, + { + test: /\.css$/i, + use: ["style-loader", "css-loader"], + } + ], + }, + resolve: { + extensions: ['.tsx', '.ts', '.js'], + }, + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'bundle.js', + }, +};