diff --git a/src/components/portfolio/image/upload/PortfolioImageUpload.tsx b/src/components/portfolio/image/upload/PortfolioImageUpload.tsx index efeab4ff..ff04c41b 100644 --- a/src/components/portfolio/image/upload/PortfolioImageUpload.tsx +++ b/src/components/portfolio/image/upload/PortfolioImageUpload.tsx @@ -128,6 +128,7 @@ const PortfolioImageUpload = ({ shouldDirty: true, shouldTouch: true, }); + event.target.value = ''; // 같은 이름 파일 있을 때만 input value 초기화 }; continue; } diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts index 256dc9ef..7499233e 100644 --- a/src/hooks/useAuth.ts +++ b/src/hooks/useAuth.ts @@ -99,7 +99,6 @@ export const useCheckDuplicateNickname = (authKeys: string[], isEnabled: boolean queryKey: authKeys, queryFn: isEnabled => isEnabled && checkDuplicateNickname(authKeys[1]), enabled: isEnabled, - staleTime: 1000, }); }; diff --git a/src/hooks/usePortfolio.ts b/src/hooks/usePortfolio.ts index 1a6b6a90..d3eb7cfa 100644 --- a/src/hooks/usePortfolio.ts +++ b/src/hooks/usePortfolio.ts @@ -27,6 +27,7 @@ export const useReadPortfolio = (portfolioId: string) => { return useQuery({ queryKey: portfolioKeys.readPortfolio(portfolioId), queryFn: () => readPortfolio(portfolioId), + enabled: !!portfolioId, }); }; diff --git a/src/pages/account/nicknameSetting/NicknameSettingPage.tsx b/src/pages/account/nicknameSetting/NicknameSettingPage.tsx index c1d45a7f..fe0985dc 100644 --- a/src/pages/account/nicknameSetting/NicknameSettingPage.tsx +++ b/src/pages/account/nicknameSetting/NicknameSettingPage.tsx @@ -46,7 +46,7 @@ const NicknameSettingPage = () => { const nickname = useDebounce(watch('nickname')); const authKeys = ['checkDuplicateNickname', nickname]; - const { data } = useCheckDuplicateNickname(authKeys, isDirty && isValid); + const { data } = useCheckDuplicateNickname(authKeys, isDirty && isValid && !!nickname); const [duplicateNicknameValidation, setDuplicateNicknameValidation] = useState(''); const [duplicated, setDuplicated] = useState(false); diff --git a/src/pages/index.ts b/src/pages/index.ts index 3df1dbeb..0d023a00 100644 --- a/src/pages/index.ts +++ b/src/pages/index.ts @@ -27,7 +27,7 @@ import RecruitPostingApply from './recruit/recruitManagePage/RecruitPostingApply import RecruitMyPostings from './recruit/recruitManagePage/RecruitMyPostings'; import CompleteSignUpPage from './account/complete/CompleteSignUpPage'; import PrivateRouter from './routes/PrivateRouter'; -import PortfolioManagementPage from './portfolio/management/PortfolioManagmentPage'; +import PortfolioManagementPage from './portfolio/management/PortfolioManagementPage'; import NotFound from './notFound/NotFound'; import AccountSetting from './account/accountSetting/AccountSetting'; diff --git a/src/pages/portfolio/edit/PortfolioEditPage.tsx b/src/pages/portfolio/edit/PortfolioEditPage.tsx index 508b3426..1fc4bd8c 100644 --- a/src/pages/portfolio/edit/PortfolioEditPage.tsx +++ b/src/pages/portfolio/edit/PortfolioEditPage.tsx @@ -30,7 +30,7 @@ import { useUploadImageFile, } from '../../../hooks'; import PORTFOLIO_EDIT_DATA from './portfolioEditData'; -import { modules, formats, fixModalBackground, zipFile } from '../../../utils'; +import { modules, formats, fixModalBackground, zipFile, checkEnterKeyDown } from '../../../utils'; import { Refresh } from '../../../assets'; import type ReactQuill from 'react-quill'; import { useRecoilValue } from 'recoil'; @@ -65,7 +65,7 @@ const PortfolioEditPage = () => { const { data: portfolio, isSuccess: isSuccessReadPortfolio } = useReadPortfolio(portfolioId); - const { register, formState, handleSubmit, control, watch, getValues, setValue } = + const { register, formState, handleSubmit, control, watch, getValues, setValue, clearErrors } = useForm({ mode: 'onChange', values: { @@ -116,6 +116,8 @@ const PortfolioEditPage = () => { // 폼 제출 const formData = getValues(); + delete formData.mainImage; + const portfolioData = { ...formData, mainImageFileName: imageResponse?.[1].fileName, @@ -181,6 +183,22 @@ const PortfolioEditPage = () => { const role = useDebounce(watch('role')) as string; const { data: roles } = useReadRoleList(role); + // 진행 기간 + useEffect(() => { + const subscription = watch(value => { + if ( + differenceInDays( + new Date(value['endDate'] as string), + new Date(value['startDate'] as string) + ) >= 0 + ) { + clearErrors(['startDate', 'endDate']); + } + }); + + return () => subscription.unsubscribe(); + }, [watch]); + // 진행 방식 const [proceedType, setProceedType] = useState(portfolio?.proceedType); const handleRadioClick = (id: string) => { @@ -259,6 +277,7 @@ const PortfolioEditPage = () => { }; useEffect(() => { + setValue('content', PORTFOLIO_EDITOR_TEMPLATE); if (isSuccessReadPortfolio) { setProceedType(portfolio?.proceedType); setSkillList(portfolio?.skills ? portfolio?.skills : []); @@ -266,10 +285,6 @@ const PortfolioEditPage = () => { } }, [isSuccessReadPortfolio]); - const checkEnterKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') e.preventDefault(); - }; - const checkTabKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Tab') event.preventDefault(); }; @@ -304,227 +319,236 @@ const PortfolioEditPage = () => { return ( <> - {isSuccessReadPortfolio && ( - checkEnterKeyDown(e)} - > - - -

