Skip to content

Commit

Permalink
feat: calendar component
Browse files Browse the repository at this point in the history
adding the calendar component using full calendar
  • Loading branch information
jxmoose committed Nov 21, 2024
1 parent 3296bd8 commit b5de0e8
Show file tree
Hide file tree
Showing 6 changed files with 333 additions and 3 deletions.
106 changes: 106 additions & 0 deletions app/availability/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
'use client';

import React, { useState } from 'react';
import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction';
import FullCalendar from '@fullcalendar/react';
import { Container } from './styles';

type Info = {
start: Date;
end: Date;
};

type DateObj = {
date: Date;
};

export default function Page() {
const [selectedDates, setSelectedDates] = useState<string[]>([]);
const [curMonth, setMonth] = useState<number>();

const handleSelect = (info: Info) => {
const { start, end } = info; // Get start and end of the selected range
while (start < end) {
if (!checkValidSelectDate(start)) {
const dateStr = start.toISOString();
setSelectedDates(prevDates =>
prevDates.includes(dateStr)
? prevDates.filter(date => date !== dateStr)
: [...prevDates, dateStr],
);
}
start.setDate(start.getDate() + 1); // Move to the next day
}
};

const updateMonth = (info: Info) => {
const middate = new Date((info.start.getTime() + info.end.getTime()) / 2);
setMonth(middate.getMonth());
};

const dayCellClassNames = (date: DateObj) => {
const dateObj = date.date;
const dateStr = dateObj.toISOString();
const month = dateObj.getMonth();
// Default classes
const classNames = [];

// Highlight selected dates
if (selectedDates.includes(dateStr)) {
classNames.push('selected-date');
if (curMonth != month) {
classNames.push('non-cur-selected');
} else {
classNames.push('cur-selected');
}
}

if (checkValidSelectDate(dateObj)) {
// 0 = Sunday, 6 = Saturday
classNames.push('unselectable');
}

// Return the dynamic class names
return classNames;
};

function checkValidSelectDate(date: Date): boolean {
const day = date.getDay();
const month = date.getMonth();
const today = new Date();

const sameDay =
date.getDate() == today.getDate() && date.getMonth() == today.getMonth();
if (
day === 0 ||
day === 6 ||
(!sameDay && date < today) ||
(curMonth != null && curMonth != month)
) {
// 0 = Sunday, 6 = Saturday
return true;
}
return false;
}

return (
<Container>
<FullCalendar
plugins={[interactionPlugin, dayGridPlugin]}
initialView="dayGridMonth"
headerToolbar={{
left: 'title',
center: '',
right: 'prev next',
}}
dayCellContent={arg => arg.date.getDate()}
selectable={true}
dayCellClassNames={dayCellClassNames}
selectOverlap={false}
select={handleSelect}
datesSet={updateMonth}
/>
</Container>
);
}
93 changes: 93 additions & 0 deletions app/availability/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import styled from 'styled-components';
import COLORS from '../../styles/colors';

export const Container = styled.div`
width: 500px;
margin: 100px;
cursor: default;
.fc-prev-button,
.fc-next-button {
background: none;
border: none;
color: black;
box-shadow: none;
}
.fc-prev-button:hover,
.fc-next-button:hover {
background-color: lightgray;
cursor: pointer;
}
.fc-highlight {
background-color: transparent;
}
.selected-date {
position: relative;
}
.cur-selected::after {
background-color: ${COLORS.bread5} !important;
}
.non-cur-selected::after {
background-color: ${COLORS.bread3} !important;
}
.selected-date::after {
content: '';
position: absolute;
width: 45px;
height: 45px;
bottom: 5.5px;
transform: translateX(11px);
border-radius: 50%;
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
z-index: -1;
}
.unselectable {
color: ${COLORS.gray8}} !important;
}
.fc-day-past div {
opacity: 1 !important;
}
.fc-day-future div {
opacity: 1 !important;
}
.fc-day-today {
background-color: transparent !important;
}
.fc-day-today::after {
content: '';
position: absolute;
transform: translateX(31px);
bottom: 125px;
width: 4px; /* Diameter of the dot */
height: 4px; /* Diameter of the dot */
background-color: ${COLORS.rose11}; /* Color of the dot */
border-radius: 50%; /* Makes the element a circle */
}
.fc-daygrid-day-frame {
display: flex;
justify-content: center;
}
.fc-daygrid-day-top {
align-items: center;
}
.fc td,
.fc table,
.fc th {
border-style: none !important;
}
`;
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@
"pre-commit": "(pnpm run tsc || true) && (pnpm run lint || true) && pnpm run prettier"
},
"dependencies": {
"@fullcalendar/core": "^6.1.15",
"@fullcalendar/daygrid": "^6.1.15",
"@fullcalendar/interaction": "^6.1.15",
"@fullcalendar/react": "^6.1.15",
"@next/font": "^14.2.15",
"@supabase/supabase-js": "^2.45.4",
"dotenv": "^16.4.5",
"fullcalendar": "^6.1.15",
"next": "^14.2.10",
"react": "^18",
"react-dom": "^18",
Expand Down
126 changes: 126 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit b5de0e8

Please sign in to comment.