// === 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 도매 사이트에서 상품을 검색하고 네이버 스마트스토어에 등록합니다
{JSON.stringify(naverPreview, null, 2)}
{creds.proxy_url || "미설정"})를 통해 호출되어야 합니다.
액세스 토큰은 서버에서 bcrypt 서명으로 발급받습니다.