Skip to content

Commit

Permalink
HOTFIX: 리스트생성 imageUrl 미삽입으로 인한 에러 해결 및 기타 수정 (#18)
Browse files Browse the repository at this point in the history
* Chore: yarn.lock 삭제후 재설치 실행

* Fix: 아이템 타이틀 양 사이드 아이콘 가려짐 오류 수정

* Fix: 리스트 생성 리퀘스트에 imageUrl 빈문자열 추가로 오류 수정 및 에러 토스트, 생성 후 리스트 상세 이동 구현

* Refactor: 버튼 비활성화 조건 중 이미지 업로드 성공을 리스트 생성 성공으로 수정
  • Loading branch information
seoyoung-min authored Feb 6, 2024
1 parent 88ced21 commit 1f2ee10
Show file tree
Hide file tree
Showing 11 changed files with 793 additions and 584 deletions.
2 changes: 1 addition & 1 deletion public/icons/dnd.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions src/app/create/_components/CreateItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ import * as styles from './CreateItem.css';
interface CreateItemProps {
onBackClick: () => void;
onSubmitClick: () => void;
isSubmitting: boolean;
}

export default function CreateItem({ onBackClick, onSubmitClick }: CreateItemProps) {
export default function CreateItem({ onBackClick, onSubmitClick, isSubmitting }: CreateItemProps) {
const {
formState: { isValid },
} = useFormContext();

return (
<div>
<Header onBackClick={onBackClick} isSubmitActive={isValid} onSubmitClick={onSubmitClick} />
<Header onBackClick={onBackClick} isSubmitActive={isValid && !isSubmitting} onSubmitClick={onSubmitClick} />
<div className={styles.article}>
<h3 className={styles.label}>
아이템 추가 <span className={styles.required}>*</span>
Expand Down
8 changes: 7 additions & 1 deletion src/app/create/_components/item/ItemLayout.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@ import { style } from '@vanilla-extract/css';
export const itemHeader = style({
width: '100%',

flexGrow: 0,

display: 'flex',
alignItems: 'center',
gap: '12px',

overflow: 'hidden',
});

export const headerIcon = style({
flexShrink: '0',
});

export const rankAndTitle = style({
width: '100%',
flexGrow: 1,

display: 'flex',
gap: '8px',
Expand Down
4 changes: 2 additions & 2 deletions src/app/create/_components/item/ItemLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ export default function ItemLayout({
return (
<>
<div className={styles.itemHeader}>
<DndIcon alt="드래그앤드롭" />
<DndIcon width="18" height="18" alt="드래그앤드롭" className={styles.headerIcon} />
<div className={styles.rankAndTitle}>
<Label colorType={index === 0 ? 'blue' : 'skyblue'}>{`${index + 1}위`}</Label>
{titleInput}
</div>
{itemLength > 3 && (
<button onClick={handleDeleteItem}>
<ClearGrayIcon alt="아이템 삭제" />
<ClearGrayIcon alt="아이템 삭제" className={styles.headerIcon} />
</button>
)}
</div>
Expand Down
4 changes: 3 additions & 1 deletion src/app/create/_components/item/Items.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export const draggingItem = style([
]);

export const title = style({
flexGrow: 1,

//body1
fontSize: '1.6rem',
fontWeight: '400',
Expand All @@ -57,7 +59,7 @@ export const comment = style({
width: '100%',
resize: 'none',

flexGrow: '1',
flexGrow: 1,

//body2
fontSize: '1.5rem',
Expand Down
15 changes: 8 additions & 7 deletions src/app/create/_components/item/Items.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ export default function Items() {
{(provided) => (
<div className={styles.itemsContainer} ref={provided.innerRef} {...provided.droppableProps}>
{items.map((item, index) => {
const errorMessage = (field: 'title' | 'comment' | 'link' | 'image') =>
const errorMessage = (field: 'title' | 'comment' | 'link' | 'imageUrl') =>
(errors as FormErrors)?.items?.[index]?.[field]?.message;
const titleError = errorMessage('title');
const commentError = errorMessage('comment');
const linkError = errorMessage('link');
// const imageError = errorMessage('image');
// const imageError = errorMessage('imageUrl');
return (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided, snapshot) => (
Expand Down Expand Up @@ -138,6 +138,7 @@ export default function Items() {
<div className={styles.linkModalChildren}>
<input
className={styles.linkInput}
type="url"
placeholder={itemPlaceholder.link}
autoComplete="off"
{...register(`items.${index}.link`, itemLinkRules)}
Expand All @@ -154,7 +155,7 @@ export default function Items() {
type="file"
accept=".jpg, .jpeg, .png"
id={`${index}-image`}
{...register(`items.${index}.image`)}
{...register(`items.${index}.imageUrl`)}
/>
}
linkPreview={
Expand All @@ -170,12 +171,12 @@ export default function Items() {
)
}
imagePreview={
watchItems[index]?.image && (
watchItems[index]?.imageUrl !== '' && (
<Preview
type="image"
imageFile={watchItems[index]?.image?.[0]}
imageFile={watchItems[index]?.imageUrl?.[0]}
handleClearButtonClick={() => {
setValue(`items.${index}.image`, '');
setValue(`items.${index}.imageUrl`, '');
}}
/>
)
Expand All @@ -195,7 +196,7 @@ export default function Items() {
title: '',
comment: '',
link: '',
image: null,
imageUrl: '',
})
}
/>
Expand Down
63 changes: 47 additions & 16 deletions src/app/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@ import { useState } from 'react';
import { FieldErrors, FormProvider, useForm } from 'react-hook-form';
import { useMutation } from '@tanstack/react-query';

import { ItemImagesType, ListCreateType } from '@/lib/types/listType';
import CreateItem from '@/app/create/_components/CreateItem';
import CreateList from '@/app/create/_components/CreateList';
import { ItemImagesType, ListCreateType } from '@/lib/types/listType';
import toasting from '@/lib/utils/toasting';
import { createList } from '../_api/list/createList';
import { uploadItemImages } from '../_api/list/uploadItemImages';
import { useRouter } from 'next/navigation';

export type FormErrors = FieldErrors<ListCreateType>;

export default function CreatePage() {
const [step, setStep] = useState<'list' | 'item'>('list');
const [newListId, setNewListId] = useState(0);
const router = useRouter();

const methods = useForm<ListCreateType>({
mode: 'onChange',
Expand All @@ -32,21 +36,21 @@ export default function CreatePage() {
title: '',
comment: '',
link: '',
image: null,
imageUrl: '',
},
{
rank: 0,
title: '',
comment: '',
link: '',
image: null,
imageUrl: '',
},
{
rank: 0,
title: '',
comment: '',
link: '',
image: null,
imageUrl: '',
},
],
},
Expand All @@ -66,44 +70,70 @@ export default function CreatePage() {
});

//데이터 쪼개기
const listData = {
const listData: ListCreateType = {
...originData,
items: originData.items.map(({ image, ...rest }) => rest),
items: originData.items.map(({ imageUrl, ...rest }) => {
return {
...rest,
imageUrl: '',
};
}),
};

const imageData: ItemImagesType = {
ownerId: originData.ownerId,
listId: 0, //temp
extensionRanks: originData.items
.map(({ rank, image }) => {
return { rank: rank, extension: image?.[0]?.type.split('/')[1] as 'jpg' | 'jpeg' | 'png' };
})
.filter(({ extension }) => extension !== null && extension !== undefined),
.filter(({ imageUrl }) => imageUrl !== '')
.map(({ rank, imageUrl }) => {
return {
rank: rank,
extension: imageUrl !== '' ? (imageUrl?.[0]?.type.split('/')[1] as 'jpg' | 'jpeg' | 'png') : '',
};
}),
};

const imageFileList: File[] = originData.items
.map(({ image }) => image?.[0] as File)
.filter((image) => image !== undefined);
.filter(({ imageUrl }) => imageUrl !== '')
.map(({ imageUrl }) => imageUrl?.[0] as File);

return { listData, imageData, imageFileList };
};

const saveImageMutation = useMutation({ mutationFn: uploadItemImages });
const {
mutate: saveImageMutate,
isPending: isUploadingImage,
data: listId,
} = useMutation({
mutationFn: uploadItemImages,
retry: 3,
retryDelay: 1000,
onError: () => {
toasting({ type: 'error', txt: '이미지를 업로드 하는 중에 오류가 발생했어요. 다시 업로드해주세요.' });
},
onSettled: () => {
router.push(`/user/${formatData().listData.ownerId}/list/${newListId}`);
},
});

const createListMutation = useMutation({
const { mutate: createListMutate, isPending: isCreatingList, isSuccess } = useMutation({
mutationFn: createList,
onSuccess: (data) => {
saveImageMutation.mutate({
setNewListId(data.listId);
saveImageMutate({
listId: data.listId,
imageData: formatData().imageData,
imageFileList: formatData().imageFileList,
});
},
onError: () => {
toasting({ type: 'error', txt: '리스트 생성에 실패했어요. 다시 시도해주세요.' });
},
});

const handleSubmit = () => {
const { listData } = formatData();
createListMutation.mutate(listData);
createListMutate(listData);
};

return (
Expand All @@ -121,6 +151,7 @@ export default function CreatePage() {
handleStepChange('list');
}}
onSubmitClick={handleSubmit}
isSubmitting={isUploadingImage || isCreatingList || isSuccess}
/>
)}
</FormProvider>
Expand Down
3 changes: 3 additions & 0 deletions src/app/user/[userId]/list/[listId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function ListPage() {
return <div>리스트페이지</div>;
}
4 changes: 2 additions & 2 deletions src/lib/types/listType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export interface ItemCreateType {
title: string;
comment: string | null;
link: string | null;
image?: FileList | null;
imageUrl: FileList | '';
}

// 리스트 생성 타입
Expand All @@ -27,7 +27,7 @@ export interface ListIdType {
// 이미지 생성 타입
export interface ItemImageType {
rank: number;
extension: 'jpg' | 'jpeg' | 'png';
extension: 'jpg' | 'jpeg' | 'png' | '';
}
export interface ItemImagesType {
ownerId: number;
Expand Down
7 changes: 6 additions & 1 deletion src/lib/utils/toasting.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { toast, ToastOptions } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';

function toasting({ type = 'default', txt = '' }) {
interface ToastingProps {
type: 'default' | 'success' | 'error' | 'warning';
txt: string;
}

function toasting({ type = 'default', txt = '' }: ToastingProps) {
const toastOption: ToastOptions = {
position: 'bottom-center',
autoClose: 1000,
Expand Down
Loading

0 comments on commit 1f2ee10

Please sign in to comment.