From f30d2ef645ab60d15199aa1a61ad818177012a9b Mon Sep 17 00:00:00 2001
From: hamxxn <150767569+hamxxn@users.noreply.github.com>
Date: Wed, 20 Aug 2025 21:41:50 +0900
Subject: [PATCH 1/3] feat: voting page
---
src/pages/promiseStatus/PromiseStatus.tsx | 8 +-
.../promiseStatus/components/Voting.css.ts | 52 +++++++
src/pages/promiseStatus/components/Voting.tsx | 130 +++++++++++++++++-
src/pages/promiseStatus/mockUp.ts | 45 ++++++
4 files changed, 229 insertions(+), 6 deletions(-)
create mode 100644 src/pages/promiseStatus/components/Voting.css.ts
diff --git a/src/pages/promiseStatus/PromiseStatus.tsx b/src/pages/promiseStatus/PromiseStatus.tsx
index 22b0cea..fbe1e47 100644
--- a/src/pages/promiseStatus/PromiseStatus.tsx
+++ b/src/pages/promiseStatus/PromiseStatus.tsx
@@ -1,8 +1,8 @@
import { useSearchParams } from 'react-router-dom';
-import Pending from './components/Pending';
+import Pending from '@/pages/promiseStatus/components/Pending';
import { PROMISE_STATUS } from '@shared/constant/promiseStatus';
-import Voting from './components/Voting';
-import Confirmed from './components/Confirmed';
+import Voting from '@/pages/promiseStatus/components/Voting';
+import Confirmed from '@/pages/promiseStatus/components/Confirmed';
export default function PromiseStatus() {
const [searchParams] = useSearchParams();
@@ -13,7 +13,7 @@ export default function PromiseStatus() {
return (
<>
{status === PROMISE_STATUS.PENDING && }
- {status === PROMISE_STATUS.VOTING && }
+ {status === PROMISE_STATUS.VOTING && }
{status === PROMISE_STATUS.CONFIRMED && }
>
);
diff --git a/src/pages/promiseStatus/components/Voting.css.ts b/src/pages/promiseStatus/components/Voting.css.ts
new file mode 100644
index 0000000..7878289
--- /dev/null
+++ b/src/pages/promiseStatus/components/Voting.css.ts
@@ -0,0 +1,52 @@
+import { vars } from '@shared/styles/theme.css';
+import { style } from '@vanilla-extract/css';
+
+export const votingWrapper = style({
+ position: 'relative',
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ background: vars.color.blue0,
+});
+
+export const containerStyle = style({
+ width: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ gap: '40px',
+ minHeight: 'calc(100vh - 9.8rem - 8rem)',
+ background: vars.color.blue0,
+});
+
+export const votingText = style({
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'flex-start',
+ gap: '1rem',
+});
+
+export const votingPlaceWrapper = style({
+ width: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'flex-start',
+ gap: '2rem',
+});
+
+export const votingButtonWrapper = style({
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ gap: '1rem',
+ width: '100%',
+});
+
+export const votingButton = style({
+ position: 'sticky',
+ bottom: '0',
+ left: '0',
+ right: '0',
+ width: '100%',
+ padding: '1.5rem',
+});
diff --git a/src/pages/promiseStatus/components/Voting.tsx b/src/pages/promiseStatus/components/Voting.tsx
index 8929525..e64bff8 100644
--- a/src/pages/promiseStatus/components/Voting.tsx
+++ b/src/pages/promiseStatus/components/Voting.tsx
@@ -1,8 +1,134 @@
+import Header from '@shared/components/header/Header';
+import { IcNavX } from '@svg/index';
+import { useNavigate } from 'react-router-dom';
+import * as styles from '@/pages/promiseStatus/components/Voting.css';
+import Container from '@shared/components/container/Container';
+import Text from '@shared/components/text/Text';
+import { mockUpVoting } from '@/pages/promiseStatus/mockUp';
+import Button from '@shared/components/button/Button';
+import { PROMISE_STATUS_CONFIG } from '@shared/constant/promiseStatus';
+import { BUTTON_VARIANTS } from '@shared/components/button/constant/button';
+import { useState } from 'react';
+
interface VotingProps {
promiseId: string;
isHost: boolean;
}
export default function Voting({ promiseId, isHost }: VotingProps) {
- console.log(promiseId, isHost);
- return
;
+ const navigate = useNavigate();
+ const handleClose = () => {
+ console.log(promiseId);
+ navigate(`/`);
+ };
+ const { promiseName, promisePlace, promiseAvailableTimes } = mockUpVoting;
+ const [selectedPlace, setSelectedPlace] = useState<
+ {
+ placeName: string;
+ placeAddress: string;
+ }[]
+ >([]);
+ const [selectedTime, setSelectedTime] = useState<
+ {
+ date: string;
+ startTime: string;
+ endTime: string;
+ }[]
+ >([]);
+
+ const handlePlaceClick = (place: { placeName: string; placeAddress: string }) => {
+ if (selectedPlace.includes(place)) {
+ setSelectedPlace(prev => prev.filter(p => p !== place));
+ } else {
+ setSelectedPlace(prev => [...prev, place]);
+ }
+ };
+
+ const handleTimeClick = (time: { date: string; startTime: string; endTime: string }) => {
+ if (selectedTime.includes(time)) {
+ setSelectedTime(prev =>
+ prev.filter(
+ t => t.date !== time.date && t.startTime !== time.startTime && t.endTime !== time.endTime
+ )
+ );
+ } else {
+ setSelectedTime(prev => [...prev, time]);
+ }
+ };
+
+ const handleVote = () => {
+ console.log(selectedPlace, selectedTime);
+ handleClose();
+ };
+ å;
+ const isFormValid = !isHost && selectedPlace.length > 0 && selectedTime.length > 0;
+
+ return (
+
+
+
+
+
+ {promiseName}의 최종 투표를 진행해주세요
+
+
+ 최적의 시간과 장소입니다
+
+ {isHost && (
+
+ 약속 생성자는 투표 할 수 없습니다.
+
+ )}
+
+
+
+
+ 약속 장소를 선택해주세요
+
+
+ {promisePlace.map(place => {
+ const isSelected = selectedPlace.includes(place);
+ return (
+
+
+
+
+ 약속 시간을 선택해주세요
+
+
+ {promiseAvailableTimes.map(time => {
+ const isSelected = selectedTime.includes(time);
+ return (
+
+
+
+
+
+
+
+ );
}
diff --git a/src/pages/promiseStatus/mockUp.ts b/src/pages/promiseStatus/mockUp.ts
index 6e14e87..66ecf48 100644
--- a/src/pages/promiseStatus/mockUp.ts
+++ b/src/pages/promiseStatus/mockUp.ts
@@ -76,3 +76,48 @@ export const mockUpPlace = {
},
],
};
+
+export const mockUpVoting = {
+ promiseName: 'KUIT BARO 2차 회의',
+ promisePlace: [
+ {
+ placeName: '강남역 스타벅스',
+ placeAddress: '서울특별시 강남구 역삼동 123-123',
+ },
+ {
+ placeName: '건국대학교 중앙도서관',
+ placeAddress: '서울특별시 강남구 역삼동 123-123',
+ },
+
+ {
+ placeName: '강남역 투썸플레이스',
+ placeAddress: '서울특별시 강남구 역삼동 123-123',
+ },
+ {
+ placeName: '건국대학교 신공학관',
+ placeAddress: '서울특별시 강남구 역삼동 123-123',
+ },
+ ],
+ promiseAvailableTimes: [
+ {
+ date: '2025-04-03',
+ startTime: '12:00:00',
+ endTime: '12:30:00',
+ },
+ {
+ date: '2025-04-03',
+ startTime: '12:00:00',
+ endTime: '12:30:00',
+ },
+ {
+ date: '2025-04-03',
+ startTime: '12:00:00',
+ endTime: '12:30:00',
+ },
+ {
+ date: '2025-04-03',
+ startTime: '12:00:00',
+ endTime: '12:30:00',
+ },
+ ],
+};
From 651f1d3feb31c830267bde806928f573a9d8a0c8 Mon Sep 17 00:00:00 2001
From: hamxxn <150767569+hamxxn@users.noreply.github.com>
Date: Wed, 20 Aug 2025 22:05:19 +0900
Subject: [PATCH 2/3] feat: confirmed page
---
src/pages/promiseStatus/PromiseStatus.tsx | 2 +-
.../promiseStatus/components/Confirmed.css.ts | 40 ++++++++++
.../promiseStatus/components/Confirmed.tsx | 76 ++++++++++++++++++-
src/pages/promiseStatus/components/Voting.tsx | 2 +-
src/pages/promiseStatus/mockUp.ts | 15 ++++
5 files changed, 129 insertions(+), 6 deletions(-)
create mode 100644 src/pages/promiseStatus/components/Confirmed.css.ts
diff --git a/src/pages/promiseStatus/PromiseStatus.tsx b/src/pages/promiseStatus/PromiseStatus.tsx
index fbe1e47..3404ad9 100644
--- a/src/pages/promiseStatus/PromiseStatus.tsx
+++ b/src/pages/promiseStatus/PromiseStatus.tsx
@@ -14,7 +14,7 @@ export default function PromiseStatus() {
<>
{status === PROMISE_STATUS.PENDING && }
{status === PROMISE_STATUS.VOTING && }
- {status === PROMISE_STATUS.CONFIRMED && }
+ {status === PROMISE_STATUS.CONFIRMED && }
>
);
}
diff --git a/src/pages/promiseStatus/components/Confirmed.css.ts b/src/pages/promiseStatus/components/Confirmed.css.ts
new file mode 100644
index 0000000..7e03f7f
--- /dev/null
+++ b/src/pages/promiseStatus/components/Confirmed.css.ts
@@ -0,0 +1,40 @@
+import { vars } from '@shared/styles/theme.css';
+import { style } from '@vanilla-extract/css';
+
+export const confirmedWrapper = style({
+ position: 'relative',
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ paddingTop: '9.8rem',
+ height: '100vh',
+});
+
+export const containerStyle = style({
+ width: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'flex-start',
+ gap: '40px',
+});
+
+export const confirmedText = style({
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'flex-start',
+ gap: '1rem',
+});
+
+export const promiseDetailWrapper = style({
+ width: '100%',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+});
+
+export const confirmedButton = style({
+ position: 'fixed',
+ bottom: '0',
+ width: '100%',
+ padding: '1.5rem',
+});
diff --git a/src/pages/promiseStatus/components/Confirmed.tsx b/src/pages/promiseStatus/components/Confirmed.tsx
index fcd82e8..0af4fe5 100644
--- a/src/pages/promiseStatus/components/Confirmed.tsx
+++ b/src/pages/promiseStatus/components/Confirmed.tsx
@@ -1,8 +1,76 @@
+import { useNavigate } from 'react-router-dom';
+import Container from '@shared/components/container/Container';
+import Text from '@shared/components/text/Text';
+import { mockUpConfirmed } from '../mockUp';
+import KakaoMap from '@shared/components/kakaoMap/KakaoMap';
+import usePlaceSearch from '@shared/components/kakaoMap/hooks/usePlaceSearch';
+import { useState, useEffect } from 'react';
+import { MAP_SIZE } from '@shared/components/kakaoMap/constant/mapSize';
+import PromiseDetail from '@shared/components/promiseDetail/PromiseDetail';
+import { PROMISE_STATUS, PROMISE_STATUS_CONFIG } from '@shared/constant/promiseStatus';
+import Button from '@shared/components/button/Button';
+import { BUTTON_VARIANTS } from '@shared/components/button/constant/button';
+import * as styles from '@/pages/promiseStatus/components/Confirmed.css';
+
interface ConfirmedProps {
promiseId: string;
- isHost: boolean;
}
-export default function Confirmed({ promiseId, isHost }: ConfirmedProps) {
- console.log(promiseId, isHost);
- return ;
+export default function Confirmed({ promiseId }: ConfirmedProps) {
+ const navigate = useNavigate();
+ const { promiseName, promisePlace, promiseAvailableTimes, promiseMembersNames } = mockUpConfirmed;
+ const { searchPlace } = usePlaceSearch();
+ const [place, setPlace] = useState<{ lat: number; lng: number } | null>(null);
+
+ useEffect(() => {
+ searchPlace(promisePlace.placeAddress)
+ .then(res => res[0])
+ .then(placeData => {
+ if (placeData) {
+ setPlace({ lat: placeData.lat, lng: placeData.lng });
+ }
+ })
+ .catch(error => {
+ console.error('Failed to search place:', error);
+ });
+ }, [searchPlace, promisePlace.placeAddress]);
+
+ const handleClose = () => {
+ console.log(promiseId);
+ navigate(`/`);
+ };
+
+ return (
+
+
+
+
+ {promiseName}
+
+
+ 약속이 확정되었어요
+
+
+
+
+
+
+
+
+
+ );
}
diff --git a/src/pages/promiseStatus/components/Voting.tsx b/src/pages/promiseStatus/components/Voting.tsx
index e64bff8..eaaea12 100644
--- a/src/pages/promiseStatus/components/Voting.tsx
+++ b/src/pages/promiseStatus/components/Voting.tsx
@@ -59,7 +59,7 @@ export default function Voting({ promiseId, isHost }: VotingProps) {
console.log(selectedPlace, selectedTime);
handleClose();
};
- å;
+
const isFormValid = !isHost && selectedPlace.length > 0 && selectedTime.length > 0;
return (
diff --git a/src/pages/promiseStatus/mockUp.ts b/src/pages/promiseStatus/mockUp.ts
index 66ecf48..36b27a4 100644
--- a/src/pages/promiseStatus/mockUp.ts
+++ b/src/pages/promiseStatus/mockUp.ts
@@ -121,3 +121,18 @@ export const mockUpVoting = {
},
],
};
+
+export const mockUpConfirmed = {
+ promiseName: 'KUIT BARO 2차 회의',
+ promisePlace: {
+ placeName: '강남역 스타벅스',
+ placeAddress: '서울특별시 강남구 역삼동 123-123',
+ },
+
+ promiseAvailableTimes: {
+ date: '2025-04-03',
+ startTime: '12:00:00',
+ endTime: '12:30:00',
+ },
+ promiseMembersNames: ['John Doe', 'Jane Doe', 'John Smith', 'Jane Smith'],
+};
From 848eb91c7940c6041532a02c0b4c64b7b3027cec Mon Sep 17 00:00:00 2001
From: hamxxn <150767569+hamxxn@users.noreply.github.com>
Date: Wed, 20 Aug 2025 22:09:48 +0900
Subject: [PATCH 3/3] fix: lint
---
src/pages/promiseStatus/PromiseStatus.tsx | 2 +-
src/pages/promiseStatus/components/Confirmed.css.ts | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/pages/promiseStatus/PromiseStatus.tsx b/src/pages/promiseStatus/PromiseStatus.tsx
index 3404ad9..eda96f6 100644
--- a/src/pages/promiseStatus/PromiseStatus.tsx
+++ b/src/pages/promiseStatus/PromiseStatus.tsx
@@ -13,7 +13,7 @@ export default function PromiseStatus() {
return (
<>
{status === PROMISE_STATUS.PENDING && }
- {status === PROMISE_STATUS.VOTING && }
+ {status === PROMISE_STATUS.VOTING && }
{status === PROMISE_STATUS.CONFIRMED && }
>
);
diff --git a/src/pages/promiseStatus/components/Confirmed.css.ts b/src/pages/promiseStatus/components/Confirmed.css.ts
index 7e03f7f..9365853 100644
--- a/src/pages/promiseStatus/components/Confirmed.css.ts
+++ b/src/pages/promiseStatus/components/Confirmed.css.ts
@@ -1,4 +1,3 @@
-import { vars } from '@shared/styles/theme.css';
import { style } from '@vanilla-extract/css';
export const confirmedWrapper = style({