Skip to content

Commit

Permalink
Merge pull request #245 from ASAP-as-soon-as-possible/feat/overallSch…
Browse files Browse the repository at this point in the history
…edule/#241

[Feat/#241] 종합 일정 시간표 구현
  • Loading branch information
simeunseo authored Jul 5, 2024
2 parents af422e5 + f72b629 commit 1c6c460
Show file tree
Hide file tree
Showing 31 changed files with 660 additions and 289 deletions.
2 changes: 1 addition & 1 deletion src/components/moleculesComponents/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Dispatch, SetStateAction, useState } from 'react';

import Text from 'components/atomComponents/Text';
import { BackIc, ExitIc, HambergerIc, LinkIc, MainLogoIc } from 'components/Icon/icon';
import { useScheduleStepContext } from 'pages/selectSchedule/context';
import { useScheduleStepContext } from 'pages/selectSchedule/contexts/useScheduleStepContext';
import { ScheduleStepType } from 'pages/selectSchedule/types';
import CopyToClipboard from 'react-copy-to-clipboard';
import { useParams } from 'react-router';
Expand Down
41 changes: 17 additions & 24 deletions src/components/timetableComponents/Timetable.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { ReactNode, useState } from 'react';
import { ReactNode } from 'react';

import styled from 'styled-components';

import { SelectedSlotType, TimetableContext } from './context';
import DateTitle from './parts/ColumnTitle';
import SlotTitle from './parts/SlotTitle';
import { ColumnStructure, DateType } from './types';
Expand All @@ -15,35 +14,29 @@ interface TimetableProps {
}

function Timetable({ timeSlots, availableDates, children, bottomItem }: TimetableProps) {
const [startSlot, setStartSlot] = useState<string | undefined>(undefined);
const [selectedSlots, setSelectedSlots] = useState<SelectedSlotType>({});

const emptyDates = Array.from({ length: 7 - availableDates.length }, (_, i) => `empty${i + 1}`);

return (
<TimetableContext.Provider
value={{
startSlot,
setStartSlot,
selectedSlots,
setSelectedSlots,
}}
>
<>
<TimetableWrapper>
<SlotTitle timeSlots={timeSlots} />
<TableWrapper>
<TableWithDateWrapper>
<DateTitle availableDates={availableDates} />
<Table>
<TableWrapper>
{availableDates.map((date) => {
const dateKey = Object.values(date).join('/');
return <Column key={dateKey}>{children({ date: dateKey, timeSlots })}</Column>;
return (
<ColumnWrapper key={dateKey}>
{children({ date: dateKey, timeSlots })}
</ColumnWrapper>
);
})}
{emptyDates && emptyDates.map((value) => <EmptyColumn key={value} />)}
</Table>
</TableWrapper>
{emptyDates && emptyDates.map((value) => <EmptyColumnWrapper key={value} />)}
</TableWrapper>
</TableWithDateWrapper>
</TimetableWrapper>
{bottomItem}
</TimetableContext.Provider>
</>
);
}

Expand All @@ -54,26 +47,26 @@ const TimetableWrapper = styled.div`
gap: 0.75rem;
`;

const TableWrapper = styled.div`
const TableWithDateWrapper = styled.div`
display: flex;
flex-direction: column;
gap: 0.8rem;
`;

const Table = styled.div`
const TableWrapper = styled.div`
display: flex;
border-bottom: 1px solid ${({ theme }) => theme.colors.grey7};
border-left: 1px solid ${({ theme }) => theme.colors.grey7};
`;

