diff --git a/src/assets/icons/camera-gray.svg b/src/assets/icons/camera-gray.svg
new file mode 100644
index 00000000..f222baaa
--- /dev/null
+++ b/src/assets/icons/camera-gray.svg
@@ -0,0 +1,12 @@
+
diff --git a/src/assets/icons/camera-white.svg b/src/assets/icons/camera-white.svg
new file mode 100644
index 00000000..22a0bdc0
--- /dev/null
+++ b/src/assets/icons/camera-white.svg
@@ -0,0 +1,12 @@
+
diff --git a/src/components/common/ImageInput.tsx b/src/components/common/ImageInput.tsx
new file mode 100644
index 00000000..0d0326b3
--- /dev/null
+++ b/src/components/common/ImageInput.tsx
@@ -0,0 +1,84 @@
+import { useEffect, useState } from 'react';
+import CameraGray from '@/assets/icons/camera-gray.svg';
+import CameraWhite from '@/assets/icons/camera-white.svg';
+
+interface ImageInputProps {
+ label: string;
+ imageUrl: string;
+ onImageChange: (file: File, previewUrl: string) => void;
+ mode?: 'create' | 'edit';
+}
+
+export default function ImageInput({
+ label,
+ imageUrl,
+ onImageChange,
+ mode = 'create',
+}: ImageInputProps) {
+ const [preview, setPreview] = useState(imageUrl);
+
+ useEffect(() => {
+ setPreview(imageUrl);
+ }, [imageUrl]);
+
+ const handleChangeFile = (e: React.ChangeEvent) => {
+ const file = e.target.files?.[0];
+ if (!file) return;
+
+ const previewUrl = URL.createObjectURL(file);
+ setPreview(previewUrl);
+ onImageChange(file, previewUrl);
+ };
+
+ useEffect(() => {
+ return () => {
+ if (preview?.startsWith('blob:')) {
+ URL.revokeObjectURL(preview);
+ }
+ };
+ }, [preview]);
+
+ return (
+
+
+
+
+
+
+
+ );
+}