Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] 파일 관련 에러 해결 #335

Merged
merged 30 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7301266
fix: 파일 업로드 하고 파트 변경 시 기존 파일 없어지는 에러 해결
eonseok-jeon Jul 31, 2024
55351f2
fix: 파일 업로드 시 버튼 disabled 처리
eonseok-jeon Jul 31, 2024
2632570
feat: 첨부파일 용량 초과 시 기존 파일 제거
eonseok-jeon Aug 1, 2024
0b0ff8b
fix: 에러 메세지와 textbox 겹치는 에러 해결
eonseok-jeon Aug 1, 2024
ec37a6e
fix: 파일 삭제 시 placeholder 안 뜨는 에러 해결
eonseok-jeon Aug 1, 2024
bc9f1c2
refactor: 불필요한 state 제거
eonseok-jeon Aug 1, 2024
5b0d9c3
refactor: 코드 리팩
eonseok-jeon Aug 1, 2024
2308c3f
fix: 두 번째 파일 부터 파일 전송 중 문구 안 뜨는 에러 수정
eonseok-jeon Aug 1, 2024
8793717
fix: 새로 올린 파일 지우면 임시저장된 데이터 넘어오는 에러 해결
eonseok-jeon Aug 1, 2024
09903fb
fix: 임시서장된 파일에 새로운 파일 업로드 했을 때 기존 파일이 그대로 보이는 에러 해결
eonseok-jeon Aug 1, 2024
ac1e6b0
fix: part 바꿨다가 다시 돌아왔을 때 파일 value 계속 남아있는 에러 해결
eonseok-jeon Aug 1, 2024
14a7f5c
fix: 중복된 key 제거
eonseok-jeon Aug 1, 2024
724e7ed
fix: 새로고침 시 파일 제출 문구 없어지는 에러 해결
eonseok-jeon Aug 1, 2024
da6628b
fix: 파일 확장자 원래대로 돌리기
eonseok-jeon Aug 1, 2024
77b87e2
chore: 파일 형식 수정
eonseok-jeon Aug 1, 2024
f8c9b5b
refactor: 파일 이름 및 placeholder 렌더 함수로 분리
eonseok-jeon Aug 1, 2024
2595912
chore: import 순서 수정
eonseok-jeon Aug 1, 2024
1b553c4
chore: 사라진 띄어쓰기 돌리기
eonseok-jeon Aug 1, 2024
6c2e5f4
fix: 파일 제거 시 placeholder 안 뜨는 에러 해결
eonseok-jeon Aug 7, 2024
8d6f9b1
refactor: 반복되는 코드 분리
eonseok-jeon Aug 7, 2024
9abae27
fix: 파일 제출 시 텍스트 박스 에러 사라지는 이슈 해결
eonseok-jeon Aug 7, 2024
32aca01
feat: 파일 업로드 에러 발생 시 안내 문구 뜨도록 수정
eonseok-jeon Aug 7, 2024
1694d3d
refactor: 파일 제거 로직 단순화
eonseok-jeon Aug 7, 2024
54738b0
merge: Merge branch 'develop' into fix/#318_after-draft
eonseok-jeon Aug 7, 2024
7e310ce
refactor: file placeholder 및 class name 로직 단순화
eonseok-jeon Aug 7, 2024
e30a7e7
feat: part 이동 되어도 파일 그대로 유지하기
eonseok-jeon Aug 7, 2024
a177907
fix: 파트 왔다갔다 할 시 파일명 defaultFile로 나타나는 에러 해결
eonseok-jeon Aug 7, 2024
f805bfb
chore: 에러 문구 수정
eonseok-jeon Aug 7, 2024
f46615c
fix: 파일 제거 하고 파트 변경 시 default file 그대로 돌아오는 에러 해결
eonseok-jeon Aug 7, 2024
4a1a6ae
refactor: 반복되는 코드 변수화
eonseok-jeon Aug 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/common/constants/validationCheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,7 @@ export const VALIDATION_CHECK = {
maxLength: 40,
errorText: '잘못된 입력 형식이에요.',
},
fileInput: {
errorText: '파일 크기가 너무 커요. 50MB 이하로 선택해주세요.',
},
};
103 changes: 64 additions & 39 deletions src/views/ApplyPage/components/FileInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useFormContext } from 'react-hook-form';