const Column = styled.div`
const ColumnWrapper = styled.div`
display: flex;
flex-direction: column;
border-right: 1px solid ${({ theme }) => theme.colors.grey7};
`;

const EmptyColumn = styled.div`
const EmptyColumnWrapper = styled.div`
display: flex;
flex-direction: column;
border-top: 1px solid ${({ theme }) => theme.colors.grey7};
Expand Down
33 changes: 0 additions & 33 deletions src/components/timetableComponents/context.ts

This file was deleted.

176 changes: 29 additions & 147 deletions src/pages/OverallSchedule/OverallSchedule.tsx
Original file line number Diff line number Diff line change
@@ -1,165 +1,47 @@
import React, { useEffect, useState } from 'react';
import { availableDatesAtom, preferTimesAtom, timeSlotUserNameAtom } from 'atoms/atom';
import { useRecoilState, useRecoilValue } from 'recoil';

import LoadingPage from 'pages/errorLoading/LoadingPage';
import { OverallScheduleData } from 'src/types/overallScheduleType';
import Text from 'components/atomComponents/Text';
import TimeTable from './components/TimeTable';
import { availableScheduleOptionApi } from 'utils/apis/legacy/availbleScheduleOptionApi';
import { getFormattedAvailableDateTimes } from './utils/getFormattedAvailableDateTimes';
import { overallScheduleApi } from 'utils/apis/legacy/overallScheduleApi';
import { styled } from 'styled-components';
import { theme } from 'styles/theme';
import { getAvailableTimes } from 'components/timetableComponents/utils';
import { useParams } from 'react-router-dom';
import styled from 'styled-components';
import { useGetOverallSchedule } from 'utils/apis/useGetOverallSchedule';
import { useGetTimetable } from 'utils/apis/useGetTimetable';

const OverallSchedule = () => {
const { meetingId } = useParams();
const [overallScheduleData, setOverallScheduleData] = useState<OverallScheduleData>();

const [availableDates, setAvailableDates] = useRecoilState(availableDatesAtom);

const [preferTimes, setPreferTimes] = useRecoilState(preferTimesAtom);

const timeSlotUserNames = useRecoilValue(timeSlotUserNameAtom);

const [memberCount, setMemberCount] = useState<number>(0);
const [totalUserNames, setTotalUserNames] = useState<string[]>();
import OverallScheduleTable from './components/OverallScheduleTable';
import Title from './components/Title';

const getAvailableScheduleOption = async () => {
try {
const { data } = await availableScheduleOptionApi(meetingId);
setAvailableDates(data.data.availableDates);
setPreferTimes(data.data.preferTimes);
} catch (err) {
console.log(err);
}
};


const getOverallSchedule = async () => {
try {
const result = await overallScheduleApi(meetingId);
const { data } = result.data;
const uniqueData = [...new Set(data.totalUserNames)];
setOverallScheduleData(data);
setMemberCount(data.memberCount);
setTotalUserNames(uniqueData);
} catch (err) {
console.log(err);
}
};

useEffect(() => {
getAvailableScheduleOption();
getOverallSchedule();
}, []);
function OverallSchedule() {
const { meetingId } = useParams();
const { data: dataTimetable, isLoading: isLoadingTimetable } = useGetTimetable(meetingId);
const { data: dataOverallSchedule, isLoading: isLoadingOverallSchedule } = useGetOverallSchedule(
meetingId,
);

const formattedAvailableDateTimes =
overallScheduleData && getFormattedAvailableDateTimes(overallScheduleData);
// 시간대 선택 단계가 없어질 것을 고려하여 상수값을 설정해놓음
const PREFER_TIMES = { startTime: '06:00', endTime: '24:00' };

return (
<OverallScheduleWrapper>
{overallScheduleData ? (
<>
<TextOneLine>
<Text font={'title1'} color={`${theme.colors.white}`}>
현재까지&nbsp;
</Text>
<Text font={'title1'} color={`${theme.colors.sub1}`}>
{memberCount.toString()}
</Text>
<Text font={'title1'} color={`${theme.colors.white}`}>
이 입력했어요
</Text>
</TextOneLine>
<TotalUserNames>
{totalUserNames &&
totalUserNames.map((name, idx) => (
<Text key={idx + name} font={'body4'} color={`${theme.colors.grey5}`}>
{name}
{idx !== totalUserNames.length - 1 ? ',' : ''}&nbsp;
</Text>
))}
</TotalUserNames>
<TimeTable
selectedSchedule={formattedAvailableDateTimes?.availableDateTimes}
availableDates={availableDates}
preferTimes={preferTimes}
scheduleType="available"
<Title memberCount={dataOverallSchedule?.memberCount} totalUserNames={dataOverallSchedule?.totalUserNames}/>
{!isLoadingTimetable &&
!isLoadingOverallSchedule &&
dataTimetable &&
dataOverallSchedule && (
<OverallScheduleTable
timeSlots={getAvailableTimes(PREFER_TIMES)}
availableDates={dataTimetable.availableDates}
dataOverallSchedule={dataOverallSchedule}
/>
<UserNameWrapper>
{!timeSlotUserNames ? (
<TextTwoLine>
<Text font={'body4'} color={`${theme.colors.grey5}`}>
블럭을 선택하면 해당 시간대에 참여가능한
</Text>
<Text font={'body4'} color={`${theme.colors.grey5}`}>
인원을 확인할 수 있어요
</Text>
</TextTwoLine>
) : (
timeSlotUserNames.map((name, idx) => (
<Text key={idx + name} font={'body2'} color={`${theme.colors.grey2}`}>
{name}
{idx !== timeSlotUserNames.length - 1 ? ',' : ''}&nbsp;
</Text>
))
)}
</UserNameWrapper>
</>
) : (
<LoadingWrapper>
<LoadingPage />
</LoadingWrapper>
)}
)}
</OverallScheduleWrapper>
);
};
}

export default OverallSchedule;

const UserNameWrapper = styled.aside`
const OverallScheduleWrapper = styled.div`
display: flex;
position: fixed;
bottom: 4.4rem;
flex-wrap: wrap;
flex-direction: column;
align-items: center;
justify-content: center;
border: 1px solid ${({ theme }) => theme.colors.grey5};
border-radius: 0.8rem;
background: ${({ theme }) => theme.colors.grey9};
width: 33.5rem;
min-height: 8.3rem;
text-align: center;
color: ${({ theme }) => theme.colors.white};
`;

