diff --git a/apps/client/src/app/apolloClient.ts b/apps/client/src/app/apolloClient.ts new file mode 100644 index 0000000..bbe3680 --- /dev/null +++ b/apps/client/src/app/apolloClient.ts @@ -0,0 +1,10 @@ +// src/apolloClient.ts + +import { ApolloClient, InMemoryCache } from "@apollo/client"; + +const client = new ApolloClient({ + uri: process.env.NEXT_PUBLIC_BACKEND_URL, // GraphQL API 엔드포인트 + cache: new InMemoryCache(), +}); + +export default client; diff --git a/apps/client/src/app/component/votepage.tsx b/apps/client/src/app/component/votepage.tsx index 12ef3e5..83e53c4 100644 --- a/apps/client/src/app/component/votepage.tsx +++ b/apps/client/src/app/component/votepage.tsx @@ -1,8 +1,8 @@ "use client"; - +import client from "../apolloClient"; +import { useState, useEffect } from "react"; import Votemenu from "../votemenu/page"; - import { Container, Card, @@ -12,33 +12,91 @@ import { DateText, InfoWrapper, TitleText, - MenuWrapper - + MenuWrapper, + ALERDYTitleText, + ALERDYCard } from "../style/votepage"; +import { useQuery } from "@apollo/client"; +import { GET_ALL_VOTES, HAS_VOTED } from "../queries"; export default function Votepage() { - const category = "재미질문"; - const title = "학교에서 가장 무서운것은?"; - const date = "2025-05-23"; + const { loading: loadingVotes, error: errorVotes, data: dataVotes } = useQuery(GET_ALL_VOTES); + const [hasUserVotedResults, setHasUserVotedResults] = useState([]); + + // 비동기적으로 투표 여부를 확인하여 상태 업데이트 + useEffect(() => { + if (dataVotes?.vote?.getAllVotes?.length) { + const fetchVoteStatuses = async () => { + const results: boolean[] = []; + for (const vote of dataVotes.vote.getAllVotes) { + if (vote?.id) { + try { + const response = await client.query({ + query: HAS_VOTED, + variables: { + votedId: vote.id, + }, + }); + results.push(response.data.voteResponse.hasUserVoted); + } catch (error) { + + + } + } else { + console.warn(`Invalid vote object detected:`, vote); + // 유효하지 않은 객체에 기본 값 추가 + } + } + setHasUserVotedResults(results); // 모든 결과를 한 번에 상태 업데이트 + }; + + fetchVoteStatuses(); + } + }, [dataVotes]); + + if (loadingVotes) return

로딩 중...

; + if (errorVotes) return

에러 발생: {errorVotes.message}

; + if (!dataVotes?.vote?.getAllVotes) return

투표 데이터 없음

