// === ISVM 검색 → 네이버 등록 (real API) === const { useState: useStateIS, useMemo: useMemoIS, useEffect: useEffectIS } = React; function ISVMScreen({ products, setProducts, goto }) { const [keyword, setKeyword] = useStateIS(""); const [sort, setSort] = useStateIS("sales"); const [limit, setLimit] = useStateIS(20); const [results, setResults] = useStateIS([]); const [loading, setLoading] = useStateIS(false); const [error, setError] = useStateIS(""); const [health, setHealth] = useStateIS(null); const [selected, setSelected] = useStateIS(new Set()); const [registerForm, setRegisterForm] = useStateIS(null); const [naverPreview, setNaverPreview] = useStateIS(null); const [registered, setRegistered] = useStateIS(new Set()); const [creds, setCreds] = useStateIS(window.API.loadCreds()); useEffectIS(() => { window.API.isvmHealth().then(setHealth); }, []); const doSearch = async () => { if (!keyword.trim()) { window.toast("검색어를 입력하세요"); return; } setLoading(true); setError(""); setResults([]); setSelected(new Set()); try { const data = await window.API.isvmSearch({ keyword: keyword.trim(), limit: parseInt(limit) || 20, sort }); setResults(data); window.toast(`${data.length}개 상품 검색 완료`); } catch (e) { const isCors = String(e.message || e).includes("Failed to fetch") || String(e.message || e).includes("NetworkError"); setError(isCors ? "ISVM 서버에 직접 연결할 수 없습니다 (CORS). 데모용 샘플 데이터를 표시합니다." : `검색 실패: ${e.message}`); // Fallback demo data so UI is testable from any origin setResults(generateDemoResults(keyword.trim())); } finally { setLoading(false); } }; const toggleOne = (code) => { const s = new Set(selected); s.has(code) ? s.delete(code) : s.add(code); setSelected(s); }; const openRegister = (item) => { setRegisterForm({ product_code: item.product_code, name: item.name, brand: item.brand || "", purchase_price: item.purchase_price, // 권장 마진 60% sale_price: Math.round((item.purchase_price * 1.6) / 10) * 10, image_url: item.image_url, stock: 50, desc: `${item.name}\n\n공급사: ${item.brand || "-"} · ISVM 코드: ${item.product_code}`, leafCategoryId: "50000000", baseFee: 3000, }); }; const previewNaverPayload = () => { const p = window.API.buildNaverProductPayload({ product_code: registerForm.product_code, name: registerForm.name, brand: registerForm.brand, desc: registerForm.desc, image_url: registerForm.image_url, salePrice: parseInt(registerForm.sale_price) || 0, stock: parseInt(registerForm.stock) || 0, }, { leafCategoryId: registerForm.leafCategoryId, baseFee: parseInt(registerForm.baseFee) || 3000, }); setNaverPreview(p); }; const submitRegister = async () => { // For real submission you must POST through your own backend proxy. // Here we simulate success and add to local product list. const newId = "P" + (10300 + products.length); const newProduct = { id: newId, name: registerForm.name, category: "fashion", // user can re-categorize after; or auto-map by leafCategoryId price: parseInt(registerForm.sale_price) || 0, costPrice: parseInt(registerForm.purchase_price) || 0, stock: parseInt(registerForm.stock) || 0, sales30d: 0, rating: 0, reviews: 0, status: "active", views30d: 0, sku: registerForm.product_code, brand: registerForm.brand || "ISVM", tone: parseInt(registerForm.product_code, 10) % 9 || 0, desc: registerForm.desc, image_url: registerForm.image_url, }; setProducts(prev => [...prev, newProduct]); setRegistered(prev => new Set(prev).add(registerForm.product_code)); setRegisterForm(null); setNaverPreview(null); window.toast(`'${newProduct.name.slice(0, 24)}…' 등록 (시뮬레이션)`); }; const corsWarning = error && error.includes("CORS"); return (

ISVM → 네이버 스마트스토어 연동

ISVM 도매 사이트에서 상품을 검색하고 네이버 스마트스토어에 등록합니다

{/* Connection bar */}
IS
ISVM Crawler API
{window.API.ISVM_BASE}
{health === true ? "연결됨" : health === false ? "연결 실패" : "확인 중..."}
N
네이버 커머스 API
{creds.naver_client_id ? `client_id: ${creds.naver_client_id.slice(0, 6)}••••••` : "자격증명 미설정"}
{creds.naver_client_id && creds.proxy_url ? "프록시 연결" : "프록시 필요"}
{/* Search bar */}
setKeyword(e.target.value)} onKeyDown={e => e.key === "Enter" && doSearch()} />
{error && (
{corsWarning ? "⚠️ CORS 차단" : "❌ 오류"}: {error} {corsWarning && (
ISVM 서버에 이 도메인을 화이트리스트에 등록하거나, 자체 백엔드를 통해 프록시하세요.
)}
)} {results.length === 0 && !loading && !error && (
키워드를 입력하고 검색하세요
ISVM 도매 사이트의 실시간 매입가/이미지가 조회됩니다
)} {results.length > 0 && ( <>
검색 결과 {results.length}개 · 선택: {selected.size} {selected.size > 0 && ( )}
{results.map(item => { const isReg = registered.has(item.product_code) || products.some(p => p.sku === item.product_code); const margin = ((item.purchase_price * 1.6 - item.purchase_price) / (item.purchase_price * 1.6) * 100).toFixed(0); return (
{item.image_url ? { e.target.style.display = "none"; }} /> : } {item.product_code} {isReg && ( 등록됨 )}
{item.name}
{item.brand || "-"}
매입가
{formatKRW(item.purchase_price)}
권장 판매가 (마진 {margin}%)
{formatKRW(Math.round(item.purchase_price * 1.6 / 10) * 10)}
); })}
)} {/* Register modal */} {registerForm && ( { setRegisterForm(null); setNaverPreview(null); }} size="lg" title="네이버 스마트스토어 상품 등록" footer={<>
}>
{registerForm.image_url ? : }
ISVM 출처
{registerForm.product_code}
매입가 {formatKRW(registerForm.purchase_price)}
setRegisterForm({ ...registerForm, name: e.target.value })} /> setRegisterForm({ ...registerForm, brand: e.target.value })} />
setRegisterForm({ ...registerForm, sale_price: parseInt(e.target.value.replace(/[^0-9]/g, "")) || 0 })} /> setRegisterForm({ ...registerForm, stock: parseInt(e.target.value) || 0 })} />
setRegisterForm({ ...registerForm, leafCategoryId: e.target.value })} /> setRegisterForm({ ...registerForm, baseFee: parseInt(e.target.value) || 0 })} />