From d44f1c364cb485e06adfc02897cfa9d2d1e66f53 Mon Sep 17 00:00:00 2001 From: jusohn Date: Fri, 27 Oct 2023 15:21:22 +0900 Subject: [PATCH 01/48] =?UTF-8?q?[FE]=20FEAT:=20config=20=EC=97=90=20?= =?UTF-8?q?=EC=97=B0=EC=9E=A5=EA=B6=8C=20=EC=82=AC=EC=9A=A9=EA=B8=B0?= =?UTF-8?q?=EA=B0=84=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config | 2 +- frontend/src/App.tsx | 2 - .../CabinetInfoArea.container.tsx | 10 +- .../LeftNav/LeftMainNav/LeftMainNav.tsx | 47 +++--- .../LeftNav/LeftSectionNav/LeftSectionNav.tsx | 13 +- .../Modals/ExtendModal/ExtendModal.tsx | 140 ++++++++++++++++++ .../TopNavButtonGroup/TopNavButtonGroup.tsx | 25 ++++ frontend/src/pages/admin/SearchPage.tsx | 2 +- frontend/src/utils/dateUtils.ts | 57 +++++-- 9 files changed, 251 insertions(+), 47 deletions(-) create mode 100644 frontend/src/components/Modals/ExtendModal/ExtendModal.tsx diff --git a/config b/config index 499029fd1..c1c4afb2c 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 499029fd15b4237ce1575038f139f20228c6b186 +Subproject commit c1c4afb2c7a7101f4c8f6e48933a7ad4ea3a4158 diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 740f2a360..ece075b5f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -8,8 +8,6 @@ import MainPage from "@/pages/MainPage"; import AdminMainPage from "@/pages/admin/AdminMainPage"; import LoadingAnimation from "@/components/Common/LoadingAnimation"; import ProfilePage from "./pages/ProfilePage"; -import "./firebase-messaging-sw" - const NotFoundPage = lazy(() => import("@/pages/NotFoundPage")); const LoginFailurePage = lazy(() => import("@/pages/LoginFailurePage")); diff --git a/frontend/src/components/CabinetInfoArea/CabinetInfoArea.container.tsx b/frontend/src/components/CabinetInfoArea/CabinetInfoArea.container.tsx index 02df2ba0d..143de05ad 100644 --- a/frontend/src/components/CabinetInfoArea/CabinetInfoArea.container.tsx +++ b/frontend/src/components/CabinetInfoArea/CabinetInfoArea.container.tsx @@ -70,7 +70,7 @@ export type TModalState = export type TAdminModalState = "returnModal" | "statusModal" | "clubLentModal"; -const calExpiredTime = (expireTime: Date) => +export const calExpiredTime = (expireTime: Date) => Math.floor( (expireTime.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24) ); @@ -102,7 +102,9 @@ const getCabinetUserList = (selectedCabinetInfo: CabinetInfo): string => { return userNameList; }; -const getDetailMessage = (selectedCabinetInfo: CabinetInfo): string | null => { +export const getDetailMessage = ( + selectedCabinetInfo: CabinetInfo +): string | null => { const { status, lentType, lents } = selectedCabinetInfo; // 밴, 고장 사물함 if (status === CabinetStatus.BANNED || status === CabinetStatus.BROKEN) @@ -120,7 +122,9 @@ const getDetailMessage = (selectedCabinetInfo: CabinetInfo): string | null => { else return null; }; -const getDetailMessageColor = (selectedCabinetInfo: CabinetInfo): string => { +export const getDetailMessageColor = ( + selectedCabinetInfo: CabinetInfo +): string => { const { status, lentType, lents } = selectedCabinetInfo; // 밴, 고장 사물함 if (status === CabinetStatus.BANNED || status === CabinetStatus.BROKEN) diff --git a/frontend/src/components/LeftNav/LeftMainNav/LeftMainNav.tsx b/frontend/src/components/LeftNav/LeftMainNav/LeftMainNav.tsx index 7dd21770e..ca630e1d4 100644 --- a/frontend/src/components/LeftNav/LeftMainNav/LeftMainNav.tsx +++ b/frontend/src/components/LeftNav/LeftMainNav/LeftMainNav.tsx @@ -72,53 +72,58 @@ const LeftMainNav = ({ ? "active cabiButton" : " cabiButton" } + src={"/src/assets/images/search.svg"} onClick={onClickSearchButton} > - +
Search - + - +
Contact
- +
Club
- +
Logout
)} {!isAdmin && ( - - - Profile - + <> + +
+ Profile +
+ )} diff --git a/frontend/src/components/LeftNav/LeftSectionNav/LeftSectionNav.tsx b/frontend/src/components/LeftNav/LeftSectionNav/LeftSectionNav.tsx index 8b93607e5..c1f4b532d 100644 --- a/frontend/src/components/LeftNav/LeftSectionNav/LeftSectionNav.tsx +++ b/frontend/src/components/LeftNav/LeftSectionNav/LeftSectionNav.tsx @@ -73,14 +73,14 @@ const LeftSectionNav = ({ title="슬랙 캐비닛 채널 새창으로 열기" > 문의하기 - + onClickClubForm()} title="동아리 사물함 사용 신청서 새창으로 열기" > 동아리 신청서 - + @@ -147,7 +147,7 @@ const SectionLinkStyled = styled.div` display: flex; align-items: center; color: var(--gray-color); - & svg { + & img { width: 15px; height: 15px; margin-left: auto; @@ -155,9 +155,10 @@ const SectionLinkStyled = styled.div` @media (hover: hover) and (pointer: fine) { &:hover { color: var(--main-color); - svg { - stroke: var(--main-color); - } + } + &:hover img { + filter: invert(33%) sepia(55%) saturate(3554%) hue-rotate(230deg) + brightness(99%) contrast(107%); } } `; diff --git a/frontend/src/components/Modals/ExtendModal/ExtendModal.tsx b/frontend/src/components/Modals/ExtendModal/ExtendModal.tsx new file mode 100644 index 000000000..ab2254f00 --- /dev/null +++ b/frontend/src/components/Modals/ExtendModal/ExtendModal.tsx @@ -0,0 +1,140 @@ +import React, { useState } from "react"; +import { useRecoilState, useRecoilValue, useSetRecoilState } from "recoil"; +import { + currentCabinetIdState, + isCurrentSectionRenderState, + myCabinetInfoState, + targetCabinetInfoState, + userState, +} from "@/recoil/atoms"; +import Modal, { IModalContents } from "@/components/Modals/Modal"; +import ModalPortal from "@/components/Modals/ModalPortal"; +import { + FailResponseModal, + SuccessResponseModal, +} from "@/components/Modals/ResponseModal/ResponseModal"; +import { additionalModalType, modalPropsMap } from "@/assets/data/maps"; +import checkIcon from "@/assets/images/checkIcon.svg"; +import { MyCabinetInfoResponseDto } from "@/types/dto/cabinet.dto"; +import { + axiosCabinetById, + axiosExtendLentPeriod, + axiosMyLentInfo, // axiosExtend, // TODO: 연장권 api 생성 후 연결해야 함 +} from "@/api/axios/axios.custom"; +import { + getExtendedDateString, + getLastDayofMonthString, +} from "@/utils/dateUtils"; + +const ExtendModal: React.FC<{ + onClose: () => void; + cabinetId: Number; +}> = (props) => { + const [showResponseModal, setShowResponseModal] = useState(false); + const [hasErrorOnResponse, setHasErrorOnResponse] = useState(false); + const [modalTitle, setModalTitle] = useState(""); + const currentCabinetId = useRecoilValue(currentCabinetIdState); + const [myInfo, setMyInfo] = useRecoilState(userState); + const [myLentInfo, setMyLentInfo] = + useRecoilState(myCabinetInfoState); + const setTargetCabinetInfo = useSetRecoilState(targetCabinetInfoState); + const setIsCurrentSectionRender = useSetRecoilState( + isCurrentSectionRenderState + ); + const formattedExtendedDate = getExtendedDateString( + myLentInfo.lents ? myLentInfo.lents[0].expiredAt : undefined + ); + const extendDetail = `사물함 연장권 사용 시, + 대여 기간이 ${formattedExtendedDate} 23:59으로 + 연장됩니다. + 연장권 사용은 취소할 수 없습니다. + 연장권을 사용하시겠습니까?`; + const extendInfoDetail = `사물함을 대여하시면 연장권 사용이 가능합니다. +연장권은 ${getLastDayofMonthString( + null, + "/" + )} 23:59 이후 만료됩니다.`; + const getModalTitle = (cabinetId: number | null) => { + return cabinetId === null + ? modalPropsMap[additionalModalType.MODAL_OWN_EXTENSION].title + : modalPropsMap[additionalModalType.MODAL_USE_EXTENSION].title; + }; + const getModalDetail = (cabinetId: number | null) => { + return cabinetId === null ? extendInfoDetail : extendDetail; + }; + const getModalProceedBtnText = (cabinetId: number | null) => { + return cabinetId === null + ? modalPropsMap[additionalModalType.MODAL_OWN_EXTENSION].confirmMessage + : modalPropsMap[additionalModalType.MODAL_USE_EXTENSION].confirmMessage; + }; + const tryExtendRequest = async (e: React.MouseEvent) => { + if (currentCabinetId === 0 || myInfo.cabinetId === null) { + setHasErrorOnResponse(true); + setModalTitle("현재 대여중인 사물함이 없습니다."); + setShowResponseModal(true); + return; + } + try { + await axiosExtendLentPeriod(); + setMyInfo({ + ...myInfo, + cabinetId: currentCabinetId, + extensible: false, + }); + setIsCurrentSectionRender(true); + setModalTitle("연장되었습니다"); + try { + const { data } = await axiosCabinetById(currentCabinetId); + setTargetCabinetInfo(data); + } catch (error) { + throw error; + } + try { + const { data: myLentInfo } = await axiosMyLentInfo(); + setMyLentInfo(myLentInfo); + } catch (error) { + throw error; + } + } catch (error: any) { + setHasErrorOnResponse(true); + setModalTitle(error.response.data.message); + } finally { + setShowResponseModal(true); + } + }; + + const extendModalContents: IModalContents = { + type: myInfo.cabinetId === null ? "penaltyBtn" : "hasProceedBtn", + icon: checkIcon, + title: getModalTitle(myInfo.cabinetId), + detail: getModalDetail(myInfo.cabinetId), + proceedBtnText: getModalProceedBtnText(myInfo.cabinetId), + onClickProceed: + myInfo.cabinetId === null + ? async (e: React.MouseEvent) => { + props.onClose(); + } + : tryExtendRequest, + closeModal: props.onClose, + }; + + return ( + + {!showResponseModal && } + {showResponseModal && + (hasErrorOnResponse ? ( + + ) : ( + + ))} + + ); +}; + +export default ExtendModal; diff --git a/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx b/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx index c9243af63..07b888c35 100644 --- a/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx +++ b/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx @@ -8,12 +8,37 @@ import { } from "@/recoil/atoms"; import TopNavButton from "@/components/TopNav/TopNavButtonGroup/TopNavButton/TopNavButton"; import { CabinetInfo } from "@/types/dto/cabinet.dto"; +import { LentDto } from "@/types/dto/lent.dto"; +import { UserDto } from "@/types/dto/user.dto"; +import CabinetStatus from "@/types/enum/cabinet.status.enum"; +import CabinetType from "@/types/enum/cabinet.type.enum"; import { axiosCabinetById, axiosDeleteCurrentBanLog, } from "@/api/axios/axios.custom"; import useMenu from "@/hooks/useMenu"; +export const getDefaultCabinetInfo = (myInfo: UserDto): CabinetInfo => ({ + building: "", + floor: 0, + cabinetId: 0, + visibleNum: 0, + lentType: CabinetType.PRIVATE, + title: null, + maxUser: 0, + status: CabinetStatus.AVAILABLE, + section: "", + lents: [ + { + userId: myInfo.userId, + name: myInfo.name, + lentHistoryId: 0, + startedAt: new Date(), + expiredAt: new Date(), + }, + ] as LentDto[], + statusNote: "", +}); const TopNavButtonGroup = ({ isAdmin }: { isAdmin?: boolean }) => { const { toggleCabinet, toggleMap, openCabinet, closeAll } = useMenu(); const [currentCabinetId, setCurrentCabinetId] = useRecoilState( diff --git a/frontend/src/pages/admin/SearchPage.tsx b/frontend/src/pages/admin/SearchPage.tsx index 7552e3e2c..92f355210 100644 --- a/frontend/src/pages/admin/SearchPage.tsx +++ b/frontend/src/pages/admin/SearchPage.tsx @@ -61,7 +61,7 @@ const SearchPage = () => { searchValue.current, currentPage.current ); - + setSearchListByIntraId(searchResult.data.result ?? []); setTotalSearchList(Math.ceil(searchResult.data.totalLength / 10) ?? 0); setTimeout(() => { diff --git a/frontend/src/utils/dateUtils.ts b/frontend/src/utils/dateUtils.ts index 81b509b43..f39e1f014 100644 --- a/frontend/src/utils/dateUtils.ts +++ b/frontend/src/utils/dateUtils.ts @@ -1,3 +1,15 @@ +export const padTo2Digits = (num: number) => { + return num.toString().padStart(2, "0"); +}; + +export const formatDate = (date: Date, divider: string) => { + return [ + date.getFullYear(), + padTo2Digits(date.getMonth() + 1), + padTo2Digits(date.getDate()), + ].join(divider); +}; + export const getExpireDateString = ( lentType: string, existExpireDate?: Date @@ -10,19 +22,38 @@ export const getExpireDateString = ( if (!existExpireDate) { expireDate.setDate(expireDate.getDate() + parseInt(addDays)); - } - const padTo2Digits = (num: number) => { - return num.toString().padStart(2, "0"); - }; - const formatDate = (date: Date) => { - return [ - date.getFullYear(), - padTo2Digits(date.getMonth() + 1), - padTo2Digits(date.getDate()), - ].join("/"); - }; - - return formatDate(expireDate); + return formatDate(expireDate, "/"); +}; + +// 공유 사물함 반납 시 남은 대여일 수 차감 (원래 남은 대여일 수 * (남은 인원 / 원래 있던 인원)) +export const getShortenedExpireDateString = ( + lentType: string, + currentNumUsers: number, + existExpireDate: Date | undefined +) => { + if (lentType != "SHARE" || existExpireDate === undefined) return; + const dayInMilisec = 1000 * 60 * 60 * 24; + const expireDateInMilisec = new Date(existExpireDate).getTime(); + let secondUntilExpire = expireDateInMilisec - new Date().getTime(); + let daysUntilExpire = Math.ceil(secondUntilExpire / dayInMilisec) - 1; + let dateRemainig = + (daysUntilExpire * (currentNumUsers - 1)) / currentNumUsers; + let newExpireDate = new Date().getTime() + dateRemainig * dayInMilisec; + return formatDate(new Date(newExpireDate), "/"); +}; + +export const getExtendedDateString = (existExpireDate?: Date) => { + let expireDate = existExpireDate ? new Date(existExpireDate) : new Date(); + expireDate.setDate( + expireDate.getDate() + parseInt(import.meta.env.VITE_EXTENDED_LENT_PERIOD) + ); + return formatDate(expireDate, "/"); +}; + +export const getLastDayofMonthString = (date: Date | null, divider: string) => { + if (date === null) date = new Date(); + let lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0); + return formatDate(lastDay, divider); }; export const getTotalPage = (totalLength: number, size: number) => { From c58570c74ac641bab7777bde6235bb9ec1a68c91 Mon Sep 17 00:00:00 2001 From: ldw Date: Fri, 27 Oct 2023 17:59:02 +0900 Subject: [PATCH 02/48] =?UTF-8?q?[BE]=20FIX=20:=20=EC=82=AC=EB=AC=BC?= =?UTF-8?q?=ED=95=A8=20expiredAt=EC=9D=B4=20=EB=A7=8C=EB=A3=8C=EC=9D=BC=20?= =?UTF-8?q?23=EC=8B=9C59=EB=B6=8459=EC=B4=88=EA=B0=80=20=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=97=B0=EC=B2=B4?= =?UTF-8?q?=EC=9D=BC=EC=9D=B4=20=ED=95=98=EB=A3=A8=20=EC=A0=81=EA=B2=8C=20?= =?UTF-8?q?=EB=82=98=EC=98=A4=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/ftclub/cabinet/lent/domain/LentPolicyImpl.java | 5 ++++- backend/src/main/java/org/ftclub/cabinet/utils/DateUtil.java | 5 +++-- config | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentPolicyImpl.java b/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentPolicyImpl.java index a39b8b081..ed6e474c4 100644 --- a/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentPolicyImpl.java +++ b/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentPolicyImpl.java @@ -62,7 +62,10 @@ public LocalDateTime generateExpirationDate(LocalDateTime now, Cabinet cabinet, LentType lentType = cabinet.getLentType(); switch (lentType) { case PRIVATE: - return now.plusDays(getDaysForLentTermPrivate()); + return now.plusDays(getDaysForLentTermPrivate()) + .withHour(23) + .withMinute(59) + .withSecond(59); case SHARE: if (activeLentHistories.isEmpty()) { return DateUtil.getInfinityDate(); diff --git a/backend/src/main/java/org/ftclub/cabinet/utils/DateUtil.java b/backend/src/main/java/org/ftclub/cabinet/utils/DateUtil.java index 3362b8d7d..562e9c471 100644 --- a/backend/src/main/java/org/ftclub/cabinet/utils/DateUtil.java +++ b/backend/src/main/java/org/ftclub/cabinet/utils/DateUtil.java @@ -76,7 +76,7 @@ public static Long calculateTwoDateDiffAbs(LocalDateTime day1, LocalDateTime day * @return day1 - day2 */ public static Long calculateTwoDateDiff(LocalDateTime day1, LocalDateTime day2) { - return Duration.between(day2, day1).toDays(); + return Duration.between(day2, day1).toDays() + 1; } /** @@ -117,10 +117,11 @@ public static boolean isSameDay(LocalDateTime now) { /** * now 가 서버의 현재 시간보다 과거인지 확입합니다. + * * @param now * @return */ - public static boolean isPast(LocalDateTime now){ + public static boolean isPast(LocalDateTime now) { LocalDate currentServerDate = LocalDate.now(); return currentServerDate.isAfter(now.toLocalDate()); } diff --git a/config b/config index 1fcfe5ea9..23c8d65ee 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 1fcfe5ea94d056be95430eb8539e286fd567dd26 +Subproject commit 23c8d65eeb5a810c2e50d6b14ad31367d0327049 From ca945f694aaf0f11a7f273060e57bc6ff8c38998 Mon Sep 17 00:00:00 2001 From: ldw Date: Fri, 27 Oct 2023 18:10:56 +0900 Subject: [PATCH 03/48] =?UTF-8?q?[BE]=20FIX=20:=20=EB=82=A8=EC=9D=80?= =?UTF-8?q?=EB=A7=8C=EB=A3=8C=EC=9D=BC=EC=9E=90-=EB=B0=98=EB=82=A9?= =?UTF-8?q?=EC=9D=BC=EC=9E=90=EC=B0=A8=EC=9D=B4=20=EC=84=B1=EA=B3=B5=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cabinet/lent/domain/LentHistoryUnitTest.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/backend/src/test/java/org/ftclub/cabinet/lent/domain/LentHistoryUnitTest.java b/backend/src/test/java/org/ftclub/cabinet/lent/domain/LentHistoryUnitTest.java index 5e8250e1b..651b1af08 100644 --- a/backend/src/test/java/org/ftclub/cabinet/lent/domain/LentHistoryUnitTest.java +++ b/backend/src/test/java/org/ftclub/cabinet/lent/domain/LentHistoryUnitTest.java @@ -1,16 +1,19 @@ package org.ftclub.cabinet.lent.domain; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.LocalDateTime; import org.ftclub.cabinet.exception.DomainException; import org.ftclub.cabinet.exception.ExceptionStatus; import org.ftclub.cabinet.utils.DateUtil; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import java.time.LocalDateTime; - -import static org.junit.jupiter.api.Assertions.*; - class LentHistoryUnitTest { @Test @@ -161,7 +164,7 @@ void isValid_FAILURE_expiredAt_INFINITY() { lentHistory.endLent(LocalDateTime.now().plusDays(5)); // 2일 연체 반납 - assertEquals(2, lentHistory.getDaysDiffEndedAndExpired()); + assertEquals(3, lentHistory.getDaysDiffEndedAndExpired()); } From 48bfbb7c7e1988fb3014c99d008014a946b61591 Mon Sep 17 00:00:00 2001 From: ldw Date: Fri, 27 Oct 2023 19:22:49 +0900 Subject: [PATCH 04/48] =?UTF-8?q?[BE]=20FIX=20:=20=EB=A7=8C=EB=A3=8C?= =?UTF-8?q?=EC=9D=BC=EC=9E=90=20=EA=B3=84=EC=82=B0=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=EA=B3=BC=20=EC=97=B0=EC=B2=B4=EC=9D=BC=20=EA=B3=84=EC=82=B0=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cabinet/lent/domain/LentHistory.java | 439 +++++++++--------- .../org/ftclub/cabinet/utils/DateUtil.java | 2 +- .../lent/domain/LentHistoryUnitTest.java | 8 +- .../lent/domain/LentPolicyUnitTest.java | 14 +- 4 files changed, 238 insertions(+), 225 deletions(-) diff --git a/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentHistory.java b/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentHistory.java index 3389adefb..f4ae0dbba 100644 --- a/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentHistory.java +++ b/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentHistory.java @@ -1,5 +1,17 @@ package org.ftclub.cabinet.lent.domain; +import static javax.persistence.FetchType.LAZY; + +import java.time.LocalDateTime; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Version; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -12,11 +24,6 @@ import org.ftclub.cabinet.utils.DateUtil; import org.ftclub.cabinet.utils.ExceptionUtil; -import javax.persistence.*; -import java.time.LocalDateTime; - -import static javax.persistence.FetchType.LAZY; - /** * lent의 기록을 관리하기 위한 data mapper */ @@ -28,215 +35,215 @@ @Log4j2 public class LentHistory { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "LENT_HISTORY_ID") - private Long lentHistoryId; - - /** - * 낙관적 락을 위한 version - */ - @Version - @Getter(AccessLevel.NONE) - private Long version = 1L; - - /** - * 대여 시작일 - */ - @Column(name = "STARTED_AT", nullable = false) - private LocalDateTime startedAt; - - /** - * 연체 시작일 - */ - @Column(name = "EXPIRED_AT") - private LocalDateTime expiredAt = null; - - /** - * 반납일 - */ - @Column(name = "ENDED_AT") - private LocalDateTime endedAt = null; - - /** - * 대여하는 유저 - */ - @Column(name = "USER_ID", nullable = false) - private Long userId; - - /** - * 대여하는 캐비넷 - */ - @Column(name = "CABINET_ID", nullable = false) - private Long cabinetId; - - @JoinColumn(name = "USER_ID", insertable = false, updatable = false) - @ManyToOne(fetch = LAZY) - private User user; - - @JoinColumn(name = "CABINET_ID", insertable = false, updatable = false) - @ManyToOne(fetch = LAZY) - private Cabinet cabinet; - - protected LentHistory(LocalDateTime startedAt, LocalDateTime expiredAt, Long userId, - Long cabinetId) { - this.startedAt = startedAt; - this.expiredAt = expiredAt; - this.userId = userId; - this.cabinetId = cabinetId; - } - - /** - * @param startedAt 대여 시작일 - * @param expiredAt 연체 시작일 - * @param userId 대여하는 user id - * @param cabinetId 대여하는 cabinet id - * @return 인자 정보를 담고있는 {@link LentHistory} - */ - public static LentHistory of(LocalDateTime startedAt, LocalDateTime expiredAt, Long userId, - Long cabinetId) { - LentHistory lentHistory = new LentHistory(startedAt, expiredAt, userId, cabinetId); - if (!lentHistory.isValid()) { - throw new DomainException(ExceptionStatus.INVALID_ARGUMENT); - } - return lentHistory; - } - - /** - * startedAt, userId, cabinetId, expiredAt 의 null 이 아닌지 확인합니다. - * - * @return 유효한 인스턴스 여부 - */ - - private boolean isValid() { - return this.startedAt != null && this.userId != null && this.cabinetId != null - && this.expiredAt != null; - } - - /** - * endedAt 보다 startedAt 이 나중이 아닌지 확인합니다. endedAt 종료시점이 null 이 아닌지 확인합니다. - * - * @param endedAt 대여 종료 날짜, 시간 - * @return - */ - private boolean isEndLentValid(LocalDateTime endedAt) { - return endedAt != null && 0 <= DateUtil.calculateTwoDateDiff(endedAt, this.startedAt); - } - - - @Override - public boolean equals(final Object other) { - if (this == other) { - return true; - } - if (!(other instanceof LentHistory)) { - return false; - } - return (this.lentHistoryId.equals(((LentHistory) other).lentHistoryId)); - } - - /** - * 대여한 아이디와 같은지 비교한다. - * - * @param cabinetId 비교하고 싶은 id - * @return boolean 같으면 true 다르면 false - */ - public boolean isCabinetIdEqual(Long cabinetId) { - return this.cabinetId.equals(cabinetId); - } - - /** - * 만료일을 변경합니다. - * - * @param expiredAt 변경하고 싶은 만료일 - */ - public void setExpiredAt(LocalDateTime expiredAt) { - log.info("setExpiredAt : {}", expiredAt); - this.expiredAt = expiredAt; - ExceptionUtil.throwIfFalse(this.isValid(), - new DomainException(ExceptionStatus.INVALID_STATUS)); - } - - /** - * 만료일이 설정 되어있는 지 확인합니다. 만료일이 {@link DateUtil}의 infinityDate와 같으면 만료일이 설정되어 있지 않다고 판단합니다. - * - * @return 설정이 되어있으면 true 아니면 false - */ - public boolean isSetExpiredAt() { - LocalDateTime expiredAt = getExpiredAt(); - if (expiredAt == null) { - return false; - } - if (expiredAt.isEqual(DateUtil.getInfinityDate())) { - return false; - } - return true; - } - - /** - * 반납일이 설정 되어있는 지 확인합니다. 반납일이 {@link DateUtil}의 infinityDate와 같으면 만료일이 설정되어 있지 않다고 판단합니다. - * - * @return 설정이 되어있으면 ture 아니면 false - */ - public boolean isSetEndedAt() { - if (getEndedAt() == null) { - return false; - } - if (getEndedAt().isEqual(DateUtil.getInfinityDate())) { - return false; - } - return true; - } - - - /** - * 반납일과 만료일의 차이를 계산합니다. - * - * @return endedAt - expiredAt의 값을(일 기준) - */ - public Long getDaysDiffEndedAndExpired() { - if (isSetExpiredAt() && isSetEndedAt()) { - return DateUtil.calculateTwoDateDiff(endedAt, expiredAt); - } - return null; - } - - /** - * 만료일이 지났는지 확인합니다. - * - * @return 만료일이 지났으면 true 아니면 false, 만료일이 설정되어 있지 않으면 false - */ - public Boolean isExpired(LocalDateTime now) { - if (isSetExpiredAt()) { - return DateUtil.calculateTwoDateDiffCeil(expiredAt, now) > 0; - } - return false; - } - - /** - * 만료일까지 남은 일수를 계산합니다. 만료시간이 설정되지 않았으면 null을 반환합니다. - * - * @return 만료일까지 남은 일수 (만료일 - 현재시간) (일 기준, 올림) - */ - public Long getDaysUntilExpiration(LocalDateTime now) { - if (isSetExpiredAt()) { - return DateUtil.calculateTwoDateDiffCeil(expiredAt, now); - } - return null; - } - - - /** - * 반납일을 설정합니다. - * - * @param now 설정하려고 하는 반납일 - */ - public void endLent(LocalDateTime now) { - log.info("setEndLent : {}", now); - ExceptionUtil.throwIfFalse((this.isEndLentValid(now)), - new DomainException(ExceptionStatus.INVALID_ARGUMENT)); - this.endedAt = now; - ExceptionUtil.throwIfFalse((this.isValid()), - new DomainException(ExceptionStatus.INVALID_STATUS)); - } + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "LENT_HISTORY_ID") + private Long lentHistoryId; + + /** + * 낙관적 락을 위한 version + */ + @Version + @Getter(AccessLevel.NONE) + private Long version = 1L; + + /** + * 대여 시작일 + */ + @Column(name = "STARTED_AT", nullable = false) + private LocalDateTime startedAt; + + /** + * 연체 시작일 + */ + @Column(name = "EXPIRED_AT") + private LocalDateTime expiredAt = null; + + /** + * 반납일 + */ + @Column(name = "ENDED_AT") + private LocalDateTime endedAt = null; + + /** + * 대여하는 유저 + */ + @Column(name = "USER_ID", nullable = false) + private Long userId; + + /** + * 대여하는 캐비넷 + */ + @Column(name = "CABINET_ID", nullable = false) + private Long cabinetId; + + @JoinColumn(name = "USER_ID", insertable = false, updatable = false) + @ManyToOne(fetch = LAZY) + private User user; + + @JoinColumn(name = "CABINET_ID", insertable = false, updatable = false) + @ManyToOne(fetch = LAZY) + private Cabinet cabinet; + + protected LentHistory(LocalDateTime startedAt, LocalDateTime expiredAt, Long userId, + Long cabinetId) { + this.startedAt = startedAt; + this.expiredAt = expiredAt; + this.userId = userId; + this.cabinetId = cabinetId; + } + + /** + * @param startedAt 대여 시작일 + * @param expiredAt 연체 시작일 + * @param userId 대여하는 user id + * @param cabinetId 대여하는 cabinet id + * @return 인자 정보를 담고있는 {@link LentHistory} + */ + public static LentHistory of(LocalDateTime startedAt, LocalDateTime expiredAt, Long userId, + Long cabinetId) { + LentHistory lentHistory = new LentHistory(startedAt, expiredAt, userId, cabinetId); + if (!lentHistory.isValid()) { + throw new DomainException(ExceptionStatus.INVALID_ARGUMENT); + } + return lentHistory; + } + + /** + * startedAt, userId, cabinetId, expiredAt 의 null 이 아닌지 확인합니다. + * + * @return 유효한 인스턴스 여부 + */ + + private boolean isValid() { + return this.startedAt != null && this.userId != null && this.cabinetId != null + && this.expiredAt != null; + } + + /** + * endedAt 보다 startedAt 이 나중이 아닌지 확인합니다. endedAt 종료시점이 null 이 아닌지 확인합니다. + * + * @param endedAt 대여 종료 날짜, 시간 + * @return + */ + private boolean isEndLentValid(LocalDateTime endedAt) { + return endedAt != null && 0 <= DateUtil.calculateTwoDateDiff(endedAt, this.startedAt); + } + + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } + if (!(other instanceof LentHistory)) { + return false; + } + return (this.lentHistoryId.equals(((LentHistory) other).lentHistoryId)); + } + + /** + * 대여한 아이디와 같은지 비교한다. + * + * @param cabinetId 비교하고 싶은 id + * @return boolean 같으면 true 다르면 false + */ + public boolean isCabinetIdEqual(Long cabinetId) { + return this.cabinetId.equals(cabinetId); + } + + /** + * 만료일을 변경합니다. + * + * @param expiredAt 변경하고 싶은 만료일 + */ + public void setExpiredAt(LocalDateTime expiredAt) { + log.info("setExpiredAt : {}", expiredAt); + this.expiredAt = expiredAt; + ExceptionUtil.throwIfFalse(this.isValid(), + new DomainException(ExceptionStatus.INVALID_STATUS)); + } + + /** + * 만료일이 설정 되어있는 지 확인합니다. 만료일이 {@link DateUtil}의 infinityDate와 같으면 만료일이 설정되어 있지 않다고 판단합니다. + * + * @return 설정이 되어있으면 true 아니면 false + */ + public boolean isSetExpiredAt() { + LocalDateTime expiredAt = getExpiredAt(); + if (expiredAt == null) { + return false; + } + if (expiredAt.isEqual(DateUtil.getInfinityDate())) { + return false; + } + return true; + } + + /** + * 반납일이 설정 되어있는 지 확인합니다. 반납일이 {@link DateUtil}의 infinityDate와 같으면 만료일이 설정되어 있지 않다고 판단합니다. + * + * @return 설정이 되어있으면 ture 아니면 false + */ + public boolean isSetEndedAt() { + if (getEndedAt() == null) { + return false; + } + if (getEndedAt().isEqual(DateUtil.getInfinityDate())) { + return false; + } + return true; + } + + + /** + * 반납일과 만료일의 차이를 계산합니다. + * + * @return endedAt - expiredAt의 값을(일 기준) + */ + public Long getDaysDiffEndedAndExpired() { + if (isSetExpiredAt() && isSetEndedAt()) { + return DateUtil.calculateTwoDateDiff(endedAt, expiredAt) + 1; + } + return null; + } + + /** + * 만료일이 지났는지 확인합니다. + * + * @return 만료일이 지났으면 true 아니면 false, 만료일이 설정되어 있지 않으면 false + */ + public Boolean isExpired(LocalDateTime now) { + if (isSetExpiredAt()) { + return DateUtil.calculateTwoDateDiffCeil(expiredAt, now) > 0; + } + return false; + } + + /** + * 만료일까지 남은 일수를 계산합니다. 만료시간이 설정되지 않았으면 null을 반환합니다. + * + * @return 만료일까지 남은 일수 (만료일 - 현재시간) (일 기준, 올림) + */ + public Long getDaysUntilExpiration(LocalDateTime now) { + if (isSetExpiredAt()) { + return DateUtil.calculateTwoDateDiffCeil(expiredAt, now); + } + return null; + } + + + /** + * 반납일을 설정합니다. + * + * @param now 설정하려고 하는 반납일 + */ + public void endLent(LocalDateTime now) { + log.info("setEndLent : {}", now); + ExceptionUtil.throwIfFalse((this.isEndLentValid(now)), + new DomainException(ExceptionStatus.INVALID_ARGUMENT)); + this.endedAt = now; + ExceptionUtil.throwIfFalse((this.isValid()), + new DomainException(ExceptionStatus.INVALID_STATUS)); + } } diff --git a/backend/src/main/java/org/ftclub/cabinet/utils/DateUtil.java b/backend/src/main/java/org/ftclub/cabinet/utils/DateUtil.java index 562e9c471..3ee13802f 100644 --- a/backend/src/main/java/org/ftclub/cabinet/utils/DateUtil.java +++ b/backend/src/main/java/org/ftclub/cabinet/utils/DateUtil.java @@ -76,7 +76,7 @@ public static Long calculateTwoDateDiffAbs(LocalDateTime day1, LocalDateTime day * @return day1 - day2 */ public static Long calculateTwoDateDiff(LocalDateTime day1, LocalDateTime day2) { - return Duration.between(day2, day1).toDays() + 1; + return Duration.between(day2, day1).toDays(); } /** diff --git a/backend/src/test/java/org/ftclub/cabinet/lent/domain/LentHistoryUnitTest.java b/backend/src/test/java/org/ftclub/cabinet/lent/domain/LentHistoryUnitTest.java index 651b1af08..714886bcb 100644 --- a/backend/src/test/java/org/ftclub/cabinet/lent/domain/LentHistoryUnitTest.java +++ b/backend/src/test/java/org/ftclub/cabinet/lent/domain/LentHistoryUnitTest.java @@ -143,13 +143,17 @@ void isValid_FAILURE_expiredAt_INFINITY() { void getDaysDiffEndedAndExpired_성공_조기반납() { LentHistory lentHistory = LentHistory.of( LocalDateTime.now(), - LocalDateTime.now().plusDays(3), + LocalDateTime.now() + .plusDays(3) + .withHour(23) + .withMinute(59) + .withSecond(59), 1L, 1L); lentHistory.endLent(LocalDateTime.now()); // 바로 반납 - assertEquals(-3, lentHistory.getDaysDiffEndedAndExpired()); + assertEquals(-2, lentHistory.getDaysDiffEndedAndExpired()); } diff --git a/backend/src/test/java/org/ftclub/cabinet/lent/domain/LentPolicyUnitTest.java b/backend/src/test/java/org/ftclub/cabinet/lent/domain/LentPolicyUnitTest.java index af5f95beb..d9260eb23 100644 --- a/backend/src/test/java/org/ftclub/cabinet/lent/domain/LentPolicyUnitTest.java +++ b/backend/src/test/java/org/ftclub/cabinet/lent/domain/LentPolicyUnitTest.java @@ -40,7 +40,11 @@ class LentPolicyUnitTest { @DisplayName("성공: 만료일자 설정 - 개인사물함") void 성공_개인사물함_generateExpirationDate() { LocalDateTime current = LocalDateTime.now(); - LocalDateTime expect = LocalDateTime.now().plusDays(21); + LocalDateTime expect = LocalDateTime.now() + .plusDays(21) + .withHour(23) + .withMinute(59) + .withSecond(59); given(cabinetProperties.getLentTermPrivate()).willReturn(21); Cabinet cabinet = mock(Cabinet.class); @@ -223,9 +227,9 @@ class LentPolicyUnitTest { /** * @See {@link LentPolicyImpl#verifyUserForLent(User, Cabinet, int, List)} - * + *

* 설계 상의 문제로 테스트 코드 비활성화 처리 해두었습니다. - */ + */ // @Test // @DisplayName("실패: 블랙홀 유저") // void 실패_BLACKHOLED_USER_verifyUserForLent() { @@ -239,7 +243,6 @@ class LentPolicyUnitTest { // // assertEquals(LentPolicyStatus.BLACKHOLED_USER, result); // } - @Test @DisplayName("실패: ALL BAN 유저") void 실패_ALL_BANNED_USER_verifyUserForLent() { @@ -316,7 +319,7 @@ class LentPolicyUnitTest { /** * @See {@link LentPolicyImpl#verifyUserForLent(User, Cabinet, int, List)} - * + *

* 설계 상의 문제로 테스트 코드 비활성화 처리 해두었습니다. */ // @Test @@ -334,7 +337,6 @@ class LentPolicyUnitTest { // // assertEquals(LentPolicyStatus.FINE, result); // } - @Test @DisplayName("실패: FULL캐비넷 대여시도") void 실패_FULL_verifyCabinetForLent() { From c8c82c9596eb8b32649a64c6b905a085292133b6 Mon Sep 17 00:00:00 2001 From: Woo Joo Chae Date: Fri, 27 Oct 2023 19:56:36 +0900 Subject: [PATCH 05/48] =?UTF-8?q?[FE]=20Frontend=20=EA=B4=80=EB=A0=A8=20co?= =?UTF-8?q?nfig=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cabinet/config/SlackBotProperties.java | 29 ------------------- config | 2 +- 2 files changed, 1 insertion(+), 30 deletions(-) delete mode 100644 backend/src/main/java/org/ftclub/cabinet/config/SlackBotProperties.java diff --git a/backend/src/main/java/org/ftclub/cabinet/config/SlackBotProperties.java b/backend/src/main/java/org/ftclub/cabinet/config/SlackBotProperties.java deleted file mode 100644 index 21349fd0a..000000000 --- a/backend/src/main/java/org/ftclub/cabinet/config/SlackBotProperties.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.ftclub.cabinet.config; - -import lombok.Getter; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -@Component -@Getter -public class SlackBotProperties { - - @Value("${spring.slack.token.singing-secret}") - private String singingSecret; - - @Value("${spring.slack.token.bot-token}") - private String botToken; - - @Value("${spring.slack.token.app-token}") - private String appToken; - - @Value("${spring.slack.token.channel}") - private String channelId; - - @Value("${spring.slack.urls.slack-find-user}") - private String slackFindUserUrl; - - @Value("${spring.slack.urls.intra-email-domain}") - private String intraDomainUrl; - -} diff --git a/config b/config index 499029fd1..c1c4afb2c 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 499029fd15b4237ce1575038f139f20228c6b186 +Subproject commit c1c4afb2c7a7101f4c8f6e48933a7ad4ea3a4158 From 4694bebefe84dc32e98e2dd558efaedbd41e8c4c Mon Sep 17 00:00:00 2001 From: Woo Joo Chae Date: Fri, 27 Oct 2023 19:57:28 +0900 Subject: [PATCH 06/48] =?UTF-8?q?[BE]=20FIX:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20slack=20=ED=8C=8C=EC=9D=BC=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/application.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index e603bbb55..2e00e6d9f 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -4,7 +4,7 @@ server: port: 4242 spring: config: - import: classpath:application-auth.yml, classpath:application-mail.yml, classpath:application-slack.yml + import: classpath:application-auth.yml, classpath:application-mail.yml activate: on-profile: prod logging: @@ -16,7 +16,7 @@ server: port: 4242 spring: config: - import: classpath:application-auth.yml, classpath:application-mail.yml, classpath:application-slack.yml + import: classpath:application-auth.yml, classpath:application-mail.yml activate: on-profile: dev logging: @@ -28,7 +28,7 @@ server: port: 2424 spring: config: - import: classpath:application-auth.yml, classpath:application-mail.yml, classpath:application-slack.yml + import: classpath:application-auth.yml, classpath:application-mail.yml activate: on-profile: local logging: From 00de379f54cf31c9d9fe9762dc740c9f5ab7e3e0 Mon Sep 17 00:00:00 2001 From: Woo Joo Chae Date: Fri, 27 Oct 2023 19:57:37 +0900 Subject: [PATCH 07/48] =?UTF-8?q?[BE]=20HOTFIX:=20main=20hotfix=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cabinet/lent/domain/LentHistory.java | 428 ++++++++--------- .../cabinet/lent/domain/LentPolicyImpl.java | 437 +++++++++--------- 2 files changed, 432 insertions(+), 433 deletions(-) diff --git a/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentHistory.java b/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentHistory.java index dd7c6a0c9..0321d2b50 100644 --- a/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentHistory.java +++ b/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentHistory.java @@ -1,5 +1,17 @@ package org.ftclub.cabinet.lent.domain; +import static javax.persistence.FetchType.LAZY; + +import java.time.LocalDateTime; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Version; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -23,215 +35,209 @@ @Log4j2 public class LentHistory { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "LENT_HISTORY_ID") - private Long lentHistoryId; - - /** - * 낙관적 락을 위한 version - */ - @Version - @Getter(AccessLevel.NONE) - private Long version = 1L; - - /** - * 대여 시작일 - */ - @Column(name = "STARTED_AT", nullable = false) - private LocalDateTime startedAt; - - /** - * 연체 시작일 - */ - @Column(name = "EXPIRED_AT") - private LocalDateTime expiredAt = null; - - /** - * 반납일 - */ - @Column(name = "ENDED_AT") - private LocalDateTime endedAt = null; - - /** - * 대여하는 유저 - */ - @Column(name = "USER_ID", nullable = false) - private Long userId; - - /** - * 대여하는 캐비넷 - */ - @Column(name = "CABINET_ID", nullable = false) - private Long cabinetId; - - @JoinColumn(name = "USER_ID", insertable = false, updatable = false) - @ManyToOne(fetch = LAZY) - private User user; - - @JoinColumn(name = "CABINET_ID", insertable = false, updatable = false) - @ManyToOne(fetch = LAZY) - private Cabinet cabinet; - - protected LentHistory(LocalDateTime startedAt, LocalDateTime expiredAt, Long userId, - Long cabinetId) { - this.startedAt = startedAt; - this.expiredAt = expiredAt; - this.userId = userId; - this.cabinetId = cabinetId; - } - - /** - * @param startedAt 대여 시작일 - * @param expiredAt 연체 시작일 - * @param userId 대여하는 user id - * @param cabinetId 대여하는 cabinet id - * @return 인자 정보를 담고있는 {@link LentHistory} - */ - public static LentHistory of(LocalDateTime startedAt, LocalDateTime expiredAt, Long userId, - Long cabinetId) { - LentHistory lentHistory = new LentHistory(startedAt, expiredAt, userId, cabinetId); - if (!lentHistory.isValid()) { - throw new DomainException(ExceptionStatus.INVALID_ARGUMENT); - } - return lentHistory; - } - - /** - * startedAt, userId, cabinetId, expiredAt 의 null 이 아닌지 확인합니다. - * - * @return 유효한 인스턴스 여부 - */ - - private boolean isValid() { - return this.startedAt != null && this.userId != null && this.cabinetId != null - && this.expiredAt != null; - } - - /** - * endedAt 보다 startedAt 이 나중이 아닌지 확인합니다. endedAt 종료시점이 null 이 아닌지 확인합니다. - * - * @param endedAt 대여 종료 날짜, 시간 - * @return - */ - private boolean isEndLentValid(LocalDateTime endedAt) { - return endedAt != null && 0 <= DateUtil.calculateTwoDateDiff(endedAt, this.startedAt); - } - - - @Override - public boolean equals(final Object other) { - if (this == other) { - return true; - } - if (!(other instanceof LentHistory)) { - return false; - } - return (this.lentHistoryId.equals(((LentHistory) other).lentHistoryId)); - } - - /** - * 대여한 아이디와 같은지 비교한다. - * - * @param cabinetId 비교하고 싶은 id - * @return boolean 같으면 true 다르면 false - */ - public boolean isCabinetIdEqual(Long cabinetId) { - return this.cabinetId.equals(cabinetId); - } - - /** - * 만료일을 변경합니다. - * - * @param expiredAt 변경하고 싶은 만료일 - */ - public void setExpiredAt(LocalDateTime expiredAt) { - log.info("setExpiredAt : {}", expiredAt); - this.expiredAt = expiredAt; - ExceptionUtil.throwIfFalse(this.isValid(), - new DomainException(ExceptionStatus.INVALID_STATUS)); - } - - /** - * 만료일이 설정 되어있는 지 확인합니다. 만료일이 {@link DateUtil}의 infinityDate와 같으면 만료일이 설정되어 있지 않다고 판단합니다. - * - * @return 설정이 되어있으면 true 아니면 false - */ - public boolean isSetExpiredAt() { - LocalDateTime expiredAt = getExpiredAt(); - if (expiredAt == null) { - return false; - } - if (expiredAt.isEqual(DateUtil.getInfinityDate())) { - return false; - } - return true; - } - - /** - * 반납일이 설정 되어있는 지 확인합니다. 반납일이 {@link DateUtil}의 infinityDate와 같으면 만료일이 설정되어 있지 않다고 판단합니다. - * - * @return 설정이 되어있으면 ture 아니면 false - */ - public boolean isSetEndedAt() { - if (getEndedAt() == null) { - return false; - } - if (getEndedAt().isEqual(DateUtil.getInfinityDate())) { - return false; - } - return true; - } - - - /** - * 반납일과 만료일의 차이를 계산합니다. - * - * @return endedAt - expiredAt의 값을(일 기준) - */ - public Long getDaysDiffEndedAndExpired() { - if (isSetExpiredAt() && isSetEndedAt()) { - return DateUtil.calculateTwoDateDiff(endedAt, expiredAt) + 1; - } - return null; - } - - /** - * 만료일이 지났는지 확인합니다. - * - * @return 만료일이 지났으면 true 아니면 false, 만료일이 설정되어 있지 않으면 false - */ - public Boolean isExpired(LocalDateTime now) { - if (isSetExpiredAt()) { - return DateUtil.calculateTwoDateDiffCeil(expiredAt, now) > 0; - } - return false; - } - - /** - * 만료일까지 남은 일수를 계산합니다. 만료시간이 설정되지 않았으면 null을 반환합니다. - * - * @return 만료일까지 남은 일수 (만료일 - 현재시간) (일 기준, 올림) - */ - public Long getDaysUntilExpiration(LocalDateTime now) { - if (isSetExpiredAt()) { - return DateUtil.calculateTwoDateDiffCeil(expiredAt, now); - } - return null; - } - - - /** - * 반납일을 설정합니다. - * - * @param now 설정하려고 하는 반납일 - */ - public void endLent(LocalDateTime now) { - log.info("setEndLent : {}", now); - ExceptionUtil.throwIfFalse((this.isEndLentValid(now)), - new DomainException(ExceptionStatus.INVALID_ARGUMENT)); - this.endedAt = now; - ExceptionUtil.throwIfFalse((this.isValid()), - new DomainException(ExceptionStatus.INVALID_STATUS)); - } + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "LENT_HISTORY_ID") + private Long lentHistoryId; + + /** + * 낙관적 락을 위한 version + */ + @Version + @Getter(AccessLevel.NONE) + private final Long version = 1L; + + /** + * 대여 시작일 + */ + @Column(name = "STARTED_AT", nullable = false) + private LocalDateTime startedAt; + + /** + * 연체 시작일 + */ + @Column(name = "EXPIRED_AT") + private LocalDateTime expiredAt = null; + + /** + * 반납일 + */ + @Column(name = "ENDED_AT") + private LocalDateTime endedAt = null; + + /** + * 대여하는 유저 + */ + @Column(name = "USER_ID", nullable = false) + private Long userId; + + /** + * 대여하는 캐비넷 + */ + @Column(name = "CABINET_ID", nullable = false) + private Long cabinetId; + + @JoinColumn(name = "USER_ID", insertable = false, updatable = false) + @ManyToOne(fetch = LAZY) + private User user; + + @JoinColumn(name = "CABINET_ID", insertable = false, updatable = false) + @ManyToOne(fetch = LAZY) + private Cabinet cabinet; + + protected LentHistory(LocalDateTime startedAt, LocalDateTime expiredAt, Long userId, + Long cabinetId) { + this.startedAt = startedAt; + this.expiredAt = expiredAt; + this.userId = userId; + this.cabinetId = cabinetId; + } + + /** + * @param startedAt 대여 시작일 + * @param expiredAt 연체 시작일 + * @param userId 대여하는 user id + * @param cabinetId 대여하는 cabinet id + * @return 인자 정보를 담고있는 {@link LentHistory} + */ + public static LentHistory of(LocalDateTime startedAt, LocalDateTime expiredAt, Long userId, + Long cabinetId) { + LentHistory lentHistory = new LentHistory(startedAt, expiredAt, userId, cabinetId); + if (!lentHistory.isValid()) { + throw new DomainException(ExceptionStatus.INVALID_ARGUMENT); + } + return lentHistory; + } + + /** + * startedAt, userId, cabinetId, expiredAt 의 null 이 아닌지 확인합니다. + * + * @return 유효한 인스턴스 여부 + */ + + private boolean isValid() { + return this.startedAt != null && this.userId != null && this.cabinetId != null + && this.expiredAt != null; + } + + /** + * endedAt 보다 startedAt 이 나중이 아닌지 확인합니다. endedAt 종료시점이 null 이 아닌지 확인합니다. + * + * @param endedAt 대여 종료 날짜, 시간 + * @return + */ + private boolean isEndLentValid(LocalDateTime endedAt) { + return endedAt != null && 0 <= DateUtil.calculateTwoDateDiff(endedAt, this.startedAt); + } + + + @Override + public boolean equals(final Object other) { + if (this == other) { + return true; + } + if (!(other instanceof LentHistory)) { + return false; + } + return (this.lentHistoryId.equals(((LentHistory) other).lentHistoryId)); + } + + /** + * 대여한 아이디와 같은지 비교한다. + * + * @param cabinetId 비교하고 싶은 id + * @return boolean 같으면 true 다르면 false + */ + public boolean isCabinetIdEqual(Long cabinetId) { + return this.cabinetId.equals(cabinetId); + } + + /** + * 만료일을 변경합니다. + * + * @param expiredAt 변경하고 싶은 만료일 + */ + public void setExpiredAt(LocalDateTime expiredAt) { + log.info("setExpiredAt : {}", expiredAt); + this.expiredAt = expiredAt; + ExceptionUtil.throwIfFalse(this.isValid(), + new DomainException(ExceptionStatus.INVALID_STATUS)); + } + + /** + * 만료일이 설정 되어있는 지 확인합니다. 만료일이 {@link DateUtil}의 infinityDate와 같으면 만료일이 설정되어 있지 않다고 판단합니다. + * + * @return 설정이 되어있으면 true 아니면 false + */ + public boolean isSetExpiredAt() { + LocalDateTime expiredAt = getExpiredAt(); + if (expiredAt == null) { + return false; + } + return !expiredAt.isEqual(DateUtil.getInfinityDate()); + } + + /** + * 반납일이 설정 되어있는 지 확인합니다. 반납일이 {@link DateUtil}의 infinityDate와 같으면 만료일이 설정되어 있지 않다고 판단합니다. + * + * @return 설정이 되어있으면 ture 아니면 false + */ + public boolean isSetEndedAt() { + if (getEndedAt() == null) { + return false; + } + return !getEndedAt().isEqual(DateUtil.getInfinityDate()); + } + + + /** + * 반납일과 만료일의 차이를 계산합니다. + * + * @return endedAt - expiredAt의 값을(일 기준) + */ + public Long getDaysDiffEndedAndExpired() { + if (isSetExpiredAt() && isSetEndedAt()) { + return DateUtil.calculateTwoDateDiff(endedAt, expiredAt) + 1; + } + return null; + } + + /** + * 만료일이 지났는지 확인합니다. + * + * @return 만료일이 지났으면 true 아니면 false, 만료일이 설정되어 있지 않으면 false + */ + public Boolean isExpired(LocalDateTime now) { + if (isSetExpiredAt()) { + return DateUtil.calculateTwoDateDiffCeil(expiredAt, now) > 0; + } + return false; + } + + /** + * 만료일까지 남은 일수를 계산합니다. 만료시간이 설정되지 않았으면 null을 반환합니다. + * + * @return 만료일까지 남은 일수 (만료일 - 현재시간) (일 기준, 올림) + */ + public Long getDaysUntilExpiration(LocalDateTime now) { + if (isSetExpiredAt()) { + return DateUtil.calculateTwoDateDiffCeil(expiredAt, now); + } + return null; + } + + + /** + * 반납일을 설정합니다. + * + * @param now 설정하려고 하는 반납일 + */ + public void endLent(LocalDateTime now) { + log.info("setEndLent : {}", now); + ExceptionUtil.throwIfFalse((this.isEndLentValid(now)), + new DomainException(ExceptionStatus.INVALID_ARGUMENT)); + this.endedAt = now; + ExceptionUtil.throwIfFalse((this.isValid()), + new DomainException(ExceptionStatus.INVALID_STATUS)); + } } diff --git a/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentPolicyImpl.java b/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentPolicyImpl.java index 34a0a320a..56f65bc7c 100644 --- a/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentPolicyImpl.java +++ b/backend/src/main/java/org/ftclub/cabinet/lent/domain/LentPolicyImpl.java @@ -27,157 +27,150 @@ @Log4j2 public class LentPolicyImpl implements LentPolicy { - private final CabinetProperties cabinetProperties; - private final ApplicationEventPublisher publisher; - private final LentRedis lentRedis; - - @Override - public LocalDateTime generateSharedCabinetExpirationDate(LocalDateTime now, - Integer totalUserCount) { - log.info("Called generateSharedCabinetExpirationDate now: {}, totalUserCount: {}", now, - totalUserCount); - return now.plusDays(getDaysForLentTermShare(totalUserCount)) - .withHour(23) - .withMinute(59) - .withSecond(0); - } - - @Override - public LocalDateTime generateExpirationDate(LocalDateTime now, Cabinet cabinet) { - log.info("Called generateExpirationDate now: {}, cabinet: {}", now, cabinet); - - if (!DateUtil.isSameDay(now)) { - throw new IllegalArgumentException("현재 시각이 아닙니다."); - } - - LentType lentType = cabinet.getLentType(); - switch (lentType) { - case PRIVATE: - return now.plusDays(getDaysForLentTermPrivate()) - .withHour(23) - .withMinute(59) - .withSecond(59); - case SHARE: - if (activeLentHistories.isEmpty()) { - return DateUtil.getInfinityDate(); - } - LentHistory lentHistory = activeLentHistories.get(0); - return generateSharedCabinetExpirationDate(now, - cabinet.getStatus(), lentHistory); - case CLUB: - return DateUtil.getInfinityDate(); - } - throw new IllegalArgumentException("대여 상태가 잘못되었습니다."); - } - - @Override - public LocalDateTime generateExtendedExpirationDate(LocalDateTime now) { - log.info("Called generateExtendedExpirationDate now: {}, cabinet: {}", now); - if (DateUtil.isPast(now)) { - throw new DomainException(ExceptionStatus.LENT_EXPIRED); - } - return now.plusDays(getDaysForLentTermPrivate()) - .withHour(23) - .withMinute(59) - .withSecond(0); - } - - @Override - public void applyExpirationDate(LentHistory curHistory, LocalDateTime expiredAt) { - log.info( - "Called applyExpirationDate curHistory: {}, expiredAt: {}", curHistory, expiredAt); - if (expiredAt == null) { - throw new DomainException(ExceptionStatus.INVALID_ARGUMENT); - } - if (DateUtil.isPast(expiredAt)) { - throw new DomainException(ExceptionStatus.INVALID_EXPIRED_AT); - } - curHistory.setExpiredAt(expiredAt); - } - - @Override - public LentPolicyStatus verifyUserForLent(User user, Cabinet cabinet, int userActiveLentCount, - List userActiveBanList) { - log.debug("Called verifyUserForLent"); - if (!user.isUserRole(UserRole.USER)) { - return LentPolicyStatus.NOT_USER; - } - if (userActiveLentCount != 0) { - return LentPolicyStatus.ALREADY_LENT_USER; - } - if (user.getBlackholedAt() != null && user.getBlackholedAt() - .isBefore(LocalDateTime.now())) { - publisher.publishEvent(UserBlackholeInfoDto.of(user)); - if (user.getBlackholedAt() != null && user.getBlackholedAt() - .isBefore(LocalDateTime.now())) { - return LentPolicyStatus.BLACKHOLED_USER; - } - } - - // 유저가 페널티 2 종류 이상 받을 수 있나? <- 실제로 그럴리 없지만 lentPolicy 객체는 그런 사실을 모르고, 유연하게 구현? - if (userActiveBanList == null || userActiveBanList.isEmpty()) { - return LentPolicyStatus.FINE; - } - LentPolicyStatus ret = LentPolicyStatus.FINE; - for (BanHistory banHistory : userActiveBanList) { - switch (banHistory.getBanType()) { - case ALL: - return LentPolicyStatus.ALL_BANNED_USER; - case SHARE: - if (cabinet.isLentType(LentType.SHARE)) { - ret = LentPolicyStatus.SHARE_BANNED_USER; - } - break; - default: - break; - } - } - return ret; - } - - @Override - public LentPolicyStatus verifyUserForLentShare(User user, Cabinet cabinet, - int userActiveLentCount, - List userActiveBanList) { - - LentPolicyStatus ret = verifyUserForLent(user, cabinet, userActiveLentCount, - userActiveBanList); - - // 유저가 패스워드를 3번 이상 틀린 경우 - Long cabinetId = cabinet.getCabinetId(); - Long userId = user.getUserId(); - // 사물함을 빌릴 수 있는 유저라면 공유 사물함 비밀번호 입력 횟수를 확인 - if (ret == LentPolicyStatus.FINE && lentRedis.isShadowKey( - cabinet.getCabinetId())) { - String passwordCount = lentRedis.getPwTrialCountInRedis( - cabinetId.toString(), - userId.toString()); - // 사물함을 빌릴 수 있는 유저면서, 해당 공유사물함에 처음 접근하는 유저인 경우 - if (passwordCount != null && Integer.parseInt(passwordCount) >= 3) { - ret = LentPolicyStatus.SHARE_BANNED_USER; - } - } - return ret; - } - - @Override - public LentPolicyStatus verifyCabinetForLent(Cabinet cabinet) { - log.info("Called verifyCabinetForLent cabinet: {}", cabinet); - // 빌릴 수 있는지 검증. 빌릴 수 없으면 return lentPolicyDto; - switch (cabinet.getStatus()) { - case FULL: - return LentPolicyStatus.FULL_CABINET; - case BROKEN: - return LentPolicyStatus.BROKEN_CABINET; - case OVERDUE: - return LentPolicyStatus.OVERDUE_CABINET; - case PENDING: - return LentPolicyStatus.PENDING_CABINET; - } - if (cabinet.isLentType(LentType.CLUB)) { - return LentPolicyStatus.LENT_CLUB; - } - // 기존의 공유사물함 정책에서 검사해야 되는 부분 -> 현재 필요 x + private final CabinetProperties cabinetProperties; + private final ApplicationEventPublisher publisher; + private final LentRedis lentRedis; + + @Override + public LocalDateTime generateSharedCabinetExpirationDate(LocalDateTime now, + Integer totalUserCount) { + log.info("Called generateSharedCabinetExpirationDate now: {}, totalUserCount: {}", now, + totalUserCount); + return now.plusDays(getDaysForLentTermShare(totalUserCount)) + .withHour(23) + .withMinute(59) + .withSecond(0); + } + + @Override + public LocalDateTime generateExpirationDate(LocalDateTime now, Cabinet cabinet) { + log.info("Called generateExpirationDate now: {}, cabinet: {}", now, cabinet); + + if (!DateUtil.isSameDay(now)) { + throw new IllegalArgumentException("현재 시각이 아닙니다."); + } + + LentType lentType = cabinet.getLentType(); + switch (lentType) { + case PRIVATE: + return now.plusDays(getDaysForLentTermPrivate()) + .withHour(23) + .withMinute(59) + .withSecond(59); + case CLUB: + return DateUtil.getInfinityDate(); + } + throw new IllegalArgumentException("대여 상태가 잘못되었습니다."); + } + + @Override + public LocalDateTime generateExtendedExpirationDate(LocalDateTime now) { + log.info("Called generateExtendedExpirationDate now: {}, cabinet: {}", now); + if (DateUtil.isPast(now)) { + throw new DomainException(ExceptionStatus.LENT_EXPIRED); + } + return now.plusDays(getDaysForLentTermPrivate()) + .withHour(23) + .withMinute(59) + .withSecond(0); + } + + @Override + public void applyExpirationDate(LentHistory curHistory, LocalDateTime expiredAt) { + log.info( + "Called applyExpirationDate curHistory: {}, expiredAt: {}", curHistory, expiredAt); + if (expiredAt == null) { + throw new DomainException(ExceptionStatus.INVALID_ARGUMENT); + } + if (DateUtil.isPast(expiredAt)) { + throw new DomainException(ExceptionStatus.INVALID_EXPIRED_AT); + } + curHistory.setExpiredAt(expiredAt); + } + + @Override + public LentPolicyStatus verifyUserForLent(User user, Cabinet cabinet, int userActiveLentCount, + List userActiveBanList) { + log.debug("Called verifyUserForLent"); + if (!user.isUserRole(UserRole.USER)) { + return LentPolicyStatus.NOT_USER; + } + if (userActiveLentCount != 0) { + return LentPolicyStatus.ALREADY_LENT_USER; + } + if (user.getBlackholedAt() != null && user.getBlackholedAt() + .isBefore(LocalDateTime.now())) { + publisher.publishEvent(UserBlackholeInfoDto.of(user)); + if (user.getBlackholedAt() != null && user.getBlackholedAt() + .isBefore(LocalDateTime.now())) { + return LentPolicyStatus.BLACKHOLED_USER; + } + } + + // 유저가 페널티 2 종류 이상 받을 수 있나? <- 실제로 그럴리 없지만 lentPolicy 객체는 그런 사실을 모르고, 유연하게 구현? + if (userActiveBanList == null || userActiveBanList.isEmpty()) { + return LentPolicyStatus.FINE; + } + LentPolicyStatus ret = LentPolicyStatus.FINE; + for (BanHistory banHistory : userActiveBanList) { + switch (banHistory.getBanType()) { + case ALL: + return LentPolicyStatus.ALL_BANNED_USER; + case SHARE: + if (cabinet.isLentType(LentType.SHARE)) { + ret = LentPolicyStatus.SHARE_BANNED_USER; + } + break; + default: + break; + } + } + return ret; + } + + @Override + public LentPolicyStatus verifyUserForLentShare(User user, Cabinet cabinet, + int userActiveLentCount, + List userActiveBanList) { + + LentPolicyStatus ret = verifyUserForLent(user, cabinet, userActiveLentCount, + userActiveBanList); + + // 유저가 패스워드를 3번 이상 틀린 경우 + Long cabinetId = cabinet.getCabinetId(); + Long userId = user.getUserId(); + // 사물함을 빌릴 수 있는 유저라면 공유 사물함 비밀번호 입력 횟수를 확인 + if (ret == LentPolicyStatus.FINE && lentRedis.isShadowKey( + cabinet.getCabinetId())) { + String passwordCount = lentRedis.getPwTrialCountInRedis( + cabinetId.toString(), + userId.toString()); + // 사물함을 빌릴 수 있는 유저면서, 해당 공유사물함에 처음 접근하는 유저인 경우 + if (passwordCount != null && Integer.parseInt(passwordCount) >= 3) { + ret = LentPolicyStatus.SHARE_BANNED_USER; + } + } + return ret; + } + + @Override + public LentPolicyStatus verifyCabinetForLent(Cabinet cabinet) { + log.info("Called verifyCabinetForLent cabinet: {}", cabinet); + // 빌릴 수 있는지 검증. 빌릴 수 없으면 return lentPolicyDto; + switch (cabinet.getStatus()) { + case FULL: + return LentPolicyStatus.FULL_CABINET; + case BROKEN: + return LentPolicyStatus.BROKEN_CABINET; + case OVERDUE: + return LentPolicyStatus.OVERDUE_CABINET; + case PENDING: + return LentPolicyStatus.PENDING_CABINET; + } + if (cabinet.isLentType(LentType.CLUB)) { + return LentPolicyStatus.LENT_CLUB; + } + // 기존의 공유사물함 정책에서 검사해야 되는 부분 -> 현재 필요 x // if (cabinet.isLentType(LentType.SHARE)) { // if (cabinetLentHistories == null || cabinetLentHistories.isEmpty()) { // return LentPolicyStatus.INTERNAL_ERROR; @@ -188,75 +181,75 @@ public LentPolicyStatus verifyCabinetForLent(Cabinet cabinet) { // return LentPolicyStatus.IMMINENT_EXPIRATION; // } // } - return LentPolicyStatus.FINE; - } - - @Override - public Integer getDaysForLentTermPrivate() { - log.debug("Called getDaysForLentTermPrivate"); - return cabinetProperties.getLentTermPrivate(); - } - - @Override - public Integer getDaysForLentTermShare(Integer totalUserCount) { - log.debug("Called getDaysForLentTermShare"); - return cabinetProperties.getLentTermShare() * totalUserCount; - } - - @Override - public Integer getDaysForNearExpiration() { - log.debug("Called getDaysForNearExpiration"); - return cabinetProperties.getPenaltyDayShare() + cabinetProperties.getPenaltyDayPadding(); - } - - @Override - public void handlePolicyStatus(LentPolicyStatus status, List banHistory) - throws ServiceException { - log.info("Called handlePolicyStatus status: {}", status); - switch (status) { - case FINE: - break; - case BROKEN_CABINET: - throw new ServiceException(ExceptionStatus.LENT_BROKEN); - case FULL_CABINET: - throw new ServiceException(ExceptionStatus.LENT_FULL); - case OVERDUE_CABINET: - throw new ServiceException(ExceptionStatus.LENT_EXPIRED); - case LENT_CLUB: - throw new ServiceException(ExceptionStatus.LENT_CLUB); - case IMMINENT_EXPIRATION: - throw new ServiceException(ExceptionStatus.LENT_EXPIRE_IMMINENT); - case ALREADY_LENT_USER: - throw new ServiceException(ExceptionStatus.LENT_ALREADY_EXISTED); - case ALL_BANNED_USER: - handleBannedUserResponse(status, banHistory.get(0)); - case SHARE_BANNED_USER: - throw new ServiceException(ExceptionStatus.SHARE_CODE_TRIAL_EXCEEDED); - case BLACKHOLED_USER: - throw new ServiceException(ExceptionStatus.BLACKHOLED_USER); - case PENDING_CABINET: - throw new ServiceException(ExceptionStatus.LENT_PENDING); - case NOT_USER: - case INTERNAL_ERROR: - default: - throw new ServiceException(ExceptionStatus.INTERNAL_SERVER_ERROR); - } - } - - public void handleBannedUserResponse(LentPolicyStatus status, BanHistory banHistory) { - log.info("Called handleBannedUserResponse: {}", status); - - LocalDateTime unbannedAt = banHistory.getUnbannedAt(); - String unbannedTimeString = unbannedAt.format( - DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); - - if (status.equals(LentPolicyStatus.ALL_BANNED_USER)) { - throw new CustomServiceException( - new CustomExceptionStatus(ExceptionStatus.ALL_BANNED_USER, unbannedTimeString)); - } else if (status.equals(LentPolicyStatus.SHARE_BANNED_USER)) { - throw new CustomServiceException( - new CustomExceptionStatus(ExceptionStatus.SHARE_BANNED_USER, - unbannedTimeString)); - } - } + return LentPolicyStatus.FINE; + } + + @Override + public Integer getDaysForLentTermPrivate() { + log.debug("Called getDaysForLentTermPrivate"); + return cabinetProperties.getLentTermPrivate(); + } + + @Override + public Integer getDaysForLentTermShare(Integer totalUserCount) { + log.debug("Called getDaysForLentTermShare"); + return cabinetProperties.getLentTermShare() * totalUserCount; + } + + @Override + public Integer getDaysForNearExpiration() { + log.debug("Called getDaysForNearExpiration"); + return cabinetProperties.getPenaltyDayShare() + cabinetProperties.getPenaltyDayPadding(); + } + + @Override + public void handlePolicyStatus(LentPolicyStatus status, List banHistory) + throws ServiceException { + log.info("Called handlePolicyStatus status: {}", status); + switch (status) { + case FINE: + break; + case BROKEN_CABINET: + throw new ServiceException(ExceptionStatus.LENT_BROKEN); + case FULL_CABINET: + throw new ServiceException(ExceptionStatus.LENT_FULL); + case OVERDUE_CABINET: + throw new ServiceException(ExceptionStatus.LENT_EXPIRED); + case LENT_CLUB: + throw new ServiceException(ExceptionStatus.LENT_CLUB); + case IMMINENT_EXPIRATION: + throw new ServiceException(ExceptionStatus.LENT_EXPIRE_IMMINENT); + case ALREADY_LENT_USER: + throw new ServiceException(ExceptionStatus.LENT_ALREADY_EXISTED); + case ALL_BANNED_USER: + handleBannedUserResponse(status, banHistory.get(0)); + case SHARE_BANNED_USER: + throw new ServiceException(ExceptionStatus.SHARE_CODE_TRIAL_EXCEEDED); + case BLACKHOLED_USER: + throw new ServiceException(ExceptionStatus.BLACKHOLED_USER); + case PENDING_CABINET: + throw new ServiceException(ExceptionStatus.LENT_PENDING); + case NOT_USER: + case INTERNAL_ERROR: + default: + throw new ServiceException(ExceptionStatus.INTERNAL_SERVER_ERROR); + } + } + + public void handleBannedUserResponse(LentPolicyStatus status, BanHistory banHistory) { + log.info("Called handleBannedUserResponse: {}", status); + + LocalDateTime unbannedAt = banHistory.getUnbannedAt(); + String unbannedTimeString = unbannedAt.format( + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); + + if (status.equals(LentPolicyStatus.ALL_BANNED_USER)) { + throw new CustomServiceException( + new CustomExceptionStatus(ExceptionStatus.ALL_BANNED_USER, unbannedTimeString)); + } else if (status.equals(LentPolicyStatus.SHARE_BANNED_USER)) { + throw new CustomServiceException( + new CustomExceptionStatus(ExceptionStatus.SHARE_BANNED_USER, + unbannedTimeString)); + } + } } From d2d699cc2df6484e67dbbb3545963817531335a3 Mon Sep 17 00:00:00 2001 From: Woo Joo Chae Date: Fri, 27 Oct 2023 22:17:54 +0900 Subject: [PATCH 08/48] [BE] HOTFIX: log level debug to info & logger message --- .../cabinet/auth/service/FtApiManager.java | 128 +++++++++--------- .../leave/absence/LeaveAbsenceManager.java | 48 ++++--- .../cabinet/utils/mail/EmailSender.java | 63 ++++----- 3 files changed, 120 insertions(+), 119 deletions(-) diff --git a/backend/src/main/java/org/ftclub/cabinet/auth/service/FtApiManager.java b/backend/src/main/java/org/ftclub/cabinet/auth/service/FtApiManager.java index fc89d5bd6..a4f56a7c7 100644 --- a/backend/src/main/java/org/ftclub/cabinet/auth/service/FtApiManager.java +++ b/backend/src/main/java/org/ftclub/cabinet/auth/service/FtApiManager.java @@ -22,69 +22,71 @@ @Log4j2 public class FtApiManager { - private final FtApiProperties ftApiProperties; - private String accessToken; - private final ObjectMapper objectMapper; - private static final Integer MAX_RETRY = 3; + private static final Integer MAX_RETRY = 3; + private final FtApiProperties ftApiProperties; + private final ObjectMapper objectMapper; + private String accessToken; - /** - * 42 토큰을 발급받는다. - */ - public void issueAccessToken() { - log.info("called issueAccessToken"); - accessToken = - WebClient.create().post() - .uri(ftApiProperties.getTokenUri()) - .body(BodyInserters.fromFormData( - ApiRequestManager.of(ftApiProperties) - .getAccessTokenRequestBodyMapWithClientSecret( - "client_credentials"))) - .retrieve() - .bodyToMono(String.class) - .map(response -> { - try { - return objectMapper.readTree(response) - .get(ftApiProperties.getAccessTokenName()).asText(); - } catch (Exception e) { - throw new RuntimeException(); - } - }) - .onErrorResume(e -> { - throw new ServiceException(ExceptionStatus.OAUTH_BAD_GATEWAY); - }) - .block(); - } + /** + * 42 토큰을 발급받는다. + */ + public void issueAccessToken() { + log.info("called issueAccessToken"); + accessToken = + WebClient.create().post() + .uri(ftApiProperties.getTokenUri()) + .body(BodyInserters.fromFormData( + ApiRequestManager.of(ftApiProperties) + .getAccessTokenRequestBodyMapWithClientSecret( + "client_credentials"))) + .retrieve() + .bodyToMono(String.class) + .map(response -> { + try { + return objectMapper.readTree(response) + .get(ftApiProperties.getAccessTokenName()).asText(); + } catch (Exception e) { + log.error("{}", e.getMessage()); + throw new RuntimeException(); + } + }) + .onErrorResume(e -> { + log.error("{}", e.getMessage()); + throw new ServiceException(ExceptionStatus.OAUTH_BAD_GATEWAY); + }) + .block(); + } - /** - * 유저의 이름으로 42API를 통해 특정 유저의 정보를 가져온다. - * - * @param name 유저의 이름 - * @return JsonNode 형태의 유저 정보 - */ - public JsonNode getFtUsersInfoByName(String name) { - log.info("called getFtUsersInfoByName {}", name); - Integer tryCount = 0; - while (tryCount < MAX_RETRY) { - try { - JsonNode results = WebClient.create().get() - .uri(ftApiProperties.getUsersInfoUri() + '/' + name) - .accept(MediaType.APPLICATION_JSON) - .headers(h -> h.setBearerAuth(accessToken)) - .retrieve() - .bodyToMono(JsonNode.class) - .block(); - return results; - } catch (Exception e) { - tryCount++; - log.info(e.getMessage()); - log.info("요청에 실패했습니다. 최대 3번 재시도합니다. 현재 시도 횟수: {}", tryCount); - this.issueAccessToken(); - if (tryCount == MAX_RETRY) { - log.error("요청에 실패했습니다. 최대 재시도 횟수를 초과했습니다. {}", e.getMessage()); - throw new ServiceException(ExceptionStatus.OAUTH_BAD_GATEWAY); - } - } - } - return null; - } + /** + * 유저의 이름으로 42API를 통해 특정 유저의 정보를 가져온다. + * + * @param name 유저의 이름 + * @return JsonNode 형태의 유저 정보 + */ + public JsonNode getFtUsersInfoByName(String name) { + log.info("called getFtUsersInfoByName {}", name); + Integer tryCount = 0; + while (tryCount < MAX_RETRY) { + try { + JsonNode results = WebClient.create().get() + .uri(ftApiProperties.getUsersInfoUri() + '/' + name) + .accept(MediaType.APPLICATION_JSON) + .headers(h -> h.setBearerAuth(accessToken)) + .retrieve() + .bodyToMono(JsonNode.class) + .block(); + return results; + } catch (Exception e) { + tryCount++; + log.info(e.getMessage()); + log.info("요청에 실패했습니다. 최대 3번 재시도합니다. 현재 시도 횟수: {}", tryCount); + this.issueAccessToken(); + if (tryCount == MAX_RETRY) { + log.error("요청에 실패했습니다. 최대 재시도 횟수를 초과했습니다. {}", e.getMessage()); + throw new ServiceException(ExceptionStatus.OAUTH_BAD_GATEWAY); + } + } + } + return null; + } } diff --git a/backend/src/main/java/org/ftclub/cabinet/utils/leave/absence/LeaveAbsenceManager.java b/backend/src/main/java/org/ftclub/cabinet/utils/leave/absence/LeaveAbsenceManager.java index 5f37b937e..b08c14e29 100644 --- a/backend/src/main/java/org/ftclub/cabinet/utils/leave/absence/LeaveAbsenceManager.java +++ b/backend/src/main/java/org/ftclub/cabinet/utils/leave/absence/LeaveAbsenceManager.java @@ -5,8 +5,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.ftclub.cabinet.auth.service.FtApiManager; -import org.ftclub.cabinet.exception.ExceptionStatus; -import org.ftclub.cabinet.exception.ServiceException; import org.ftclub.cabinet.lent.service.LentService; import org.ftclub.cabinet.user.service.UserService; import org.springframework.http.HttpStatus; @@ -18,29 +16,29 @@ @Log4j2 public class LeaveAbsenceManager { - private final FtApiManager ftAPIManager; - private final LentService lentService; - private final UserService userService; + private final FtApiManager ftAPIManager; + private final LentService lentService; + private final UserService userService; - private Boolean isLeaveAbsence(JsonNode jsonUserInfo) { - return !jsonUserInfo.get("active?").asBoolean(); - } + private Boolean isLeaveAbsence(JsonNode jsonUserInfo) { + return !jsonUserInfo.get("active?").asBoolean(); + } - public void handleLeaveAbsence(Long userId, String name) { - log.debug("called handleLeaveAbsence {} {}", userId, name); - try { - JsonNode jsonUserInfo = ftAPIManager.getFtUsersInfoByName(name); - if (isLeaveAbsence(jsonUserInfo)) { - lentService.terminateLentCabinet(userId); - } - } catch (HttpClientErrorException e) { - log.error("handleLeaveAbsence HttpClientErrorException {}", e.getStatusCode()); - if (e.getStatusCode().equals(HttpStatus.NOT_FOUND)) { - lentService.terminateLentCabinet(userId); - userService.deleteUser(userId, LocalDateTime.now()); - } - } catch (Exception e) { - log.error("handleLeaveAbsence Exception: {}, {}", userId, name, e); - } - } + public void handleLeaveAbsence(Long userId, String name) { + log.info("called handleLeaveAbsence {} {}", userId, name); + try { + JsonNode jsonUserInfo = ftAPIManager.getFtUsersInfoByName(name); + if (isLeaveAbsence(jsonUserInfo)) { + lentService.terminateLentCabinet(userId); + } + } catch (HttpClientErrorException e) { + log.error("handleLeaveAbsence HttpClientErrorException {}", e.getStatusCode()); + if (e.getStatusCode().equals(HttpStatus.NOT_FOUND)) { + lentService.terminateLentCabinet(userId); + userService.deleteUser(userId, LocalDateTime.now()); + } + } catch (Exception e) { + log.error("handleLeaveAbsence Exception: {}, {}", userId, name, e); + } + } } diff --git a/backend/src/main/java/org/ftclub/cabinet/utils/mail/EmailSender.java b/backend/src/main/java/org/ftclub/cabinet/utils/mail/EmailSender.java index 289656cd2..ac69fb52f 100644 --- a/backend/src/main/java/org/ftclub/cabinet/utils/mail/EmailSender.java +++ b/backend/src/main/java/org/ftclub/cabinet/utils/mail/EmailSender.java @@ -17,35 +17,36 @@ @Log4j2 public class EmailSender { - private final JavaMailSender javaMailSender; - private final ITemplateEngine templateEngine; - private final GmailProperties gmailProperties; - - public void sendMail(String name, String to, String subject, String template) - throws MessagingException, MailException { - log.info("called EmailSender for {}, {}, {}", name, to, subject); - if (gmailProperties.getIsProduction() == false) { - log.debug("개발 환경이므로 메일을 보내지 않습니다."); - return; - } - MimeMessage message = javaMailSender.createMimeMessage(); - MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); - - helper.setFrom(gmailProperties.getDisplaySenderName() + " <" + gmailProperties.getUsername() + ">"); - helper.setTo(to); - helper.setSubject(subject); - - Context context = new Context(); - context.setVariable("name", name); - - String htmlContent = templateEngine.process(template, context); - helper.setText(htmlContent, true); - - try { - javaMailSender.send(message); - log.info("{} ({})에게 메일을 성공적으로 보냈습니다.", name, to); - } catch (MailException e) { - log.error("메일 전송 중 오류가 발생했습니다: {}", e.getMessage()); - } - } + private final JavaMailSender javaMailSender; + private final ITemplateEngine templateEngine; + private final GmailProperties gmailProperties; + + public void sendMail(String name, String to, String subject, String template) + throws MessagingException, MailException { + log.info("called EmailSender for {}, {}, {}", name, to, subject); + if (!gmailProperties.getIsProduction()) { + log.info("개발 환경이므로 메일을 보내지 않습니다."); + return; + } + MimeMessage message = javaMailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); + + helper.setFrom(gmailProperties.getDisplaySenderName() + " <" + gmailProperties.getUsername() + + ">"); + helper.setTo(to); + helper.setSubject(subject); + + Context context = new Context(); + context.setVariable("name", name); + + String htmlContent = templateEngine.process(template, context); + helper.setText(htmlContent, true); + + try { + javaMailSender.send(message); + log.info("{} ({})에게 메일을 성공적으로 보냈습니다.", name, to); + } catch (MailException e) { + log.error("메일 전송 중 오류가 발생했습니다: {}", e.getMessage()); + } + } } From 99217bd2600041315277daf15067ce86573baeca Mon Sep 17 00:00:00 2001 From: Woo Joo Chae Date: Fri, 27 Oct 2023 22:18:32 +0900 Subject: [PATCH 09/48] =?UTF-8?q?[BE]=20HOTFIX:=20=EB=B8=94=EB=9E=99?= =?UTF-8?q?=ED=99=80=20=ED=99=95=EC=9D=B8=EB=A1=9C=EC=A7=81=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../utils/scheduler/SystemScheduler.java | 113 +++++++++--------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/backend/src/main/java/org/ftclub/cabinet/utils/scheduler/SystemScheduler.java b/backend/src/main/java/org/ftclub/cabinet/utils/scheduler/SystemScheduler.java index 7a49c5d3e..ca0cd007e 100644 --- a/backend/src/main/java/org/ftclub/cabinet/utils/scheduler/SystemScheduler.java +++ b/backend/src/main/java/org/ftclub/cabinet/utils/scheduler/SystemScheduler.java @@ -23,63 +23,64 @@ @Log4j2 public class SystemScheduler { - private final LeaveAbsenceManager leaveAbsenceManager; - private final OverdueManager overdueManager; - private final LentService lentService; - private final UserService userService; - private final BlackholeManager blackholeManager; + private static final long DELAY_TIME = 2000; + private final LeaveAbsenceManager leaveAbsenceManager; + private final OverdueManager overdueManager; + private final LentService lentService; + private final UserService userService; + private final BlackholeManager blackholeManager; - private static final long DELAY_TIME = 2000; + /** + * 매일 자정마다 대여 기록을 확인하여, 연체 메일 발송 및 휴학생 처리를 트리거하는 메소드 2초 간격으로 블랙홀 검증 + */ + @Scheduled(cron = "${spring.schedule.cron.leave-absence}") + public void checkAllLents() { + log.info("called checkAllLents"); + List activeLents = lentService.getAllActiveLentHistories(); + for (ActiveLentHistoryDto activeLent : activeLents) { + overdueManager.handleOverdue(activeLent); +/* + leaveAbsenceManager.handleLeaveAbsence(activeLent.getUserId(), activeLent.getName()); + try { + Thread.sleep(DELAY_TIME); + } catch (InterruptedException e) { + log.error(e.getMessage()); + } +*/ + } + } - /** - * 매일 자정마다 대여 기록을 확인하여, 연체 메일 발송 및 휴학생 처리를 트리거하는 메소드 2초 간격으로 블랙홀 검증 - */ - @Scheduled(cron = "${spring.schedule.cron.leave-absence}") - public void checkAllLents() { - log.info("called checkAllLents"); - List activeLents = lentService.getAllActiveLentHistories(); - for (ActiveLentHistoryDto activeLent : activeLents) { - overdueManager.handleOverdue(activeLent); - leaveAbsenceManager.handleLeaveAbsence(activeLent.getUserId(), activeLent.getName()); - try { - Thread.sleep(DELAY_TIME); - } catch (InterruptedException e) { - log.error(e.getMessage()); - } - } - } + /** + * 매주 월요일 자정 42분에 블랙홀에 빠진 유저 처리를 트리거하는 메소드 2초 간격으로 블랙홀 검증 + */ + @Scheduled(cron = "${spring.schedule.cron.risk-of-blackhole}") + public void checkRiskOfBlackhole() { + log.info("called checkRiskOfBlackhole"); + List blackholeInfos = userService.getAllRiskOfBlackholeInfo(); + for (UserBlackholeInfoDto blackholeInfo : blackholeInfos) { + blackholeManager.handleBlackhole(blackholeInfo); + try { + Thread.sleep(DELAY_TIME); + } catch (InterruptedException e) { + log.error(e.getMessage()); + } + } + } - /** - * 매주 월요일 자정 42분에 블랙홀에 빠진 유저 처리를 트리거하는 메소드 2초 간격으로 블랙홀 검증 - */ - @Scheduled(cron = "${spring.schedule.cron.risk-of-blackhole}") - public void checkRiskOfBlackhole() { - log.info("called checkRiskOfBlackhole"); - List blackholeInfos = userService.getAllRiskOfBlackholeInfo(); - for (UserBlackholeInfoDto blackholeInfo : blackholeInfos) { - blackholeManager.handleBlackhole(blackholeInfo); - try { - Thread.sleep(DELAY_TIME); - } catch (InterruptedException e) { - log.error(e.getMessage()); - } - } - } - - /** - * 매월 1일 01시 42분에 블랙홀에 빠질 위험이 없는 유저들의 블랙홀 처리를 트리거하는 메소드 2초 간격으로 블랙홀 검증 - */ - @Scheduled(cron = "${spring.schedule.cron.no-risk-of-blackhole}") - public void checkNoRiskOfBlackhole() { - log.info("called checkNoRiskOfBlackhole"); - List blackholeInfos = userService.getAllNoRiskOfBlackholeInfo(); - for (UserBlackholeInfoDto blackholeInfo : blackholeInfos) { - blackholeManager.handleBlackhole(blackholeInfo); - try { - Thread.sleep(DELAY_TIME); - } catch (InterruptedException e) { - log.error(e.getMessage()); - } - } - } + /** + * 매월 1일 01시 42분에 블랙홀에 빠질 위험이 없는 유저들의 블랙홀 처리를 트리거하는 메소드 2초 간격으로 블랙홀 검증 + */ + @Scheduled(cron = "${spring.schedule.cron.no-risk-of-blackhole}") + public void checkNoRiskOfBlackhole() { + log.info("called checkNoRiskOfBlackhole"); + List blackholeInfos = userService.getAllNoRiskOfBlackholeInfo(); + for (UserBlackholeInfoDto blackholeInfo : blackholeInfos) { + blackholeManager.handleBlackhole(blackholeInfo); + try { + Thread.sleep(DELAY_TIME); + } catch (InterruptedException e) { + log.error(e.getMessage()); + } + } + } } From 4ff3d4d32e95eca780e8097125d236c258ea0218 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Tue, 31 Oct 2023 13:09:51 +0900 Subject: [PATCH 10/48] =?UTF-8?q?[BE]=20FIX:=20=ED=94=84=EB=9F=AC=EB=8D=95?= =?UTF-8?q?=EC=85=98=EC=97=90=EC=84=9C=20Config=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EC=9D=84=20=EB=9F=B0=ED=83=80=EC=9E=84=EC=97=90=20=EC=A3=BC?= =?UTF-8?q?=EC=9E=85=20=EB=B0=9B=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?#1408?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/back-ci.yaml | 9 +-------- config | 2 +- deploy-dev/deploy.sh | 9 +++++++++ deploy-dev/pinpoint-application/Dockerfile | 2 +- deploy-dev/pinpoint-application/docker-compose.yml | 1 + deploy-main/deploy.sh | 9 +++++++++ deploy-main/pinpoint-application/Dockerfile | 2 +- deploy-main/pinpoint-application/docker-compose.yml | 1 + 8 files changed, 24 insertions(+), 11 deletions(-) diff --git a/.github/workflows/back-ci.yaml b/.github/workflows/back-ci.yaml index 9ff01cf47..aff32248f 100644 --- a/.github/workflows/back-ci.yaml +++ b/.github/workflows/back-ci.yaml @@ -9,18 +9,10 @@ jobs: backend-CI: runs-on: ubuntu-latest steps: - - name: Make ssh file - run: | - mkdir -p ~/.ssh - echo '${{ secrets.SSH_CONFIG }}' | base64 -d > ~/.ssh/id_rsa - chmod 400 ~/.ssh/id_rsa - name: 체크아웃 uses: actions/checkout@v3 with: submodule: true - - name: 서브모듈 업데이트 - run: | - git submodule update --init config - name: JDK 11 설정 uses: actions/setup-java@v3 @@ -37,6 +29,7 @@ jobs: key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-gradle- + - name: Gradle 빌드 run: | cd backend diff --git a/config b/config index c1c4afb2c..3b36ff98d 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit c1c4afb2c7a7101f4c8f6e48933a7ad4ea3a4158 +Subproject commit 3b36ff98d8c7f38c35db68d2f8569aa46d011a30 diff --git a/deploy-dev/deploy.sh b/deploy-dev/deploy.sh index 271ad6e6f..9adfddbba 100644 --- a/deploy-dev/deploy.sh +++ b/deploy-dev/deploy.sh @@ -1,5 +1,14 @@ #!/bin/bash +cd /home/ec2-user + +if [ ! -d "/home/ec2-user/server-config" ]; then + git clone git@github.com:42cabi/config.git server-config +fi + +cd server-config +git pull origin main + mkdir -p /home/ec2-user/deploy/zip cd /home/ec2-user/deploy/zip/ diff --git a/deploy-dev/pinpoint-application/Dockerfile b/deploy-dev/pinpoint-application/Dockerfile index 269bf57f7..e73266084 100644 --- a/deploy-dev/pinpoint-application/Dockerfile +++ b/deploy-dev/pinpoint-application/Dockerfile @@ -6,4 +6,4 @@ RUN ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime COPY build/cabinet-0.0.1-SNAPSHOT.jar cabi.jar -CMD java -jar -Dspring.profiles.active=dev ${JAVA_OPTS} cabi.jar +CMD java -jar -Dspring.profiles.active=dev ${JAVA_OPTS} cabi.jar --spring.config.location=file:/resources/application-dev.yml diff --git a/deploy-dev/pinpoint-application/docker-compose.yml b/deploy-dev/pinpoint-application/docker-compose.yml index 41949559c..4131b241d 100644 --- a/deploy-dev/pinpoint-application/docker-compose.yml +++ b/deploy-dev/pinpoint-application/docker-compose.yml @@ -13,6 +13,7 @@ services: volumes: - data-volume:/pinpoint-agent - $HOME/logs/:/logs/ + - $HOME/server-config/backend/src/main/resources:/resources/ environment: JAVA_OPTS: "-javaagent:/pinpoint-agent/pinpoint-bootstrap-${PINPOINT_VERSION}.jar -Dpinpoint.agentId=${AGENT_ID} -Dpinpoint.applicationName=${APP_NAME} -Dpinpoint.profiler.profiles.active=${SPRING_PROFILES}" networks: diff --git a/deploy-main/deploy.sh b/deploy-main/deploy.sh index 271ad6e6f..9adfddbba 100644 --- a/deploy-main/deploy.sh +++ b/deploy-main/deploy.sh @@ -1,5 +1,14 @@ #!/bin/bash +cd /home/ec2-user + +if [ ! -d "/home/ec2-user/server-config" ]; then + git clone git@github.com:42cabi/config.git server-config +fi + +cd server-config +git pull origin main + mkdir -p /home/ec2-user/deploy/zip cd /home/ec2-user/deploy/zip/ diff --git a/deploy-main/pinpoint-application/Dockerfile b/deploy-main/pinpoint-application/Dockerfile index d4806ca50..a530fc6d2 100644 --- a/deploy-main/pinpoint-application/Dockerfile +++ b/deploy-main/pinpoint-application/Dockerfile @@ -6,4 +6,4 @@ RUN ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime COPY build/cabinet-0.0.1-SNAPSHOT.jar cabi.jar -CMD java -jar -Dspring.profiles.active=prod ${JAVA_OPTS} cabi.jar +CMD java -jar -Dspring.profiles.active=prod ${JAVA_OPTS} cabi.jar --spring.config.location=file:/resources/application-prod.yml diff --git a/deploy-main/pinpoint-application/docker-compose.yml b/deploy-main/pinpoint-application/docker-compose.yml index 73ad6e184..29609e4a6 100644 --- a/deploy-main/pinpoint-application/docker-compose.yml +++ b/deploy-main/pinpoint-application/docker-compose.yml @@ -13,6 +13,7 @@ services: volumes: - data-volume:/pinpoint-agent - $HOME/logs/:/logs/ + - $HOME/server-config/backend/src/main/resources:/resources/ environment: JAVA_OPTS: "-javaagent:/pinpoint-agent/pinpoint-bootstrap-${PINPOINT_VERSION}.jar -Dpinpoint.agentId=${AGENT_ID} -Dpinpoint.applicationName=${APP_NAME} -Dpinpoint.profiler.profiles.active=${SPRING_PROFILES}" networks: From 1073dad5fa3883dfbee33045332774b587f16ad3 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Tue, 31 Oct 2023 21:10:07 +0900 Subject: [PATCH 11/48] [BE] FIX: update config #1408 --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index 3b36ff98d..c1c4afb2c 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 3b36ff98d8c7f38c35db68d2f8569aa46d011a30 +Subproject commit c1c4afb2c7a7101f4c8f6e48933a7ad4ea3a4158 From cff5fd0773eacf9424ce645aa6765a3f57e32929 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Tue, 31 Oct 2023 21:28:44 +0900 Subject: [PATCH 12/48] =?UTF-8?q?[BE]=20FIX:=20application.yml=20mail=20->?= =?UTF-8?q?=20alarm=20=EB=B3=80=EA=B2=BD=20#1408?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/application.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 2e00e6d9f..d3bf9b1da 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -4,7 +4,7 @@ server: port: 4242 spring: config: - import: classpath:application-auth.yml, classpath:application-mail.yml + import: classpath:application-auth.yml, classpath:application-alarm.yml activate: on-profile: prod logging: @@ -16,7 +16,7 @@ server: port: 4242 spring: config: - import: classpath:application-auth.yml, classpath:application-mail.yml + import: classpath:application-auth.yml, classpath:application-alarm.yml activate: on-profile: dev logging: @@ -28,7 +28,7 @@ server: port: 2424 spring: config: - import: classpath:application-auth.yml, classpath:application-mail.yml + import: classpath:application-auth.yml, classpath:application-alarm.yml activate: on-profile: local logging: From 177d4a157b8ecb181109b4e1f8f89e399e7a5b21 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Tue, 31 Oct 2023 22:02:02 +0900 Subject: [PATCH 13/48] =?UTF-8?q?[BE]=20FIX:=20application-alarm.yml=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20#1408?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index c1c4afb2c..344f7d489 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit c1c4afb2c7a7101f4c8f6e48933a7ad4ea3a4158 +Subproject commit 344f7d489d04363f8e15b759fed52f871f73a1ed From e7ca1d57f4df8ca198a3af3426a5a2665780d55a Mon Sep 17 00:00:00 2001 From: Siwon Choi <83565255+sichoi42@users.noreply.github.com> Date: Sun, 5 Nov 2023 16:03:02 +0900 Subject: [PATCH 14/48] =?UTF-8?q?[BE]=20FIX:=20ci/cd=20with=20submodule=20?= =?UTF-8?q?=EC=98=B5=EC=85=98=20=EC=A0=9C=EA=B1=B0=20#1408?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/back-ci.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/back-ci.yaml b/.github/workflows/back-ci.yaml index aff32248f..a33331fd5 100644 --- a/.github/workflows/back-ci.yaml +++ b/.github/workflows/back-ci.yaml @@ -11,8 +11,6 @@ jobs: steps: - name: 체크아웃 uses: actions/checkout@v3 - with: - submodule: true - name: JDK 11 설정 uses: actions/setup-java@v3 From 29db1c18683cdf13008b28ba34d5bb2cdaaa51b4 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Tue, 7 Nov 2023 20:09:56 +0900 Subject: [PATCH 15/48] =?UTF-8?q?[BE]=20FIX:=20FCM=20=EC=9E=84=EC=8B=9C=20?= =?UTF-8?q?=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/ftclub/cabinet/firebase/FCMInitializer.java | 2 +- .../ftclub/cabinet/utils/overdue/manager/OverdueManager.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java b/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java index 8e7321b67..55da2db4f 100644 --- a/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java +++ b/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java @@ -12,7 +12,7 @@ import org.springframework.stereotype.Component; @Slf4j -@Component +//@Component public class FCMInitializer { @Value("${firebase.messaging.credentials.path}") diff --git a/backend/src/main/java/org/ftclub/cabinet/utils/overdue/manager/OverdueManager.java b/backend/src/main/java/org/ftclub/cabinet/utils/overdue/manager/OverdueManager.java index 3e76b1043..dc9972767 100644 --- a/backend/src/main/java/org/ftclub/cabinet/utils/overdue/manager/OverdueManager.java +++ b/backend/src/main/java/org/ftclub/cabinet/utils/overdue/manager/OverdueManager.java @@ -69,7 +69,7 @@ public void handleOverdue(ActiveLentHistoryDto activeLent) { try { emailSender.sendMail(activeLent.getName(), activeLent.getEmail(), subject, template); - fcmService.sendPushMessage(activeLent.getName(), overdueType, activeLent.getDaysLeftFromExpireDate()); +// fcmService.sendPushMessage(activeLent.getName(), overdueType, activeLent.getDaysLeftFromExpireDate()); } catch (Exception e) { e.printStackTrace(); } From 007d3b8671a70c64d73aa769b0820951c44cd0ad Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Tue, 7 Nov 2023 20:13:55 +0900 Subject: [PATCH 16/48] =?UTF-8?q?[BE]=20FIX:=20FCM=20=EC=9E=84=EC=8B=9C=20?= =?UTF-8?q?=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cabinet/firebase/FCMTestController.java | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/backend/src/main/java/org/ftclub/cabinet/firebase/FCMTestController.java b/backend/src/main/java/org/ftclub/cabinet/firebase/FCMTestController.java index 4db09bd0b..a8893e68b 100644 --- a/backend/src/main/java/org/ftclub/cabinet/firebase/FCMTestController.java +++ b/backend/src/main/java/org/ftclub/cabinet/firebase/FCMTestController.java @@ -1,34 +1,34 @@ -package org.ftclub.cabinet.firebase; - -import java.time.Duration; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -import org.ftclub.cabinet.firebase.fcm.service.FCMService; -import org.ftclub.cabinet.redis.service.RedisService; -import org.ftclub.cabinet.utils.overdue.manager.OverdueType; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/v4/fcm") -@Log4j2 -public class FCMTestController { - private final RedisService redisService; - private final FCMService fcmService; - - @PostMapping("test/{name}/{token}") - public void test(@PathVariable("name") String name, @PathVariable("token") String token) { - log.info("called test, name: {}, token: {}", name, token); - redisService.save(name, token, Duration.ofDays(1)); - } - - @PostMapping("test2/{name}") - public void test2(@PathVariable("name") String name) { - log.info("called test2"); - fcmService.sendPushMessage(name, OverdueType.OVERDUE, 1L); - } -} +//package org.ftclub.cabinet.firebase; +// +//import java.time.Duration; +//import lombok.RequiredArgsConstructor; +//import lombok.extern.log4j.Log4j2; +// +//import org.ftclub.cabinet.firebase.fcm.service.FCMService; +//import org.ftclub.cabinet.redis.service.RedisService; +//import org.ftclub.cabinet.utils.overdue.manager.OverdueType; +//import org.springframework.web.bind.annotation.PathVariable; +//import org.springframework.web.bind.annotation.PostMapping; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.RestController; +// +//@RestController +//@RequiredArgsConstructor +//@RequestMapping("/v4/fcm") +//@Log4j2 +//public class FCMTestController { +// private final RedisService redisService; +// private final FCMService fcmService; +// +// @PostMapping("test/{name}/{token}") +// public void test(@PathVariable("name") String name, @PathVariable("token") String token) { +// log.info("called test, name: {}, token: {}", name, token); +// redisService.save(name, token, Duration.ofDays(1)); +// } +// +// @PostMapping("test2/{name}") +// public void test2(@PathVariable("name") String name) { +// log.info("called test2"); +// fcmService.sendPushMessage(name, OverdueType.OVERDUE, 1L); +// } +//} From e384809af07ebc015af514c9465aae4d97cbff35 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Tue, 7 Nov 2023 20:17:32 +0900 Subject: [PATCH 17/48] =?UTF-8?q?[BE]=20FIX:=20FCM=20=EC=9E=84=EC=8B=9C=20?= =?UTF-8?q?=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cabinet/firebase/FCMInitializer.java | 36 --------- .../cabinet/firebase/FCMTestController.java | 34 -------- .../firebase/fcm/service/FCMService.java | 79 ------------------- .../utils/overdue/manager/OverdueManager.java | 4 +- 4 files changed, 2 insertions(+), 151 deletions(-) delete mode 100644 backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java delete mode 100644 backend/src/main/java/org/ftclub/cabinet/firebase/FCMTestController.java delete mode 100644 backend/src/main/java/org/ftclub/cabinet/firebase/fcm/service/FCMService.java diff --git a/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java b/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java deleted file mode 100644 index 55da2db4f..000000000 --- a/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.ftclub.cabinet.firebase; - -import com.google.auth.oauth2.GoogleCredentials; -import com.google.firebase.FirebaseApp; -import com.google.firebase.FirebaseOptions; -import java.io.IOException; -import java.io.InputStream; -import javax.annotation.PostConstruct; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.ClassPathResource; -import org.springframework.stereotype.Component; - -@Slf4j -//@Component -public class FCMInitializer { - - @Value("${firebase.messaging.credentials.path}") - private String credentialsPath; - - @PostConstruct - public void initialize() throws IOException { - ClassPathResource resource = new ClassPathResource(credentialsPath); - - try (InputStream inputStream = resource.getInputStream()) { - FirebaseOptions options = FirebaseOptions.builder() - .setCredentials(GoogleCredentials.fromStream(inputStream)) - .build(); - - if (FirebaseApp.getApps().isEmpty()) { - FirebaseApp.initializeApp(options); - log.info("Firebase application has been initialized"); - } - } - } -} diff --git a/backend/src/main/java/org/ftclub/cabinet/firebase/FCMTestController.java b/backend/src/main/java/org/ftclub/cabinet/firebase/FCMTestController.java deleted file mode 100644 index a8893e68b..000000000 --- a/backend/src/main/java/org/ftclub/cabinet/firebase/FCMTestController.java +++ /dev/null @@ -1,34 +0,0 @@ -//package org.ftclub.cabinet.firebase; -// -//import java.time.Duration; -//import lombok.RequiredArgsConstructor; -//import lombok.extern.log4j.Log4j2; -// -//import org.ftclub.cabinet.firebase.fcm.service.FCMService; -//import org.ftclub.cabinet.redis.service.RedisService; -//import org.ftclub.cabinet.utils.overdue.manager.OverdueType; -//import org.springframework.web.bind.annotation.PathVariable; -//import org.springframework.web.bind.annotation.PostMapping; -//import org.springframework.web.bind.annotation.RequestMapping; -//import org.springframework.web.bind.annotation.RestController; -// -//@RestController -//@RequiredArgsConstructor -//@RequestMapping("/v4/fcm") -//@Log4j2 -//public class FCMTestController { -// private final RedisService redisService; -// private final FCMService fcmService; -// -// @PostMapping("test/{name}/{token}") -// public void test(@PathVariable("name") String name, @PathVariable("token") String token) { -// log.info("called test, name: {}, token: {}", name, token); -// redisService.save(name, token, Duration.ofDays(1)); -// } -// -// @PostMapping("test2/{name}") -// public void test2(@PathVariable("name") String name) { -// log.info("called test2"); -// fcmService.sendPushMessage(name, OverdueType.OVERDUE, 1L); -// } -//} diff --git a/backend/src/main/java/org/ftclub/cabinet/firebase/fcm/service/FCMService.java b/backend/src/main/java/org/ftclub/cabinet/firebase/fcm/service/FCMService.java deleted file mode 100644 index d8cd9bfb2..000000000 --- a/backend/src/main/java/org/ftclub/cabinet/firebase/fcm/service/FCMService.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.ftclub.cabinet.firebase.fcm.service; - -import com.google.firebase.messaging.FirebaseMessaging; -import com.google.firebase.messaging.Message; -import java.util.Optional; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.ftclub.cabinet.config.DomainProperties; -import org.ftclub.cabinet.redis.service.RedisService; -import org.ftclub.cabinet.utils.overdue.manager.OverdueType; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -@Slf4j -public class FCMService { - private final RedisService redisService; - private final DomainProperties domainProperties; - private static final String ICON_FILE_PATH = "/src/assets/images/logo.svg"; - - - public void sendPushMessage(String name, OverdueType overdueType, Long daysLeftFromExpireDate) { - log.info("called sendPushMessage name = {}, overdueType = {}, daysLeftFromExpireDate = {}", name, overdueType, - daysLeftFromExpireDate); - - Optional token = redisService.findByKey(name, String.class); - if (token.isEmpty()) { - log.warn("\"{}\"에 해당하는 디바이스 토큰이 존재하지 않습니다.", name); - return; - } - - switch (overdueType) { - case NONE: - log.warn("overdueType이 NONE입니다. name = {}, overdueType = {}, daysLeftFromExpireDate = {}", name, overdueType, - daysLeftFromExpireDate); - break; - case SOON_OVERDUE: - sendSoonOverdueMessage(token.get(), name, daysLeftFromExpireDate); - break; - case OVERDUE: - sendOverdueMessage(token.get(), name, daysLeftFromExpireDate); - break; - } - } - - private void sendOverdueMessage(String token, String name, Long daysLeftFromExpireDate) { - log.info( - "called sendOverdueMessage token = {}, name = {}, daysLeftFromExpireDate = {}", - token, name, daysLeftFromExpireDate); - Message message = Message.builder() - .putData("title", " 연체 알림") - .putData("body", name + "님, 대여한 사물함이 " + Math.abs(daysLeftFromExpireDate) + "일 연체되었습니다.") - .putData("icon", domainProperties.getFeHost() + ICON_FILE_PATH) - .putData("click_action", domainProperties.getFeHost()) - .setToken(token) - .build(); - - FirebaseMessaging.getInstance().sendAsync(message); - } - - private void sendSoonOverdueMessage(String token, String name, Long daysLeftFromExpireDate) { - log.info( - "called sendSoonOverdueMessage token = {}, name = {}, daysLeftFromExpireDate = {}", - token, name, daysLeftFromExpireDate); - if (token.isEmpty()) { - log.warn("\"{}\"에 해당하는 디바이스 토큰이 존재하지 않습니다.", name); - return; - } - Message message = Message.builder() - .putData("title", " 연체 예정 알림") - .putData("body", "대여한 사물함이 " + daysLeftFromExpireDate + "일 후 연체됩니다.") - .putData("icon", domainProperties.getFeHost() + ICON_FILE_PATH) - .putData("click_action", domainProperties.getFeHost()) - .setToken(token) - .build(); - - FirebaseMessaging.getInstance().sendAsync(message); - } -} diff --git a/backend/src/main/java/org/ftclub/cabinet/utils/overdue/manager/OverdueManager.java b/backend/src/main/java/org/ftclub/cabinet/utils/overdue/manager/OverdueManager.java index dc9972767..509619659 100644 --- a/backend/src/main/java/org/ftclub/cabinet/utils/overdue/manager/OverdueManager.java +++ b/backend/src/main/java/org/ftclub/cabinet/utils/overdue/manager/OverdueManager.java @@ -6,7 +6,7 @@ import org.ftclub.cabinet.cabinet.service.CabinetService; import org.ftclub.cabinet.config.MailOverdueProperties; import org.ftclub.cabinet.dto.ActiveLentHistoryDto; -import org.ftclub.cabinet.firebase.fcm.service.FCMService; +//import org.ftclub.cabinet.firebase.fcm.service.FCMService; import org.ftclub.cabinet.utils.mail.EmailSender; import org.springframework.stereotype.Component; @@ -22,7 +22,7 @@ public class OverdueManager { private final EmailSender emailSender; - private final FCMService fcmService; +// private final FCMService fcmService; private final CabinetService cabinetService; private final MailOverdueProperties mailOverdueProperties; From 7d45e3c5d1c39c00f57f77f00b32608a50982f62 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Tue, 7 Nov 2023 20:23:29 +0900 Subject: [PATCH 18/48] =?UTF-8?q?[BE]=20FIX:=20dev=20config=201=EA=B0=9C?= =?UTF-8?q?=EB=A1=9C=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index 344f7d489..a703b7102 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 344f7d489d04363f8e15b759fed52f871f73a1ed +Subproject commit a703b7102924cf35a8dd3478157d0713679cd938 From 471d4aabde0fb1ea7ecafb5322835dd4eb3b551e Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Tue, 7 Nov 2023 20:31:11 +0900 Subject: [PATCH 19/48] =?UTF-8?q?[BE]=20FIX:=20dev=20config=201=EA=B0=9C?= =?UTF-8?q?=EB=A1=9C=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index a703b7102..8600ecc58 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit a703b7102924cf35a8dd3478157d0713679cd938 +Subproject commit 8600ecc5878ac43abd796797d1fca2da0b8d55f6 From 59dd5eb10cec915c8e152d204e76c11f5f0f72b1 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Tue, 7 Nov 2023 20:35:49 +0900 Subject: [PATCH 20/48] =?UTF-8?q?[BE]=20FIX:=20dev=20config=201=EA=B0=9C?= =?UTF-8?q?=EB=A1=9C=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index 8600ecc58..d756a78f9 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 8600ecc5878ac43abd796797d1fca2da0b8d55f6 +Subproject commit d756a78f95ebb891a1abdb40e79561f3419a3c43 From d13e1160b4e11e097b880dd0f89b2c87cb495685 Mon Sep 17 00:00:00 2001 From: jusohn Date: Fri, 27 Oct 2023 15:21:22 +0900 Subject: [PATCH 21/48] =?UTF-8?q?[FE]=20FEAT:=20config=20=EC=97=90=20?= =?UTF-8?q?=EC=97=B0=EC=9E=A5=EA=B6=8C=20=EC=82=AC=EC=9A=A9=EA=B8=B0?= =?UTF-8?q?=EA=B0=84=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.tsx | 2 - .../CabinetInfoArea.container.tsx | 10 ++-- .../LeftNav/LeftMainNav/LeftMainNav.tsx | 47 ++++++++++--------- .../LeftNav/LeftSectionNav/LeftSectionNav.tsx | 13 ++--- .../Modals/ExtendModal/ExtendModal.tsx | 3 +- .../TopNavButtonGroup/TopNavButtonGroup.tsx | 2 +- frontend/src/pages/admin/SearchPage.tsx | 2 +- frontend/src/utils/dateUtils.ts | 16 +++++-- 8 files changed, 55 insertions(+), 40 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 740f2a360..ece075b5f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -8,8 +8,6 @@ import MainPage from "@/pages/MainPage"; import AdminMainPage from "@/pages/admin/AdminMainPage"; import LoadingAnimation from "@/components/Common/LoadingAnimation"; import ProfilePage from "./pages/ProfilePage"; -import "./firebase-messaging-sw" - const NotFoundPage = lazy(() => import("@/pages/NotFoundPage")); const LoginFailurePage = lazy(() => import("@/pages/LoginFailurePage")); diff --git a/frontend/src/components/CabinetInfoArea/CabinetInfoArea.container.tsx b/frontend/src/components/CabinetInfoArea/CabinetInfoArea.container.tsx index 7891009b5..b0b420632 100644 --- a/frontend/src/components/CabinetInfoArea/CabinetInfoArea.container.tsx +++ b/frontend/src/components/CabinetInfoArea/CabinetInfoArea.container.tsx @@ -85,7 +85,7 @@ export type TModalState = export type TAdminModalState = "returnModal" | "statusModal" | "clubLentModal"; -const calExpiredTime = (expireTime: Date) => +export const calExpiredTime = (expireTime: Date) => Math.floor( (expireTime.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24) ); @@ -118,7 +118,9 @@ const getCabinetUserList = (selectedCabinetInfo: CabinetInfo): string => { return userNameList; }; -const getDetailMessage = (selectedCabinetInfo: CabinetInfo): string | null => { +export const getDetailMessage = ( + selectedCabinetInfo: CabinetInfo +): string | null => { const { status, lentType, lents } = selectedCabinetInfo; // 밴, 고장 사물함 if (status === CabinetStatus.BANNED || status === CabinetStatus.BROKEN) @@ -136,7 +138,9 @@ const getDetailMessage = (selectedCabinetInfo: CabinetInfo): string | null => { else return null; }; -const getDetailMessageColor = (selectedCabinetInfo: CabinetInfo): string => { +export const getDetailMessageColor = ( + selectedCabinetInfo: CabinetInfo +): string => { const { status, lentType, lents } = selectedCabinetInfo; // 밴, 고장 사물함 if (status === CabinetStatus.BANNED || status === CabinetStatus.BROKEN) diff --git a/frontend/src/components/LeftNav/LeftMainNav/LeftMainNav.tsx b/frontend/src/components/LeftNav/LeftMainNav/LeftMainNav.tsx index 7dd21770e..ca630e1d4 100644 --- a/frontend/src/components/LeftNav/LeftMainNav/LeftMainNav.tsx +++ b/frontend/src/components/LeftNav/LeftMainNav/LeftMainNav.tsx @@ -72,53 +72,58 @@ const LeftMainNav = ({ ? "active cabiButton" : " cabiButton" } + src={"/src/assets/images/search.svg"} onClick={onClickSearchButton} > - +

Search
- + - +
Contact
- +
Club
- +
Logout
)} {!isAdmin && ( - - - Profile - + <> + +
+ Profile +
+ )} diff --git a/frontend/src/components/LeftNav/LeftSectionNav/LeftSectionNav.tsx b/frontend/src/components/LeftNav/LeftSectionNav/LeftSectionNav.tsx index 8b93607e5..c1f4b532d 100644 --- a/frontend/src/components/LeftNav/LeftSectionNav/LeftSectionNav.tsx +++ b/frontend/src/components/LeftNav/LeftSectionNav/LeftSectionNav.tsx @@ -73,14 +73,14 @@ const LeftSectionNav = ({ title="슬랙 캐비닛 채널 새창으로 열기" > 문의하기 - + onClickClubForm()} title="동아리 사물함 사용 신청서 새창으로 열기" > 동아리 신청서 - + @@ -147,7 +147,7 @@ const SectionLinkStyled = styled.div` display: flex; align-items: center; color: var(--gray-color); - & svg { + & img { width: 15px; height: 15px; margin-left: auto; @@ -155,9 +155,10 @@ const SectionLinkStyled = styled.div` @media (hover: hover) and (pointer: fine) { &:hover { color: var(--main-color); - svg { - stroke: var(--main-color); - } + } + &:hover img { + filter: invert(33%) sepia(55%) saturate(3554%) hue-rotate(230deg) + brightness(99%) contrast(107%); } } `; diff --git a/frontend/src/components/Modals/ExtendModal/ExtendModal.tsx b/frontend/src/components/Modals/ExtendModal/ExtendModal.tsx index a0127fe84..ab2254f00 100644 --- a/frontend/src/components/Modals/ExtendModal/ExtendModal.tsx +++ b/frontend/src/components/Modals/ExtendModal/ExtendModal.tsx @@ -51,7 +51,8 @@ const ExtendModal: React.FC<{ 연장권을 사용하시겠습니까?`; const extendInfoDetail = `사물함을 대여하시면 연장권 사용이 가능합니다. 연장권은 ${getLastDayofMonthString( - null + null, + "/" )} 23:59 이후 만료됩니다.`; const getModalTitle = (cabinetId: number | null) => { return cabinetId === null diff --git a/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx b/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx index 09303d52c..e5788fe6f 100644 --- a/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx +++ b/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx @@ -19,7 +19,7 @@ import { } from "@/api/axios/axios.custom"; import useMenu from "@/hooks/useMenu"; -const getDefaultCabinetInfo = (myInfo: UserDto): CabinetInfo => ({ +export const getDefaultCabinetInfo = (myInfo: UserDto): CabinetInfo => ({ building: "", floor: 0, cabinetId: 0, diff --git a/frontend/src/pages/admin/SearchPage.tsx b/frontend/src/pages/admin/SearchPage.tsx index 7552e3e2c..92f355210 100644 --- a/frontend/src/pages/admin/SearchPage.tsx +++ b/frontend/src/pages/admin/SearchPage.tsx @@ -61,7 +61,7 @@ const SearchPage = () => { searchValue.current, currentPage.current ); - + setSearchListByIntraId(searchResult.data.result ?? []); setTotalSearchList(Math.ceil(searchResult.data.totalLength / 10) ?? 0); setTimeout(() => { diff --git a/frontend/src/utils/dateUtils.ts b/frontend/src/utils/dateUtils.ts index 1433798b6..ef8e4efc1 100644 --- a/frontend/src/utils/dateUtils.ts +++ b/frontend/src/utils/dateUtils.ts @@ -2,12 +2,12 @@ export const padTo2Digits = (num: number) => { return num.toString().padStart(2, "0"); }; -export const formatDate = (date: Date) => { +export const formatDate = (date: Date, divider: string) => { return [ date.getFullYear(), padTo2Digits(date.getMonth() + 1), padTo2Digits(date.getDate()), - ].join("/"); + ].join(divider); }; export const getExpireDateString = ( @@ -22,7 +22,7 @@ export const getExpireDateString = ( if (!existExpireDate) expireDate.setDate(expireDate.getDate() + parseInt(addDays)); - return formatDate(expireDate); + return formatDate(expireDate, "/"); }; // 공유 사물함 반납 시 남은 대여일 수 차감 (원래 남은 대여일 수 * (남은 인원 / 원래 있던 인원)) @@ -39,7 +39,7 @@ export const getShortenedExpireDateString = ( let dateRemainig = (daysUntilExpire * (currentNumUsers - 1)) / currentNumUsers; let newExpireDate = new Date().getTime() + dateRemainig * dayInMilisec; - return formatDate(new Date(newExpireDate)); + return formatDate(new Date(newExpireDate), "/"); }; export const getExtendedDateString = (existExpireDate?: Date) => { @@ -47,7 +47,13 @@ export const getExtendedDateString = (existExpireDate?: Date) => { expireDate.setDate( expireDate.getDate() + parseInt(import.meta.env.VITE_EXTENDED_LENT_PERIOD) ); - return formatDate(expireDate); + return formatDate(expireDate, "/"); +}; + +export const getLastDayofMonthString = (date: Date | null, divider: string) => { + if (date === null) date = new Date(); + let lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0); + return formatDate(lastDay, divider); }; export const getLastDayofMonthString = (date: Date | null) => { From f4c461dbcc9cc752974130ba83c8b80dc820b71b Mon Sep 17 00:00:00 2001 From: jusohn Date: Thu, 9 Nov 2023 02:58:13 +0900 Subject: [PATCH 22/48] =?UTF-8?q?[FE]=20FEAT:=20ProfilePage=20=EC=97=90=20?= =?UTF-8?q?=EB=B3=B4=EC=97=AC=EC=A7=80=EB=8A=94=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=ED=99=94=20=EB=B0=8F=20=EA=B0=84=EC=86=8C?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Card/Card.tsx | 60 +++++++ frontend/src/components/Card/CardStyles.ts | 83 ++++++++++ .../ExtensionCard/ExtensionCard.container.tsx | 17 ++ .../Card/ExtensionCard/ExtensionCard.tsx | 44 +++++ .../LentInfoCard/LentInfoCard.container.tsx | 55 +++++++ .../Card/LentInfoCard/LentInfoCard.tsx | 151 ++++++++++++++++++ .../NotificationCard.container.tsx | 7 + .../NotificationCard/NotificationCard.tsx | 36 +++++ .../ProfileCard/ProfileCard.container.tsx | 47 ++++++ .../Card/ProfileCard/ProfileCard.tsx | 52 ++++++ .../ThemeColorCard.container.tsx | 69 ++++++++ .../Card/ThemeColorCard/ThemeColorCard.tsx | 106 ++++++++++++ frontend/src/pages/ProfilePage.tsx | 93 +++++++---- 13 files changed, 789 insertions(+), 31 deletions(-) create mode 100644 frontend/src/components/Card/Card.tsx create mode 100644 frontend/src/components/Card/CardStyles.ts create mode 100644 frontend/src/components/Card/ExtensionCard/ExtensionCard.container.tsx create mode 100644 frontend/src/components/Card/ExtensionCard/ExtensionCard.tsx create mode 100644 frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx create mode 100644 frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx create mode 100644 frontend/src/components/Card/NotificationCard/NotificationCard.container.tsx create mode 100644 frontend/src/components/Card/NotificationCard/NotificationCard.tsx create mode 100644 frontend/src/components/Card/ProfileCard/ProfileCard.container.tsx create mode 100644 frontend/src/components/Card/ProfileCard/ProfileCard.tsx create mode 100644 frontend/src/components/Card/ThemeColorCard/ThemeColorCard.container.tsx create mode 100644 frontend/src/components/Card/ThemeColorCard/ThemeColorCard.tsx diff --git a/frontend/src/components/Card/Card.tsx b/frontend/src/components/Card/Card.tsx new file mode 100644 index 000000000..97953442b --- /dev/null +++ b/frontend/src/components/Card/Card.tsx @@ -0,0 +1,60 @@ +import React from "react"; +import { + CardButtonStyled, + CardButtonWrapper, + CardHeaderStyled, + CardStyled, + CardTitleStyled, +} from "@/components/Card/CardStyles"; + +export interface IButtonProps { + label: string; + onClick?: () => void; + backgroundColor?: string; + color?: string; + isClickable: boolean; + isExtensible?: boolean; +} + +interface CardProps { + title: string; + children: React.ReactElement; + buttons?: IButtonProps[]; + gridArea: string; + width?: string; + height?: string; +} + +const Card = ({ + title, + gridArea, + width = "350px", + height = "163px", + buttons, + children, +}: CardProps) => { + return ( + + + {title} + + {buttons?.map((button, index) => ( + + {button.label} + + ))} + + + {children} + + ); +}; + +export default Card; diff --git a/frontend/src/components/Card/CardStyles.ts b/frontend/src/components/Card/CardStyles.ts new file mode 100644 index 000000000..4b0f1537e --- /dev/null +++ b/frontend/src/components/Card/CardStyles.ts @@ -0,0 +1,83 @@ +import styled from "styled-components"; + +export const CardStyled = styled.div<{ + width: string; + height: string; + gridArea: string; +}>` + width: ${(props) => props.width}; + border-radius: 10px; + background-color: var(--lightgary-color); + display: flex; + flex-direction: column; + align-items: center; + height: ${(props) => props.height}; + grid-area: ${(props) => props.gridArea}; +`; + +export const CardHeaderStyled = styled.div` + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 20px 10px 30px; +`; + +export const CardTitleStyled = styled.div` + font-size: 1.2em; + font-weight: bold; + margin-right: auto; +`; + +export const CardButtonWrapper = styled.div` + display: flex; +`; + +export const CardButtonStyled = styled.div<{ + backgroundColor?: string; + color?: string; + isClickable?: boolean; + isExtensible?: boolean; +}>` + background-color: ${(props) => + props.backgroundColor ? props.backgroundColor : "var(--white)"}; + color: ${(props) => + props.color + ? props.color + : props.isExtensible + ? "var(--main-color)" + : "var(--gray-color)"}; + padding: 5px 15px; + border: none; + border-radius: 5px; + cursor: ${(props) => (props.isClickable ? "pointer" : "default")}; + margin-left: 10px; +`; + +export const CardContentWrapper = styled.div` + background-color: var(--white); + border-radius: 10px; + padding: 15px 0; + margin: 10px 5px 0 5px; + width: 90%; + display: flex; + flex-direction: column; +`; + +export const CardContentStyled = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + margin: 5px 0 5px 0; +`; + +export const ContentInfoStyled = styled.div` + display: flex; + margin: 0 0 0 10px; +`; + +export const ContentDeatilStyled = styled.div` + display: flex; + margin: 0 10px 5px 0; + font-weight: bold; +`; diff --git a/frontend/src/components/Card/ExtensionCard/ExtensionCard.container.tsx b/frontend/src/components/Card/ExtensionCard/ExtensionCard.container.tsx new file mode 100644 index 000000000..4eea4b1dd --- /dev/null +++ b/frontend/src/components/Card/ExtensionCard/ExtensionCard.container.tsx @@ -0,0 +1,17 @@ +import ExtensionCard from "@/components/Card/ExtensionCard/ExtensionCard"; + +const ExtensionCardContainer = ({ extensible }: { extensible: boolean }) => { + return ( + {}, + isClickable: false, + isExtensible: extensible, + }} + /> + ); +}; + +export default ExtensionCardContainer; diff --git a/frontend/src/components/Card/ExtensionCard/ExtensionCard.tsx b/frontend/src/components/Card/ExtensionCard/ExtensionCard.tsx new file mode 100644 index 000000000..592f0c9d6 --- /dev/null +++ b/frontend/src/components/Card/ExtensionCard/ExtensionCard.tsx @@ -0,0 +1,44 @@ +import Card, { IButtonProps } from "@/components/Card/Card"; +import { + CardContentStyled, + CardContentWrapper, + ContentDeatilStyled, + ContentInfoStyled, +} from "@/components/Card/CardStyles"; +import { getLastDayofMonthString } from "@/utils/dateUtils"; + +interface ExtensionProps { + extensible: boolean; + button: IButtonProps; +} + +const ExtensionCard = ({ extensible, button }: ExtensionProps) => { + return ( + + + + 사용 기한 + + {extensible ? getLastDayofMonthString(null, ".") : "-"} + + + + 연장 기간 + + {extensible + ? parseInt(import.meta.env.VITE_EXTENDED_LENT_PERIOD) + "일" + : "-"} + + + + + ); +}; + +export default ExtensionCard; diff --git a/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx b/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx new file mode 100644 index 000000000..b0f102312 --- /dev/null +++ b/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx @@ -0,0 +1,55 @@ +import { useRecoilValue } from "recoil"; +import { myCabinetInfoState } from "@/recoil/atoms"; +import LentInfoCard from "@/components/Card/LentInfoCard/LentInfoCard"; +import { CabinetInfo } from "@/types/dto/cabinet.dto"; +import CabinetType from "@/types/enum/cabinet.type.enum"; + +export interface MyCabinetInfo { + floor: number; + section: string; + cabinetId: number; + visibleNum: number; + lentType: CabinetType; + userNameList: string; + expireDate?: Date; + isLented: boolean; + previousUserName: string; +} + +const LentInfoCardContainer = () => { + const myCabinetInfo = useRecoilValue(myCabinetInfoState); + + const getCabinetUserList = (selectedCabinetInfo: CabinetInfo): string => { + const { lents } = selectedCabinetInfo; + // 동아리 사물함인 경우 cabinet_title 에 있는 동아리 이름 반환 + // if (lentType === "CLUB" && title) return TitleStyled; + if (lents.length === 0) return ""; + // 그 외에는 유저리스트 반환 + return new Array(lents.length) + .fill(null) + .map((_, idx) => lents[idx]) + .map((info) => (info ? info.name : "")) + .join(", "); + }; + + const cabinetLentInfo: MyCabinetInfo | null = myCabinetInfo + ? { + floor: myCabinetInfo.floor, + section: myCabinetInfo.section, + cabinetId: myCabinetInfo.cabinetId, + visibleNum: myCabinetInfo.visibleNum, + lentType: myCabinetInfo.lentType, + userNameList: getCabinetUserList(myCabinetInfo), + expireDate: + myCabinetInfo.lents.length !== 0 + ? myCabinetInfo.lents[0].expiredAt + : undefined, + isLented: myCabinetInfo.lents.length !== 0, + previousUserName: myCabinetInfo.previousUserName, + } + : null; + + return ; +}; + +export default LentInfoCardContainer; diff --git a/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx b/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx new file mode 100644 index 000000000..86801650c --- /dev/null +++ b/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx @@ -0,0 +1,151 @@ +import styled from "styled-components"; +import Card from "@/components/Card/Card"; +import { + CardContentStyled, + CardContentWrapper, + ContentDeatilStyled, + ContentInfoStyled, +} from "@/components/Card/CardStyles"; +import { MyCabinetInfo } from "@/components/Card/LentInfoCard/LentInfoCard.container"; +import { cabinetIconSrcMap } from "@/assets/data/maps"; +import CabinetType from "@/types/enum/cabinet.type.enum"; +import { formatDate, getRemainingTime } from "@/utils/dateUtils"; + +const LentInfoCard = ({ + cabinetInfo, +}: { + cabinetInfo: MyCabinetInfo | null; +}) => { + return ( + + <> + + + {cabinetInfo!.visibleNum !== 0 ? cabinetInfo!.visibleNum : "-"} + + + + {cabinetInfo!.floor !== 0 + ? cabinetInfo!.floor + "층 - " + cabinetInfo!.section + : ""} + + {cabinetInfo?.isLented && ( + + + + {cabinetInfo!.userNameList} + + + )} + + + + + 사용 기간 + + {cabinetInfo?.isLented + ? `${ + cabinetInfo!.lentType === "PRIVATE" + ? parseInt(import.meta.env.VITE_PRIVATE_LENT_PERIOD) + : parseInt(import.meta.env.VITE_SHARE_LENT_PERIOD) + }일` + : "-"} + + + + 남은 기간 + + {cabinetInfo?.expireDate + ? getRemainingTime(cabinetInfo?.expireDate) + "일" + : "-"} + + + + 종료 일자 + + {cabinetInfo?.expireDate + ? formatDate(new Date(cabinetInfo?.expireDate), ".") + : "-"} + + + + + + 이전 대여자 + + {cabinetInfo?.previousUserName || "-"} + + + + + + ); +}; + +const CabinetInfoWrapper = styled.div` + display: flex; + width: 80%; + margin: 10px 0 15px 0; + align-items: center; +`; + +const CabinetRectangleStyled = styled.div<{ + isMine: boolean; +}>` + width: 60px; + height: 60px; + line-height: 60px; + border-radius: 10px; + margin-right: 20px; + background-color: var(--mine); + font-size: 32px; + text-align: center; +`; + +const CabinetInfoDetailStyled = styled.div` + display: flex; + flex-direction: column; + align-items: center; +`; + +const CabinetInfoTextStyled = styled.div<{ + fontSize: string; + fontColor: string; +}>` + font-size: ${(props) => props.fontSize}; + font-weight: 400; + line-height: 28px; + color: ${(props) => props.fontColor}; + text-align: center; + white-space: pre-line; +`; + +const CabinetUserListWrapper = styled.div` + display: flex; + flex-direction: row; + align-items: center; +`; + +const CabinetIconStyled = styled.div<{ cabinetType: CabinetType }>` + width: 24px; + height: 24px; + min-width: 24px; + min-height: 24px; + margin-right: 10px; + background-image: url(${(props) => cabinetIconSrcMap[props.cabinetType]}); + background-size: contain; + background-repeat: no-repeat; +`; + +export default LentInfoCard; diff --git a/frontend/src/components/Card/NotificationCard/NotificationCard.container.tsx b/frontend/src/components/Card/NotificationCard/NotificationCard.container.tsx new file mode 100644 index 000000000..b807e5066 --- /dev/null +++ b/frontend/src/components/Card/NotificationCard/NotificationCard.container.tsx @@ -0,0 +1,7 @@ +import NotificationCard from "@/components/Card/NotificationCard/NotificationCard"; + +const NotificationCardContainer = () => { + return ; +}; + +export default NotificationCardContainer; diff --git a/frontend/src/components/Card/NotificationCard/NotificationCard.tsx b/frontend/src/components/Card/NotificationCard/NotificationCard.tsx new file mode 100644 index 000000000..17b04ac5b --- /dev/null +++ b/frontend/src/components/Card/NotificationCard/NotificationCard.tsx @@ -0,0 +1,36 @@ +import Card from "@/components/Card/Card"; +import { + CardContentStyled, + CardContentWrapper, + ContentInfoStyled, +} from "@/components/Card/CardStyles"; +import PillButton from "@/components/Common/PillButton"; +import ToggleSwitch from "@/components/Common/ToggleSwitch"; + +const NotificationCard = () => { + return ( + + + + 메일 + + + + 슬랙 + + + + 브라우저 + + + + + ); +}; + +export default NotificationCard; diff --git a/frontend/src/components/Card/ProfileCard/ProfileCard.container.tsx b/frontend/src/components/Card/ProfileCard/ProfileCard.container.tsx new file mode 100644 index 000000000..f62303f92 --- /dev/null +++ b/frontend/src/components/Card/ProfileCard/ProfileCard.container.tsx @@ -0,0 +1,47 @@ +import { useNavigate } from "react-router-dom"; +import { useResetRecoilState } from "recoil"; +import { + currentBuildingNameState, + currentFloorNumberState, + currentSectionNameState, +} from "@/recoil/atoms"; +import ProfileCard from "@/components/Card/ProfileCard/ProfileCard"; +import { removeCookie } from "@/api/react_cookie/cookies"; + +const ProfileCardContainer = ({ name }: { name: string | null }) => { + const navigator = useNavigate(); + const resetCurrentFloor = useResetRecoilState(currentFloorNumberState); + const resetCurrentSection = useResetRecoilState(currentSectionNameState); + const resetBuilding = useResetRecoilState(currentBuildingNameState); + + const onClickLogoutButton = (): void => { + if (import.meta.env.VITE_IS_LOCAL === "true") { + removeCookie("access_token", { + path: "/", + domain: "localhost", + }); + } else { + removeCookie("access_token", { + path: "/", + domain: "cabi.42seoul.io", + }); + } + resetBuilding(); + resetCurrentFloor(); + resetCurrentSection(); + navigator("/login"); + }; + + return ( + + ); +}; + +export default ProfileCardContainer; diff --git a/frontend/src/components/Card/ProfileCard/ProfileCard.tsx b/frontend/src/components/Card/ProfileCard/ProfileCard.tsx new file mode 100644 index 000000000..f6da8f4c5 --- /dev/null +++ b/frontend/src/components/Card/ProfileCard/ProfileCard.tsx @@ -0,0 +1,52 @@ +import styled from "styled-components"; +import Card, { IButtonProps } from "@/components/Card/Card"; + +type ProfileProps = { + name: string | null; + button: IButtonProps; +}; + +const ProfileCard = ({ name, button }: ProfileProps) => { + return ( + + + + + {name} + {name}@student.42seoul.kr + + + + ); +}; + +const ProfileContent = styled.div` + display: flex; + align-items: center; + padding: 10px; +`; + +const ProfileImage = styled.img` + width: 60px; + height: 60px; + border-radius: 10px; + margin-right: 20px; +`; + +const ProfileDetailWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; +`; + +const ProfileDetail = styled.div` + padding: 5px 0 0 0; +`; + +export default ProfileCard; diff --git a/frontend/src/components/Card/ThemeColorCard/ThemeColorCard.container.tsx b/frontend/src/components/Card/ThemeColorCard/ThemeColorCard.container.tsx new file mode 100644 index 000000000..6bdf81584 --- /dev/null +++ b/frontend/src/components/Card/ThemeColorCard/ThemeColorCard.container.tsx @@ -0,0 +1,69 @@ +import { useEffect, useState } from "react"; +import ThemeColorCard from "@/components/Card/ThemeColorCard/ThemeColorCard"; + +const ThemeColorCardContainer = () => { + const savedColor = localStorage.getItem("mainColor"); + const defaultColor = "#9747ff"; + const [mainColor, setMainColor] = useState( + savedColor ? savedColor : defaultColor + ); + const [showColorPicker, setShowColorPicker] = useState(false); + const root: HTMLElement = document.documentElement; + + const handleChange = (mainColor: { hex: string }) => { + const selectedColor: string = mainColor.hex; + setMainColor(selectedColor); + }; + + const handleReset = () => { + setMainColor(defaultColor); + root.style.setProperty("--main-color", defaultColor); + root.style.setProperty("--lightpurple-color", "#b18cff"); + localStorage.setItem("mainColor", defaultColor); + }; + + const handleSave = () => { + localStorage.setItem("mainColor", mainColor); + root.style.setProperty("--main-color", mainColor); + toggleColorPicker(true); + }; + + const handleCancel = () => { + const savedColor = localStorage.getItem("mainColor"); + root.style.setProperty("--main-color", savedColor); + toggleColorPicker(true); + }; + + const toggleColorPicker = (isChange: boolean) => { + if (isChange) setShowColorPicker(!showColorPicker); + }; + + const confirmBeforeUnload = (e: BeforeUnloadEvent) => { + if (mainColor !== localStorage.getItem("mainColor")) { + e.returnValue = + "변경된 색상이 저장되지 않을 수 있습니다. 계속하시겠습니까?"; + } + }; + + useEffect(() => { + root.style.setProperty("--main-color", mainColor); + window.addEventListener("beforeunload", confirmBeforeUnload); + return () => { + window.removeEventListener("beforeunload", confirmBeforeUnload); + }; + }, [mainColor]); + + return ( + + ); +}; + +export default ThemeColorCardContainer; diff --git a/frontend/src/components/Card/ThemeColorCard/ThemeColorCard.tsx b/frontend/src/components/Card/ThemeColorCard/ThemeColorCard.tsx new file mode 100644 index 000000000..b44476a24 --- /dev/null +++ b/frontend/src/components/Card/ThemeColorCard/ThemeColorCard.tsx @@ -0,0 +1,106 @@ +import React from "react"; +import { TwitterPicker } from "react-color"; +import styled from "styled-components"; +import Card from "@/components/Card/Card"; +import { + CardContentStyled, + CardContentWrapper, + ContentInfoStyled, +} from "@/components/Card/CardStyles"; + +interface ThemeColorProps { + showColorPicker: boolean; + setShowColorPicker: React.Dispatch>; + handleChange: (mainColor: { hex: string }) => void; + handleReset: () => void; + handleSave: () => void; + handleCancel: () => void; + mainColor: string; +} + +const ThemeColorCard = ({ + showColorPicker, + setShowColorPicker, + handleChange, + handleReset, + handleSave, + handleCancel, + mainColor, +}: ThemeColorProps) => { + return ( + <> + {showColorPicker && } + + + + + 메인 컬러 + setShowColorPicker(!showColorPicker)} + /> + + {showColorPicker && ( + + )} + + + + + ); +}; + +const BackgroundOverlayStyled = styled.div` + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.4); + z-index: 0; +`; + +const ThemeColorCardWrapper = styled.div` + z-index: 1; +`; + +const MainColorButtonStyled = styled.button` + width: 28px; + height: 28px; + background-color: var(--main-color); + margin: 0 10px 5px 0; + border-radius: 8px; +`; + +export default ThemeColorCard; diff --git a/frontend/src/pages/ProfilePage.tsx b/frontend/src/pages/ProfilePage.tsx index b08bae54e..91570b4ef 100644 --- a/frontend/src/pages/ProfilePage.tsx +++ b/frontend/src/pages/ProfilePage.tsx @@ -1,44 +1,75 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; +import { useRecoilState, useRecoilValue } from "recoil"; import styled from "styled-components"; -import ThemeColorContainer from "@/components/Profile/ThemeColor.container"; +import { myCabinetInfoState, userState } from "@/recoil/atoms"; +import ExtensionCardContainer from "@/components/Card/ExtensionCard/ExtensionCard.container"; +import LentInfoCardContainer from "@/components/Card/LentInfoCard/LentInfoCard.container"; +import NotificationCardContainer from "@/components/Card/NotificationCard/NotificationCard.container"; +import ProfileCardContainer from "@/components/Card/ProfileCard/ProfileCard.container"; +import ThemeColorCardContainer from "@/components/Card/ThemeColorCard/ThemeColorCard.container"; +import LoadingAnimation from "@/components/Common/LoadingAnimation"; +import { getDefaultCabinetInfo } from "@/components/TopNav/TopNavButtonGroup/TopNavButtonGroup"; const ProfilePage = () => { - const [showThemeChange, setShowThemeChange] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [myCabinetInfo, setMyCabinetInfo] = useRecoilState(myCabinetInfoState); + const myInfo = useRecoilValue(userState); + const defaultCabinetInfo = getDefaultCabinetInfo(myInfo); + + useEffect(() => { + setIsLoading(true); + if (!myCabinetInfo) { + setMyCabinetInfo({ + ...defaultCabinetInfo, + memo: "", + shareCode: 0, + previousUserName: "", + }); + } + setIsLoading(false); + }, []); + return ( - - {showThemeChange && } - - - - + <> + {isLoading ? ( + + ) : ( + + + + + + + + )} + ); }; -const WrapperStyled = styled.div` - display: flex; - flex-direction: column; +const CardGridWrapper = styled.div` + display: grid; + padding: 60px 0; justify-content: center; align-items: center; - padding: 70px 0; - @media screen and (max-width: 768px) { - padding: 40px 20px; - } -`; - -const BackgroundOverlayStyled = styled.div` - position: fixed; - top: 0; - left: 0; width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.4); - z-index: 0; -`; + grid-gap: 20px; + grid-template-columns: 350px 350px; + grid-template-rows: 163px 183px 215px; + grid-template-areas: + "profile lentInfo" + "extension lentInfo" + "theme notification"; -const ItemStyeld = styled.div` - z-index: 1; + @media (max-width: 768px) { + grid-template-columns: 350px; + grid-template-rows: 163px 366px 183px 215px 215px; + grid-template-areas: + "profile" + "lentInfo" + "extension" + "theme" + "notification"; + } `; + export default ProfilePage; From 56594371dd46f9346859daf86bed4f8ebd94dfd0 Mon Sep 17 00:00:00 2001 From: jusohn Date: Thu, 9 Nov 2023 02:59:18 +0900 Subject: [PATCH 23/48] =?UTF-8?q?[FE]=20FEAT:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=EC=9D=84=20=EC=9C=84=ED=95=9C=20ToggleSwitch?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Common/ToggleSwitch.tsx | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 frontend/src/components/Common/ToggleSwitch.tsx diff --git a/frontend/src/components/Common/ToggleSwitch.tsx b/frontend/src/components/Common/ToggleSwitch.tsx new file mode 100644 index 000000000..b75b73818 --- /dev/null +++ b/frontend/src/components/Common/ToggleSwitch.tsx @@ -0,0 +1,96 @@ +import React, { useState } from "react"; +import styled, { css } from "styled-components"; + +interface ToggleSwitchInterface { + id: string; + onChange?: (checked: boolean) => void; + checked: boolean; + disabled?: boolean; +} + +const ToggleSwitch = ({ + id, + onChange, + checked = false, + disabled, +}: ToggleSwitchInterface) => { + const [isChecked, setIsChecked] = useState(checked); + + const handleChange = (event: React.ChangeEvent) => { + const newState = event.target.checked; + setIsChecked(newState); + if (onChange) { + onChange(newState); + } + }; + + return ( + + + + + + + ); +}; + +const ToggleSwitchContainerStyled = styled.div<{ disabled?: boolean }>` + display: inline-block; + position: relative; + margin-right: 10px; + opacity: ${(props) => (props.disabled ? 0.5 : 1)}; +`; + +const InputStyled = styled.input.attrs({ type: "checkbox" })` + opacity: 0; + position: absolute; + width: 0; + height: 0; +`; + +const ToggleSwitchStyled = styled.label<{ + checked: boolean; + disabled?: boolean; +}>` + cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")}; + display: inline-block; + position: relative; + background: ${(props) => + props.checked ? "var(--main-color)" : "var(--lightgary-color)"}; + width: 56px; + height: 28px; + border-radius: 50px; + transition: background-color 0.2s ease; + &:after { + content: ""; + position: absolute; + top: 2px; + left: 2px; + width: 24px; + height: 24px; + transition: 0.2s; + transform: ${(props) => + props.checked ? "translateX(28px)" : "translateX(0)"}; + } +`; + +const ToggleKnobStyled = styled.span<{ checked: boolean }>` + position: absolute; + top: 2px; + left: 2px; + width: 24px; + height: 24px; + border-radius: 50%; + background: var(--white); + transition: transform 0.2s; + transform: ${(props) => + props.checked ? "translateX(28px)" : "translateX(0)"}; +`; + +export default ToggleSwitch; From b962cff92f83b16cb2076f1d1881fbc15b2eee87 Mon Sep 17 00:00:00 2001 From: jusohn Date: Thu, 9 Nov 2023 03:01:47 +0900 Subject: [PATCH 24/48] =?UTF-8?q?[FE]=20REFACTOR:=20=EB=82=A0=EC=A7=9C=20s?= =?UTF-8?q?tring=20=EC=9D=84=20=EC=9C=A0=EC=97=B0=ED=95=98=EA=B2=8C=20?= =?UTF-8?q?=EB=B0=94=EA=BE=B8=EA=B8=B0=20=EC=9C=84=ED=95=B4=20formatDate?= =?UTF-8?q?=20=EC=97=90=20divier=20=EB=A5=BC=20=EC=A0=95=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/utils/dateUtils.ts | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/frontend/src/utils/dateUtils.ts b/frontend/src/utils/dateUtils.ts index ef8e4efc1..5dc6829e5 100644 --- a/frontend/src/utils/dateUtils.ts +++ b/frontend/src/utils/dateUtils.ts @@ -2,7 +2,8 @@ export const padTo2Digits = (num: number) => { return num.toString().padStart(2, "0"); }; -export const formatDate = (date: Date, divider: string) => { +export const formatDate = (date: Date | null, divider: string) => { + if (date === null) return ""; return [ date.getFullYear(), padTo2Digits(date.getMonth() + 1), @@ -22,7 +23,8 @@ export const getExpireDateString = ( if (!existExpireDate) expireDate.setDate(expireDate.getDate() + parseInt(addDays)); - return formatDate(expireDate, "/"); + return formatDate(expireDate, "/"); + } }; // 공유 사물함 반납 시 남은 대여일 수 차감 (원래 남은 대여일 수 * (남은 인원 / 원래 있던 인원)) @@ -66,10 +68,19 @@ export const getTotalPage = (totalLength: number, size: number) => { return Math.ceil(totalLength / size); }; -export const getFormatDate = (date: Date | null): string => { - if (!date) return ""; - const year = date.getFullYear(); - const month = (date.getMonth() + 1).toString().padStart(2, "0"); - const day = date.getDate().toString().padStart(2, "0"); - return `${year}/${month}/${day}`; +export const calExpiredTime = (expireTime: Date) => + Math.floor( + (expireTime.getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24) + ); + +export const getRemainingTime = (expireTime: Date | undefined) => { + if (!expireTime) return 0; + const remainTime = calExpiredTime(new Date(expireTime)); + return remainTime < 0 ? -remainTime : remainTime; +}; + +export const getExpireDate = (date: Date | undefined) => { + if (!date) return null; + if (date.toString().slice(0, 4) === "9999") return null; + return date.toString().slice(0, 10); }; From a559d5ca7a8efed1563f905b12b8f25c5d4b2b50 Mon Sep 17 00:00:00 2001 From: jusohn Date: Thu, 9 Nov 2023 03:18:58 +0900 Subject: [PATCH 25/48] =?UTF-8?q?[FE]=20FIX:=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EB=90=98=EC=A7=80=20=EC=95=8A=EC=9D=80=20date=20fo?= =?UTF-8?q?rmat=20=ED=95=A8=EC=88=98=20=EC=9D=B4=EB=A6=84=20=EB=B0=8F=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=EB=B6=80=EB=B6=84=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Modals/OverduePenaltyModal/OverduePenaltyModal.tsx | 7 ++++--- frontend/src/utils/dateUtils.ts | 9 +-------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/Modals/OverduePenaltyModal/OverduePenaltyModal.tsx b/frontend/src/components/Modals/OverduePenaltyModal/OverduePenaltyModal.tsx index 2551273b9..d1bdb83a5 100644 --- a/frontend/src/components/Modals/OverduePenaltyModal/OverduePenaltyModal.tsx +++ b/frontend/src/components/Modals/OverduePenaltyModal/OverduePenaltyModal.tsx @@ -5,7 +5,7 @@ import ModalPortal from "@/components/Modals/ModalPortal"; import { additionalModalType, modalPropsMap } from "@/assets/data/maps"; import errorIcon from "@/assets/images/errorIcon.svg"; import CabinetStatus from "@/types/enum/cabinet.status.enum"; -import { getFormatDate } from "@/utils/dateUtils"; +import { formatDate } from "@/utils/dateUtils"; const OverduePenaltyModal: React.FC<{ status: CabinetStatus | additionalModalType; @@ -14,8 +14,9 @@ const OverduePenaltyModal: React.FC<{ }> = (props) => { const unbannedAtDate = props.unbannedAt ? new Date(props.unbannedAt) : null; - const penaltyDateDetail = `패널티 기간은 ${getFormatDate( - unbannedAtDate + const penaltyDateDetail = `패널티 기간은 ${formatDate( + unbannedAtDate, + "/" )} 23:59 까지 입니다. 해당 기간까지 대여를 하실 수 없습니다.`; const localStorageKey = "hideOverdueModalForOneDay"; diff --git a/frontend/src/utils/dateUtils.ts b/frontend/src/utils/dateUtils.ts index 5dc6829e5..bc832a54e 100644 --- a/frontend/src/utils/dateUtils.ts +++ b/frontend/src/utils/dateUtils.ts @@ -23,8 +23,7 @@ export const getExpireDateString = ( if (!existExpireDate) expireDate.setDate(expireDate.getDate() + parseInt(addDays)); - return formatDate(expireDate, "/"); - } + return formatDate(expireDate, "/"); }; // 공유 사물함 반납 시 남은 대여일 수 차감 (원래 남은 대여일 수 * (남은 인원 / 원래 있던 인원)) @@ -58,12 +57,6 @@ export const getLastDayofMonthString = (date: Date | null, divider: string) => { return formatDate(lastDay, divider); }; -export const getLastDayofMonthString = (date: Date | null) => { - if (date === null) date = new Date(); - let lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0); - return formatDate(lastDay); -}; - export const getTotalPage = (totalLength: number, size: number) => { return Math.ceil(totalLength / size); }; From 260df1a55507cbfed3bb51c27856f2d883fc990c Mon Sep 17 00:00:00 2001 From: jusohn Date: Thu, 9 Nov 2023 03:31:58 +0900 Subject: [PATCH 26/48] =?UTF-8?q?[FE]=20FIX:=20LeftMainNav=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=82=AC=EC=9A=A9=EB=90=98=EB=8A=94=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=ED=91=9C=EC=8B=9C=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20src=20=EC=9B=90=EC=83=81=EB=B3=B5=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/LeftNav/LeftMainNav/LeftMainNav.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/LeftNav/LeftMainNav/LeftMainNav.tsx b/frontend/src/components/LeftNav/LeftMainNav/LeftMainNav.tsx index ca630e1d4..162cc943a 100644 --- a/frontend/src/components/LeftNav/LeftMainNav/LeftMainNav.tsx +++ b/frontend/src/components/LeftNav/LeftMainNav/LeftMainNav.tsx @@ -194,7 +194,7 @@ const BottomBtnsStyled = styled.ul` text-align: center; `; -const BottomBtnStyled = styled.li` +const BottomBtnStyled = styled.li<{ src: string }>` width: 100%; min-height: 48px; line-height: 1.125rem; @@ -217,6 +217,7 @@ const BottomBtnStyled = styled.li` height: 24px; margin: 0 auto; margin-bottom: 4px; + background-image: url(${(props) => props.src}); } &.active { color: var(--main-color); From d9a4270ef55527ddf3626108d0020a642c8b0770 Mon Sep 17 00:00:00 2001 From: jusohn Date: Thu, 9 Nov 2023 10:24:59 +0900 Subject: [PATCH 27/48] =?UTF-8?q?[FE]=20FIX:=20=EC=82=AC=EB=AC=BC=ED=95=A8?= =?UTF-8?q?=20=EB=AF=B8=EB=8C=80=EC=97=AC=20=EC=8B=9C=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=B6=9C=EB=A0=A5=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EB=8D=94=EB=AF=B8=20=EB=B0=98=EB=82=A9=EC=9D=BC=EC=9E=90=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=EB=A5=BC=20undefined=20=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CabinetInfoArea/CabinetInfoArea.container.tsx | 7 +++++-- .../TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx | 10 +--------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/CabinetInfoArea/CabinetInfoArea.container.tsx b/frontend/src/components/CabinetInfoArea/CabinetInfoArea.container.tsx index b0b420632..6845b64d6 100644 --- a/frontend/src/components/CabinetInfoArea/CabinetInfoArea.container.tsx +++ b/frontend/src/components/CabinetInfoArea/CabinetInfoArea.container.tsx @@ -107,7 +107,7 @@ const getCabinetUserList = (selectedCabinetInfo: CabinetInfo): string => { // 동아리 사물함인 경우 cabinet_title에 있는 동아리 이름 반환 const { lentType, title, maxUser, lents } = selectedCabinetInfo; if (lentType === "CLUB" && title) return title; - else if (maxUser === 0) return lents[0].name; + else if (maxUser === 0) return ""; // 그 외에는 유저리스트 반환 const userNameList = new Array(maxUser) @@ -193,7 +193,10 @@ const CabinetInfoAreaContainer = (): JSX.Element => { status: targetCabinetInfo.status, lentType: targetCabinetInfo.lentType, userNameList: getCabinetUserList(targetCabinetInfo), - expireDate: targetCabinetInfo.lents[0]?.expiredAt, + expireDate: + targetCabinetInfo.lents.length !== 0 + ? targetCabinetInfo.lents[0].expiredAt + : undefined, detailMessage: getDetailMessage(targetCabinetInfo), detailMessageColor: getDetailMessageColor(targetCabinetInfo), isAdmin: isAdmin, diff --git a/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx b/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx index e5788fe6f..f1de85a82 100644 --- a/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx +++ b/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx @@ -29,15 +29,7 @@ export const getDefaultCabinetInfo = (myInfo: UserDto): CabinetInfo => ({ maxUser: 0, status: CabinetStatus.AVAILABLE, section: "", - lents: [ - { - userId: myInfo.userId, - name: myInfo.name, - lentHistoryId: 0, - startedAt: new Date(), - expiredAt: new Date(), - }, - ] as LentDto[], + lents: [] as LentDto[], statusNote: "", }); const TopNavButtonGroup = ({ isAdmin }: { isAdmin?: boolean }) => { From e51c2064aed4944f2629576c29d70bc2fe1edb79 Mon Sep 17 00:00:00 2001 From: jusohn Date: Thu, 9 Nov 2023 10:25:31 +0900 Subject: [PATCH 28/48] =?UTF-8?q?[FE]=20FIX:=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=EC=97=90=EC=84=9C=20=EB=B3=B4=EC=9D=B4?= =?UTF-8?q?=EB=8A=94=20margin-bottom=20=EC=9D=84=20=EC=95=8C=EB=A7=9E?= =?UTF-8?q?=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Card/CardStyles.ts | 2 +- .../src/components/Card/NotificationCard/NotificationCard.tsx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/components/Card/CardStyles.ts b/frontend/src/components/Card/CardStyles.ts index 4b0f1537e..a94b068af 100644 --- a/frontend/src/components/Card/CardStyles.ts +++ b/frontend/src/components/Card/CardStyles.ts @@ -58,7 +58,7 @@ export const CardContentWrapper = styled.div` background-color: var(--white); border-radius: 10px; padding: 15px 0; - margin: 10px 5px 0 5px; + margin: 5px 5px 10px 5px; width: 90%; display: flex; flex-direction: column; diff --git a/frontend/src/components/Card/NotificationCard/NotificationCard.tsx b/frontend/src/components/Card/NotificationCard/NotificationCard.tsx index 17b04ac5b..1b3205ae6 100644 --- a/frontend/src/components/Card/NotificationCard/NotificationCard.tsx +++ b/frontend/src/components/Card/NotificationCard/NotificationCard.tsx @@ -4,7 +4,6 @@ import { CardContentWrapper, ContentInfoStyled, } from "@/components/Card/CardStyles"; -import PillButton from "@/components/Common/PillButton"; import ToggleSwitch from "@/components/Common/ToggleSwitch"; const NotificationCard = () => { From f918faf140f9cd6fe6efb85216d0f7c4e3c8ee48 Mon Sep 17 00:00:00 2001 From: jusohn Date: Thu, 9 Nov 2023 10:28:51 +0900 Subject: [PATCH 29/48] =?UTF-8?q?[FE]=20REFACTOR:=20CardStyles=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20Card=20=EA=B4=80=EB=A0=A8=20styled=20=EB=A5=BC=20Ca?= =?UTF-8?q?rd=20=EB=A1=9C=20=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Card/Card.tsx | 62 +++++++++++++++++++--- frontend/src/components/Card/CardStyles.ts | 54 ------------------- 2 files changed, 55 insertions(+), 61 deletions(-) diff --git a/frontend/src/components/Card/Card.tsx b/frontend/src/components/Card/Card.tsx index 97953442b..d5702f403 100644 --- a/frontend/src/components/Card/Card.tsx +++ b/frontend/src/components/Card/Card.tsx @@ -1,11 +1,5 @@ import React from "react"; -import { - CardButtonStyled, - CardButtonWrapper, - CardHeaderStyled, - CardStyled, - CardTitleStyled, -} from "@/components/Card/CardStyles"; +import styled from "styled-components"; export interface IButtonProps { label: string; @@ -57,4 +51,58 @@ const Card = ({ ); }; +export const CardStyled = styled.div<{ + width: string; + height: string; + gridArea: string; +}>` + width: ${(props) => props.width}; + border-radius: 10px; + background-color: var(--lightgary-color); + display: flex; + flex-direction: column; + align-items: center; + height: ${(props) => props.height}; + grid-area: ${(props) => props.gridArea}; +`; + +export const CardHeaderStyled = styled.div` + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 20px 10px 30px; +`; + +export const CardTitleStyled = styled.div` + font-size: 1.2em; + font-weight: bold; + margin-right: auto; +`; + +export const CardButtonWrapper = styled.div` + display: flex; +`; + +export const CardButtonStyled = styled.div<{ + backgroundColor?: string; + color?: string; + isClickable?: boolean; + isExtensible?: boolean; +}>` + background-color: ${(props) => + props.backgroundColor ? props.backgroundColor : "var(--white)"}; + color: ${(props) => + props.color + ? props.color + : props.isExtensible + ? "var(--main-color)" + : "var(--gray-color)"}; + padding: 5px 15px; + border: none; + border-radius: 5px; + cursor: ${(props) => (props.isClickable ? "pointer" : "default")}; + margin-left: 10px; +`; + export default Card; diff --git a/frontend/src/components/Card/CardStyles.ts b/frontend/src/components/Card/CardStyles.ts index a94b068af..49893b216 100644 --- a/frontend/src/components/Card/CardStyles.ts +++ b/frontend/src/components/Card/CardStyles.ts @@ -1,59 +1,5 @@ import styled from "styled-components"; -export const CardStyled = styled.div<{ - width: string; - height: string; - gridArea: string; -}>` - width: ${(props) => props.width}; - border-radius: 10px; - background-color: var(--lightgary-color); - display: flex; - flex-direction: column; - align-items: center; - height: ${(props) => props.height}; - grid-area: ${(props) => props.gridArea}; -`; - -export const CardHeaderStyled = styled.div` - width: 100%; - display: flex; - justify-content: space-between; - align-items: center; - padding: 20px 20px 10px 30px; -`; - -export const CardTitleStyled = styled.div` - font-size: 1.2em; - font-weight: bold; - margin-right: auto; -`; - -export const CardButtonWrapper = styled.div` - display: flex; -`; - -export const CardButtonStyled = styled.div<{ - backgroundColor?: string; - color?: string; - isClickable?: boolean; - isExtensible?: boolean; -}>` - background-color: ${(props) => - props.backgroundColor ? props.backgroundColor : "var(--white)"}; - color: ${(props) => - props.color - ? props.color - : props.isExtensible - ? "var(--main-color)" - : "var(--gray-color)"}; - padding: 5px 15px; - border: none; - border-radius: 5px; - cursor: ${(props) => (props.isClickable ? "pointer" : "default")}; - margin-left: 10px; -`; - export const CardContentWrapper = styled.div` background-color: var(--white); border-radius: 10px; From 330e8b42fb2465494295460b6f01ee3145cf8226 Mon Sep 17 00:00:00 2001 From: jusohn Date: Thu, 9 Nov 2023 10:40:51 +0900 Subject: [PATCH 30/48] =?UTF-8?q?[FE]=20FEAT:=20LentInfoCard=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=9C=A0=EC=A0=80=20=EC=88=98=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=9D=BC=20=ED=8F=B0=ED=8A=B8=20=ED=81=AC=EA=B8=B0=EB=A5=BC=20?= =?UTF-8?q?=EA=B0=80=EB=B3=80=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EC=A0=95?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LentInfoCard/LentInfoCard.container.tsx | 2 ++ .../Card/LentInfoCard/LentInfoCard.tsx | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx b/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx index b0f102312..64a4d8e38 100644 --- a/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx +++ b/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx @@ -10,6 +10,7 @@ export interface MyCabinetInfo { cabinetId: number; visibleNum: number; lentType: CabinetType; + userCount: number; userNameList: string; expireDate?: Date; isLented: boolean; @@ -39,6 +40,7 @@ const LentInfoCardContainer = () => { cabinetId: myCabinetInfo.cabinetId, visibleNum: myCabinetInfo.visibleNum, lentType: myCabinetInfo.lentType, + userCount: myCabinetInfo.lents.length, userNameList: getCabinetUserList(myCabinetInfo), expireDate: myCabinetInfo.lents.length !== 0 diff --git a/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx b/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx index 86801650c..19c03f0a1 100644 --- a/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx +++ b/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx @@ -16,10 +16,20 @@ const LentInfoCard = ({ }: { cabinetInfo: MyCabinetInfo | null; }) => { + const calculateFontSize = (userCount: number): string => { + const baseSize = 1; + const decrement = 0.2; + const minSize = 0.6; + const calculatedSize = Math.max( + baseSize - (userCount - 1) * decrement, + minSize + ); + return `${calculatedSize}rem`; + }; return ( @@ -43,7 +53,10 @@ const LentInfoCard = ({ title={cabinetInfo!.lentType} cabinetType={cabinetInfo!.lentType} /> - + {cabinetInfo!.userNameList} @@ -95,7 +108,7 @@ const LentInfoCard = ({ const CabinetInfoWrapper = styled.div` display: flex; - width: 80%; + width: 85%; margin: 10px 0 15px 0; align-items: center; `; From 1f8f4ed81454b82f4b26e556dc8ac437ca87b6c9 Mon Sep 17 00:00:00 2001 From: jusohn Date: Thu, 9 Nov 2023 11:04:32 +0900 Subject: [PATCH 31/48] =?UTF-8?q?[FE]=20FIX:=20getDefaultCainbetInfo()=20?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=ED=95=84=EC=9A=94=EC=97=86=EB=8A=94=20myI?= =?UTF-8?q?nfo=20=EC=9D=B8=EC=9E=90=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx | 4 ++-- frontend/src/pages/ProfilePage.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx b/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx index f1de85a82..43f18a37d 100644 --- a/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx +++ b/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx @@ -19,7 +19,7 @@ import { } from "@/api/axios/axios.custom"; import useMenu from "@/hooks/useMenu"; -export const getDefaultCabinetInfo = (myInfo: UserDto): CabinetInfo => ({ +export const getDefaultCabinetInfo = () => ({ building: "", floor: 0, cabinetId: 0, @@ -44,7 +44,7 @@ const TopNavButtonGroup = ({ isAdmin }: { isAdmin?: boolean }) => { const [myCabinetInfo, setMyCabinetInfo] = useRecoilState(myCabinetInfoState); const { pathname } = useLocation(); const navigator = useNavigate(); - const defaultCabinetInfo = getDefaultCabinetInfo(myInfo); + const defaultCabinetInfo = getDefaultCabinetInfo(); const resetCabinetInfo = () => { setMyCabinetInfo({ ...defaultCabinetInfo, diff --git a/frontend/src/pages/ProfilePage.tsx b/frontend/src/pages/ProfilePage.tsx index 91570b4ef..9ecd46118 100644 --- a/frontend/src/pages/ProfilePage.tsx +++ b/frontend/src/pages/ProfilePage.tsx @@ -14,7 +14,7 @@ const ProfilePage = () => { const [isLoading, setIsLoading] = useState(true); const [myCabinetInfo, setMyCabinetInfo] = useRecoilState(myCabinetInfoState); const myInfo = useRecoilValue(userState); - const defaultCabinetInfo = getDefaultCabinetInfo(myInfo); + const defaultCabinetInfo = getDefaultCabinetInfo(); useEffect(() => { setIsLoading(true); From 655d461a082df27d82cc8262714b335f9422ccbd Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Thu, 9 Nov 2023 13:22:34 +0900 Subject: [PATCH 32/48] =?UTF-8?q?[BE]=20FIX:=20netty=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/build.gradle b/backend/build.gradle index c05ae0242..1c7c43402 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -74,7 +74,6 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' - implementation 'io.netty:netty-resolver-dns-native-macos:4.1.68.Final:osx-aarch_64' implementation 'org.mapstruct:mapstruct:1.5.5.Final' runtimeOnly 'org.mariadb.jdbc:mariadb-java-client:2.7.9' From 7e90b570ce59a41108dac00c1708a394c3651cd9 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Thu, 9 Nov 2023 13:56:49 +0900 Subject: [PATCH 33/48] =?UTF-8?q?[BE]=20FIX:=20config=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index d756a78f9..a4578d6e3 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit d756a78f95ebb891a1abdb40e79561f3419a3c43 +Subproject commit a4578d6e39e7f8dc87d2276538f41f5fa6c2d04e From 71cd0f8d6cba17b123e023e7993cee297647d1c6 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Thu, 9 Nov 2023 14:03:15 +0900 Subject: [PATCH 34/48] =?UTF-8?q?[BE]=20FIX:=20config=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index a4578d6e3..75f3ff0ca 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit a4578d6e39e7f8dc87d2276538f41f5fa6c2d04e +Subproject commit 75f3ff0cad7e879fe0d0c1a5124d47ac07e3ce59 From ac3c0346897f89fb873135c2b04f358a7aed34dc Mon Sep 17 00:00:00 2001 From: jusohn Date: Thu, 9 Nov 2023 22:40:15 +0900 Subject: [PATCH 35/48] =?UTF-8?q?[FE]=20FIX:=20=EB=B0=98=EB=82=A9=20?= =?UTF-8?q?=EC=8B=9C=20myCabinetInfoState=20=EA=B0=80=20undefined=20?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=EB=90=98=EC=96=B4=20ProfilePage?= =?UTF-8?q?=20=EA=B0=80=20=EB=A0=8C=EB=8D=94=EB=A7=81=EB=90=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20=EC=9D=B4=EC=8A=88=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LentInfoCard/LentInfoCard.container.tsx | 21 +++++++++++++----- .../Card/LentInfoCard/LentInfoCard.tsx | 22 ++++++++----------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx b/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx index 64a4d8e38..34ba8bf37 100644 --- a/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx +++ b/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx @@ -1,6 +1,7 @@ import { useRecoilValue } from "recoil"; import { myCabinetInfoState } from "@/recoil/atoms"; import LentInfoCard from "@/components/Card/LentInfoCard/LentInfoCard"; +import { getDefaultCabinetInfo } from "@/components/TopNav/TopNavButtonGroup/TopNavButtonGroup"; import { CabinetInfo } from "@/types/dto/cabinet.dto"; import CabinetType from "@/types/enum/cabinet.type.enum"; @@ -22,10 +23,7 @@ const LentInfoCardContainer = () => { const getCabinetUserList = (selectedCabinetInfo: CabinetInfo): string => { const { lents } = selectedCabinetInfo; - // 동아리 사물함인 경우 cabinet_title 에 있는 동아리 이름 반환 - // if (lentType === "CLUB" && title) return TitleStyled; if (lents.length === 0) return ""; - // 그 외에는 유저리스트 반환 return new Array(lents.length) .fill(null) .map((_, idx) => lents[idx]) @@ -33,7 +31,9 @@ const LentInfoCardContainer = () => { .join(", "); }; - const cabinetLentInfo: MyCabinetInfo | null = myCabinetInfo + const defaultCabinetInfo: CabinetInfo = getDefaultCabinetInfo(); + + const cabinetLentInfo: MyCabinetInfo = myCabinetInfo ? { floor: myCabinetInfo.floor, section: myCabinetInfo.section, @@ -49,7 +49,18 @@ const LentInfoCardContainer = () => { isLented: myCabinetInfo.lents.length !== 0, previousUserName: myCabinetInfo.previousUserName, } - : null; + : { + floor: defaultCabinetInfo.floor, + section: defaultCabinetInfo.section, + cabinetId: defaultCabinetInfo.cabinetId, + visibleNum: defaultCabinetInfo.visibleNum, + lentType: defaultCabinetInfo.lentType, + userCount: 0, + userNameList: "", + expireDate: undefined, + isLented: false, + previousUserName: "", + }; return ; }; diff --git a/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx b/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx index 19c03f0a1..adb715e97 100644 --- a/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx +++ b/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx @@ -11,11 +11,7 @@ import { cabinetIconSrcMap } from "@/assets/data/maps"; import CabinetType from "@/types/enum/cabinet.type.enum"; import { formatDate, getRemainingTime } from "@/utils/dateUtils"; -const LentInfoCard = ({ - cabinetInfo, -}: { - cabinetInfo: MyCabinetInfo | null; -}) => { +const LentInfoCard = ({ cabinetInfo }: { cabinetInfo: MyCabinetInfo }) => { const calculateFontSize = (userCount: number): string => { const baseSize = 1; const decrement = 0.2; @@ -36,28 +32,28 @@ const LentInfoCard = ({ <> - {cabinetInfo!.visibleNum !== 0 ? cabinetInfo!.visibleNum : "-"} + {cabinetInfo.visibleNum !== 0 ? cabinetInfo.visibleNum : "-"} - {cabinetInfo!.floor !== 0 - ? cabinetInfo!.floor + "층 - " + cabinetInfo!.section + {cabinetInfo.floor !== 0 + ? cabinetInfo.floor + "층 - " + cabinetInfo.section : ""} {cabinetInfo?.isLented && ( - {cabinetInfo!.userNameList} + {cabinetInfo.userNameList} )} @@ -69,7 +65,7 @@ const LentInfoCard = ({ {cabinetInfo?.isLented ? `${ - cabinetInfo!.lentType === "PRIVATE" + cabinetInfo.lentType === "PRIVATE" ? parseInt(import.meta.env.VITE_PRIVATE_LENT_PERIOD) : parseInt(import.meta.env.VITE_SHARE_LENT_PERIOD) }일` From bbec5ae063a430a0c2c4e150aedcf249709cd6a4 Mon Sep 17 00:00:00 2001 From: jusohn Date: Thu, 9 Nov 2023 22:42:14 +0900 Subject: [PATCH 36/48] =?UTF-8?q?[FE]=20FIX:=20ProfilePage=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20myCabinetInfoState=20=EC=A0=9C=EA=B1=B0=20#1410?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/ProfilePage.tsx | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/frontend/src/pages/ProfilePage.tsx b/frontend/src/pages/ProfilePage.tsx index 9ecd46118..262e860b7 100644 --- a/frontend/src/pages/ProfilePage.tsx +++ b/frontend/src/pages/ProfilePage.tsx @@ -1,31 +1,20 @@ import { useEffect, useState } from "react"; -import { useRecoilState, useRecoilValue } from "recoil"; +import { useRecoilValue } from "recoil"; import styled from "styled-components"; -import { myCabinetInfoState, userState } from "@/recoil/atoms"; +import { userState } from "@/recoil/atoms"; import ExtensionCardContainer from "@/components/Card/ExtensionCard/ExtensionCard.container"; import LentInfoCardContainer from "@/components/Card/LentInfoCard/LentInfoCard.container"; import NotificationCardContainer from "@/components/Card/NotificationCard/NotificationCard.container"; import ProfileCardContainer from "@/components/Card/ProfileCard/ProfileCard.container"; import ThemeColorCardContainer from "@/components/Card/ThemeColorCard/ThemeColorCard.container"; import LoadingAnimation from "@/components/Common/LoadingAnimation"; -import { getDefaultCabinetInfo } from "@/components/TopNav/TopNavButtonGroup/TopNavButtonGroup"; const ProfilePage = () => { const [isLoading, setIsLoading] = useState(true); - const [myCabinetInfo, setMyCabinetInfo] = useRecoilState(myCabinetInfoState); const myInfo = useRecoilValue(userState); - const defaultCabinetInfo = getDefaultCabinetInfo(); useEffect(() => { setIsLoading(true); - if (!myCabinetInfo) { - setMyCabinetInfo({ - ...defaultCabinetInfo, - memo: "", - shareCode: 0, - previousUserName: "", - }); - } setIsLoading(false); }, []); From b6bd46477a441398256f30f809a45dfd5e1e2e85 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Tue, 7 Nov 2023 20:09:56 +0900 Subject: [PATCH 37/48] =?UTF-8?q?[BE]=20FIX:=20FCM=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?restore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cabinet/firebase/FCMInitializer.java | 43 ++++++++++ .../firebase/fcm/service/FCMService.java | 79 +++++++++++++++++++ .../utils/overdue/manager/OverdueManager.java | 6 +- backend/src/main/resources/dev/.gitkeep | 0 backend/src/main/resources/prod/.gitkeep | 0 config | 2 +- 6 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java create mode 100644 backend/src/main/java/org/ftclub/cabinet/firebase/fcm/service/FCMService.java create mode 100644 backend/src/main/resources/dev/.gitkeep create mode 100644 backend/src/main/resources/prod/.gitkeep diff --git a/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java b/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java new file mode 100644 index 000000000..6b568c885 --- /dev/null +++ b/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java @@ -0,0 +1,43 @@ +package org.ftclub.cabinet.firebase; + +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.nio.file.Paths; +import javax.annotation.PostConstruct; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class FCMInitializer { + + + @Value("${firebase.messaging.credentials.path}") + private String credentialsPath; + private final ResourceLoader resourceLoader; + + @PostConstruct + public void initialize() throws IOException { + Path currentPath = Paths.get("").toAbsolutePath().normalize(); + Resource resource = resourceLoader.getResource("file:" + currentPath + credentialsPath); + try (InputStream inputStream = resource.getInputStream()) { + FirebaseOptions options = FirebaseOptions.builder() + .setCredentials(GoogleCredentials.fromStream(inputStream)) + .build(); + if (FirebaseApp.getApps().isEmpty()) { + FirebaseApp.initializeApp(options); + log.info("Firebase application has been initialized"); + } + } + } +} + diff --git a/backend/src/main/java/org/ftclub/cabinet/firebase/fcm/service/FCMService.java b/backend/src/main/java/org/ftclub/cabinet/firebase/fcm/service/FCMService.java new file mode 100644 index 000000000..4f1e0f910 --- /dev/null +++ b/backend/src/main/java/org/ftclub/cabinet/firebase/fcm/service/FCMService.java @@ -0,0 +1,79 @@ +package org.ftclub.cabinet.firebase.fcm.service; + +import com.google.firebase.messaging.FirebaseMessaging; +import com.google.firebase.messaging.Message; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.ftclub.cabinet.config.DomainProperties; +import org.ftclub.cabinet.redis.service.RedisService; +import org.ftclub.cabinet.utils.overdue.manager.OverdueType; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +@Slf4j +public class FCMService { + private final RedisService redisService; + private final DomainProperties domainProperties; + private static final String ICON_FILE_PATH = "/src/assets/images/logo.svg"; + + + public void sendPushMessage(String name, OverdueType overdueType, Long daysLeftFromExpireDate) { + log.info("called sendPushMessage name = {}, overdueType = {}, daysLeftFromExpireDate = {}", name, overdueType, + daysLeftFromExpireDate); + + Optional token = redisService.findByKey(name, String.class); + if (token.isEmpty()) { + log.warn("\"{}\"에 해당하는 디바이스 토큰이 존재하지 않습니다.", name); + return; + } + + switch (overdueType) { + case NONE: + log.warn("overdueType이 NONE입니다. name = {}, overdueType = {}, daysLeftFromExpireDate = {}", name, overdueType, + daysLeftFromExpireDate); + break; + case SOON_OVERDUE: + sendSoonOverdueMessage(token.get(), name, daysLeftFromExpireDate); + break; + case OVERDUE: + sendOverdueMessage(token.get(), name, daysLeftFromExpireDate); + break; + } + } + + private void sendOverdueMessage(String token, String name, Long daysLeftFromExpireDate) { + log.info( + "called sendOverdueMessage token = {}, name = {}, daysLeftFromExpireDate = {}", + token, name, daysLeftFromExpireDate); + Message message = Message.builder() + .putData("title", " 연체 알림") + .putData("body", name + "님, 대여한 사물함이 " + Math.abs(daysLeftFromExpireDate) + "일 연체되었습니다.") + .putData("icon", domainProperties.getFeHost() + ICON_FILE_PATH) + .putData("click_action", domainProperties.getFeHost()) + .setToken(token) + .build(); + + FirebaseMessaging.getInstance().sendAsync(message); + } + + private void sendSoonOverdueMessage(String token, String name, Long daysLeftFromExpireDate) { + log.info( + "called sendSoonOverdueMessage token = {}, name = {}, daysLeftFromExpireDate = {}", + token, name, daysLeftFromExpireDate); + if (token.isEmpty()) { + log.warn("\"{}\"에 해당하는 디바이스 토큰이 존재하지 않습니다.", name); + return; + } + Message message = Message.builder() + .putData("title", " 연체 예정 알림") + .putData("body", "대여한 사물함이 " + daysLeftFromExpireDate + "일 후 연체됩니다.") + .putData("icon", domainProperties.getFeHost() + ICON_FILE_PATH) + .putData("click_action", domainProperties.getFeHost()) + .setToken(token) + .build(); + + FirebaseMessaging.getInstance().sendAsync(message); + } +} \ No newline at end of file diff --git a/backend/src/main/java/org/ftclub/cabinet/utils/overdue/manager/OverdueManager.java b/backend/src/main/java/org/ftclub/cabinet/utils/overdue/manager/OverdueManager.java index 509619659..3e76b1043 100644 --- a/backend/src/main/java/org/ftclub/cabinet/utils/overdue/manager/OverdueManager.java +++ b/backend/src/main/java/org/ftclub/cabinet/utils/overdue/manager/OverdueManager.java @@ -6,7 +6,7 @@ import org.ftclub.cabinet.cabinet.service.CabinetService; import org.ftclub.cabinet.config.MailOverdueProperties; import org.ftclub.cabinet.dto.ActiveLentHistoryDto; -//import org.ftclub.cabinet.firebase.fcm.service.FCMService; +import org.ftclub.cabinet.firebase.fcm.service.FCMService; import org.ftclub.cabinet.utils.mail.EmailSender; import org.springframework.stereotype.Component; @@ -22,7 +22,7 @@ public class OverdueManager { private final EmailSender emailSender; -// private final FCMService fcmService; + private final FCMService fcmService; private final CabinetService cabinetService; private final MailOverdueProperties mailOverdueProperties; @@ -69,7 +69,7 @@ public void handleOverdue(ActiveLentHistoryDto activeLent) { try { emailSender.sendMail(activeLent.getName(), activeLent.getEmail(), subject, template); -// fcmService.sendPushMessage(activeLent.getName(), overdueType, activeLent.getDaysLeftFromExpireDate()); + fcmService.sendPushMessage(activeLent.getName(), overdueType, activeLent.getDaysLeftFromExpireDate()); } catch (Exception e) { e.printStackTrace(); } diff --git a/backend/src/main/resources/dev/.gitkeep b/backend/src/main/resources/dev/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/backend/src/main/resources/prod/.gitkeep b/backend/src/main/resources/prod/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/config b/config index 75f3ff0ca..fa8e91acf 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 75f3ff0cad7e879fe0d0c1a5124d47ac07e3ce59 +Subproject commit fa8e91acfeadbc6e9133531b9aa7992a605ee7ea From e47163ffdeb6579b043e0f5a8d5488e56907cab7 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Sun, 12 Nov 2023 16:21:50 +0900 Subject: [PATCH 38/48] =?UTF-8?q?[BE]=20FIX:=20backend=20config=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index fa8e91acf..25dc8c4d7 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit fa8e91acfeadbc6e9133531b9aa7992a605ee7ea +Subproject commit 25dc8c4d7225aba7727bfb45912d5b6455c9784c From e9b69329c3f7dfca8cce8d19c24d541de9825e08 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Mon, 13 Nov 2023 13:04:12 +0900 Subject: [PATCH 39/48] =?UTF-8?q?[BE]=20FIX:=20backend=20config=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index 25dc8c4d7..b8662a35c 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 25dc8c4d7225aba7727bfb45912d5b6455c9784c +Subproject commit b8662a35ca28c652c60a255d214d0ceb0ab62132 From 687c3cfe6cca61d23b464f4602972369005a7438 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Mon, 13 Nov 2023 13:10:38 +0900 Subject: [PATCH 40/48] =?UTF-8?q?[BE]=20FIX:=20backend=20config=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index b8662a35c..a4abdb872 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit b8662a35ca28c652c60a255d214d0ceb0ab62132 +Subproject commit a4abdb8723b146a9718eb73c0a29d8b2ca55e720 From dad0f2c1a602f76584096336fab66f70a3774ab1 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Mon, 13 Nov 2023 13:27:13 +0900 Subject: [PATCH 41/48] =?UTF-8?q?[BE]=20FIX:=20=EB=94=94=EB=B2=84=EA=B9=85?= =?UTF-8?q?=EC=9A=A9=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/ftclub/cabinet/firebase/FCMInitializer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java b/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java index 6b568c885..0949e7b53 100644 --- a/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java +++ b/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java @@ -20,7 +20,6 @@ @RequiredArgsConstructor public class FCMInitializer { - @Value("${firebase.messaging.credentials.path}") private String credentialsPath; private final ResourceLoader resourceLoader; @@ -28,6 +27,8 @@ public class FCMInitializer { @PostConstruct public void initialize() throws IOException { Path currentPath = Paths.get("").toAbsolutePath().normalize(); + log.info(credentialsPath); + log.info(currentPath + credentialsPath); Resource resource = resourceLoader.getResource("file:" + currentPath + credentialsPath); try (InputStream inputStream = resource.getInputStream()) { FirebaseOptions options = FirebaseOptions.builder() @@ -40,4 +41,3 @@ public void initialize() throws IOException { } } } - From e307427ce8b98da5029eae69b334dd051e9135de Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Mon, 13 Nov 2023 13:30:44 +0900 Subject: [PATCH 42/48] =?UTF-8?q?[BE]=20FIX:=20backend=20config=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index a4abdb872..d13dee939 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit a4abdb8723b146a9718eb73c0a29d8b2ca55e720 +Subproject commit d13dee939d3110fda3ea907fc6203a0bd7c7b5e0 From a41a3ee18d422752f227f29b1454b39278f53f06 Mon Sep 17 00:00:00 2001 From: sichoi42 <42.4.sichoi@gmail.com> Date: Mon, 13 Nov 2023 13:35:26 +0900 Subject: [PATCH 43/48] =?UTF-8?q?[BE]=20FIX:=20=EB=94=94=EB=B2=84=EA=B9=85?= =?UTF-8?q?=EC=9A=A9=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/org/ftclub/cabinet/firebase/FCMInitializer.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java b/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java index 0949e7b53..2f7dcdbe6 100644 --- a/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java +++ b/backend/src/main/java/org/ftclub/cabinet/firebase/FCMInitializer.java @@ -27,8 +27,6 @@ public class FCMInitializer { @PostConstruct public void initialize() throws IOException { Path currentPath = Paths.get("").toAbsolutePath().normalize(); - log.info(credentialsPath); - log.info(currentPath + credentialsPath); Resource resource = resourceLoader.getResource("file:" + currentPath + credentialsPath); try (InputStream inputStream = resource.getInputStream()) { FirebaseOptions options = FirebaseOptions.builder() From 0107a09b2a4eafd15d53468b9c3559974fc7df27 Mon Sep 17 00:00:00 2001 From: jusohn Date: Fri, 17 Nov 2023 15:38:02 +0900 Subject: [PATCH 44/48] =?UTF-8?q?[FE]=20FIX:=20CardContentStyled=20padding?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=EB=A1=9C=20=EC=A0=95=EB=B3=B4=EC=99=80=20?= =?UTF-8?q?=EC=B9=B4=EB=93=9C=EB=B0=95=EC=8A=A4=20=EA=B0=84=EA=B2=A9=20?= =?UTF-8?q?=EC=A1=B0=EC=A0=95=20#1410?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/Card/CardStyles.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/Card/CardStyles.ts b/frontend/src/components/Card/CardStyles.ts index 49893b216..7e061cb02 100644 --- a/frontend/src/components/Card/CardStyles.ts +++ b/frontend/src/components/Card/CardStyles.ts @@ -15,6 +15,7 @@ export const CardContentStyled = styled.div` justify-content: space-between; align-items: center; margin: 5px 0 5px 0; + padding: 0 10px 0 10px; `; export const ContentInfoStyled = styled.div` From f0d1e843c5dc5e88e158c42a0005131bb018f222 Mon Sep 17 00:00:00 2001 From: jusohn Date: Fri, 17 Nov 2023 15:39:56 +0900 Subject: [PATCH 45/48] =?UTF-8?q?[FE]=20FIX:=20=EB=8C=80=EC=97=AC=EC=A4=91?= =?UTF-8?q?,=20=EB=8C=80=EC=97=AC=EC=A4=91=20=EC=95=84=EB=8B=98,=20?= =?UTF-8?q?=ED=8C=A8=EB=84=90=ED=8B=B0=20=EC=83=81=ED=83=9C=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=9D=BC=20LentInfoCard=20=EB=82=B4=20=EC=82=AC?= =?UTF-8?q?=EB=AC=BC=ED=95=A8=20=EC=83=89=EA=B9=94=20=EC=A1=B0=EC=A0=95=20?= =?UTF-8?q?#1410?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LentInfoCard/LentInfoCard.container.tsx | 9 +++- .../Card/LentInfoCard/LentInfoCard.tsx | 50 +++++++++++++++---- .../TopNavButtonGroup/TopNavButtonGroup.tsx | 2 +- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx b/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx index 34ba8bf37..5d7149773 100644 --- a/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx +++ b/frontend/src/components/Card/LentInfoCard/LentInfoCard.container.tsx @@ -1,5 +1,5 @@ import { useRecoilValue } from "recoil"; -import { myCabinetInfoState } from "@/recoil/atoms"; +import { myCabinetInfoState, targetUserInfoState } from "@/recoil/atoms"; import LentInfoCard from "@/components/Card/LentInfoCard/LentInfoCard"; import { getDefaultCabinetInfo } from "@/components/TopNav/TopNavButtonGroup/TopNavButtonGroup"; import { CabinetInfo } from "@/types/dto/cabinet.dto"; @@ -16,10 +16,13 @@ export interface MyCabinetInfo { expireDate?: Date; isLented: boolean; previousUserName: string; + status: string; } const LentInfoCardContainer = () => { const myCabinetInfo = useRecoilValue(myCabinetInfoState); + const targetUserInfo = useRecoilValue(targetUserInfoState); + const bannedAt = targetUserInfo ? !!targetUserInfo.bannedAt : false; const getCabinetUserList = (selectedCabinetInfo: CabinetInfo): string => { const { lents } = selectedCabinetInfo; @@ -48,6 +51,7 @@ const LentInfoCardContainer = () => { : undefined, isLented: myCabinetInfo.lents.length !== 0, previousUserName: myCabinetInfo.previousUserName, + status: myCabinetInfo.status, } : { floor: defaultCabinetInfo.floor, @@ -60,9 +64,10 @@ const LentInfoCardContainer = () => { expireDate: undefined, isLented: false, previousUserName: "", + status: defaultCabinetInfo.status, }; - return ; + return ; }; export default LentInfoCardContainer; diff --git a/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx b/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx index adb715e97..f17ab4602 100644 --- a/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx +++ b/frontend/src/components/Card/LentInfoCard/LentInfoCard.tsx @@ -7,11 +7,22 @@ import { ContentInfoStyled, } from "@/components/Card/CardStyles"; import { MyCabinetInfo } from "@/components/Card/LentInfoCard/LentInfoCard.container"; -import { cabinetIconSrcMap } from "@/assets/data/maps"; +import { + cabinetIconSrcMap, + cabinetLabelColorMap, + cabinetStatusColorMap, +} from "@/assets/data/maps"; +import CabinetStatus from "@/types/enum/cabinet.status.enum"; import CabinetType from "@/types/enum/cabinet.type.enum"; import { formatDate, getRemainingTime } from "@/utils/dateUtils"; -const LentInfoCard = ({ cabinetInfo }: { cabinetInfo: MyCabinetInfo }) => { +const LentInfoCard = ({ + cabinetInfo, + bannedAt, +}: { + cabinetInfo: MyCabinetInfo; + bannedAt: boolean; +}) => { const calculateFontSize = (userCount: number): string => { const baseSize = 1; const decrement = 0.2; @@ -31,8 +42,15 @@ const LentInfoCard = ({ cabinetInfo }: { cabinetInfo: MyCabinetInfo }) => { > <> - - {cabinetInfo.visibleNum !== 0 ? cabinetInfo.visibleNum : "-"} + + {cabinetInfo.visibleNum !== 0 + ? cabinetInfo.visibleNum + : !!bannedAt + ? "!" + : "-"} ` width: 60px; height: 60px; line-height: 60px; border-radius: 10px; margin-right: 20px; - background-color: var(--mine); + background-color: ${(props) => + props.banned + ? "var(--expired)" + : props.status === "FULL" + ? "var(--mine)" + : cabinetStatusColorMap[props.status]}; + color: ${(props) => + props.banned + ? "var(--white)" + : props.status && props.status !== "PENDING" + ? cabinetLabelColorMap[props.status] + : "var(--black)"}; font-size: 32px; text-align: center; `; @@ -125,7 +155,7 @@ const CabinetRectangleStyled = styled.div<{ const CabinetInfoDetailStyled = styled.div` display: flex; flex-direction: column; - align-items: center; + align-items: flex-start; `; const CabinetInfoTextStyled = styled.div<{ @@ -147,10 +177,8 @@ const CabinetUserListWrapper = styled.div` `; const CabinetIconStyled = styled.div<{ cabinetType: CabinetType }>` - width: 24px; - height: 24px; - min-width: 24px; - min-height: 24px; + width: 18px; + height: 18px; margin-right: 10px; background-image: url(${(props) => cabinetIconSrcMap[props.cabinetType]}); background-size: contain; diff --git a/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx b/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx index 43f18a37d..0eb26778e 100644 --- a/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx +++ b/frontend/src/components/TopNav/TopNavButtonGroup/TopNavButtonGroup.tsx @@ -27,7 +27,7 @@ export const getDefaultCabinetInfo = () => ({ lentType: CabinetType.PRIVATE, title: null, maxUser: 0, - status: CabinetStatus.AVAILABLE, + status: CabinetStatus.PENDING, section: "", lents: [] as LentDto[], statusNote: "", From b1a7424a9aa6676d2591d1509bd03270a51a6eb5 Mon Sep 17 00:00:00 2001 From: jusohn Date: Fri, 17 Nov 2023 15:40:44 +0900 Subject: [PATCH 46/48] =?UTF-8?q?[FE]=20FIX:=20myInfo=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=20=EC=8B=9C=20=EB=A6=AC=EB=A0=8C=EB=8D=94=EB=A7=81=20=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20#1410?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/ProfilePage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/pages/ProfilePage.tsx b/frontend/src/pages/ProfilePage.tsx index 262e860b7..b621bc381 100644 --- a/frontend/src/pages/ProfilePage.tsx +++ b/frontend/src/pages/ProfilePage.tsx @@ -16,7 +16,7 @@ const ProfilePage = () => { useEffect(() => { setIsLoading(true); setIsLoading(false); - }, []); + }, [myInfo]); return ( <> From f2042402ce37c19c6dba11c270716ed8402a747e Mon Sep 17 00:00:00 2001 From: jusohn Date: Fri, 17 Nov 2023 15:53:06 +0900 Subject: [PATCH 47/48] =?UTF-8?q?[FE]=20FIX:=20grid=20template=20area=20?= =?UTF-8?q?=EC=BD=94=EB=A7=A8=ED=8A=B8=20=EC=B6=94=EA=B0=80=20#1410?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/ProfilePage.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/src/pages/ProfilePage.tsx b/frontend/src/pages/ProfilePage.tsx index b621bc381..896d950f7 100644 --- a/frontend/src/pages/ProfilePage.tsx +++ b/frontend/src/pages/ProfilePage.tsx @@ -44,10 +44,9 @@ const CardGridWrapper = styled.div` grid-gap: 20px; grid-template-columns: 350px 350px; grid-template-rows: 163px 183px 215px; - grid-template-areas: - "profile lentInfo" - "extension lentInfo" - "theme notification"; + grid-template-areas: "profile lentInfo" // h: 163px h: 366px + "extension lentInfo" // h: 183px + "theme notification"; // h: 215px h: 215px @media (max-width: 768px) { grid-template-columns: 350px; From 7540abe32388406fcac660d26e6719b0ae0aa84a Mon Sep 17 00:00:00 2001 From: jusohn Date: Fri, 17 Nov 2023 15:54:12 +0900 Subject: [PATCH 48/48] [FE] FIX: updated config #1410 --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index 765d1d2f9..d13dee939 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 765d1d2f9fff6a622b4000e3518373890175e28d +Subproject commit d13dee939d3110fda3ea907fc6203a0bd7c7b5e0