import 'firebase/compat/storage';
import { STATE_CHANGED, storage } from '@constants/firebase.ts';
import { VALIDATION_CHECK } from '@constants/validationCheck';

import IconPlusButton from './icons/IconPlusButton';
import { container, errorText, fileInput, fileLabelVar, fileNameVar, textWrapper } from './style.css';
Expand All @@ -18,22 +19,33 @@ interface FileInputProps {
}

const LIMIT_SIZE = 1024 ** 2 * 50; // 50MB
const ACCEPTED_FORMATS = '.pdf, .pptx';
const ACCEPTED_FORMATS = '.pdf';

const FileInput = ({ section, id, isReview, disabled, defaultFile }: FileInputProps) => {
const [isError, setIsError] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);

const [uploadPercent, setUploadPercent] = useState(-1);
const [file, setFile] = useState<File | null>(null);
const [fileName, setFileName] = useState('');

const fileName = file ? file.name : '';
const inputRef = useRef<HTMLInputElement>(null);
const isFileUploading = uploadPercent < 100 && uploadPercent >= 0;
const isFileSending = uploadPercent === 100;
const disabledStatus = disabled || isReview || isFileUploading || isFileSending;

const {
register,
setValue,
setError,
clearErrors,
getValues,
formState: { errors },
} = useFormContext();

const { register, setValue, clearErrors, getValues } = useFormContext();
const { id: defaultFileId, file: defaultFileUrl, fileName: defaultFileName } = defaultFile || {};

