"use client"; import useSWR from "swr"; import { useState, useRef } from "react"; import { Modal } from "@/app/components/ui/Modal"; const fetcher = (url: string) => fetch(url).then((r) => r.json()); export default function AdminPartnersPage() { const { data, mutate } = useSWR<{ partners: any[] }>("/api/admin/partners", fetcher); const { data: catData, mutate: mutateCategories } = useSWR<{ categories: any[] }>("/api/admin/partner-categories", fetcher); const partners = data?.partners ?? []; const categories = catData?.categories ?? []; const [form, setForm] = useState({ name: "", latitude: "", longitude: "", address: "", imageUrl: "", categoryId: "" }); const [uploading, setUploading] = useState(false); const fileInputRef = useRef(null); const editFileInputRef = useRef(null); const [editUploading, setEditUploading] = useState(false); const [editingId, setEditingId] = useState(null); const [editDraft, setEditDraft] = useState(null); const [showCreateModal, setShowCreateModal] = useState(false); const [showEditModal, setShowEditModal] = useState(false); async function onSelectFile(e: React.ChangeEvent) { const inputEl = e.currentTarget; const file = inputEl.files?.[0]; if (!file) return; try { setUploading(true); const fd = new FormData(); fd.append("file", file); const r = await fetch("/api/uploads", { method: "POST", body: fd }); const json = await r.json(); if (!r.ok) throw new Error(json?.error || "upload_failed"); setForm((f) => ({ ...f, imageUrl: json.url })); } catch (err) { console.error(err); alert("이미지 업로드 중 오류가 발생했습니다."); } finally { setUploading(false); if (inputEl) inputEl.value = ""; } } async function onSelectEditFile(e: React.ChangeEvent) { const inputEl = e.currentTarget; const file = inputEl.files?.[0]; if (!file) return; try { setEditUploading(true); const fd = new FormData(); fd.append("file", file); const r = await fetch("/api/uploads", { method: "POST", body: fd }); const json = await r.json(); if (!r.ok) throw new Error(json?.error || "upload_failed"); setEditDraft((d: any) => ({ ...(d || {}), imageUrl: json.url })); } catch (err) { console.error(err); alert("이미지 업로드 중 오류가 발생했습니다."); } finally { setEditUploading(false); if (inputEl) inputEl.value = ""; } } async function create() { // 필수값 검증: 이름/카테고리/위도/경도 if (!form.name || !form.categoryId || !String(form.latitude).trim() || !String(form.longitude).trim()) { alert("이름, 카테고리, 위도, 경도를 모두 선택/입력해 주세요."); return; } const lat = Number(form.latitude); const lon = Number(form.longitude); if (!isFinite(lat) || !isFinite(lon)) { alert("위도/경도는 숫자여야 합니다."); return; } const payload = { ...form, latitude: lat, longitude: lon, categoryId: form.categoryId } as any; const r = await fetch("/api/admin/partners", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(payload) }); if (r.ok) { setForm({ name: "", latitude: "", longitude: "", address: "", imageUrl: "", categoryId: "" }); mutate(); setShowCreateModal(false); } else { let msg = "저장에 실패했습니다."; try { const j = await r.json(); msg = j?.message || j?.error || msg; if (r.status === 409 && j?.error === "duplicate_name") msg = "이미 존재하는 업체명입니다."; if (r.status === 400) msg = msg || "입력값을 확인해 주세요."; } catch {} alert(msg); } } return (

제휴업체 관리

setShowCreateModal(false)}>

제휴업체 추가

{(() => { const inputStyle: React.CSSProperties = { border: "1px solid #ddd", borderRadius: 6, padding: "8px 10px", width: "100%" }; return (
setForm({ ...form, name: e.target.value })} />
setForm({ ...form, latitude: e.target.value })} />
setForm({ ...form, longitude: e.target.value })} />
setForm({ ...form, imageUrl: e.target.value })} /> {uploading && 업로드 중...}
setForm({ ...form, address: e.target.value })} />
{form.imageUrl && (
미리보기
미리보기
)}
); })()}
{ setShowEditModal(false); setEditingId(null); setEditDraft(null); }}>

제휴업체 수정

{(() => { const inputStyle: React.CSSProperties = { border: "1px solid #ddd", borderRadius: 6, padding: "8px 10px", width: "100%" }; return (
setEditDraft({ ...editDraft, name: e.target.value })} />
setEditDraft({ ...editDraft, latitude: e.target.value })} />
setEditDraft({ ...editDraft, longitude: e.target.value })} />
setEditDraft({ ...editDraft, imageUrl: e.target.value })} /> {editUploading && 업로드 중...}
setEditDraft({ ...editDraft, address: e.target.value })} />
{editDraft?.imageUrl && (
미리보기
미리보기
)}
); })()}
    {partners.map((p) => (
  • {p.imageUrl ? ( {p.name} ) : (
    )}
    {false ? ( <> ) : ( <>
    {p.name} {p.categoryRef ? [{p.categoryRef.name}] : null}
    {p.address || "(주소 없음)"}
    위도 {p.latitude}
    경도 {p.longitude}
    )}
  • ))}

카테고리 관리

); } function CategoryManager({ categories, onChanged }: { categories: any[]; onChanged: () => void }) { const [name, setName] = useState(""); return (
setName(e.target.value)} className="h-9 px-3 rounded-md border border-neutral-300 flex-1" />
    {categories.map((c: any) => (
  • {c.name}
  • ))}
); }