포트폴리오 작성

-

- 작성하신 포트폴리오는 프로필을 통해 보여집니다. 진행했던 내용을 자유롭게 - 작성해보세요! -

-
-
- - - - 슬라이드 이미지 - - - {LABEL.image} - setModalOpen(true)} - /> - {modalOpen && ( - - setModalOpen(false)} /> - - )} - - checkEnterKeyDown(e)} + > + + +

포트폴리오 작성

+

+ 작성하신 포트폴리오는 프로필을 통해 보여집니다. 진행했던 내용을 자유롭게 작성해보세요! +

+
+
+ + + + 슬라이드 이미지 + + + {LABEL.image} + setModalOpen(true)} /> - - -
-
- - - - 기본 정보 - - {/* 포트폴리오 제목 */} - + setModalOpen(false)} /> + + )} + + + + +
+
+ + + + 기본 정보 + + {/* 포트폴리오 제목 */} + + {/* 포트폴리오 한줄 소개 */} + + + {/* 분야 */} + - {/* 포트폴리오 한줄 소개 */} - + + {/* 진행기간 */} + + 진행기간 - {/* 분야 */} - - {/* 역할 */} - - - {/* 진행기간 */} - - 진행기간 - - { + rules={{ + required: '시작일을 설정해주세요', + validate: (startDate: string) => { + if (watch('endDate')) { return ( differenceInDays( new Date(watch('endDate') as string), new Date(startDate) - ) >= 0 || '시작일을 종료일보다 빠르게 설정해주세요' + ) >= 0 || '시작일을 종료일 이전으로 설정해주세요' ); - }, - }} - /> - - - - {/* 진행방식 */} - - 진행방식 - - - {PROCEED_TYPE.map(type => ( - -
- {type} -
-
- ))} -
- - {formState?.errors['proceedType']?.message} - -
-
- {/* 스킬 */} - - + { + if (watch('startDate')) { + return ( + differenceInDays( + new Date(endDate), + new Date(watch('startDate') as string) + ) >= 0 || '종료일을 시작일 이후로 설정해주세요' + ); + } + }, + }} /> - - {skillList?.map(({ ...props }, index) => ( - + + + {/* 진행방식 */} + + 진행방식 + + + {PROCEED_TYPE.map(type => ( + +
+ {type} +
+
))}
-
+ + {formState?.errors['proceedType']?.message} + +
-
-
-
- - - - 상세 내용 - - {LABEL.content} - { - ref(e); - if (quillRef) quillRef.current = e; - }} - value={watch('content')} - defaultValue={portfolio?.content ?? PORTFOLIO_EDITOR_TEMPLATE} - onChange={handleChangeEditor} - modules={modules} - formats={formats} - onKeyDown={checkTabKeyDown} - {...PORTFOLIO_EDIT_DATA.content} + {/* 스킬 */} + + - - -
-
- - - - 링크 - - addLink()} /> - - {links?.map((link, index) => ( - + {skillList?.map(({ ...props }, index) => ( + ))} - + - -
-
- - - navigate(-1)} /> - - +
+
+
-
- )} + + + + 상세 내용 + + {LABEL.content} + { + ref(e); + if (quillRef) quillRef.current = e; + }} + value={watch('content')} + defaultValue={portfolio?.content ?? PORTFOLIO_EDITOR_TEMPLATE} + onChange={handleChangeEditor} + modules={modules} + formats={formats} + onKeyDown={checkTabKeyDown} + {...PORTFOLIO_EDIT_DATA.content} + /> + + +
+
+ + + + 링크 + + addLink()} /> + + {links?.map((link, index) => ( + + ))} + + + +
+
+ + + navigate(-1)} /> + + + + + ) {alertOpen && ( diff --git a/src/pages/portfolio/management/PortfolioManagmentPage.tsx b/src/pages/portfolio/management/PortfolioManagementPage.tsx similarity index 100% rename from src/pages/portfolio/management/PortfolioManagmentPage.tsx rename to src/pages/portfolio/management/PortfolioManagementPage.tsx diff --git a/src/pages/profile/edit/ProfileEditPage.tsx b/src/pages/profile/edit/ProfileEditPage.tsx index 7eeac74b..85e6528c 100644 --- a/src/pages/profile/edit/ProfileEditPage.tsx +++ b/src/pages/profile/edit/ProfileEditPage.tsx @@ -36,7 +36,7 @@ import { } from '../../../hooks'; import { useNavigate } from 'react-router-dom'; import { useReadInfinitePortfolioList } from '../../../hooks/usePortfolio'; -import { fixModalBackground } from '../../../utils'; +import { fixModalBackground, checkEnterKeyDown } from '../../../utils'; interface FormValues { nickname?: string; @@ -389,7 +389,7 @@ const ProfileEditPage = () => { return ( <> - + checkEnterKeyDown(e)}> { + if (e.key === 'Enter' && e.target.tagName !== 'TEXTAREA') e.preventDefault(); +}; + +export default checkEnterKeyDown; diff --git a/src/utils/index.ts b/src/utils/index.ts index bfe23fc3..9d714005 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -13,6 +13,7 @@ import isNotNumber from './isNotNumber'; import calculateDate from './calculateDate'; import throttle from './throttle'; import addClassToEmptyPTags from './addClassToEmptyPTags'; +import checkEnterKeyDown from './checkEnterKeyDown'; export { modules, @@ -31,4 +32,5 @@ export { calculateDate, throttle, addClassToEmptyPTags, + checkEnterKeyDown, };