Skip to content

Commit c02f7c9

Browse files
committed
feat: 이미지 업로드/삭제 기능 구현 (#258)
1 parent c230c37 commit c02f7c9

File tree

5 files changed

+113
-36
lines changed

5 files changed

+113
-36
lines changed

services/one-app/src/app/(site)/lost-found/[lostId]/edit/page.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
const LostFoundEditPage = () => {
1+
'use client';
2+
3+
import { useGetLostFoundDetail } from '../../_lib/get';
4+
5+
type Props = {
6+
params: {
7+
lostId: string;
8+
};
9+
};
10+
11+
const LostFoundEditPage = ({ params: { lostId } }: Props) => {
12+
const { data } = useGetLostFoundDetail(lostId);
13+
214
return <h1>수정 페이지</h1>;
315
};
416

services/one-app/src/app/(site)/lost-found/_components/LostFoundForm.tsx

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,79 @@
11
'use client';
22

3+
import { useState } from 'react';
34
import ArrowLeftIcon from '@/common/assets/icons/arrow-left';
5+
import CloseCircleIcon from '@/common/assets/icons/close-circle';
46
import ImagePlaceHolder from '@/common/assets/icons/image-placeholder';
57
import SelectLineDrawer from './SelectLineDrawer';
68

7-
import type { LostFound } from '@/model/LostFound';
8-
99
type Props = {
10-
lostFoundData: LostFound | null;
10+
lostFoundData?: any | null;
1111
};
1212

13-
const LostFoundForm = ({ lostFoundData }: Props) => {
13+
const MAX_IMAGE_LENGTH = 5;
14+
15+
const LostFoundForm = ({ lostFoundData = null }: Props) => {
16+
const [images, setImages] = useState<string[]>([]);
17+
18+
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
19+
if (images.length >= MAX_IMAGE_LENGTH) return;
20+
const fileBlob = e.target.files?.[0];
21+
if (!fileBlob) return;
22+
const fileUrl = URL.createObjectURL(fileBlob);
23+
setImages((prev) => [...prev, fileUrl]);
24+
e.target.value = '';
25+
};
26+
27+
const onDeleteImage = (index: number) => {
28+
setImages((prev) => prev.filter((_, i) => i !== index));
29+
};
30+
1431
return (
1532
<>
16-
<div className="flex justify-between h-[50px] px-[20px] py-[12px]">
33+
<div className="flex justify-between px-[20px] py-[12px]">
1734
<ArrowLeftIcon />
1835
<button className="px-[13px] py-[6px] bg-[#2ACF6C] text-[#FBFBFB] text-xs rounded-[3px] font-medium">
1936
등록
2037
</button>
2138
</div>
22-
<form className="px-5 max-w-[520px]">
39+
<form className="px-5 max-w-[520px] overflow-hidden">
2340
<div className="mb-8">
2441
<p className="font-medium mb-3">유실물 상세정보</p>
25-
<label
26-
htmlFor="image-upload"
27-
className="flex justify-center items-center size-16 border rounded-[10px] border-[#CED0DD] cursor-pointer"
42+
<div
43+
className={`flex pt-1.5 overflow-x-auto overflow-scroll [&::-webkit-scrollbar]:hidden`}
2844
>
29-
<ImagePlaceHolder />
30-
<input
31-
id="image-upload"
32-
type="file"
33-
className="hidden"
34-
multiple
35-
></input>
36-
</label>
45+
<label
46+
htmlFor="image-upload"
47+
className="flex justify-center items-center size-16 min-w-16 border rounded-[10px] border-[#CED0DD] cursor-pointer"
48+
>
49+
<ImagePlaceHolder />
50+
<input
51+
id="image-upload"
52+
type="file"
53+
className="hidden"
54+
multiple
55+
onChange={handleFileChange}
56+
/>
57+
</label>
58+
{images.map((curSrc, index) => {
59+
return (
60+
<div className="relative min-w-16">
61+
<img
62+
className="border size-16 rounded-[10px] border-[#CED0DD] ml-2"
63+
src={curSrc}
64+
key={`${curSrc}-${index}`}
65+
/>
66+
<button
67+
type="button"
68+
className="absolute -top-1.5 -right-1.5 z-10"
69+
onClick={() => onDeleteImage(index)}
70+
>
71+
<CloseCircleIcon />
72+
</button>
73+
</div>
74+
);
75+
})}
76+
</div>
3777
</div>
3878

3979
<div className="mb-8">
@@ -94,9 +134,9 @@ const LostFoundForm = ({ lostFoundData }: Props) => {
94134
<p className="mr-1">자세한 설명</p>
95135
<span className="inline-block w-[5px] h-[5px] bg-red-500 rounded-full" />
96136
</div>
97-
<div className="w-full border rounded-[5px] p-3">
137+
<textarea className="w-full border rounded-[5px] p-3">
98138
에디터 영역 (lexical로 전환 필요)
99-
</div>
139+
</textarea>
100140
</div>
101141
</form>
102142
</>
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
import LostFoundForm from '../_components/LostFoundForm';
2+
13
const LostFoundCreatePage = () => {
2-
return <h1>유실물 등록 페이지</h1>;
4+
return (
5+
<main>
6+
<LostFoundForm />
7+
</main>
8+
);
39
};
410

511
export default LostFoundCreatePage;

services/one-app/src/common/assets/icons/close-circle.tsx

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,34 @@ import * as React from 'react';
33
function CloseCircleIcon() {
44
return (
55
<svg
6-
width="16"
7-
height="16"
8-
viewBox="0 0 16 16"
6+
width="24"
7+
height="24"
8+
viewBox="0 0 24 24"
99
fill="none"
1010
xmlns="http://www.w3.org/2000/svg"
1111
>
12-
<circle cx="8" cy="8" r="7" fill="#DFDFDF" />
13-
<path d="M5.5 5.5L10.5 10.5" stroke="white" strokeLinecap="round" />
14-
<path d="M5.5 10.5L10.5 5.5" stroke="white" strokeLinecap="round" />
12+
<g clip-path="url(#clip0_6089_5611)">
13+
<rect width="24" height="24" rx="12" fill="#33333E" />
14+
<path
15+
d="M16 16L8 8"
16+
stroke="white"
17+
stroke-width="2"
18+
stroke-linecap="round"
19+
stroke-linejoin="round"
20+
/>
21+
<path
22+
d="M16 8L8 16"
23+
stroke="white"
24+
stroke-width="2"
25+
stroke-linecap="round"
26+
stroke-linejoin="round"
27+
/>
28+
</g>
29+
<defs>
30+
<clipPath id="clip0_6089_5611">
31+
<rect width="24" height="24" rx="12" fill="white" />
32+
</clipPath>
33+
</defs>
1534
</svg>
1635
);
1736
}

services/one-app/src/common/assets/icons/image-placeholder.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,23 @@ function ImagePlaceHolder() {
1212
<path
1313
d="M20 4H4C3.44772 4 3 4.44772 3 5V19C3 19.5523 3.44772 20 4 20H20C20.5523 20 21 19.5523 21 19V5C21 4.44772 20.5523 4 20 4Z"
1414
stroke="#CED0DD"
15-
stroke-width="2"
16-
stroke-linecap="round"
17-
stroke-linejoin="round"
15+
strokeWidth="2"
16+
strokeLinecap="round"
17+
strokeLinejoin="round"
1818
/>
1919
<path
2020
d="M3 17L8.22999 10.8983C8.63274 10.4285 9.36128 10.4335 9.75746 10.9089L13.299 15.1588C13.6754 15.6105 14.3585 15.6415 14.7743 15.2257L16.21 13.79C16.6314 13.3686 17.3256 13.407 17.698 13.8724L21 18"
2121
stroke="#CED0DD"
22-
stroke-width="2"
23-
stroke-linecap="round"
24-
stroke-linejoin="round"
22+
strokeWidth="2"
23+
strokeLinecap="round"
24+
strokeLinejoin="round"
2525
/>
2626
<path
2727
d="M14 9C14 8.44772 14.4477 8 15 8C15.5523 8 16 8.44772 16 9C16 9.55228 15.5523 10 15 10C14.4477 10 14 9.55228 14 9Z"
2828
stroke="#CED0DD"
29-
stroke-width="2"
30-
stroke-linecap="round"
31-
stroke-linejoin="round"
29+
strokeWidth="2"
30+
strokeLinecap="round"
31+
strokeLinejoin="round"
3232
/>
3333
</svg>
3434
);

0 commit comments

Comments
 (0)