${e.dateRange}
70 |71 | ${e.name} 72 | ${e.location 73 | ? `
${e.location}
` 74 | : ''} 75 | 76 | ${ 77 | e.description 78 | ? `├── .gitignore ├── README.md ├── favicon.svg ├── functions └── calFetch.mjs ├── index.html ├── main.js ├── netlify.toml ├── package-lock.json ├── package.json ├── postcss.config.js ├── style.css └── tailwind.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | # Local Netlify folder 7 | .netlify 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Custom Google Calendar Event Page 2 | 3 | Google Calendar is the default system for a reason: it’s stable, ubiquitous, and easy-to-use. Displaying calendar data, however can be a bit clunky, especially if you want your event page to reflect your site’s branding. In this series, I’ll show you how to: 4 | - Create a custom calendar event page with HTML, Tailwind CSS, and ViteJS 5 | - How to publish your site to Netlify 6 | - How to get a Google API key and store it safely as a Netlify environmental variable 7 | - How to write your own custom serverless function, allowing for dynamic query string parameters 8 | - How to fetch dynamic calendar data from your Google Calendar endpoint and display it on your site. 9 | 10 | 🔗 Key Links 🔗 11 | - Live code: https://codinginpublic.dev/projects/google-calendar-event-page/ 12 | - GitHub: https://github.com/coding-in-public/google-calendar-event-page 13 | Help me make it better! Contribute to the community-improvements branch on GitHub. 14 | 15 | 📹 Other Videos in this Series 📹 16 | - Full Playlist: https://www.youtube.com/watch?v=SOsGToYI0MQ&list=PLoqZcxvpWzzeuzWsIpH-N1b3vnRVbrdm3 17 | - Video 1: https://youtu.be/SOsGToYI0MQ 18 | - Video 2: https://youtu.be/CObyHiI7TEQ 19 | - Video 3: https://youtu.be/wf5gVe8hWnk 20 | - Video 4: https://youtu.be/_ec2ps7w8s4 21 | 22 | --------------------------------------- 23 | 24 | 🔗 Additional Links 🔗 25 | - NodeJS: https://nodejs.org/en/ 26 | - Vite JS: https://vitejs.dev/ 27 | - Tailwind: https://tailwindcss.com/docs/installation/using-postcss 28 | 29 | --------------------------------------- 30 | 31 | 🌐 Connect With Me 🌐 32 | - Website: https://www.codinginpublic.dev 33 | - Blog: https://www.chrispennington.blog 34 | - Twitter: https://twitter.com/cpenned 35 | -------------------------------------------------------------------------------- /favicon.svg: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /functions/calFetch.mjs: -------------------------------------------------------------------------------- 1 | import fetch from 'node-fetch'; 2 | 3 | const { CAL_API, CAL_ID } = process.env; 4 | const BASEPARAMS = `orderBy=startTime&singleEvents=true&timeMin=${new Date().toISOString()}` 5 | const BASEURL = `https://www.googleapis.com/calendar/v3/calendars/${CAL_ID}/events?${BASEPARAMS}` 6 | 7 | const HEADERS = { 8 | 'Content-Type': 'application/json', 9 | 'Access-Control-Allow-Methods': 'GET', 10 | } 11 | 12 | exports.handler = async function (event, context) { 13 | const finalURL = `${BASEURL}${event.queryStringParameters.maxResults ? `&maxResults=${event.queryStringParameters.maxResults}` : ''}&key=${CAL_API}` 14 | try { 15 | if (event.httpMethod === 'GET') { 16 | return fetch(finalURL) 17 | .then((response) => response.json()) 18 | .then((data) => ({ 19 | statusCode: 200, 20 | body: JSON.stringify(data.items, null, 2), 21 | HEADERS 22 | })) 23 | } 24 | return { 25 | statusCode: 401 26 | } 27 | } catch (e) { 28 | console.error(e) 29 | return { 30 | statusCode: 500, 31 | body: e.toString() 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |Loading Events
23 |${e.dateRange}
70 |${e.location}
` 74 | : ''} 75 | 76 | ${ 77 | e.description 78 | ? `🙀 Something went wrong!
` 105 | console.log(e); 106 | } 107 | } 108 | loadEvents(); 109 | 110 | eventContainer.addEventListener('click', (e) =>{ 111 | if(e.target.hasAttribute('aria-expanded')){ 112 | e.target.setAttribute('aria-expanded', e.target.getAttribute('aria-expanded') === 'false' ? 'true' : 'false'); 113 | e.target.querySelector('svg').classList.toggle('rotate-180'); 114 | e.target.nextElementSibling.classList.toggle('hidden'); 115 | } 116 | }) 117 | eventAmtToFetch.addEventListener('change', (e) => loadEvents(eventAmtToFetch.value)) -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | functions = 'functions' -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-cal-api", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "preview": "vite preview" 8 | }, 9 | "devDependencies": { 10 | "autoprefixer": "^10.4.2", 11 | "netlify-cli": "^8.18.1", 12 | "postcss": "^8.4.5", 13 | "tailwindcss": "^3.0.18", 14 | "vite": "^2.7.2" 15 | }, 16 | "dependencies": { 17 | "node-fetch": "^3.2.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | } 6 | } -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["*.{html,js}"], 3 | safelist: [ 4 | 'bg-blue-500', 5 | 'bg-amber-500', 6 | 'bg-rose-500', 7 | 'bg-indigo-500', 8 | 'bg-pink-500', 9 | 'text-blue-50', 10 | 'text-amber-50', 11 | 'text-rose-50', 12 | 'text-indigo-50', 13 | 'text-pink-50', 14 | 'ring-blue-500', 15 | 'ring-amber-500', 16 | 'ring-rose-500', 17 | 'ring-indigo-500', 18 | 'ring-pink-500', 19 | 'shadow-blue-200', 20 | 'shadow-amber-200', 21 | 'shadow-rose-200', 22 | 'shadow-indigo-200', 23 | 'shadow-pink-200', 24 | ], 25 | theme: { 26 | extend: { 27 | gridTemplateRows: { 28 | 'auto1': 'auto 1fr', 29 | }, 30 | gridTemplateColumns: { 31 | 'cards': 'repeat(auto-fit, minmax(250px, 1fr))', 32 | } 33 | }, 34 | }, 35 | plugins: [], 36 | } --------------------------------------------------------------------------------