diff --git a/src/App.tsx b/src/App.tsx index a691387c..5fcbe0b8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -43,6 +43,12 @@ const router = createBrowserRouter([ ]); const App = () => { + const isMobile = /Mobi/i.test(window.navigator.userAgent); + if (isMobile) { + alert('PC로 지원해주세요.'); + window.location.href = 'https://makers.sopt.org/recruit'; + } + const sessionRef = useRef(null); const [isLight, setIsLight] = useState(true); diff --git a/src/common/components/Input/components/InputTheme/index.tsx b/src/common/components/Input/components/InputTheme/index.tsx index 8617d2ad..1ea7fc17 100644 --- a/src/common/components/Input/components/InputTheme/index.tsx +++ b/src/common/components/Input/components/InputTheme/index.tsx @@ -137,7 +137,7 @@ export const TextBox이메일 = ({ return ( { - const [seconds, setSeconds] = useState(INITIAL_TIME); - const timerRef = useRef(null); - const clearTimer = () => { - setSeconds(INITIAL_TIME); - timerRef.current && clearInterval(timerRef.current); - }; + const [seconds, setSeconds] = useState(INITIAL_TIME - 1); useEffect(() => { + let timeout: NodeJS.Timeout | null = null; + if (isActive) { - timerRef.current = setInterval(() => { - setSeconds((prev) => { - if (prev <= 1) { - onResetTimer(); - clearTimer(); - return INITIAL_TIME; - } - return prev - 1; - }); - }, 1000); + const initialDate = new Date(); + const ExpiryTime = new Date(initialDate.getTime() + INITIAL_TIME * 1000); + + const tick = () => { + const now = new Date(); + const diffInSeconds = differenceInSeconds(ExpiryTime, now); + + if (diffInSeconds > 0) { + setSeconds(diffInSeconds === INITIAL_TIME ? INITIAL_TIME - 1 : diffInSeconds); + timeout = setTimeout(tick, 1000 - (now.getTime() % 1000)); + } else { + onResetTimer(); + setSeconds(INITIAL_TIME - 1); + } + }; + + tick(); + } else { + setSeconds(INITIAL_TIME - 1); } return () => { - clearTimer(); + if (timeout) { + clearTimeout(timeout); + } }; }, [isActive, onResetTimer]); diff --git a/src/common/components/Layout/components/Header/index.tsx b/src/common/components/Layout/components/Header/index.tsx index 5f240162..e96ba87a 100644 --- a/src/common/components/Layout/components/Header/index.tsx +++ b/src/common/components/Layout/components/Header/index.tsx @@ -25,7 +25,7 @@ const Header = () => { const menuItems = isMakers ? MENU_ITEMS_MAKERS : MENU_ITEMS; const handleClickLogo = () => { - pathname === '/' ? navigate(0) : navigate('/'); + pathname === '/' ? window.location.reload() : navigate('/'); }; const handleLogout = () => { @@ -33,7 +33,7 @@ const Header = () => { reset(); localStorage.removeItem('soptApplyAccessToken'); localStorage.removeItem('soptApplyAccessTokenExpiredTime'); - pathname === '/' ? navigate(0) : navigate('/'); + pathname === '/' ? window.location.reload() : navigate('/'); }; return ( <> diff --git a/src/common/components/Layout/components/Header/style.css.ts b/src/common/components/Layout/components/Header/style.css.ts index 6c1c2cfd..a0b3d299 100644 --- a/src/common/components/Layout/components/Header/style.css.ts +++ b/src/common/components/Layout/components/Header/style.css.ts @@ -10,9 +10,11 @@ export const container = style({ position: 'sticky', top: 0, - width: 1440, + maxWidth: 1440, + width: '100%', + minWidth: 1000, margin: '0 auto', - padding: '22px 376px 22px 360px', + padding: '22px 156px 22px 150px', backgroundColor: theme.color.background, diff --git a/src/common/components/Layout/style.css.ts b/src/common/components/Layout/style.css.ts index c31540ee..111a6b3a 100644 --- a/src/common/components/Layout/style.css.ts +++ b/src/common/components/Layout/style.css.ts @@ -6,7 +6,7 @@ export const container = style({ display: 'flex', flexDirection: 'column', alignItems: 'center', - minWidth: 1440, + minWidth: 1000, backgroundColor: theme.color.background, }); diff --git a/src/common/components/Textarea/components/Input/index.tsx b/src/common/components/Textarea/components/Input/index.tsx index 16d94b64..828941f9 100644 --- a/src/common/components/Textarea/components/Input/index.tsx +++ b/src/common/components/Textarea/components/Input/index.tsx @@ -14,9 +14,16 @@ import { interface InputProps extends TextareaHTMLAttributes { name: Path; maxCount: number; + isFileInput?: boolean; } -const Input = ({ name, maxCount, required, ...textareaElements }: InputProps) => { +const Input = ({ + name, + maxCount, + required, + isFileInput, + ...textareaElements +}: InputProps) => { const { watch, register, @@ -26,7 +33,7 @@ const Input = ({ name, maxCount, required, ...textareaEle } = useFormContext(); const state = errors[name] ? 'error' : 'default'; - const textareaSize = maxCount > 100 ? 'lg' : 'sm'; + const textareaSize = isFileInput || maxCount <= 100 ? 'sm' : 'lg'; const textCount = watch(name)?.length; useEffect(() => { diff --git a/src/common/components/Textarea/components/Label/index.tsx b/src/common/components/Textarea/components/Label/index.tsx index c6ab25a9..a3e79d3b 100644 --- a/src/common/components/Textarea/components/Label/index.tsx +++ b/src/common/components/Textarea/components/Label/index.tsx @@ -1,24 +1,50 @@ -import { requireDot, labelStyle } from './style.css'; +import { Fragment, type HTMLAttributes } from 'react'; -import type { HTMLAttributes } from 'react'; +import { requireDot, labelStyle } from './style.css'; interface LabelProps extends HTMLAttributes { - children: string | number; + children: string; maxCount: number; required: boolean; label: string; } const Label = ({ children, maxCount, required, label, ...headerElementProps }: LabelProps) => { + const questionArray = children.split('\n'); + const firstEmptyIndex = questionArray.indexOf(''); + + const renderQuestions = (questions: string[], limit: number) => + questions.slice(0, limit).map((item, idx) => ( + + {item} + {idx !== limit - 1 && '\n'} + + )); + + const renderRestQuestions = (questions: string[]) => + questions.slice(firstEmptyIndex).map((item, idx) => ( + + {item} {`\n`} + + )); + return (

); diff --git a/src/common/components/Textarea/components/Label/style.css.ts b/src/common/components/Textarea/components/Label/style.css.ts index f5660ac1..838f408e 100644 --- a/src/common/components/Textarea/components/Label/style.css.ts +++ b/src/common/components/Textarea/components/Label/style.css.ts @@ -12,7 +12,6 @@ export const labelStyle = style({ export const requireDot = style({ position: 'absolute', bottom: 5, - right: -10, display: 'inline-block', borderRadius: '100%', width: 8, diff --git a/src/common/components/Textarea/index.tsx b/src/common/components/Textarea/index.tsx index ed7a85eb..97812ef0 100644 --- a/src/common/components/Textarea/index.tsx +++ b/src/common/components/Textarea/index.tsx @@ -1,4 +1,4 @@ -import { type ReactNode, type TextareaHTMLAttributes, useId } from 'react'; +import { type ReactElement, type TextareaHTMLAttributes, useId } from 'react'; import Input from './components/Input'; import Label from './components/Label'; @@ -9,8 +9,8 @@ import type { FieldValues, Path } from 'react-hook-form'; interface TextareaProps extends TextareaHTMLAttributes { name: Path; maxCount: number; - children: string | number; - extraInput?: ReactNode; + children: string; + extraInput?: ReactElement; } const Textarea = ({ @@ -29,7 +29,14 @@ const Textarea = ({ {children} {extraInput} - + ); }; diff --git a/src/common/constants/validationCheck.ts b/src/common/constants/validationCheck.ts index 6cef8be9..c0a69866 100644 --- a/src/common/constants/validationCheck.ts +++ b/src/common/constants/validationCheck.ts @@ -55,7 +55,7 @@ export const VALIDATION_CHECK = { errorText: '잘못된 입력 형식이에요.', }, textInput: { - pattern: /^[가-힣\s.,·()\-a-zA-Z\d]+$/, + pattern: /^[가-힣\s.,·()\-a-zA-Z\d&]+$/, maxLength: 40, errorText: '잘못된 입력 형식이에요.', }, diff --git a/src/common/hooks/useCheckBrowser.ts b/src/common/hooks/useCheckBrowser.ts new file mode 100644 index 00000000..7d942b6c --- /dev/null +++ b/src/common/hooks/useCheckBrowser.ts @@ -0,0 +1,12 @@ +import { useEffect } from 'react'; + +const useCheckBrowser = () => { + useEffect(() => { + const isChrome = /Chrome/i.test(window.navigator.userAgent); + if (!isChrome) { + alert('원활한 지원을 위해 크롬(Chrome) 브라우저 사용을 권장드려요.'); + } + }, []); +}; + +export default useCheckBrowser; diff --git a/src/views/ApplyPage/components/ApplyInfo/index.tsx b/src/views/ApplyPage/components/ApplyInfo/index.tsx index 3e61a815..fe8254b0 100644 --- a/src/views/ApplyPage/components/ApplyInfo/index.tsx +++ b/src/views/ApplyPage/components/ApplyInfo/index.tsx @@ -48,8 +48,8 @@ const ApplyInfo = memo(({ isReview }: { isReview: boolean }) => {
{!isReview && (
    - - · 지원서 작성 전에{' '} +
  • + 지원서 작성 전에{` `} { 모집 및 활동 일정 을 꼭 숙지하고 지원해 주시기 바랍니다. - +
  • {APPLY_INFO.sections.map(({ id, content }) => ( -
  • - ·{' '} +
  • {content.map(({ text, weight }) => ( - + {text} ))} diff --git a/src/views/ApplyPage/components/ApplyInfo/style.css.ts b/src/views/ApplyPage/components/ApplyInfo/style.css.ts index c590709a..4875cdc2 100644 --- a/src/views/ApplyPage/components/ApplyInfo/style.css.ts +++ b/src/views/ApplyPage/components/ApplyInfo/style.css.ts @@ -19,16 +19,19 @@ export const infoWrapper = style({ export const infoItems = style({ color: theme.color.lighterText, ...theme.font.BODY_1_18_M, + marginLeft: 20, + listStyle: 'outside', whiteSpace: 'pre-wrap', letterSpacing: '-0.27px', -}); -export const infoItemsBold = style([ - infoItems, - { - fontWeight: 700, + '::marker': { + fontSize: 10, }, -]); +}); + +export const infoItemsBold = style({ + fontWeight: 700, +}); export const dateWrapper = style({ display: 'flex', diff --git a/src/views/ApplyPage/components/BottomSection/index.tsx b/src/views/ApplyPage/components/BottomSection/index.tsx index 3341ae2c..e223ea80 100644 --- a/src/views/ApplyPage/components/BottomSection/index.tsx +++ b/src/views/ApplyPage/components/BottomSection/index.tsx @@ -35,7 +35,7 @@ const BottomSection = ({ isReview, knownPath }: BottomSectionProps) => {

    {isMakers - ? 'SOPT makers의 행사 및 정기 모임은 매주 일요일에 진행됩니다.' + ? 'SOPT makers의 행사 및 정기 모임은 일요일에 진행됩니다.' : 'SOPT의 행사 및 세미나는 매주 토요일에 진행됩니다.'}

    diff --git a/src/views/ApplyPage/components/CommonSection/index.tsx b/src/views/ApplyPage/components/CommonSection/index.tsx index 5dce73d3..59aa5a33 100644 --- a/src/views/ApplyPage/components/CommonSection/index.tsx +++ b/src/views/ApplyPage/components/CommonSection/index.tsx @@ -32,8 +32,8 @@ const CommonSection = ({ isReview, refCallback, questions, commonQuestionsDraft return (
    - {charLimit == null && } - {charLimit != null && ( + {!charLimit && } + {!!charLimit && (