From 9852ced3696c41127566475c7e10401b4cfce39c Mon Sep 17 00:00:00 2001 From: "SeungJu, Lee" <84257439+SeungJL@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:24:24 +0900 Subject: [PATCH] feat: calendar (#218) --- components/atoms/ColorLabel.tsx | 24 ++ components/atoms/LocationSelector.tsx | 4 +- components/atoms/MonthNav.tsx | 42 +-- components/atoms/PointCircle.tsx | 5 +- components/atoms/PointCircleText.tsx | 22 -- components/molecules/PointCircleTextRow.tsx | 21 -- components/molecules/groups/ButtonGroups.tsx | 58 ++-- components/molecules/rows/ColorLabelRow.tsx | 21 ++ components/organisms/Calendar.tsx | 200 ++++++++++++ .../calendarSchedule.ts} | 168 +++++++--- constants/contentsText/accordionContents.ts | 2 +- constants/location.ts | 27 +- .../studyConstants/studyLocationConstants.ts | 1 - modals/InviteUserModal.tsx | 4 +- pageTemplates/gather/GatherLocationFilter.tsx | 6 +- pageTemplates/home/HomeCalendarSection.tsx | 50 +++ pageTemplates/home/HomeClubSection.tsx | 272 ---------------- pageTemplates/home/HomeRankingSection.tsx | 2 +- pageTemplates/home/HomeReviewSection.tsx | 2 +- pageTemplates/home/study/HomeStudyChart.tsx | 13 +- pageTemplates/home/study/HomeStudyCol.tsx | 4 +- .../study/studyController/StudyController.tsx | 4 +- pageTemplates/record/RecordCalendar.tsx | 154 +++++---- .../record/RecordCalendarSetting.tsx | 17 +- .../record/RecordLocationCategory.tsx | 87 ++--- pageTemplates/record/RecordOverview.tsx | 1 - pageTemplates/record/RecordSkeleton.tsx | 300 ------------------ .../record/detail/RecordDetailStudyBlock.tsx | 6 +- .../register/location/LocationMember.tsx | 4 +- .../SecretSquare/SecretSquareCategories.tsx | 8 +- pageTemplates/square/SquareLoungeSection.tsx | 30 +- pages/calendar/index.tsx | 11 +- pages/eventCalendar.tsx | 278 ---------------- pages/home/index.tsx | 4 +- pages/review/index.tsx | 6 +- pages/study/writing/place.tsx | 14 +- 36 files changed, 695 insertions(+), 1177 deletions(-) create mode 100644 components/atoms/ColorLabel.tsx delete mode 100644 components/atoms/PointCircleText.tsx delete mode 100644 components/molecules/PointCircleTextRow.tsx create mode 100644 components/molecules/rows/ColorLabelRow.tsx create mode 100644 components/organisms/Calendar.tsx rename constants/{settingValue/eventContents.ts => contents/calendarSchedule.ts} (59%) create mode 100644 pageTemplates/home/HomeCalendarSection.tsx delete mode 100644 pageTemplates/home/HomeClubSection.tsx delete mode 100644 pageTemplates/record/RecordSkeleton.tsx delete mode 100644 pages/eventCalendar.tsx diff --git a/components/atoms/ColorLabel.tsx b/components/atoms/ColorLabel.tsx new file mode 100644 index 000000000..aed3712e5 --- /dev/null +++ b/components/atoms/ColorLabel.tsx @@ -0,0 +1,24 @@ +import { Box, Flex } from "@chakra-ui/react"; + +import { CustomColor } from "../../types/globals/interaction"; +import PointCircle from "./PointCircle"; + +export interface ColorLabelProps { + color?: CustomColor; + colorText?: string; + //이후 colorText를 color로 변경 + text: string; +} + +function ColorLabel({ text, color, colorText }: ColorLabelProps) { + return ( + + + + {text} + + + ); +} + +export default ColorLabel; diff --git a/components/atoms/LocationSelector.tsx b/components/atoms/LocationSelector.tsx index eec5cf27e..19525d2ba 100644 --- a/components/atoms/LocationSelector.tsx +++ b/components/atoms/LocationSelector.tsx @@ -1,7 +1,7 @@ import { Select } from "@chakra-ui/react"; import { ChangeEvent, useEffect, useRef, useState } from "react"; -import { LOCATION_CONVERT } from "../../constants/location"; +import { LOCATION_TO_FULLNAME } from "../../constants/location"; import { DispatchType } from "../../types/hooks/reactTypes"; import { ActiveLocation } from "../../types/services/locationTypes"; import { isLocationType } from "../../utils/validationUtils"; @@ -57,7 +57,7 @@ export default function LocationSelector({ > {options.map((option, idx) => ( ))} diff --git a/components/atoms/MonthNav.tsx b/components/atoms/MonthNav.tsx index e63628145..f23a4dac0 100644 --- a/components/atoms/MonthNav.tsx +++ b/components/atoms/MonthNav.tsx @@ -1,42 +1,30 @@ +import { Box, Flex } from "@chakra-ui/react"; import { Dayjs } from "dayjs"; -import styled from "styled-components"; import { DispatchType } from "../../types/hooks/reactTypes"; -interface IMonthNav { - month: number; - setNavMonth: DispatchType; +interface MonthNavProps { + monthNum: number; + changeMonth: DispatchType; } -function MonthNav({ month, setNavMonth }: IMonthNav) { - const onClick = (dir: "left" | "right") => { - if (dir === "left") setNavMonth((old) => old.subtract(1, "month")); - else setNavMonth((old) => old.add(1, "month")); +function MonthNav({ monthNum, changeMonth }: MonthNavProps) { + const handleMonthChange = (dir: "left" | "right") => { + if (dir === "left") changeMonth((old) => old.subtract(1, "month")); + else changeMonth((old) => old.add(1, "month")); }; return ( - - onClick("left")}> + + handleMonthChange("left")}> - - {month + 1}월 - onClick("right")}> + + {monthNum + 1}월 + handleMonthChange("right")}> - - + + ); } -const Layout = styled.div` - display: flex; - align-items: center; - - font-size: 20px; - font-weight: 700; -`; - -const IconWrapper = styled.button` - padding: 0 var(--gap-1); -`; - export default MonthNav; diff --git a/components/atoms/PointCircle.tsx b/components/atoms/PointCircle.tsx index d17ee17e9..edde14d02 100644 --- a/components/atoms/PointCircle.tsx +++ b/components/atoms/PointCircle.tsx @@ -4,12 +4,13 @@ import { CustomColor } from "../../types/globals/interaction"; interface PointCircleProps { color?: CustomColor; + colorText?: string; } -function PointCircle({ color = "mint" }: PointCircleProps) { +function PointCircle({ color = "mint", colorText }: PointCircleProps) { return ( - + ); } diff --git a/components/atoms/PointCircleText.tsx b/components/atoms/PointCircleText.tsx deleted file mode 100644 index 974448313..000000000 --- a/components/atoms/PointCircleText.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Box, Flex } from "@chakra-ui/react"; - -import { CustomColor } from "../../types/globals/interaction"; -import PointCircle from "./PointCircle"; - -export interface PointCircleTextProps { - color?: CustomColor; - text: string; -} - -function PointCircleText({ text, color }: PointCircleTextProps) { - return ( - - - - {text} - - - ); -} - -export default PointCircleText; diff --git a/components/molecules/PointCircleTextRow.tsx b/components/molecules/PointCircleTextRow.tsx deleted file mode 100644 index 3c3316b61..000000000 --- a/components/molecules/PointCircleTextRow.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { Box, Flex } from "@chakra-ui/react"; - -import PointCircleText, { PointCircleTextProps } from "../atoms/PointCircleText"; - -interface PointCircleTextRowProps { - props: PointCircleTextProps[]; -} - -function PointCircleTextRow({ props }: PointCircleTextRowProps) { - return ( - - {props.map((prop, idx) => ( - - - - ))} - - ); -} - -export default PointCircleTextRow; diff --git a/components/molecules/groups/ButtonGroups.tsx b/components/molecules/groups/ButtonGroups.tsx index c7a24658a..b2fc08987 100644 --- a/components/molecules/groups/ButtonGroups.tsx +++ b/components/molecules/groups/ButtonGroups.tsx @@ -1,44 +1,64 @@ import { Button, Flex } from "@chakra-ui/react"; import styled from "styled-components"; -export interface IButtonOptions { +export interface ButtonOptionsProps { text: string; func: () => void; + color?: string; } export interface IButtonGroups { - buttonItems: IButtonOptions[]; + buttonOptionsArr: ButtonOptionsProps[]; currentValue: string; size?: "sm" | "md"; isWrap?: boolean; isEllipse?: boolean; + type?: "block" | "text"; } export default function ButtonGroups({ - buttonItems, + buttonOptionsArr, currentValue, size, isWrap = false, isEllipse = false, + type = "block", }: IButtonGroups) { + console.log(buttonOptionsArr, currentValue); return ( - {buttonItems.map((buttonData, idx) => ( - - + {buttonOptionsArr.map((buttonOptions, idx) => ( + + {type === "block" ? ( + + ) : ( + + )} ))} diff --git a/components/molecules/rows/ColorLabelRow.tsx b/components/molecules/rows/ColorLabelRow.tsx new file mode 100644 index 000000000..6d830b787 --- /dev/null +++ b/components/molecules/rows/ColorLabelRow.tsx @@ -0,0 +1,21 @@ +import { Box, Flex } from "@chakra-ui/react"; + +import ColorLabel, { ColorLabelProps } from "../../atoms/ColorLabel"; + +interface ColorLabelRowProps { + props: ColorLabelProps[]; +} + +function ColorLabelRow({ props }: ColorLabelRowProps) { + return ( + + {props.map((prop, idx) => ( + + + + ))} + + ); +} + +export default ColorLabelRow; diff --git a/components/organisms/Calendar.tsx b/components/organisms/Calendar.tsx new file mode 100644 index 000000000..38d8353b0 --- /dev/null +++ b/components/organisms/Calendar.tsx @@ -0,0 +1,200 @@ +import { Box, Flex, Grid } from "@chakra-ui/react"; +import dayjs, { Dayjs } from "dayjs"; +import styled from "styled-components"; + +import { COLOR_TABLE } from "../../constants/colorConstants"; +import { CalendarContentProps } from "../../constants/contents/calendarSchedule"; +import { DAYS_OF_WEEK } from "../../constants/util/util"; + +interface CalendarProps { + monthFirstDate: Dayjs; + calendarContents: CalendarContentProps[]; +} + +export interface CalendarScheduleProps { + content: string; + color: string; + isFirst: boolean; + isLast: boolean; + blockIdx: number; +} + +export interface DaySchedules { + first: string | null; + second: string | null; + third: string | null; +} + +const DAY_BLOCK_WIDTH = 50; + +const CALENDAR_DAY_COLOR = { + sun: "var(--color-red)", + sat: "var(--color-blue)", +}; + +const SCHEDULE_TYPE_TO_COLOR = { + main: COLOR_TABLE[0], + schedule: COLOR_TABLE[5], + event: COLOR_TABLE[3], +}; + +const generateCalendarDates = (monthFirstDate: Dayjs) => { + const daysInMonth = monthFirstDate.daysInMonth(); + const frontBlankDate = monthFirstDate.day(); + const totalDate = daysInMonth + frontBlankDate; + const rowsInMonth = totalDate <= 35 ? 5 : 6; + return Array.from({ length: 7 * rowsInMonth }, (_, idx) => + idx < frontBlankDate || idx >= totalDate ? null : idx - frontBlankDate + 1, + ); +}; + +function Calendar({ monthFirstDate, calendarContents }: CalendarProps) { + const calendarDates = generateCalendarDates(monthFirstDate); + let endingSchedules = []; + + const daySchedules: DaySchedules = { + first: null, + second: null, + third: null, + }; + + const getDaySchedules = (date: number): CalendarScheduleProps[] => { + return calendarContents.reduce((acc: CalendarScheduleProps[], schedule) => { + const isFirstDay = date === schedule.start; + const isEndDay = date === schedule.end; + if (schedule.start <= date && date <= schedule.end) { + acc.push({ + content: schedule.content, + color: SCHEDULE_TYPE_TO_COLOR[schedule.type], + isFirst: isFirstDay, + isLast: isEndDay, + blockIdx: schedule?.blockIdx, + }); + if (isFirstDay) fillSchedule(schedule.content); + if (isEndDay) endingSchedules.push(schedule.content); + } + return acc; + }, []); + }; + + const deleteSchedule = (content: string) => { + for (const key in daySchedules) { + if (daySchedules[key] === content) daySchedules[key] = null; + } + }; + + const fillSchedule = (content: string) => { + const availableKey = Object.keys(daySchedules).find((key) => !daySchedules[key]); + if (availableKey) daySchedules[availableKey] = content; + }; + + return ( + + + {DAYS_OF_WEEK.map((day) => ( + + {day} + + ))} + + + {calendarDates?.map((item, idx) => { + const day = idx % 7 === 0 ? "sun" : idx % 7 === 6 ? "sat" : null; + const isToday = monthFirstDate.date(item).isSame(dayjs(), "day"); + const contentArr = getDaySchedules(item); + const dateInfo = Object.values(daySchedules).map((title) => + contentArr?.find((c) => c.content === title), + ); + + endingSchedules.forEach((item) => deleteSchedule(item)); + endingSchedules = []; + + return ( + + + {!isToday ? ( + item + ) : ( + + {item} + + )} + + <> + {dateInfo.map((item, idx2) => { + return ( + + {item?.isFirst ? item?.content : "\u00A0"} + + ); + })} + + + ); + })} + + + ); +} + +const EventBlock = styled.div<{ + color: string; + isFirst: boolean; + isLast: boolean; +}>` + font-size: 10px; + margin-bottom: 2px; + font-weight: 400; + white-space: nowrap; + color: white; + background-color: ${(props) => props.color}; + position: relative; + z-index: ${(props) => (props.isFirst ? 4 : 0)}; + padding-left: ${(props) => (props.isFirst ? "var(--gap-1)" : 0)}; + padding-right: ${(props) => (props.isLast ? "var(--gap-1)" : 0)}; +`; + +export default Calendar; diff --git a/constants/settingValue/eventContents.ts b/constants/contents/calendarSchedule.ts similarity index 59% rename from constants/settingValue/eventContents.ts rename to constants/contents/calendarSchedule.ts index 6ec359373..9ce228b28 100644 --- a/constants/settingValue/eventContents.ts +++ b/constants/contents/calendarSchedule.ts @@ -1,42 +1,40 @@ -import { TABLE_COLORS } from "../styles"; - -type Content = { +export interface CalendarContentProps { content: string; start: number; end: number; - color: string; - text: string; + type: "event" | "schedule" | "main"; + text?: string; blockIdx?: number; -}; +} -export const EVENT_CONTENT_2023: Record = { +export const EVENT_CONTENT_2023: Record = { 10: [ { content: "[10월] 에타 홍보 이벤트 추첨", start: 22, end: 24, - color: TABLE_COLORS[2], + type: "event", text: "에타에 동아리 홍보글을 올려주시면 100 포인트와 추첨을 통해 치킨 기프티콘을 드려요!", }, { content: "[시험기간] 랜덤선물 이벤트", start: 16, end: 22, - color: TABLE_COLORS[0], + type: "event", text: "항상 돌아오는 시험기간 파이팅 이벤트... 매일 단톡방에서 랜덤한 선착순 선물을 뿌립니다!", }, { content: "[시험기간] 스터디 투표 2배 이벤트 ", start: 16, end: 22, - color: TABLE_COLORS[3], + type: "event", text: "시험 기간에 스터디에 투표하면 점수를 2배로 받아요!", }, { content: "[오프라인] 번개 이벤트", start: 29, end: 31, - color: TABLE_COLORS[6], + type: "event", text: "진행 예정", }, ], @@ -45,21 +43,21 @@ export const EVENT_CONTENT_2023: Record = { content: "수원/안양 정기모임", start: 17, end: 18, - color: TABLE_COLORS[2], + type: "schedule", text: "정기모임", }, { content: "양천/강남", start: 18, end: 18, - color: TABLE_COLORS[0], + type: "schedule", text: "정기모임", }, { content: "정기 모임", start: 19, end: 19, - color: TABLE_COLORS[0], + type: "schedule", text: "정기모임", blockIdx: 1, }, @@ -67,7 +65,7 @@ export const EVENT_CONTENT_2023: Record = { content: "11월 홍보 이벤트 당첨자 선별", start: 26, end: 30, - color: TABLE_COLORS[3], + type: "event", text: "11월 홍보 이벤트 당첨자 선별", }, ], @@ -76,61 +74,61 @@ export const EVENT_CONTENT_2023: Record = { content: "시험 기간 이벤트", start: 4, end: 8, - color: TABLE_COLORS[0], + type: "event", text: "이벤트", }, { content: "홍보 이벤트 추첨", start: 22, end: 24, - color: TABLE_COLORS[1], + type: "event", text: "이벤트", }, { content: "수원/강남 펭귄 핫팩", start: 17, end: 31, - color: TABLE_COLORS[2], + type: "event", text: "이벤트", }, ], }; -export const EVENT_CONTENT_2024: Record = { +export const EVENT_CONTENT_2024: Record = { 6: [ { content: "시험기간 응원 선물 이벤트", start: 10, end: 14, - color: "var(--color-blue)", + type: "event", text: "시험기간 응원 기념으로 매일 단톡방에서 기프티콘을 뿌립니다!", }, { content: "동아리 1차 용인 MT", start: 25, end: 26, - color: "var(--color-mint)", + type: "schedule", text: "", }, { content: "모임 활성화 이벤트", start: 19, end: 20, - color: "var(--color-blue)", + type: "event", text: "이제 종강하고 동아리 내에서 본격적으로 다양한 모임을 진행해보려고 하는데요! 모임을 개최하고 진행해주시는 분께는 매번 5000원의 지원금을 드립니다!", }, { content: "소모임 개설 기간", start: 24, end: 30, - color: "var(--color-orange)", + type: "main", text: "방학동안 스터디 뿐만 아니라 다양한 장르의 그룹을 활성화 해보려고 해요! 토익, 자격증 등의 스터디 뿐만 아니라 카페 탐방, 영화 관람, 보드게임, 러닝, 취미 활동 등 모든 모임 개설이 가능합니다. 모임장에게는 2만원씩 지원 혜택이 있습니다.", }, { content: "홍보 이벤트", start: 24, end: 30, - color: "var(--color-blue)", + type: "event", text: "에타 홍보에 참여하고 상품 받아가세요! 총 10만원 쏩니다!", }, ], @@ -139,84 +137,170 @@ export const EVENT_CONTENT_2024: Record = { content: "동아리 2차 대성리 MT", start: 3, end: 4, - color: "var(--color-mint)", + type: "schedule", text: "", }, { content: "소모임 편성", start: 6, end: 7, - color: "var(--color-orange)", + type: "main", text: "", }, { content: "모임 활성화 이벤트", start: 8, end: 9, - color: "var(--color-blue)", + type: "event", text: "", }, { content: "알고리즘 공모전", start: 11, end: 12, - color: "var(--color-blue)", + type: "event", text: "", }, { content: "정기모임 진행 주간", - start: 19, + start: 18, end: 21, - color: "var(--color-mint)", + type: "schedule", text: "", }, { content: "라운지 및 피드, 채팅, 인스타 기능 출시", - start: 29, + start: 28, end: 31, - color: "var(--color-mint)", + type: "main", text: "", }, ], 8: [ { - content: "조모임 진행 기간", + content: "월간 체크", + start: 1, + end: 1, + type: "main", + text: "", + }, + { + content: "조모임 진행 주간", start: 8, end: 11, - color: "var(--color-orange)", + type: "schedule", text: "", }, { content: "커뮤니티 출시", start: 5, end: 6, - color: "var(--color-mint)", + type: "main", text: "", }, { - content: "지역별 정기모임 기간", - start: 15, - end: 18, - color: "var(--color-orange)", + content: "에브리타임 홍보 이벤트 시작 ~ ", + start: 13, + end: 15, + type: "event", + text: "", + }, + { + content: "지역 정기모임 주간", + start: 14, + end: 17, + type: "schedule", + text: "", + }, + + { + content: "온라인 스터디 오픈", + start: 20, + end: 22, + type: "main", text: "", }, { content: "추억의 뽑기 이벤트", start: 22, end: 23, - color: "var(--color-blue)", + type: "event", text: "", }, { content: "동아리 정비 기간", start: 26, end: 30, - color: "var(--color-mint)", + type: "schedule", + text: "", + }, + { + content: "한줄 카피라이팅 이벤트", + start: 28, + end: 30, + type: "event", + text: "", + }, + ], + 9: [ + { + content: "동아리 리뉴얼 ~ ", + start: 2, + end: 4, + type: "main", + text: "에브리타임에 홍보하면 매주 2분께 올리브영 기프티콘을 드려요!", + }, + { + content: "에타 홍보 이벤트 ~ ", + start: 3, + end: 5, + type: "event", + text: "에브리타임에 홍보하면 매주 2분께 올리브영 기프티콘을 드려요!", + }, + { + content: "디스코드 오픈 이벤트 ~ ", + start: 10, + end: 12, + type: "event", + text: "동아리 스터디 디스코드 채널이 생겼습니다! 같이 공부하고 이벤트 상품 받아가세요!", + }, + { + content: "열공 스터디 이벤트 ~ ", + start: 9, + end: 11, + type: "event", + text: "동아리 스터디 디스코드 채널이 생겼습니다! 같이 공부하고 이벤트 상품 받아가세요!", + }, + { + content: "유령인원 정리 기간", + start: 16, + end: 17, + type: "main", + text: "", + }, + { + content: "동아리 전체 정기모임", + start: 13, + end: 14, + type: "main", + text: "", + }, + { + content: "지역 정기모임 주간", + start: 19, + end: 22, + type: "schedule", + text: "", + }, + { + content: "ABOUT 빙고판 이벤트", + start: 17, + end: 28, + type: "event", text: "", }, ], - 9: [], 10: [], 11: [], 12: [], diff --git a/constants/contentsText/accordionContents.ts b/constants/contentsText/accordionContents.ts index 0a486fc73..88baeeef8 100644 --- a/constants/contentsText/accordionContents.ts +++ b/constants/contentsText/accordionContents.ts @@ -1,5 +1,5 @@ import { IAccordionContent } from "../../components/molecules/Accordion"; -import { EVENT_ALWAYS, EVENT_CONTENT_2024 } from "../../constants/settingValue/eventContents"; +import { EVENT_ALWAYS, EVENT_CONTENT_2024 } from "../contents/calendarSchedule"; //회원가입 질문 컨텐츠 export const ACCORDION_CONTENT_FEE: IAccordionContent[] = [ diff --git a/constants/location.ts b/constants/location.ts index 6e40f0d35..b8a30ba04 100644 --- a/constants/location.ts +++ b/constants/location.ts @@ -2,11 +2,12 @@ import { ActiveLocation, InactiveLocation, Location, - LocationEn, + LocationEn } from "../types/services/locationTypes"; -import { TABLE_COLORS } from "./styles"; +import { COLOR_TABLE } from "./colorConstants"; + +export const LOCATION_OPEN: ActiveLocation[] = ["수원", "양천", "강남", "동대문", "인천", "안양"]; -export const LOCATION_OPEN: ActiveLocation[] = ["수원", "양천", "안양", "강남", "동대문", "인천"]; export const LOCATION_RECRUITING: InactiveLocation[] = [ "마포", "성남", @@ -23,7 +24,7 @@ export const LOCATION_RECRUITING: InactiveLocation[] = [ export const LOCATION_ALL = [...LOCATION_OPEN, ...LOCATION_RECRUITING]; -export const LOCATION_CONVERT: Record = { +export const LOCATION_TO_FULLNAME: Record = { 수원: "수원시", 양천: "양천구 · 영등포구", 안양: "안양 인근 지역", @@ -76,15 +77,6 @@ export const LOCATION_MEMBER_CNT: { 시흥: { member: 7, new: 2 }, }; -export const LOCATION_TABLE_COLOR: Record = { - 수원: TABLE_COLORS[0], - 양천: TABLE_COLORS[3], - 안양: TABLE_COLORS[2], - 강남: TABLE_COLORS[1], - 인천: TABLE_COLORS[4], - 동대문: TABLE_COLORS[5], -}; - export const krToEnMapping: Record = { 수원: "suw", 강남: "gan", @@ -124,3 +116,12 @@ export const enToKrMapping: Record = { buc: "부천", sih: "시흥", }; + +export const LOCATION_TO_COLOR: Record = { + 수원: COLOR_TABLE[0], + 양천: COLOR_TABLE[1], + 강남: COLOR_TABLE[3], + 동대문: COLOR_TABLE[4], + 인천: COLOR_TABLE[2], + 안양: COLOR_TABLE[5], +}; diff --git a/constants/serviceConstants/studyConstants/studyLocationConstants.ts b/constants/serviceConstants/studyConstants/studyLocationConstants.ts index 68645e127..c8b78f57d 100644 --- a/constants/serviceConstants/studyConstants/studyLocationConstants.ts +++ b/constants/serviceConstants/studyConstants/studyLocationConstants.ts @@ -208,7 +208,6 @@ export const PLACE_TO_LOCATION = createPlaceToLocationMap(LOCATION_TO_PLACE); function createPlaceToLocationMap(obj: LocationToPlace) { const placeToLocationMap: { [key: string]: ActiveLocation } = {}; - Object.entries(obj).forEach(([location, ids]) => { ids.forEach((id) => { placeToLocationMap[id] = location as ActiveLocation; diff --git a/modals/InviteUserModal.tsx b/modals/InviteUserModal.tsx index 739ef7b14..95b97b96b 100644 --- a/modals/InviteUserModal.tsx +++ b/modals/InviteUserModal.tsx @@ -61,7 +61,7 @@ export default function InviteUserModal({ setIsModal }: IInviteUserModal) { }, }; - const buttonItems = [ + const buttonOptionsArr = [ { text: "수원", func: () => setLocation("수원"), @@ -90,7 +90,7 @@ export default function InviteUserModal({ setIsModal }: IInviteUserModal) { return ( - + onClickButton("전체"), @@ -64,7 +64,7 @@ export default function GatherLocationFilter() { return ( { + buttonOptionsArr={buttonOptionsArr.sort((x, y) => { if (x.text === "전체") return -1; if (y.text === "전체") return 1; if (x.text === defaultLocation) return -1; diff --git a/pageTemplates/home/HomeCalendarSection.tsx b/pageTemplates/home/HomeCalendarSection.tsx new file mode 100644 index 000000000..d12547e84 --- /dev/null +++ b/pageTemplates/home/HomeCalendarSection.tsx @@ -0,0 +1,50 @@ +import { Box, Flex } from "@chakra-ui/react"; +import dayjs from "dayjs"; +import { useState } from "react"; + +import { ColorLabelProps } from "../../components/atoms/ColorLabel"; +import MonthNav from "../../components/atoms/MonthNav"; +import Accordion from "../../components/molecules/Accordion"; +import ColorLabelRow from "../../components/molecules/rows/ColorLabelRow"; +import Calendar from "../../components/organisms/Calendar"; +import { COLOR_TABLE } from "../../constants/colorConstants"; +import { EVENT_CONTENT_2024 } from "../../constants/contents/calendarSchedule"; +import { ACCORDION_CONTENT_EVENT } from "../../constants/contentsText/accordionContents"; + +const SCHEDULE_CATEGORIES: ColorLabelProps[] = [ + { + text: "공식 행사", + colorText: COLOR_TABLE[0], + }, + { + text: "이벤트", + colorText: COLOR_TABLE[3], + }, + { + text: "일정", + colorText: COLOR_TABLE[5], + }, +]; + +function HomeCalendarSection() { + const [monthFirstDate, setMonthFirstDate] = useState(dayjs().startOf("month")); + const monthNum = monthFirstDate.month(); + + const calendarContents = + monthFirstDate.year() === 2024 ? EVENT_CONTENT_2024[monthFirstDate.month() + 1] : null; + return ( + <> + + + + + + + 일정 상세정보 + + + + ); +} + +export default HomeCalendarSection; diff --git a/pageTemplates/home/HomeClubSection.tsx b/pageTemplates/home/HomeClubSection.tsx deleted file mode 100644 index 8206ded7b..000000000 --- a/pageTemplates/home/HomeClubSection.tsx +++ /dev/null @@ -1,272 +0,0 @@ -import { Box, Flex } from "@chakra-ui/react"; -import dayjs from "dayjs"; -import { Fragment, useState } from "react"; -import styled from "styled-components"; - -import MonthNav from "../../components/atoms/MonthNav"; -import { PointCircleTextProps } from "../../components/atoms/PointCircleText"; -import Accordion from "../../components/molecules/Accordion"; -import PointCircleTextRow from "../../components/molecules/PointCircleTextRow"; -import { ACCORDION_CONTENT_EVENT } from "../../constants/contentsText/accordionContents"; -import { EVENT_CONTENT_2024 } from "../../constants/settingValue/eventContents"; -import { DAYS_OF_WEEK } from "../../constants/util/util"; -const DAYS_TITLE = ["포인트 2배", null, null, null, null, null, "점수 2배"]; - -interface IEventContent { - content: string; - color: string; - isFirst: boolean; - isLast: boolean; - blockIdx?: number; -} - -function HomeClubSection() { - const [navMonth, setNavMonth] = useState(dayjs().startOf("month")); - - const getFilledDates = (navMonth: dayjs.Dayjs) => { - const daysInMonth = navMonth.daysInMonth(); - const frontBlankDate = navMonth.day(); - const totalDate = daysInMonth + frontBlankDate; - const rowsInMonth = totalDate <= 35 ? 5 : 6; - return Array.from({ length: 7 * rowsInMonth }, (_, idx) => - idx < frontBlankDate || idx >= totalDate ? null : idx - frontBlankDate + 1, - ); - }; - - const filledDates = getFilledDates(navMonth); - - const eventBlocks: { [key: string]: string } = { - first: null, - second: null, - third: null, - }; - - let endBlocks = []; - - const filledContents = (date: number) => { - const eventArr = navMonth.year() === 2024 ? EVENT_CONTENT_2024[navMonth.month() + 1] : null; - - if (!eventArr) return; - return eventArr.reduce((acc: IEventContent[], event) => { - const isFirstDay = date === event.start; - const isEndDay = date === event.end; - if (event.start <= date && date <= event.end) { - acc.push({ - content: event.content, - color: event.color, - isFirst: isFirstDay, - isLast: isEndDay, - blockIdx: event?.blockIdx, - }); - if (isFirstDay) fillEventDate(event.content); - if (isEndDay) endBlocks.push(event.content); - } - return acc; - }, []); - }; - - const fillEventDate = (content: string) => { - const availableKey = Object.keys(eventBlocks).find((key) => !eventBlocks[key]); - if (availableKey) eventBlocks[availableKey] = content; - }; - - const deleteEventDate = (content: string) => { - for (const key in eventBlocks) { - if (eventBlocks[key] === content) eventBlocks[key] = null; - } - }; - - const textRowObj: PointCircleTextProps[] = [ - { - text: "공식 행사", - color: "mint", - }, - { - text: "이벤트", - color: "blue", - }, - { - text: "일정", - color: "orange", - }, - ]; - - return ( - <> - - - - - - - {DAYS_TITLE.map((day, idx) => ( -
{day}
- ))} -
- - {DAYS_OF_WEEK.map((day) => ( -
{day}
- ))} -
- - {filledDates?.map((item, idx) => { - const day = idx % 7 === 0 ? "sun" : idx % 7 === 6 ? "sat" : null; - const isToday = navMonth.date(item).isSame(dayjs(), "day"); - - const contentArr = filledContents(item); - const dateInfo = Object.values(eventBlocks).map((title) => - contentArr?.find((c) => c.content === title), - ); - - endBlocks.forEach((item) => deleteEventDate(item)); - endBlocks = []; - - return ( - - - {!isToday ? item : {item}} - - - {dateInfo.map((item, idx2) => { - return ( - - {item?.blockIdx !== undefined && ( - -   - - )} - - {item?.isFirst ? item?.content : "\u00A0"} - - - ); - })} - - - ); - })} - -
- - 이벤트 상세정보 - - - - ); -} - -const Calendar = styled.div` - width: 364px; - margin: 0 auto; - display: flex; - flex-direction: column; -`; - -const WeekTitleHeader = styled.div` - display: flex; - justify-content: space-between; - font-size: 10px; - color: var(--color-mint); - - margin-bottom: var(--gap-1); - font-weight: 600; - > div { - flex: 1; - text-align: center; - } -`; - -const DayOfWeek = styled.div` - display: flex; - justify-content: space-between; - background-color: var(--gray-200); - padding: var(--gap-1) 0; - font-size: 12px; - > div { - flex: 1; - text-align: center; - } - > div:first-child { - color: var(--color-red); - } - > div:last-child { - color: #6bafff; - } -`; - -const CalendarDates = styled.div` - display: grid; - background-color: white; - grid-template-columns: repeat(7, 1fr); - border: var(--border); - border-radius: var(--rounded); - > div:first-child { - color: var(--color-red); - } -`; - -const DateBlock = styled.div` - width: 52px; - padding-top: var(--gap-1); - font-size: 12px; - font-weight: 600; - text-align: center; - flex: 1; - border-top: var(--border); -`; - -const Date = styled.div<{ day: "sun" | "sat"; isToday: boolean }>` - position: relative; - height: 18px; - margin-bottom: var(--gap-1); - color: ${(props) => - props.isToday - ? "white" - : props.day === "sun" - ? "var(--color-red)" - : props.day === "sat" - ? "var(--color-blue)" - : null}; -`; - -const DateContent = styled.div``; - -const EventBlock = styled.div<{ - color: string; - isFirst: boolean; - isLast: boolean; -}>` - font-size: 10px; - margin-bottom: 2px; - font-weight: 400; - white-space: nowrap; - color: white; - background-color: ${(props) => props.color}; - position: relative; - - z-index: ${(props) => (props.isFirst ? 4 : 0)}; - padding-left: ${(props) => (props.isFirst ? "var(--gap-1)" : 0)}; - padding-right: ${(props) => (props.isLast ? "var(--gap-1)" : 0)}; -`; - -const TodayCircle = styled.div` - position: absolute; - top: 50%; - left: 50%; - width: 18px; - height: 18px; - border-radius: 50%; - transform: translate(-50%, -50%); - background-color: var(--gray-800); - color: white; -`; - -export default HomeClubSection; diff --git a/pageTemplates/home/HomeRankingSection.tsx b/pageTemplates/home/HomeRankingSection.tsx index 351edc77e..7f42d2d77 100644 --- a/pageTemplates/home/HomeRankingSection.tsx +++ b/pageTemplates/home/HomeRankingSection.tsx @@ -1,5 +1,5 @@ import RecommendationBannerCard from "../../components/organisms/cards/RecommendationBannerCard"; -import { HOME_RECOMMENDATION_TAB_CONTENTS } from "../../constants/contents/HomeRecommendationTab"; +import { HOME_RECOMMENDATION_TAB_CONTENTS } from "../../constants/contents/homeRecommendationTab"; function HomeRankingSection() { return ( diff --git a/pageTemplates/home/HomeReviewSection.tsx b/pageTemplates/home/HomeReviewSection.tsx index 00025e037..e52454a15 100644 --- a/pageTemplates/home/HomeReviewSection.tsx +++ b/pageTemplates/home/HomeReviewSection.tsx @@ -24,7 +24,7 @@ export default function HomeReviewSection() { } + rightComponent={} /> {imageArr && } diff --git a/pageTemplates/home/study/HomeStudyChart.tsx b/pageTemplates/home/study/HomeStudyChart.tsx index e321becac..f5768632d 100644 --- a/pageTemplates/home/study/HomeStudyChart.tsx +++ b/pageTemplates/home/study/HomeStudyChart.tsx @@ -1,11 +1,11 @@ import { Box } from "@chakra-ui/react"; import dayjs from "dayjs"; import dynamic from "next/dynamic"; -import { useRouter } from "next/navigation"; import HighlightedTextButton from "../../../components/atoms/buttons/HighlightedTextButton"; import SectionBar from "../../../components/molecules/bars/SectionBar"; import { ChartStudyOptions } from "../../../components/organisms/chart/ChartOptions"; +import { useToast } from "../../../hooks/custom/CustomToast"; import { VoteCntProps } from "../../../types/models/studyTypes/studyRecords"; interface HomeStudyChartProps { @@ -13,7 +13,8 @@ interface HomeStudyChartProps { } function HomeStudyChart({ voteCntArr }: HomeStudyChartProps) { - const router = useRouter(); + // const router = useRouter(); + const toast = useToast(); const ApexCharts = dynamic(() => import("react-apexcharts"), { ssr: false }); const filtered: VoteCntProps[] = voteCntArr?.reduce((acc, cur) => { @@ -35,13 +36,15 @@ function HomeStudyChart({ voteCntArr }: HomeStudyChartProps) { xArr.push(dayjs(obj.date).date() + ""); }); + const onClick = () => { + toast("warning", "24년 9월 5일 오픈"); + }; + return ( <> router.push("/calendar")} /> - } + rightComponent={} /> - - - - {filterData?.map((item, idx) => { - const arrivedInfo = item?.arrivedInfoList; +function RecordCalendar({ filterData, monthFirstDate }: IRecordCalendar) { + console.log(2, filterData); + const calendarContents: CalendarContentProps[] = filterData.flatMap((data) => { + const arrivedInfo = data?.arrivedInfoList; + const date = data?.date; + const dayjsDate = date && dayjsToStr(monthFirstDate.date(date)); + let openLocation = null; + for (const key in LOCATION_OPEN_DATE) { + if (LOCATION_OPEN_DATE[key] === dayjsDate) openLocation = key; + } + const openStudyLocation: Set<{ + location: string; + cnt: number; + }> = new Set(); + arrivedInfo?.forEach((place) => { + openStudyLocation.add({ + location: PLACE_TO_LOCATION[place.placeId], + cnt: place.arrivedInfo.length, + }); + }); + console.log(24, arrivedInfo, openLocation, openStudyLocation); + let tempCnt = 0; - const date = item?.date; - const dayjsDate = date && dayjsToStr(navMonth.date(date)); - let openLocation = null; - for (const key in LOCATION_OPEN_DATE) { - if (LOCATION_OPEN_DATE[key] === dayjsDate) openLocation = key; - } - const openStudyLocation: Set<{ - location: string; - cnt: number; - }> = new Set(); - arrivedInfo?.forEach((place) => { - openStudyLocation.add({ - location: PLACE_TO_LOCATION[place.placeId], - cnt: place.arrivedInfo.length, - }); - }); - let tempCnt = 0; - return ( - - {!openLocation ? ( - {date} - ) : ( - {date} - )} - {Array.from(openStudyLocation).map((location, idx) => { - if (idx > 2 || location.cnt < 2) return null; - tempCnt++; - return ( - - {tempCnt < 4 || openStudyLocation.size <= 3 ? ( - "Open" - ) : ( - - )} - - ); - })} - - ); - })} - - + const resData = Array.from(openStudyLocation) + .map((location, idx) => { + if (idx > 2 || location.cnt < 2) return null; + tempCnt++; + return { + content: "오픈", + start: date, + end: date, + type: "main" as "main" | "event" | "schedule", + blockIdx: tempCnt - 1, + }; + }) + .filter((place) => place !== null); + return resData; + // { + // Array.from(openStudyLocation).map((location, idx) => { + // if (idx > 2 || location.cnt < 2) return null; + // tempCnt++; + // return ( + // + // {tempCnt < 4 || openStudyLocation.size <= 3 ? ( + // "스터디 오픈" + // ) : ( + // + // )} + // + // ); + // }); + // } + }); + console.log(calendarContents); + return ( + + + + // + // + // + // {filterData?.map((data, idx) => { + // + // return ( + // + // {!openLocation ? ( + // {date} + // ) : ( + // {date} + // )} + // {Array.from(openStudyLocation).map((location, idx) => { + // if (idx > 2 || location.cnt < 2) return null; + // tempCnt++; + // return ( + // + // {tempCnt < 4 || openStudyLocation.size <= 3 ? ( + // "스터디 오픈" + // ) : ( + // + // )} + // + // ); + // })} + // + // ); + // })} + // + // ); } function DayOfWeek() { @@ -104,7 +150,7 @@ const LocationOpen = styled.div<{ location: Location }>` display: flex; justify-content: center; align-items: center; - border: ${(props) => `2px solid ${LOCATION_TABLE_COLOR[props.location]}`}; + border: ${(props) => `2px solid ${LOCATION_TO_COLOR[props.location]}`}; width: 24px; height: 24px; border-radius: 50%; @@ -112,7 +158,7 @@ const LocationOpen = styled.div<{ location: Location }>` const Open = styled.div<{ location: Location }>` font-size: 10px; - color: ${(props) => LOCATION_TABLE_COLOR[props.location] || "var(--gray-500)"}; + color: ${(props) => LOCATION_TO_COLOR[props.location]}; `; const DayLine = styled.div` diff --git a/pageTemplates/record/RecordCalendarSetting.tsx b/pageTemplates/record/RecordCalendarSetting.tsx index 4c9a8c189..0c10caa35 100644 --- a/pageTemplates/record/RecordCalendarSetting.tsx +++ b/pageTemplates/record/RecordCalendarSetting.tsx @@ -1,6 +1,7 @@ import dayjs, { Dayjs } from "dayjs"; import { useEffect } from "react"; +import { ALL_스터디인증 } from "../../constants/serviceConstants/studyConstants/studyPlaceConstants"; import { useErrorToast } from "../../hooks/custom/CustomToast"; import { useStudyAttendRecordQuery } from "../../hooks/study/queries"; import { DispatchBoolean, DispatchType } from "../../types/hooks/reactTypes"; @@ -40,11 +41,17 @@ function RecordCalendarSetting({ ? null : { date: idx - frontBlankDate + 1, arrivedInfoList: [] }, ); - studyRecords.forEach((item) => { - const filledIdx = dayjs(item.date).date() + frontBlankDate - 1; - const data = filledDates[filledIdx]; - if (data) data.arrivedInfoList = item.arrivedInfoList; - }); + + studyRecords + .map((study) => ({ + arrivedInfoList: study.arrivedInfoList.filter((item) => item.placeId !== ALL_스터디인증), + date: study.date, + })) + .forEach((item) => { + const filledIdx = dayjs(item.date).date() + frontBlankDate - 1; + const data = filledDates[filledIdx]; + if (data) data.arrivedInfoList = item.arrivedInfoList; + }); setArrivedCalendar(filledDates); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isLoading, navMonth, studyRecords]); diff --git a/pageTemplates/record/RecordLocationCategory.tsx b/pageTemplates/record/RecordLocationCategory.tsx index ebc25eb9e..93f17287d 100644 --- a/pageTemplates/record/RecordLocationCategory.tsx +++ b/pageTemplates/record/RecordLocationCategory.tsx @@ -1,11 +1,12 @@ +import { Box } from "@chakra-ui/react"; import { useEffect, useState } from "react"; -import styled from "styled-components"; -import { LOCATION_CONVERT, LOCATION_OPEN, LOCATION_TABLE_COLOR } from "../../constants/location"; +import ButtonGroups from "../../components/molecules/groups/ButtonGroups"; +import { LOCATION_OPEN, LOCATION_TO_COLOR, LOCATION_TO_FULLNAME } from "../../constants/location"; import { PLACE_TO_LOCATION } from "../../constants/serviceConstants/studyConstants/studyLocationConstants"; import { DispatchType } from "../../types/hooks/reactTypes"; import { IArrivedData } from "../../types/models/studyTypes/studyRecords"; -import { Location, LocationFilterType } from "../../types/services/locationTypes"; +import { ActiveLocationAll } from "../../types/services/locationTypes"; interface IRecordLocationCategory { initialData: IArrivedData[]; @@ -13,12 +14,7 @@ interface IRecordLocationCategory { } function RecordLocationCategory({ initialData, setFilterData }: IRecordLocationCategory) { - const [category, setCategory] = useState("전체"); - - const onClickBadge = (value: Location) => { - if (value === category) setCategory("전체"); - else setCategory(value); - }; + const [category, setCategory] = useState("전체"); useEffect(() => { if (!initialData) return; @@ -35,58 +31,31 @@ function RecordLocationCategory({ initialData, setFilterData }: IRecordLocationC } }, [category, initialData, setFilterData]); + const buttonOptionsArr = (["전체", ...LOCATION_OPEN] as ActiveLocationAll[]).map((location) => ({ + text: LOCATION_TO_FULLNAME[location] || "전체", + func: () => { + if (location === category) setCategory("전체"); + else setCategory(location); + }, + color: LOCATION_TO_COLOR[location], + })); + return ( - - - {LOCATION_OPEN.map((location) => ( - - ))} - - + + + ); } -const Layout = styled.div` - padding: 0 var(--gap-4); - height: 36px; - display: flex; - align-items: center; - justify-content: space-between; - background-color: var(--gray-200); - border-top: 1px solid var(--gray-300); - border-bottom: 1px solid var(--gray-300); - > div { - display: flex; - align-items: center; - } - > span:last-child { - font-size: 10px; - color: var(--gray-600); - } -`; - -const SpaceBadge = styled.section` - display: flex; - align-items: center; -`; - -const Button = styled.button<{ - location: Location; - category: LocationFilterType; -}>` - margin-right: var(--gap-3); - font-weight: 600; - color: ${(props) => LOCATION_TABLE_COLOR[props.location]}; - font-size: ${(props) => (props.category === props.location ? "14px" : "12px")}; - opacity: ${(props) => - props.category !== "전체" && props.category !== props.location ? "0.7" : "1"}; -`; - export default RecordLocationCategory; diff --git a/pageTemplates/record/RecordOverview.tsx b/pageTemplates/record/RecordOverview.tsx index 238200092..ca529a676 100644 --- a/pageTemplates/record/RecordOverview.tsx +++ b/pageTemplates/record/RecordOverview.tsx @@ -107,7 +107,6 @@ const MyRecordItem = styled.div` flex-direction: column; justify-content: space-around; height: 100%; - > div { display: flex; align-items: center; diff --git a/pageTemplates/record/RecordSkeleton.tsx b/pageTemplates/record/RecordSkeleton.tsx deleted file mode 100644 index cec0e61e6..000000000 --- a/pageTemplates/record/RecordSkeleton.tsx +++ /dev/null @@ -1,300 +0,0 @@ -import dayjs from "dayjs"; -import styled from "styled-components"; - -import Skeleton from "../../components/atoms/skeleton/Skeleton"; -import { LOCATION_CONVERT, LOCATION_OPEN, LOCATION_TABLE_COLOR } from "../../constants/location"; -import { Location } from "../../types/services/locationTypes"; - -interface IRecordSkeleton { - isCalendar: boolean; -} - -function RecordSkeleton({ isCalendar }: IRecordSkeleton) { - const blankDate = Array.from( - { - length: dayjs().date(1).day(), - }, - (_, i) => i + 1, - ); - - const totalDate = Array.from( - { - length: dayjs().daysInMonth(), - }, - (_, i) => i + 1, - ); - return ( - - - - -
- 스터디 오픈 - - temp - -
-
- 참여한 인원 - - temp - -
-
- -
- 내 참여 횟수 - - temp - -
-
- 내 최근 참여 - - temp - -
-
-
-
- - - {LOCATION_OPEN.map((location) => ( - - {LOCATION_CONVERT[location]} - - ))} - - - {isCalendar ? ( - - - - {blankDate?.map((item) => )} - - {totalDate?.map((item, idx) => ( - - {item} - - Open - - - ))} - - - ) : ( - - {new Array(6).fill(0).map((_, idx) => ( - - - temp - - - {new Array(2).fill(0).map((_, idx2) => ( - - - - tempte - - - temp - - - - {new Array(3).fill(0).map((who, idx3) => ( - - temp - - ))} - - - ))} - - - ))} - - )} -
- ); -} -function DayOfWeek() { - return ( - - - - - - - - - - ); -} - -const Layout = styled.div``; - -/** overview */ -const RecordOverview = styled.div` - padding: var(--gap-3) var(--gap-4); - display: flex; - justify-content: space-between; - align-items: center; - height: 80px; -`; - -const MyRecord = styled.div` - display: flex; - height: 100%; - > div:first-child { - width: 125px; - } - > div:last-child { - width: 140px; - } -`; - -const MyRecordItem = styled.div` - display: flex; - flex-direction: column; - justify-content: space-around; - height: 100%; - - > div { - display: flex; - align-items: center; - } -`; -const ContentName = styled.span` - margin-right: var(--gap-2); - color: var(--gray-600); - font-size: 13px; -`; - -const ContentValue = styled.span` - font-weight: 700; - font-size: 14px; - color: var(--gray-700); -`; -const SpaceBadge = styled.section` - display: flex; - align-items: center; -`; - -const Button2 = styled.button<{ - location: Location; -}>` - margin-right: var(--gap-3); - font-weight: 600; - color: ${(props) => LOCATION_TABLE_COLOR[props.location]}; - font-size: 12px; -`; - -/** category */ -const Category = styled.div` - padding: 0 var(--gap-4); - height: 36px; - display: flex; - align-items: center; - justify-content: space-between; - background-color: var(--gray-200); - border-top: 1px solid var(--gray-300); - border-bottom: 1px solid var(--gray-300); - > div { - display: flex; - align-items: center; - } - > span:last-child { - font-size: 10px; - color: var(--gray-600); - } -`; - -/** calendar */ - -const Calendar = styled.div``; - -const CallenderDays = styled.div` - display: grid; - grid-auto-rows: 72px; - grid-template-columns: repeat(7, 1fr); - margin: 0 var(--gap-1); - font-size: 14px; -`; -const DayItem = styled.div` - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - border: 1px solid var(--gray-200); -`; - -const DayItemDate = styled.span<{ isToday: boolean }>` - color: ${(props) => (props.isToday ? "var(--color-mint)" : null)}; - font-weight: ${(props) => (props.isToday ? "600" : null)}; - font-size: ${(props) => (props.isToday ? "15px" : null)}; -`; - -const Open = styled.div` - font-size: 10px; -`; - -const DayLine = styled.div` - margin: var(--gap-2) 24px; - display: flex; - justify-content: space-between; - color: var(--gray-600); - font-size: 12px; -`; - -/** detail */ - -export const Detail = styled.div` - display: flex; - flex-direction: column; -`; -const Block = styled.div` - border-top: 4px solid var(--gray-200); - padding: var(--gap-3) var(--gap-4); - padding-bottom: 0; -`; -const Date = styled.div` - margin-bottom: var(--gap-3); - font-size: 13px; - color: var(--gray-700); -`; -const StudyInfo = styled.div` - font-size: 12px; - color: var(--gray-700); -`; -const PlaceInfo = styled.div` - margin-bottom: var(--gap-3); -`; - -const PlaceName = styled.div` - display: flex; - align-items: center; - color: var(--gray-700); - font-size: 14px; - > span:first-child { - font-weight: 600; - margin-right: var(--gap-1); - } -`; -const OpenLocation = styled.span` - font-size: 11px; -`; - -const MemberWrapper = styled.div` - margin-top: var(--gap-3); - - display: grid; - grid-template-columns: repeat(auto-fill, minmax(20%, auto)); - align-items: center; - line-height: 2; -`; - -const Member = styled.span` - margin-right: var(--gap-1); - color: var(--gray-600); -`; -export default RecordSkeleton; diff --git a/pageTemplates/record/detail/RecordDetailStudyBlock.tsx b/pageTemplates/record/detail/RecordDetailStudyBlock.tsx index ab569113e..29522d8e2 100644 --- a/pageTemplates/record/detail/RecordDetailStudyBlock.tsx +++ b/pageTemplates/record/detail/RecordDetailStudyBlock.tsx @@ -1,7 +1,7 @@ import { Fragment } from "react"; import styled from "styled-components"; -import { LOCATION_TABLE_COLOR } from "../../../constants/location"; +import { LOCATION_TO_COLOR } from "../../../constants/location"; import { PLACE_TO_NAME } from "../../../constants/serviceConstants/studyConstants/studyCafeNameConstants"; import { PLACE_TO_LOCATION } from "../../../constants/serviceConstants/studyConstants/studyLocationConstants"; import { Location } from "../../../types/services/locationTypes"; @@ -59,7 +59,7 @@ const PlaceInfo = styled.div<{ location: Location }>` width: 148px; flex-shrink: 0; margin-bottom: var(--gap-4); - border: ${(props) => `1px solid ${LOCATION_TABLE_COLOR[props.location]}`}; + border: ${(props) => `1px solid ${LOCATION_TO_COLOR[props.location]}`}; border-radius: var(--rounded-lg); padding: var(--gap-2); @@ -79,7 +79,7 @@ const PlaceName = styled.div` const OpenLocation = styled.span<{ location: Location }>` font-size: 11px; - color: ${(props) => LOCATION_TABLE_COLOR[props.location]}; + color: ${(props) => LOCATION_TO_COLOR[props.location]}; `; const MemberWrapper = styled.div` diff --git a/pageTemplates/register/location/LocationMember.tsx b/pageTemplates/register/location/LocationMember.tsx index 90ba38724..d1e82cfe0 100644 --- a/pageTemplates/register/location/LocationMember.tsx +++ b/pageTemplates/register/location/LocationMember.tsx @@ -1,12 +1,12 @@ import styled from "styled-components"; -import { LOCATION_CONVERT, LOCATION_MEMBER_CNT } from "../../../constants/location"; +import { LOCATION_MEMBER_CNT, LOCATION_TO_FULLNAME } from "../../../constants/location"; import { Location } from "../../../types/services/locationTypes"; function LocationMember({ location }: { location: Location }) { return ( - {LOCATION_CONVERT[location]} + {LOCATION_TO_FULLNAME[location]} {LOCATION_MEMBER_CNT[location].new} diff --git a/pageTemplates/square/SecretSquare/SecretSquareCategories.tsx b/pageTemplates/square/SecretSquare/SecretSquareCategories.tsx index 57648b426..70face7c4 100644 --- a/pageTemplates/square/SecretSquare/SecretSquareCategories.tsx +++ b/pageTemplates/square/SecretSquare/SecretSquareCategories.tsx @@ -1,7 +1,9 @@ import { Box } from "@chakra-ui/react"; import { Dispatch, SetStateAction } from "react"; -import ButtonGroups, { IButtonOptions } from "../../../components/molecules/groups/ButtonGroups"; +import ButtonGroups, { + ButtonOptionsProps, +} from "../../../components/molecules/groups/ButtonGroups"; import { type SecretSquareCategoryWithAll } from "../../../types/models/square"; const SECRET_SQUARE_CATEGORY: SecretSquareCategoryWithAll[] = [ @@ -21,7 +23,7 @@ function SecretSquareCategories({ category: selectedCategory, setCategory, }: SecretSquareCategoryProps) { - const buttonItems: IButtonOptions[] = SECRET_SQUARE_CATEGORY.map((category) => ({ + const buttonOptionsArr: ButtonOptionsProps[] = SECRET_SQUARE_CATEGORY.map((category) => ({ text: `#${category}`, func: () => setCategory(category), })); @@ -29,7 +31,7 @@ function SecretSquareCategories({ return ( { - return { - text: `${textObj[category]}`, - func: () => { - newSearchParams.set("category", category); - router.replace(`/square?${newSearchParams}`); - setCategory(category); - }, - }; - }, - ); - - + const buttonOptionsArr: ButtonOptionsProps[] = ( + ["all", "gather", "group"] as (FeedType | "all")[] + ).map((category) => { + return { + text: `${textObj[category]}`, + func: () => { + newSearchParams.set("category", category); + router.replace(`/square?${newSearchParams}`); + setCategory(category); + }, + }; + }); return ( - - + + - {!isLoading ? ( + {!isLoading && ( <> {isCalendar ? ( - + ) : ( )} - ) : ( - )} diff --git a/pages/eventCalendar.tsx b/pages/eventCalendar.tsx deleted file mode 100644 index 156ae2c8e..000000000 --- a/pages/eventCalendar.tsx +++ /dev/null @@ -1,278 +0,0 @@ -import { Box, Flex } from "@chakra-ui/react"; -import dayjs from "dayjs"; -import { Fragment, useState } from "react"; -import styled from "styled-components"; - -import MonthNav from "../components/atoms/MonthNav"; -import { PointCircleTextProps } from "../components/atoms/PointCircleText"; -import Header from "../components/layouts/Header"; -import Slide from "../components/layouts/PageSlide"; -import Accordion from "../components/molecules/Accordion"; -import PointCircleTextRow from "../components/molecules/PointCircleTextRow"; -import { ACCORDION_CONTENT_EVENT } from "../constants/contentsText/accordionContents"; -import { EVENT_CONTENT_2024 } from "../constants/settingValue/eventContents"; -import { DAYS_OF_WEEK } from "../constants/util/util"; -const DAYS_TITLE = ["포인트 2배", null, null, null, null, null, "점수 2배"]; - -interface IEventContent { - content: string; - color: string; - isFirst: boolean; - isLast: boolean; - blockIdx?: number; -} - -function EventCalendar() { - const [navMonth, setNavMonth] = useState(dayjs().startOf("month")); - - const getFilledDates = (navMonth: dayjs.Dayjs) => { - const daysInMonth = navMonth.daysInMonth(); - const frontBlankDate = navMonth.day(); - const totalDate = daysInMonth + frontBlankDate; - const rowsInMonth = totalDate <= 35 ? 5 : 6; - return Array.from({ length: 7 * rowsInMonth }, (_, idx) => - idx < frontBlankDate || idx >= totalDate ? null : idx - frontBlankDate + 1, - ); - }; - - const filledDates = getFilledDates(navMonth); - - const eventBlocks: { [key: string]: string } = { - first: null, - second: null, - third: null, - }; - - let endBlocks = []; - - const filledContents = (date: number) => { - const eventArr = navMonth.year() === 2024 ? EVENT_CONTENT_2024[navMonth.month() + 1] : null; - - if (!eventArr) return; - return eventArr.reduce((acc: IEventContent[], event) => { - const isFirstDay = date === event.start; - const isEndDay = date === event.end; - if (event.start <= date && date <= event.end) { - acc.push({ - content: event.content, - color: event.color, - isFirst: isFirstDay, - isLast: isEndDay, - blockIdx: event?.blockIdx, - }); - if (isFirstDay) fillEventDate(event.content); - if (isEndDay) endBlocks.push(event.content); - } - return acc; - }, []); - }; - - const fillEventDate = (content: string) => { - const availableKey = Object.keys(eventBlocks).find((key) => !eventBlocks[key]); - if (availableKey) eventBlocks[availableKey] = content; - }; - - const deleteEventDate = (content: string) => { - for (const key in eventBlocks) { - if (eventBlocks[key] === content) eventBlocks[key] = null; - } - }; - - const textRowObj: PointCircleTextProps[] = [ - { - text: "공식 행사", - color: "mint", - }, - { - text: "이벤트", - color: "blue", - }, - { - text: "일정", - color: "orange", - }, - ]; - - return ( - <> -
- - - - - - - - {DAYS_TITLE.map((day, idx) => ( -
{day}
- ))} -
- - {DAYS_OF_WEEK.map((day) => ( -
{day}
- ))} -
- - {filledDates?.map((item, idx) => { - const day = idx % 7 === 0 ? "sun" : idx % 7 === 6 ? "sat" : null; - const isToday = navMonth.date(item).isSame(dayjs(), "day"); - - const contentArr = filledContents(item); - const dateInfo = Object.values(eventBlocks).map((title) => - contentArr?.find((c) => c.content === title), - ); - - endBlocks.forEach((item) => deleteEventDate(item)); - endBlocks = []; - - return ( - - - {!isToday ? item : {item}} - - - {dateInfo.map((item, idx2) => { - return ( - - {item?.blockIdx !== undefined && ( - -   - - )} - - {item?.isFirst ? item?.content : "\u00A0"} - - - ); - })} - - - ); - })} - -
- - 이벤트 상세정보 - - -
- - ); -} - -const Calendar = styled.div` - width: 364px; - margin: 0 auto; - display: flex; - flex-direction: column; -`; - -const WeekTitleHeader = styled.div` - display: flex; - justify-content: space-between; - font-size: 10px; - color: var(--color-mint); - - margin-bottom: var(--gap-1); - font-weight: 600; - > div { - flex: 1; - text-align: center; - } -`; - -const DayOfWeek = styled.div` - display: flex; - justify-content: space-between; - background-color: var(--gray-200); - padding: var(--gap-1) 0; - font-size: 12px; - > div { - flex: 1; - text-align: center; - } - > div:first-child { - color: var(--color-red); - } - > div:last-child { - color: #6bafff; - } -`; - -const CalendarDates = styled.div` - display: grid; - background-color: white; - grid-template-columns: repeat(7, 1fr); - border: var(--border); - border-radius: var(--rounded); - > div:first-child { - color: var(--color-red); - } -`; - -const DateBlock = styled.div<{ isToday: boolean }>` - width: 52px; - padding-top: var(--gap-1); - font-size: 12px; - font-weight: 600; - text-align: center; - flex: 1; - border-top: var(--border); - background-color: ${(props) => (props.isToday ? "var(--gray-200)" : null)}; -`; - -const Date = styled.div<{ day: "sun" | "sat"; isToday: boolean }>` - position: relative; - height: 18px; - margin-bottom: var(--gap-1); - color: ${(props) => - props.isToday - ? "white" - : props.day === "sun" - ? "var(--color-red)" - : props.day === "sat" - ? "var(--color-blue)" - : null}; -`; - -const DateContent = styled.div``; - -const EventBlock = styled.div<{ - color: string; - isFirst: boolean; - isLast: boolean; -}>` - font-size: 10px; - margin-bottom: 2px; - font-weight: 400; - white-space: nowrap; - color: white; - background-color: ${(props) => props.color}; - position: relative; - - z-index: ${(props) => (props.isFirst ? 4 : 0)}; - padding-left: ${(props) => (props.isFirst ? "var(--gap-1)" : 0)}; - padding-right: ${(props) => (props.isLast ? "var(--gap-1)" : 0)}; -`; - -const TodayCircle = styled.div` - position: absolute; - top: 50%; - left: 50%; - width: 18px; - height: 18px; - border-radius: 50%; - transform: translate(-50%, -50%); - background-color: var(--gray-800); - color: white; -`; - -export default EventCalendar; diff --git a/pages/home/index.tsx b/pages/home/index.tsx index 09c55ad0b..d55eaf779 100644 --- a/pages/home/index.tsx +++ b/pages/home/index.tsx @@ -2,7 +2,7 @@ import { Box } from "@chakra-ui/react"; import { useState } from "react"; import Slide from "../../components/layouts/PageSlide"; -import HomeClubSection from "../../pageTemplates/home/HomeClubSection"; +import HomeCalendarSection from "../../pageTemplates/home/HomeCalendarSection"; import HomeGatherSection from "../../pageTemplates/home/HomeGatherSection"; import HomeHeader from "../../pageTemplates/home/homeHeader/HomeHeader"; import HomeInitialSetting from "../../pageTemplates/home/HomeInitialSetting"; @@ -26,7 +26,7 @@ function Home() { ) : tab === "번개" ? ( ) : tab === "캘린더" ? ( - + ) : tab === "추천" ? ( ) : null} diff --git a/pages/review/index.tsx b/pages/review/index.tsx index 086fc1c85..21a77f2ae 100644 --- a/pages/review/index.tsx +++ b/pages/review/index.tsx @@ -10,7 +10,7 @@ import KakaoShareBtn from "../../components/atoms/Icons/KakaoShareBtn"; import { MainLoading } from "../../components/atoms/loaders/MainLoading"; import Header from "../../components/layouts/Header"; import Slide from "../../components/layouts/PageSlide"; -import ButtonGroups, { IButtonOptions } from "../../components/molecules/groups/ButtonGroups"; +import ButtonGroups, { ButtonOptionsProps } from "../../components/molecules/groups/ButtonGroups"; import { LOCATION_OPEN } from "../../constants/location"; import { WEB_URL } from "../../constants/system"; import { useErrorToast } from "../../hooks/custom/CustomToast"; @@ -109,7 +109,7 @@ function Review() { setVisibleCnt((old) => old + 8); }; - const buttonArr: IButtonOptions[] = ["전체", ...LOCATION_OPEN].map((location) => ({ + const buttonArr: ButtonOptionsProps[] = ["전체", ...LOCATION_OPEN].map((location) => ({ text: location, func: () => location === "전체" @@ -136,7 +136,7 @@ function Review() { <> diff --git a/pages/study/writing/place.tsx b/pages/study/writing/place.tsx index 94e1cdcee..593d2439d 100644 --- a/pages/study/writing/place.tsx +++ b/pages/study/writing/place.tsx @@ -7,10 +7,12 @@ import styled from "styled-components"; import BottomNav from "../../../components/layouts/BottomNav"; import Header from "../../../components/layouts/Header"; import Slide from "../../../components/layouts/PageSlide"; -import ButtonGroups, { IButtonOptions } from "../../../components/molecules/groups/ButtonGroups"; +import ButtonGroups, { + ButtonOptionsProps, +} from "../../../components/molecules/groups/ButtonGroups"; import ProgressStatus from "../../../components/molecules/ProgressStatus"; import SearchLocation from "../../../components/organisms/SearchLocation"; -import { LOCATION_CONVERT, LOCATION_OPEN } from "../../../constants/location"; +import { LOCATION_OPEN, LOCATION_TO_FULLNAME } from "../../../constants/location"; import { useFailToast } from "../../../hooks/custom/CustomToast"; import RegisterLayout from "../../../pageTemplates/register/RegisterLayout"; import RegisterOverview from "../../../pageTemplates/register/RegisterOverview"; @@ -60,8 +62,8 @@ function WritingStudyPlace() { router.push(`/study/writing/content`); }; - const buttonItems: IButtonOptions[] = LOCATION_OPEN.map((locationInfo) => ({ - text: LOCATION_CONVERT[locationInfo], + const buttonOptionsArr: ButtonOptionsProps[] = LOCATION_OPEN.map((locationInfo) => ({ + text: LOCATION_TO_FULLNAME[locationInfo], func: () => setLocation(locationInfo), })); @@ -77,8 +79,8 @@ function WritingStudyPlace() {