From 8159d5ea05bea9f1b13a30e881fc56f36ec41775 Mon Sep 17 00:00:00 2001 From: woojoung Date: Wed, 11 Dec 2024 13:39:57 +0900 Subject: [PATCH 001/146] =?UTF-8?q?Test=20:=20serviceAvailabillity.test.ts?= =?UTF-8?q?x=20=ED=8C=8C=EC=9D=BC=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BookingSearchContainer.test.tsx | 14 +- .../FilterPriceSlide.test.tsx | 23 ++- .../FilterPriceSlide/PriceRangeInput.tsx | 137 ------------------ .../Navigator/ThemeNavigator.test.tsx | 15 +- .../ServiceAvailability.test.tsx | 75 ++++++++++ 5 files changed, 117 insertions(+), 147 deletions(-) delete mode 100644 src/components/FilterPriceSlide/PriceRangeInput.tsx create mode 100644 src/components/ServiceAvailability/ServiceAvailability.test.tsx diff --git a/src/components/BookingSearchContainer/BookingSearchContainer.test.tsx b/src/components/BookingSearchContainer/BookingSearchContainer.test.tsx index 8a94af6..ca0142e 100644 --- a/src/components/BookingSearchContainer/BookingSearchContainer.test.tsx +++ b/src/components/BookingSearchContainer/BookingSearchContainer.test.tsx @@ -1,10 +1,15 @@ import { render, screen } from '@testing-library/react'; import BookingSearchContainer from './BookingSearchContainer'; import { describe, expect, test } from 'vitest'; +import { MemoryRouter } from 'react-router-dom'; describe('BookingSearchContainer', () => { test('컴포넌트가 올바르게 렌더링된다', async () => { - render(); + render( + + + , + ); const locationText = await screen.findByText('전체지역'); expect(locationText).toBeInTheDocument(); expect(screen.getByText('예약 날짜와 시간을 선택해주세요.')).toBeInTheDocument(); @@ -12,7 +17,12 @@ describe('BookingSearchContainer', () => { // 초기 상태 테스트 test('초기 상태의 버튼들이 올바른 텍스트를 가진다', () => { - render(); + render( + + {' '} + + , + ); const locationButton = screen.getByText('전체지역'); const dateButton = screen.getByText('예약 날짜와 시간을 선택해주세요.'); expect(locationButton).toBeInTheDocument(); diff --git a/src/components/FilterPriceSlide/FilterPriceSlide.test.tsx b/src/components/FilterPriceSlide/FilterPriceSlide.test.tsx index cd33f80..0e735d3 100644 --- a/src/components/FilterPriceSlide/FilterPriceSlide.test.tsx +++ b/src/components/FilterPriceSlide/FilterPriceSlide.test.tsx @@ -1,25 +1,38 @@ import { render, screen, fireEvent } from '@testing-library/react'; import FilterPriceSlideComponent from './FilterPriceSlide'; import { describe, expect, test } from 'vitest'; +import { MemoryRouter } from 'react-router-dom'; describe('FilterPriceSlideComponent', () => { test('초기값이 올바르게 렌더링되는지 확인', () => { - render(); + render( + + + , + ); expect(screen.getByText(/원~/)).toHaveTextContent('10000원~'); expect(screen.getByText(/원 이상/)).toHaveTextContent('200000 원 이상'); }); test('최소값 슬라이더 변경 시 값이 업데이트되는지 확인', () => { - render(); - const minSlider = screen.getByLabelText('slider1'); + render( + + + , + ); + const minSlider = screen.getByLabelText('slider1'); // 슬라이더의 레이블을 수정하여 올바른 슬라이더를 선택 fireEvent.change(minSlider, { target: { value: '15000' } }); expect(screen.getByText(/원~/)).toHaveTextContent('15000원~'); }); test('최대값 슬라이더 변경 시 값이 업데이트되는지 확인', () => { - render(); - const maxSlider = screen.getByLabelText('slider2'); + render( + + + , + ); + const maxSlider = screen.getByLabelText('slider2'); // 슬라이더의 레이블을 수정하여 올바른 슬라이더를 선택 fireEvent.change(maxSlider, { target: { value: '180000' } }); expect(screen.getByText(/원 이상/)).toHaveTextContent('180000 원 이상'); diff --git a/src/components/FilterPriceSlide/PriceRangeInput.tsx b/src/components/FilterPriceSlide/PriceRangeInput.tsx deleted file mode 100644 index 64324cb..0000000 --- a/src/components/FilterPriceSlide/PriceRangeInput.tsx +++ /dev/null @@ -1,137 +0,0 @@ -// /** @jsxImportSource @emotion/react */ -// import styled from '@emotion/styled'; -// import variables from '@styles/Variables'; -// import { useState } from 'react'; - -// //NOTE - 호환 이슈가 발생 할 수 있어 남겨놓음 사용 금지 -// const PriceRangeInput = () => { -// const [leftValue, setLeftValue] = useState(1); -// const [rightValue, setRightValue] = useState(20); - -// const handleLeftChange = (e: React.ChangeEvent) => { -// const newLeftValue = Number(e.target.value); -// if (newLeftValue <= rightValue) { -// setLeftValue(newLeftValue); -// } -// }; - -// const handleRightChange = (e: React.ChangeEvent) => { -// const newRightValue = Number(e.target.value); -// if (newRightValue >= leftValue) { -// setRightValue(newRightValue); -// } -// }; - -// return ( -// -// -// {leftValue}만원~ -// {rightValue} 만원 이상 -// - -// -// -// -// -// - -// -// 1만원 -// 10만원 -// 20만원 -// -// -// ); -// }; - -// export default PriceRangeInput; - -// const RangeWrapper = styled.div` -// width: 100%; -// max-width: 500px; -// margin: 0 auto; -// position: relative; -// `; - -// const RangeTrack = styled.div` -// width: 100%; -// height: 0.8rem; -// background-color: ${variables.colors.gray200}; -// border-radius: ${variables.borderRadius}; -// position: relative; -// `; - -// const RangeHighlight = styled.div<{ -// left: number; -// right: number; -// }>` -// position: absolute; -// height: 0.8rem; -// background-color: ${variables.colors.primary}; -// border-radius: 4px; -// left: ${(props) => props.left}%; -// right: ${(props) => props.right}%; -// `; - -// const StyledRangeInputLeft = styled.input` -// position: absolute; -// top: -10px; -// width: 100%; -// appearance: none; -// background: transparent; -// z-index: 10; - -// &::-webkit-slider-thumb { -// appearance: none; -// width: 2.8rem; -// height: 2.8rem; -// background: #fff8e1; -// border: 0.2rem solid ${variables.colors.primary}; -// border-radius: 50%; -// cursor: pointer; -// } -// `; - -// const StyledRangeInput = styled.input` -// position: absolute; -// top: -10px; -// width: 100%; -// appearance: none; -// background: transparent; -// z-index: 10; - -// &::-webkit-slider-thumb { -// appearance: none; -// width: 2.8rem; -// height: 2.8rem; -// background: #fff8e1; -// border: 0.2rem solid ${variables.colors.primary}; -// border-radius: 50%; -// cursor: pointer; -// } -// `; - -// const ValueDisplay = styled.div` -// width: 100%; -// display: flex; -// justify-content: center; -// align-items: center; - -// margin-bottom: 3rem; -// `; -// const ValueDisplaySpanStyle = styled.span` -// font-size: ${variables.size.large}; -// font-weight: 600; -// `; - -// const RangeDisplay = styled.div` -// width: 100%; -// display: flex; -// justify-content: space-between; -// align-items: center; -// margin-top: 1.6rem; -// `; -// const RangeDisplaySpanStyle = styled.span` -// font-size: ${variables.size.medium}; -// color: ${variables.colors.gray500}; -// `; diff --git a/src/components/Navigator/ThemeNavigator.test.tsx b/src/components/Navigator/ThemeNavigator.test.tsx index 78c1f06..6ef6238 100644 --- a/src/components/Navigator/ThemeNavigator.test.tsx +++ b/src/components/Navigator/ThemeNavigator.test.tsx @@ -2,10 +2,15 @@ import { describe, expect, test } from 'vitest'; import ThemeNavigator from './ThemeNavigator'; import { render, screen, fireEvent } from '@testing-library/react'; import variables from '@styles/Variables'; +import { MemoryRouter } from 'react-router-dom'; describe('ThemeNavigator', () => { test('renders all themes', () => { - render(); + render( + + + , + ); const themes = ['전체', '몽환', '내추럴', '러블리', '시크', '청순', '상큼']; themes.forEach((theme) => { @@ -13,8 +18,12 @@ describe('ThemeNavigator', () => { }); }); - test('changes active theme on button click', () => { - render(); + test('버튼 클릭 시 활성화', () => { + render( + + + , + ); const naturalButton = screen.getByText('내추럴'); // 클릭 전 '내추럴' 버튼이 비활성화 상태인지 확인 diff --git a/src/components/ServiceAvailability/ServiceAvailability.test.tsx b/src/components/ServiceAvailability/ServiceAvailability.test.tsx new file mode 100644 index 0000000..7d71c5a --- /dev/null +++ b/src/components/ServiceAvailability/ServiceAvailability.test.tsx @@ -0,0 +1,75 @@ +import { render, screen, fireEvent } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import ServiceAvailability from './ServiceAvailability'; +import useBottomSheetState from '@store/useBottomSheetStateStore'; +import useResetState from '@hooks/useResetState'; +import { vi } from 'vitest'; + +vi.mock('@store/useBottomSheetStateStore', () => ({ + __esModule: true, + default: vi.fn(), +})); + +vi.mock('@hooks/useResetState', () => ({ + __esModule: true, + default: vi.fn(), +})); + +describe('ServiceAvailability', () => { + const closeBottomSheetMock = vi.fn(); + const resetStateMock = vi.fn(); + + beforeEach(() => { + (useBottomSheetState as unknown as jest.Mock).mockReturnValue({ closeBottomSheet: closeBottomSheetMock }); + (useResetState as unknown as jest.Mock).mockReturnValue({ resetState: resetStateMock }); + }); + + test('renders ServiceAvailability component', () => { + render( + + + , + ); + + expect(screen.getByText('1:1 보정')).toBeInTheDocument(); + expect(screen.getByText('적용하기')).toBeInTheDocument(); + expect(screen.getByText('초기화')).toBeInTheDocument(); + }); + + test('handles button click and updates selected buttons', () => { + render( + + + , + ); + + const button = screen.getByText('1:1 보정'); + fireEvent.click(button); + }); + + test('calls closeBottomSheet on apply click', () => { + render( + + + , + ); + + const applyButton = screen.getByText('적용하기'); + fireEvent.click(applyButton); + + expect(closeBottomSheetMock).toHaveBeenCalled(); // 바텀 시트 닫기 함수 호출 확인 + }); + + test('calls resetState on reset click', () => { + render( + + + , + ); + + const resetButton = screen.getByText('초기화'); + fireEvent.click(resetButton); + + expect(resetStateMock).toHaveBeenCalled(); // 상태 초기화 함수 호출 확인 + }); +}); From 198ba782fab17e79be63c8096137868a91b5bf3b Mon Sep 17 00:00:00 2001 From: woojoung Date: Wed, 11 Dec 2024 14:07:33 +0900 Subject: [PATCH 002/146] =?UTF-8?q?Conf=20:=20=ED=82=A4=EC=9B=8C=EB=93=9C?= =?UTF-8?q?=20=EC=88=98=EC=A1=B0=E3=85=93=E3=85=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ServiceAvailability/ServiceAvailability.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ServiceAvailability/ServiceAvailability.tsx b/src/components/ServiceAvailability/ServiceAvailability.tsx index dfcfb7d..583991d 100644 --- a/src/components/ServiceAvailability/ServiceAvailability.tsx +++ b/src/components/ServiceAvailability/ServiceAvailability.tsx @@ -31,7 +31,7 @@ const ServiceAvailability = () => { // 인덱스에 해당하는 버튼의 제목을 반환하는 함수 const getButtonTitle = (index: number) => { - const titles = ['보정', '원본', '주차', '헤메코', '정장대여', '탈의실', '파우더룸']; + const titles = ['보정', '원본', '주차', '헤메코', '정장', '탈의실', '파우더룸']; return titles[index]; // 제목 배열에서 인덱스에 해당하는 제목 반환 }; From c459e484f640f4a28be0bda7cdd36f03db29d00b Mon Sep 17 00:00:00 2001 From: s0zzang Date: Wed, 11 Dec 2024 14:50:19 +0900 Subject: [PATCH 003/146] =?UTF-8?q?Package:=20@testing-library/user-event?= =?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 --- package.json | 1 + pnpm-lock.yaml | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/package.json b/package.json index c23ccf7..83e15ab 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ }, "devDependencies": { "@eslint/js": "^9.16.0", + "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.14", "@types/react": "^18.3.13", "@types/react-dom": "^18.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 92ccf2f..a0c1650 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,6 +48,9 @@ importers: '@eslint/js': specifier: ^9.16.0 version: 9.16.0 + '@testing-library/user-event': + specifier: ^14.5.2 + version: 14.5.2(@testing-library/dom@10.4.0) '@types/jest': specifier: ^29.5.14 version: 29.5.14 @@ -745,6 +748,12 @@ packages: '@types/react-dom': optional: true + '@testing-library/user-event@14.5.2': + resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -2560,6 +2569,10 @@ snapshots: '@types/react': 18.3.13 '@types/react-dom': 18.3.1 + '@testing-library/user-event@14.5.2(@testing-library/dom@10.4.0)': + dependencies: + '@testing-library/dom': 10.4.0 + '@types/aria-query@5.0.4': {} '@types/babel__core@7.20.5': From 48bf7afcf8b607a58382b114466d4e2c97bff185 Mon Sep 17 00:00:00 2001 From: s0zzang Date: Wed, 11 Dec 2024 14:50:31 +0900 Subject: [PATCH 004/146] =?UTF-8?q?Test:=20=EC=BA=98=EB=A6=B0=EB=8D=94=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Calendar/Calendar.test.tsx | 51 +++++++++++++++++++++++ src/components/Calendar/Calendar.tsx | 1 + 2 files changed, 52 insertions(+) create mode 100644 src/components/Calendar/Calendar.test.tsx diff --git a/src/components/Calendar/Calendar.test.tsx b/src/components/Calendar/Calendar.test.tsx new file mode 100644 index 0000000..e493706 --- /dev/null +++ b/src/components/Calendar/Calendar.test.tsx @@ -0,0 +1,51 @@ +import { render, screen } from '@testing-library/react'; +import { beforeEach, describe, expect, test } from 'vitest'; +import Calendar from './Calendar'; +// import { convertToDateFormat, useSelectDateStore } from '@store/useSelectDate'; +// import userEvent from '@testing-library/user-event'; + +// 지역과 날짜(시간)가 선택되면 메인으로 돌아가고 그것에 맞춰 리스트가 필터링되는 행동이 필요해 +// 사용자가 달력을 확인하고 달력의 기능을 사용할 수 있는지 확인할 수 있는 행동이 필요해 +// 달력을 통해 특정 날짜를 선택할 수 있는지 확인할 수 있는 행동이 필요해 + +describe('달력 컴포넌트', () => { + const today = new Date(); + const todayMonth = today.getMonth(); + + beforeEach(() => { + render(); + }); + + test('달력이 오류 없이 렌더링 된다', () => { + const monthText = screen.getByText(todayMonth); + expect(monthText).toBeInTheDocument(); + }); + + test('1일을 선택하면 1일이 전역 상태 값에 담긴다', () => { + const buttons = screen.getAllByRole('button'); + buttons.forEach((button) => { + console.log(button.getAttribute('name')); // name 속성 출력 + console.log(button.textContent); // 내부 텍스트 출력 + }); + + // 전역 date 상태 초기화 + // useSelectDateStore.setState({ date: '' }); + + const selectedDate = screen.getByRole('button', { name: /1일/i }); + expect(selectedDate).toBeInTheDocument; + + // const { date } = useSelectDateStore.getState(); + + // 버튼 클릭 + // fireEvent.click(selectedDate[0]); + // userEvent.click(selectedDate); + + // 상태 검증 + // expect(date).toBe(''); // 값이 변경되었는지 확인 + + // const { setDate } = useSelectDateStore.getState(); + // setDate('2024-12-11'); + // 화면에 상태가 반영되었는지 확인 + // expect(screen.getByText(/Selected Date: 2024-12-11/i)).toBeInTheDocument(); + }); +}); diff --git a/src/components/Calendar/Calendar.tsx b/src/components/Calendar/Calendar.tsx index 74d1856..7ba8494 100644 --- a/src/components/Calendar/Calendar.tsx +++ b/src/components/Calendar/Calendar.tsx @@ -128,6 +128,7 @@ const Calendar = () => { > ))} From a1aafaab89f57380051aad83a49db62eb28143cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=ED=9D=AC=EC=84=A0?= Date: Wed, 11 Dec 2024 16:43:21 +0900 Subject: [PATCH 005/146] =?UTF-8?q?Feat:=20=EA=B2=80=EC=83=89=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=A7=80=EC=97=AD=20=EC=84=A0=ED=83=9D=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Home/components/LocalDateSelectionModal.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pages/Home/components/LocalDateSelectionModal.tsx b/src/pages/Home/components/LocalDateSelectionModal.tsx index 5b8245f..2f6648d 100644 --- a/src/pages/Home/components/LocalDateSelectionModal.tsx +++ b/src/pages/Home/components/LocalDateSelectionModal.tsx @@ -9,9 +9,11 @@ import { useState } from 'react'; import DateBottomSheet from './DateBottomSheet'; import { useNavigate } from 'react-router-dom'; import useBottomSheetState from '@store/useBottomSheetStateStore'; +import LocationSelectionModal from './LocationSelectionModal'; const LocalDateSelectionModal = ({ modalId }: { modalId: number }) => { const [selectedDate, setSelectedDate] = useState({ date: '', time: '' }); + const [selectedLocation, setSelectedLocation] = useState(null); const { openBottomSheet } = useBottomSheetState(); const navigate = useNavigate(); @@ -29,6 +31,7 @@ const LocalDateSelectionModal = ({ modalId }: { modalId: number }) => { const setParams = () => { const currentParams = new URLSearchParams(window.location.search); currentParams.set('requestedDateTime', `${selectedDate.date}${selectedDate.time}`); + currentParams.set('requestedLocation', `${selectedDate.date}`); navigate(`?${currentParams.toString()}`); }; @@ -44,7 +47,7 @@ const LocalDateSelectionModal = ({ modalId }: { modalId: number }) => { return `${selectedDateForUi} ${selectedTimeForUi === '00시' ? '' : selectedTimeForUi}`; }; - const handleOpenLocation = () => {}; + const handleOpenLocation = () => openBottomSheet(, '지역 선택'); const handleOpenDate = () => openBottomSheet(, ''); return ( @@ -53,7 +56,7 @@ const LocalDateSelectionModal = ({ modalId }: { modalId: number }) => { <> @@ -88,6 +99,10 @@ const DivStyle = styled.div<{ isFirst: boolean; isLast: boolean }>` ` border-bottom: unset; `} + + &:hover { + cursor: pointer; + } `; const ItemContentStyle = styled.div` diff --git a/src/pages/Studio/StudioMain/StudioMain.tsx b/src/pages/Studio/StudioMain/StudioMain.tsx new file mode 100644 index 0000000..0654f91 --- /dev/null +++ b/src/pages/Studio/StudioMain/StudioMain.tsx @@ -0,0 +1,8 @@ +import { useParams } from 'react-router-dom'; + +const StudioMain = () => { + const { _id } = useParams(); + return <>{_id} 스튜디오; +}; + +export default StudioMain; diff --git a/src/pages/Studio/StudioMenu/StudioMenu.tsx b/src/pages/Studio/StudioMenu/StudioMenu.tsx new file mode 100644 index 0000000..ffc7acf --- /dev/null +++ b/src/pages/Studio/StudioMenu/StudioMenu.tsx @@ -0,0 +1,8 @@ +import { useParams } from 'react-router-dom'; + +const StudioMenu = () => { + const { _id } = useParams(); + return <>{_id} 스튜디오 메뉴; +}; + +export default StudioMenu; diff --git a/src/pages/Studio/StudioPorfolio/StudioPortfolio.tsx b/src/pages/Studio/StudioPorfolio/StudioPortfolio.tsx new file mode 100644 index 0000000..33f8663 --- /dev/null +++ b/src/pages/Studio/StudioPorfolio/StudioPortfolio.tsx @@ -0,0 +1,8 @@ +import { useParams } from 'react-router-dom'; + +const StudioPortfolio = () => { + const { _id } = useParams(); + return <>{_id} 스튜디오 포트폴리오; +}; + +export default StudioPortfolio; diff --git a/src/pages/Studio/StudioReview/StudioReview.tsx b/src/pages/Studio/StudioReview/StudioReview.tsx new file mode 100644 index 0000000..7d9a9a0 --- /dev/null +++ b/src/pages/Studio/StudioReview/StudioReview.tsx @@ -0,0 +1,8 @@ +import { useParams } from 'react-router-dom'; + +const StudioReview = () => { + const { _id } = useParams(); + return <>{_id} 스튜디오 리뷰; +}; + +export default StudioReview; diff --git a/src/routes.tsx b/src/routes.tsx index 74a3da7..12dd671 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -1,6 +1,10 @@ import Home from '@pages/Home/Home'; import Search from '@pages/search/Search'; import SearchResults from '@pages/search/SearchResult'; +import StudioMain from '@pages/Studio/StudioMain/StudioMain'; +import StudioMenu from '@pages/Studio/StudioMenu/StudioMenu'; +import StudioPortfolio from '@pages/Studio/StudioPorfolio/StudioPortfolio'; +import StudioReview from '@pages/Studio/StudioReview/StudioReview'; import { createBrowserRouter } from 'react-router-dom'; const router = createBrowserRouter([ @@ -16,6 +20,15 @@ const router = createBrowserRouter([ path: 'search/results', element: , }, + { + path: 'studio/:_id', + children: [ + { index: true, element: }, + { path: 'menu', element: }, + { path: 'portfolio', element: }, + { path: 'review', element: }, + ], + }, ]); export default router; From edbcd72a1ec64913795951a10eee7f56bbfcbd22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=EB=AF=BC?= Date: Thu, 12 Dec 2024 15:45:50 +0900 Subject: [PATCH 013/146] =?UTF-8?q?Feat:=20=EB=B3=B5=EC=82=AC=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/CopyButton/CopyButton.tsx | 71 ++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 src/components/CopyButton/CopyButton.tsx diff --git a/src/components/CopyButton/CopyButton.tsx b/src/components/CopyButton/CopyButton.tsx new file mode 100644 index 0000000..1262585 --- /dev/null +++ b/src/components/CopyButton/CopyButton.tsx @@ -0,0 +1,71 @@ +/** @jsxImportSource @emotion/react */ +import { css } from '@emotion/react'; +import variables from '@styles/Variables'; +import { useState } from 'react'; + +interface CopyButtonProps { + text: string; + buttonLabel?: string; +} + +const CopyButton = ({ text, buttonLabel = '복사' }: CopyButtonProps) => { + const [isCopied, setIsCopied] = useState(false); + + const handleCopy = async () => { + await navigator.clipboard.writeText(text); + setIsCopied(true); + setTimeout(() => setIsCopied(false), 1500); + }; + + return ( +
+ {text} +
+ + {isCopied && 복사되었습니다! 🎉} +
+
+ ); +}; + +export default CopyButton; + +const containerStyle = css` + display: flex; + flex-direction: column; + justify-content: center; + gap: 1rem; +`; + +const textStyle = css` + font-size: 1.4rem + color: ${variables.colors.gray800}; +`; + +const buttonRowStyle = css` + display: flex; + align-items: center; + gap: 1rem; +`; + +const buttonStyle = css` + padding: 0.6rem 1.2rem; + background-color: ${variables.colors.gray400}; + border-radius: 0.4rem; + cursor: pointer; + max-width: 5rem; + width: 100%; + text-align: center; + transition: background-color 0.3s; + + &:hover { + background-color: ${variables.colors.gray500}; + } +`; + +const feedbackStyle = css` + font-size: 1.2rem; + color: #${variables.colors.gray200}; +`; From de6243b0dbd657e503936ecd85e97332c98589a3 Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Thu, 12 Dec 2024 15:54:34 +0900 Subject: [PATCH 014/146] =?UTF-8?q?Hotfix:=20img=20=ED=83=9C=EA=B7=B8=20al?= =?UTF-8?q?t=20=EC=86=8D=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Studio/StudioItem.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Studio/StudioItem.tsx b/src/components/Studio/StudioItem.tsx index 83a9f3a..52890f7 100644 --- a/src/components/Studio/StudioItem.tsx +++ b/src/components/Studio/StudioItem.tsx @@ -56,24 +56,24 @@ const StudioItem = ({ item, isFirst, isLast }: { item: IStudioItem; isFirst: boo {`${item.name}`}
- + 평점

{item.rating} {` (${item.review_count}개의 평가)`}

- + 가격

{`${getMinPrice(item.menus)}원~`}

- + 주소

{`${item.addressGu} ${item.address}`}

- + 영업 시간

{`${item.open_time.slice(0, -3)} - ${item.close_time.slice(0, -3)}`}

From 06b0f59c171be6ad2c7fdd7da3a3ecc7f9f5867d Mon Sep 17 00:00:00 2001 From: woojoung Date: Thu, 12 Dec 2024 14:11:47 +0900 Subject: [PATCH 015/146] =?UTF-8?q?Docs=20:=20index.html=20en=3D>=20ko=20?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index c17266e..ab4192e 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ - + From 2973109c14ef411b4f0f8df81325796e1db3f875 Mon Sep 17 00:00:00 2001 From: woojoung Date: Thu, 12 Dec 2024 14:13:39 +0900 Subject: [PATCH 016/146] =?UTF-8?q?Docs=20:=20reviews=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/reviews/ReviewPage.tsx | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/pages/reviews/ReviewPage.tsx diff --git a/src/pages/reviews/ReviewPage.tsx b/src/pages/reviews/ReviewPage.tsx new file mode 100644 index 0000000..6485a55 --- /dev/null +++ b/src/pages/reviews/ReviewPage.tsx @@ -0,0 +1,5 @@ +const ReviewPage = () => { + return
ReviewPage
; +}; + +export default ReviewPage; From 3413e08aad6d9226971a582ab8bb6b782b2d448c Mon Sep 17 00:00:00 2001 From: woojoung Date: Thu, 12 Dec 2024 16:37:23 +0900 Subject: [PATCH 017/146] =?UTF-8?q?Conf=20:=20reviewPage=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/reviews/ReviewPage.tsx | 39 ++++++++++++++++++- .../reviews/components/ReviewImageList.tsx | 16 ++++++++ src/routes.tsx | 1 + 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 src/pages/reviews/components/ReviewImageList.tsx diff --git a/src/pages/reviews/ReviewPage.tsx b/src/pages/reviews/ReviewPage.tsx index 6485a55..d7aab31 100644 --- a/src/pages/reviews/ReviewPage.tsx +++ b/src/pages/reviews/ReviewPage.tsx @@ -1,5 +1,42 @@ +/** @jsxImportSource @emotion/react */ +import styled from '@emotion/styled'; +import { TypoTitleXsM } from '@styles/Common'; + +/** 스튜디오 아이템 상세 리뷰 컴포넌트 */ const ReviewPage = () => { - return
ReviewPage
; + return ( + <> + 헤더 위치 + 네브바 위치 + + +

리뷰사진 (140)

+ +
+ + ); }; export default ReviewPage; + +const ForHeader = styled.div` + width: 100%; + height: 5.6rem; + box-shadow: inset 0px 0px 10px red; +`; +const ForNavBar = styled.div` + width: 100%; + height: 4rem; + box-shadow: inset 0px 0px 10px blue; +`; +const ReviewPhotosWrapperStyle = styled.div` + width: 100%; + height: 8rem; + margin-top: 2.2rem; +`; +const PhotoContainerStyle = styled.div` + margin-top: 0.8rem; + width: 100%; + height: 8rem; + background-color: gray; +`; diff --git a/src/pages/reviews/components/ReviewImageList.tsx b/src/pages/reviews/components/ReviewImageList.tsx new file mode 100644 index 0000000..0b85feb --- /dev/null +++ b/src/pages/reviews/components/ReviewImageList.tsx @@ -0,0 +1,16 @@ +/** @jsxImportSource @emotion/react */ +import styled from '@emotion/styled'; + +/** 이미지만 받아서 보여주는 (자식) 컴포넌트 */ +const ReviewImageList = () => { + return 이미지 리스트; +}; + +export default ReviewImageList; + +const PhotoContainerStyle = styled.div` + margin-top: 0.8rem; + width: 100%; + height: 8rem; + box-shadow: inset 0px 0px 10px gray; +`; diff --git a/src/routes.tsx b/src/routes.tsx index 12dd671..32756a9 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -1,4 +1,5 @@ import Home from '@pages/Home/Home'; +import ReviewPage from '@pages/reviews/ReviewPage'; import Search from '@pages/search/Search'; import SearchResults from '@pages/search/SearchResult'; import StudioMain from '@pages/Studio/StudioMain/StudioMain'; From 93ec303e4fe0386071ec9fac5c2654b425ec39dd Mon Sep 17 00:00:00 2001 From: woojoung Date: Thu, 12 Dec 2024 16:57:35 +0900 Subject: [PATCH 018/146] =?UTF-8?q?Conf=20:=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EB=B3=91=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Studio/StudioReview/StudioReview.tsx | 100 +++++++++++++++++- .../components/ReviewImageList.tsx | 0 src/pages/reviews/ReviewPage.tsx | 42 -------- 3 files changed, 97 insertions(+), 45 deletions(-) rename src/pages/{reviews => Studio/StudioReview}/components/ReviewImageList.tsx (100%) delete mode 100644 src/pages/reviews/ReviewPage.tsx diff --git a/src/pages/Studio/StudioReview/StudioReview.tsx b/src/pages/Studio/StudioReview/StudioReview.tsx index 7d9a9a0..15cf4e0 100644 --- a/src/pages/Studio/StudioReview/StudioReview.tsx +++ b/src/pages/Studio/StudioReview/StudioReview.tsx @@ -1,8 +1,102 @@ -import { useParams } from 'react-router-dom'; +/** @jsxImportSource @emotion/react */ +import styled from '@emotion/styled'; +import { useState } from 'react'; +import Button from '@components/Button/Button'; +import { TypoTitleXsM } from '@styles/Common'; +import variables from '@styles/Variables'; +import ReviewImageList from './components/ReviewImageList'; const StudioReview = () => { - const { _id } = useParams(); - return <>{_id} 스튜디오 리뷰; + const [selectedFilter, setSelectedFilter] = useState('all'); + + const filterButtons = [ + { label: '전체', value: '1' }, + { label: '증명사진', value: '2' }, + { label: '상품명1', value: '3' }, + { label: '상품명2', value: '4' }, + ]; + + const handleFilterClick = (value: string) => { + setSelectedFilter(value); + console.log('선택된 필터:', value); + }; + + return ( + <> + 헤더 위치 + 네브바 위치 + +

리뷰사진 (140)

+ +
+ + + {filterButtons.map((button) => ( +
{baseYear}년 {baseMonth + 1}월 @@ -100,7 +102,7 @@ const Calendar = () => { `} onClick={() => changeMonth(1)} > - 다음 달로 + 다음 달로 이동 @@ -133,6 +135,9 @@ const Calendar = () => { ))} +

+ 선택된 날짜: {activeDay} +

); From f696c88b12da12fe68e5bb824c7d79249330ddf9 Mon Sep 17 00:00:00 2001 From: s0zzang Date: Thu, 12 Dec 2024 17:26:55 +0900 Subject: [PATCH 023/146] =?UTF-8?q?Test:=20=EC=BA=98=EB=A6=B0=EB=8D=94=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/Calendar.test.tsx | 57 ++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/tests/Calendar.test.tsx b/tests/Calendar.test.tsx index 21fb5b5..d0e62a0 100644 --- a/tests/Calendar.test.tsx +++ b/tests/Calendar.test.tsx @@ -1,16 +1,12 @@ import Calendar from '@components/Calendar/Calendar'; import { render, screen } from '@testing-library/react'; import { beforeEach, describe, expect, test } from 'vitest'; -// import { convertToDateFormat, useSelectDateStore } from '@store/useSelectDate'; -// import userEvent from '@testing-library/user-event'; - -// 지역과 날짜(시간)가 선택되면 메인으로 돌아가고 그것에 맞춰 리스트가 필터링되는 행동이 필요해 -// 사용자가 달력을 확인하고 달력의 기능을 사용할 수 있는지 확인할 수 있는 행동이 필요해 -// 달력을 통해 특정 날짜를 선택할 수 있는지 확인할 수 있는 행동이 필요해 +import { convertToDateFormat } from '@store/useSelectDate'; +import userEvent from '@testing-library/user-event'; describe('달력 컴포넌트', () => { const today = new Date(); - const todayMonth = today.getMonth(); + const [_, todayMonth, todayDate] = convertToDateFormat(today).split('-'); beforeEach(() => { render(); @@ -21,31 +17,38 @@ describe('달력 컴포넌트', () => { expect(monthText).toBeInTheDocument(); }); - test('1일을 선택하면 1일이 전역 상태 값에 담긴다', () => { - const buttons = screen.getAllByRole('button'); - buttons.forEach((button) => { - console.log(button.getAttribute('name')); // name 속성 출력 - console.log(button.textContent); // 내부 텍스트 출력 - }); + test('월 변경 버튼을 클릭하면 월이 변경된다', async () => { + const monthText = screen.getByText(new RegExp(`${todayMonth}월`)); + const toNextMonthButton = screen.getByText('다음 달로 이동'); + const toPrevMonthButton = screen.getByText('이전 달로 이동'); + + // 다음 달로 이동 버튼 클릭 + await userEvent.click(toNextMonthButton); + expect(monthText).toHaveTextContent(`${todayMonth !== '12' ? +todayMonth + 1 : 1}월`); - // 전역 date 상태 초기화 - // useSelectDateStore.setState({ date: '' }); + // 이전 달로 이동 버튼 클릭 + await userEvent.click(toPrevMonthButton); + expect(monthText).toHaveTextContent(`${todayMonth !== '12' ? +todayMonth - 1 : 12}월`); + }); - const selectedDate = screen.getByRole('button', { name: /1일/i }); - expect(selectedDate).toBeInTheDocument; + test('내일 날짜를 클릭하면 내일 날짜로 선택된 날짜의 값이 변경된다', async () => { + const selectedDate = screen.getByText(/선택된 날짜/); - // const { date } = useSelectDateStore.getState(); + // 선택된 버튼 : 내일 날짜 || 내일 날짜가 달력에 없는 경우, 오늘 날짜 + const selectedButton = screen.getAllByRole('button', { name: `${+todayDate + 1} 일` })[0] || screen.getAllByRole('button', { name: `${todayDate} 일` })[0]; - // 버튼 클릭 - // fireEvent.click(selectedDate[0]); - // userEvent.click(selectedDate); + // 내일 날짜의 버튼 클릭 + await userEvent.click(selectedButton); + // 선택된 날짜가 내일 날짜로 변경되는지 확인 + expect(selectedDate).toHaveTextContent(todayDate); + }); - // 상태 검증 - // expect(date).toBe(''); // 값이 변경되었는지 확인 + test('"오늘"을 클릭하면 오늘 날짜로 이동한다', async () => { + const selectedDate = screen.getByText(/선택된 날짜/); + const toTodayButton = screen.getByText(/오늘/); - // const { setDate } = useSelectDateStore.getState(); - // setDate('2024-12-11'); - // 화면에 상태가 반영되었는지 확인 - // expect(screen.getByText(/Selected Date: 2024-12-11/i)).toBeInTheDocument(); + // '오늘' 버튼 클릭 + await userEvent.click(toTodayButton); + expect(selectedDate).toHaveTextContent(convertToDateFormat(today)); }); }); From 45e24b91eb7602e5354f46a5a41550a94ccc176e Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Thu, 12 Dec 2024 17:55:26 +0900 Subject: [PATCH 024/146] =?UTF-8?q?Feat:=20=EB=B6=81=EB=A7=88=ED=81=AC=20?= =?UTF-8?q?=EA=B3=B5=ED=86=B5=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=ED=99=94?= =?UTF-8?q?=20=EB=B0=8F=20=EB=B6=81=EB=A7=88=ED=81=AC=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?/=ED=95=B4=EC=A0=9C=20=ED=9B=85=20=EC=84=A4=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Bookmark/Bookmark.tsx | 61 ++++++++++++++++++++++++++++ src/components/Studio/StudioItem.tsx | 19 +++------ src/hooks/useBookmark.ts | 32 +++++++++++++++ 3 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 src/components/Bookmark/Bookmark.tsx create mode 100644 src/hooks/useBookmark.ts diff --git a/src/components/Bookmark/Bookmark.tsx b/src/components/Bookmark/Bookmark.tsx new file mode 100644 index 0000000..17c1077 --- /dev/null +++ b/src/components/Bookmark/Bookmark.tsx @@ -0,0 +1,61 @@ +/** @jsxImportSource @emotion/react */ +import styled from '@emotion/styled'; +import useBookmark from '@hooks/useBookmark'; +import { Hidden } from '@styles/Common'; +import variables from '@styles/Variables'; +import { useState } from 'react'; + +const Bookmark = ({ id, count, isBookmarked }: { id: number; count: number; isBookmarked: boolean }) => { + const [isActive, setIsActive] = useState(isBookmarked); + const [bookmarkCount, setBookmarkCount] = useState(count); + const handleBookmark = useBookmark(isActive); + + // 북마크 설정/해제 api 호출 + const handleClick = async (e: React.MouseEvent) => { + e.stopPropagation(); + + await handleBookmark(1, id); + + setIsActive(!isActive); + + if (isActive) { + setBookmarkCount((count) => count - 1); + } else { + setBookmarkCount((count) => count + 1); + } + }; + + return ( + + +

{bookmarkCount}

+
+ ); +}; + +export default Bookmark; + +const BookmarkStyle = styled.div` + & > button { + width: 2.4rem; + height: 2.4rem; + margin: 0 auto; + + & > img { + width: 100%; + aspect-ratio: 1/1; + margin: 0 auto; + } + } + + & > p { + color: ${variables.colors.gray600}; + margin: 0 auto; + text-align: center; + font-size: 1rem; + line-height: 1.2; + } +`; diff --git a/src/components/Studio/StudioItem.tsx b/src/components/Studio/StudioItem.tsx index 52890f7..0b914d2 100644 --- a/src/components/Studio/StudioItem.tsx +++ b/src/components/Studio/StudioItem.tsx @@ -1,22 +1,17 @@ /** @jsxImportSource @emotion/react */ +import Bookmark from '@components/Bookmark/Bookmark'; import ImageSwiper from '@components/ImageSwiper/ImageSwiper'; import styled from '@emotion/styled'; -import { Hidden, TypoTitleSmS } from '@styles/Common'; +import { TypoTitleSmS } from '@styles/Common'; import variables from '@styles/Variables'; import { useNavigate } from 'react-router-dom'; import { IMenus, IPortfolio, IStudioItem } from 'types/types'; const StudioItem = ({ item, isFirst, isLast }: { item: IStudioItem; isFirst: boolean; isLast: boolean }) => { const navigate = useNavigate(); - // 북마크 설정/해제 api 호출 - const handleClickBookmark = (e: React.MouseEvent) => { - e.stopPropagation(); - - console.log(`북마크 ${item.bookmark ? '해제' : '설정'}`); - }; // 스튜디오 클릭 시 navigate - const handleClickStudio = () => { + const handleClick = () => { navigate(`/studio/${item.id}`); }; @@ -48,7 +43,7 @@ const StudioItem = ({ item, isFirst, isLast }: { item: IStudioItem; isFirst: boo }; return ( - + @@ -79,11 +74,7 @@ const StudioItem = ({ item, isFirst, isLast }: { item: IStudioItem; isFirst: boo - -

{item.bookmark_count}

+
diff --git a/src/hooks/useBookmark.ts b/src/hooks/useBookmark.ts new file mode 100644 index 0000000..1fd1fd2 --- /dev/null +++ b/src/hooks/useBookmark.ts @@ -0,0 +1,32 @@ +// userId === 1 => 로그인/회원가입 시 변경 예정 +const postBookmark = async (userId: number = 1, studioId: number) => { + const response = await fetch(`${import.meta.env.VITE_TOUCHEESE_API}/user/bookmark/${userId}/${studioId}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + console.error('Failed to bookmark'); + } +}; + +const deleteBookmark = async (userId: number = 1, studioId: number) => { + const response = await fetch(`${import.meta.env.VITE_TOUCHEESE_API}/user/bookmark/${userId}/${studioId}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + console.error('Failed to bookmark'); + } +}; + +const useBookmark = (isBookmarked: boolean) => { + return isBookmarked ? deleteBookmark : postBookmark; +}; + +export default useBookmark; From 1003096941a727e4f0cb0785228b8b462eec693b Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Thu, 12 Dec 2024 19:49:46 +0900 Subject: [PATCH 025/146] =?UTF-8?q?Refactor:=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=20state=20=EA=B5=AC=EC=A1=B0=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Bookmark/Bookmark.tsx | 31 ++++++++++++++++------------ 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/components/Bookmark/Bookmark.tsx b/src/components/Bookmark/Bookmark.tsx index 17c1077..d5e0e15 100644 --- a/src/components/Bookmark/Bookmark.tsx +++ b/src/components/Bookmark/Bookmark.tsx @@ -5,10 +5,17 @@ import { Hidden } from '@styles/Common'; import variables from '@styles/Variables'; import { useState } from 'react'; +interface IBookmarkState { + isActive: boolean; + bookmarkCount: number; +} + const Bookmark = ({ id, count, isBookmarked }: { id: number; count: number; isBookmarked: boolean }) => { - const [isActive, setIsActive] = useState(isBookmarked); - const [bookmarkCount, setBookmarkCount] = useState(count); - const handleBookmark = useBookmark(isActive); + const [bookmark, setBookmark] = useState({ + isActive: isBookmarked, + bookmarkCount: count, + }); + const handleBookmark = useBookmark(bookmark.isActive); // 북마크 설정/해제 api 호출 const handleClick = async (e: React.MouseEvent) => { @@ -16,22 +23,20 @@ const Bookmark = ({ id, count, isBookmarked }: { id: number; count: number; isBo await handleBookmark(1, id); - setIsActive(!isActive); - - if (isActive) { - setBookmarkCount((count) => count - 1); - } else { - setBookmarkCount((count) => count + 1); - } + setBookmark((state: IBookmarkState) => ({ + ...state, + isActive: !state.isActive, + bookmarkCount: bookmark.isActive ? state.bookmarkCount - 1 : state.bookmarkCount + 1, + })); }; return ( -

{bookmarkCount}

+

{bookmark.bookmarkCount}

); }; From c369ed4fac7f932619fed75bd1b37b663ac85051 Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Thu, 12 Dec 2024 19:53:45 +0900 Subject: [PATCH 026/146] =?UTF-8?q?Hotfix:=20setState=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Bookmark/Bookmark.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Bookmark/Bookmark.tsx b/src/components/Bookmark/Bookmark.tsx index d5e0e15..e664f8a 100644 --- a/src/components/Bookmark/Bookmark.tsx +++ b/src/components/Bookmark/Bookmark.tsx @@ -26,7 +26,7 @@ const Bookmark = ({ id, count, isBookmarked }: { id: number; count: number; isBo setBookmark((state: IBookmarkState) => ({ ...state, isActive: !state.isActive, - bookmarkCount: bookmark.isActive ? state.bookmarkCount - 1 : state.bookmarkCount + 1, + bookmarkCount: state.isActive ? state.bookmarkCount - 1 : state.bookmarkCount + 1, })); }; From e1a71e651c41418d7ebdccca58d5537e0ce2b665 Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Thu, 12 Dec 2024 20:22:57 +0900 Subject: [PATCH 027/146] =?UTF-8?q?Hotfix:=20state=EC=9D=98=20Props=20?= =?UTF-8?q?=EB=AF=B8=EB=9F=AC=EB=A7=81=20=EC=B5=9C=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Bookmark/Bookmark.tsx | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/components/Bookmark/Bookmark.tsx b/src/components/Bookmark/Bookmark.tsx index e664f8a..2e80ab9 100644 --- a/src/components/Bookmark/Bookmark.tsx +++ b/src/components/Bookmark/Bookmark.tsx @@ -5,17 +5,10 @@ import { Hidden } from '@styles/Common'; import variables from '@styles/Variables'; import { useState } from 'react'; -interface IBookmarkState { - isActive: boolean; - bookmarkCount: number; -} - -const Bookmark = ({ id, count, isBookmarked }: { id: number; count: number; isBookmarked: boolean }) => { - const [bookmark, setBookmark] = useState({ - isActive: isBookmarked, - bookmarkCount: count, - }); - const handleBookmark = useBookmark(bookmark.isActive); +const Bookmark = ({ id, count: initialCount, isBookmarked: initialBookmark }: { id: number; count: number; isBookmarked: boolean }) => { + const [isActive, setIsActive] = useState(initialBookmark); + const handleBookmark = useBookmark(isActive); + const count = initialCount; // 북마크 설정/해제 api 호출 const handleClick = async (e: React.MouseEvent) => { @@ -23,20 +16,16 @@ const Bookmark = ({ id, count, isBookmarked }: { id: number; count: number; isBo await handleBookmark(1, id); - setBookmark((state: IBookmarkState) => ({ - ...state, - isActive: !state.isActive, - bookmarkCount: state.isActive ? state.bookmarkCount - 1 : state.bookmarkCount + 1, - })); + setIsActive(!isActive); }; return ( -

{bookmark.bookmarkCount}

+

{isActive ? count - 1 : count + 1}

); }; From 80995e63a4505b9cd301a04828bd5d0bcb68ceff Mon Sep 17 00:00:00 2001 From: s0zzang Date: Thu, 12 Dec 2024 21:14:08 +0900 Subject: [PATCH 028/146] =?UTF-8?q?Fix:=20=EC=A7=80=EC=97=AD=20=EB=B0=8F?= =?UTF-8?q?=20=EB=82=A0=EC=A7=9C=20=EC=84=A0=ED=83=9D=20=EB=AA=A8=EB=8B=AC?= =?UTF-8?q?=EC=9D=B4=202=EB=B2=88=20=ED=98=B8=EC=B6=9C=EB=90=98=EC=96=B4?= =?UTF-8?q?=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Home/Home.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index d815217..7f27808 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -76,7 +76,6 @@ const Home = () => { - ); From 83b1399e3d5bd99a8cb6c1c33558626fe7e72850 Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Fri, 13 Dec 2024 12:47:47 +0900 Subject: [PATCH 029/146] =?UTF-8?q?Done:=20=EB=B6=81=EB=A7=88=ED=81=AC=20?= =?UTF-8?q?=EC=B5=9C=EC=A2=85=20=EC=88=98=EC=A0=95=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Bookmark/Bookmark.tsx | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/components/Bookmark/Bookmark.tsx b/src/components/Bookmark/Bookmark.tsx index 2e80ab9..4fa859e 100644 --- a/src/components/Bookmark/Bookmark.tsx +++ b/src/components/Bookmark/Bookmark.tsx @@ -5,27 +5,37 @@ import { Hidden } from '@styles/Common'; import variables from '@styles/Variables'; import { useState } from 'react'; +interface IBookmarkState { + isActive: boolean; + count: number; +} + const Bookmark = ({ id, count: initialCount, isBookmarked: initialBookmark }: { id: number; count: number; isBookmarked: boolean }) => { - const [isActive, setIsActive] = useState(initialBookmark); - const handleBookmark = useBookmark(isActive); - const count = initialCount; + const [bookmark, setBookmark] = useState({ + isActive: initialBookmark, + count: initialCount, + }); + const handleBookmark = useBookmark(bookmark.isActive); // 북마크 설정/해제 api 호출 const handleClick = async (e: React.MouseEvent) => { e.stopPropagation(); await handleBookmark(1, id); - - setIsActive(!isActive); + setBookmark((state) => ({ + ...state, + isActive: !state.isActive, + count: state.isActive ? state.count - 1 : state.count + 1, + })); }; return ( -

{isActive ? count - 1 : count + 1}

+

{bookmark.count}

); }; From 430147c3ef43875aa211884f9461d02b983ea78e Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Fri, 13 Dec 2024 13:29:09 +0900 Subject: [PATCH 030/146] =?UTF-8?q?Feat:=20=EB=A9=94=EB=89=B4=20=EC=83=81?= =?UTF-8?q?=EC=84=B8,=20=EB=A6=AC=EB=B7=B0=20=EB=AA=A8=EC=95=84=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20=EB=9D=BC=EC=9A=B0=ED=8C=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Studio/StudioMenu/StudioMenuDetail.tsx | 8 +++++++ .../StudioReview/StudioReviewPhotos.tsx | 5 +++++ src/routes.tsx | 21 +++++++++++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 src/pages/Studio/StudioMenu/StudioMenuDetail.tsx create mode 100644 src/pages/Studio/StudioReview/StudioReviewPhotos.tsx diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx new file mode 100644 index 0000000..565c30c --- /dev/null +++ b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx @@ -0,0 +1,8 @@ +import { useParams } from 'react-router-dom'; + +const StudioMenuDetail = () => { + const { _menuId } = useParams(); + return <>스튜디오 메뉴 {_menuId} 상세; +}; + +export default StudioMenuDetail; diff --git a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx new file mode 100644 index 0000000..941667c --- /dev/null +++ b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx @@ -0,0 +1,5 @@ +const StudioReviewPhotos = () => { + return <>스튜디오 리뷰 모아보기; +}; + +export default StudioReviewPhotos; diff --git a/src/routes.tsx b/src/routes.tsx index a077d9d..cd0cc50 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -3,8 +3,10 @@ import Search from '@pages/search/Search'; import SearchResults from '@pages/search/SearchResult'; import StudioMain from '@pages/Studio/StudioMain/StudioMain'; import StudioMenu from '@pages/Studio/StudioMenu/StudioMenu'; +import StudioMenuDetail from '@pages/Studio/StudioMenu/StudioMenuDetail'; import StudioPortfolio from '@pages/Studio/StudioPortfolio/StudioPortfolio'; import StudioReview from '@pages/Studio/StudioReview/StudioReview'; +import StudioReviewPhotos from '@pages/Studio/StudioReview/StudioReviewPhotos'; import { createBrowserRouter } from 'react-router-dom'; const router = createBrowserRouter([ @@ -24,9 +26,24 @@ const router = createBrowserRouter([ path: 'studio/:_id', children: [ { index: true, element: }, - { path: 'menu', element: }, + { + path: 'menu', + children: [ + { index: true, element: }, + { path: ':_menuId', element: }, + ], + }, { path: 'portfolio', element: }, - { path: 'review', element: }, + { + path: 'review', + children: [ + { + index: true, + element: , + }, + { path: 'photos', element: }, + ], + }, ], }, ]); From 84f9af41f894b03f837cd4eb3cb93a4d126cee41 Mon Sep 17 00:00:00 2001 From: s0zzang Date: Fri, 13 Dec 2024 13:56:03 +0900 Subject: [PATCH 031/146] =?UTF-8?q?Test:=20=EC=A7=80=EC=97=AD=20=EB=B0=8F?= =?UTF-8?q?=20=EB=82=A0=EC=A7=9C=20=EC=84=A0=ED=83=9D=20=EB=AA=A8=EB=8B=AC?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20(=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EB=AF=B8=EC=99=84=EB=A3=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/LocalDateSelectionModal.test.tsx | 82 ++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 tests/LocalDateSelectionModal.test.tsx diff --git a/tests/LocalDateSelectionModal.test.tsx b/tests/LocalDateSelectionModal.test.tsx new file mode 100644 index 0000000..be6955c --- /dev/null +++ b/tests/LocalDateSelectionModal.test.tsx @@ -0,0 +1,82 @@ +import Home from '@pages/Home/Home'; +import { convertToDateFormat } from '@store/useSelectDate'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { MemoryRouter } from 'react-router-dom'; +import { expect, test } from 'vitest'; + +describe('지역 및 날짜 선택 모달', () => { + const queryClient = new QueryClient(); + const renderWithQueryClient = (children: React.ReactNode, { route = '/' } = {}) => + render( + + {children} + , + ); + + beforeEach(async () => { + renderWithQueryClient(, { route: '/' }); + }); + + test('지역 및 날짜 선택 모달을 활성화한다', async () => { + // 지역 및 날짜 선택 모달 오픈 + const openModalButton = screen.getByText(/전체지역/); + await userEvent.click(openModalButton); + + // 지역 및 날짜 선택 모달 오픈 확인 + const modalTitle = screen.getAllByRole('heading', { name: '지역, 날짜 선택' })[0]; + expect(modalTitle).toBeInTheDocument(); + }); + + test('"지역 선택" 버튼을 누르면 해당 바텀 시트가 활성화 된다', async () => { + // 지역 선택 버튼 탐색 + const localBottomSheetOpenButton = screen.getByRole('button', { name: '지역 선택' }); + await userEvent.click(localBottomSheetOpenButton); + + // 지역 선택 바텀시트 검증 + const bottomSheetTitle = screen.getByRole('heading', { name: '지역 선택' }); + expect(bottomSheetTitle).toBeInTheDocument(); + + // 바텀시트 종료 + const closeButton = screen.getByRole('button', { name: '바텀시트 닫기' }); + userEvent.click(closeButton); + + // 바텀시트 종료 검증 + await waitFor(() => expect(closeButton).not.toBeInTheDocument()); + }); + + test('"예약 날짜 선택" 버튼을 누르면 해당 바텀 시트가 활성화 된다', async () => { + const today = new Date(); + + // 날짜 선택 버튼 탐색 + const dateBottomSheetOpenButton = screen.getByRole('button', { name: '예약 날짜 선택' }); + await userEvent.click(dateBottomSheetOpenButton); + + // 날짜 선택 바텀시트 검증 + const bottomSheetTitle = screen.getByText(convertToDateFormat(today)); + expect(bottomSheetTitle).toBeInTheDocument(); + + // 바텀시트 종료 + const closeButton = screen.getByRole('button', { name: '바텀시트 닫기' }); + userEvent.click(closeButton); + + // 바텀시트 종료 검증 + await waitFor(() => expect(closeButton).not.toBeInTheDocument()); + }); + + test('적용하기 버튼을 누르면 모달이 꺼지고 파라미터가 변경된다', async () => { + const modalTitle = screen.getByRole('heading', { name: '지역, 날짜 선택' }); + + // 모달 종료 버튼 클릭 + const applyButton = screen.getByRole('button', { name: '적용하기' }); + await userEvent.click(applyButton); + + // 모달 종료 검증 + expect(modalTitle).not.toBeInTheDocument(); + + // 파라미터 변경 검증 추가 예정 + // requestedDateTime + // requestedLocation + }); +}); From b5ae9de6afb66ae492edf76635eeef33b255d187 Mon Sep 17 00:00:00 2001 From: s0zzang Date: Fri, 13 Dec 2024 13:59:13 +0900 Subject: [PATCH 032/146] =?UTF-8?q?Refactor:=20=EB=AA=A8=EB=8B=AC,=20?= =?UTF-8?q?=EB=B0=94=ED=85=80=EC=8B=9C=ED=8A=B8=20=EB=8B=AB=EA=B8=B0=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20name=20=EB=B3=80=EA=B2=BD=20(=EB=8B=AB?= =?UTF-8?q?=EA=B8=B0=20->=20=EB=AA=A8=EB=8B=AC=20=EB=8B=AB=EA=B8=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/BottomSheet/BottomSheet.tsx | 2 +- src/components/Modal/Modal.tsx | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/BottomSheet/BottomSheet.tsx b/src/components/BottomSheet/BottomSheet.tsx index a9b6134..348ab7b 100644 --- a/src/components/BottomSheet/BottomSheet.tsx +++ b/src/components/BottomSheet/BottomSheet.tsx @@ -38,7 +38,7 @@ const BottomSheet = () => {

{title}

diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index 1623c4a..06d1296 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -27,15 +27,13 @@ const Modal = ({ modalId = 1, size = 'default', title, children, withBtn = true, const { isOpen, close } = useModal(modalId); const handleClose = () => close(); - console.log(size); - return ( isOpen && ( {isOpen} - 닫기 + 모달 닫기

{title}

From 508bb829fb3a02d8993df964b2a1ece765c95a2f Mon Sep 17 00:00:00 2001 From: s0zzang Date: Fri, 13 Dec 2024 13:59:37 +0900 Subject: [PATCH 033/146] =?UTF-8?q?Refactor:=20=EB=B0=94=ED=85=80=EC=8B=9C?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91=EB=B3=B5=20=ED=98=B8=EC=B6=9C=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Home/components/LocalDateSelectionModal.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/Home/components/LocalDateSelectionModal.tsx b/src/pages/Home/components/LocalDateSelectionModal.tsx index 2f6648d..1d15c2e 100644 --- a/src/pages/Home/components/LocalDateSelectionModal.tsx +++ b/src/pages/Home/components/LocalDateSelectionModal.tsx @@ -62,7 +62,6 @@ const LocalDateSelectionModal = ({ modalId }: { modalId: number }) => { {selectedDate.date ? changeformatDateForUi(selectedDate) : '예약 날짜 선택'} - From 680cbc5a5f86d6dfcdd01db6ec12f29a4d4e3a2a Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Fri, 13 Dec 2024 15:37:03 +0900 Subject: [PATCH 034/146] =?UTF-8?q?Done:=20=EA=B3=B5=ED=86=B5=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=83=81=EB=8B=A8=20=ED=83=AD=20?= =?UTF-8?q?=EC=9E=91=EC=97=85=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Navigator/StudioNavigator.tsx | 97 ++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/components/Navigator/StudioNavigator.tsx diff --git a/src/components/Navigator/StudioNavigator.tsx b/src/components/Navigator/StudioNavigator.tsx new file mode 100644 index 0000000..23401ed --- /dev/null +++ b/src/components/Navigator/StudioNavigator.tsx @@ -0,0 +1,97 @@ +import styled from '@emotion/styled'; +import variables from '@styles/Variables'; +import { NavLink } from 'react-router-dom'; + +const StudioNavigator = ({ _id }: { _id: string }) => { + return ( + + + + (isActive ? 'active' : '')} end> + + + + + (isActive ? 'active' : '')} end> + 메뉴 + + + + (isActive ? 'active' : '')} end> + 포트폴리오 + + + + (isActive ? 'active' : '')} end> + 리뷰 + + + + + ); +}; + +const NavStyle = styled.nav``; + +const UlStyle = styled.ul` + display: flex; +`; + +const LiStyle = styled.li` + flex-grow: 1; + flex-shrink: 0; + flex-basis: 0; +`; + +const NavLinkStyle = styled(NavLink)` + position: relative; + display: block; + width: 100%; + padding: 1rem 0; + text-align: center; + color: ${variables.colors.gray600}; + + & > span { + position: relative; + font-size: 1.6rem; + font-weight: 500; + line-height: 2.4rem; + } + + &::after { + content: ''; + position: absolute; + background-color: ${variables.colors.gray300}; + height: 0.1rem; + left: 0; + right: 0; + bottom: 0; + } + + &.active { + color: ${variables.colors.black}; + } + + &.active::before { + content: ''; + position: absolute; + background-color: ${variables.colors.black}; + height: 0.2rem; + left: 0; + right: 0; + bottom: 0; + } + + &.active > span::after { + content: ''; + position: absolute; + right: calc(-4 * sqrt(2) * 0.1rem); + top: calc(-2 * sqrt(2) * 0.1rem); + width: 0.4rem; + height: 0.4rem; + background-color: ${variables.colors.primary}; + transform: translateX(-25%) rotate(45deg); + } +`; + +export default StudioNavigator; From e0d4b99f037ee432e65d096aa3596cd3f56a78a9 Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Fri, 13 Dec 2024 16:52:28 +0900 Subject: [PATCH 035/146] =?UTF-8?q?Fix:=20Home=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Home/Home.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 7f27808..b2b030d 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -8,11 +8,10 @@ import ThemeNavigator from '@components/Navigator/ThemeNavigator'; import ServiceAvailability from '@components/ServiceAvailability/ServiceAvailability'; import StudioList from '@components/Studio/StudioList'; import styled from '@emotion/styled'; +import useBottomSheetState from '@store/useBottomSheetStateStore'; import variables from '@styles/Variables'; import { useEffect, useRef, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; -import LocalDateSelectionModal from './components/LocalDateSelectionModal'; -import useBottomSheetState from '@store/useBottomSheetStateStore'; interface IFixedProps { isFixed: boolean; From 2cb2ca86905ac6874ec689f7c3bbbcfceb4dc6ef Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Fri, 13 Dec 2024 17:35:31 +0900 Subject: [PATCH 036/146] =?UTF-8?q?Hotfix:=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=20=EB=B2=84=ED=8A=BC=20type=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Bookmark/Bookmark.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Bookmark/Bookmark.tsx b/src/components/Bookmark/Bookmark.tsx index 4fa859e..ee81613 100644 --- a/src/components/Bookmark/Bookmark.tsx +++ b/src/components/Bookmark/Bookmark.tsx @@ -31,7 +31,7 @@ const Bookmark = ({ id, count: initialCount, isBookmarked: initialBookmark }: { return ( - From 058b3d2a059e9bd5c8cbb2b69aea972597def2c9 Mon Sep 17 00:00:00 2001 From: s0zzang Date: Fri, 13 Dec 2024 17:44:59 +0900 Subject: [PATCH 037/146] =?UTF-8?q?Package:=20react-responsive-masonry=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 ++ pnpm-lock.yaml | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/package.json b/package.json index 83e15ab..8070060 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-helmet-async": "^2.0.5", + "react-responsive-masonry": "^2.6.0", "react-router-dom": "^7.0.2", "react-virtuoso": "^4.12.3", "swiper": "^11.1.15", @@ -32,6 +33,7 @@ "@types/jest": "^29.5.14", "@types/react": "^18.3.13", "@types/react-dom": "^18.3.1", + "@types/react-responsive-masonry": "^2.1.3", "@vitejs/plugin-react": "^4.3.4", "@vitest/ui": "^2.1.8", "eslint": "^9.16.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0c1650..a381843 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: react-helmet-async: specifier: ^2.0.5 version: 2.0.5(react@18.3.1) + react-responsive-masonry: + specifier: ^2.6.0 + version: 2.6.0 react-router-dom: specifier: ^7.0.2 version: 7.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -60,6 +63,9 @@ importers: '@types/react-dom': specifier: ^18.3.1 version: 18.3.1 + '@types/react-responsive-masonry': + specifier: ^2.1.3 + version: 2.1.3 '@vitejs/plugin-react': specifier: ^4.3.4 version: 4.3.4(vite@6.0.2(@types/node@22.10.1)) @@ -802,6 +808,9 @@ packages: '@types/react-dom@18.3.1': resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==} + '@types/react-responsive-masonry@2.1.3': + resolution: {integrity: sha512-aOFUtv3QwNMmy0BgpQpvivQ/+vivMTB6ARrzf9eTSXsLzXpVnfEtjpHpSknYDnr8KaQmlgeauAj8E7wo/qMOTg==} + '@types/react@18.3.13': resolution: {integrity: sha512-ii/gswMmOievxAJed4PAHT949bpYjPKXvXo1v6cRB/kqc2ZR4n+SgyCyvyc5Fec5ez8VnUumI1Vk7j6fRyRogg==} @@ -1612,6 +1621,9 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-responsive-masonry@2.6.0: + resolution: {integrity: sha512-aD1NMUOoRHoL2PT6k4b/+MtH8ZbSLxk8nr6O04HVLXjy7hFRrVXcHNTHscuTtBC70w0hEsJTATHHUxToQsY3PA==} + react-router-dom@7.0.2: resolution: {integrity: sha512-VJOQ+CDWFDGaWdrG12Nl+d7yHtLaurNgAQZVgaIy7/Xd+DojgmYLosFfZdGz1wpxmjJIAkAMVTKWcvkx1oggAw==} engines: {node: '>=20.0.0'} @@ -2629,6 +2641,10 @@ snapshots: dependencies: '@types/react': 18.3.13 + '@types/react-responsive-masonry@2.1.3': + dependencies: + '@types/react': 18.3.13 + '@types/react@18.3.13': dependencies: '@types/prop-types': 15.7.13 @@ -3497,6 +3513,8 @@ snapshots: react-refresh@0.14.2: {} + react-responsive-masonry@2.6.0: {} + react-router-dom@7.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 From 6cea13b5b88c3cc644a23100a7b0e4b34ef21aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=EB=AF=BC?= Date: Sun, 15 Dec 2024 15:34:51 +0900 Subject: [PATCH 038/146] =?UTF-8?q?Cont:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=8A=AC=EB=9D=BC=EC=9D=B4=EB=93=9C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ImageSwiper/ImageSwiper.tsx | 62 +++++++++++++++++----- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/src/components/ImageSwiper/ImageSwiper.tsx b/src/components/ImageSwiper/ImageSwiper.tsx index 33fcbf5..7ac75d1 100644 --- a/src/components/ImageSwiper/ImageSwiper.tsx +++ b/src/components/ImageSwiper/ImageSwiper.tsx @@ -2,31 +2,39 @@ import { css } from '@emotion/react'; import { Swiper, SwiperSlide, SwiperProps } from 'swiper/react'; import 'swiper/css'; -import { Mousewheel } from 'swiper/modules'; +import { Autoplay, Mousewheel, Pagination } from 'swiper/modules'; import variables from '@styles/Variables'; interface ImageSwiperProps extends SwiperProps { images: string[]; + imageStyle?: ReturnType; } -const ImageSwiper = ({ images, modules = [Mousewheel], mousewheel = { forceToAxis: true, sensitivity: 1 }, spaceBetween = 3, slidesPerView = 4, ...props }: ImageSwiperProps) => { +const ImageSwiper = ({ + images, + modules = [Mousewheel, Pagination, Autoplay], + mousewheel = { forceToAxis: true, sensitivity: 1 }, + spaceBetween = 3, + slidesPerView = 4, + imageStyle, + ...props +}: ImageSwiperProps) => { + const isPaginationActive = slidesPerView === 1; + return ( {images.map((image, index) => ( - {`이미지 + {`이미지 ))} @@ -35,8 +43,38 @@ const ImageSwiper = ({ images, modules = [Mousewheel], mousewheel = { forceToAxi export default ImageSwiper; -const imageStyle = css` - width: 9.4rem; - height: 11.8rem; +const swiperStyle = css` + width: calc(100% + ${variables.layoutPadding}); + margin-right: ${variables.layoutPadding}; + margin-bottom: 1.4rem; + + .swiper-pagination { + position: absolute; + bottom: 15px; + width: 100%; + display: flex; + justify-content: center; + z-index: 10; + } + + .swiper-pagination-bullet { + background-color: ${variables.colors.white}; + opacity: 0.8; + width: 20px; + height: 3px; + transition: all 0.3s ease; + margin: 0 1px; + cursor: pointer; + } + + .swiper-pagination-bullet-active { + background-color: ${variables.colors.black}; + opacity: 1; + } +`; + +const defaultImageStyle = css` + width: 100%; + height: auto; object-fit: cover; `; From 1a07e6d6445f8b09da006f76c066758ce938caae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=ED=9D=AC=EC=84=A0?= Date: Sun, 15 Dec 2024 19:58:00 +0900 Subject: [PATCH 039/146] =?UTF-8?q?Hotfix:=20=ED=8C=8C=EC=9D=BC=EB=AA=85?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{LocationSelectionModal.tsx => LocationBottmSheet.tsx} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/pages/Home/components/{LocationSelectionModal.tsx => LocationBottmSheet.tsx} (97%) diff --git a/src/pages/Home/components/LocationSelectionModal.tsx b/src/pages/Home/components/LocationBottmSheet.tsx similarity index 97% rename from src/pages/Home/components/LocationSelectionModal.tsx rename to src/pages/Home/components/LocationBottmSheet.tsx index 7025da4..2ab0bb8 100644 --- a/src/pages/Home/components/LocationSelectionModal.tsx +++ b/src/pages/Home/components/LocationBottmSheet.tsx @@ -6,7 +6,7 @@ import Button from '@components/Button/Button'; type LocationItem = string; -const LocationSelectionModal = ({ +const LocationBottomSheet = ({ setSelectedLocation, initialSelectedLocation, }: { @@ -101,4 +101,4 @@ const ListItem = styled.li<{ isSelected: boolean }>` `} `; -export default LocationSelectionModal; +export default LocationBottomSheet; From da0705f6e1fa37f39de3f8e081b9121e4058126c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=ED=9D=AC=EC=84=A0?= Date: Sun, 15 Dec 2024 19:58:31 +0900 Subject: [PATCH 040/146] =?UTF-8?q?Hotfix:=20=ED=8C=8C=EC=9D=BC=EB=AA=85?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20?= =?UTF-8?q?import=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Home/components/LocalDateSelectionModal.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/Home/components/LocalDateSelectionModal.tsx b/src/pages/Home/components/LocalDateSelectionModal.tsx index ad83860..b50d565 100644 --- a/src/pages/Home/components/LocalDateSelectionModal.tsx +++ b/src/pages/Home/components/LocalDateSelectionModal.tsx @@ -9,7 +9,8 @@ import { useState } from 'react'; import DateBottomSheet from './DateBottomSheet'; import { useNavigate } from 'react-router-dom'; import useBottomSheetState from '@store/useBottomSheetStateStore'; -import LocationSelectionModal from './LocationSelectionModal'; + +import LocationBottomSheet from './LocationBottmSheet'; const LocalDateSelectionModal = ({ modalId }: { modalId: number }) => { const [selectedDate, setSelectedDate] = useState({ date: '', time: '' }); @@ -47,7 +48,7 @@ const LocalDateSelectionModal = ({ modalId }: { modalId: number }) => { return `${selectedDateForUi} ${selectedTimeForUi === '00시' ? '' : selectedTimeForUi}`; }; - const handleOpenLocation = () => openBottomSheet(, '지역 선택'); + const handleOpenLocation = () => openBottomSheet(, '지역 선택'); const handleOpenDate = () => openBottomSheet(, ''); return ( From b59b97255f4c041395e0a80bddc78a2085395be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=ED=9D=AC=EC=84=A0?= Date: Sun, 15 Dec 2024 19:59:29 +0900 Subject: [PATCH 041/146] =?UTF-8?q?Type:=20=EB=A9=94=EC=9D=B8=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20=EB=B2=84=ED=8A=BC=20prop=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=ED=83=80=EC=9E=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Home/Home.tsx | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index d815217..b3f6c8b 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -18,6 +18,23 @@ interface IFixedProps { isFixed: boolean; } +export type SortBy = { + VIEW_COUNT: string; + POPULARITY: string; + RATING: string; + REVIEW_COUNT: string; +}; + +export type Options = { + 원본: string; + 주차: string; + 보정: string; + 헤메코: string; + 정장: string; + 탈의실: string; + 파우더룸: string; +}; + const Home = () => { const [searchParams] = useSearchParams(); const [isFixed, setIsFixed] = useState(false); @@ -57,6 +74,9 @@ const Home = () => { openBottomSheet(, '매장정보'); }; + const sortBy: SortBy = { VIEW_COUNT: '조회순', POPULARITY: '인기순', RATING: '평점순', REVIEW_COUNT: '리뷰 많은순' }; + const options: Options = { 보정: '보정', 원본: '원본', 주차: '주차', 헤메코: '헤메코', 정장: '정장', 탈의실: '탈의실', 파우더룸: '파우더룸' }; + return ( <>
@@ -66,9 +86,9 @@ const Home = () => {
-
+
{children}
@@ -69,12 +149,12 @@ const SlideUp = keyframes` } `; -const SheetStyle = (max: number) => css` +const SheetStyle = (sheetHeight: number, moveY: number) => css` position: fixed; z-index: 999; left: 0; - bottom: 0; - max-height: ${max}px; /* 최대 높이 제한 */ + //(초기 위치값 - 드래그한 만큼) + top: ${`calc(100% - ${sheetHeight}px + ${moveY}px)`}; width: 100%; border-radius: 2rem 2rem 0 0; display: flex; @@ -84,6 +164,7 @@ const SheetStyle = (max: number) => css` box-sizing: border-box; background-color: ${variables.colors.white}; animation: ${SlideUp} 0.3s ease-in-out forwards; + transition: transform 0.2s ease-out; `; const SheetHeadStyle = css` @@ -94,6 +175,7 @@ const SheetHeadStyle = css` background-color: ${variables.colors.white}; & h4 { + color: ${variables.colors.gray800}; font-size: 1.6rem; } @@ -102,15 +184,14 @@ const SheetHeadStyle = css` background-image: url('/img/icon-close.svg'); background-position: center; background-repeat: no-repeat; - background-size: 2rem; - width: 2rem; - height: 2rem; + background-size: 1.5rem; + width: 1.5em; + height: 1.5rem; } `; -const SheetContentStyle = (height: number) => css` +const SheetContentStyle = css` width: 100%; - height: ${height}; overflow-y: auto; overflow-x: hidden; `; From 15238b0888e12c7a549df4786d6203860013391a Mon Sep 17 00:00:00 2001 From: kyungmim Date: Tue, 17 Dec 2024 15:39:24 +0900 Subject: [PATCH 044/146] =?UTF-8?q?Edit:=20=EB=B0=94=ED=85=80=EC=8B=9C?= =?UTF-8?q?=ED=8A=B8=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/BottomSheet/BottomSheet.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/BottomSheet/BottomSheet.tsx b/src/components/BottomSheet/BottomSheet.tsx index fa79d47..5f286b6 100644 --- a/src/components/BottomSheet/BottomSheet.tsx +++ b/src/components/BottomSheet/BottomSheet.tsx @@ -9,7 +9,7 @@ const BottomSheet = () => { const { isOpen, children, title, closeBottomSheet } = useBottomSheetState(); const [sheetHeight, setSheetHeight] = useState(0); // 바텀시트 높이 const [translateY, setTranslateY] = useState(0); // 바텀시트 드래그 시 위치 Y값 - const [isDragg, setIsDragg] = useState(false); //드래그 상태값값 + const [isDrag, setIsDrag] = useState(false); //드래그 상 const sheet = useRef(null); // 바텀시트 전체 const content = useRef(null); // 바텀시트 컨텐츠 @@ -56,12 +56,12 @@ const BottomSheet = () => { if (sheet.current && sheet.current.contains(target)) { // 드래그 시작 Y위치값 저장 startY.current = clientY; - setIsDragg(true); + setIsDrag(true); } }; const handleDragMove = (e: TouchEvent | MouseEvent) => { - if (!isDragg || startY.current === null || !sheet.current) return; + if (!isDrag || startY.current === null || !sheet.current) return; //터치&마우스 이벤트 구분하는 코드 const clientY = e instanceof TouchEvent ? e.touches[0].clientY : e.clientY; @@ -75,7 +75,7 @@ const BottomSheet = () => { }; const handleDragEnd = () => { - setIsDragg(false); + setIsDrag(false); // 컨텐츠 3/1 이상 드래그하면 닫기 if (translateY > sheetHeight / 3) { @@ -103,7 +103,7 @@ const BottomSheet = () => { sheet.current.removeEventListener('touchend', handleDragEnd); } }; - }, [isDragg, translateY, sheetHeight, closeBottomSheet]); + }, [isDrag, translateY, sheetHeight, closeBottomSheet]); return ( isOpen && ( From 6317f240377328083f3755cf2400f01824772182 Mon Sep 17 00:00:00 2001 From: kyungmim Date: Tue, 17 Dec 2024 16:56:07 +0900 Subject: [PATCH 045/146] =?UTF-8?q?Feat:=20=EC=8A=A4=ED=8A=9C=EB=94=94?= =?UTF-8?q?=EC=98=A4=20=EB=A9=94=EB=89=B4=20=EB=AA=A9=EB=A1=9D=20UI=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/img/icon-chevronright.svg | 3 + src/pages/Studio/StudioMenu/StudioMenu.tsx | 21 ++++- .../Studio/StudioMenu/StudioMenuItem.tsx | 90 +++++++++++++++++++ 3 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 public/img/icon-chevronright.svg create mode 100644 src/pages/Studio/StudioMenu/StudioMenuItem.tsx diff --git a/public/img/icon-chevronright.svg b/public/img/icon-chevronright.svg new file mode 100644 index 0000000..1beed88 --- /dev/null +++ b/public/img/icon-chevronright.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/pages/Studio/StudioMenu/StudioMenu.tsx b/src/pages/Studio/StudioMenu/StudioMenu.tsx index ffc7acf..1f754d6 100644 --- a/src/pages/Studio/StudioMenu/StudioMenu.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenu.tsx @@ -1,8 +1,27 @@ +/** @jsxImportSource @emotion/react */ +import { css } from '@emotion/react'; +import BackButton from '@components/BackButton/BackButton'; +import StudioNavigator from '@components/Navigator/StudioNavigator'; +import StudioMenuItem from './StudioMenuItem'; import { useParams } from 'react-router-dom'; const StudioMenu = () => { const { _id } = useParams(); - return <>{_id} 스튜디오 메뉴; + return ( + <> + + +
+ +
+ + ); }; export default StudioMenu; + +const ItemLIstStyle = css` + display: flex; + flex-direction: column; + margin-top: 0.4rem; +`; diff --git a/src/pages/Studio/StudioMenu/StudioMenuItem.tsx b/src/pages/Studio/StudioMenu/StudioMenuItem.tsx new file mode 100644 index 0000000..99ff1f7 --- /dev/null +++ b/src/pages/Studio/StudioMenu/StudioMenuItem.tsx @@ -0,0 +1,90 @@ +/** @jsxImportSource @emotion/react */ +import { css } from '@emotion/react'; +import variables from '@styles/Variables'; +import { useNavigate } from 'react-router-dom'; + +const StudioMenuItem = ({ StudioId }: { StudioId: string | undefined }) => { + const navigate = useNavigate(); + return ( + <> +
navigate(`/studio/${StudioId}/menu/1`)}> +
이미지
+
+
+

메뉴 제목

+

메뉴에 대한 간단한 설명이 들어갑니다. 최대 2줄이 들어갑니다. 글자 초과화면 더보기 누르기...

+
+
+

000000원

+ 리뷰00 +
+
+
+ + ); +}; + +export default StudioMenuItem; + +const MeunItemWrapperStyle = css` + display: flex; + gap: 1.4rem; + padding: 1.4rem 0; + box-sizing: border-box; + border-bottom: 0.1rem solid ${variables.colors.gray300}; +`; + +const MenuCoverStyle = css` + max-width: 9.4rem; + width: 100%; + aspect-ratio: 1 / 1.2; + background: #ddd; +`; + +const MenuDescStyle = css` + display: flex; + flex-direction: column; + gap: 1.8rem; +`; + +const MenuHeadStyle = css` + & h4 { + font-size: ${variables.size.medium}; + font-weight: 500; + display: flex; + align-items: center; + margin-bottom: 0.4rem; + + &::after { + content: ''; + display: inline-block; + width: 1.6rem; + height: 1.6rem; + background-image: url(/img/icon-chevronright.svg); + background-position: center; + background-repeat: no-repeat; + } + } + + & p { + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + -webkit-line-clamp: 2; + font-size: 1.2rem; + font-weight: 300; + color: ${variables.colors.gray800}; + } +`; + +const MenuPriceReviewStyle = css` + & p { + font-size: 1.4rem; + font-weight: 700; + } + + & span { + font-size: 1rem; + color: ${variables.colors.gray700}; + } +`; From d145476d628646039998748985febd7407e60f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=ED=9D=AC=EC=84=A0?= Date: Wed, 18 Dec 2024 12:40:16 +0900 Subject: [PATCH 046/146] =?UTF-8?q?Cont:=20reset=20=EC=9E=91=EC=97=85=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Home/Home.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 7b4c947..c11c8c8 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -10,7 +10,7 @@ import StudioList from '@components/Studio/StudioList'; import styled from '@emotion/styled'; import variables from '@styles/Variables'; import { useEffect, useRef, useState } from 'react'; -import { useSearchParams } from 'react-router-dom'; +import { useNavigate, useSearchParams } from 'react-router-dom'; import LocalDateSelectionModal from './components/LocalDateSelectionModal'; import useBottomSheetState from '@store/useBottomSheetStateStore'; @@ -39,7 +39,7 @@ const Home = () => { const [searchParams] = useSearchParams(); const [isFixed, setIsFixed] = useState(false); const homeRef = useRef(null); - + const navigate = useNavigate(); // 스크롤에 따라 Navigator 고정 useEffect(() => { const handleScroll = () => { @@ -74,6 +74,11 @@ const Home = () => { openBottomSheet(, '매장정보'); }; + const handleReset = () => { + console.log('reset'); + navigate('/'); + }; + const sortBy: SortBy = { VIEW_COUNT: '조회순', POPULARITY: '인기순', RATING: '평점순', REVIEW_COUNT: '리뷰 많은순' }; const options: Options = { 보정: '보정', 원본: '원본', 주차: '주차', 헤메코: '헤메코', 정장: '정장', 탈의실: '탈의실', 파우더룸: '파우더룸' }; @@ -85,7 +90,7 @@ const Home = () => { - {isCopied && 복사되었습니다! 🎉} @@ -36,26 +40,44 @@ const containerStyle = css` display: flex; flex-direction: column; justify-content: center; + gap: 1rem; + margin-top: 1rem; `; -const textStyle = css` - font-size: 1.4rem - color: ${variables.colors.gray800}; +const contentRowStyle = css` + display: flex; + align-items: center; + gap: 0.4rem; `; const buttonRowStyle = css` display: flex; align-items: center; - gap: 1rem; + gap: 0.5rem; +`; + +const iconStyle = css` + width: 1.6rem; /* 아이콘 크기 조정 */ + height: 1.6rem; +`; + +const textStyle = css` + font-size: 1.4rem + color: ${variables.colors.gray800}; `; const buttonStyle = css` - padding: 0.6rem 1.2rem; + display: flex; + align-items: center; + line-height: 1; + font-size: 1.2rem; + padding: 0.7rem; + gap: 0.5rem; background-color: ${variables.colors.gray400}; - border-radius: 0.4rem; + border-radius: 2rem; cursor: pointer; - max-width: 5rem; + max-width: 7rem; width: 100%; text-align: center; transition: background-color 0.3s; From 70bfbc2a6aceefaa50f3519fbc91367330e8b9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=ED=9D=AC=EC=84=A0?= Date: Wed, 18 Dec 2024 13:01:32 +0900 Subject: [PATCH 053/146] =?UTF-8?q?Style:=20=EB=B2=84=ED=8A=BC=20=EA=B3=B5?= =?UTF-8?q?=ED=86=B5=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20border=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 --- src/components/Button/Button.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 92ec754..d241240 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -114,13 +114,13 @@ const Button = ({ ${variant === 'white' && ` background-color: ${variables.colors.white}; color:${variables.colors.gray900}; - border: solid${variables.colors.gray400}; + border: 0.1rem solid${variables.colors.gray400}; `} ${variant === 'white' && active && ` background-color: ${variables.colors.primary50}; - border: solid${variables.colors.primary500}; + border: 0.1rem solid${variables.colors.primary500}; color:${variables.colors.gray900}; `} From 049ac53b645c500115f76c8e15917dc5024a48a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=ED=9D=AC=EC=84=A0?= Date: Wed, 18 Dec 2024 13:01:46 +0900 Subject: [PATCH 054/146] =?UTF-8?q?Hotfix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20import=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Home/Home.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index e16c787..a2f1fab 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -11,7 +11,6 @@ import styled from '@emotion/styled'; import variables from '@styles/Variables'; import { useEffect, useRef, useState } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; -import LocalDateSelectionModal from './components/LocalDateSelectionModal'; import useBottomSheetState from '@store/useBottomSheetStateStore'; interface IFixedProps { From 02ea1e2e9bda44b6cb79ac982d11e085b5490185 Mon Sep 17 00:00:00 2001 From: s0zzang Date: Wed, 18 Dec 2024 13:12:35 +0900 Subject: [PATCH 055/146] =?UTF-8?q?Cont:=20masonry=20=EA=B0=81=20=EC=9A=94?= =?UTF-8?q?=EC=86=8C=EC=97=90=20=ED=81=B4=EB=A6=AD=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=84=EB=8B=AC=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?rest=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Masonry/Masonry.tsx | 6 ++++-- src/pages/Studio/StudioPortfolio/StudioPortfolio.tsx | 7 ++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/Masonry/Masonry.tsx b/src/components/Masonry/Masonry.tsx index 84b25e1..e1b524b 100644 --- a/src/components/Masonry/Masonry.tsx +++ b/src/components/Masonry/Masonry.tsx @@ -1,11 +1,13 @@ import { ReactNode } from 'react'; import Masonry, { ResponsiveMasonry } from 'react-responsive-masonry'; -const MasonryList = ({ children, gap = '.2rem', breakPoints = { 300: 2, 1024: 4 } }: { children: ReactNode; gap?: string; breakPoints?: { [key: number]: number } }) => { +const MasonryList = ({ children, gap = '.2rem', breakPoints = { 300: 2, 1024: 4 }, ...rest }: { children: ReactNode; gap?: string; breakPoints?: { [key: number]: number } }) => { return ( <> - {children} + + {children} + ); diff --git a/src/pages/Studio/StudioPortfolio/StudioPortfolio.tsx b/src/pages/Studio/StudioPortfolio/StudioPortfolio.tsx index c66fa0e..c280f9f 100644 --- a/src/pages/Studio/StudioPortfolio/StudioPortfolio.tsx +++ b/src/pages/Studio/StudioPortfolio/StudioPortfolio.tsx @@ -8,13 +8,18 @@ const StudioPortfolio = () => { // 포트폴리오 api 개발 전이라 스튜디오 리스트에서 이미지 추출하여 예시 작성 const { data } = useGetStudios(1, 'filter', searchParams + ''); const datas = data?.content[12].portfolios; + const handleClick = () => { + console.log('click'); + }; return ( <> {datas && ( {datas.map(({ url, studio, id }) => ( - {`${studio}-${id}`} +
+ {`${studio}-${id}`} +
))}
)} From 876c55a54e7f275ea9481751cea91da31732e41b Mon Sep 17 00:00:00 2001 From: s0zzang Date: Wed, 18 Dec 2024 13:16:39 +0900 Subject: [PATCH 056/146] =?UTF-8?q?Style:=20masonry=20=EA=B0=81=20?= =?UTF-8?q?=EC=9A=94=EC=86=8C=EC=97=90=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20-=20=ED=8F=AC=EC=9D=B8=ED=84=B0=20?= =?UTF-8?q?=EC=BB=A4=EC=84=9C,=20=EB=84=88=EB=B9=84=20=EC=A7=80=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Masonry/Masonry.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/Masonry/Masonry.tsx b/src/components/Masonry/Masonry.tsx index e1b524b..5e1afcf 100644 --- a/src/components/Masonry/Masonry.tsx +++ b/src/components/Masonry/Masonry.tsx @@ -1,11 +1,24 @@ +/** @jsxImportSource @emotion/react */ + +import { css } from '@emotion/react'; import { ReactNode } from 'react'; import Masonry, { ResponsiveMasonry } from 'react-responsive-masonry'; const MasonryList = ({ children, gap = '.2rem', breakPoints = { 300: 2, 1024: 4 }, ...rest }: { children: ReactNode; gap?: string; breakPoints?: { [key: number]: number } }) => { + const masonryItem = css` + cursor: pointer; + + img { + width: 100%; + height: auto; + vertical-align: top; + } + `; + return ( <> - + {children} From aa5f94c12e5e6616d1b686f4d1fc252aa4850715 Mon Sep 17 00:00:00 2001 From: kyungmim Date: Wed, 18 Dec 2024 13:22:42 +0900 Subject: [PATCH 057/146] =?UTF-8?q?Feat:=20=EC=8A=A4=ED=8A=9C=EB=94=94?= =?UTF-8?q?=EC=98=A4=20=EB=A9=94=EB=89=B4=20=EB=AA=A9=EB=A1=9D=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=ED=86=B5=EC=8B=A0=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Studio/StudioMenu/StudioMenu.tsx | 27 ++++++++++++-- .../Studio/StudioMenu/StudioMenuItem.tsx | 19 ++++++---- src/types/types.ts | 35 +++++++++++++++++++ 3 files changed, 71 insertions(+), 10 deletions(-) diff --git a/src/pages/Studio/StudioMenu/StudioMenu.tsx b/src/pages/Studio/StudioMenu/StudioMenu.tsx index 1f754d6..54c1959 100644 --- a/src/pages/Studio/StudioMenu/StudioMenu.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenu.tsx @@ -4,16 +4,37 @@ import BackButton from '@components/BackButton/BackButton'; import StudioNavigator from '@components/Navigator/StudioNavigator'; import StudioMenuItem from './StudioMenuItem'; import { useParams } from 'react-router-dom'; +import { useEffect, useState } from 'react'; +import { IMenuListRes } from 'types/types'; const StudioMenu = () => { const { _id } = useParams(); + const [data, setData] = useState(); + + const fetchMeun = async () => { + const res = await fetch(`${import.meta.env.VITE_TOUCHEESE_API}/studio/detail/${_id}/menu`, { + method: 'GET', + }); + + if (!res.ok) { + console.error('Failed to fetch data'); + } + + const data = await res.json(); + + setData(data); + }; + + useEffect(() => { + fetchMeun(); + }, []); + + const StudioMenuList = data?.map((item) => ); return ( <> -
- -
+
{StudioMenuList}
); }; diff --git a/src/pages/Studio/StudioMenu/StudioMenuItem.tsx b/src/pages/Studio/StudioMenu/StudioMenuItem.tsx index 99ff1f7..b315502 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuItem.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuItem.tsx @@ -2,21 +2,25 @@ import { css } from '@emotion/react'; import variables from '@styles/Variables'; import { useNavigate } from 'react-router-dom'; +import { IMenuListRes } from 'types/types'; -const StudioMenuItem = ({ StudioId }: { StudioId: string | undefined }) => { +const StudioMenuItem = ({ StudioId, data }: { StudioId: string | undefined; data: IMenuListRes | undefined }) => { const navigate = useNavigate(); + return ( <> -
navigate(`/studio/${StudioId}/menu/1`)}> -
이미지
+
navigate(`/studio/${StudioId}/menu/${data?.id}`)}> +
+ 메뉴 대표 사진 +
-

메뉴 제목

-

메뉴에 대한 간단한 설명이 들어갑니다. 최대 2줄이 들어갑니다. 글자 초과화면 더보기 누르기...

+

{data?.name}

+

{data?.description}

-

000000원

- 리뷰00 +

{data?.price.toLocaleString('ko-KR')}원

+ 리뷰 {data?.reviewCount}
@@ -32,6 +36,7 @@ const MeunItemWrapperStyle = css` padding: 1.4rem 0; box-sizing: border-box; border-bottom: 0.1rem solid ${variables.colors.gray300}; + cursor: pointer; `; const MenuCoverStyle = css` diff --git a/src/types/types.ts b/src/types/types.ts index 0a452e7..c2da60d 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -82,3 +82,38 @@ export interface IStudioRes { first: boolean; empty: boolean; } + +export interface IAdditionalOptionsRes { + createTime: string | null; + description: string; + duration: string | null; + id: number; + menu: string; + menuId: number; + name: string; + price: number; + updateTime: string | null; +} + +export interface IMenuImagesRes { + created_at: string | null; + id: number; + menu: string; + menuId: number; + updated_at: string | null; + url: string; +} +export interface IMenuListRes { + additionalOptions: IAdditionalOptionsRes[]; + menuImages: IMenuImagesRes[]; + description: string; + duration: string | null; + id: number; + name: string; + price: number; + reviewCount: number; + studioId: number; + studioName: string; + created_at: string | null; + updated_at: string | null; +} From 25334cec2a9944f4ecbe76c1d4a18a9f17d8c67e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=EB=AF=BC?= Date: Wed, 18 Dec 2024 13:37:40 +0900 Subject: [PATCH 058/146] =?UTF-8?q?Feat:=20=ED=97=A4=EB=8D=94=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Header/Header.tsx | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/components/Header/Header.tsx diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx new file mode 100644 index 0000000..dfb1ad2 --- /dev/null +++ b/src/components/Header/Header.tsx @@ -0,0 +1,30 @@ +/** @jsxImportSource @emotion/react */ +import BackButton from '@components/BackButton/BackButton'; +import { css } from '@emotion/react'; +import { TypoTitleXsM } from '@styles/Common'; + +interface HeaderProps { + title?: string; + backTo?: string; +} + +const Header = ({ title, backTo }: HeaderProps) => { + return ( +
+ +

{title}

+
+ ); +}; + +export default Header; + +const headerStyle = css` + display: flex; + align-items: center; + padding-bottom: 1.4rem; +`; + +const additionalStyle = css` + margin-left: 1rem; +`; From f14015213c784695d759b37576c9ce6ce224333a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=EB=AF=BC?= Date: Wed, 18 Dec 2024 14:00:08 +0900 Subject: [PATCH 059/146] =?UTF-8?q?Style:=20=ED=97=A4=EB=8D=94=20customSty?= =?UTF-8?q?le=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Header/Header.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index dfb1ad2..993f939 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -1,18 +1,19 @@ /** @jsxImportSource @emotion/react */ import BackButton from '@components/BackButton/BackButton'; -import { css } from '@emotion/react'; +import { css, SerializedStyles } from '@emotion/react'; import { TypoTitleXsM } from '@styles/Common'; interface HeaderProps { title?: string; backTo?: string; + customStyle?: SerializedStyles; } -const Header = ({ title, backTo }: HeaderProps) => { +const Header = ({ title, backTo, customStyle }: HeaderProps) => { return ( -
+
-

{title}

+ {title &&

{title}

}
); }; @@ -25,6 +26,6 @@ const headerStyle = css` padding-bottom: 1.4rem; `; -const additionalStyle = css` +const additionalH1Style = css` margin-left: 1rem; `; From 9768a8aa0dd4945cd0aea2b0d922914de3f2bc14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=EB=AF=BC?= Date: Wed, 18 Dec 2024 14:35:28 +0900 Subject: [PATCH 060/146] =?UTF-8?q?Style:=20=EC=A7=80=EB=8F=84=20=EB=A7=88?= =?UTF-8?q?=EC=BB=A4=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/img/icon-map-pin.svg | 20 ++++++++++++++++++++ src/components/Kakao/KakaoMap.tsx | 9 ++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 public/img/icon-map-pin.svg diff --git a/public/img/icon-map-pin.svg b/public/img/icon-map-pin.svg new file mode 100644 index 0000000..1199f17 --- /dev/null +++ b/public/img/icon-map-pin.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Kakao/KakaoMap.tsx b/src/components/Kakao/KakaoMap.tsx index 7693d8d..b7fd527 100644 --- a/src/components/Kakao/KakaoMap.tsx +++ b/src/components/Kakao/KakaoMap.tsx @@ -58,7 +58,14 @@ const KakaoMap = ({ addressSi, addressGu, address }: KakaoMapProps) => { <>
- +
From 7d95c5af725e05569aa9679920c55b3170294480 Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Wed, 18 Dec 2024 14:47:16 +0900 Subject: [PATCH 061/146] =?UTF-8?q?Asset:=20White=20=EB=8B=AB=EA=B8=B0=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=EC=BD=98=20=EC=B6=94=EA=B0=80=20&=20Gray500?= =?UTF-8?q?=20=EB=8B=AB=EA=B8=B0=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EB=B3=80=EA=B2=BD=EC=9C=BC=EB=A1=9C=20=EC=9D=B8?= =?UTF-8?q?=ED=95=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/img/{icon-close.svg => icon-close-gray500.svg} | 0 public/img/icon-close-white.svg | 3 +++ src/components/BottomSheet/BottomSheet.tsx | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) rename public/img/{icon-close.svg => icon-close-gray500.svg} (100%) create mode 100644 public/img/icon-close-white.svg diff --git a/public/img/icon-close.svg b/public/img/icon-close-gray500.svg similarity index 100% rename from public/img/icon-close.svg rename to public/img/icon-close-gray500.svg diff --git a/public/img/icon-close-white.svg b/public/img/icon-close-white.svg new file mode 100644 index 0000000..7ef5310 --- /dev/null +++ b/public/img/icon-close-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/BottomSheet/BottomSheet.tsx b/src/components/BottomSheet/BottomSheet.tsx index 348ab7b..1af7f86 100644 --- a/src/components/BottomSheet/BottomSheet.tsx +++ b/src/components/BottomSheet/BottomSheet.tsx @@ -99,7 +99,7 @@ const SheetHeadStyle = css` & button { display: block; - background-image: url('/img/icon-close.svg'); + background-image: url('/img/icon-close-gray500.svg'); background-position: center; background-repeat: no-repeat; background-size: 2rem; From 0dc023ac86e85001243e369a48c6edf4c7e1da9b Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Wed, 18 Dec 2024 14:47:50 +0900 Subject: [PATCH 062/146] =?UTF-8?q?Refactor:=20=EB=A6=AC=EB=B7=B0=EC=9A=A9?= =?UTF-8?q?=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20Swiper=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ImageSwiper/ImageSwiper.tsx | 21 +++++++++++++++++++-- src/components/Studio/StudioItem.tsx | 20 ++------------------ src/types/types.ts | 8 ++++++++ 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/components/ImageSwiper/ImageSwiper.tsx b/src/components/ImageSwiper/ImageSwiper.tsx index 33fcbf5..4982aa9 100644 --- a/src/components/ImageSwiper/ImageSwiper.tsx +++ b/src/components/ImageSwiper/ImageSwiper.tsx @@ -4,12 +4,29 @@ import { Swiper, SwiperSlide, SwiperProps } from 'swiper/react'; import 'swiper/css'; import { Mousewheel } from 'swiper/modules'; import variables from '@styles/Variables'; +import { IPortfolio, IReviewImages } from 'types/types'; interface ImageSwiperProps extends SwiperProps { - images: string[]; + images: IPortfolio[] | IReviewImages[]; } const ImageSwiper = ({ images, modules = [Mousewheel], mousewheel = { forceToAxis: true, sensitivity: 1 }, spaceBetween = 3, slidesPerView = 4, ...props }: ImageSwiperProps) => { + // 이미지 5개 불러오기 + const getImages = (photos: IPortfolio[] | IReviewImages[]) => { + let images: string[] = []; + const porfolios = photos.slice(0, 5); + + if (porfolios.length) { + porfolios.forEach((photo: IPortfolio | IReviewImages) => { + images.push(photo.url); + }); + } else { + images.push('/img/img-nopic.png'); + } + + return images; + }; + return ( - {images.map((image, index) => ( + {getImages(images).map((image, index) => ( {`이미지 diff --git a/src/components/Studio/StudioItem.tsx b/src/components/Studio/StudioItem.tsx index 0b914d2..3cc8287 100644 --- a/src/components/Studio/StudioItem.tsx +++ b/src/components/Studio/StudioItem.tsx @@ -5,7 +5,7 @@ import styled from '@emotion/styled'; import { TypoTitleSmS } from '@styles/Common'; import variables from '@styles/Variables'; import { useNavigate } from 'react-router-dom'; -import { IMenus, IPortfolio, IStudioItem } from 'types/types'; +import { IMenus, IStudioItem } from 'types/types'; const StudioItem = ({ item, isFirst, isLast }: { item: IStudioItem; isFirst: boolean; isLast: boolean }) => { const navigate = useNavigate(); @@ -26,25 +26,9 @@ const StudioItem = ({ item, isFirst, isLast }: { item: IStudioItem; isFirst: boo return minPrice; }; - // 이미지 5개 불러오기 - const getImages = (portfolio: IPortfolio[]) => { - let images: string[] = []; - const porfolios = portfolio.slice(0, 5); - - if (porfolios.length) { - porfolios.forEach((portfolio: IPortfolio) => { - images.push(portfolio.url); - }); - } else { - images.push('/img/img-nopic.png'); - } - - return images; - }; - return ( - + diff --git a/src/types/types.ts b/src/types/types.ts index 0a452e7..1994c5d 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -9,6 +9,14 @@ export interface IPortfolio { updated_at: null | string; } +export interface IReviewImages { + id: number; + reviewId: number; + url: string; + created_at: string; + updated_at: string; +} + export interface IMenus { id: number; studio: string; From 85623b64016e351c1e2ad4729c732b94af744363 Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Wed, 18 Dec 2024 14:48:02 +0900 Subject: [PATCH 063/146] =?UTF-8?q?Cont:=20=EB=94=A4=20=EB=AA=A8=EB=8B=AC?= =?UTF-8?q?=20=EC=9E=91=EC=97=85=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Modal/Modal.tsx | 69 +++++++++++++------ .../components/LocalDateSelectionModal.tsx | 8 +-- src/pages/Studio/StudioDimTest.tsx | 28 ++++++++ src/pages/Studio/components/DimmedModal.tsx | 18 +++++ src/routes.tsx | 3 + 5 files changed, 100 insertions(+), 26 deletions(-) create mode 100644 src/pages/Studio/StudioDimTest.tsx create mode 100644 src/pages/Studio/components/DimmedModal.tsx diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index 06d1296..5de202c 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -3,18 +3,30 @@ import Button from '@components/Button/Button'; import styled from '@emotion/styled'; import useModal from '@hooks/useModal'; -import { Hidden, TypoTitleSmS } from '@styles/Common'; +import { Hidden, TypoBodyMdR, TypoTitleSmS } from '@styles/Common'; import variables from '@styles/Variables'; interface ModalProp { - title: string; + type: 'default' | 'dimmed' | 'fullscreen'; + title?: string; children: JSX.Element | string; modalId?: number; - size?: string; withBtn?: boolean; buttons?: { text: string; event: () => void }[]; } +interface IModalStyle { + type: 'default' | 'dimmed' | 'fullscreen'; +} + +interface ITitleStyle { + type: 'dimmed' | 'fullscreen'; +} + +interface ICloseBtnStyle { + mode: 'dimmed' | 'fullscreen'; +} + /** * * 모달 사용 방법 * 1. import `useModal` : `const modal = useModal()` @@ -23,21 +35,32 @@ interface ModalProp { * - 태그 전달 : `

모달2

` * - 모달 내 버튼 : {text: string, event: MouseEventHandler}[] */ -const Modal = ({ modalId = 1, size = 'default', title, children, withBtn = true, buttons = [] }: ModalProp) => { +const Modal = ({ modalId = 1, type = 'default', title, children, withBtn = true, buttons = [] }: ModalProp) => { const { isOpen, close } = useModal(modalId); const handleClose = () => close(); return ( isOpen && ( - - - {isOpen} - - 모달 닫기 - -

{title}

-
- + + {/* FullScreen 모달 헤더 */} + {type === 'fullscreen' && ( + + {isOpen} + + 모달 닫기 + + {title &&

{title}

} +
+ )} + {/* Dim 처리 모달 헤더 */} + {type === 'dimmed' && ( + + {title &&

{title}

} + + 모달 닫기 + +
+ )} {children} {withBtn && {buttons?.map((btn) => + + + + + ); +}; + +export default StudioDimTest; diff --git a/src/pages/Studio/components/DimmedModal.tsx b/src/pages/Studio/components/DimmedModal.tsx new file mode 100644 index 0000000..5721f5d --- /dev/null +++ b/src/pages/Studio/components/DimmedModal.tsx @@ -0,0 +1,18 @@ +import Modal from '@components/Modal/Modal'; +import styled from '@emotion/styled'; + +const DimmedModal = () => { + return ( + <> + + 모달 + + + ); +}; + +const DimmedModalStyle = styled.div` + box-shadow: inset 0 0 10px yellow; +`; + +export default DimmedModal; diff --git a/src/routes.tsx b/src/routes.tsx index cd0cc50..27e11f9 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -1,6 +1,7 @@ import Home from '@pages/Home/Home'; import Search from '@pages/search/Search'; import SearchResults from '@pages/search/SearchResult'; +import StudioDimTest from '@pages/Studio/StudioDimTest'; import StudioMain from '@pages/Studio/StudioMain/StudioMain'; import StudioMenu from '@pages/Studio/StudioMenu/StudioMenu'; import StudioMenuDetail from '@pages/Studio/StudioMenu/StudioMenuDetail'; @@ -34,6 +35,8 @@ const router = createBrowserRouter([ ], }, { path: 'portfolio', element: }, + // 추후 삭제 예정 + { path: 'dimtest', element: }, { path: 'review', children: [ From 770a45facb143ad19dad620604265ac12e74bb32 Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Wed, 18 Dec 2024 15:17:36 +0900 Subject: [PATCH 064/146] =?UTF-8?q?Hotfix:=20Home=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0=20&=20=EC=9D=B4?= =?UTF-8?q?=EB=AF=B8=EC=A7=80=20Swiper=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ImageSwiper/ImageSwiper.tsx | 58 +++++++++++++++++----- src/pages/Home/Home.tsx | 4 +- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/components/ImageSwiper/ImageSwiper.tsx b/src/components/ImageSwiper/ImageSwiper.tsx index 4982aa9..cc67768 100644 --- a/src/components/ImageSwiper/ImageSwiper.tsx +++ b/src/components/ImageSwiper/ImageSwiper.tsx @@ -2,15 +2,25 @@ import { css } from '@emotion/react'; import { Swiper, SwiperSlide, SwiperProps } from 'swiper/react'; import 'swiper/css'; -import { Mousewheel } from 'swiper/modules'; +import { Autoplay, Mousewheel, Pagination } from 'swiper/modules'; import variables from '@styles/Variables'; import { IPortfolio, IReviewImages } from 'types/types'; interface ImageSwiperProps extends SwiperProps { images: IPortfolio[] | IReviewImages[]; + imageStyle?: ReturnType; } -const ImageSwiper = ({ images, modules = [Mousewheel], mousewheel = { forceToAxis: true, sensitivity: 1 }, spaceBetween = 3, slidesPerView = 4, ...props }: ImageSwiperProps) => { +const ImageSwiper = ({ + images, + modules = [Mousewheel, Pagination, Autoplay], + mousewheel = { forceToAxis: true, sensitivity: 1 }, + spaceBetween = 3, + slidesPerView = 3.6, + imageStyle, + ...props +}: ImageSwiperProps) => { + const isPaginationActive = slidesPerView === 1; // 이미지 5개 불러오기 const getImages = (photos: IPortfolio[] | IReviewImages[]) => { let images: string[] = []; @@ -29,21 +39,18 @@ const ImageSwiper = ({ images, modules = [Mousewheel], mousewheel = { forceToAxi return ( {getImages(images).map((image, index) => ( - {`이미지 + {`이미지 ))} @@ -52,8 +59,35 @@ const ImageSwiper = ({ images, modules = [Mousewheel], mousewheel = { forceToAxi export default ImageSwiper; -const imageStyle = css` - width: 9.4rem; - height: 11.8rem; +const swiperStyle = css` + width: calc(100% + ${variables.layoutPadding}); + margin-right: ${variables.layoutPadding}; + margin-bottom: 1.4rem; + .swiper-pagination { + position: absolute; + bottom: 15px; + width: 100%; + display: flex; + justify-content: center; + z-index: 10; + } + .swiper-pagination-bullet { + background-color: ${variables.colors.white}; + opacity: 0.8; + width: 20px; + height: 3px; + transition: all 0.3s ease; + margin: 0 1px; + cursor: pointer; + } + .swiper-pagination-bullet-active { + background-color: ${variables.colors.black}; + opacity: 1; + } +`; + +const defaultImageStyle = css` + width: 100%; + height: auto; object-fit: cover; `; diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 4fa282e..23ffe06 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -11,7 +11,7 @@ import styled from '@emotion/styled'; import useBottomSheetState from '@store/useBottomSheetStateStore'; import variables from '@styles/Variables'; import { useEffect, useRef, useState } from 'react'; -import { useSearchParams } from 'react-router-dom'; +import { useNavigate, useSearchParams } from 'react-router-dom'; interface IFixedProps { isFixed: boolean; @@ -87,7 +87,7 @@ const Home = () => { - + ); }; diff --git a/src/pages/Studio/components/DimmedModal.tsx b/src/pages/Studio/components/DimmedModal.tsx index 5721f5d..b10bc41 100644 --- a/src/pages/Studio/components/DimmedModal.tsx +++ b/src/pages/Studio/components/DimmedModal.tsx @@ -1,18 +1,30 @@ +/** @jsxImportSource @emotion/react */ import Modal from '@components/Modal/Modal'; +import { css } from '@emotion/react'; import styled from '@emotion/styled'; +import { Swiper } from 'swiper/react'; -const DimmedModal = () => { +const DimmedModal = ({ children }: { children: React.ReactNode }) => { return ( <> - 모달 + + + {children} + + ); }; const DimmedModalStyle = styled.div` - box-shadow: inset 0 0 10px yellow; + width: 100%; + display: flex; +`; + +const swiperStyle = css` + color: white; `; export default DimmedModal; From 6e35716163835a14eb6295f58e5adf93cb8c4cf2 Mon Sep 17 00:00:00 2001 From: kyungmim Date: Wed, 18 Dec 2024 21:58:02 +0900 Subject: [PATCH 082/146] =?UTF-8?q?Cont:=20=EB=A9=94=EB=89=B4=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20UI=EC=9E=91=EC=97=85?= =?UTF-8?q?=EC=A4=91=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/img/icon-camera-roll.svg | 3 + public/img/icon-check-white.svg | 3 + public/img/icon-crop.svg | 10 + public/img/icon-folder.svg | 3 + .../Studio/StudioMenu/StudioMenuDetail.tsx | 74 ++++++- .../StudioMenu/StudioMenuDetailInfo.tsx | 200 ++++++++++++++++++ 6 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 public/img/icon-camera-roll.svg create mode 100644 public/img/icon-check-white.svg create mode 100644 public/img/icon-crop.svg create mode 100644 public/img/icon-folder.svg create mode 100644 src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx diff --git a/public/img/icon-camera-roll.svg b/public/img/icon-camera-roll.svg new file mode 100644 index 0000000..e1c3638 --- /dev/null +++ b/public/img/icon-camera-roll.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/img/icon-check-white.svg b/public/img/icon-check-white.svg new file mode 100644 index 0000000..de535cd --- /dev/null +++ b/public/img/icon-check-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/img/icon-crop.svg b/public/img/icon-crop.svg new file mode 100644 index 0000000..d51d448 --- /dev/null +++ b/public/img/icon-crop.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/img/icon-folder.svg b/public/img/icon-folder.svg new file mode 100644 index 0000000..b010df8 --- /dev/null +++ b/public/img/icon-folder.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx index 565c30c..3adeae0 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx @@ -1,8 +1,80 @@ +/** @jsxImportSource @emotion/react */ +import { css } from '@emotion/react'; +import variables from '@styles/Variables'; +import Header from '@components/Header/Header'; import { useParams } from 'react-router-dom'; +import { TypoBodyMdM, TypoTitleSmS } from '@styles/Common'; +import StudioMenuDetailInfo from './StudioMenuDetailInfo'; const StudioMenuDetail = () => { const { _menuId } = useParams(); - return <>스튜디오 메뉴 {_menuId} 상세; + + return ( + <> +
+
+

사진 메뉴 이름

+

사진메뉴설명입니다유유유유유유유

+
+ +
    +
  • 정보
  • +
  • 리뷰 00
  • +
+ + + + ); }; export default StudioMenuDetail; + +const MenuDescStyle = css` + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1.8rem 0; + + & h2 { + ${TypoTitleSmS} + } + + & p { + ${TypoBodyMdM}; + color: ${variables.colors.gray800}; + } +`; + +const TapMenuStyle = css` + color: ${variables.colors.gray800}; + display: flex; + width: 100%; + text-align: center; + + & li { + cursor: pointer; + ${TypoBodyMdM}; + position: relative; + padding: 1rem 0; + text-align: center; + flex-grow: 1; + + &::before { + content: ''; + position: absolute; + background-color: ${variables.colors.gray300}; + height: 0.2rem; + left: 0; + right: 0; + bottom: 0; + } + + &.active { + color: ${variables.colors.black}; + } + + &.active::before { + background-color: ${variables.colors.black}; + } + } +`; diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx new file mode 100644 index 0000000..10fcdc8 --- /dev/null +++ b/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx @@ -0,0 +1,200 @@ +/** @jsxImportSource @emotion/react */ +import { css } from '@emotion/react'; +import variables from '@styles/Variables'; +import Button from '@components/Button/Button'; +import { TypoBodyMdM, TypoBodyMdR, TypoCapSmR, TypoTitleSmS, TypoTitleXsM } from '@styles/Common'; + +const StudioMenuDetailInfo = () => { + return ( + <> +
+
+

예상 소요 시간

+

약 60분

+
+
+

기본 촬영 수

+

70-80컷

+
+
+

인화 사이즈

+

4x6in

+
+
+

기본 제공 파일

+

3포즈 리터칭 JPG파일

+
+
+ +
+

기본 가격

+

900000원

+
+ +
+

추가 옵션

+ +
+
+
+ +
+ +

+30,000원

+
+ +
+
+ +
+ +

+10,000원

+
+
+
+ +
+
+ 총 결제금액 +

70,000원

+
+ +
+ + ); +}; + +export default StudioMenuDetailInfo; + +const MenuInfoStyle = css` + display: flex; + flex-direction: column; + gap: 0.6rem; + padding: 1.4rem 0; + + .menuInfoItem { + display: flex; + justify-content: space-between; + gap: 1rem; + ${TypoBodyMdR} + + & h4 { + color: ${variables.colors.gray800}; + background-position: left; + padding-left: 2rem; + background-repeat: no-repeat; + background-size: 1.6rem; + + &.time { + background-image: url(/img/icon-time.svg); + } + &.camera { + background-image: url(/img/icon-camera-roll.svg); + } + &.crop { + background-image: url(/img/icon-crop.svg); + } + &.folder { + background-image: url(/img/icon-folder.svg); + } + } + } +`; + +const TotalPriceStyle = css` + display: flex; + justify-content: space-between; + gap: 1rem; + padding: 1.8rem 0; + ${TypoTitleXsM} + position: relative; + + &::before { + content: ''; + display: block; + position: absolute; + left: -1.6rem; + right: -1.6rem; + bottom: -1rem; + height: 1rem; + background-color: ${variables.colors.gray300}; + } +`; + +const AddOptionsWrapperStyle = css` + display: flex; + flex-direction: column; + margin: 1rem 0 2rem; + + & h3 { + padding: 1.8rem 0; + ${TypoTitleXsM} + } + + .optionList { + display: flex; + flex-direction: column; + padding: 1.2rem 0; + } +`; + +const AddOptionsListStyle = css` + display: flex; + flex-direction: column; + + & fieldset { + display: flex; + gap: 1rem; + padding: 1.2rem 0; + height: 4.4rem; + + .customCheckbox { + width: 1.8rem; + height: 1.8rem; + border: 0.2rem solid ${variables.colors.gray600}; + border-radius: 0.4rem; + } + + label { + ${TypoBodyMdR} + margin-right: auto; + } + + p { + ${TypoBodyMdM} + } + } +`; + +const FixedBtnBoxStyle = css` + display: flex; + gap: 2rem; + align-items: center; + position: fixed; + left: 0; + right: 0; + bottom: 0; + background-color: ${variables.colors.white}; + padding: 2rem 1.6rem; + border-top: 0.1rem solid ${variables.colors.gray300}; + + .totalPrice { + display: flex; + flex-direction: column; + min-width: 10rem; + + & span { + ${TypoCapSmR} + color: ${variables.colors.gray600}; + } + + & p { + ${TypoTitleSmS} + } + } +`; From ef2f51a3d2162987b10f0260dd95b21feb1eb589 Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Thu, 19 Dec 2024 12:35:57 +0900 Subject: [PATCH 083/146] =?UTF-8?q?Remove:=20=EB=94=A4=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Studio/StudioDimTest.tsx | 220 ----------------------------- src/routes.tsx | 3 - 2 files changed, 223 deletions(-) delete mode 100644 src/pages/Studio/StudioDimTest.tsx diff --git a/src/pages/Studio/StudioDimTest.tsx b/src/pages/Studio/StudioDimTest.tsx deleted file mode 100644 index 6d5702d..0000000 --- a/src/pages/Studio/StudioDimTest.tsx +++ /dev/null @@ -1,220 +0,0 @@ -import useModal from '@hooks/useModal'; -import { SwiperSlide } from 'swiper/react'; -import DimmedModal from './components/DimmedModal'; - -const StudioDimTest = () => { - const { open } = useModal(1); - - const handleClick = () => { - console.log('클릭'); - open(); - }; - - const items = [ - { - id: 530, - userId: 1, - userName: '김멋사', - menuId: 71, - menuName: '증명사진', - content: '촬영 분위기가 편안해서 자연스러운 사진을 얻을 수 있었습니다.', - rating: 3, - reviewImages: [ - { - id: 1285, - reviewId: 530, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - ], - created_at: '2024-12-13T05:05:39', - updated_at: '2024-12-13T05:05:39', - imageExists: true, - }, - { - id: 531, - userId: 1, - userName: '김멋사', - menuId: 71, - menuName: '증명사진', - content: '스텝분들이 친절하고 프로페셔널하셔서 마음에 들었어요.', - rating: 3, - reviewImages: [ - { - id: 1286, - reviewId: 531, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - { - id: 1287, - reviewId: 531, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - { - id: 1288, - reviewId: 531, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - ], - created_at: '2024-12-13T05:05:39', - updated_at: '2024-12-13T05:05:39', - imageExists: true, - }, - { - id: 532, - userId: 1, - userName: '김멋사', - menuId: 71, - menuName: '증명사진', - content: '예약부터 촬영까지 모든 과정이 매끄럽게 진행됐습니다.', - rating: 4, - reviewImages: [ - { - id: 1289, - reviewId: 532, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - { - id: 1290, - reviewId: 532, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - { - id: 1291, - reviewId: 532, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - { - id: 1292, - reviewId: 532, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - { - id: 1293, - reviewId: 532, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - ], - created_at: '2024-12-13T05:05:39', - updated_at: '2024-12-13T05:05:39', - imageExists: true, - }, - { - id: 533, - userId: 1, - userName: '김멋사', - menuId: 71, - menuName: '증명사진', - content: '사진 수정 요청도 친절히 대응해주셔서 기분이 좋았어요.', - rating: 3, - reviewImages: [ - { - id: 1294, - reviewId: 533, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - { - id: 1295, - reviewId: 533, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - { - id: 1296, - reviewId: 533, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - { - id: 1297, - reviewId: 533, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - { - id: 1298, - reviewId: 533, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - ], - created_at: '2024-12-13T05:05:39', - updated_at: '2024-12-13T05:05:39', - imageExists: true, - }, - { - id: 534, - userId: 1, - userName: '김멋사', - menuId: 71, - menuName: '증명사진', - content: '사진 수정 요청도 친절히 대응해주셔서 기분이 좋았어요.', - rating: 5, - reviewImages: [ - { - id: 1299, - reviewId: 534, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - { - id: 1300, - reviewId: 534, - url: 'https://picsum.photos/600/400', - created_at: '2024-12-13T05:29:35.219594', - updated_at: '2024-12-13T05:29:35.219594', - }, - ], - created_at: '2024-12-13T05:05:39', - updated_at: '2024-12-13T05:05:39', - imageExists: true, - }, - ]; - - const slides = items.map((item) => ( - -
{item.content}
-
- )); - - return ( - <> - - - - - - ); -}; - -export default StudioDimTest; diff --git a/src/routes.tsx b/src/routes.tsx index 27e11f9..cd0cc50 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -1,7 +1,6 @@ import Home from '@pages/Home/Home'; import Search from '@pages/search/Search'; import SearchResults from '@pages/search/SearchResult'; -import StudioDimTest from '@pages/Studio/StudioDimTest'; import StudioMain from '@pages/Studio/StudioMain/StudioMain'; import StudioMenu from '@pages/Studio/StudioMenu/StudioMenu'; import StudioMenuDetail from '@pages/Studio/StudioMenu/StudioMenuDetail'; @@ -35,8 +34,6 @@ const router = createBrowserRouter([ ], }, { path: 'portfolio', element: }, - // 추후 삭제 예정 - { path: 'dimtest', element: }, { path: 'review', children: [ From 7bdb9eef460111b50bfa69e66b49ffc7424cbd0f Mon Sep 17 00:00:00 2001 From: s0zzang Date: Thu, 19 Dec 2024 12:51:30 +0900 Subject: [PATCH 084/146] =?UTF-8?q?Cont:=20=ED=8F=AC=ED=8A=B8=ED=8F=B4?= =?UTF-8?q?=EB=A6=AC=EC=98=A4=20=EC=A7=84=ED=96=89=EC=A4=91=20(=ED=8F=AC?= =?UTF-8?q?=ED=8A=B8=ED=8F=B4=EB=A6=AC=EC=98=A4=20API=20-=20=EB=A9=94?= =?UTF-8?q?=EB=89=B4=EB=AA=85=20=EC=B6=94=EA=B0=80=EB=90=98=EB=A9=B4=20?= =?UTF-8?q?=EC=9E=AC=EC=B0=A9=EC=88=98=20=EC=98=88=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../StudioPortfolio/StudioPortfolio.tsx | 78 +++++++++++++++---- 1 file changed, 62 insertions(+), 16 deletions(-) diff --git a/src/pages/Studio/StudioPortfolio/StudioPortfolio.tsx b/src/pages/Studio/StudioPortfolio/StudioPortfolio.tsx index c280f9f..aa384f8 100644 --- a/src/pages/Studio/StudioPortfolio/StudioPortfolio.tsx +++ b/src/pages/Studio/StudioPortfolio/StudioPortfolio.tsx @@ -1,30 +1,76 @@ -import { useGetStudios } from '@hooks/useGetStudios'; -import { useSearchParams } from 'react-router-dom'; +import Button from '@components/Button/Button'; +import Header from '@components/Header/Header'; import MasonryList from '@components/Masonry/Masonry'; +import EmptyMessage from '@components/Message/EmptyMessage'; +import StudioNavigator from '@components/Navigator/StudioNavigator'; +import styled from '@emotion/styled'; +import { useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { IPortfolio } from 'types/types'; const StudioPortfolio = () => { - const [searchParams] = useSearchParams(); - - // 포트폴리오 api 개발 전이라 스튜디오 리스트에서 이미지 추출하여 예시 작성 - const { data } = useGetStudios(1, 'filter', searchParams + ''); - const datas = data?.content[12].portfolios; + const [data, setData] = useState([]); + const [studioName, setStudioName] = useState(''); + const { _id } = useParams() as { _id: string }; const handleClick = () => { console.log('click'); }; + const fetchPortfolio = async () => { + try { + const response = await fetch(`${import.meta.env.VITE_TOUCHEESE_API}/studio/detail/${_id}/portfolio`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + const data = await response.json(); + setData(data.content); + } catch (err) { + console.error('Failed to fetch data'); + } + }; + + useEffect(() => { + fetchPortfolio(); + if (data.length) setStudioName(data[0].studio); + }, []); + return ( <> - {datas && ( - - {datas.map(({ url, studio, id }) => ( -
- {`${studio}-${id}`} -
- ))} -
- )} +
+ + + +
  • +
  • +
    + + + {data && data.length ? ( + + {data.map(({ url, studio, id }) => ( +
    + {`${studio}-${id}`} +
    + ))} +
    + ) : ( + + )} +
    ); }; export default StudioPortfolio; + +const FilterBoxStyle = styled.ul` + display: flex; + margin: 1.2rem 0; +`; + +const ListStyle = styled.div` + min-height: 50vh; +`; From 90867ac372b4106f60cdf4f4ec3082aedde4e992 Mon Sep 17 00:00:00 2001 From: kyungmim Date: Thu, 19 Dec 2024 12:53:32 +0900 Subject: [PATCH 085/146] =?UTF-8?q?Feat:=20=EB=A9=94=EB=89=B4=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=83=AD=EB=A9=94?= =?UTF-8?q?=EB=89=B4=20=EA=B5=AC=ED=98=84=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Studio/StudioMenu/StudioMenuDetail.tsx | 20 +++++++++++++------ .../StudioMenu/StudioMenuDetailReview.tsx | 5 +++++ 2 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 src/pages/Studio/StudioMenu/StudioMenuDetailReview.tsx diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx index 3adeae0..37b818e 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx @@ -5,9 +5,12 @@ import Header from '@components/Header/Header'; import { useParams } from 'react-router-dom'; import { TypoBodyMdM, TypoTitleSmS } from '@styles/Common'; import StudioMenuDetailInfo from './StudioMenuDetailInfo'; +import { useState } from 'react'; +import StudioMenuDetailReview from './StudioMenuDetailReview'; const StudioMenuDetail = () => { const { _menuId } = useParams(); + const [tabMenuState, setTabMenuState] = useState('info'); return ( <> @@ -17,12 +20,16 @@ const StudioMenuDetail = () => {

    사진메뉴설명입니다유유유유유유유

    - - {tabMenuState === 'info' && } + {tabMenuState === 'info' && } {tabMenuState === 'review' && } +
    +
    + 총 결제금액 +

    {totalPrice?.toLocaleString('ko-KR')}원

    +
    + +
    ); }; @@ -85,7 +104,7 @@ const HeaderCustomStyle = (scrollY: boolean): SerializedStyles => { z-index: 50; padding: 1.6rem 1rem; ${scrollY && 'background-color: #fff; box-shadow: 0 0.4rem .5rem rgba(0, 0, 0, 0.1);'}; - transition: all 0.3s; + transition: all 0.1s; `; }; @@ -140,6 +159,30 @@ const TabMenuStyle = css` } `; -const ImgaeAddStyle = css` - box-shadow: inset 0 0 10px blue; +const FixedBtnBoxStyle = css` + display: flex; + gap: 2rem; + align-items: center; + position: fixed; + left: 0; + right: 0; + bottom: 0; + background-color: ${variables.colors.white}; + padding: 1.6rem; + border-top: 0.1rem solid ${variables.colors.gray300}; + + .totalPrice { + display: flex; + flex-direction: column; + min-width: 10rem; + + & span { + ${TypoCapSmR} + color: ${variables.colors.gray600}; + } + + & p { + ${TypoTitleSmS} + } + } `; diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx index d453687..d0e817e 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx @@ -1,23 +1,21 @@ /** @jsxImportSource @emotion/react */ import { css } from '@emotion/react'; import variables from '@styles/Variables'; -import Button from '@components/Button/Button'; -import { TypoBodyMdM, TypoBodyMdR, TypoCapSmR, TypoTitleSmS, TypoTitleXsM } from '@styles/Common'; +import { TypoBodyMdR, TypoTitleXsM } from '@styles/Common'; import { IMenuListRes } from 'types/types'; -import { useState } from 'react'; - -const StudioMenuDetailInfo = ({ infoItem }: { infoItem: IMenuListRes | undefined }) => { - const [totalPrice, setTotalPrice] = useState(infoItem ? infoItem.price : 0); +import { Dispatch, SetStateAction, useState } from 'react'; +const StudioMenuDetailInfo = ({ infoItem, setTotalPrice }: { infoItem: IMenuListRes | undefined; setTotalPrice: Dispatch> }) => { const handleOptionClick = (price: number, e: React.ChangeEvent) => { const isChecked = e.target.checked; setTotalPrice((prev) => (isChecked ? prev + price : prev - price)); }; - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - console.log('최종 결제 금액:', totalPrice); - }; + // 추후 예약 기능시 필요 + // const handleSubmit = (e: React.FormEvent) => { + // e.preventDefault(); + // console.log('최종 결제 금액:', totalPrice); + // }; return ( <> @@ -45,7 +43,8 @@ const StudioMenuDetailInfo = ({ infoItem }: { infoItem: IMenuListRes | undefined

    {infoItem?.price.toLocaleString('ko-KR')}원

    -
    + + {/* / */}

    추가 옵션

    @@ -61,15 +60,6 @@ const StudioMenuDetailInfo = ({ infoItem }: { infoItem: IMenuListRes | undefined ))}
    - -
    -
    - 총 결제금액 -

    {totalPrice?.toLocaleString('ko-KR')}원

    -
    - -
    ); @@ -192,31 +182,3 @@ const AddOptionItemStyle = css` margin-right: auto; } `; - -const FixedBtnBoxStyle = css` - display: flex; - gap: 2rem; - align-items: center; - position: fixed; - left: 0; - right: 0; - bottom: 0; - background-color: ${variables.colors.white}; - padding: 2rem 1.6rem; - border-top: 0.1rem solid ${variables.colors.gray300}; - - .totalPrice { - display: flex; - flex-direction: column; - min-width: 10rem; - - & span { - ${TypoCapSmR} - color: ${variables.colors.gray600}; - } - - & p { - ${TypoTitleSmS} - } - } -`; From 793214306013a59af656a27d43263470b41337a3 Mon Sep 17 00:00:00 2001 From: woojoung Date: Thu, 19 Dec 2024 17:20:50 +0900 Subject: [PATCH 110/146] =?UTF-8?q?UI=20:=20=EB=93=9C=EB=9E=8D=EB=8B=A4?= =?UTF-8?q?=EC=9A=B4=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=82=AC=EC=A7=84?= =?UTF-8?q?=EB=A7=8C=20=EB=B3=B4=EA=B8=B0=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ImageSwiper/ImageSwiper.tsx | 1 + .../StudioReview/StudioReviewPhotos.tsx | 21 +++++++++++++------ .../StudioReview/components/DropDown.tsx | 4 ++-- .../StudioReview/components/ReviewContent.tsx | 4 +++- .../components/StudioReviewCategories.tsx | 19 ++++------------- .../components/StudioReviewImageList.tsx | 6 +++--- .../components/StudioReviewItem.tsx | 8 ++++++- 7 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/components/ImageSwiper/ImageSwiper.tsx b/src/components/ImageSwiper/ImageSwiper.tsx index 27738ad..9cf2726 100644 --- a/src/components/ImageSwiper/ImageSwiper.tsx +++ b/src/components/ImageSwiper/ImageSwiper.tsx @@ -12,6 +12,7 @@ interface ImageSwiperProps extends SwiperProps { imgprops?: { loading?: string; onLoad?: (e: React.SyntheticEvent) => void; + onClick?: () => void; }; } diff --git a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx index 70c3470..7cc6a21 100644 --- a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx +++ b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx @@ -7,6 +7,9 @@ import { useQuery } from '@tanstack/react-query'; import { useState } from 'react'; import { useParams, useSearchParams } from 'react-router-dom'; import { IReviewImages } from 'types/types'; +import DimmedModal from '@pages/Studio/components/DimmedModal'; +import { SwiperSlide } from 'swiper/react'; +import useModal from '@hooks/useModal'; interface IReviewImagesResponse { totalElements: number; @@ -21,8 +24,10 @@ const StudioReviewPhotos = () => { const [selectedMenuId, setSelectedMenuId] = useState(null); const [_, setSearchParams] = useSearchParams(); + const { open } = useModal(1); const fetchReviewImage = async () => { // 리뷰사진 모아보기 조회 + const url = new URL(`${import.meta.env.VITE_TOUCHEESE_API}/studio/detail/${_id}/reviewImage`); if (selectedMenuId) { @@ -56,10 +61,6 @@ const StudioReviewPhotos = () => { if (error) return
    에러가 발생했습니다
    ; if (!reviewImages) return null; - const handleOpenImageModal = () => { - console.log('이미지 클릭됨'); - }; - return ( <>
    @@ -80,12 +81,20 @@ const StudioReviewPhotos = () => { - {reviewImages.imageDtos.map(({ id, url }) => ( -
    + {reviewImages.imageDtos.map(({ id, url }, index) => ( +
    open()}> {`리뷰
    ))} + + + {reviewImages.imageDtos.map(({ id, url }) => ( + + {`리뷰 + + ))} + ); diff --git a/src/pages/Studio/StudioReview/components/DropDown.tsx b/src/pages/Studio/StudioReview/components/DropDown.tsx index 90953d2..3c65be7 100644 --- a/src/pages/Studio/StudioReview/components/DropDown.tsx +++ b/src/pages/Studio/StudioReview/components/DropDown.tsx @@ -85,8 +85,8 @@ const FilterDropdown = styled.div` display: flex; align-items: center; justify-content: space-between; - width: fit-content; - min-width: 23rem; + width: 100%; + height: 3.3rem; padding: 0.8rem 1rem; border: 1px solid ${variables.colors.gray400}; diff --git a/src/pages/Studio/StudioReview/components/ReviewContent.tsx b/src/pages/Studio/StudioReview/components/ReviewContent.tsx index 9f590a3..4f855bf 100644 --- a/src/pages/Studio/StudioReview/components/ReviewContent.tsx +++ b/src/pages/Studio/StudioReview/components/ReviewContent.tsx @@ -1,6 +1,7 @@ import React from 'react'; import styled from '@emotion/styled'; import variables from '@styles/Variables'; +import { TypoCapSmR } from '@styles/Common'; interface ReviewContentProps { content: string; @@ -37,7 +38,8 @@ const MoreButton = styled.button` background: none; border: none; color: ${variables.colors.gray600}; - font-size: 1.4rem; + text-decoration: underline; + ${TypoCapSmR} cursor: pointer; margin-top: 0.5rem; `; diff --git a/src/pages/Studio/StudioReview/components/StudioReviewCategories.tsx b/src/pages/Studio/StudioReview/components/StudioReviewCategories.tsx index 7bb40b9..3970460 100644 --- a/src/pages/Studio/StudioReview/components/StudioReviewCategories.tsx +++ b/src/pages/Studio/StudioReview/components/StudioReviewCategories.tsx @@ -1,5 +1,4 @@ /** @jsxImportSource @emotion/react */ -import Button from '@components/Button/Button'; import styled from '@emotion/styled'; import variables from '@styles/Variables'; import { useState } from 'react'; @@ -21,21 +20,15 @@ const StudioReviewCategories = ({ avgRating, totalReviewNum, menuNameList, menuI const handleOptionSelect = (option: string) => { setSelectedOption(option); - if (option === '전체리뷰') { - onFilterChange(null); - } else { - const index = menuNameList.indexOf(option); - if (index !== -1) { - onFilterChange(menuIdList[index]); - } - } + const menuIndex = menuNameList.indexOf(option); + const selectedMenuId = option === '전체리뷰' ? null : menuIdList[menuIndex]; + onFilterChange(selectedMenuId); }; return ( -
    @@ -49,13 +50,13 @@ const MenuCoverStyle = css` const MenuDescStyle = css` display: flex; flex-direction: column; - gap: 1.8rem; + gap: 1rem; + justify-content: space-between; `; const MenuHeadStyle = css` & h4 { - font-size: ${variables.size.medium}; - font-weight: 500; + ${TypoBodyMdM} display: flex; align-items: center; margin-bottom: 0.4rem; @@ -76,20 +77,19 @@ const MenuHeadStyle = css` -webkit-box-orient: vertical; overflow: hidden; -webkit-line-clamp: 2; - font-size: 1.2rem; - font-weight: 300; + ${TypoCapSmR} color: ${variables.colors.gray800}; } `; const MenuPriceReviewStyle = css` & p { - font-size: 1.4rem; - font-weight: 700; + ${TypoBodyMdSb} + margin-bottom:.4rem; } & span { - font-size: 1rem; + ${TypoCapSmR} color: ${variables.colors.gray700}; } `; From 19bddc34131c77de15ba424a43385e02eb67097e Mon Sep 17 00:00:00 2001 From: kyungmim Date: Thu, 19 Dec 2024 17:57:52 +0900 Subject: [PATCH 114/146] =?UTF-8?q?Edit:=20=EB=A9=94=EB=89=B4=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=98=A4=ED=83=80=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 --- src/pages/Studio/StudioMenu/StudioMenu.tsx | 4 ++-- src/pages/Studio/StudioMenu/StudioMenuDetail.tsx | 4 ++-- src/pages/Studio/StudioMenu/StudioMenuItem.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/Studio/StudioMenu/StudioMenu.tsx b/src/pages/Studio/StudioMenu/StudioMenu.tsx index a3b9117..bbd6459 100644 --- a/src/pages/Studio/StudioMenu/StudioMenu.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenu.tsx @@ -11,7 +11,7 @@ const StudioMenu = () => { const { _id } = useParams(); const [data, setData] = useState(); - const fetchMeun = async () => { + const fetchMenu = async () => { const res = await fetch(`${import.meta.env.VITE_TOUCHEESE_API}/studio/detail/${_id}/menu`, { method: 'GET', headers: { @@ -29,7 +29,7 @@ const StudioMenu = () => { }; useEffect(() => { - fetchMeun(); + fetchMenu(); }, []); const StudioMenuList = data?.map((item) => ); diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx index a0f5591..032b56b 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx @@ -18,7 +18,7 @@ const StudioMenuDetail = () => { const [scrollY, setScrollY] = useState(false); const [totalPrice, setTotalPrice] = useState(data ? data.price : 0); - const fetchMeunDetail = async () => { + const fetchMenuDetail = async () => { const res = await fetch(`${import.meta.env.VITE_TOUCHEESE_API}/studio/detail/menu/${_menuId}`, { method: 'GET', headers: { @@ -36,7 +36,7 @@ const StudioMenuDetail = () => { useEffect(() => { const fetchAndSetData = async () => { - const result = await fetchMeunDetail(); + const result = await fetchMenuDetail(); setData(result); setTotalPrice(result.price); }; diff --git a/src/pages/Studio/StudioMenu/StudioMenuItem.tsx b/src/pages/Studio/StudioMenu/StudioMenuItem.tsx index 1425996..2eab7d3 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuItem.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuItem.tsx @@ -10,7 +10,7 @@ const StudioMenuItem = ({ StudioId, data }: { StudioId: string | undefined; data return ( <> -
    navigate(`/studio/${StudioId}/menu/${data?.id}`)}> +
    navigate(`/studio/${StudioId}/menu/${data?.id}`)}>
    메뉴 대표 사진
    @@ -31,7 +31,7 @@ const StudioMenuItem = ({ StudioId, data }: { StudioId: string | undefined; data export default StudioMenuItem; -const MeunItemWrapperStyle = css` +const MenuItemWrapperStyle = css` display: flex; gap: 1.4rem; padding: 1.4rem 0; From 0efdef56be42a3885d2c149c71bfc026a6412b7d Mon Sep 17 00:00:00 2001 From: kyungmim Date: Thu, 19 Dec 2024 19:06:23 +0900 Subject: [PATCH 115/146] =?UTF-8?q?Edit:=20=EB=A9=94=EB=89=B4=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=20=ED=97=A4=EB=8D=94=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Studio/StudioMenu/StudioMenu.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/pages/Studio/StudioMenu/StudioMenu.tsx b/src/pages/Studio/StudioMenu/StudioMenu.tsx index bbd6459..fdd4e15 100644 --- a/src/pages/Studio/StudioMenu/StudioMenu.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenu.tsx @@ -6,6 +6,7 @@ import StudioMenuItem from './StudioMenuItem'; import { useParams } from 'react-router-dom'; import { useEffect, useState } from 'react'; import { IMenuListRes } from 'types/types'; +import Header from '@components/Header/Header'; const StudioMenu = () => { const { _id } = useParams(); @@ -24,18 +25,22 @@ const StudioMenu = () => { } const data = await res.json(); - - setData(data); + return data; }; useEffect(() => { - fetchMenu(); + const fetchAndSetData = async () => { + const result = await fetchMenu(); + setData(result); + }; + + fetchAndSetData(); }, []); const StudioMenuList = data?.map((item) => ); return ( <> - +
    {StudioMenuList}
    From 64e3e75d6883753e8012e9bc0c10364ed1b0bfdf Mon Sep 17 00:00:00 2001 From: kyungmim Date: Thu, 19 Dec 2024 20:15:01 +0900 Subject: [PATCH 116/146] =?UTF-8?q?Feat:=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=9D=B4=EB=8F=99=20=EC=8B=9C=20=EC=98=B5=EC=85=98?= =?UTF-8?q?=20=EC=B2=B4=ED=81=AC=20=EC=83=81=ED=83=9C=20=EC=9C=A0=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Studio/StudioMenu/StudioMenuDetail.tsx | 7 ++++++- .../Studio/StudioMenu/StudioMenuDetailInfo.tsx | 17 ++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx index 032b56b..a2bd9b6 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx @@ -17,6 +17,9 @@ const StudioMenuDetail = () => { const [data, setData] = useState(); const [scrollY, setScrollY] = useState(false); const [totalPrice, setTotalPrice] = useState(data ? data.price : 0); + const [checkState, setCheckState] = useState>({}); + + console.log(checkState); const fetchMenuDetail = async () => { const res = await fetch(`${import.meta.env.VITE_TOUCHEESE_API}/studio/detail/menu/${_menuId}`, { @@ -76,8 +79,9 @@ const StudioMenuDetail = () => { 리뷰 {data?.reviewCount ? data?.reviewCount : '0'} - {tabMenuState === 'info' && } + {tabMenuState === 'info' && } {tabMenuState === 'review' && } +
    총 결제금액 @@ -167,6 +171,7 @@ const FixedBtnBoxStyle = css` background-color: ${variables.colors.white}; padding: 1.6rem; border-top: 0.1rem solid ${variables.colors.gray300}; + z-index: 30; .totalPrice { display: flex; diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx index efbe833..d4d32f4 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx @@ -5,10 +5,21 @@ import { TypoBodyMdM, TypoBodyMdR, TypoBodyMdSb, TypoTitleXsB, TypoTitleXsM } fr import { IMenuListRes } from 'types/types'; import { Dispatch, SetStateAction } from 'react'; -const StudioMenuDetailInfo = ({ infoItem, setTotalPrice }: { infoItem: IMenuListRes | undefined; setTotalPrice: Dispatch> }) => { - const handleOptionClick = (price: number, e: React.ChangeEvent) => { +const StudioMenuDetailInfo = ({ + infoItem, + setTotalPrice, + checkState, + setCheckState, +}: { + infoItem: IMenuListRes | undefined; + setTotalPrice: Dispatch>; + checkState: Record; + setCheckState: Dispatch>>; +}) => { + const handleOptionClick = (price: number, id: number, e: React.ChangeEvent) => { const isChecked = e.target.checked; setTotalPrice((prev) => (isChecked ? prev + price : prev - price)); + setCheckState((prev) => ({ ...prev, [id]: isChecked })); }; // 추후 예약 기능시 필요 @@ -51,7 +62,7 @@ const StudioMenuDetailInfo = ({ infoItem, setTotalPrice }: { infoItem: IMenuList {infoItem?.additionalOptions.map((item) => (
    - handleOptionClick(item.price, e)} /> + handleOptionClick(item.price, item.id, e)} checked={checkState[item.id]} /> From ff862b059cd1574a50955fa80cd2b81f14f47293 Mon Sep 17 00:00:00 2001 From: woojoung Date: Thu, 19 Dec 2024 20:43:46 +0900 Subject: [PATCH 117/146] =?UTF-8?q?Chore:=20=EB=A6=AC=EC=95=A1=ED=8A=B8=20?= =?UTF-8?q?=ED=97=AC=EB=A9=A7=20=EC=A0=81=EC=9A=A9=20,=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=97=AC=EB=A9=A7?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 16 +++++--- src/components/Error/ErrorBoundary.tsx | 38 +++++++++++++++++++ src/pages/Home/Home.tsx | 3 ++ .../Studio/StudioReview/StudioReview.tsx | 10 +++++ 4 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 src/components/Error/ErrorBoundary.tsx diff --git a/src/App.tsx b/src/App.tsx index eff344f..c038d55 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,17 +4,21 @@ import variables from '@styles/Variables'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { RouterProvider } from 'react-router-dom'; import router from './routes.tsx'; +import { HelmetProvider } from 'react-helmet-async'; const queryClient = new QueryClient(); function App() { return ( - - - - - - + // SEO 최적화를 위해 HelmetProvider 적용 + + + + + + + + ); } diff --git a/src/components/Error/ErrorBoundary.tsx b/src/components/Error/ErrorBoundary.tsx new file mode 100644 index 0000000..240efb5 --- /dev/null +++ b/src/components/Error/ErrorBoundary.tsx @@ -0,0 +1,38 @@ +import { Component, ErrorInfo, ReactNode } from 'react'; + +interface Props { + children: ReactNode; + fallback?: ReactNode; +} + +interface State { + hasError: boolean; + error?: Error; +} + +class ErrorBoundary extends Component { + state: State = { hasError: false }; + + constructor(props: Props) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(error: Error) { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, errorInfo: ErrorInfo) { + console.error('ErrorBoundary caught an error:', error, errorInfo); + } + + render() { + if (this.state.hasError) { + return this.props.fallback ||

    문제가 발생했습니다.

    ; + } + + return this.props.children; + } +} + +export default ErrorBoundary; diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 23ffe06..f6e19e9 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -34,6 +34,9 @@ export type Options = { 파우더룸: string; }; +const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); +console.log('접속한 기기:', isMobile ? '모바일' : '데스크톱'); + const Home = () => { const [searchParams] = useSearchParams(); const [isFixed, setIsFixed] = useState(false); diff --git a/src/pages/Studio/StudioReview/StudioReview.tsx b/src/pages/Studio/StudioReview/StudioReview.tsx index 961bd17..ee28b9a 100644 --- a/src/pages/Studio/StudioReview/StudioReview.tsx +++ b/src/pages/Studio/StudioReview/StudioReview.tsx @@ -10,6 +10,7 @@ import { IReviewImages } from 'types/types'; import { useStudioReviews } from '@hooks/useStudioReviews'; import Header from '@components/Header/Header'; import { useState } from 'react'; +import { Helmet } from 'react-helmet-async'; // 리뷰 데이터의 타입 정의 interface Review { content: string; @@ -52,6 +53,15 @@ const StudioReview = () => { return ( <> + {data && ( + + {`스튜디오 리뷰 | ${totalReviewNum}개의 리뷰`} + + + + + )} +
    From da0c6b96548459076c7d76a6bc68647e73723c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=EB=AF=BC?= Date: Thu, 19 Dec 2024 21:22:29 +0900 Subject: [PATCH 118/146] =?UTF-8?q?Feat:=20=EA=B3=B5=EC=9C=A0=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=82=B4=EB=B6=80=20=EB=A7=81=ED=81=AC=EB=B3=B5?= =?UTF-8?q?=EC=82=AC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/img/icon-copy-link.svg | 4 +++ src/components/Share/Share.tsx | 53 +++++++++++++++++++++++----------- 2 files changed, 40 insertions(+), 17 deletions(-) create mode 100644 public/img/icon-copy-link.svg diff --git a/public/img/icon-copy-link.svg b/public/img/icon-copy-link.svg new file mode 100644 index 0000000..210f89e --- /dev/null +++ b/public/img/icon-copy-link.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/Share/Share.tsx b/src/components/Share/Share.tsx index 4f08713..48b3d68 100644 --- a/src/components/Share/Share.tsx +++ b/src/components/Share/Share.tsx @@ -3,6 +3,7 @@ import { css } from '@emotion/react'; import useBottomSheetState from '@store/useBottomSheetStateStore'; import BottomSheet from '@components/BottomSheet/BottomSheet'; import KakaoShareButton from '@components/Kakao/KakaoShare'; +import useClipboard from '@hooks/useClipboard'; interface ShareProps { title: string; @@ -13,14 +14,24 @@ interface ShareProps { const Share = ({ title, description, imageUrl, webUrl }: ShareProps) => { const { openBottomSheet } = useBottomSheetState(); + const { copyToClipboard } = useClipboard(webUrl); const handleOpenKakaoShare = () => { openBottomSheet(
    -
    - +
    +
    + +
    + 카카오톡 +
    + +
    +
    + 링크 복사 +
    + 링크복사
    - 카카오톡
    , '공유 옵션 선택', ); @@ -45,32 +56,40 @@ const containerStyle = css` const shareOptionsStyle = css` display: flex; - flex-direction: column; - jusfify-content: center; + justify-content: center; align-items: center; margin-top: 0.5rem; + gap: 2rem; `; -const shareButtonStyle = css` +const iconWithLabel = css` display: flex; + flex-direction: column; align-items: center; - gap: 0.8rem; + justify-content: center; + gap: 0.5rem; +`; + +const shareButtonStyle = css` cursor: pointer; width: 4.8rem; height: 4.8rem; + + img { + width: 100%; + height: 100%; + } `; const textStyle = css` font-size: 1.2rem; `; -//사용예시 -{ - /* */ -} +/* 사용예시 + +*/ From 7b0099b723f71bf92dbf097e098832d414c1ad75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=EB=AF=BC?= Date: Thu, 19 Dec 2024 21:23:34 +0900 Subject: [PATCH 119/146] =?UTF-8?q?Feat:=20=ED=81=B4=EB=A6=BD=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=20=ED=9B=85=20=EC=83=9D=EC=84=B1=20=EB=B0=8F=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{CopyButton.tsx => CopyLocation.tsx} | 16 +++++----------- src/components/Kakao/KakaoMap.tsx | 4 ++-- src/hooks/useClipBoard.ts | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 13 deletions(-) rename src/components/CopyButton/{CopyButton.tsx => CopyLocation.tsx} (80%) create mode 100644 src/hooks/useClipBoard.ts diff --git a/src/components/CopyButton/CopyButton.tsx b/src/components/CopyButton/CopyLocation.tsx similarity index 80% rename from src/components/CopyButton/CopyButton.tsx rename to src/components/CopyButton/CopyLocation.tsx index 8d5e2e2..065f4c6 100644 --- a/src/components/CopyButton/CopyButton.tsx +++ b/src/components/CopyButton/CopyLocation.tsx @@ -1,21 +1,15 @@ /** @jsxImportSource @emotion/react */ import { css } from '@emotion/react'; import variables from '@styles/Variables'; -import { useState } from 'react'; +import useClipboard from '@hooks/useClipboard'; interface CopyButtonProps { text: string; buttonLabel?: string; } -const CopyButton = ({ text, buttonLabel }: CopyButtonProps) => { - const [isCopied, setIsCopied] = useState(false); - - const handleCopy = async () => { - await navigator.clipboard.writeText(text); - setIsCopied(true); - setTimeout(() => setIsCopied(false), 1500); - }; +const CopyLocation = ({ text, buttonLabel }: CopyButtonProps) => { + const { isCopied, copyToClipboard } = useClipboard(text); return (
    @@ -24,7 +18,7 @@ const CopyButton = ({ text, buttonLabel }: CopyButtonProps) => { {text}
    - @@ -34,7 +28,7 @@ const CopyButton = ({ text, buttonLabel }: CopyButtonProps) => { ); }; -export default CopyButton; +export default CopyLocation; const containerStyle = css` display: flex; diff --git a/src/components/Kakao/KakaoMap.tsx b/src/components/Kakao/KakaoMap.tsx index 6538d8d..f0557dd 100644 --- a/src/components/Kakao/KakaoMap.tsx +++ b/src/components/Kakao/KakaoMap.tsx @@ -1,5 +1,5 @@ /** @jsxImportSource @emotion/react */ -import CopyButton from '@components/CopyButton/CopyButton'; +import CopyLocation from '@components/CopyButton/CopyLocation'; import { css } from '@emotion/react'; import { useEffect, useState } from 'react'; import { Map, MapMarker, useKakaoLoader } from 'react-kakao-maps-sdk'; @@ -68,7 +68,7 @@ const KakaoMap = ({ addressSi, addressGu, address }: KakaoMapProps) => { />
    - + ); }; diff --git a/src/hooks/useClipBoard.ts b/src/hooks/useClipBoard.ts new file mode 100644 index 0000000..4ac427e --- /dev/null +++ b/src/hooks/useClipBoard.ts @@ -0,0 +1,15 @@ +import { useState } from 'react'; + +const Clipboard = (text: string) => { + const [isCopied, setIsCopied] = useState(false); + + const copyToClipboard = async () => { + await navigator.clipboard.writeText(text); + setIsCopied(true); + setTimeout(() => setIsCopied(false), 1500); + }; + + return { isCopied, copyToClipboard }; +}; + +export default Clipboard; From d69b14f99f931cf28db6792399dd9d515122f996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=ED=9D=AC=EC=84=A0?= Date: Fri, 20 Dec 2024 13:00:24 +0900 Subject: [PATCH 120/146] =?UTF-8?q?Hotfix:=20query=20key=20=EC=84=A0?= =?UTF-8?q?=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useGetStudioDetail.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hooks/useGetStudioDetail.ts b/src/hooks/useGetStudioDetail.ts index 76273f0..989205c 100644 --- a/src/hooks/useGetStudioDetail.ts +++ b/src/hooks/useGetStudioDetail.ts @@ -17,10 +17,12 @@ const fetchStudioDetail = async (studioId: string): Promise => { export const useGetStudioDetail = (studioId: string) => { return useQuery({ - queryKey: ['studioDetail'], + queryKey: ['studioDetail', studioId], queryFn: () => fetchStudioDetail(studioId), staleTime: 1000 * 60 * 60 * 2, refetchOnWindowFocus: false, + gcTime: 5 * 60 * 1000, + enabled: !!studioId, // placeholderData: true, }); }; From ca0d42bbde0cc76845fe60bfde1d16e7d5794841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=ED=9D=AC=EC=84=A0?= Date: Fri, 20 Dec 2024 13:00:57 +0900 Subject: [PATCH 121/146] =?UTF-8?q?Style:=20=EC=95=84=EC=9D=B4=EC=BD=98=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=88=98=EC=A0=95=20Cont:=20?= =?UTF-8?q?=ED=8F=AC=ED=8A=B8=ED=8F=B4=EB=A6=AC=EC=98=A4=20=EB=AF=B8?= =?UTF-8?q?=EB=A6=AC=EB=B3=B4=EA=B8=B0=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Studio/StudioMain/StudioMain.tsx | 71 +++++++++++++++++----- 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/src/pages/Studio/StudioMain/StudioMain.tsx b/src/pages/Studio/StudioMain/StudioMain.tsx index ad3a933..9e5c956 100644 --- a/src/pages/Studio/StudioMain/StudioMain.tsx +++ b/src/pages/Studio/StudioMain/StudioMain.tsx @@ -1,9 +1,10 @@ /** @jsxImportSource @emotion/react */ -import BackButton from '@components/BackButton/BackButton'; + +import Header from '@components/Header/Header'; import StudioNavigator from '@components/Navigator/StudioNavigator'; import { css } from '@emotion/react'; import { useGetStudioDetail } from '@hooks/useGetStudioDetail'; -import { TypoBodyMdR, TypoBodyMdSb, TypoCapSmR, TypoTitleMdSb } from '@styles/Common'; +import { DividerStyle, TypoBodyMdR, TypoBodyMdSb, TypoCapSmR, TypoTitleMdSb } from '@styles/Common'; import variables from '@styles/Variables'; import { useParams } from 'react-router-dom'; @@ -12,6 +13,7 @@ import { useParams } from 'react-router-dom'; const StudioMain = () => { const { _id } = useParams(); const { data, isLoading, error } = useGetStudioDetail(`${_id}`); + console.log(data?.portfolios.map((v) => v)); if (isLoading) { return
    Loading...
    ; @@ -24,18 +26,28 @@ const StudioMain = () => { if (!data) { return
    로딩
    ; } + + /**이미지 5개 이하일때 대체할 이미지 */ + const placeHolderImage = '/img/img-nopic.png'; + const missingImgCount = data.portfolios.length < 5 ? 5 - data.portfolios.length : 0; + const portfolioWithPlaceHolders = [...data.portfolios, ...Array(missingImgCount).fill({ url: placeHolderImage })]; + return ( <> - - {/* 이미지 들어갈자리 */} -
    이미지
    +
    + {/* 이미지 */} +
    + {portfolioWithPlaceHolders.slice(0, 5).map((v, i) => ( + {`Portfolio + ))} +
    {/* 스튜디오 정보 */}

    {`${data.name}`}

    - 리뷰 평점 + 리뷰 평점

    {`${data.rating}`}

    {`(${data.review_count}개의 평가)`}

    @@ -59,7 +71,7 @@ const StudioMain = () => {
    - 주소 + 주소

    {`${data.address}` === 'undefined' ? '주소 수집중' : `${data.address}`}

    @@ -77,6 +89,7 @@ const StudioMain = () => {
    {/* gray 여백 들어갈 자리 */} + {/* 네비게이션 바 */} {/* 홈 기본 정보 */} @@ -90,12 +103,40 @@ const StudioMain = () => { export default StudioMain; +const HeaderStyle = css` + position: absolute; + z-index: 1; + padding-top: 1.8rem; +`; + +const portfolioPreviewStyle = css` + display: grid; + gap: 0.2rem; + grid-template-columns: 2fr 1fr 1fr; + grid-template-rows: repeat(2, 1fr); + width: calc(100% + 3.2rem); + margin-left: -1.6rem; + margin-bottom: 2rem; + + & > img { + aspect-ratio: 1/1; + object-fit: cover; + width: calc(100% - 0.1rem); + } + + & > img:first-of-type { + grid-column: span 1; /* 첫 번째 이미지는 2개의 열을 차지 */ + grid-row: span 2; /* 첫 번째 이미지는 2개의 행을 차지 */ + width: 100%; + } +`; + const StudioInfoTitleStyle = css` display: flex; justify-content: space-between; & > div { - margin-bottom: 1rem; + margin-bottom: 2rem; & > h2 { ${TypoTitleMdSb} margin-bottom: 0.4rem; @@ -103,12 +144,11 @@ const StudioInfoTitleStyle = css` & > div { display: flex; + align-items: center; & > img { - align-items: center; - justify-content: center; margin-right: 0.4rem; - width: 2rem; - height: 2rem; + width: 1.6rem; + height: 1.6rem; } & > p + p { @@ -120,6 +160,7 @@ const StudioInfoTitleStyle = css` `; const StudioInfoStyle = css` + position: relative; dl { display: flex; flex-direction: column; @@ -132,9 +173,10 @@ const StudioInfoStyle = css` dt { display: flex; margin-right: 1rem; + img { - width: 2rem; - height: 2rem; + width: 1.7rem; + height: 1.7rem; } } @@ -149,6 +191,7 @@ const StudioInfoStyle = css` } } } + ${DividerStyle} } `; From 8597948015eb5f7fd462334f0781cb9d94fb5e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=ED=9D=AC=EC=84=A0?= Date: Fri, 20 Dec 2024 13:01:27 +0900 Subject: [PATCH 122/146] =?UTF-8?q?Style:=20=EA=B3=B5=ED=86=B5=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20gray=20=EC=97=AC=EB=B0=B1=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 --- src/styles/Common.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/Common.tsx b/src/styles/Common.tsx index 24a9a8b..b11c8e3 100644 --- a/src/styles/Common.tsx +++ b/src/styles/Common.tsx @@ -58,6 +58,7 @@ export const TypoCapSmM = css` `; export const DividerStyle = css` + padding-bottom: calc(2rem + 1rem); &::after { content: ''; position: absolute; From d1437f2efd7d0078fde1f0b9a0da8682bae44606 Mon Sep 17 00:00:00 2001 From: kyungmim Date: Fri, 20 Dec 2024 13:21:09 +0900 Subject: [PATCH 123/146] =?UTF-8?q?Feat:=20=EB=A9=94=EB=89=B4=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Studio/StudioMenu/StudioMenuDetail.tsx | 4 +- .../StudioMenu/StudioMenuDetailReview.tsx | 42 ++++++++++++++++++- .../components/StudioReviewItem.tsx | 2 +- src/types/types.ts | 12 ++---- 4 files changed, 46 insertions(+), 14 deletions(-) diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx index a2bd9b6..879d589 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx @@ -19,8 +19,6 @@ const StudioMenuDetail = () => { const [totalPrice, setTotalPrice] = useState(data ? data.price : 0); const [checkState, setCheckState] = useState>({}); - console.log(checkState); - const fetchMenuDetail = async () => { const res = await fetch(`${import.meta.env.VITE_TOUCHEESE_API}/studio/detail/menu/${_menuId}`, { method: 'GET', @@ -80,7 +78,7 @@ const StudioMenuDetail = () => { {tabMenuState === 'info' && } - {tabMenuState === 'review' && } + {data && tabMenuState === 'review' && }
    diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetailReview.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetailReview.tsx index c2a076a..9ea76ff 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuDetailReview.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuDetailReview.tsx @@ -1,5 +1,43 @@ -const StudioMenuDetailReview = () => { - return <>안녕 나 리뷰; +/** @jsxImportSource @emotion/react */ +import { css } from '@emotion/react'; +import StudioReviewItem, { Review } from '../StudioReview/components/StudioReviewItem'; + +const StudioMenuDetailReview = ({ reviewItem = [], rating = 0 }: { reviewItem: Review[]; rating: number }) => { + const reviewList = reviewItem.map((item) => ); + + return ( + <> +
    +
    +
    + 평점 아이콘 + {Math.trunc(rating * 10) / 10} +
    +

    {reviewItem?.length}개의 리뷰

    +
    +
    {reviewList}
    +
    + + ); }; export default StudioMenuDetailReview; + +const ReviewrapperStyle = css` + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1.8rem 0; +`; + +const ReviewHead = css` + display: flex; + align-items: center; + gap: 0.8rem; + + .reviewRating { + display: flex; + align-items: center; + gap: 0.2rem; + } +`; diff --git a/src/pages/Studio/StudioReview/components/StudioReviewItem.tsx b/src/pages/Studio/StudioReview/components/StudioReviewItem.tsx index 22a0fca..e9e7016 100644 --- a/src/pages/Studio/StudioReview/components/StudioReviewItem.tsx +++ b/src/pages/Studio/StudioReview/components/StudioReviewItem.tsx @@ -11,7 +11,7 @@ import { formatTimeAgo } from '@utils/formatTimeAgo'; import { IReviewImages } from 'types/types'; // 리뷰 데이터의 타입 정의 -interface Review { +export interface Review { content: string; created_at: string; id: number; diff --git a/src/types/types.ts b/src/types/types.ts index a178c54..83d92f2 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -1,3 +1,5 @@ +import { Review } from '@pages/Studio/StudioReview/components/StudioReviewItem'; + export interface IPortfolio { id: number; studio: string; @@ -103,14 +105,6 @@ export interface IAdditionalOptionsRes { updateTime: string | null; } -export interface IMenuImagesRes { - id: number; - menuId: number; - menu: string; - url: string; - created_at: string | null; - updated_at: string | null; -} export interface IMenuListRes { id: number; studioId: number; @@ -121,6 +115,8 @@ export interface IMenuListRes { duration: string | null; additionalOptions: IAdditionalOptionsRes[]; menuImages: IPortfolio[] | IReviewImages[]; + avgScore: number; + reviews: { content: Review[] }; reviewCount: number; created_at: string | null; updated_at: string | null; From bdce668030ebe2d359e633b29b20c3f14e3b10a7 Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Fri, 20 Dec 2024 13:23:18 +0900 Subject: [PATCH 124/146] =?UTF-8?q?Hotfix:=20ThemeNavigator=20import=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/ThemeNavigator.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ThemeNavigator.test.tsx b/tests/ThemeNavigator.test.tsx index 6ef6238..bccd954 100644 --- a/tests/ThemeNavigator.test.tsx +++ b/tests/ThemeNavigator.test.tsx @@ -1,8 +1,8 @@ import { describe, expect, test } from 'vitest'; -import ThemeNavigator from './ThemeNavigator'; import { render, screen, fireEvent } from '@testing-library/react'; import variables from '@styles/Variables'; import { MemoryRouter } from 'react-router-dom'; +import ThemeNavigator from '@components/Navigator/ThemeNavigator'; describe('ThemeNavigator', () => { test('renders all themes', () => { From 4947c2f6c4e95f9f732f832714c1d7aab3191b16 Mon Sep 17 00:00:00 2001 From: kyungmim Date: Fri, 20 Dec 2024 13:29:17 +0900 Subject: [PATCH 125/146] =?UTF-8?q?Edit:=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Studio/StudioMenu/StudioMenuDetail.tsx | 4 ++-- src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx | 12 ++++++------ .../Studio/StudioMenu/StudioMenuDetailReview.tsx | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx index 879d589..d8fd4b7 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx @@ -13,9 +13,9 @@ import Button from '@components/Button/Button'; const StudioMenuDetail = () => { const { _menuId } = useParams(); - const [tabMenuState, setTabMenuState] = useState('info'); const [data, setData] = useState(); const [scrollY, setScrollY] = useState(false); + const [tabMenuState, setTabMenuState] = useState('info'); const [totalPrice, setTotalPrice] = useState(data ? data.price : 0); const [checkState, setCheckState] = useState>({}); @@ -77,7 +77,7 @@ const StudioMenuDetail = () => { 리뷰 {data?.reviewCount ? data?.reviewCount : '0'} - {tabMenuState === 'info' && } + {data && tabMenuState === 'info' && } {data && tabMenuState === 'review' && }
    diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx index d4d32f4..3fcfa26 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx @@ -11,7 +11,7 @@ const StudioMenuDetailInfo = ({ checkState, setCheckState, }: { - infoItem: IMenuListRes | undefined; + infoItem: IMenuListRes; setTotalPrice: Dispatch>; checkState: Record; setCheckState: Dispatch>>; @@ -51,7 +51,7 @@ const StudioMenuDetailInfo = ({

    기본 가격

    -

    {infoItem?.price.toLocaleString('ko-KR')}원

    +

    {infoItem.price.toLocaleString('ko-KR')}원

    @@ -59,15 +59,15 @@ const StudioMenuDetailInfo = ({

    추가 옵션

    - {infoItem?.additionalOptions.map((item) => ( + {infoItem.additionalOptions.map((item) => (
    - handleOptionClick(item.price, item.id, e)} checked={checkState[item.id]} /> -
    -

    +{item?.price.toLocaleString('ko-KR')}원

    +

    +{item.price.toLocaleString('ko-KR')}원

    ))}
    diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetailReview.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetailReview.tsx index 9ea76ff..24e2fd4 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuDetailReview.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuDetailReview.tsx @@ -13,7 +13,7 @@ const StudioMenuDetailReview = ({ reviewItem = [], rating = 0 }: { reviewItem: R 평점 아이콘 {Math.trunc(rating * 10) / 10}
    -

    {reviewItem?.length}개의 리뷰

    +

    {reviewItem.length}개의 리뷰

    {reviewList}
    From aa29ce7592aedc2c1c3bb67aa772c6dcec50fc83 Mon Sep 17 00:00:00 2001 From: woojoung Date: Fri, 20 Dec 2024 13:30:06 +0900 Subject: [PATCH 126/146] =?UTF-8?q?Feat:=20=EC=84=B1=EB=8A=A5=20=EC=B5=9C?= =?UTF-8?q?=EC=A0=81=ED=99=94=EB=A5=BC=20=EC=9C=84=ED=95=9C=20Lazy=20Impor?= =?UTF-8?q?t=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20EB,SUSPENSE=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 9 +++++++-- src/components/Error/ErrorBoundary.tsx | 2 -- .../StudioReview/StudioReviewPhotos.tsx | 5 ++--- src/routes.tsx | 20 ++++++++++--------- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index c038d55..abf1b27 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,17 +5,22 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { RouterProvider } from 'react-router-dom'; import router from './routes.tsx'; import { HelmetProvider } from 'react-helmet-async'; +import ErrorBoundary from '@components/Error/ErrorBoundary.tsx'; +import { Suspense } from 'react'; const queryClient = new QueryClient(); function App() { return ( - // SEO 최적화를 위해 HelmetProvider 적용 - + 문제가 발생했습니다.}> + 로딩 중...~~~~딩 중...~~~~딩 중...~~~~딩 중...~~~~}> + + + diff --git a/src/components/Error/ErrorBoundary.tsx b/src/components/Error/ErrorBoundary.tsx index 240efb5..bae7888 100644 --- a/src/components/Error/ErrorBoundary.tsx +++ b/src/components/Error/ErrorBoundary.tsx @@ -11,8 +11,6 @@ interface State { } class ErrorBoundary extends Component { - state: State = { hasError: false }; - constructor(props: Props) { super(props); this.state = { hasError: false }; diff --git a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx index 7cc6a21..1eacd13 100644 --- a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx +++ b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx @@ -46,7 +46,7 @@ const StudioReviewPhotos = () => { const { data: reviewImages, - isLoading, + error, } = useQuery({ queryKey: ['reviewImages', _id, selectedMenuId], @@ -57,7 +57,6 @@ const StudioReviewPhotos = () => { retry: 3, }); - if (isLoading) return
    로딩 중...
    ; if (error) return
    에러가 발생했습니다
    ; if (!reviewImages) return null; @@ -81,7 +80,7 @@ const StudioReviewPhotos = () => { - {reviewImages.imageDtos.map(({ id, url }, index) => ( + {reviewImages.imageDtos.map(({ id, url }) => (
    open()}> {`리뷰
    diff --git a/src/routes.tsx b/src/routes.tsx index cd0cc50..8659fa3 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -1,14 +1,16 @@ -import Home from '@pages/Home/Home'; -import Search from '@pages/search/Search'; -import SearchResults from '@pages/search/SearchResult'; -import StudioMain from '@pages/Studio/StudioMain/StudioMain'; -import StudioMenu from '@pages/Studio/StudioMenu/StudioMenu'; -import StudioMenuDetail from '@pages/Studio/StudioMenu/StudioMenuDetail'; -import StudioPortfolio from '@pages/Studio/StudioPortfolio/StudioPortfolio'; -import StudioReview from '@pages/Studio/StudioReview/StudioReview'; -import StudioReviewPhotos from '@pages/Studio/StudioReview/StudioReviewPhotos'; +import { lazy } from 'react'; import { createBrowserRouter } from 'react-router-dom'; +const Home = lazy(() => import('@pages/Home/Home')); +const Search = lazy(() => import('@pages/search/Search')); +const SearchResults = lazy(() => import('@pages/search/SearchResult')); +const StudioMain = lazy(() => import('@pages/Studio/StudioMain/StudioMain')); +const StudioMenu = lazy(() => import('@pages/Studio/StudioMenu/StudioMenu')); +const StudioMenuDetail = lazy(() => import('@pages/Studio/StudioMenu/StudioMenuDetail')); +const StudioPortfolio = lazy(() => import('@pages/Studio/StudioPortfolio/StudioPortfolio')); +const StudioReview = lazy(() => import('@pages/Studio/StudioReview/StudioReview')); +const StudioReviewPhotos = lazy(() => import('@pages/Studio/StudioReview/StudioReviewPhotos')); + const router = createBrowserRouter([ { path: '/', From d2b2dec6b87c3cbb44ca6036d8dbd1c5bc62cd8f Mon Sep 17 00:00:00 2001 From: kyungmim Date: Fri, 20 Dec 2024 13:36:04 +0900 Subject: [PATCH 127/146] =?UTF-8?q?Style:=20=EB=A9=94=EB=89=B4=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?padding=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../StudioMenu/StudioMenuDetailInfo.tsx | 95 ++++++++++--------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx index 3fcfa26..360253c 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx @@ -30,59 +30,67 @@ const StudioMenuDetailInfo = ({ return ( <> -
    -
    -

    예상 소요 시간

    -

    약 60분

    -
    -
    -

    기본 촬영 수

    -

    70-80컷

    -
    -
    -

    인화 사이즈

    -

    4x6in

    -
    -
    -

    기본 제공 파일

    -

    3포즈 리터칭 JPG파일

    -
    -
    - -
    -

    기본 가격

    -

    {infoItem.price.toLocaleString('ko-KR')}원

    -
    - - - {/* / */} -

    추가 옵션

    - -
    - {infoItem.additionalOptions.map((item) => ( -
    -
    - handleOptionClick(item.price, item.id, e)} checked={checkState[item.id]} /> - -
    -

    +{item.price.toLocaleString('ko-KR')}원

    -
    - ))} -
    - +
    +
    +
    +

    예상 소요 시간

    +

    약 60분

    +
    +
    +

    기본 촬영 수

    +

    70-80컷

    +
    +
    +

    인화 사이즈

    +

    4x6in

    +
    +
    +

    기본 제공 파일

    +

    3포즈 리터칭 JPG파일

    +
    +
    + +
    +

    기본 가격

    +

    {infoItem.price.toLocaleString('ko-KR')}원

    +
    + +
    + {/* / */} +

    추가 옵션

    + +
    + {infoItem.additionalOptions.map((item) => ( +
    +
    + handleOptionClick(item.price, item.id, e)} checked={checkState[item.id]} /> + +
    +

    +{item.price.toLocaleString('ko-KR')}원

    +
    + ))} +
    +
    +
    ); }; export default StudioMenuDetailInfo; +const MenuInfoWrapperStyle = css` + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1.8rem 0; +`; + const MenuInfoStyle = css` display: flex; flex-direction: column; gap: 0.6rem; - padding: 1.4rem 0; .menuInfoItem { display: flex; @@ -162,7 +170,6 @@ const AddOptionsListStyle = css` & fieldset { display: flex; - padding: 1.2rem 0; height: 4.4rem; & p { From 474160e53f6c720f01fb35ab62b0285eab185d45 Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Fri, 20 Dec 2024 13:43:17 +0900 Subject: [PATCH 128/146] =?UTF-8?q?Test:=20StudioNavigator=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/StudioNavigtor.test.tsx | 122 ++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 tests/StudioNavigtor.test.tsx diff --git a/tests/StudioNavigtor.test.tsx b/tests/StudioNavigtor.test.tsx new file mode 100644 index 0000000..9db30d1 --- /dev/null +++ b/tests/StudioNavigtor.test.tsx @@ -0,0 +1,122 @@ +import StudioNavigator from '@components/Navigator/StudioNavigator'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { MemoryRouter, useLocation } from 'react-router-dom'; +import { describe, test } from 'vitest'; + +// 현재 경로 파악을 위한 컴포넌트 +const LocationCheck = () => { + const location = useLocation(); + return
    {location.pathname}
    ; +}; + +describe('StudioNavigator Test', () => { + const studioId = '146'; + const initialRoute = `/studio/${studioId}`; + + const renderWithRouter = (nest: string = '') => { + render( + + + + , + ); + }; + + // 상단 탭 렌더링 + test('스튜디오 상세에서 상단 탭을 렌더링한다.', () => { + renderWithRouter(); + expect(screen.getByRole('link', { name: /홈/i })).toBeInTheDocument(); + expect(screen.getByRole('link', { name: /메뉴/i })).toBeInTheDocument(); + expect(screen.getByRole('link', { name: /리뷰/i })).toBeInTheDocument(); + expect(screen.getByRole('link', { name: /포트폴리오/i })).toBeInTheDocument(); + }); + + // 홈을 누르면 스튜디오 상세 홈으로 이동 + test('상단 탭에서 홈을 누르면 홈 화면으로 이동한다.', async () => { + renderWithRouter('menu'); + const user = userEvent.setup(); + const homeLink = screen.getByRole('link', { name: /홈/i }); + const menuLink = screen.getByRole('link', { name: /메뉴/i }); + const portfolioLink = screen.getByRole('link', { name: /포트폴리오/i }); + const reviewLink = screen.getByRole('link', { name: /리뷰/i }); + + // 사용자가 홈 링크 클릭 + await user.click(homeLink); + + // 페이지 이동 확인 + expect(screen.getByTestId('location')).toHaveTextContent(`/studio/${studioId}`); + + // 클릭 후, 홈 경로가 활성화되었는지 확인 + expect(homeLink).toHaveClass('active'); + expect(menuLink).not.toHaveClass('active'); + expect(portfolioLink).not.toHaveClass('active'); + expect(reviewLink).not.toHaveClass('active'); + }); + + // 메뉴를 누르면 스튜디오 상세 메뉴로 이동 + test('상단 탭에서 메뉴를 누르면 메뉴 화면으로 이동한다.', async () => { + renderWithRouter(); + const user = userEvent.setup(); + const homeLink = screen.getByRole('link', { name: /홈/i }); + const menuLink = screen.getByRole('link', { name: /메뉴/i }); + const portfolioLink = screen.getByRole('link', { name: /포트폴리오/i }); + const reviewLink = screen.getByRole('link', { name: /리뷰/i }); + + // 사용자가 홈 링크 클릭 + await user.click(menuLink); + + // 페이지 이동 확인 + expect(screen.getByTestId('location')).toHaveTextContent(`/studio/${studioId}/menu`); + + // 클릭 후, 홈 경로가 활성화되었는지 확인 + expect(menuLink).toHaveClass('active'); + expect(homeLink).not.toHaveClass('active'); + expect(portfolioLink).not.toHaveClass('active'); + expect(reviewLink).not.toHaveClass('active'); + }); + + // 포트폴리오를 누르면 스튜디오 상세 포트폴리오로 이동 + test('상단 탭에서 포트폴리오를 누르면 포트폴리오 화면으로 이동한다.', async () => { + renderWithRouter(); + const user = userEvent.setup(); + const homeLink = screen.getByRole('link', { name: /홈/i }); + const menuLink = screen.getByRole('link', { name: /메뉴/i }); + const portfolioLink = screen.getByRole('link', { name: /포트폴리오/i }); + const reviewLink = screen.getByRole('link', { name: /리뷰/i }); + + // 사용자가 홈 링크 클릭 + await user.click(portfolioLink); + + // 페이지 이동 확인 + expect(screen.getByTestId('location')).toHaveTextContent(`/studio/${studioId}/portfolio`); + + // 클릭 후, 홈 경로가 활성화되었는지 확인 + expect(portfolioLink).toHaveClass('active'); + expect(homeLink).not.toHaveClass('active'); + expect(menuLink).not.toHaveClass('active'); + expect(reviewLink).not.toHaveClass('active'); + }); + + // 리뷰를 누르면 스튜디오 상세 리뷰로 이동 + test('상단 탭에서 리뷰를 누르면 리뷰 화면으로 이동한다.', async () => { + renderWithRouter(); + const user = userEvent.setup(); + const homeLink = screen.getByRole('link', { name: /홈/i }); + const menuLink = screen.getByRole('link', { name: /메뉴/i }); + const portfolioLink = screen.getByRole('link', { name: /포트폴리오/i }); + const reviewLink = screen.getByRole('link', { name: /리뷰/i }); + + // 사용자가 홈 링크 클릭 + await user.click(reviewLink); + + // 페이지 이동 확인 + expect(screen.getByTestId('location')).toHaveTextContent(`/studio/${studioId}/review`); + + // 클릭 후, 홈 경로가 활성화되었는지 확인 + expect(reviewLink).toHaveClass('active'); + expect(homeLink).not.toHaveClass('active'); + expect(menuLink).not.toHaveClass('active'); + expect(portfolioLink).not.toHaveClass('active'); + }); +}); From 753925d305a3302c381b4af8ae1dd72387ed24f0 Mon Sep 17 00:00:00 2001 From: woojoung Date: Fri, 20 Dec 2024 13:52:42 +0900 Subject: [PATCH 129/146] =?UTF-8?q?Feat:=20SEO=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=ED=97=AC=EB=A9=A7=20=ED=83=80=EC=9D=B4=ED=8B=80,?= =?UTF-8?q?=20=EB=A9=94=ED=83=80=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 +- src/pages/Studio/StudioReview/StudioReview.tsx | 2 +- src/pages/Studio/StudioReview/StudioReviewPhotos.tsx | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index abf1b27..93f7b1b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,7 +17,7 @@ function App() { 문제가 발생했습니다.}> - 로딩 중...~~~~딩 중...~~~~딩 중...~~~~딩 중...~~~~}> + 로딩 중.. 서스펜스 버전}> diff --git a/src/pages/Studio/StudioReview/StudioReview.tsx b/src/pages/Studio/StudioReview/StudioReview.tsx index ee28b9a..324ea82 100644 --- a/src/pages/Studio/StudioReview/StudioReview.tsx +++ b/src/pages/Studio/StudioReview/StudioReview.tsx @@ -55,7 +55,7 @@ const StudioReview = () => { <> {data && ( - {`스튜디오 리뷰 | ${totalReviewNum}개의 리뷰`} + {`스튜디오 리뷰 - ${totalReviewNum}개의 리뷰`} diff --git a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx index 1eacd13..19478b7 100644 --- a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx +++ b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx @@ -10,6 +10,7 @@ import { IReviewImages } from 'types/types'; import DimmedModal from '@pages/Studio/components/DimmedModal'; import { SwiperSlide } from 'swiper/react'; import useModal from '@hooks/useModal'; +import { Helmet } from 'react-helmet-async'; interface IReviewImagesResponse { totalElements: number; @@ -62,6 +63,13 @@ const StudioReviewPhotos = () => { return ( <> + {reviewImages && ( + + {`리뷰 사진모아보기 - ${reviewImages.totalElements}개`} + + + )} +
    From da995c390dedee729c066003f60a536b2c3bb6db Mon Sep 17 00:00:00 2001 From: woojoung Date: Thu, 19 Dec 2024 17:20:50 +0900 Subject: [PATCH 130/146] =?UTF-8?q?UI=20:=20=EB=93=9C=EB=9E=8D=EB=8B=A4?= =?UTF-8?q?=EC=9A=B4=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=82=AC=EC=A7=84?= =?UTF-8?q?=EB=A7=8C=20=EB=B3=B4=EA=B8=B0=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ImageSwiper/ImageSwiper.tsx | 1 + .../StudioReview/StudioReviewPhotos.tsx | 21 +++++++++++++------ .../StudioReview/components/DropDown.tsx | 4 ++-- .../StudioReview/components/ReviewContent.tsx | 4 +++- .../components/StudioReviewCategories.tsx | 19 ++++------------- .../components/StudioReviewImageList.tsx | 6 +++--- .../components/StudioReviewItem.tsx | 8 ++++++- 7 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/components/ImageSwiper/ImageSwiper.tsx b/src/components/ImageSwiper/ImageSwiper.tsx index fd529fa..9ff8ace 100644 --- a/src/components/ImageSwiper/ImageSwiper.tsx +++ b/src/components/ImageSwiper/ImageSwiper.tsx @@ -15,6 +15,7 @@ interface ImageSwiperProps extends SwiperProps { customStyle?: ReturnType; loading?: string; onLoad?: (e: React.SyntheticEvent) => void; + onClick?: () => void; }; } diff --git a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx index 70c3470..7cc6a21 100644 --- a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx +++ b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx @@ -7,6 +7,9 @@ import { useQuery } from '@tanstack/react-query'; import { useState } from 'react'; import { useParams, useSearchParams } from 'react-router-dom'; import { IReviewImages } from 'types/types'; +import DimmedModal from '@pages/Studio/components/DimmedModal'; +import { SwiperSlide } from 'swiper/react'; +import useModal from '@hooks/useModal'; interface IReviewImagesResponse { totalElements: number; @@ -21,8 +24,10 @@ const StudioReviewPhotos = () => { const [selectedMenuId, setSelectedMenuId] = useState(null); const [_, setSearchParams] = useSearchParams(); + const { open } = useModal(1); const fetchReviewImage = async () => { // 리뷰사진 모아보기 조회 + const url = new URL(`${import.meta.env.VITE_TOUCHEESE_API}/studio/detail/${_id}/reviewImage`); if (selectedMenuId) { @@ -56,10 +61,6 @@ const StudioReviewPhotos = () => { if (error) return
    에러가 발생했습니다
    ; if (!reviewImages) return null; - const handleOpenImageModal = () => { - console.log('이미지 클릭됨'); - }; - return ( <>
    @@ -80,12 +81,20 @@ const StudioReviewPhotos = () => { - {reviewImages.imageDtos.map(({ id, url }) => ( -
    + {reviewImages.imageDtos.map(({ id, url }, index) => ( +
    open()}> {`리뷰
    ))} + + + {reviewImages.imageDtos.map(({ id, url }) => ( + + {`리뷰 + + ))} + ); diff --git a/src/pages/Studio/StudioReview/components/DropDown.tsx b/src/pages/Studio/StudioReview/components/DropDown.tsx index 90953d2..3c65be7 100644 --- a/src/pages/Studio/StudioReview/components/DropDown.tsx +++ b/src/pages/Studio/StudioReview/components/DropDown.tsx @@ -85,8 +85,8 @@ const FilterDropdown = styled.div` display: flex; align-items: center; justify-content: space-between; - width: fit-content; - min-width: 23rem; + width: 100%; + height: 3.3rem; padding: 0.8rem 1rem; border: 1px solid ${variables.colors.gray400}; diff --git a/src/pages/Studio/StudioReview/components/ReviewContent.tsx b/src/pages/Studio/StudioReview/components/ReviewContent.tsx index 9f590a3..4f855bf 100644 --- a/src/pages/Studio/StudioReview/components/ReviewContent.tsx +++ b/src/pages/Studio/StudioReview/components/ReviewContent.tsx @@ -1,6 +1,7 @@ import React from 'react'; import styled from '@emotion/styled'; import variables from '@styles/Variables'; +import { TypoCapSmR } from '@styles/Common'; interface ReviewContentProps { content: string; @@ -37,7 +38,8 @@ const MoreButton = styled.button` background: none; border: none; color: ${variables.colors.gray600}; - font-size: 1.4rem; + text-decoration: underline; + ${TypoCapSmR} cursor: pointer; margin-top: 0.5rem; `; diff --git a/src/pages/Studio/StudioReview/components/StudioReviewCategories.tsx b/src/pages/Studio/StudioReview/components/StudioReviewCategories.tsx index 7bb40b9..3970460 100644 --- a/src/pages/Studio/StudioReview/components/StudioReviewCategories.tsx +++ b/src/pages/Studio/StudioReview/components/StudioReviewCategories.tsx @@ -1,5 +1,4 @@ /** @jsxImportSource @emotion/react */ -import Button from '@components/Button/Button'; import styled from '@emotion/styled'; import variables from '@styles/Variables'; import { useState } from 'react'; @@ -21,21 +20,15 @@ const StudioReviewCategories = ({ avgRating, totalReviewNum, menuNameList, menuI const handleOptionSelect = (option: string) => { setSelectedOption(option); - if (option === '전체리뷰') { - onFilterChange(null); - } else { - const index = menuNameList.indexOf(option); - if (index !== -1) { - onFilterChange(menuIdList[index]); - } - } + const menuIndex = menuNameList.indexOf(option); + const selectedMenuId = option === '전체리뷰' ? null : menuIdList[menuIndex]; + onFilterChange(selectedMenuId); }; return ( -
    }> + 로딩 중...~~~~딩 중...~~~~딩 중...~~~~딩 중...~~~~}> + + + diff --git a/src/components/Error/ErrorBoundary.tsx b/src/components/Error/ErrorBoundary.tsx index 240efb5..bae7888 100644 --- a/src/components/Error/ErrorBoundary.tsx +++ b/src/components/Error/ErrorBoundary.tsx @@ -11,8 +11,6 @@ interface State { } class ErrorBoundary extends Component { - state: State = { hasError: false }; - constructor(props: Props) { super(props); this.state = { hasError: false }; diff --git a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx index 7cc6a21..1eacd13 100644 --- a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx +++ b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx @@ -46,7 +46,7 @@ const StudioReviewPhotos = () => { const { data: reviewImages, - isLoading, + error, } = useQuery({ queryKey: ['reviewImages', _id, selectedMenuId], @@ -57,7 +57,6 @@ const StudioReviewPhotos = () => { retry: 3, }); - if (isLoading) return
    로딩 중...
    ; if (error) return
    에러가 발생했습니다
    ; if (!reviewImages) return null; @@ -81,7 +80,7 @@ const StudioReviewPhotos = () => { - {reviewImages.imageDtos.map(({ id, url }, index) => ( + {reviewImages.imageDtos.map(({ id, url }) => (
    open()}> {`리뷰
    diff --git a/src/routes.tsx b/src/routes.tsx index cd0cc50..8659fa3 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -1,14 +1,16 @@ -import Home from '@pages/Home/Home'; -import Search from '@pages/search/Search'; -import SearchResults from '@pages/search/SearchResult'; -import StudioMain from '@pages/Studio/StudioMain/StudioMain'; -import StudioMenu from '@pages/Studio/StudioMenu/StudioMenu'; -import StudioMenuDetail from '@pages/Studio/StudioMenu/StudioMenuDetail'; -import StudioPortfolio from '@pages/Studio/StudioPortfolio/StudioPortfolio'; -import StudioReview from '@pages/Studio/StudioReview/StudioReview'; -import StudioReviewPhotos from '@pages/Studio/StudioReview/StudioReviewPhotos'; +import { lazy } from 'react'; import { createBrowserRouter } from 'react-router-dom'; +const Home = lazy(() => import('@pages/Home/Home')); +const Search = lazy(() => import('@pages/search/Search')); +const SearchResults = lazy(() => import('@pages/search/SearchResult')); +const StudioMain = lazy(() => import('@pages/Studio/StudioMain/StudioMain')); +const StudioMenu = lazy(() => import('@pages/Studio/StudioMenu/StudioMenu')); +const StudioMenuDetail = lazy(() => import('@pages/Studio/StudioMenu/StudioMenuDetail')); +const StudioPortfolio = lazy(() => import('@pages/Studio/StudioPortfolio/StudioPortfolio')); +const StudioReview = lazy(() => import('@pages/Studio/StudioReview/StudioReview')); +const StudioReviewPhotos = lazy(() => import('@pages/Studio/StudioReview/StudioReviewPhotos')); + const router = createBrowserRouter([ { path: '/', From 0d58f77e2ad0efbfde489955b761cb7162c470da Mon Sep 17 00:00:00 2001 From: woojoung Date: Fri, 20 Dec 2024 13:52:42 +0900 Subject: [PATCH 133/146] =?UTF-8?q?Feat:=20SEO=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=ED=97=AC=EB=A9=A7=20=ED=83=80=EC=9D=B4=ED=8B=80,?= =?UTF-8?q?=20=EB=A9=94=ED=83=80=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 +- src/pages/Studio/StudioReview/StudioReview.tsx | 2 +- src/pages/Studio/StudioReview/StudioReviewPhotos.tsx | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index abf1b27..93f7b1b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,7 +17,7 @@ function App() { 문제가 발생했습니다.}> - 로딩 중...~~~~딩 중...~~~~딩 중...~~~~딩 중...~~~~}> + 로딩 중.. 서스펜스 버전}> diff --git a/src/pages/Studio/StudioReview/StudioReview.tsx b/src/pages/Studio/StudioReview/StudioReview.tsx index ee28b9a..324ea82 100644 --- a/src/pages/Studio/StudioReview/StudioReview.tsx +++ b/src/pages/Studio/StudioReview/StudioReview.tsx @@ -55,7 +55,7 @@ const StudioReview = () => { <> {data && ( - {`스튜디오 리뷰 | ${totalReviewNum}개의 리뷰`} + {`스튜디오 리뷰 - ${totalReviewNum}개의 리뷰`} diff --git a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx index 1eacd13..19478b7 100644 --- a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx +++ b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx @@ -10,6 +10,7 @@ import { IReviewImages } from 'types/types'; import DimmedModal from '@pages/Studio/components/DimmedModal'; import { SwiperSlide } from 'swiper/react'; import useModal from '@hooks/useModal'; +import { Helmet } from 'react-helmet-async'; interface IReviewImagesResponse { totalElements: number; @@ -62,6 +63,13 @@ const StudioReviewPhotos = () => { return ( <> + {reviewImages && ( + + {`리뷰 사진모아보기 - ${reviewImages.totalElements}개`} + + + )} +
    From 37aac883e3036e3682ee763edda0fd6206217bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=ED=9D=AC=EC=84=A0?= Date: Fri, 20 Dec 2024 14:18:05 +0900 Subject: [PATCH 134/146] =?UTF-8?q?Add:=20=EB=8D=94=EB=B3=B4=EA=B8=B0=20?= =?UTF-8?q?=EC=95=84=EC=9D=B4=EC=BD=98=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/img/icon-morePreview.svg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 public/img/icon-morePreview.svg diff --git a/public/img/icon-morePreview.svg b/public/img/icon-morePreview.svg new file mode 100644 index 0000000..0def773 --- /dev/null +++ b/public/img/icon-morePreview.svg @@ -0,0 +1,3 @@ + + + From 4de6a80b7a980d25c7f3eddfa2fe9d5937348fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=ED=9D=AC=EC=84=A0?= Date: Fri, 20 Dec 2024 14:18:16 +0900 Subject: [PATCH 135/146] =?UTF-8?q?Style:=20=EB=B2=84=ED=8A=BC=20=EA=B3=B5?= =?UTF-8?q?=ED=86=B5=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=ED=83=80?= =?UTF-8?q?=EC=9D=B4=ED=8F=AC=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Button/Button.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index d241240..2d4b278 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -1,6 +1,7 @@ /** @jsxImportSource @emotion/react */ import { css } from '@emotion/react'; +import { TypoBodyMdM, TypoCapSmM, TypoTitleXsM } from '@styles/Common'; import variables from '@styles/Variables'; import React from 'react'; @@ -73,17 +74,17 @@ const Button = ({ const sizeStyles = { small: css` - font-size: small; + font-size: ${TypoCapSmM}; height: 3rem; padding: 0 1rem; `, medium: css` - font-size: medium; + font-size: ${TypoBodyMdM}; height: 3.6rem; padding: 0 1rem; `, large: css` - font-size: ${variables.size.big}; + font-size: ${TypoTitleXsM}; height: 4.8rem; `, }; From 3a51063d3bcafec1350bfae820ee30405241b4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=ED=9D=AC=EC=84=A0?= Date: Fri, 20 Dec 2024 14:18:32 +0900 Subject: [PATCH 136/146] =?UTF-8?q?Done:=20=ED=8F=AC=ED=8A=B8=ED=8F=B4?= =?UTF-8?q?=EB=A6=AC=EC=98=A4=20=EB=8D=94=EB=B3=B4=EA=B8=B0=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Studio/StudioMain/StudioMain.tsx | 61 +++++++++++++++++++--- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/src/pages/Studio/StudioMain/StudioMain.tsx b/src/pages/Studio/StudioMain/StudioMain.tsx index 9e5c956..af839de 100644 --- a/src/pages/Studio/StudioMain/StudioMain.tsx +++ b/src/pages/Studio/StudioMain/StudioMain.tsx @@ -3,17 +3,19 @@ import Header from '@components/Header/Header'; import StudioNavigator from '@components/Navigator/StudioNavigator'; import { css } from '@emotion/react'; +import styled from '@emotion/styled'; import { useGetStudioDetail } from '@hooks/useGetStudioDetail'; -import { DividerStyle, TypoBodyMdR, TypoBodyMdSb, TypoCapSmR, TypoTitleMdSb } from '@styles/Common'; +import { DividerStyle, TypoBodyMdR, TypoBodyMdSb, TypoCapSmM, TypoCapSmR, TypoTitleMdSb } from '@styles/Common'; import variables from '@styles/Variables'; -import { useParams } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router-dom'; ///studio/detail/{studioId} const StudioMain = () => { const { _id } = useParams(); const { data, isLoading, error } = useGetStudioDetail(`${_id}`); - console.log(data?.portfolios.map((v) => v)); + const navigate = useNavigate(); + console.log(data?.portfolios.length); if (isLoading) { return
    Loading...
    ; @@ -31,15 +33,22 @@ const StudioMain = () => { const placeHolderImage = '/img/img-nopic.png'; const missingImgCount = data.portfolios.length < 5 ? 5 - data.portfolios.length : 0; const portfolioWithPlaceHolders = [...data.portfolios, ...Array(missingImgCount).fill({ url: placeHolderImage })]; - + console.log(portfolioWithPlaceHolders); return ( <>
    {/* 이미지 */}
    - {portfolioWithPlaceHolders.slice(0, 5).map((v, i) => ( + {portfolioWithPlaceHolders.slice(0, 4).map((v, i) => ( {`Portfolio ))} +
    + 사진5 + navigate(`/studio/${_id}/portfolio`)}> + 더보기 + {data?.portfolios.length >= 5 ? `+ ${data?.portfolios.length - 5}` : ''} + +
    {/* 스튜디오 정보 */} @@ -121,13 +130,53 @@ const portfolioPreviewStyle = css` & > img { aspect-ratio: 1/1; object-fit: cover; - width: calc(100% - 0.1rem); + width: 100%; + height: 100%; } & > img:first-of-type { grid-column: span 1; /* 첫 번째 이미지는 2개의 열을 차지 */ grid-row: span 2; /* 첫 번째 이미지는 2개의 행을 차지 */ width: 100%; + height: 100%; + } +`; + +const portfolioPsitionStyle = css` + position: relative; + width: 100%; + height: 100%; + + & > img { + aspect-ratio: 1/1; + object-fit: cover; + width: 100%; + height: 100%; + } +`; + +const DimOverlayStyle = styled.div` + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + display: flex; + flex-direction: column; + gap: 0.2rem; + + & > img { + width: 1.8rem; + height: 1.8rem; + } + + & > span { + color: ${variables.colors.white}; + ${TypoCapSmM} } `; From 83a0df7f01417f97235802e83b899686f4dc7353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=84=ED=9D=AC=EC=84=A0?= Date: Fri, 20 Dec 2024 14:21:39 +0900 Subject: [PATCH 137/146] =?UTF-8?q?Logs:=20=EB=A1=9C=EA=B7=B8=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Studio/StudioMain/StudioMain.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/pages/Studio/StudioMain/StudioMain.tsx b/src/pages/Studio/StudioMain/StudioMain.tsx index af839de..f023fe3 100644 --- a/src/pages/Studio/StudioMain/StudioMain.tsx +++ b/src/pages/Studio/StudioMain/StudioMain.tsx @@ -13,13 +13,8 @@ import { useNavigate, useParams } from 'react-router-dom'; const StudioMain = () => { const { _id } = useParams(); - const { data, isLoading, error } = useGetStudioDetail(`${_id}`); + const { data, error } = useGetStudioDetail(`${_id}`); const navigate = useNavigate(); - console.log(data?.portfolios.length); - - if (isLoading) { - return
    Loading...
    ; - } if (error instanceof Error) { return
    Error: {error.message}
    ; @@ -33,7 +28,7 @@ const StudioMain = () => { const placeHolderImage = '/img/img-nopic.png'; const missingImgCount = data.portfolios.length < 5 ? 5 - data.portfolios.length : 0; const portfolioWithPlaceHolders = [...data.portfolios, ...Array(missingImgCount).fill({ url: placeHolderImage })]; - console.log(portfolioWithPlaceHolders); + return ( <>
    From 03179123b9f51c4ad3b52e6634086500b3e57d26 Mon Sep 17 00:00:00 2001 From: JWJung-99 <39busy@naver.com> Date: Fri, 20 Dec 2024 14:25:51 +0900 Subject: [PATCH 138/146] =?UTF-8?q?Style:=20StudioNavigator=20=EA=B0=80?= =?UTF-8?q?=EC=83=81=20=ED=81=B4=EB=9E=98=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 --- src/components/Navigator/StudioNavigator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Navigator/StudioNavigator.tsx b/src/components/Navigator/StudioNavigator.tsx index ba1b2ee..27a8917 100644 --- a/src/components/Navigator/StudioNavigator.tsx +++ b/src/components/Navigator/StudioNavigator.tsx @@ -59,7 +59,7 @@ const NavLinkStyle = styled(NavLink)` line-height: 2.4rem; } - &::after { + &::before { content: ''; position: absolute; background-color: ${variables.colors.gray300}; From a55885f2648ff8d1a030372ac4f2d05c1a7d51db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=EB=AF=BC?= Date: Fri, 20 Dec 2024 14:26:28 +0900 Subject: [PATCH 139/146] =?UTF-8?q?Cont:=20=EA=B3=B5=EC=9C=A0=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/CopyButton/CopyLocation.tsx | 16 ++-- src/components/Kakao/KaKaoShare.tsx | 1 - src/components/Share/Share.tsx | 95 ---------------------- src/components/Share/ShareButton.tsx | 46 +++++++++++ src/components/Share/ShareOptions.tsx | 93 +++++++++++++++++++++ 5 files changed, 150 insertions(+), 101 deletions(-) delete mode 100644 src/components/Share/Share.tsx create mode 100644 src/components/Share/ShareButton.tsx create mode 100644 src/components/Share/ShareOptions.tsx diff --git a/src/components/CopyButton/CopyLocation.tsx b/src/components/CopyButton/CopyLocation.tsx index 065f4c6..9135e2b 100644 --- a/src/components/CopyButton/CopyLocation.tsx +++ b/src/components/CopyButton/CopyLocation.tsx @@ -1,7 +1,7 @@ /** @jsxImportSource @emotion/react */ import { css } from '@emotion/react'; import variables from '@styles/Variables'; -import useClipboard from '@hooks/useClipboard'; +import { useState } from 'react'; interface CopyButtonProps { text: string; @@ -9,7 +9,13 @@ interface CopyButtonProps { } const CopyLocation = ({ text, buttonLabel }: CopyButtonProps) => { - const { isCopied, copyToClipboard } = useClipboard(text); + const [isCopied, setIsCopied] = useState(false); + + const copyToClipboard = async () => { + await navigator.clipboard.writeText(text); + setIsCopied(true); + setTimeout(() => setIsCopied(false), 1500); + }; return (
    @@ -22,7 +28,7 @@ const CopyLocation = ({ text, buttonLabel }: CopyButtonProps) => { 주소 복사 아이콘 {buttonLabel} - {isCopied && 복사되었습니다! 🎉} + {isCopied && Copied! 🎉}
    ); @@ -52,7 +58,7 @@ const buttonRowStyle = css` `; const iconStyle = css` - width: 1.6rem; /* 아이콘 크기 조정 */ + width: 1.6rem; height: 1.6rem; `; @@ -83,5 +89,5 @@ const buttonStyle = css` const feedbackStyle = css` font-size: 1.2rem; - color: #${variables.colors.gray200}; + color: ${variables.colors.gray800}; `; diff --git a/src/components/Kakao/KaKaoShare.tsx b/src/components/Kakao/KaKaoShare.tsx index c6933b4..804744a 100644 --- a/src/components/Kakao/KaKaoShare.tsx +++ b/src/components/Kakao/KaKaoShare.tsx @@ -1,5 +1,4 @@ /** @jsxImportSource @emotion/react */ -import { css } from '@emotion/react'; import { useEffect } from 'react'; declare global { diff --git a/src/components/Share/Share.tsx b/src/components/Share/Share.tsx deleted file mode 100644 index 48b3d68..0000000 --- a/src/components/Share/Share.tsx +++ /dev/null @@ -1,95 +0,0 @@ -/** @jsxImportSource @emotion/react */ -import { css } from '@emotion/react'; -import useBottomSheetState from '@store/useBottomSheetStateStore'; -import BottomSheet from '@components/BottomSheet/BottomSheet'; -import KakaoShareButton from '@components/Kakao/KakaoShare'; -import useClipboard from '@hooks/useClipboard'; - -interface ShareProps { - title: string; - description: string; - imageUrl: string; - webUrl: string; -} - -const Share = ({ title, description, imageUrl, webUrl }: ShareProps) => { - const { openBottomSheet } = useBottomSheetState(); - const { copyToClipboard } = useClipboard(webUrl); - - const handleOpenKakaoShare = () => { - openBottomSheet( -
    -
    -
    - -
    - 카카오톡 -
    - -
    -
    - 링크 복사 -
    - 링크복사 -
    -
    , - '공유 옵션 선택', - ); - }; - return ( -
    - - -
    - ); -}; - -export default Share; - -const containerStyle = css` - display: inline-block; - width: auto; - height: auto; -`; - -const shareOptionsStyle = css` - display: flex; - justify-content: center; - align-items: center; - margin-top: 0.5rem; - gap: 2rem; -`; - -const iconWithLabel = css` - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 0.5rem; -`; - -const shareButtonStyle = css` - cursor: pointer; - width: 4.8rem; - height: 4.8rem; - - img { - width: 100%; - height: 100%; - } -`; - -const textStyle = css` - font-size: 1.2rem; -`; - -/* 사용예시 - -*/ diff --git a/src/components/Share/ShareButton.tsx b/src/components/Share/ShareButton.tsx new file mode 100644 index 0000000..7b4062c --- /dev/null +++ b/src/components/Share/ShareButton.tsx @@ -0,0 +1,46 @@ +/** @jsxImportSource @emotion/react */ +import { css, SerializedStyles } from '@emotion/react'; +import useBottomSheetState from '@store/useBottomSheetStateStore'; +import BottomSheet from '@components/BottomSheet/BottomSheet'; +import ShareOptions from '@components/Share/ShareOptions'; + +interface ShareProps { + title: string; + description: string; + imageUrl: string; + webUrl: string; +} + +const ShareButton = ({ title, description, imageUrl, webUrl }: ShareProps) => { + const { openBottomSheet } = useBottomSheetState(); + + const handleOpenBottomSheet = () => { + openBottomSheet(, '공유 옵션 선택'); + }; + + return ( +
    + + +
    + ); +}; + +export default ShareButton; + +const containerStyle = css` + display: inline-block; + width: auto; + height: auto; +`; + +/* 사용예시 + +*/ diff --git a/src/components/Share/ShareOptions.tsx b/src/components/Share/ShareOptions.tsx new file mode 100644 index 0000000..e235b65 --- /dev/null +++ b/src/components/Share/ShareOptions.tsx @@ -0,0 +1,93 @@ +/** @jsxImportSource @emotion/react */ +import { css } from '@emotion/react'; +import KakaoShareButton from '@components/Kakao/KakaoShare'; +import variables from '@styles/Variables'; +import { useState } from 'react'; +import { createPortal } from 'react-dom'; + +interface ShareProps { + title: string; + description: string; + imageUrl: string; + webUrl: string; +} + +const ShareOptions = ({ title, description, imageUrl, webUrl }: ShareProps) => { + const [isCopied, setIsCopied] = useState(false); + + const copyToClipboard = async () => { + await navigator.clipboard.writeText(webUrl); + setIsCopied(true); + setTimeout(() => setIsCopied(false), 1500); + }; + + return ( +
    +
    +
    + +
    + 카카오톡 +
    + +
    +
    + 링크 복사 +
    + 링크복사 + {/* 피드백 메시지를 포털을 통해 바텀시트 외부로 렌더링 */} + {isCopied && + createPortal( +
    Copied! 🎉
    , + document.body, // 바텀시트 밖에 메시지를 렌더링 + )} +
    +
    + ); +}; + +export default ShareOptions; + +const shareOptionsStyle = css` + display: flex; + justify-content: center; + align-items: center; + margin-top: 0.5rem; + gap: 2rem; +`; + +const iconWithLabel = css` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.5rem; +`; + +const shareButtonStyle = css` + cursor: pointer; + width: 4.8rem; + height: 4.8rem; + + img { + width: 100%; + height: 100%; + } +`; + +const textStyle = css` + font-size: 1.2rem; +`; + +const feedbackStyle = css` + font-size: 1.2rem; + color: ${variables.colors.gray800}; + position: fixed; + background-color: ${variables.colors.gray800}; + color: ${variables.colors.white}; + border-radius: 0.5rem; + padding: 0.3rem 0.5rem; + bottom: 2rem; + z-index: 1000; + left: 18.5rem; +`; From 78d2e8e2929542a19a2e5cb7533dee1463560a0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=EB=AF=BC?= Date: Fri, 20 Dec 2024 14:27:16 +0900 Subject: [PATCH 140/146] =?UTF-8?q?Cont:=20useClipBoard=20=ED=9B=85=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useClipBoard.ts | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 src/hooks/useClipBoard.ts diff --git a/src/hooks/useClipBoard.ts b/src/hooks/useClipBoard.ts deleted file mode 100644 index 4ac427e..0000000 --- a/src/hooks/useClipBoard.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useState } from 'react'; - -const Clipboard = (text: string) => { - const [isCopied, setIsCopied] = useState(false); - - const copyToClipboard = async () => { - await navigator.clipboard.writeText(text); - setIsCopied(true); - setTimeout(() => setIsCopied(false), 1500); - }; - - return { isCopied, copyToClipboard }; -}; - -export default Clipboard; From 12a7ce75d45daab6a0b71382a1519821095618d5 Mon Sep 17 00:00:00 2001 From: woojoung Date: Fri, 20 Dec 2024 14:53:33 +0900 Subject: [PATCH 141/146] =?UTF-8?q?Feat:=20=EB=A6=AC=EB=B7=B0=20=EC=BB=A8?= =?UTF-8?q?=ED=85=90=EC=B8=A0=203=EC=A4=84=20=EC=9D=B4=EC=83=81=20?= =?UTF-8?q?=EC=9D=BC=20=EA=B2=BD=EC=9A=B0=EB=A7=8C=20=EB=8D=94=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20=ED=99=9C=EC=84=B1=ED=99=94=20=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/img/icon-none.svg | 1 + src/components/ImageSwiper/ImageSwiper.tsx | 3 +-- .../StudioPortfolio/StudioPortfolio.tsx | 2 +- .../StudioReview/components/ReviewContent.tsx | 22 ++++++++++++++++--- .../components/StudioReviewItem.tsx | 9 ++++---- 5 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 public/img/icon-none.svg diff --git a/public/img/icon-none.svg b/public/img/icon-none.svg new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/public/img/icon-none.svg @@ -0,0 +1 @@ + diff --git a/src/components/ImageSwiper/ImageSwiper.tsx b/src/components/ImageSwiper/ImageSwiper.tsx index 9ff8ace..a7ccf87 100644 --- a/src/components/ImageSwiper/ImageSwiper.tsx +++ b/src/components/ImageSwiper/ImageSwiper.tsx @@ -11,8 +11,7 @@ interface ImageSwiperProps extends SwiperProps { imageStyle?: ReturnType; imgprops?: { - - customStyle?: ReturnType; + customStyle?: ReturnType; loading?: string; onLoad?: (e: React.SyntheticEvent) => void; onClick?: () => void; diff --git a/src/pages/Studio/StudioPortfolio/StudioPortfolio.tsx b/src/pages/Studio/StudioPortfolio/StudioPortfolio.tsx index aa384f8..a369edc 100644 --- a/src/pages/Studio/StudioPortfolio/StudioPortfolio.tsx +++ b/src/pages/Studio/StudioPortfolio/StudioPortfolio.tsx @@ -38,7 +38,7 @@ const StudioPortfolio = () => { return ( <> -
    +
    diff --git a/src/pages/Studio/StudioReview/components/ReviewContent.tsx b/src/pages/Studio/StudioReview/components/ReviewContent.tsx index 4f855bf..4cababb 100644 --- a/src/pages/Studio/StudioReview/components/ReviewContent.tsx +++ b/src/pages/Studio/StudioReview/components/ReviewContent.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useRef, useEffect, useState } from 'react'; import styled from '@emotion/styled'; import variables from '@styles/Variables'; import { TypoCapSmR } from '@styles/Common'; @@ -10,10 +10,26 @@ interface ReviewContentProps { } const ReviewContent = ({ content, isOpen, setIsOpen }: ReviewContentProps): JSX.Element => { + const [isTextOverflow, setIsTextOverflow] = useState(false); + const textRef = useRef(null); + + useEffect(() => { + const element = textRef.current; + if (element) { + // 실제 콘텐츠 높이와 표시되는 라인 수를 비교 + const lineHeight = parseInt(window.getComputedStyle(element).lineHeight); + const height = element.scrollHeight; + const lines = height / lineHeight; + setIsTextOverflow(lines > 3); + } + }, [content]); + return ( - {content} - setIsOpen(!isOpen)}>{isOpen ? '접기' : '더보기'} + + {content + content + content} + + {isTextOverflow && setIsOpen(!isOpen)}>{isOpen ? '접기' : '더보기'}} ); }; diff --git a/src/pages/Studio/StudioReview/components/StudioReviewItem.tsx b/src/pages/Studio/StudioReview/components/StudioReviewItem.tsx index bf3e6ad..8ec1159 100644 --- a/src/pages/Studio/StudioReview/components/StudioReviewItem.tsx +++ b/src/pages/Studio/StudioReview/components/StudioReviewItem.tsx @@ -66,11 +66,12 @@ const StudioReviewItem = ({ review }: { review: Review }) => { export default StudioReviewItem; const StudioReviewItemContainerStyle = styled.div<{ isOpen: boolean }>` - width: 100%; - - margin-top: 1rem; + width: 100vw; + margin-left: calc(-1 * ${variables.layoutPadding}); + padding: 0 ${variables.layoutPadding}; + padding-top: 1rem; border-bottom: 1px solid ${variables.colors.gray300}; - background-color: ${({ isOpen }) => (isOpen ? variables.colors.gray100 : 'transparent')}; + background-color: ${({ isOpen }) => (isOpen ? variables.colors.gray200 : 'transparent')}; transition: background-color 0.2s ease; `; From 8845f88a2199cd9034ccc1d59082ed6c2798488e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=EB=AF=BC?= Date: Fri, 20 Dec 2024 14:59:17 +0900 Subject: [PATCH 142/146] =?UTF-8?q?Style:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=8A=AC=EB=9D=BC=EC=9D=B4=EB=93=9C=20=EC=8A=A4=ED=83=80?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ImageSwiper/ImageSwiper.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/components/ImageSwiper/ImageSwiper.tsx b/src/components/ImageSwiper/ImageSwiper.tsx index 9ff8ace..6a842dc 100644 --- a/src/components/ImageSwiper/ImageSwiper.tsx +++ b/src/components/ImageSwiper/ImageSwiper.tsx @@ -9,13 +9,11 @@ import { IPortfolio, IReviewImages } from 'types/types'; interface ImageSwiperProps extends SwiperProps { images: IPortfolio[] | IReviewImages[]; imageStyle?: ReturnType; - - imgprops?: { - customStyle?: ReturnType; + imgprops?: { + customStyle?: ReturnType; loading?: string; onLoad?: (e: React.SyntheticEvent) => void; - onClick?: () => void; }; } @@ -72,9 +70,7 @@ const ImageSwiper = ({ export default ImageSwiper; const containerFullStyle = css` - width: 100vw; margin-left: calc(-1 * ${variables.layoutPadding}); - margin-right: calc(-1 * ${variables.layoutPadding}); `; const containerDefaultStyle = css` From e73a51c8f9b8815a29cbb748e787026db4a2bdd0 Mon Sep 17 00:00:00 2001 From: woojoung Date: Fri, 20 Dec 2024 15:08:23 +0900 Subject: [PATCH 143/146] =?UTF-8?q?Hotfix=20:=20=EC=B6=A9=EB=8F=8C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Studio/StudioReview/StudioReviewPhotos.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx index 19478b7..1e4fb8a 100644 --- a/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx +++ b/src/pages/Studio/StudioReview/StudioReviewPhotos.tsx @@ -28,7 +28,7 @@ const StudioReviewPhotos = () => { const { open } = useModal(1); const fetchReviewImage = async () => { // 리뷰사진 모아보기 조회 - + // const url = new URL(`${import.meta.env.VITE_TOUCHEESE_API}/studio/detail/${_id}/reviewImage`); if (selectedMenuId) { From a2b0702e990ff6602e3036d8b94c76b25c52c619 Mon Sep 17 00:00:00 2001 From: kyungmim Date: Fri, 20 Dec 2024 15:17:39 +0900 Subject: [PATCH 144/146] =?UTF-8?q?Hotfix:=20=EB=A9=94=EB=89=B4=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=98=A4=EB=A5=98=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 --- src/components/ImageSwiper/ImageSwiper.tsx | 5 +---- src/pages/Studio/StudioMenu/StudioMenu.tsx | 5 ++--- src/pages/Studio/StudioMenu/StudioMenuDetail.tsx | 2 +- .../Studio/StudioMenu/StudioMenuDetailInfo.tsx | 13 +++++++++---- src/types/types.ts | 5 ++++- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/components/ImageSwiper/ImageSwiper.tsx b/src/components/ImageSwiper/ImageSwiper.tsx index 9ff8ace..625813d 100644 --- a/src/components/ImageSwiper/ImageSwiper.tsx +++ b/src/components/ImageSwiper/ImageSwiper.tsx @@ -11,8 +11,7 @@ interface ImageSwiperProps extends SwiperProps { imageStyle?: ReturnType; imgprops?: { - - customStyle?: ReturnType; + customStyle?: ReturnType; loading?: string; onLoad?: (e: React.SyntheticEvent) => void; onClick?: () => void; @@ -72,9 +71,7 @@ const ImageSwiper = ({ export default ImageSwiper; const containerFullStyle = css` - width: 100vw; margin-left: calc(-1 * ${variables.layoutPadding}); - margin-right: calc(-1 * ${variables.layoutPadding}); `; const containerDefaultStyle = css` diff --git a/src/pages/Studio/StudioMenu/StudioMenu.tsx b/src/pages/Studio/StudioMenu/StudioMenu.tsx index fdd4e15..736c59c 100644 --- a/src/pages/Studio/StudioMenu/StudioMenu.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenu.tsx @@ -1,6 +1,5 @@ /** @jsxImportSource @emotion/react */ import { css } from '@emotion/react'; -import BackButton from '@components/BackButton/BackButton'; import StudioNavigator from '@components/Navigator/StudioNavigator'; import StudioMenuItem from './StudioMenuItem'; import { useParams } from 'react-router-dom'; @@ -40,8 +39,8 @@ const StudioMenu = () => { const StudioMenuList = data?.map((item) => ); return ( <> -
    - +
    +
    {StudioMenuList}
    ); diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx index d8fd4b7..3ac60b1 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuDetail.tsx @@ -16,7 +16,7 @@ const StudioMenuDetail = () => { const [data, setData] = useState(); const [scrollY, setScrollY] = useState(false); const [tabMenuState, setTabMenuState] = useState('info'); - const [totalPrice, setTotalPrice] = useState(data ? data.price : 0); + const [totalPrice, setTotalPrice] = useState(0); const [checkState, setCheckState] = useState>({}); const fetchMenuDetail = async () => { diff --git a/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx b/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx index 360253c..dc99bb6 100644 --- a/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx +++ b/src/pages/Studio/StudioMenu/StudioMenuDetailInfo.tsx @@ -16,6 +16,9 @@ const StudioMenuDetailInfo = ({ checkState: Record; setCheckState: Dispatch>>; }) => { + const [hours, minutes, seconds] = infoItem.duration ? infoItem.duration.split(':').map(Number) : [0, 0, 0]; + const totalMinutes = hours * 60 + minutes + seconds / 60; + const handleOptionClick = (price: number, id: number, e: React.ChangeEvent) => { const isChecked = e.target.checked; setTotalPrice((prev) => (isChecked ? prev + price : prev - price)); @@ -28,25 +31,27 @@ const StudioMenuDetailInfo = ({ // console.log('최종 결제 금액:', totalPrice); // }; + console.log(infoItem); + return ( <>

    예상 소요 시간

    -

    약 60분

    +

    약 {infoItem.duration ? totalMinutes : 60}분

    기본 촬영 수

    -

    70-80컷

    +

    {infoItem.pictureNum ? infoItem.pictureNum : `70-80컷`}

    인화 사이즈

    -

    4x6in

    +

    {infoItem.pictureSize ? infoItem.pictureSize : '4x6in'}

    기본 제공 파일

    -

    3포즈 리터칭 JPG파일

    +

    {infoItem.offerFile ? infoItem.offerFile : '3포즈 리터칭 JPG파일'}

    diff --git a/src/types/types.ts b/src/types/types.ts index 83d92f2..370273d 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -112,9 +112,12 @@ export interface IMenuListRes { name: string; description: string; price: number; - duration: string | null; additionalOptions: IAdditionalOptionsRes[]; menuImages: IPortfolio[] | IReviewImages[]; + duration: string | null; //소요시간 + offerFile: string | null; //기본 제공 파일 + pictureNum: string | null; //촬영 수 + pictureSize: string | null; //인화 사이즈 avgScore: number; reviews: { content: Review[] }; reviewCount: number; From 5b3715240f657e4e52b014553b1902006fa00383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A7=80=EB=AF=BC?= Date: Fri, 20 Dec 2024 15:21:36 +0900 Subject: [PATCH 145/146] =?UTF-8?q?Fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=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 --- src/components/Share/ShareButton.tsx | 11 +---------- src/components/Share/ShareOptions.tsx | 10 +++------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/components/Share/ShareButton.tsx b/src/components/Share/ShareButton.tsx index 7b4062c..8eb93b2 100644 --- a/src/components/Share/ShareButton.tsx +++ b/src/components/Share/ShareButton.tsx @@ -1,5 +1,5 @@ /** @jsxImportSource @emotion/react */ -import { css, SerializedStyles } from '@emotion/react'; +import { css } from '@emotion/react'; import useBottomSheetState from '@store/useBottomSheetStateStore'; import BottomSheet from '@components/BottomSheet/BottomSheet'; import ShareOptions from '@components/Share/ShareOptions'; @@ -35,12 +35,3 @@ const containerStyle = css` width: auto; height: auto; `; - -/* 사용예시 - -*/ diff --git a/src/components/Share/ShareOptions.tsx b/src/components/Share/ShareOptions.tsx index e235b65..2ca51e9 100644 --- a/src/components/Share/ShareOptions.tsx +++ b/src/components/Share/ShareOptions.tsx @@ -1,6 +1,6 @@ /** @jsxImportSource @emotion/react */ import { css } from '@emotion/react'; -import KakaoShareButton from '@components/Kakao/KakaoShare'; +import KakaoShareButton from '@components/Kakao/KaKaoShare'; import variables from '@styles/Variables'; import { useState } from 'react'; import { createPortal } from 'react-dom'; @@ -35,12 +35,8 @@ const ShareOptions = ({ title, description, imageUrl, webUrl }: ShareProps) => { 링크 복사
    링크복사 - {/* 피드백 메시지를 포털을 통해 바텀시트 외부로 렌더링 */} - {isCopied && - createPortal( -
    Copied! 🎉
    , - document.body, // 바텀시트 밖에 메시지를 렌더링 - )} + + {isCopied && createPortal(
    Copied! 🎉
    , document.body)} ); From 427279af2ba6941a4896d2fd57c02604602fc307 Mon Sep 17 00:00:00 2001 From: kyungmim Date: Fri, 20 Dec 2024 15:24:15 +0900 Subject: [PATCH 146/146] =?UTF-8?q?Type:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=8A=A4=EC=99=80=EC=9D=B4=ED=8D=BC=20=EC=B6=A9=EB=8F=8C?= =?UTF-8?q?=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ImageSwiper/ImageSwiper.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/ImageSwiper/ImageSwiper.tsx b/src/components/ImageSwiper/ImageSwiper.tsx index 625813d..6a842dc 100644 --- a/src/components/ImageSwiper/ImageSwiper.tsx +++ b/src/components/ImageSwiper/ImageSwiper.tsx @@ -9,12 +9,11 @@ import { IPortfolio, IReviewImages } from 'types/types'; interface ImageSwiperProps extends SwiperProps { images: IPortfolio[] | IReviewImages[]; imageStyle?: ReturnType; - + customStyle?: ReturnType; imgprops?: { customStyle?: ReturnType; loading?: string; onLoad?: (e: React.SyntheticEvent) => void; - onClick?: () => void; }; }