fix: Remove car_id property from adminAddVehicle call to fix TypeScript error
This commit is contained in:
207
temp_products_edit_page.tsx
Normal file
207
temp_products_edit_page.tsx
Normal file
@@ -0,0 +1,207 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useRouter, useParams } from "next/navigation";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
import { productsApi, ProductAdmin, getUploadUrl } from "@/lib/api";
|
||||
import ProductForm from "@/components/admin/ProductForm";
|
||||
import { Loader2, Upload, Trash2 } from "lucide-react";
|
||||
import YouTubeVideoManager from "@/components/admin/YouTubeVideoManager";
|
||||
|
||||
export default function EditProductPage() {
|
||||
const { token } = useAuth();
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const productId = Number(params.id);
|
||||
|
||||
const [product, setProduct] = useState<ProductAdmin | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
|
||||
const fetchProduct = async () => {
|
||||
if (!token) return;
|
||||
try {
|
||||
const data = await productsApi.adminGetOne(productId, token);
|
||||
setProduct(data);
|
||||
} catch (err) {
|
||||
alert("제품을 찾을 수 없습니다.");
|
||||
router.push("/admin/products");
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchProduct();
|
||||
}, [token, productId]);
|
||||
|
||||
const handleSubmit = async (data: any) => {
|
||||
if (!token) return;
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
await productsApi.adminUpdate(productId, data, token);
|
||||
alert("저장되었습니다.");
|
||||
fetchProduct();
|
||||
} catch (err) {
|
||||
alert("저장에 실패했습니다.");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleImageUpload = async (
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
isMain: boolean
|
||||
) => {
|
||||
if (!token || !e.target.files?.[0]) return;
|
||||
|
||||
setIsUploading(true);
|
||||
try {
|
||||
await productsApi.adminUploadImage(
|
||||
productId,
|
||||
e.target.files[0],
|
||||
isMain,
|
||||
token
|
||||
);
|
||||
fetchProduct();
|
||||
} catch (err) {
|
||||
alert("이미지 업로드에 실패했습니다.");
|
||||
} finally {
|
||||
setIsUploading(false);
|
||||
e.target.value = "";
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteImage = async (imageId: number) => {
|
||||
if (!token || !confirm("이미지를 삭제하시겠습니까?")) return;
|
||||
|
||||
try {
|
||||
await productsApi.adminDeleteImage(imageId, token);
|
||||
fetchProduct();
|
||||
} catch (err) {
|
||||
alert("이미지 삭제에 실패했습니다.");
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<Loader2 className="w-8 h-8 animate-spin text-[#3B82F6]" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!product) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-8">제품 수정</h1>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
{/* Form */}
|
||||
<div className="lg:col-span-2">
|
||||
<ProductForm
|
||||
initialData={product}
|
||||
onSubmit={handleSubmit}
|
||||
isSubmitting={isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Image Upload */}
|
||||
<div className="space-y-6">
|
||||
{/* Main Image */}
|
||||
<div className="bg-white rounded-xl p-6 shadow-sm">
|
||||
<h3 className="font-semibold text-gray-900 mb-4">대표 이미지</h3>
|
||||
{product.main_image ? (
|
||||
<div className="relative">
|
||||
<img
|
||||
src={getUploadUrl(product.main_image)}
|
||||
alt="Main"
|
||||
className="w-full h-48 object-cover rounded-lg"
|
||||
/>
|
||||
<label className="absolute bottom-2 right-2 px-3 py-1.5 bg-white/90 text-sm font-medium rounded-lg cursor-pointer hover:bg-white transition-colors">
|
||||
변경
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className="hidden"
|
||||
onChange={(e) => handleImageUpload(e, true)}
|
||||
disabled={isUploading}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
) : (
|
||||
<label className="flex flex-col items-center justify-center h-48 border-2 border-dashed border-gray-200 rounded-lg cursor-pointer hover:border-[#3B82F6] transition-colors">
|
||||
{isUploading ? (
|
||||
<Loader2 className="w-8 h-8 animate-spin text-[#3B82F6]" />
|
||||
) : (
|
||||
<>
|
||||
<Upload className="w-8 h-8 text-gray-400 mb-2" />
|
||||
<span className="text-sm text-gray-500">
|
||||
클릭하여 업로드
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className="hidden"
|
||||
onChange={(e) => handleImageUpload(e, true)}
|
||||
disabled={isUploading}
|
||||
/>
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Additional Images */}
|
||||
<div className="bg-white rounded-xl p-6 shadow-sm">
|
||||
<h3 className="font-semibold text-gray-900 mb-4">추가 이미지</h3>
|
||||
<div className="grid grid-cols-2 gap-3 mb-4">
|
||||
{product.images?.map((img) => (
|
||||
<div key={img.id} className="relative group">
|
||||
<img
|
||||
src={getUploadUrl(img.image_url)}
|
||||
alt=""
|
||||
className="w-full h-24 object-cover rounded-lg"
|
||||
/>
|
||||
<button
|
||||
onClick={() => handleDeleteImage(img.id)}
|
||||
className="absolute top-1 right-1 p-1 bg-red-500 text-white rounded opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<label className="flex items-center justify-center py-3 border-2 border-dashed border-gray-200 rounded-lg cursor-pointer hover:border-[#3B82F6] transition-colors">
|
||||
{isUploading ? (
|
||||
<Loader2 className="w-5 h-5 animate-spin text-[#3B82F6]" />
|
||||
) : (
|
||||
<>
|
||||
<Upload className="w-5 h-5 text-gray-400 mr-2" />
|
||||
<span className="text-sm text-gray-500">이미지 추가</span>
|
||||
</>
|
||||
)}
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className="hidden"
|
||||
onChange={(e) => handleImageUpload(e, false)}
|
||||
disabled={isUploading}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* YouTube Videos */}
|
||||
<YouTubeVideoManager
|
||||
entityType="product"
|
||||
entityId={productId}
|
||||
entityTitle={product.name_ko}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user