Skip to content

Commit

Permalink
[Deploy] 정말 최종 배포
Browse files Browse the repository at this point in the history
  • Loading branch information
eonseok-jeon authored Jul 31, 2024
2 parents 360d8e3 + 7ac42ea commit a3d19d1
Show file tree
Hide file tree
Showing 25 changed files with 159 additions and 79 deletions.
6 changes: 6 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLDialogElement>(null);

const [isLight, setIsLight] = useState(true);
Expand Down
3 changes: 2 additions & 1 deletion src/common/components/Input/components/InputTheme/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ export const TextBox이메일 = ({
return (
<TextBox label="이메일" name="email" required>
<InputLine
style={{ paddingRight: isActive ? 50 : 16 }}
style={{ width: 308, paddingRight: isActive ? 50 : 16 }}
name="email"
placeholder="이메일을 입력해주세요."
type="email"
Expand All @@ -153,6 +153,7 @@ export const TextBox이메일 = ({
<Timer isActive={isActive} onResetTimer={handleResetTimer} />
</InputLine>
<InputLine
style={{ width: 308 }}
id="verification-code"
readOnly={!isActive}
name="code"
Expand Down
45 changes: 27 additions & 18 deletions src/common/components/Input/components/Timer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, useRef, useState } from 'react';
import { differenceInSeconds } from 'date-fns';
import { useEffect, useState } from 'react';

import { timer } from './style.css';
import { TimerProps } from './types';
Expand All @@ -8,29 +9,37 @@ const INITIAL_TIME = 300;

// TextBox 내부 타이머
const Timer = ({ isActive, onResetTimer }: TimerProps) => {
const [seconds, setSeconds] = useState(INITIAL_TIME);
const timerRef = useRef<NodeJS.Timeout | null>(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]);

Expand Down
4 changes: 2 additions & 2 deletions src/common/components/Layout/components/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ const Header = () => {
const menuItems = isMakers ? MENU_ITEMS_MAKERS : MENU_ITEMS;

const handleClickLogo = () => {
pathname === '/' ? navigate(0) : navigate('/');
pathname === '/' ? window.location.reload() : navigate('/');
};

const handleLogout = () => {
track('click-gnb-logout');
reset();
localStorage.removeItem('soptApplyAccessToken');
localStorage.removeItem('soptApplyAccessTokenExpiredTime');
pathname === '/' ? navigate(0) : navigate('/');
pathname === '/' ? window.location.reload() : navigate('/');
};
return (
<>
Expand Down
6 changes: 4 additions & 2 deletions src/common/components/Layout/components/Header/style.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
2 changes: 1 addition & 1 deletion src/common/components/Layout/style.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const container = style({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
minWidth: 1440,
minWidth: 1000,
backgroundColor: theme.color.background,
});

Expand Down
11 changes: 9 additions & 2 deletions src/common/components/Textarea/components/Input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,16 @@ import {
interface InputProps<T extends FieldValues> extends TextareaHTMLAttributes<HTMLTextAreaElement> {
name: Path<T>;
maxCount: number;
isFileInput?: boolean;
}

const Input = <T extends FieldValues>({ name, maxCount, required, ...textareaElements }: InputProps<T>) => {
const Input = <T extends FieldValues>({
name,
maxCount,
required,
isFileInput,
...textareaElements
}: InputProps<T>) => {
const {
watch,
register,
Expand All @@ -26,7 +33,7 @@ const Input = <T extends FieldValues>({ 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(() => {
Expand Down
42 changes: 34 additions & 8 deletions src/common/components/Textarea/components/Label/index.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLHeadingElement> {
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) => (
<Fragment key={`${item}-${idx}`}>
{item}
{idx !== limit - 1 && '\n'}
</Fragment>
));

const renderRestQuestions = (questions: string[]) =>
questions.slice(firstEmptyIndex).map((item, idx) => (
<Fragment key={`${item}-${idx}`}>
{item} {`\n`}
</Fragment>
));

return (
<h4 className={labelStyle} {...headerElementProps}>
<label style={{ cursor: 'pointer' }} htmlFor={label}>
<span>{children}</span>
<span style={{ position: 'relative' }}>
{' '}
({maxCount}자)
{required && <i className={requireDot} />}
<span>
{firstEmptyIndex === -1 ? children : renderQuestions(questionArray, firstEmptyIndex)}
<span style={{ position: 'relative' }}>
{' '}
({maxCount}자)
{required && <i className={requireDot} />}
</span>
</span>
{firstEmptyIndex !== -1 && (
<>
<br />
<span>{renderRestQuestions(questionArray)}</span>
</>
)}
</label>
</h4>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
15 changes: 11 additions & 4 deletions src/common/components/Textarea/index.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -9,8 +9,8 @@ import type { FieldValues, Path } from 'react-hook-form';
interface TextareaProps<T extends FieldValues> extends TextareaHTMLAttributes<HTMLTextAreaElement> {
name: Path<T>;
maxCount: number;
children: string | number;
extraInput?: ReactNode;
children: string;
extraInput?: ReactElement;
}

const Textarea = <T extends FieldValues>({
Expand All @@ -29,7 +29,14 @@ const Textarea = <T extends FieldValues>({
{children}
</Label>
{extraInput}
<Input id={id} name={name} required={required} maxCount={maxCount} {...textareaElements} />
<Input
id={id}
name={name}
required={required}
maxCount={maxCount}
isFileInput={extraInput?.props.defaultFile}
{...textareaElements}
/>
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/common/constants/validationCheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '잘못된 입력 형식이에요.',
},
Expand Down
12 changes: 12 additions & 0 deletions src/common/hooks/useCheckBrowser.ts
Original file line number Diff line number Diff line change
@@ -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;
11 changes: 5 additions & 6 deletions src/views/ApplyPage/components/ApplyInfo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ const ApplyInfo = memo(({ isReview }: { isReview: boolean }) => {
<section className={infoContainer}>
{!isReview && (
<ul className={infoWrapper}>
<span key="first-info" className={infoItems}>
&#183; 지원서 작성 전에{' '}
<li key="first-info" className={infoItems}>
지원서 작성 전에{` `}
<a
href="https://makers.sopt.org/recruit"
className={infoItemsBold}
Expand All @@ -58,12 +58,11 @@ const ApplyInfo = memo(({ isReview }: { isReview: boolean }) => {
모집 및 활동 일정
</a>
을 꼭 숙지하고 지원해 주시기 바랍니다.
</span>
</li>
{APPLY_INFO.sections.map(({ id, content }) => (
<li key={id}>
&#183;{' '}
<li key={id} className={infoItems}>
{content.map(({ text, weight }) => (
<span key={text} className={weight === 'normal' ? infoItems : infoItemsBold}>
<span key={text} className={weight === 'strong' ? infoItemsBold : ''}>
{text}
</span>
))}
Expand Down
15 changes: 9 additions & 6 deletions src/views/ApplyPage/components/ApplyInfo/style.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
2 changes: 1 addition & 1 deletion src/views/ApplyPage/components/BottomSection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const BottomSection = ({ isReview, knownPath }: BottomSectionProps) => {
<div id="check-necessary" className={doubleLineCheck}>
<p className={label}>
{isMakers
? 'SOPT makers의 행사 및 정기 모임은 매주 일요일에 진행됩니다.'
? 'SOPT makers의 행사 및 정기 모임은 일요일에 진행됩니다.'
: 'SOPT의 행사 및 세미나는 매주 토요일에 진행됩니다.'}
</p>
<Checkbox checked={isReview ? true : undefined} name="attendance" required>
Expand Down
6 changes: 3 additions & 3 deletions src/views/ApplyPage/components/CommonSection/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ const CommonSection = ({ isReview, refCallback, questions, commonQuestionsDraft

return (
<div key={value}>
{charLimit == null && <Info value={value} />}
{charLimit != null && (
{!charLimit && <Info value={value} />}
{!!charLimit && (
<Textarea
name={`common${id}`}
defaultValue={defaultValue}
Expand All @@ -49,7 +49,7 @@ const CommonSection = ({ isReview, refCallback, questions, commonQuestionsDraft
<FileInput section="common" id={id} isReview={isReview} defaultFile={defaultFile} />
) : urls ? (
<LinkInput urls={urls} />
) : null
) : undefined
}
required={!optional}
disabled={isReview}>
Expand Down
7 changes: 6 additions & 1 deletion src/views/ApplyPage/components/FileInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,12 @@ const FileInput = ({ section, id, isReview, disabled, defaultFile }: FileInputPr
onChange={(e) => handleFileChange(e, id)}
ref={inputRef}
className={fileInput}
disabled={disabled || isReview}
disabled={
disabled ||
isReview ||
(uploadPercent >= 0 && uploadPercent < 100) ||
(uploadPercent === 100 && fileName === '')
}
/>
<label
htmlFor={`file-${id}`}
Expand Down
14 changes: 8 additions & 6 deletions src/views/ApplyPage/components/Info/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { container, info } from './style.css';

const Info = ({ value }: { value: string }) => {
return (
<article className={container}>
{value.split('\n').map((text) => (
<p className={info} key={text}>
{text && <>&#183;</>} {text}
</p>
))}
<article>
<ol className={container}>
{value?.split('\n').map((text) => (
<li className={info} key={text}>
{text}
</li>
))}
</ol>
</article>
);
};
Expand Down
Loading

0 comments on commit a3d19d1

Please sign in to comment.