Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
jxmoose committed Dec 9, 2024
1 parent c33d5a0 commit 795239f
Show file tree
Hide file tree
Showing 18 changed files with 935 additions and 217 deletions.
169 changes: 169 additions & 0 deletions app/availability/days/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
'use client';

import React, { useContext, useState } from 'react';
import { useRouter } from 'next/navigation';
import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction';
import FullCalendar from '@fullcalendar/react';
import Back from '@/public/images/back.svg';
import {
AvailabilityContext,
defaultRange,
TimeRange,
} from '@/utils/availabilityContext';
import {
BackButton,
Button,
ButtonContainer,
Container,
ContinueText,
Divider,
EventName,
Image,
ProgressBarContainer,
Rectangle,
Title,
} from '../styles';
import { CalendarContainer } from './styles';

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

type DateObj = {
date: Date;
};

export default function Page() {
const [curMonth, setMonth] = useState<number>();
const router = useRouter();
const availabilityContext = useContext(AvailabilityContext);

if (!availabilityContext) return null;

const { generalInfo, days, setDays, times, setTimes } = availabilityContext;

const handleSelect = (info: Info) => {
const { start, end } = info; // Get start and end of the selected range
while (start < end) {
if (!checkValidSelectDate(start)) {
const dateStr: string = start.toISOString();
if (days.includes(dateStr)) {
setDays(days.filter(days => days !== dateStr));
} else {
setDays([...days, 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 (days.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;
}

const handleSubmit = async () => {
if (!days) {
return;
}
setDays(days.sort((a, b) => a.localeCompare(b)));
console.log(times);
const initTimes: { [date: string]: TimeRange[] } = {};
for (const day of days) {
if (day in times) {
initTimes[day] = times[day];
} else {
initTimes[day] = [structuredClone(defaultRange)];
}
}
setTimes(initTimes);
router.push('/availability/times');
};

const handleBack = () => {
router.push('/availability/details');
};
return (
<Container>
<BackButton onClick={handleBack}>
<Image src={Back} alt="Back icon" />
</BackButton>
<Title $fontWeight={500}> What day&apos;s work best? </Title>
<ProgressBarContainer>
<Rectangle variant="dark" width="50%" />
<Rectangle variant="light" width="50%" />
</ProgressBarContainer>
<CalendarContainer>
<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}
/>
</CalendarContainer>
<ButtonContainer>
<EventName $fontWeight={500}> {generalInfo.eventName} </EventName>
<Divider />
<Button onClick={handleSubmit} disabled={days.length == 0}>
<ContinueText>Continue</ContinueText>
</Button>
</ButtonContainer>
</Container>
);
}
95 changes: 95 additions & 0 deletions app/availability/days/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import styled from 'styled-components';
import COLORS from '../../../styles/colors';

export const CalendarContainer = styled.div`
margin-top: 5rem;
margin-left: 2rem;
width: 500px;
height: 500px;
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;
}
`;
92 changes: 92 additions & 0 deletions app/availability/details/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
'use client';

import { useContext } from 'react';
import { useRouter } from 'next/navigation';
import Back from '@/public/images/back.svg';
import { AvailabilityContext } from '@/utils/availabilityContext';
import {
Asterisk,
BackButton,
Button,
ButtonContainer,
Container,
ContinueText,
Divider,
Image,
ProgressBarContainer,
QuestionsContainer,
Rectangle,
Title,
} from '../styles';
import {
AdditionalInfo,
AdditionalInfoInput,
EventName,
EventNameInput,
} from './styles';

export default function Page() {
const router = useRouter();
const availabilityContext = useContext(AvailabilityContext);

if (!availabilityContext) return null;

const { generalInfo, setGeneralInfo } = availabilityContext;

const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
) => {
const { name, value } = e.target;
setGeneralInfo({
...generalInfo,
[name]: value,
});
};

const handleSubmit = async () => {
if (!generalInfo.eventName) {
return;
}
router.push('/availability/days');
};

const handleBack = () => {
router.push('/availability');
};

return (
<Container>
<QuestionsContainer>
<BackButton onClick={handleBack}>
<Image src={Back} alt="Back icon" />
</BackButton>
<Title $fontWeight={500}> What&apos;s the Occasion? </Title>
<ProgressBarContainer>
<Rectangle variant="dark" width="25%" />
<Rectangle variant="light" width="75%" />
</ProgressBarContainer>
<EventName>
Event Name &nbsp; <Asterisk> * </Asterisk>
</EventName>
<EventNameInput
placeholder="Event Name"
name="eventName"
value={generalInfo.eventName}
onChange={handleChange}
/>
<AdditionalInfo> Additional Info </AdditionalInfo>
<AdditionalInfoInput
name="additionalInfo"
value={generalInfo.additionalInfo}
onChange={handleChange}
/>
</QuestionsContainer>
<ButtonContainer>
<Divider />
<Button onClick={handleSubmit} disabled={!generalInfo.eventName}>
<ContinueText>Continue</ContinueText>
</Button>
</ButtonContainer>
</Container>
);
}
31 changes: 31 additions & 0 deletions app/availability/details/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import styled from 'styled-components';
import COLORS from '../../../styles/colors';
import { Sans } from '../../../styles/fonts';
import { P } from '../../../styles/text';

export const EventName = styled.div`
display: flex;
flex-direction: row;
font-weight: 500;
`;

export const EventNameInput = styled.input`
height: 1.5rem;
text-indent: 0.5rem;
border-radius: 8px;
border: 1px solid ${COLORS.gray6};
${Sans.style}
`;

export const AdditionalInfo = styled(P)`
margin-top: 1.25rem;
font-weight: 500;
`;

export const AdditionalInfoInput = styled.textarea`
height: 7rem;
border-radius: 8px;
border: 1px solid ${COLORS.gray6};
padding: 0.5rem;
${Sans.style}
`;
6 changes: 6 additions & 0 deletions app/availability/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ReactNode } from 'react';
import { AvailabilityProvider } from '@/utils/availabilityContext';

export default function Layout({ children }: { children: ReactNode }) {
return <AvailabilityProvider>{children}</AvailabilityProvider>;
}
Loading

0 comments on commit 795239f

Please sign in to comment.