; + + + return ( - - - {category} - - {title} - - {date} 투표마감 - - - - - - - - + {dataVotes.vote.getAllVotes.map((vote: any, i: number) => ( + hasUserVotedResults[i++] === false ? ( + // 투표하지 않은 경우 +
+ + + {vote.category} + + {vote.title} + + {vote.finishedAt} 투표마감 + + + + + + + + +
+ ) : ( + // 이미 투표한 경우 +
+ + 이미 투표를 완료했습니다. + +
+ ) + ))}
); } diff --git a/apps/client/src/app/images/arrow4.svg b/apps/client/src/app/images/arrow4.svg new file mode 100644 index 0000000..14e43d6 --- /dev/null +++ b/apps/client/src/app/images/arrow4.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/client/src/app/images/arrow5.svg b/apps/client/src/app/images/arrow5.svg new file mode 100644 index 0000000..2a2b9e0 --- /dev/null +++ b/apps/client/src/app/images/arrow5.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/client/src/app/layout.tsx b/apps/client/src/app/layout.tsx index 4d6d1c4..4774fd3 100644 --- a/apps/client/src/app/layout.tsx +++ b/apps/client/src/app/layout.tsx @@ -2,6 +2,18 @@ import "./globals.css"; import Sidebar from "./component/sidebar"; +import { + ApolloClient, + InMemoryCache, + ApolloProvider as Provider, +} from '@apollo/client'; + + +const client = new ApolloClient({ + uri: process.env.NEXT_PUBLIC_BACKEND_URL, + cache: new InMemoryCache(), +}); + export default function RootLayout({ children, @@ -11,6 +23,7 @@ export default function RootLayout({ return ( +
@@ -20,6 +33,7 @@ export default function RootLayout({ {children}
+
); diff --git a/apps/client/src/app/mutations.js b/apps/client/src/app/mutations.js new file mode 100644 index 0000000..4b9893f --- /dev/null +++ b/apps/client/src/app/mutations.js @@ -0,0 +1,27 @@ +import { gql } from '@apollo/client'; +export const POST_VOTE = gql` + mutation MyMutation($title: String!, $category: String!, $options: [String!]!) { + vote { + createVote(input: { title: $title, category: $category, options: $options }) { + id + } + } + } +`; + + +export const POST_VOTERESUlT = gql` +mutation MyMutation($voteId : ID!,$optionId : ID!) { + voteResponse { + createVoteResponse(input: {voteId: $voteId, optionId: $optionId}) { + id + optionContent + optionId + userId + voteTitle + voteId + createdAt + } + } +} +`; \ No newline at end of file diff --git a/apps/client/src/app/mypage/page.tsx b/apps/client/src/app/mypage/page.tsx index e0a6fc7..c413059 100644 --- a/apps/client/src/app/mypage/page.tsx +++ b/apps/client/src/app/mypage/page.tsx @@ -2,6 +2,7 @@ import React from "react"; import arrow from "@/app/images/arrow3.svg"; +import { useRouter } from "next/navigation"; import { Container, Title, @@ -22,7 +23,22 @@ import { const name = "1134박기주"; const email = "24.013@bssm.hs.kr"; + + export default function Mypage() { + + const router = useRouter() ; + + +function Goreport(){ + router.push("/report"); +} + +function Gomyvote(){ + router.push("/myvote"); +} + + return ( 마이페이지 @@ -35,7 +51,7 @@ export default function Mypage() { + setShowModal(!showModal)}>투표 제작하기 - - - - ); diff --git a/apps/client/src/app/report/page.tsx b/apps/client/src/app/report/page.tsx new file mode 100644 index 0000000..b0f0a6b --- /dev/null +++ b/apps/client/src/app/report/page.tsx @@ -0,0 +1,139 @@ +"use client"; + +import React, { useState } from "react"; +import arrow from "@/app/images/arrow4.svg"; +import arrow5 from "@/app/images/arrow5.svg"; +import Image from "next/image"; +import { useRouter } from "next/navigation"; + + +import { + Container, + Title, + Sub, + ContentBox, + ListBox, + ListItem, + ListItemInner, + ListTitle, + ListInfo, + DetailBox, + DetailTitle, + DetailInfo, + DetailContent, + DetailStatus, + Images, + BackButtonWrapper, + BackArrowImage, + DetailWrapper, + CheckResultButton +} from "../style/report"; + +const name = "1134박기주"; +const email = "24.013@bssm.hs.kr"; + +type Post = { + id: string; + title: string; + writer: string; + date: string; + content: string; +}; + +const posts: Post[] = [ + { + id: "001", + title: "차수민쌤이랑 데이트 박제현쌤이랑 데이트", + writer: "오주현", + date: "2025.06.22", + content: "부산소프트웨어 마이스터고등학교는 총 64명이 한 학년에 있으며 곽상미 선생님이 참 귀여우시다 ㅎㅎ 그리고 짜장이가 참귀엽다 진짜 카와이하다 뽀뽀하고싶다,,ㅠㅠ" + }, + { + id: "002", + title: "오늘 점심 뭐 먹지?", + writer: "김예은", + date: "2025.06.21", + content: "오늘 점심은 김치볶음밥 먹을까? 아니면 짜장면?" + }, + { + id: "003", + title: "React 훅 정리 노트 공유합니다!", + writer: "이정환", + date: "2025.06.20", + content: "useState, useEffect, useMemo 등 정리해둔 노트 공유할게요." + }, + { + id: "004", + title: "Next.js 서버 컴포넌트 사용기", + writer: "정은지", + date: "2025.06.19", + content: "서버 컴포넌트를 사용해서 SSR 성능이 향상됨." + }, + { + id: "005", + title: "스타벅스 쿠폰 나눔해요~", + writer: "박지민", + date: "2025.06.18", + content: "쿠폰 유효기간 7월 15일까지! 필요하신 분 댓글 주세요~" + } +]; + + + +export default function Mypage() { + const [selectedPost, setSelectedPost] = useState(null); + const router = useRouter() ; + + function gotoback(){ + router.push("/mypage") + } + + return ( + + + + 마이페이지 + + 신고기록 조회 + + + {posts.map((post) => ( + + selectedPost?.id === post.id + ? setSelectedPost(null) + : setSelectedPost(post) + } + selected={selectedPost?.id === post.id} + > + + {post.title} + +

{post.writer}

+

{post.date}

+

{post.id}

+
+
+
+ ))} +
+ + {selectedPost && ( + + + {selectedPost.title} + {selectedPost.date} 제작 + {selectedPost.content} + + 결과확인하기 + + + + 현재 검토중인 질문입니다 + + )} +
+
+ ); +} diff --git a/apps/client/src/app/style/Mypage.ts b/apps/client/src/app/style/Mypage.ts index 422110f..e69a851 100644 --- a/apps/client/src/app/style/Mypage.ts +++ b/apps/client/src/app/style/Mypage.ts @@ -6,7 +6,7 @@ import Image from "next/image"; export const Container = styled.div` height: 100vh; padding: 3rem; - margin-left : 10vh; + margin-left : 24vh; `; @@ -64,6 +64,9 @@ export const Input = styled.input` `; export const StyledArrowImage = styled(Image)` + position : absolute; + left : 35vh; + top:-8px; transition: filter 0.1s ease; `; @@ -124,7 +127,8 @@ export const ButtonSection = styled.div` display : flex; justify-content : start; align-items: start; - gap : 25vh; + position : relative; + `; export const Buttonp = styled.p` @@ -137,6 +141,7 @@ export const Buttonp = styled.p` export const ButtonSub = styled.p` font-size: 1.4vh; font-weight: 400; + color : #A6A6A6 `; @@ -146,4 +151,5 @@ export const ButtonContent = styled.div` flex-direction : column; align-items : start; gap : 3vh; + margin-left : 4px; `; \ No newline at end of file diff --git a/apps/client/src/app/style/Vote.ts b/apps/client/src/app/style/Vote.ts index 920e13c..f3f4b23 100644 --- a/apps/client/src/app/style/Vote.ts +++ b/apps/client/src/app/style/Vote.ts @@ -1,9 +1,11 @@ import styled from "styled-components"; export const VoteContainer = styled.div` + margin-top :10vh; display: flex; flex-direction: column; gap: 4vh; position: relative; left: 16vh; + margin-bottom :10vh; `; \ No newline at end of file diff --git a/apps/client/src/app/style/Votemake.ts b/apps/client/src/app/style/Votemake.ts index cabc29f..59bb6f3 100644 --- a/apps/client/src/app/style/Votemake.ts +++ b/apps/client/src/app/style/Votemake.ts @@ -147,3 +147,35 @@ export const WarnP = styled.p` color: #ED3020; `; + + +export const OptionButton = styled(Button)` +position: relative; + +color: #0158de; +background-color: #FFFFFF; +border-color: #0158de; + +&:hover { + background-color: #004cc0; + color: #FFFFFF; +} + +width : 12vh; +height : 4vh; +border: 2px solid #0158de; +border-radius: 9999px; + +display : flex; +align-items : center; +justify-content : center; + +`; + + + +export const OptionC = styled.div` + display : flex; + gap : 2vh; + margin-bottom : 3vh; +` \ No newline at end of file diff --git a/apps/client/src/app/style/Votemenu.ts b/apps/client/src/app/style/Votemenu.ts index 1010dab..446a7e1 100644 --- a/apps/client/src/app/style/Votemenu.ts +++ b/apps/client/src/app/style/Votemenu.ts @@ -3,7 +3,7 @@ import styled from "styled-components"; export const Container = styled.div` display: flex; flex-direction: column; - gap: 4vh; + gap: 1vh; align-items: center; `; @@ -47,9 +47,9 @@ export const OptionLabel = styled.p` export const SubmitButton = styled.button.attrs(() => ({ type: "button", }))<{ $active: boolean }>` - position: relative; - left: 47vh; - top: 0vh; + position: absolute; + left: 95vh; + top: 32vh; font-family: "P_Regular"; width: 16vh; height: 4vh; diff --git a/apps/client/src/app/style/myvote.ts b/apps/client/src/app/style/myvote.ts new file mode 100644 index 0000000..74fd26e --- /dev/null +++ b/apps/client/src/app/style/myvote.ts @@ -0,0 +1,214 @@ +"use client"; + +import styled from "styled-components"; +import Image from "next/image"; + +export const Container = styled.div` + height: 100vh; + padding: 3rem; + width : 100%; + margin-left : 35vh; + +`; + +export const Title = styled.h1` + font-size: 5vh; + font-weight: 500; + margin-bottom:1rem; +`; + +export const Sub = styled.h1` + font-size: 3vh; + font-weight: 350; + margin-bottom: 5rem; +`; + + +export const ContentBox = styled.div` + display: flex; + flex-direction: row; + align-items: center; + justify-content: start; + gap: 1vh; + width: 100%; + height : 30vh; + border-radius: 2vh; +`; + +export const ProfileImage = styled(Image)` + width: 15vh; + height: 15vh; + margin-top : 14vh; + +`; + +export const InfoContainer = styled.div` + display: flex; + flex-direction: column; + gap: 1.25rem; + margin-top : 12vh; +`; + +export const Label = styled.p` + font-size: 2vh; + font-weight: 400; + margin-bottom:2vh; +`; + +export const Input = styled.input` + width: 40vh; + height: 40px; + border-radius: 8px; + border: none; + padding: 0 1rem; + font-size: 0.95rem; + background-color: white; + + + &::placeholder { + color: #a0a0a0; + } +`; + +export const StyledArrowImage = styled(Image)` + transition: filter 0.1s ease; +`; + +export const Button = styled.button` + + width: 43vh; + height: 12vh; + background-color: #FFFFFF; + + font-weight: 600; + border: 2px solid #8B8B8B; + border-radius: 6px; + cursor: pointer; + + &:hover { + border: 2px solid #0050d7; + ${StyledArrowImage} { + filter: invert(22%) sepia(100%) saturate(5740%) hue-rotate(213deg) brightness(95%) contrast(99%); + + } +`; + + + + +export const Form = styled.div` + display : flex; + flex-direction : column; + gap : 10vh; + +`; + + +export const Name = styled.p` + font-size: 2.5vh; +`; + + +export const NameForm = styled.div` + display : flex; + justify-content : start; + align-items: end; + gap : 2vh; + margin-left : 6vh; + margin-top : -15vh; +`; + +export const ButtonForm = styled.div` + display : flex; + justify-content : start; + align-items: end; + gap : 2vh; + margin-left : 6vh; + margin-top : 0vh; +`; + +export const ButtonSection = styled.div` + display : flex; + justify-content : start; + align-items: start; + gap : 25vh; + +`; + +export const Buttonp = styled.p` + font-size: 2vh; + font-weight: 400; + +`; + + +export const ButtonSub = styled.p` + font-size: 1.4vh; + font-weight: 400; +`; + + + +export const ButtonContent = styled.div` + display : flex; + flex-direction : column; + align-items : start; + gap : 3vh; + margin-left : 4px; +`; +// 리스트 박스 (왼쪽) +export const ListBox = styled.div` + width: 124vh; + height: 60vh; + background-color: #ffffff; + border-radius: 1vh; + display: flex; + flex-direction: column; + align-items: center; + padding-top: 2vh; + padding-bottom: 2vh; + overflow-y: scroll; +`; + +// 리스트 항목 +export const ListItem = styled.div<{ selected?: boolean }>` + cursor: pointer; + background-color: #fafafa; + width: 90%; + height: 9vh; + margin-bottom: 1vh; + display: flex; + flex-direction: column; + border-radius: 5px; + transition: background-color 0.2s ease; + outline : none; + &:hover { + background-color: #eee; + } + + ${({ selected }) => + selected && + ` + background-color: #f0f0f0; + `} +`; + +// 리스트 내부 텍스트 영역 +export const ListItemInner = styled.div` + margin-left: 2vh; + display: flex; + flex-direction: column; + gap: 1vh; +`; + +export const ListTitle = styled.p` + font-size: 2vh; + font-weight: 400; +`; + +export const ListInfo = styled.div` + display: flex; + gap: 2vh; + font-size: 1.5vh; + color: #888; +`; \ No newline at end of file diff --git a/apps/client/src/app/style/report.ts b/apps/client/src/app/style/report.ts new file mode 100644 index 0000000..77b46ef --- /dev/null +++ b/apps/client/src/app/style/report.ts @@ -0,0 +1,305 @@ +"use client"; + +import styled from "styled-components"; +import Image from "next/image"; + +export const Container = styled.div` + height: 100vh; + padding: 3rem; + width : 100%; + margin-left : 35vh; + +`; + +export const Title = styled.h1` + font-size: 5vh; + font-weight: 500; + margin-bottom:1rem; + position : relative; +`; + +export const Sub = styled.h1` + font-size: 3vh; + font-weight: 350; + margin-bottom: 5rem; +`; + + +export const ContentBox = styled.div` + display: flex; + flex-direction: row; + align-items: center; + justify-content: start; + gap: 1vh; + width: 100%; + height : 30vh; + border-radius: 2vh; +`; + +export const ProfileImage = styled(Image)` + width: 15vh; + height: 15vh; + margin-top : 14vh; + +`; + +export const InfoContainer = styled.div` + display: flex; + flex-direction: column; + gap: 1.25rem; + margin-top : 12vh; +`; + +export const Label = styled.p` + font-size: 2vh; + font-weight: 400; + margin-bottom:2vh; +`; + +export const Input = styled.input` + width: 40vh; + height: 40px; + border-radius: 8px; + border: none; + padding: 0 1rem; + font-size: 0.95rem; + background-color: white; + + + &::placeholder { + color: #a0a0a0; + } +`; + +export const StyledArrowImage = styled(Image)` + transition: filter 0.1s ease; +`; + +export const Button = styled.button` + + width: 43vh; + height: 12vh; + background-color: #FFFFFF; + + font-weight: 600; + border: 2px solid #8B8B8B; + border-radius: 6px; + cursor: pointer; + + &:hover { + border: 2px solid #0050d7; + ${StyledArrowImage} { + filter: invert(22%) sepia(100%) saturate(5740%) hue-rotate(213deg) brightness(95%) contrast(99%); + + } +`; + + + + +export const Form = styled.div` + display : flex; + flex-direction : column; + gap : 10vh; + +`; + + +export const Name = styled.p` + font-size: 2.5vh; +`; + + +export const NameForm = styled.div` + display : flex; + justify-content : start; + align-items: end; + gap : 2vh; + margin-left : 6vh; + margin-top : -15vh; +`; + +export const ButtonForm = styled.div` + display : flex; + justify-content : start; + align-items: end; + gap : 2vh; + margin-left : 6vh; + margin-top : 0vh; +`; + +export const ButtonSection = styled.div` + display : flex; + justify-content : start; + align-items: start; + gap : 25vh; + +`; + +export const Buttonp = styled.p` + font-size: 2vh; + font-weight: 400; + +`; + + +export const ButtonSub = styled.p` + font-size: 1.4vh; + font-weight: 400; +`; + + + +export const ButtonContent = styled.div` + display : flex; + flex-direction : column; + align-items : start; + gap : 3vh; + margin-left : 4px; +`; +// 리스트 박스 (왼쪽) +export const ListBox = styled.div` + width: 60vh; + height: 60vh; + background-color: #ffffff; + border-radius: 1vh; + display: flex; + flex-direction: column; + align-items: center; + padding-top: 2vh; + padding-bottom: 2vh; + overflow-y: scroll; +`; + +// 리스트 항목 +export const ListItem = styled.div<{ selected?: boolean }>` + cursor: pointer; + background-color: #fafafa; + width: 90%; + height: 9vh; + margin-bottom: 1vh; + display: flex; + flex-direction: column; + border-radius: 5px; + transition: background-color 0.2s ease; + outline : none; + &:hover { + background-color: #eee; + } + + ${({ selected }) => + selected && + ` + background-color: #f0f0f0; + `} +`; + +// 리스트 내부 텍스트 영역 +export const ListItemInner = styled.div` + margin-left: 2vh; + display: flex; + flex-direction: column; + gap: 1vh; +`; + +export const ListTitle = styled.p` + font-size: 2vh; + font-weight: 400; + +`; + +export const ListInfo = styled.div` + display: flex; + gap: 2vh; + font-size: 1.5vh; + color: #888; +`; + +// 상세 정보 박스 (오른쪽) +export const DetailBox = styled.div` + width: 60vh; + height: 55vh; + background-color: #ffffff; + border-radius: 1vh; + display : flex; + flex-direction : column; + align-items: center; + +`; + +export const DetailTitle = styled.p` + font-size: 2.7vh; + font-weight: 500; + position : absolute; + top:-20vh; + + +`; + +export const DetailInfo = styled.p` + font-size: 1.5vh; + position : absolute; + top:-15vh; +`; + +export const DetailContent = styled.p` + font-size: 2vh; + position : absolute; + top:-7vh; + width:100% + +`; + +export const DetailStatus = styled.p` + font-size: 1.3vh; + color: #ff9900; + position : absolute; + top:70vh; + padding-right:30vh; + +`; + + +export const Images = styled(Image)` + position : relative; + top:-1px; +`; + + + +export const BackButtonWrapper = styled.div` + display: flex; + align-items: center; + gap: 3vh; + +`; + +export const BackArrowImage = styled(Image)` + position: relative; + top: -1vh; + cursor: pointer; +`; + +export const DetailWrapper = styled.div` + width: 45vh; + position: relative; + display: flex; + flex-direction: column; + align-items: flex-start; +`; + +export const CheckResultButton = styled.button` + position: relative; + top:6vh; + background-color: #0158DE; + border: none; + width: 14vh; + height: 4vh; + border-radius: 3vh; + color: #ffffff; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 1vh; +`; diff --git a/apps/client/src/app/style/votepage.ts b/apps/client/src/app/style/votepage.ts index 438f52d..5d66cc4 100644 --- a/apps/client/src/app/style/votepage.ts +++ b/apps/client/src/app/style/votepage.ts @@ -1,12 +1,26 @@ import styled from "styled-components"; -export const Container = styled.div``; +export const Container = styled.div` + display : flex; + flex-direction : column; + gap : 10vh; +`; export const Card = styled.div` + border-radius : 10px; + background-color: #FFFFFF; + width: 120vh; + height: 60vh; +`; + +export const ALERDYCard = styled.div` + display : flex; + align-items : center; + justify-content : center; + border-radius : 10px; background-color: #FFFFFF; width: 120vh; height: 60vh; - border-radius: 2vh; `; export const InfoWrapper = styled.div` @@ -24,6 +38,7 @@ export const CategoryText = styled.p` export const TitleWrapper = styled.div` position: absolute; top: 3vh; + width : 100%; `; export const TitleText = styled.p` @@ -32,6 +47,12 @@ export const TitleText = styled.p` font-size: 3vh; `; +export const ALERDYTitleText = styled.p` + color: #000000; + font-family: 'P_Regular'; + font-size: 3vh; +`; + export const DateWrapper = styled.div` position: absolute; top: 4vh; @@ -44,8 +65,7 @@ export const DateText = styled.p` `; export const MenuWrapper = styled.div` - position: absolute; - top: 20vh; - left: 5vh; - height: auto; + position : absolute; + margin-top : -40vh; + margin-left : 5vh; `; diff --git a/apps/client/src/app/success/page.tsx b/apps/client/src/app/success/page.tsx index 3816c41..db816da 100644 --- a/apps/client/src/app/success/page.tsx +++ b/apps/client/src/app/success/page.tsx @@ -3,14 +3,59 @@ import React from "react"; import Image from "next/image"; import check from "@/app/images/check.svg"; import { useRouter } from "next/navigation"; - import { Wrapper, Container, Message, Button } from "../style/End"; +import { useSearchParams } from "next/navigation"; + + +type Props = { + searchParams: Record; +}; + +import { useMutation } from '@apollo/client'; +import {POST_VOTE} from "../mutations" + +// mutation 정의 (variables 선언 포함) + export default function Endvote() { + const searchParams = useSearchParams(); + + + const [post_vote, { loading, error }] = useMutation(POST_VOTE); + const router = useRouter(); + const category = searchParams.get("category") as string || ""; // 변수명 맞춤 + const title = searchParams.get("title") as string || ""; + const optionsss = searchParams.get("options"); + + + let parsedOptions: string[] = []; + + if (typeof optionsss === "string") { + try { + parsedOptions = JSON.parse(optionsss); + } catch (e) { + console.error("JSON parse error:", e); + } + } else if (Array.isArray(optionsss)) { + parsedOptions = optionsss; + } + + console.log(parsedOptions,typeof(parsedOptions)) - function gomain() { - router.push("/"); + async function gomain() { + try { + await post_vote({ + variables: { + title, + category, + options: parsedOptions, + } + }); + router.push("/"); + } catch (err) { + console.error(err); + } } return ( diff --git a/apps/client/src/app/tailvote/page.tsx b/apps/client/src/app/tailvote/page.tsx index 2e4f3b2..8456007 100644 --- a/apps/client/src/app/tailvote/page.tsx +++ b/apps/client/src/app/tailvote/page.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react"; import Tailvote_modal from "../Tailvote_modal/page"; import { useRouter } from "next/navigation"; +import { useSearchParams } from "next/navigation"; import { Overlay, Wrapper, @@ -18,9 +19,22 @@ import { } from "../style/Tailvote"; export default function Tailvote() { - const category = "재미질문 > 꼬리질문1"; - const title = "그 존재가 가장 무서운 이유는 무엇인가요?"; - const date = "2025-05-23"; + const searchParams = useSearchParams(); + const choseoption = searchParams.get("choseoptions"); + const fihishedAt = searchParams.get("fihishedAt"); + let datas = null; + if(choseoption != null){ + datas = JSON.parse(choseoption); + + } + console.log(datas.voteResponse.createVoteResponse.optionContent) + const choseoptions = datas.voteResponse.createVoteResponse.optionContent + + const category = "재미질문 > 꼬리질문"; + const title = choseoptions + "을(를) 선택한 이유는 무엇인가요?"; + const date = fihishedAt; + + console.log(date) const [textcontext, changetext] = useState(""); const [modalOpen, setModalOpen] = useState(false); diff --git a/apps/client/src/app/votemake/page.tsx b/apps/client/src/app/votemake/page.tsx index 1165fc8..4579499 100644 --- a/apps/client/src/app/votemake/page.tsx +++ b/apps/client/src/app/votemake/page.tsx @@ -2,15 +2,18 @@ import { useEffect, useState } from "react"; import AiLimitModal from "@/components/Ailimitmodal"; +import PleaseTitle from "@/components/PleaseTitle"; import OptionModal from "@/components/OptionModal"; import WornModal from "@/components/WornModal"; import { useRouter } from "next/navigation"; +import { useQuery } from '@apollo/client'; +import { GET_AIOPTION } from '../queries'; + import { Container, TitleWrapper, TitleInputWrapper, - TitleLabel, TitleInput, OptionsWrapper, OptionRow, @@ -21,93 +24,124 @@ import { ButtonsRow, InnerButtonsWrapper, Button, - SubmitButton,WarnP + SubmitButton, + WarnP, + OptionButton, + OptionC } from "../style/Votemake"; export default function Votemake() { - const [options, setOptions] = useState(["", ""]); + const [optionss, setOptionss] = useState(["", ""]); const [aiUseCount, setAiUseCount] = useState(0); const [showModal, setShowModal] = useState(false); const [showModal_option, setShowModal_option] = useState(false); const [showModal_worn, setShowModal_worn] = useState(false); + const [showModal_title, setShowModal_title] = useState(false); const [title, settitle] = useState(""); const [IStitle, setIstitle] = useState(false); - const router = useRouter() ; + const router = useRouter(); const maxAiUse = 3; const handleAddOption = () => { - if (options.length < 5) { - setOptions([...options, ""]); + if (optionss.length < 5) { + setOptionss([...optionss, ""]); } }; const handleChangeOption = (index: number, value: string) => { - const updated = [...options]; + const updated = [...optionss]; updated[index] = value; - setOptions(updated); + setOptionss(updated); }; const handleRemoveOption = (index: number) => { - const updated = options.filter((_, i) => i !== index); - setOptions(updated); + const updated = optionss.filter((_, i) => i !== index); + setOptionss(updated); }; - const exampleOptions = [ - "AI 추천 선지 1", - "AI 추천 선지 2", - "AI 추천 선지 3", - "AI 추천 선지 4", - "AI 추천 선지 5", - ]; - const submitVote = () => { - if (title.trim() === "") { - return; - } - else if (options.length < 2|| options.some(option => option.trim() === "")) { + } else if (optionss.length < 2 || optionss.some(option => option.trim() === "")) { setShowModal_option(true); return; - - } - - else{ + } else { setShowModal_worn(true); } - - } + }; useEffect(() => { setIstitle(!(title.trim() !== "")); }, [title]); - + + + const { loading, error, data, refetch } = useQuery(GET_AIOPTION, { + variables: { + count: optionss.length, + title: title, + }, + }); + const handleAIRecommend = () => { - if (aiUseCount >= maxAiUse) { - setShowModal(true); - return; - } + if (title.length === 0) { + setShowModal_title(true); + } else { + if (aiUseCount >= maxAiUse) { + setShowModal(true); + return; + } - const updated = options.map((option, idx) => - option.trim() === "" ? exampleOptions[idx] || `AI 추천 선지 ${idx + 1}` : option - ); + - setOptions(updated); - setAiUseCount(aiUseCount + 1); + if (loading) { + console.log("AI가 선지를 생성 중입니다..."); + } else if (error) { + console.error("Error:", error.message); + } else if (data) { + console.log("데이터 생성 완료"); + setOptionss(data.optionGenerator.generateOptions.options); + setAiUseCount(aiUseCount + 1); + } else { + refetch(); // 데이터를 다시 가져오도록 요청 + } + } }; + const [catego, setcate] = useState(""); + return ( <> + {showModal_title && setShowModal_title(false)} />} {showModal && setShowModal(false)} />} {showModal_option && setShowModal_option(false)} />} - {showModal_worn && setShowModal_worn(false)} onMain={() => router.push("/recommend")} />} + {showModal_worn && ( + setShowModal_worn(false)} + onMain={() => { + const query = new URLSearchParams({ + catego: catego, + title: title, + optionsss: JSON.stringify(optionss), + }).toString(); + + router.push(`/recommend?${query}`); + }} + /> + )} - 투표 만들기 - settitle(e.target.value)} /> + + setcate("재미")}>재미 + setcate("진지")}>진지 + + settitle(e.target.value)} + /> {IStitle ? (
필수입력사항입니다! @@ -118,7 +152,7 @@ export default function Votemake() { - {options.map((option, index) => ( + {optionss.map((option, index) => ( - {options.length < 5 && ( + {optionss.length < 5 && ( + )} diff --git a/apps/client/src/app/votemenu/page.tsx b/apps/client/src/app/votemenu/page.tsx index 1f4ca6b..5c66e1e 100644 --- a/apps/client/src/app/votemenu/page.tsx +++ b/apps/client/src/app/votemenu/page.tsx @@ -2,6 +2,8 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; +import { useSearchParams } from "next/navigation"; + import { Container, VoteOption, @@ -10,33 +12,64 @@ import { SubmitButton, } from "../style/Votemenu"; +import { useMutation } from "@apollo/client"; +import { POST_VOTERESUlT } from "../mutations"; -export default function Votemenu() { - const Lists = [ - "까먹고 기숙사 소등 안 한 날 마주친 사감선생님", - "깃 충돌", - "학생증 검사중 깨달은 학교에 두고온 내 학생증", - ]; - +export default function Votemenu({ + options, + voteid, + votefihishedAt +}: { + options: any[]; + voteid: string; + votefihishedAt: string // voteid를 문자열로 전달받음 +}) { + const searchParams = useSearchParams(); const navigate = useRouter(); const [selected, setSelected] = useState(null); + const [postVoteResult] = useMutation(POST_VOTERESUlT); + const handleChange = (index: number) => { setSelected((prev) => (prev === index ? null : index)); }; - const voting = () => { + const voting = async () => { if (selected !== null) { - setSelected(null); - navigate.push("/tailvote"); + const selectedOption = options[selected]; // 선택된 옵션 가져오기 + try { + console.log(voteid) + console.log(selectedOption.id) + const response = await postVoteResult({ + variables: { + voteId : voteid, // 전달받은 voteid 사용 + optionId: selectedOption.id, // 선택된 옵션 ID 전달 + }, + }); + console.log("투표 성공:", response.data); + setSelected(null); + const query = new URLSearchParams({ + choseoptions : JSON.stringify(response.data), + fihishedAt : String(votefihishedAt) + }); + navigate.push(`/tailvote?${query}`); + + } catch (error) { + console.error("투표 실패:", error); + } } else { alert("투표하고 진행해주세요"); } }; + if (!Array.isArray(options)) { + console.warn("Votemenu: options가 배열이 아님", options); + return

옵션이 존재하지 않습니다.

; + } + return ( - {Lists.map((item, index) => ( + {options.map((item: any, index: number) => ( handleChange(index)} /> - {item} + {item.content} ))} diff --git a/apps/client/src/components/PleaseTitle.tsx b/apps/client/src/components/PleaseTitle.tsx new file mode 100644 index 0000000..275bed9 --- /dev/null +++ b/apps/client/src/components/PleaseTitle.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import { + ModalOverlay, + ModalContent, + CloseButton, + Title, + Description, + Buttons +} from "../app/style/Modal" + +interface ModalProps { + onClose: () => void; +} + +export default function PleaseTitle({ onClose }: ModalProps) { + return ( + + + 안내 + AI 선지 추천을 사용하시려면 투표제목을 입력해주세요. + + 확인 + + + + ); +} diff --git a/package-lock.json b/package-lock.json index 368e952..53e79b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,17 +10,13 @@ "packages/*" ], "dependencies": { - "lucide-react": "^0.513.0", - "pnpm": "^10.10.0", - "react-router-dom": "^7.6.0", + "@apollo/client": "^3.13.8", + "graphql": "^16.11.0", + "lucide-react": "^0.525.0", "styled-components": "^6.1.18" }, "devDependencies": { - "@types/styled-components": "^5.1.34", - "autoprefixer": "^10.4.21", - "postcss": "^8.5.3", "prettier": "^3.0.0", - "tailwindcss": "^4.1.5", "turbo": "^2.5.2" } }, @@ -75,6 +71,48 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@apollo/client": { + "version": "3.13.8", + "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.13.8.tgz", + "integrity": "sha512-YM9lQpm0VfVco4DSyKooHS/fDTiKQcCHfxr7i3iL6a0kP/jNO5+4NFK6vtRDxaYisd5BrwOZHLJpPBnvRVpKPg==", + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "@wry/caches": "^1.0.0", + "@wry/equality": "^0.5.6", + "@wry/trie": "^0.5.0", + "graphql-tag": "^2.12.6", + "hoist-non-react-statics": "^3.3.2", + "optimism": "^0.18.0", + "prop-types": "^15.7.2", + "rehackt": "^0.1.0", + "symbol-observable": "^4.0.0", + "ts-invariant": "^0.10.3", + "tslib": "^2.3.0", + "zen-observable-ts": "^1.2.5" + }, + "peerDependencies": { + "graphql": "^15.0.0 || ^16.0.0", + "graphql-ws": "^5.5.5 || ^6.0.3", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc", + "subscriptions-transport-ws": "^0.9.0 || ^0.11.0" + }, + "peerDependenciesMeta": { + "graphql-ws": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "subscriptions-transport-ws": { + "optional": true + } + } + }, "node_modules/@emnapi/core": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", @@ -267,6 +305,15 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1252,17 +1299,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", - "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0" - } - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1291,7 +1327,7 @@ "version": "19.1.3", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.3.tgz", "integrity": "sha512-dLWQ+Z0CkIvK1J8+wrDPwGxEYFA4RAyHoZPxHVGspYmFVnwGSNT24cGIhFJrtfRnWVuW8X7NO52gCXmhkVUWGQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -1307,18 +1343,6 @@ "@types/react": "^19.0.0" } }, - "node_modules/@types/styled-components": { - "version": "5.1.34", - "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.34.tgz", - "integrity": "sha512-mmiVvwpYklFIv9E8qfxuPyIt/OuyIrn6gMOAMOFUO3WJfSrSE+sGUoa4PiZj77Ut7bKZpaa6o1fBKS/4TOEvnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/hoist-non-react-statics": "*", - "@types/react": "*", - "csstype": "^3.0.2" - } - }, "node_modules/@types/stylis": { "version": "4.2.5", "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", @@ -1802,6 +1826,54 @@ "win32" ] }, + "node_modules/@wry/caches": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz", + "integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wry/context": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz", + "integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wry/equality": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz", + "integrity": "sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wry/trie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz", + "integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -2068,44 +2140,6 @@ "node": ">= 0.4" } }, - "node_modules/autoprefixer": { - "version": "10.4.21", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", - "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.24.4", - "caniuse-lite": "^1.0.30001702", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2194,39 +2228,6 @@ "node": ">=8" } }, - "node_modules/browserslist": { - "version": "4.24.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", - "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001716", - "electron-to-chromium": "^1.5.149", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -2691,13 +2692,6 @@ "dev": true, "license": "MIT" }, - "node_modules/electron-to-chromium": { - "version": "1.5.151", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.151.tgz", - "integrity": "sha512-Rl6uugut2l9sLojjS4H4SAr3A4IgACMLgpuEMPYCVcKydzfyPrn5absNRju38IhQOf/NwjJY8OGWjlteqYeBCA==", - "dev": true, - "license": "ISC" - }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -2903,16 +2897,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -3622,20 +3606,6 @@ "node": ">= 0.6" } }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, "node_modules/fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", @@ -3827,6 +3797,30 @@ "dev": true, "license": "MIT" }, + "node_modules/graphql": { + "version": "16.11.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", + "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-tag": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", + "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -3925,7 +3919,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "react-is": "^16.7.0" @@ -4478,7 +4471,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -4854,7 +4846,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -4864,9 +4855,9 @@ } }, "node_modules/lucide-react": { - "version": "0.513.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.513.0.tgz", - "integrity": "sha512-CJZKq2g8Y8yN4Aq002GahSXbG2JpFv9kXwyiOAMvUBv7pxeOFHUWKB0mO7MiY4ZVFCV4aNjv2BJFq/z3DgKPQg==", + "version": "0.525.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.525.0.tgz", + "integrity": "sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -5115,28 +5106,10 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5278,6 +5251,18 @@ "wrappy": "1" } }, + "node_modules/optimism": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.1.tgz", + "integrity": "sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==", + "license": "MIT", + "dependencies": { + "@wry/caches": "^1.0.0", + "@wry/context": "^0.7.0", + "@wry/trie": "^0.5.0", + "tslib": "^2.3.0" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5435,22 +5420,6 @@ "node": ">=16.20.0" } }, - "node_modules/pnpm": { - "version": "10.10.0", - "resolved": "https://registry.npmjs.org/pnpm/-/pnpm-10.10.0.tgz", - "integrity": "sha512-1hXbJG/nDyXc/qbY1z3ueCziPiJF48T2+Igkn7VoFJMYY33Kc8LFyO8qTKDVZX+5VnGIv6tH9WbR7mzph4FcOQ==", - "license": "MIT", - "bin": { - "pnpm": "bin/pnpm.cjs", - "pnpx": "bin/pnpx.cjs" - }, - "engines": { - "node": ">=18.12" - }, - "funding": { - "url": "https://opencollective.com/pnpm" - } - }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -5526,7 +5495,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -5646,56 +5614,8 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, - "node_modules/react-router": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz", - "integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==", - "license": "MIT", - "dependencies": { - "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/react-router-dom": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.0.tgz", - "integrity": "sha512-DYgm6RDEuKdopSyGOWZGtDfSm7Aofb8CCzgkliTjtu/eDuB0gcsv6qdFhhi8HdtmA+KHkt5MfZ5K2PdzjugYsA==", - "license": "MIT", - "dependencies": { - "react-router": "7.6.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - } - }, - "node_modules/react-router/node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -5740,6 +5660,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rehackt": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz", + "integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -5974,12 +5912,6 @@ "node": ">= 18" } }, - "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", - "license": "MIT" - }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -6479,6 +6411,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, "node_modules/tailwindcss": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.5.tgz", @@ -6577,6 +6518,18 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-invariant": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz", + "integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -6887,37 +6840,6 @@ "@unrs/resolver-binding-win32-x64-msvc": "1.7.2" } }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -7073,6 +6995,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zen-observable": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", + "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==", + "license": "MIT" + }, + "node_modules/zen-observable-ts": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz", + "integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==", + "license": "MIT", + "dependencies": { + "zen-observable": "0.8.15" + } + }, "node_modules/zod": { "version": "3.24.4", "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz", diff --git a/package.json b/package.json index 9619579..5d23534 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,9 @@ "turbo": "^2.5.2" }, "dependencies": { + "@apollo/client": "^3.13.8", + "graphql": "^16.11.0", + "lucide-react": "^0.525.0", "styled-components": "^6.1.18" } -} \ No newline at end of file +}