const OverallScheduleWrapper = styled.main`
margin-bottom: 16.1rem;
margin-bottom: 16.4rem;
`;

const TextOneLine = styled.div`
display: flex;
flex-wrap: wrap;
margin-top: 3.7rem;
width: 100%;
`;

const TotalUserNames = styled.div`
display: flex;
margin-top: 1.2rem;
margin-bottom: 2.4rem;
`;

const LoadingWrapper = styled.div`
position: relative;
top: 25rem;
width: 100%;
`;

const TextTwoLine = styled.div`
display:flex;
flex-direction:column;
align-items: center;
justify-content:center;
`
45 changes: 45 additions & 0 deletions src/pages/OverallSchedule/components/OverallScheduleColumn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Slot from 'components/timetableComponents/parts/Slot';
import { ColumnStructure } from 'components/timetableComponents/types';
import { theme } from 'styles/theme';
import { TimeSlot } from 'utils/apis/useGetOverallSchedule';

import { useSlotClick } from '../hooks/useSlotClick';

interface OverallScheduleColumnProps extends ColumnStructure {
availableSlotInfo: TimeSlot[];
}

function OverallScheduleColumn({ date, timeSlots, availableSlotInfo }: OverallScheduleColumnProps) {

const { clickedSlot, onClickSlot } = useSlotClick();

const getTimeSlotStyle = (colorLevel: number, slotId:string) => {
const COLOR :{ [key : number]: string } = {
0: 'transparent',
1: theme.colors.level1,
2: theme.colors.level2,
3: theme.colors.level3,
4: theme.colors.level4,
5: theme.colors.level5,
};

const isClickedSlot = clickedSlot === slotId;
return `
background-color: ${isClickedSlot && colorLevel!==0 ? theme.colors.sub1 : COLOR[colorLevel]};
cursor: ${colorLevel !== 0 ? 'pointer' : 'default'};
`
}

return (
<>
{timeSlots.map((timeSlot) => {
const { colorLevel = 0, userNames = [] } = availableSlotInfo.find((info) => info.time === timeSlot) ?? {};
const slotId = `${date}/${timeSlot}`;

return <Slot key={slotId} slotId={slotId} slotStyle={getTimeSlotStyle(colorLevel, slotId)} onClick={()=>onClickSlot(slotId, userNames)}/>;
})}
</>
);
}

export default OverallScheduleColumn;
Loading

0 comments on commit 1c6c460

Please sign in to comment.