Skip to content

Commit

Permalink
Merge pull request #89 from EmploymentRescueTeam/feature/#73
Browse files Browse the repository at this point in the history
게시물수정
  • Loading branch information
LeHiHo authored Oct 24, 2023
2 parents e8f9ce9 + 3b69103 commit 631a2d0
Show file tree
Hide file tree
Showing 6 changed files with 334 additions and 8 deletions.
2 changes: 0 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 37 additions & 1 deletion src/api/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const postProducts = async (
formData.append('title', title);
formData.append('categoryName', categoryName);
formData.append('content', content);
formData.append('price', price.toString());
formData.append('price', price);

// 여러 이미지를 처리하는 경우
if (images) {
Expand All @@ -51,11 +51,47 @@ export const deleteProducts = async (id: number) => {
return res;
};

// 상품수정
export const putProducts = async (
id: number,
title: string,
categoryName: string,
content: string,
price: string,
images?: FileList | string[] | null,
) => {
const formData = new FormData();
formData.append('title', title);
formData.append('categoryName', categoryName);
formData.append('content', content);
formData.append('price', price);

// 여러 이미지를 처리하는 경우
if (images) {
for (let i = 0; i < images.length; i++) {
formData.append('images', images[i]);
}
}

const config = {
headers: {
'Content-Type': 'multipart/form-data',
},
params: {
productId: id,
},
};

const res = await client.put(`products/${id}`, formData, config);
return res;
};

export const getProductCategory = async () => {
const res = await client.get(`/products/categories`);
return res.data;
};

// 상품정보 불러오기
export const getProducts = async (searchWord?: string, category?: string) => {
const res = await client.get('/products?pageSize=100', {
params: {
Expand Down
9 changes: 9 additions & 0 deletions src/app/product/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import ProductPut from '@/templates/product/productPut';

export default function Page() {
return (
<>
<ProductPut />
</>
);
}
4 changes: 3 additions & 1 deletion src/templates/product/productDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import 'slick-carousel/slick/slick-theme.css';
import 'slick-carousel/slick/slick.css';

import ProductDelete from './productDelete';
import ProductPut from './productPut';

type Seller = {
sellerId: number;
Expand Down Expand Up @@ -173,9 +174,10 @@ export const ProductDetail = () => {
role="button"
ref={menuRef}
className="product-detail__menu">
<div onClick={() => router.push('/product/edit')}>
<div onClick={() => router.push(`/product/${id}/edit`)}>
게시글 수정
</div>

<div onClick={toggleModal}>삭제</div>
</div>
)}
Expand Down
236 changes: 236 additions & 0 deletions src/templates/product/productPut.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
'use client';

import '@/styles/templates/write/write.scss';
import Header from '@/components/header';
import { useState, useEffect } from 'react';
import { putProducts, getProductDetail } from '@/api/service';
import CategoryModal from '@/templates/write/categoryModal';
import { useHandleImg } from '@/templates/write/useHandleImg';
import Btn from '@/components/btn';
import { useRouter, usePathname } from 'next/navigation';
import { AXIOSResponse } from '@/types/interface';

export default function ProductPut() {
const [title, setTitle] = useState<string>('');
const [category, setCategory] = useState<string>('');
const [content, setContent] = useState<string>('');
const [price, setPrice] = useState<string>('');
const [isModal, setIsModal] = useState<boolean>(false);
const [imgFiles, setImgFiles] = useState<File[] | string[]>([]);

type Product = {
id: number;
title: string;
price: string;
categoryName: string;
content: string;
images: string[];
status: string;
likes: number;
myProduct: boolean;
seller: Seller;
sellerProductInfos: sellerProductInfos[];
like: boolean;
};

const router = useRouter();
const id = parseInt(usePathname().split('/')[2]);

// const urlToFile = async (
// url: string,
// filename: string,
// mimeType: string,
// ): Promise<File> => {
// const res = await fetch(url, { mode: 'no-cors' });
// const blob = await res.blob();
// return new File([blob], filename, { type: mimeType });
// };

useEffect(() => {
const fetchData = async () => {
try {
const res: AXIOSResponse<Product> = await getProductDetail(id);
if (res.statusCode === 200) {
const {
title = '',
categoryName = '',
content = '',
price = '',
} = res.data;

setTitle(title);
setCategory(categoryName);
setContent(content);
setPrice(price);
const imageUrls = res.data.images; // 먼저 images를 가져온다
setImgFiles(imageUrls);
// if (imageUrls) {
// const files = await Promise.all(
// imageUrls.map((url) => urlToFile(url, 'filename', 'image/*')), // filename과 mimeType은 적절하게 설정
// );
// setImgFiles(files);
// }
}
} catch (error: any) {
if (error.res) {
const errorData = error.res?.data;
console.error('get failed:', errorData);
} else {
console.error('An unexpected error occurred:', error);
}
}
};
fetchData();
}, [id]);

const generateUniqueId = (image: File, index: number): string => {
return `${image.lastModified}-${image.name}-${index}`;
};

const toggleModal = () => {
setIsModal(!isModal);
};

const handleSelectCategory = (selectedCategory: string) => {
setCategory(selectedCategory);
};

// console.log('imgFiles', imgFiles);
const { imageArray, images, removeImage, handleImageChange } =
useHandleImg(imgFiles);

const handleEdit = async () => {
try {
if (images) {
const res = await putProducts(
id,
title,
category,
content,
price,
images,
);
if (res.data.statusCode === 200) {
router.push('/main');
console.log('수정완료');
} else {
console.error('Post failed:', res);
}
}
} catch (error: any) {
if (error.response) {
const errorData = error.response?.data;
console.error('Post failed:', errorData);
} else {
console.error('An unexpected error occurred:', error);
}
}
};

const imgCount = imageArray?.length;

return (
<>
<Header
goBack={true}
title={'중고거래 글쓰기'}
button={<div className="writePage__temporaryStorage">임시저장</div>}
/>
<div className="writePage">
<form className="writePage__input">
<div className="writePage__input-container">
<div className="previewImg">
<label htmlFor="fileInput" className="writePage__input-UploadBox">
<img src="/svg/camera.svg" alt="camera" />
<div>
<span className="imgCount">{imgCount}</span>/10
</div>
<input
className="writePage__input-fileUpload"
accept="image/*"
id="fileInput"
type="file"
name="product_img"
onChange={handleImageChange}
multiple
/>
</label>
{imageArray.map((image: any, index: number) => (
<div
className="previewImg-item"
key={generateUniqueId(image, index)}>
<img
src={
image instanceof Blob || image instanceof File
? URL.createObjectURL(image)
: image
}
alt={`Uploaded ${index}`}
/>
<div className="x_btn" onClick={() => removeImage(index)}>
<img src="/svg/x_btn.svg" alt="x_btn" />
</div>
</div>
))}
</div>
</div>

<p>제목</p>
<input
type="text"
name="product_title"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="제목 입력해주세요"
/>
<div>
<p>카테고리</p>
<input
type="text"
readOnly
name="product_category"
value={category}
onClick={toggleModal}
placeholder="카테고리를 선택해주세요"
/>
<CategoryModal
isModal={isModal}
onClose={toggleModal}
selectCategory={handleSelectCategory}
/>
</div>
<p>가격</p>
<input
type="text" // 'number' 대신 'text'를 사용
value={price}
onChange={(e) => {
const newValue = e.target.value;
// 숫자 또는 빈 문자열만 허용
const isNumeric = /^[0-9]*$/.test(newValue);
if (isNumeric) {
setPrice(newValue);
}
}}
placeholder="가격을 입력해주세요"
/>
<p>자세한 설명</p>
<textarea
name="product_content"
value={content}
onChange={(e) => setContent(e.target.value)}
className="writePage__input-content"
placeholder="게시글 내용을 작성해주세요. 품목 및 판매금지품목은 게시가
제한됩니다."></textarea>
</form>
<footer>
<Btn
type="button"
disabled={!title || !category || !content || !price || !images}
onClick={handleEdit}
label="작성완료"
/>
</footer>
</div>
</>
);
}
Loading

0 comments on commit 631a2d0

Please sign in to comment.