From 8905a74b61b8de978fe6d98015b5816b766e456c Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Sun, 27 Apr 2025 11:54:49 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=9D=BC=EC=9A=B0=ED=84=B0=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customerService/inquiry/Inquiry.styled.ts | 3 +++ .../common/customerService/inquiry/Inquiry.tsx | 5 +++++ src/components/common/header/Header.tsx | 2 +- src/routes/AppRoutes.tsx | 13 +++++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/components/common/customerService/inquiry/Inquiry.styled.ts create mode 100644 src/components/common/customerService/inquiry/Inquiry.tsx diff --git a/src/components/common/customerService/inquiry/Inquiry.styled.ts b/src/components/common/customerService/inquiry/Inquiry.styled.ts new file mode 100644 index 00000000..7945f72d --- /dev/null +++ b/src/components/common/customerService/inquiry/Inquiry.styled.ts @@ -0,0 +1,3 @@ +import styled from 'styled-components'; + +export const Container = styled.main``; diff --git a/src/components/common/customerService/inquiry/Inquiry.tsx b/src/components/common/customerService/inquiry/Inquiry.tsx new file mode 100644 index 00000000..aa7bdf96 --- /dev/null +++ b/src/components/common/customerService/inquiry/Inquiry.tsx @@ -0,0 +1,5 @@ +import * as S from './Inquiry.styled'; + +export default function Inquiry() { + return ; +} diff --git a/src/components/common/header/Header.tsx b/src/components/common/header/Header.tsx index b8523038..5a63f191 100644 --- a/src/components/common/header/Header.tsx +++ b/src/components/common/header/Header.tsx @@ -74,7 +74,7 @@ function Header() { 공고관리 - + 문의하기 e.preventDefault()}> diff --git a/src/routes/AppRoutes.tsx b/src/routes/AppRoutes.tsx index f4ea88fd..8e3e9a09 100644 --- a/src/routes/AppRoutes.tsx +++ b/src/routes/AppRoutes.tsx @@ -23,6 +23,9 @@ const FAQ = lazy(() => import('../components/common/customerService/faq/FAQ')); const Notice = lazy( () => import('../components/common/customerService/notice/Notice') ); +const Inquiry = lazy( + () => import('../components/common/customerService/inquiry/Inquiry') +); const MyPage = lazy(() => import('../pages/mypage/MyPage')); const UserPage = lazy(() => import('../pages/userpage/UserPage')); const Apply = lazy(() => import('../pages/apply/ApplyStep')); @@ -132,6 +135,16 @@ const AppRoutes = () => { ), }, + { + path: ROUTES.inquiry, + element: ( + + + + + + ), + }, { path: ROUTES.createProject, element: ( From 0f7d73eca4e27a8816dc89dd874689c1e0580622 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Sun, 27 Apr 2025 12:17:45 +0900 Subject: [PATCH 02/11] =?UTF-8?q?refactor:=20constant=20=EC=BB=A8=EB=B2=A4?= =?UTF-8?q?=EC=85=98=EC=97=90=EB=94=B0=EB=A5=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/mypage/activityLog/ActivityLog.tsx | 4 ++-- src/components/mypage/notifications/Notifications.tsx | 4 ++-- src/constants/customerService.ts | 0 src/constants/myPageFilter.ts | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 src/constants/customerService.ts diff --git a/src/components/mypage/activityLog/ActivityLog.tsx b/src/components/mypage/activityLog/ActivityLog.tsx index 50fab35f..3bf257a7 100644 --- a/src/components/mypage/activityLog/ActivityLog.tsx +++ b/src/components/mypage/activityLog/ActivityLog.tsx @@ -1,10 +1,10 @@ -import { activityFilter } from '../../../constants/myPageFilter'; +import { ACTIVITY_FILTER } from '../../../constants/myPageFilter'; import ContentTab from '../ContentTab'; export default function ActivityLog() { return ( <> - + ); } diff --git a/src/components/mypage/notifications/Notifications.tsx b/src/components/mypage/notifications/Notifications.tsx index 32fe1b85..dd0ed37d 100644 --- a/src/components/mypage/notifications/Notifications.tsx +++ b/src/components/mypage/notifications/Notifications.tsx @@ -1,8 +1,8 @@ -import { notificationFilter } from '../../../constants/myPageFilter'; +import { NOTIFICATION_FILTER } from '../../../constants/myPageFilter'; import ContentTab from '../ContentTab'; export default function Notifications() { return ( - + ); } diff --git a/src/constants/customerService.ts b/src/constants/customerService.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/constants/myPageFilter.ts b/src/constants/myPageFilter.ts index a31a4dcb..67c6edbe 100644 --- a/src/constants/myPageFilter.ts +++ b/src/constants/myPageFilter.ts @@ -1,6 +1,6 @@ import { ROUTES } from './routes'; -export const notificationFilter = [ +export const NOTIFICATION_FILTER = [ { title: '전체', url: ``, id: 0 }, { title: '지원한 프로젝트', @@ -22,7 +22,7 @@ export const notificationFilter = [ }, ] as const; -export const activityFilter = [ +export const ACTIVITY_FILTER = [ { title: '내 댓글', url: ROUTES.comments, id: 0 }, { title: '내 문의글', url: ROUTES.activityInquiries, id: 1 }, ] as const; From 2bbf3c1ccedacaed5c0e6180eae16d42f719276a Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Mon, 28 Apr 2025 18:45:22 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=20ui=20formData=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customerService/inquiry/Inquiry.styled.ts | 192 +++++++++++++++++- .../customerService/inquiry/Inquiry.tsx | 100 ++++++++- .../FilteringContents.styled.ts | 14 +- .../filteringContents/FilteringContents.tsx | 5 +- .../filtering/Filtering.styled.ts | 14 +- .../filteringContents/filtering/Filtering.tsx | 5 +- src/constants/customerService.ts | 9 + src/routes/AppRoutes.tsx | 8 +- 8 files changed, 335 insertions(+), 12 deletions(-) diff --git a/src/components/common/customerService/inquiry/Inquiry.styled.ts b/src/components/common/customerService/inquiry/Inquiry.styled.ts index 7945f72d..75e40c85 100644 --- a/src/components/common/customerService/inquiry/Inquiry.styled.ts +++ b/src/components/common/customerService/inquiry/Inquiry.styled.ts @@ -1,3 +1,191 @@ -import styled from 'styled-components'; +import { ChevronDownIcon } from '@heroicons/react/24/outline'; +import styled, { css } from 'styled-components'; -export const Container = styled.main``; +export const Container = styled.main` + width: 100%; + display: flex; + justify-content: center; + flex-direction: column; +`; + +export const Header = styled.header` + margin: 1rem 0; + display: flex; + justify-content: center; +`; + +export const HeaderTitle = styled.h1``; + +export const InquiryForm = styled.form` + width: 100%; + display: flex; + justify-content: center; +`; + +export const InquiryWrapper = styled.div` + width: 49rem; +`; + +export const Nav = styled.nav` + width: 100%; + display: flex; + gap: 0.5rem; +`; + +export const CategoryWrapper = styled.div` + position: relative; +`; + +export const CategorySelect = styled.button<{ $isOpen: boolean }>` + padding: 0.3rem 0.5rem; + width: 9rem; + display: flex; + justify-content: space-between; + align-items: center; + font-size: 1.3rem; + border: 1px solid ${({ theme }) => theme.color.border}; + border-radius: ${({ theme }) => theme.borderRadius.primary}; + + svg { + width: 1.3rem; + height: 1.3rem; + transition: transform 300ms ease-in-out; + transform: rotate(0deg); + ${({ $isOpen }) => + $isOpen && + css` + transform: rotate(180deg); + `} + } +`; + +export const CategoryValueInput = styled.input` + position: absolute; + width: 0; + height: 0; + overflow: hidden; +`; + +export const CategoryButtonWrapper = styled.div` + width: 9rem; + position: absolute; + display: flex; + flex-direction: column; + overflow: hidden; + border: 1px solid ${({ theme }) => theme.color.border}; + border-radius: ${({ theme }) => theme.borderRadius.primary}; + background: ${({ theme }) => theme.color.white}; +`; + +export const CategoryButton = styled.button` + font-size: 1.3rem; + padding: 0.5rem; + display: flex; + justify-content: start; + align-items: center; + + &:hover { + background: ${({ theme }) => theme.color.navy}; + color: ${({ theme }) => theme.color.white}; + } +`; + +export const InputInquiryTitle = styled.input` + padding: 0.2rem 0.8rem; + width: calc(100% - 8rem); + font-size: 1.3rem; + border: 1px solid ${({ theme }) => theme.color.border}; + border-radius: ${({ theme }) => theme.borderRadius.primary}; +`; + +export const ContentWrapper = styled.section` + width: 100%; +`; + +export const Content = styled.textarea` + resize: none; + margin: 0.5rem 0; + padding: 1rem; + height: 55vh; + width: 100%; + border: 1px solid ${({ theme }) => theme.color.border}; + border-radius: ${({ theme }) => theme.borderRadius.primary}; + font-size: 1rem; +`; + +export const InquiryFileWrapper = styled.div` + display: flex; + height: 40px; +`; + +export const InquiryFileLabel = styled.label` + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + font-size: 1rem; + width: 6rem; + background: ${({ theme }) => theme.color.navy}; + color: ${({ theme }) => theme.color.white}; + border: 1px solid ${({ theme }) => theme.color.navy}; + border-radius: ${({ theme }) => theme.borderRadius.primary} 0 0 + ${({ theme }) => theme.borderRadius.primary}; + + &:hover { + background: ${({ theme }) => theme.color.lightgrey}; + color: ${({ theme }) => theme.color.navy}; + border: 1px solid ${({ theme }) => theme.color.navy}; + transition: all 0.3s ease-in-out; + } +`; + +export const InquiryShowFile = styled.span` + display: flex; + justify-content: start; + align-items: center; + padding: 0.5rem; + border: 1px solid ${({ theme }) => theme.color.border}; + width: 40%; + color: ${({ theme }) => theme.color.navy}; + border-radius: 0 ${({ theme }) => theme.borderRadius.primary} + ${({ theme }) => theme.borderRadius.primary} 0; +`; + +export const InquiryFile = styled.input` + position: absolute; + width: 0; + height: 0; + overflow: hidden; +`; + +export const FileImg = styled.img` + margin-left: 0.5rem; + width: 60px; + height: 40px; +`; + +export const SendButtonWrapper = styled.div` + width: 100%; + display: flex; + justify-content: end; +`; + +export const SendButton = styled.button` + display: flex; + justify-content: center; + align-items: center; + font-size: 1rem; + width: 6rem; + background: ${({ theme }) => theme.color.navy}; + border-radius: ${({ theme }) => theme.borderRadius.large}; + color: ${({ theme }) => theme.color.white}; + border: 1px solid ${({ theme }) => theme.color.navy}; + padding: 0.5em; + + &:hover { + background: ${({ theme }) => theme.color.lightgrey}; + color: ${({ theme }) => theme.color.navy}; + border: 1px solid ${({ theme }) => theme.color.navy}; + transition: all 0.3s ease-in-out; + } +`; diff --git a/src/components/common/customerService/inquiry/Inquiry.tsx b/src/components/common/customerService/inquiry/Inquiry.tsx index aa7bdf96..6b92a609 100644 --- a/src/components/common/customerService/inquiry/Inquiry.tsx +++ b/src/components/common/customerService/inquiry/Inquiry.tsx @@ -1,5 +1,103 @@ +import { Fragment } from 'react/jsx-runtime'; +import { + EMPTY_IMAGE, + INQUIRY_CATEGORY, +} from '../../../../constants/customerService'; import * as S from './Inquiry.styled'; +import { ChevronDownIcon } from '@heroicons/react/24/outline'; +import React, { useState } from 'react'; export default function Inquiry() { - return ; + const [categoryValue, setCategoryValue] = useState('카테고리'); + const [isOpen, setIsOpen] = useState(false); + const [fileValue, setFileValue] = useState('선택된 파일이 없습니다.'); + const [fileImage, setFileImage] = useState(EMPTY_IMAGE); + + const handleSubmitInquiry = (e: React.FormEvent) => { + e.preventDefault(); + console.log(e); + + const formData = new FormData(e.currentTarget as HTMLFormElement); + for (const [key, value] of formData.entries()) { + console.log(key, value); + } + }; + + const handleClickCategoryValue = (value: string) => { + setCategoryValue(value); + setIsOpen((prev) => !prev); + }; + const handleChangeFile = (e: React.ChangeEvent) => { + const value = e.target.value; + const file = e.target.files?.[0]; + setFileValue(value); + setFileImage(file ? URL.createObjectURL(file) : EMPTY_IMAGE); + }; + + return ( + + + DevPals 문의하기 + + + + + + setIsOpen((prev) => !prev)} + $isOpen={isOpen} + > + {categoryValue} + + + {isOpen && ( + + {INQUIRY_CATEGORY.map((category) => ( + + handleClickCategoryValue(category.title)} + > + {category.title} + + + ))} + + )} + + + + + + + 파일찾기 + {fileValue} + handleChangeFile(e)} + /> + + + + + 제출 + + + + + ); } diff --git a/src/components/home/searchFiltering/filteringContents/FilteringContents.styled.ts b/src/components/home/searchFiltering/filteringContents/FilteringContents.styled.ts index f20ae36d..2e02dcc9 100644 --- a/src/components/home/searchFiltering/filteringContents/FilteringContents.styled.ts +++ b/src/components/home/searchFiltering/filteringContents/FilteringContents.styled.ts @@ -1,4 +1,4 @@ -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; export const Container = styled.div` width: 100%; @@ -35,7 +35,7 @@ export const SkillTagButtonWrapper = styled.div` z-index: 1000; `; -export const SkillTagButton = styled.button` +export const SkillTagButton = styled.button<{ $isOpen: boolean }>` border-radius: 1.5rem; width: 100%; height: 100%; @@ -43,6 +43,16 @@ export const SkillTagButton = styled.button` display: flex; justify-content: space-between; align-items: center; + + svg { + transition: transform 300ms ease-in-out; + transform: rotate(0deg); + ${({ $isOpen }) => + $isOpen && + css` + transform: rotate(180deg); + `} + } `; export const SkillTagBoxWrapper = styled.div` diff --git a/src/components/home/searchFiltering/filteringContents/FilteringContents.tsx b/src/components/home/searchFiltering/filteringContents/FilteringContents.tsx index 32dad24f..827d19ae 100644 --- a/src/components/home/searchFiltering/filteringContents/FilteringContents.tsx +++ b/src/components/home/searchFiltering/filteringContents/FilteringContents.tsx @@ -40,7 +40,10 @@ export default function FilteringContents() { return ( - + 언어선택 diff --git a/src/components/home/searchFiltering/filteringContents/filtering/Filtering.styled.ts b/src/components/home/searchFiltering/filteringContents/filtering/Filtering.styled.ts index 47edbe94..07b813b3 100644 --- a/src/components/home/searchFiltering/filteringContents/filtering/Filtering.styled.ts +++ b/src/components/home/searchFiltering/filteringContents/filtering/Filtering.styled.ts @@ -1,4 +1,4 @@ -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; export const Container = styled.div``; @@ -16,7 +16,7 @@ export const RefWrapper = styled.div` border-radius: 1.5rem; `; -export const DefaultValueButton = styled.button` +export const DefaultValueButton = styled.button<{ $isOpen: boolean }>` width: 100%; height: 100%; display: flex; @@ -24,6 +24,16 @@ export const DefaultValueButton = styled.button` align-items: center; padding: 0 1rem; border-radius: 1.5rem; + + svg { + transition: transform 300ms ease-in-out; + transform: rotate(0deg); + ${({ $isOpen }) => + $isOpen && + css` + transform: rotate(180deg); + `} + } `; export const SelectWrapper = styled.div` diff --git a/src/components/home/searchFiltering/filteringContents/filtering/Filtering.tsx b/src/components/home/searchFiltering/filteringContents/filtering/Filtering.tsx index 28edb79a..585c7f94 100644 --- a/src/components/home/searchFiltering/filteringContents/filtering/Filtering.tsx +++ b/src/components/home/searchFiltering/filteringContents/filtering/Filtering.tsx @@ -73,7 +73,10 @@ export default function Filtering({ selects, defaultValue }: FilteringProps) { - + {changeValue} diff --git a/src/constants/customerService.ts b/src/constants/customerService.ts index e69de29b..f595f562 100644 --- a/src/constants/customerService.ts +++ b/src/constants/customerService.ts @@ -0,0 +1,9 @@ +export const INQUIRY_CATEGORY = [ + { title: '사이트 오류' }, + { title: '공고모집' }, + { title: '제안하기' }, + { title: '기타' }, +] as const; + +export const EMPTY_IMAGE = + 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' as const; diff --git a/src/routes/AppRoutes.tsx b/src/routes/AppRoutes.tsx index 8e3e9a09..95cf64c8 100644 --- a/src/routes/AppRoutes.tsx +++ b/src/routes/AppRoutes.tsx @@ -139,9 +139,11 @@ const AppRoutes = () => { path: ROUTES.inquiry, element: ( - - - + + + + + ), }, From e567a5e11a48372bfc77225c80fa053fc7a7ca98 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Tue, 29 Apr 2025 21:43:41 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat:=20=EB=AC=B8=EC=9D=98=20api=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0,=20=EB=AA=A8=EB=8B=AC=EC=B0=BD=20=EC=B6=94?= =?UTF-8?q?=ED=9B=84=20=EA=B8=B0=EB=8A=A5=EA=B5=AC=ED=98=84=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EC=98=88=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/http.api.ts | 2 +- src/api/inquiry.api.ts | 14 +++++++ .../customerService/inquiry/Inquiry.tsx | 41 +++++++++++++++---- src/constants/customerService.ts | 5 +++ src/hooks/usePostInquiry.ts | 14 +++++++ src/models/inquiry.ts | 5 +++ 6 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 src/api/inquiry.api.ts create mode 100644 src/hooks/usePostInquiry.ts create mode 100644 src/models/inquiry.ts diff --git a/src/api/http.api.ts b/src/api/http.api.ts index a5a163da..19e10257 100644 --- a/src/api/http.api.ts +++ b/src/api/http.api.ts @@ -2,7 +2,7 @@ import axios, { AxiosRequestConfig } from 'axios'; import useAuthStore, { getTokens } from '../store/authStore'; export const BASE_URL = `${import.meta.env.VITE_APP_API_BASE_URL}`; -const DEFAULT_TIMEOUT = 15000; +const DEFAULT_TIMEOUT = 30000; export const createClient = (config?: AxiosRequestConfig) => { const { storeLogin, storeLogout } = useAuthStore.getState(); diff --git a/src/api/inquiry.api.ts b/src/api/inquiry.api.ts new file mode 100644 index 00000000..0c96ee99 --- /dev/null +++ b/src/api/inquiry.api.ts @@ -0,0 +1,14 @@ +import { httpClient } from './http.api'; + +export const postInquiry = async (formData: FormData) => { + try { + const response = await httpClient.post('/inquiry', formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + console.log(response); + } catch (e) { + console.log('문의하기 에러', e); + } +}; diff --git a/src/components/common/customerService/inquiry/Inquiry.tsx b/src/components/common/customerService/inquiry/Inquiry.tsx index 6b92a609..981947a4 100644 --- a/src/components/common/customerService/inquiry/Inquiry.tsx +++ b/src/components/common/customerService/inquiry/Inquiry.tsx @@ -2,25 +2,49 @@ import { Fragment } from 'react/jsx-runtime'; import { EMPTY_IMAGE, INQUIRY_CATEGORY, + INQUIRY_MESSAGE, } from '../../../../constants/customerService'; import * as S from './Inquiry.styled'; import { ChevronDownIcon } from '@heroicons/react/24/outline'; import React, { useState } from 'react'; +import { postInquiry } from '../../../../api/inquiry.api'; +import type { InquiryFormData } from '../../../../models/inquiry'; export default function Inquiry() { - const [categoryValue, setCategoryValue] = useState('카테고리'); const [isOpen, setIsOpen] = useState(false); - const [fileValue, setFileValue] = useState('선택된 파일이 없습니다.'); + const [categoryValue, setCategoryValue] = useState( + INQUIRY_MESSAGE.categoryDefault + ); + const [title, setTitle] = useState(''); + const [content, setContent] = useState(''); + const [fileValue, setFileValue] = useState( + INQUIRY_MESSAGE.fileDefault + ); const [fileImage, setFileImage] = useState(EMPTY_IMAGE); const handleSubmitInquiry = (e: React.FormEvent) => { e.preventDefault(); - console.log(e); const formData = new FormData(e.currentTarget as HTMLFormElement); - for (const [key, value] of formData.entries()) { - console.log(key, value); - } + + const formDataObj: InquiryFormData = { + category: formData.get('category') as string, + title: formData.get('title') as string, + content: formData.get('content') as string, + }; + + const data = new Blob([JSON.stringify(formDataObj)], { + type: 'application/json', + }); + + formData.append('inquiryDto', data); + + postInquiry(formData); + setCategoryValue(INQUIRY_MESSAGE.categoryDefault); + setFileValue(INQUIRY_MESSAGE.fileDefault); + setFileImage(EMPTY_IMAGE); + setTitle(''); + setContent(''); }; const handleClickCategoryValue = (value: string) => { @@ -76,15 +100,16 @@ export default function Inquiry() { name='title' type='text' placeholder='제목을 입력하세요.' + value={title} /> - + 파일찾기 {fileValue} { + const queryClient = useQueryClient(); + + const mutate = useMutation({ + mutationFn: (formData) => postInquiry(formData), + onSuccess: () => { + queryClient.invalidateQueries(); + }, + }); +}; diff --git a/src/models/inquiry.ts b/src/models/inquiry.ts new file mode 100644 index 00000000..6899e943 --- /dev/null +++ b/src/models/inquiry.ts @@ -0,0 +1,5 @@ +export interface InquiryFormData { + category: string; + title: string; + content: string; +} From f9d538a51e3411e1eb5baaa8f209610c235d7494 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Tue, 29 Apr 2025 21:45:56 +0900 Subject: [PATCH 05/11] =?UTF-8?q?fix:=20http=20time=20out=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/api/http.api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/http.api.ts b/src/api/http.api.ts index 19e10257..a5a163da 100644 --- a/src/api/http.api.ts +++ b/src/api/http.api.ts @@ -2,7 +2,7 @@ import axios, { AxiosRequestConfig } from 'axios'; import useAuthStore, { getTokens } from '../store/authStore'; export const BASE_URL = `${import.meta.env.VITE_APP_API_BASE_URL}`; -const DEFAULT_TIMEOUT = 30000; +const DEFAULT_TIMEOUT = 15000; export const createClient = (config?: AxiosRequestConfig) => { const { storeLogin, storeLogout } = useAuthStore.getState(); From b7a30d7b96300739c33a77eb0eee09f89f3b5a64 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Tue, 29 Apr 2025 22:47:17 +0900 Subject: [PATCH 06/11] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9,=20=EA=B0=92=EC=9D=B4=20=EC=97=86=EC=9D=84?= =?UTF-8?q?=EB=95=8C=20=EC=A0=9C=EC=B6=9C=ED=95=98=EC=A7=80=20=EB=AA=BB?= =?UTF-8?q?=ED=95=98=EA=B2=8C=20=EB=A7=89=EC=95=84=EB=86=93=EA=B8=B0=20//?= =?UTF-8?q?=20=EC=B6=94=ED=9B=84=20=EB=AA=A8=EB=8B=AC=EC=84=A4=EC=A0=95?= =?UTF-8?q?=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customerService/inquiry/Inquiry.tsx | 39 ++++++++++++++----- src/hooks/usePostInquiry.ts | 2 + 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/components/common/customerService/inquiry/Inquiry.tsx b/src/components/common/customerService/inquiry/Inquiry.tsx index 981947a4..11a84624 100644 --- a/src/components/common/customerService/inquiry/Inquiry.tsx +++ b/src/components/common/customerService/inquiry/Inquiry.tsx @@ -7,10 +7,11 @@ import { import * as S from './Inquiry.styled'; import { ChevronDownIcon } from '@heroicons/react/24/outline'; import React, { useState } from 'react'; -import { postInquiry } from '../../../../api/inquiry.api'; import type { InquiryFormData } from '../../../../models/inquiry'; +import { usePostInquiry } from '../../../../hooks/usePostInquiry'; export default function Inquiry() { + const { mutate: postInquiry } = usePostInquiry(); const [isOpen, setIsOpen] = useState(false); const [categoryValue, setCategoryValue] = useState( INQUIRY_MESSAGE.categoryDefault @@ -20,7 +21,7 @@ export default function Inquiry() { const [fileValue, setFileValue] = useState( INQUIRY_MESSAGE.fileDefault ); - const [fileImage, setFileImage] = useState(EMPTY_IMAGE); + const [fileImage, setFileImage] = useState(null); const handleSubmitInquiry = (e: React.FormEvent) => { e.preventDefault(); @@ -39,12 +40,24 @@ export default function Inquiry() { formData.append('inquiryDto', data); - postInquiry(formData); - setCategoryValue(INQUIRY_MESSAGE.categoryDefault); - setFileValue(INQUIRY_MESSAGE.fileDefault); - setFileImage(EMPTY_IMAGE); - setTitle(''); - setContent(''); + // 모달처리하기 + let isValid = true; + + Array.from(formData.entries()).forEach(([key, value]) => { + if (key === 'category' && value === INQUIRY_MESSAGE.categoryDefault) + return (isValid = false); + if (key === 'title' && value === '') return (isValid = false); + if (key === 'content' && value === '') return (isValid = false); + }); + + if (isValid) { + postInquiry(formData); + setCategoryValue(INQUIRY_MESSAGE.categoryDefault); + setFileValue(INQUIRY_MESSAGE.fileDefault); + setFileImage(EMPTY_IMAGE); + setTitle(''); + setContent(''); + } }; const handleClickCategoryValue = (value: string) => { @@ -101,10 +114,16 @@ export default function Inquiry() { type='text' placeholder='제목을 입력하세요.' value={title} + onChange={(e) => setTitle(e.target.value)} /> - + setContent(e.target.value)} + > 파일찾기 {fileValue} @@ -115,7 +134,7 @@ export default function Inquiry() { id='upload' onChange={(e) => handleChangeFile(e)} /> - + {fileImage && } diff --git a/src/hooks/usePostInquiry.ts b/src/hooks/usePostInquiry.ts index 5f3b5ea3..23520ae9 100644 --- a/src/hooks/usePostInquiry.ts +++ b/src/hooks/usePostInquiry.ts @@ -11,4 +11,6 @@ export const usePostInquiry = () => { queryClient.invalidateQueries(); }, }); + + return mutate; }; From ed5c7fcc0a7228922c80c6cc48cb51094a5ca91c Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Tue, 29 Apr 2025 23:10:22 +0900 Subject: [PATCH 07/11] =?UTF-8?q?refactor:=20form=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20state=20=ED=95=98=EB=82=98=EC=9D=98=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=EB=A1=9C=20=EA=B4=80=EB=A6=AC=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customerService/inquiry/Inquiry.tsx | 68 +++++++++++-------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/src/components/common/customerService/inquiry/Inquiry.tsx b/src/components/common/customerService/inquiry/Inquiry.tsx index 11a84624..dc1708cc 100644 --- a/src/components/common/customerService/inquiry/Inquiry.tsx +++ b/src/components/common/customerService/inquiry/Inquiry.tsx @@ -10,18 +10,24 @@ import React, { useState } from 'react'; import type { InquiryFormData } from '../../../../models/inquiry'; import { usePostInquiry } from '../../../../hooks/usePostInquiry'; +interface FormStateType { + category: string; + title: string; + content: string; + fileValue: string; + fileImage: string | null; +} + export default function Inquiry() { const { mutate: postInquiry } = usePostInquiry(); const [isOpen, setIsOpen] = useState(false); - const [categoryValue, setCategoryValue] = useState( - INQUIRY_MESSAGE.categoryDefault - ); - const [title, setTitle] = useState(''); - const [content, setContent] = useState(''); - const [fileValue, setFileValue] = useState( - INQUIRY_MESSAGE.fileDefault - ); - const [fileImage, setFileImage] = useState(null); + const [form, setForm] = useState({ + category: INQUIRY_MESSAGE.categoryDefault, + title: '', + content: '', + fileValue: INQUIRY_MESSAGE.fileDefault, + fileImage: null, + }); const handleSubmitInquiry = (e: React.FormEvent) => { e.preventDefault(); @@ -52,23 +58,25 @@ export default function Inquiry() { if (isValid) { postInquiry(formData); - setCategoryValue(INQUIRY_MESSAGE.categoryDefault); - setFileValue(INQUIRY_MESSAGE.fileDefault); - setFileImage(EMPTY_IMAGE); - setTitle(''); - setContent(''); + setForm({ + category: INQUIRY_MESSAGE.categoryDefault, + title: '', + content: '', + fileValue: INQUIRY_MESSAGE.fileDefault, + fileImage: null, + }); } }; - const handleClickCategoryValue = (value: string) => { - setCategoryValue(value); + const handleClickCategoryValue = (category: string) => { + setForm((prev) => ({ ...prev, category })); setIsOpen((prev) => !prev); }; const handleChangeFile = (e: React.ChangeEvent) => { - const value = e.target.value; - const file = e.target.files?.[0]; - setFileValue(value); - setFileImage(file ? URL.createObjectURL(file) : EMPTY_IMAGE); + const fileValue = e.target.value; + const image = e.target.files?.[0]; + const fileImage = image ? URL.createObjectURL(image) : null; + setForm((prev) => ({ ...prev, fileValue, fileImage })); }; return ( @@ -88,11 +96,11 @@ export default function Inquiry() { onClick={() => setIsOpen((prev) => !prev)} $isOpen={isOpen} > - {categoryValue} + {form.category} {isOpen && ( @@ -113,20 +121,24 @@ export default function Inquiry() { name='title' type='text' placeholder='제목을 입력하세요.' - value={title} - onChange={(e) => setTitle(e.target.value)} + value={form.title} + onChange={(e) => + setForm((prev) => ({ ...prev, title: e.target.value })) + } /> setContent(e.target.value)} + value={form.content} + onChange={(e) => + setForm((prev) => ({ ...prev, content: e.target.value })) + } > 파일찾기 - {fileValue} + {form.fileValue} handleChangeFile(e)} /> - {fileImage && } + {form.fileImage && } From 44d32c284c03752daa03da16f460039ccfd81e66 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Tue, 29 Apr 2025 23:37:05 +0900 Subject: [PATCH 08/11] =?UTF-8?q?fix:=20customerService=20=EB=94=94?= =?UTF-8?q?=EB=A0=89=ED=86=A0=EB=A6=AC=20pages=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../customerService/CustomerServiceHeader.styled.ts | 0 .../customerService/CustomerServiceHeader.tsx | 0 .../customerService/MoveInquiredLink.styled.ts | 0 .../customerService/MoveInquiredLink.tsx | 0 .../common => pages}/customerService/faq/FAQ.styled.ts | 0 .../common => pages}/customerService/faq/FAQ.tsx | 0 .../customerService/inquiry/Inquiry.styled.ts | 1 - .../customerService/inquiry/Inquiry.tsx | 7 +++---- .../customerService/notice/Notice.styled.ts | 0 .../common => pages}/customerService/notice/Notice.tsx | 0 src/routes/AppRoutes.tsx | 10 +++------- 11 files changed, 6 insertions(+), 12 deletions(-) rename src/{components/common => pages}/customerService/CustomerServiceHeader.styled.ts (100%) rename src/{components/common => pages}/customerService/CustomerServiceHeader.tsx (100%) rename src/{components/common => pages}/customerService/MoveInquiredLink.styled.ts (100%) rename src/{components/common => pages}/customerService/MoveInquiredLink.tsx (100%) rename src/{components/common => pages}/customerService/faq/FAQ.styled.ts (100%) rename src/{components/common => pages}/customerService/faq/FAQ.tsx (100%) rename src/{components/common => pages}/customerService/inquiry/Inquiry.styled.ts (98%) rename src/{components/common => pages}/customerService/inquiry/Inquiry.tsx (96%) rename src/{components/common => pages}/customerService/notice/Notice.styled.ts (100%) rename src/{components/common => pages}/customerService/notice/Notice.tsx (100%) diff --git a/src/components/common/customerService/CustomerServiceHeader.styled.ts b/src/pages/customerService/CustomerServiceHeader.styled.ts similarity index 100% rename from src/components/common/customerService/CustomerServiceHeader.styled.ts rename to src/pages/customerService/CustomerServiceHeader.styled.ts diff --git a/src/components/common/customerService/CustomerServiceHeader.tsx b/src/pages/customerService/CustomerServiceHeader.tsx similarity index 100% rename from src/components/common/customerService/CustomerServiceHeader.tsx rename to src/pages/customerService/CustomerServiceHeader.tsx diff --git a/src/components/common/customerService/MoveInquiredLink.styled.ts b/src/pages/customerService/MoveInquiredLink.styled.ts similarity index 100% rename from src/components/common/customerService/MoveInquiredLink.styled.ts rename to src/pages/customerService/MoveInquiredLink.styled.ts diff --git a/src/components/common/customerService/MoveInquiredLink.tsx b/src/pages/customerService/MoveInquiredLink.tsx similarity index 100% rename from src/components/common/customerService/MoveInquiredLink.tsx rename to src/pages/customerService/MoveInquiredLink.tsx diff --git a/src/components/common/customerService/faq/FAQ.styled.ts b/src/pages/customerService/faq/FAQ.styled.ts similarity index 100% rename from src/components/common/customerService/faq/FAQ.styled.ts rename to src/pages/customerService/faq/FAQ.styled.ts diff --git a/src/components/common/customerService/faq/FAQ.tsx b/src/pages/customerService/faq/FAQ.tsx similarity index 100% rename from src/components/common/customerService/faq/FAQ.tsx rename to src/pages/customerService/faq/FAQ.tsx diff --git a/src/components/common/customerService/inquiry/Inquiry.styled.ts b/src/pages/customerService/inquiry/Inquiry.styled.ts similarity index 98% rename from src/components/common/customerService/inquiry/Inquiry.styled.ts rename to src/pages/customerService/inquiry/Inquiry.styled.ts index 75e40c85..ba8f1e47 100644 --- a/src/components/common/customerService/inquiry/Inquiry.styled.ts +++ b/src/pages/customerService/inquiry/Inquiry.styled.ts @@ -1,4 +1,3 @@ -import { ChevronDownIcon } from '@heroicons/react/24/outline'; import styled, { css } from 'styled-components'; export const Container = styled.main` diff --git a/src/components/common/customerService/inquiry/Inquiry.tsx b/src/pages/customerService/inquiry/Inquiry.tsx similarity index 96% rename from src/components/common/customerService/inquiry/Inquiry.tsx rename to src/pages/customerService/inquiry/Inquiry.tsx index dc1708cc..e3741c96 100644 --- a/src/components/common/customerService/inquiry/Inquiry.tsx +++ b/src/pages/customerService/inquiry/Inquiry.tsx @@ -1,14 +1,13 @@ import { Fragment } from 'react/jsx-runtime'; import { - EMPTY_IMAGE, INQUIRY_CATEGORY, INQUIRY_MESSAGE, -} from '../../../../constants/customerService'; +} from '../../../constants/customerService'; import * as S from './Inquiry.styled'; import { ChevronDownIcon } from '@heroicons/react/24/outline'; import React, { useState } from 'react'; -import type { InquiryFormData } from '../../../../models/inquiry'; -import { usePostInquiry } from '../../../../hooks/usePostInquiry'; +import type { InquiryFormData } from '../../../models/inquiry'; +import { usePostInquiry } from '../../../hooks/usePostInquiry'; interface FormStateType { category: string; diff --git a/src/components/common/customerService/notice/Notice.styled.ts b/src/pages/customerService/notice/Notice.styled.ts similarity index 100% rename from src/components/common/customerService/notice/Notice.styled.ts rename to src/pages/customerService/notice/Notice.styled.ts diff --git a/src/components/common/customerService/notice/Notice.tsx b/src/pages/customerService/notice/Notice.tsx similarity index 100% rename from src/components/common/customerService/notice/Notice.tsx rename to src/pages/customerService/notice/Notice.tsx diff --git a/src/routes/AppRoutes.tsx b/src/routes/AppRoutes.tsx index 95cf64c8..c4f80484 100644 --- a/src/routes/AppRoutes.tsx +++ b/src/routes/AppRoutes.tsx @@ -19,13 +19,9 @@ const ChangePassword = lazy( const Main = lazy(() => import('../pages/main/Index')); const Layout = lazy(() => import('../components/common/layout/Layout')); const Home = lazy(() => import('../pages/home/Home')); -const FAQ = lazy(() => import('../components/common/customerService/faq/FAQ')); -const Notice = lazy( - () => import('../components/common/customerService/notice/Notice') -); -const Inquiry = lazy( - () => import('../components/common/customerService/inquiry/Inquiry') -); +const FAQ = lazy(() => import('../pages/customerService/faq/FAQ')); +const Notice = lazy(() => import('../pages/customerService/notice/Notice')); +const Inquiry = lazy(() => import('../pages/customerService/inquiry/Inquiry')); const MyPage = lazy(() => import('../pages/mypage/MyPage')); const UserPage = lazy(() => import('../pages/userpage/UserPage')); const Apply = lazy(() => import('../pages/apply/ApplyStep')); From c1850208e321a6880b3aea39334985ded4dddd94 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Wed, 30 Apr 2025 15:41:56 +0900 Subject: [PATCH 09/11] =?UTF-8?q?fix:=20pages=20=EC=9D=B4=EB=8F=99?= =?UTF-8?q?=ED=9B=84=20=EA=B2=BD=EB=A1=9C=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/mypage/ContentTab.tsx | 2 +- src/pages/customerService/MoveInquiredLink.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/mypage/ContentTab.tsx b/src/components/mypage/ContentTab.tsx index e99abce3..9a2ab23e 100644 --- a/src/components/mypage/ContentTab.tsx +++ b/src/components/mypage/ContentTab.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'; import * as S from './ContentTab.styled'; import { Link, Outlet, useLocation } from 'react-router-dom'; import { ROUTES } from '../../constants/routes'; -import MovedInquiredLink from '../common/customerService/MoveInquiredLink'; +import MovedInquiredLink from '../../pages/customerService/MoveInquiredLink'; interface Filter { title: string; diff --git a/src/pages/customerService/MoveInquiredLink.tsx b/src/pages/customerService/MoveInquiredLink.tsx index 7361b8aa..331ea4ee 100644 --- a/src/pages/customerService/MoveInquiredLink.tsx +++ b/src/pages/customerService/MoveInquiredLink.tsx @@ -1,4 +1,4 @@ -import { ROUTES } from '../../../constants/routes'; +import { ROUTES } from '../../constants/routes'; import * as S from './MoveInquiredLink.styled'; export default function MovedInquiredLink() { From c3b4cdd8cbc301a18668ae4f30b952bbd25f0353 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Wed, 30 Apr 2025 16:17:55 +0900 Subject: [PATCH 10/11] =?UTF-8?q?fix:=20mypage=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20Use?= =?UTF-8?q?rJoinProject.tsx=20=EC=8A=A4=ED=83=80=EC=9D=BC=EB=93=9C=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=EC=99=80=20=ED=83=80=EC=9E=85=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../userPage/joinedProject/UserJoinProject.tsx | 12 ++++++------ src/models/userProject.ts | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/userPage/joinedProject/UserJoinProject.tsx b/src/components/userPage/joinedProject/UserJoinProject.tsx index ba46642c..05acafbd 100644 --- a/src/components/userPage/joinedProject/UserJoinProject.tsx +++ b/src/components/userPage/joinedProject/UserJoinProject.tsx @@ -22,9 +22,9 @@ const UserJoinProject = () => { return ( - - 참여한 프로젝트 리스트 - + + 참여한 프로젝트 리스트 + {userJoinedProjectListData?.acceptedProjects && userJoinedProjectListData?.acceptedProjects?.length > 0 ? ( @@ -44,9 +44,9 @@ const UserJoinProject = () => { )} - - 기획한 프로젝트 리스트 - + + 기획한 프로젝트 리스트 + {userJoinedProjectListData?.ownProjects && userJoinedProjectListData?.ownProjects?.length > 0 ? ( diff --git a/src/models/userProject.ts b/src/models/userProject.ts index f843a084..e1aa1ab0 100644 --- a/src/models/userProject.ts +++ b/src/models/userProject.ts @@ -27,7 +27,7 @@ export interface ApiAppliedProject extends ApiCommonType { export interface SelectUserProject { acceptedProjects: JoinedProject[]; - ownProjects: AppliedProject[]; + ownProjects: JoinedProject[]; } export interface ApiSelectUserProject extends ApiCommonType { From 6526cdf7f69aa900b74fb7b95e0ea859ee1d3d95 Mon Sep 17 00:00:00 2001 From: YouD0313 <102004480+YouD0313@users.noreply.github.com> Date: Wed, 30 Apr 2025 16:30:20 +0900 Subject: [PATCH 11/11] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=EC=B0=B8?= =?UTF-8?q?=EA=B3=A0=20=EC=88=98=EC=A0=95-=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=9A=A9=EB=9F=89=20=EC=A0=9C=ED=95=9C,?= =?UTF-8?q?=20url=EA=B0=9D=EC=B2=B4=20=ED=95=B4=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/customerService/inquiry/Inquiry.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/pages/customerService/inquiry/Inquiry.tsx b/src/pages/customerService/inquiry/Inquiry.tsx index e3741c96..6fc9322e 100644 --- a/src/pages/customerService/inquiry/Inquiry.tsx +++ b/src/pages/customerService/inquiry/Inquiry.tsx @@ -5,7 +5,7 @@ import { } from '../../../constants/customerService'; import * as S from './Inquiry.styled'; import { ChevronDownIcon } from '@heroicons/react/24/outline'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import type { InquiryFormData } from '../../../models/inquiry'; import { usePostInquiry } from '../../../hooks/usePostInquiry'; @@ -74,10 +74,27 @@ export default function Inquiry() { const handleChangeFile = (e: React.ChangeEvent) => { const fileValue = e.target.value; const image = e.target.files?.[0]; + + // 파일 크기 제한 (예: 5MB) + const MAX_FILE_SIZE = 5 * 1024 * 1024; + if (image && image.size > MAX_FILE_SIZE) { + alert('파일 크기는 5MB 이하만 가능합니다.'); + e.target.value = ''; + return; + } + const fileImage = image ? URL.createObjectURL(image) : null; setForm((prev) => ({ ...prev, fileValue, fileImage })); }; + useEffect(() => { + return () => { + if (form.fileImage) { + URL.revokeObjectURL(form.fileImage); + } + }; + }, [form.fileImage]); + return (