const handleFileUpload = (file: File, id: number) => {
const storageRef = storage.ref();
const uploadTask = storageRef.child(`recruiting/applicants/question/${file.name}${nanoid(7)}`).put(file);

track(`click-apply-add_file${id}`);

uploadTask.on(
Expand All @@ -49,13 +61,15 @@ const FileInput = ({ section, id, isReview, disabled, defaultFile }: FileInputPr
() => {
uploadTask.snapshot.ref.getDownloadURL().then((url) => {
const urlWithoutToken = url.split('&token=')[0];
setFile(file);

setFileName(file.name);
setValue(`file${id}`, {
recruitingQuestionId: id,
file: urlWithoutToken,
fileName: file.name,
});
getValues(`${section}${id}`) === '' && setValue(`${section}${id}`, '파일 제출');
setUploadPercent(-2);
clearErrors(`${section}${id}`);
track(`done-apply-add_file${id}`);
});
Expand All @@ -65,26 +79,30 @@ const FileInput = ({ section, id, isReview, disabled, defaultFile }: FileInputPr

const handleFileChange = (e: ChangeEvent<HTMLInputElement>, id: number) => {
const file = e.target.files?.[0];

if (file) {
if (LIMIT_SIZE < file.size) {
setIsError(true);
setError(`file${id}`, { type: 'maxLength', message: VALIDATION_CHECK.fileInput.errorText });
setUploadPercent(-1);
setValue(`file${id}`, undefined);
getValues(`${section}${id}`) === '파일 제출' && setValue(`${section}${id}`, '');
eonseok-jeon marked this conversation as resolved.
Show resolved Hide resolved

if (inputRef.current) {
inputRef.current.value = '';
}
setFile(null);
setFileName('delete-file');
} else {
setIsError(false);
setFile(null);
clearErrors(`file${id}`);
handleFileUpload(file, id);
}
}
};

const handleClickIcon = () => {
eonseok-jeon marked this conversation as resolved.
Show resolved Hide resolved
if (inputRef.current) {
if (file) {
if (fileName) {
inputRef.current.value = '';
setFile(null);
setFileName('delete-file');
setValue(`file${id}`, undefined);
setUploadPercent(-1);
getValues(`${section}${id}`) === '파일 제출' && setValue(`${section}${id}`, '');
Expand All @@ -100,6 +118,25 @@ const FileInput = ({ section, id, isReview, disabled, defaultFile }: FileInputPr
}
};

const getFileNameClass = () => {
if (uploadPercent === -1 && defaultFileName) {
return fileName === 'delete-file' ? 'default' : 'selected';
} else {
return fileName === '' ? 'default' : 'selected';
}
};

const getDisplayText = () => {
if (uploadPercent === -1 && defaultFileName) {
return fileName === 'delete-file' ? '50mb 이하 | pdf' : defaultFileName;
} else {
if (uploadPercent < 0 && fileName === '') return '50mb 이하 | pdf';
else if (isFileUploading) return `업로드 중... ${uploadPercent}/100% 완료`;
else if (isFileSending) return '파일을 전송하고 있어요... 잠시만 기다려주세요...';
else return fileName;
}
};
eonseok-jeon marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
if (defaultFileId && defaultFileUrl && defaultFileName) {
setValue(`file${defaultFileId}`, {
Expand All @@ -108,7 +145,14 @@ const FileInput = ({ section, id, isReview, disabled, defaultFile }: FileInputPr
recruitingQuestionId: defaultFileId,
});
}
}, [defaultFileId, defaultFileUrl, defaultFileName, setValue]);

if (getValues(`file${id}`) && getValues(`${section}${id}`) === '') setValue(`${section}${id}`, '파일 제출');

return () => {
setValue(`file${id}`, undefined);
getValues(`${section}${id}`) === '파일 제출' && setValue(`${section}${id}`, '');
};
}, [section, id, defaultFileId, defaultFileUrl, defaultFileName, getValues, setValue]);

return (
<div className={container}>
Expand All @@ -120,42 +164,23 @@ const FileInput = ({ section, id, isReview, disabled, defaultFile }: FileInputPr
onChange={(e) => handleFileChange(e, id)}
ref={inputRef}
className={`amp-unmask ${fileInput}`}
disabled={
disabled ||
isReview ||
(uploadPercent >= 0 && uploadPercent < 100) ||
(uploadPercent === 100 && fileName === '')
}
disabled={disabledStatus}
/>
<label
htmlFor={`file-${id}`}
className={fileLabelVar[isError ? 'error' : fileName === '' ? 'default' : 'selected']}>
className={fileLabelVar[errors[`file${id}`] ? 'error' : fileName === '' ? 'default' : 'selected']}>
<div className={textWrapper}>
<span>파일</span>
{(uploadPercent !== -1 || !defaultFileName) && (
<span className={fileNameVar[fileName === '' ? 'default' : 'selected']}>
{uploadPercent < 0 && fileName === ''
? '50mb 이하 | pdf, pptx'
: uploadPercent < 100
? `업로드 중... ${uploadPercent}/100% 완료`
: uploadPercent === 100 && fileName === ''
? '파일을 전송하고 있어요... 잠시만 기다려주세요...'
: fileName}
</span>
)}
{uploadPercent === -1 && defaultFileName && (
<span className={fileNameVar['selected']}>{defaultFileName}</span>
)}
<span className={fileNameVar[getFileNameClass()]}>{getDisplayText()}</span>
</div>
<IconPlusButton
isSelected={getValues()[`file${id}`]}
isSelected={fileName !== 'delete-file' && getValues(`file${id}`)}
onClickIcon={handleClickIcon}
disabled={disabled || isReview}
disabled={disabledStatus}
/>
</label>
{isError && <p className={errorText}>첨부파일 용량을 초과했어요</p>}
{errors[`file${id}`] && <p className={errorText}>{errors[`file${id}`]?.message as string}</p>}
</div>
);
};

export default FileInput;
4 changes: 2 additions & 2 deletions src/views/ApplyPage/components/Info/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ const Info = ({ value }: { value: string }) => {
return (
<article>
<ol className={container}>
{value?.split('\n').map((text) => (
<li className={info} key={text}>
{value?.split('\n').map((text, idx) => (
<li className={info} key={`${idx},${text}`}>
{text}
</li>
))}
Expand Down