⚡ InstaList
Snap · Price · List

Free tier includes 10 listings/month. No credit card required.

Reset your password

Enter your email and we'll send a reset link.

Set new password

Choose a strong password for your account.

InstaList
👋 Welcome to InstaList!
Snap a photo of anything you want to sell — AI identifies it, pulls comps, and lists it on eBay in seconds.
● Free
🛒 Not connected
7
days
Free Trial
⚡ Upgrade Your Plan
Unlock more listings and features
Pro
$14.99/mo
300 listings/mo
Business
$39.99/mo
1,000 listings/mo
Manage subscription →
🎁
Refer a friend — get 10 free AI Identifies
They get 5 free too.
Loading your link...
🔧 Admin
Loading...
📝 Saved Drafts
No drafts — items you identify but don't list are saved here.
Price Watch
AI
No items on watch yet.

⚡ InstaList

Snap It · Price It · List It Instantly

← Dashboard
What are you selling?
Trading Cards Sports, Pokemon, MTG
Memorabilia Jerseys, signed items
Comics Comics, manga, CGC
Sneakers Shoes, Jordans, Yeezy
Electronics Phones, consoles, PCs
Toys Hot Wheels, Funko, LEGO
Coins Coins, currency, bullion
Jewelry & Watches Rolex, rings, gold
Video Games Games, consoles, WATA
Automobiles Cars, trucks, parts
← Back
Change category
Front
📷
Front
Required for AI
✓
Back
🔄
Back
Recommended for AI
✓
🏷️
TAG Graded Slab?
Point your camera at the cert number on the label — we'll pull TAG's pro photos automatically
← Exit Bulk
Item 1
Done →
Item photo
📷
Tap to photograph item
Up to 12 photos per item
Photos — 1/12
In Queue
← Add More
Review Queue

Identifying item...

← Back

Item Details

📸 Listing Photos

💰 Comp Prices

Fetching comps...

Your Price

$

📋 Condition

Graded cards auto-set to "Graded" on eBay. Override here if needed.

📦 Shipping

List On

✅

Listed!

+parseFloat(d.baseline_price).toFixed(2):'⏳ Pending'; var current=d.last_checked_price?'$'+parseFloat(d.last_checked_price).toFixed(2):'Pending'; var changeBadge=''; if(d.baseline_price&&d.last_checked_price){ var p=((d.last_checked_price-d.baseline_price)/d.baseline_price*100).toFixed(1); var isUp=p>0; var color=isUp?'#4ade80':'#f87171'; var bg=isUp?'rgba(74,222,128,.1)':'rgba(248,113,113,.1)'; var arrow=isUp?'↑':'↓'; changeBadge='
'+arrow+(isUp?'+':'')+p+'%
'; } var lastCheck=d.last_checked_at?new Date(d.last_checked_at).toLocaleDateString('en-US',{month:'short',day:'numeric'}):'Never'; var isPending=d.watch_status==='pending_confirm'; var statusBadge=isPending ?'
' +'📬 Action needed — tap to list or skip' +'
' :''; var thumb=(d.photo_urls&&d.photo_urls[0]) ?'' :'
🤖
'; return '
' +statusBadge +'
' +thumb +'
' +'
'+(d.title||'Untitled')+'
' +'
' +'
'+current+'
' +changeBadge +'
' +'
' +'
›
' +'
' +'
' +'
Baseline
'+baseline+'
' +'
Threshold
'+(d.rise_threshold_pct?'+'+d.rise_threshold_pct+'%':'—')+'
' +'
Checked
'+lastCheck+'
' +'
' +'
'; }).join(''); document.getElementById('watchlistItems').innerHTML=searchHtml+rows; }catch(e){el.style.display='none';} } // ── Watch Detail Sheet ──────────────────────────────────────────────────────── function showWatchDetailSheet(id) { var d = window._watchCache && window._watchCache[id]; if (!d) return; var existing = document.getElementById('watchDetailSheet'); if (existing) existing.remove(); var isPending = d.watch_status === 'pending_confirm'; var baseline = d.baseline_price ? '$' + parseFloat(d.baseline_price).toFixed(2) : '—'; var current = d.last_checked_price ? '$' + parseFloat(d.last_checked_price).toFixed(2) : 'Pending'; var listPrice = d.list_price ? '$' + parseFloat(d.list_price).toFixed(2) : current; var lastCheck = d.last_checked_at ? new Date(d.last_checked_at).toLocaleDateString('en-US', {month:'short', day:'numeric', hour:'numeric', minute:'2-digit'}) : 'Never'; var changePct = ''; var changeColor = '#71717a'; if (d.baseline_price && d.last_checked_price) { var p = ((d.last_checked_price - d.baseline_price) / d.baseline_price * 100).toFixed(1); changeColor = p > 0 ? '#4ade80' : '#f87171'; changePct = (p > 0 ? '+' : '') + p + '%'; } var thumb = (d.photo_urls && d.photo_urls[0]) ? '' : '
🤖
'; var sheet = document.createElement('div'); sheet.id = 'watchDetailSheet'; sheet.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.85);z-index:10001;display:flex;align-items:flex-end;justify-content:center;padding:0;'; var html = '
'; // Header html += '
' + thumb + '
' + '
' + escapeHtml(d.title || 'Untitled') + '
' + '
' + (d.notify_only ? '📬 Notify mode' : '⚡ Auto-list mode') + '
' + '
' + '' + '
'; // Alert banner for pending items if (isPending) { html += '
' + '
🔔 Price Alert Triggered
' + '
The comp price crossed your threshold. List now or skip to keep watching.
' + '
'; } // Price stats — baseline is editable var baselineVal = d.baseline_price ? parseFloat(d.baseline_price).toFixed(2) : ''; var listPriceVal = d.list_price ? parseFloat(d.list_price).toFixed(2) : ''; html += '
' + '
' + '
Current Comp
' + '
' + current + '
' + (changePct ? '
' + changePct + '
' : '') + '
' + '
' + '
Checked
' + '
' + lastCheck + '
' + '
'; // Editable item details var wd = d.item_data || {}; var watchFields = [ { label: 'Title', key: '_title', value: d.title || '' }, { label: 'Item / Player', key: 'player', value: wd.player || wd.name || '' }, { label: 'Year', key: 'year', value: wd.year || '' }, { label: 'Grade', key: 'grade', value: wd.grade || '' }, { label: 'Grader', key: 'grader', value: wd.grader || '' }, { label: 'Set', key: 'set', value: wd.set || '' }, { label: 'Brand', key: 'brand', value: wd.brand || '' }, { label: 'Variation', key: 'variation', value: wd.variation || '' }, { label: 'Sport', key: 'sport', value: wd.sport || '' }, { label: 'Card #', key: 'cardNumber', value: wd.cardNumber || '' }, { label: 'Serial #', key: 'serial', value: wd.serial || '' }, ].filter(function(f) { return f.value || f.key === '_title'; }); html += '
' + '
' + '
Item Details
' + '
tap to edit
' + '
' + '
' + watchFields.map(function(f, i) { return '
' + '' + '' + '
'; }).join('') + '
' + '' + '
'; // Editable settings html += '
' + '
Watch Settings
' // Baseline + '
' + '
Baseline Price
' + '
' + '$' + '' + '
' // Rise threshold + '
' + '
Rise Threshold
' + '
' + '+' + '' + '%' + '
' // Floor price + '
' + '
Floor Price
' + '
' + '$' + '' + '
' // List price + '
' + '
List At Price
' + '
' + '$' + '' + '
' // Save settings button + '' + '
'; // Photo editing html += '
'; html += ''; html += '
'; // Action buttons — always show List on eBay and Move to Collection html += '
'; html += ''; html += ''; html += '
'; if (isPending) { html += ''; } html += ''; html += '
'; sheet.innerHTML = html; document.body.appendChild(sheet); sheet.addEventListener('click', function(e) { if (e.target === sheet) sheet.remove(); }); // Prevent Android from auto-focusing an input and showing keyboard setTimeout(function() { if (document.activeElement && (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA')) document.activeElement.blur(); }, 50); } // ── Watch Actions ───────────────────────────────────────────────────────────── function watchActionListNow(id) { var d = window._watchCache && window._watchCache[id]; if (!d) return; var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); itemData = d.item_data || {}; itemData.title = d.title; selectedCategory = { id: d.category || 'trading_cards', key: d.category || 'cards', name: d.category || 'Trading Cards', icon: '📦' }; listingPhotos = (d.photo_urls || []).map(function(url, i) { return { dataUrl: url, label: photoLabel(i) }; }); frontBase64 = listingPhotos.length > 0 ? listingPhotos[0].dataUrl : null; backBase64 = listingPhotos.length > 1 ? listingPhotos[1].dataUrl : null; _cameFromCollection = true; document.getElementById('screenDashboard').style.display = 'none'; document.getElementById('mainApp').style.display = 'block'; renderDetails(itemData); initPhotoManager(); showScreen('screenReview'); renderReviewActions(); applyShippingDefaults(); var pi = document.getElementById('priceInput'); if (pi) pi.value = (d.list_price || d.last_checked_price || d.baseline_price || ''); showToast('Review and list when ready', 'success'); } async function watchActionSkip(id) { try { var res = await fetch('/api/drafts?action=watch_skip', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id }), }); var data = await res.json(); if (!data.success) throw new Error(data.error); var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); showToast(data.message || 'Skipped — will check again tomorrow', 'success'); loadWatchlist(); } catch(err) { showToast(err.message, 'error'); } } async function watchActionRemove(id) { try { var res = await fetch('/api/drafts?action=watch_remove', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id }), }); var data = await res.json(); if (!data.success) throw new Error(data.error); var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); showToast(data.message || 'Watch removed', 'success'); loadWatchlist(); } catch(err) { showToast(err.message, 'error'); } } async function moveWatchToCollection(id) { var draft = window._watchCache && window._watchCache[id]; if (!draft) { showToast('Watch not found', 'error'); return; } if (!confirm('Move "' + (draft.title || 'this item').substring(0, 40) + '" to Collection?')) return; try { // Create collection item from watch draft data var res = await fetch('/api/collection?action=add', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ item_data: draft.item_data, category: draft.category || 'cards', category_key: draft.category || 'cards', title: draft.title, photo_urls: draft.photo_urls || [], current_price: draft.last_checked_price || draft.baseline_price || draft.price, }), }); var data = await res.json(); if (!data.success) throw new Error(data.error || 'Could not add to collection.'); // Remove the watch await fetch('/api/drafts?action=watch_remove', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id }), }); var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); showToast('Moved to Collection!', 'success'); loadWatchlist(); loadCollection('recent'); } catch(err) { showToast(err.message, 'error'); } } async function saveWatchEdits(id) { var draft = window._watchCache && window._watchCache[id]; if (!draft) { showToast('Watch not found', 'error'); return; } var fields = document.querySelectorAll('[data-watch-field]'); var updatedData = Object.assign({}, draft.item_data || {}); var newTitle = null; fields.forEach(function(input) { var key = input.getAttribute('data-watch-field'); var val = input.value.trim(); if (key === '_title') { newTitle = val || draft.title; } else { updatedData[key] = val || null; } }); try { var res = await fetch('/api/drafts?action=save', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id, title: newTitle || draft.title, category: draft.category, item_data: updatedData, existing_photo_urls: draft.photo_urls || [], price_watch_enabled: true, }), }); var data = await res.json(); if (!data.success) throw new Error(data.error || 'Could not save'); // Update cache draft.item_data = updatedData; if (newTitle) draft.title = newTitle; window._watchCache[id] = draft; showToast('Item details saved! Comps will update at next check.', 'success'); } catch(err) { showToast(err.message, 'error'); } } async function watchActionSaveSettings(id) { var baseline = parseFloat((document.getElementById('watchEditBaseline') || {}).value) || null; var rise = parseInt((document.getElementById('watchEditRise') || {}).value) || null; var floor = parseFloat((document.getElementById('watchEditFloor') || {}).value) || null; var listPrice = parseFloat((document.getElementById('watchEditListPrice') || {}).value) || null; if (!baseline || baseline <= 0) { showToast('Baseline price is required', 'error'); return; } try { var res = await fetch('/api/drafts?action=watch_update', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id, baseline_price: baseline, rise_threshold_pct: rise, floor_price: floor, list_price: listPrice, }), }); var data = await res.json(); if (!data.success) throw new Error(data.error); showToast('Settings saved ✓', 'success'); // Update cache if (window._watchCache && window._watchCache[id]) { window._watchCache[id].baseline_price = baseline; window._watchCache[id].rise_threshold_pct = rise; window._watchCache[id].floor_price = floor; window._watchCache[id].list_price = listPrice; } var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); loadWatchlist(); } catch(err) { showToast(err.message, 'error'); } } // ─── Watch Photo Editor (FIX 4) ────────────────────────────────────────────── function editWatchPhotos(id) { var d = window._watchCache && window._watchCache[id]; if (!d) return; var existing = document.getElementById('watchPhotosModal'); if (existing) existing.remove(); var photos = (d.photo_urls || []).slice(); var modal = document.createElement('div'); modal.id = 'watchPhotosModal'; modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.88);z-index:10002;display:flex;align-items:flex-end;justify-content:center;padding:0;'; function renderGrid() { var grid = document.getElementById('watchPhotosGrid'); if (!grid) return; var countEl = document.getElementById('watchPhotoCount'); if (countEl) countEl.textContent = photos.length + '/12'; if (!photos.length) { grid.innerHTML = '
No photos yet
'; return; } grid.innerHTML = photos.map(function(url, i) { return '
' + '' + (i === 0 ? '
MAIN
' : '') + '' + '
'; }).join(''); } modal.innerHTML = '
' + '
' + '
📷 Edit Photos
' + '
' + photos.length + '/12' + '
' + '
' + '' + '' + '
'; document.body.appendChild(modal); modal.addEventListener('click', function(e) { if (e.target === modal) modal.remove(); }); renderGrid(); window._wpPhotos = photos; window._wpId = id; window._wpRender = renderGrid; window._wpRemove = function(i) { photos.splice(i, 1); renderGrid(); }; window._wpAdd = function() { var input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.capture = 'environment'; input.style.display = 'none'; input.onchange = async function(e) { var file = e.target.files && e.target.files[0]; if (!file || photos.length >= 12) return; try { var compressed = await compressImage(file); photos.push(compressed); renderGrid(); } catch(err) { showToast('Could not add photo', 'error'); } input.remove(); }; document.body.appendChild(input); input.click(); }; window._wpSave = async function() { var btn = document.getElementById('watchPhotoSaveBtn'); if (btn) { btn.disabled = true; btn.textContent = 'Saving...'; } var urlPhotos = photos.filter(function(p) { return p.startsWith('http'); }); var b64Photos = photos.filter(function(p) { return !p.startsWith('http'); }).map(function(p) { return p.includes(',') ? p.split(',')[1] : p; }); try { var res = await fetch('/api/drafts?action=save', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: window._wpId, existing_photo_urls: urlPhotos, photos: b64Photos, price_watch_enabled: true }) }); var data = await res.json(); if (!data.success) throw new Error(data.error || 'Save failed'); if (window._watchCache && window._watchCache[window._wpId]) { window._watchCache[window._wpId].photo_urls = urlPhotos; } document.getElementById('watchPhotosModal').remove(); showToast('Photos saved!', 'success'); loadWatchlist(); } catch(err) { showToast('Could not save: ' + err.message, 'error'); if (btn) { btn.disabled = false; btn.textContent = '💾 Save Photos'; } } }; } + parseFloat(d.baseline_price).toFixed(2) : '⏳ Pending'; var current = d.last_checked_price ? '$' + parseFloat(d.last_checked_price).toFixed(2) : 'Pending'; var listPrice = d.list_price ? '$' + parseFloat(d.list_price).toFixed(2) : current; var lastCheck = d.last_checked_at ? new Date(d.last_checked_at).toLocaleDateString('en-US', {month:'short', day:'numeric', hour:'numeric', minute:'2-digit'}) : 'Never'; var changePct = ''; var changeColor = '#71717a'; if (d.baseline_price && d.last_checked_price) { var p = ((d.last_checked_price - d.baseline_price) / d.baseline_price * 100).toFixed(1); changeColor = p > 0 ? '#4ade80' : '#f87171'; changePct = (p > 0 ? '+' : '') + p + '%'; } var thumb = (d.photo_urls && d.photo_urls[0]) ? '' : '
🤖
'; var sheet = document.createElement('div'); sheet.id = 'watchDetailSheet'; sheet.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.85);z-index:10001;display:flex;align-items:flex-end;justify-content:center;padding:0;'; var html = '
'; // Header html += '
' + thumb + '
' + '
' + escapeHtml(d.title || 'Untitled') + '
' + '
' + (d.notify_only ? '📬 Notify mode' : '⚡ Auto-list mode') + '
' + '
' + '' + '
'; // Alert banner for pending items if (isPending) { html += '
' + '
🔔 Price Alert Triggered
' + '
The comp price crossed your threshold. List now or skip to keep watching.
' + '
'; } // Price stats — baseline is editable var baselineVal = d.baseline_price ? parseFloat(d.baseline_price).toFixed(2) : ''; var listPriceVal = d.list_price ? parseFloat(d.list_price).toFixed(2) : ''; html += '
' + '
' + '
Current Comp
' + '
' + current + '
' + (changePct ? '
' + changePct + '
' : '') + '
' + '
' + '
Checked
' + '
' + lastCheck + '
' + '
'; // Editable item details var wd = d.item_data || {}; var watchFields = [ { label: 'Title', key: '_title', value: d.title || '' }, { label: 'Item / Player', key: 'player', value: wd.player || wd.name || '' }, { label: 'Year', key: 'year', value: wd.year || '' }, { label: 'Grade', key: 'grade', value: wd.grade || '' }, { label: 'Grader', key: 'grader', value: wd.grader || '' }, { label: 'Set', key: 'set', value: wd.set || '' }, { label: 'Brand', key: 'brand', value: wd.brand || '' }, { label: 'Variation', key: 'variation', value: wd.variation || '' }, { label: 'Sport', key: 'sport', value: wd.sport || '' }, { label: 'Card #', key: 'cardNumber', value: wd.cardNumber || '' }, { label: 'Serial #', key: 'serial', value: wd.serial || '' }, ].filter(function(f) { return f.value || f.key === '_title'; }); html += '
' + '
' + '
Item Details
' + '
tap to edit
' + '
' + '
' + watchFields.map(function(f, i) { return '
' + '' + '' + '
'; }).join('') + '
' + '' + '
'; // Editable settings html += '
' + '
Watch Settings
' // Baseline + '
' + '
Baseline Price
' + '
' + '$' + '' + '
' // Rise threshold + '
' + '
Rise Threshold
' + '
' + '+' + '' + '%' + '
' // Floor price + '
' + '
Floor Price
' + '
' + '$' + '' + '
' // List price + '
' + '
List At Price
' + '
' + '$' + '' + '
' // Save settings button + '' + '
'; // Photo editing html += '
'; html += ''; html += '
'; // Action buttons — always show List on eBay and Move to Collection html += '
'; html += ''; html += ''; html += '
'; if (isPending) { html += ''; } html += ''; html += '
'; sheet.innerHTML = html; document.body.appendChild(sheet); sheet.addEventListener('click', function(e) { if (e.target === sheet) sheet.remove(); }); // Prevent Android from auto-focusing an input and showing keyboard setTimeout(function() { if (document.activeElement && (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA')) document.activeElement.blur(); }, 50); } // ── Watch Actions ───────────────────────────────────────────────────────────── function watchActionListNow(id) { var d = window._watchCache && window._watchCache[id]; if (!d) return; var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); itemData = d.item_data || {}; itemData.title = d.title; selectedCategory = { id: d.category || 'trading_cards', key: d.category || 'cards', name: d.category || 'Trading Cards', icon: '📦' }; listingPhotos = (d.photo_urls || []).map(function(url, i) { return { dataUrl: url, label: photoLabel(i) }; }); frontBase64 = listingPhotos.length > 0 ? listingPhotos[0].dataUrl : null; backBase64 = listingPhotos.length > 1 ? listingPhotos[1].dataUrl : null; _cameFromCollection = true; document.getElementById('screenDashboard').style.display = 'none'; document.getElementById('mainApp').style.display = 'block'; renderDetails(itemData); initPhotoManager(); showScreen('screenReview'); renderReviewActions(); applyShippingDefaults(); var pi = document.getElementById('priceInput'); if (pi) pi.value = (d.list_price || d.last_checked_price || d.baseline_price || ''); showToast('Review and list when ready', 'success'); } async function watchActionSkip(id) { try { var res = await fetch('/api/drafts?action=watch_skip', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id }), }); var data = await res.json(); if (!data.success) throw new Error(data.error); var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); showToast(data.message || 'Skipped — will check again tomorrow', 'success'); loadWatchlist(); } catch(err) { showToast(err.message, 'error'); } } async function watchActionRemove(id) { try { var res = await fetch('/api/drafts?action=watch_remove', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id }), }); var data = await res.json(); if (!data.success) throw new Error(data.error); var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); showToast(data.message || 'Watch removed', 'success'); loadWatchlist(); } catch(err) { showToast(err.message, 'error'); } } async function moveWatchToCollection(id) { var draft = window._watchCache && window._watchCache[id]; if (!draft) { showToast('Watch not found', 'error'); return; } if (!confirm('Move "' + (draft.title || 'this item').substring(0, 40) + '" to Collection?')) return; try { // Create collection item from watch draft data var res = await fetch('/api/collection?action=add', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ item_data: draft.item_data, category: draft.category || 'cards', category_key: draft.category || 'cards', title: draft.title, photo_urls: draft.photo_urls || [], current_price: draft.last_checked_price || draft.baseline_price || draft.price, }), }); var data = await res.json(); if (!data.success) throw new Error(data.error || 'Could not add to collection.'); // Remove the watch await fetch('/api/drafts?action=watch_remove', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id }), }); var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); showToast('Moved to Collection!', 'success'); loadWatchlist(); loadCollection('recent'); } catch(err) { showToast(err.message, 'error'); } } async function saveWatchEdits(id) { var draft = window._watchCache && window._watchCache[id]; if (!draft) { showToast('Watch not found', 'error'); return; } var fields = document.querySelectorAll('[data-watch-field]'); var updatedData = Object.assign({}, draft.item_data || {}); var newTitle = null; fields.forEach(function(input) { var key = input.getAttribute('data-watch-field'); var val = input.value.trim(); if (key === '_title') { newTitle = val || draft.title; } else { updatedData[key] = val || null; } }); try { var res = await fetch('/api/drafts?action=save', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id, title: newTitle || draft.title, category: draft.category, item_data: updatedData, existing_photo_urls: draft.photo_urls || [], price_watch_enabled: true, }), }); var data = await res.json(); if (!data.success) throw new Error(data.error || 'Could not save'); // Update cache draft.item_data = updatedData; if (newTitle) draft.title = newTitle; window._watchCache[id] = draft; showToast('Item details saved! Comps will update at next check.', 'success'); } catch(err) { showToast(err.message, 'error'); } } async function watchActionSaveSettings(id) { var baseline = parseFloat((document.getElementById('watchEditBaseline') || {}).value) || null; var rise = parseInt((document.getElementById('watchEditRise') || {}).value) || null; var floor = parseFloat((document.getElementById('watchEditFloor') || {}).value) || null; var listPrice = parseFloat((document.getElementById('watchEditListPrice') || {}).value) || null; if (!baseline || baseline <= 0) { showToast('Baseline price is required', 'error'); return; } try { var res = await fetch('/api/drafts?action=watch_update', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id, baseline_price: baseline, rise_threshold_pct: rise, floor_price: floor, list_price: listPrice, }), }); var data = await res.json(); if (!data.success) throw new Error(data.error); showToast('Settings saved ✓', 'success'); // Update cache if (window._watchCache && window._watchCache[id]) { window._watchCache[id].baseline_price = baseline; window._watchCache[id].rise_threshold_pct = rise; window._watchCache[id].floor_price = floor; window._watchCache[id].list_price = listPrice; } var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); loadWatchlist(); } catch(err) { showToast(err.message, 'error'); } } // ─── Watch Photo Editor (FIX 4) ────────────────────────────────────────────── function editWatchPhotos(id) { var d = window._watchCache && window._watchCache[id]; if (!d) return; var existing = document.getElementById('watchPhotosModal'); if (existing) existing.remove(); var photos = (d.photo_urls || []).slice(); var modal = document.createElement('div'); modal.id = 'watchPhotosModal'; modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.88);z-index:10002;display:flex;align-items:flex-end;justify-content:center;padding:0;'; function renderGrid() { var grid = document.getElementById('watchPhotosGrid'); if (!grid) return; var countEl = document.getElementById('watchPhotoCount'); if (countEl) countEl.textContent = photos.length + '/12'; if (!photos.length) { grid.innerHTML = '
No photos yet
'; return; } grid.innerHTML = photos.map(function(url, i) { return '
' + '' + (i === 0 ? '
MAIN
' : '') + '' + '
'; }).join(''); } modal.innerHTML = '
' + '
' + '
📷 Edit Photos
' + '
' + photos.length + '/12' + '
' + '
' + '' + '' + '
'; document.body.appendChild(modal); modal.addEventListener('click', function(e) { if (e.target === modal) modal.remove(); }); renderGrid(); window._wpPhotos = photos; window._wpId = id; window._wpRender = renderGrid; window._wpRemove = function(i) { photos.splice(i, 1); renderGrid(); }; window._wpAdd = function() { var input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.capture = 'environment'; input.style.display = 'none'; input.onchange = async function(e) { var file = e.target.files && e.target.files[0]; if (!file || photos.length >= 12) return; try { var compressed = await compressImage(file); photos.push(compressed); renderGrid(); } catch(err) { showToast('Could not add photo', 'error'); } input.remove(); }; document.body.appendChild(input); input.click(); }; window._wpSave = async function() { var btn = document.getElementById('watchPhotoSaveBtn'); if (btn) { btn.disabled = true; btn.textContent = 'Saving...'; } var urlPhotos = photos.filter(function(p) { return p.startsWith('http'); }); var b64Photos = photos.filter(function(p) { return !p.startsWith('http'); }).map(function(p) { return p.includes(',') ? p.split(',')[1] : p; }); try { var res = await fetch('/api/drafts?action=save', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: window._wpId, existing_photo_urls: urlPhotos, photos: b64Photos, price_watch_enabled: true }) }); var data = await res.json(); if (!data.success) throw new Error(data.error || 'Save failed'); if (window._watchCache && window._watchCache[window._wpId]) { window._watchCache[window._wpId].photo_urls = urlPhotos; } document.getElementById('watchPhotosModal').remove(); showToast('Photos saved!', 'success'); loadWatchlist(); } catch(err) { showToast('Could not save: ' + err.message, 'error'); if (btn) { btn.disabled = false; btn.textContent = '💾 Save Photos'; } } }; } +parseFloat(d.baseline_price).toFixed(2):'⏳ Pending'; var current=d.last_checked_price?'$'+parseFloat(d.last_checked_price).toFixed(2):'Pending'; var changeBadge=''; if(d.baseline_price&&d.last_checked_price){ var p=((d.last_checked_price-d.baseline_price)/d.baseline_price*100).toFixed(1); var isUp=p>0; var color=isUp?'#4ade80':'#f87171'; var bg=isUp?'rgba(74,222,128,.1)':'rgba(248,113,113,.1)'; var arrow=isUp?'↑':'↓'; changeBadge='
'+arrow+(isUp?'+':'')+p+'%
'; } var lastCheck=d.last_checked_at?new Date(d.last_checked_at).toLocaleDateString('en-US',{month:'short',day:'numeric'}):'Never'; var isPending=d.watch_status==='pending_confirm'; var statusBadge=isPending ?'
' +'📬 Action needed — tap to list or skip' +'
' :''; var thumb=(d.photo_urls&&d.photo_urls[0]) ?'' :'
🤖
'; return '
' +statusBadge +'
' +thumb +'
' +'
'+(d.title||'Untitled')+'
' +'
' +'
'+current+'
' +changeBadge +'
' +'
' +'
›
' +'
' +'
' +'
Baseline
'+baseline+'
' +'
Threshold
'+(d.rise_threshold_pct?'+'+d.rise_threshold_pct+'%':'—')+'
' +'
Checked
'+lastCheck+'
' +'
' +'
'; }).join(''); document.getElementById('watchlistItems').innerHTML=searchHtml+rows; }catch(e){el.style.display='none';} } // ── Watch Detail Sheet ──────────────────────────────────────────────────────── function showWatchDetailSheet(id) { var d = window._watchCache && window._watchCache[id]; if (!d) return; var existing = document.getElementById('watchDetailSheet'); if (existing) existing.remove(); var isPending = d.watch_status === 'pending_confirm'; var baseline = d.baseline_price ? '$' + parseFloat(d.baseline_price).toFixed(2) : '—'; var current = d.last_checked_price ? '$' + parseFloat(d.last_checked_price).toFixed(2) : 'Pending'; var listPrice = d.list_price ? '$' + parseFloat(d.list_price).toFixed(2) : current; var lastCheck = d.last_checked_at ? new Date(d.last_checked_at).toLocaleDateString('en-US', {month:'short', day:'numeric', hour:'numeric', minute:'2-digit'}) : 'Never'; var changePct = ''; var changeColor = '#71717a'; if (d.baseline_price && d.last_checked_price) { var p = ((d.last_checked_price - d.baseline_price) / d.baseline_price * 100).toFixed(1); changeColor = p > 0 ? '#4ade80' : '#f87171'; changePct = (p > 0 ? '+' : '') + p + '%'; } var thumb = (d.photo_urls && d.photo_urls[0]) ? '' : '
🤖
'; var sheet = document.createElement('div'); sheet.id = 'watchDetailSheet'; sheet.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.85);z-index:10001;display:flex;align-items:flex-end;justify-content:center;padding:0;'; var html = '
'; // Header html += '
' + thumb + '
' + '
' + escapeHtml(d.title || 'Untitled') + '
' + '
' + (d.notify_only ? '📬 Notify mode' : '⚡ Auto-list mode') + '
' + '
' + '' + '
'; // Alert banner for pending items if (isPending) { html += '
' + '
🔔 Price Alert Triggered
' + '
The comp price crossed your threshold. List now or skip to keep watching.
' + '
'; } // Price stats — baseline is editable var baselineVal = d.baseline_price ? parseFloat(d.baseline_price).toFixed(2) : ''; var listPriceVal = d.list_price ? parseFloat(d.list_price).toFixed(2) : ''; html += '
' + '
' + '
Current Comp
' + '
' + current + '
' + (changePct ? '
' + changePct + '
' : '') + '
' + '
' + '
Checked
' + '
' + lastCheck + '
' + '
'; // Editable item details var wd = d.item_data || {}; var watchFields = [ { label: 'Title', key: '_title', value: d.title || '' }, { label: 'Item / Player', key: 'player', value: wd.player || wd.name || '' }, { label: 'Year', key: 'year', value: wd.year || '' }, { label: 'Grade', key: 'grade', value: wd.grade || '' }, { label: 'Grader', key: 'grader', value: wd.grader || '' }, { label: 'Set', key: 'set', value: wd.set || '' }, { label: 'Brand', key: 'brand', value: wd.brand || '' }, { label: 'Variation', key: 'variation', value: wd.variation || '' }, { label: 'Sport', key: 'sport', value: wd.sport || '' }, { label: 'Card #', key: 'cardNumber', value: wd.cardNumber || '' }, { label: 'Serial #', key: 'serial', value: wd.serial || '' }, ].filter(function(f) { return f.value || f.key === '_title'; }); html += '
' + '
' + '
Item Details
' + '
tap to edit
' + '
' + '
' + watchFields.map(function(f, i) { return '
' + '' + '' + '
'; }).join('') + '
' + '' + '
'; // Editable settings html += '
' + '
Watch Settings
' // Baseline + '
' + '
Baseline Price
' + '
' + '$' + '' + '
' // Rise threshold + '
' + '
Rise Threshold
' + '
' + '+' + '' + '%' + '
' // Floor price + '
' + '
Floor Price
' + '
' + '$' + '' + '
' // List price + '
' + '
List At Price
' + '
' + '$' + '' + '
' // Save settings button + '' + '
'; // Photo editing html += '
'; html += ''; html += '
'; // Action buttons — always show List on eBay and Move to Collection html += '
'; html += ''; html += ''; html += '
'; if (isPending) { html += ''; } html += ''; html += '
'; sheet.innerHTML = html; document.body.appendChild(sheet); sheet.addEventListener('click', function(e) { if (e.target === sheet) sheet.remove(); }); // Prevent Android from auto-focusing an input and showing keyboard setTimeout(function() { if (document.activeElement && (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA')) document.activeElement.blur(); }, 50); } // ── Watch Actions ───────────────────────────────────────────────────────────── function watchActionListNow(id) { var d = window._watchCache && window._watchCache[id]; if (!d) return; var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); itemData = d.item_data || {}; itemData.title = d.title; selectedCategory = { id: d.category || 'trading_cards', key: d.category || 'cards', name: d.category || 'Trading Cards', icon: '📦' }; listingPhotos = (d.photo_urls || []).map(function(url, i) { return { dataUrl: url, label: photoLabel(i) }; }); frontBase64 = listingPhotos.length > 0 ? listingPhotos[0].dataUrl : null; backBase64 = listingPhotos.length > 1 ? listingPhotos[1].dataUrl : null; _cameFromCollection = true; document.getElementById('screenDashboard').style.display = 'none'; document.getElementById('mainApp').style.display = 'block'; renderDetails(itemData); initPhotoManager(); showScreen('screenReview'); renderReviewActions(); applyShippingDefaults(); var pi = document.getElementById('priceInput'); if (pi) pi.value = (d.list_price || d.last_checked_price || d.baseline_price || ''); showToast('Review and list when ready', 'success'); } async function watchActionSkip(id) { try { var res = await fetch('/api/drafts?action=watch_skip', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id }), }); var data = await res.json(); if (!data.success) throw new Error(data.error); var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); showToast(data.message || 'Skipped — will check again tomorrow', 'success'); loadWatchlist(); } catch(err) { showToast(err.message, 'error'); } } async function watchActionRemove(id) { try { var res = await fetch('/api/drafts?action=watch_remove', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id }), }); var data = await res.json(); if (!data.success) throw new Error(data.error); var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); showToast(data.message || 'Watch removed', 'success'); loadWatchlist(); } catch(err) { showToast(err.message, 'error'); } } async function moveWatchToCollection(id) { var draft = window._watchCache && window._watchCache[id]; if (!draft) { showToast('Watch not found', 'error'); return; } if (!confirm('Move "' + (draft.title || 'this item').substring(0, 40) + '" to Collection?')) return; try { // Create collection item from watch draft data var res = await fetch('/api/collection?action=add', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ item_data: draft.item_data, category: draft.category || 'cards', category_key: draft.category || 'cards', title: draft.title, photo_urls: draft.photo_urls || [], current_price: draft.last_checked_price || draft.baseline_price || draft.price, }), }); var data = await res.json(); if (!data.success) throw new Error(data.error || 'Could not add to collection.'); // Remove the watch await fetch('/api/drafts?action=watch_remove', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id }), }); var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); showToast('Moved to Collection!', 'success'); loadWatchlist(); loadCollection('recent'); } catch(err) { showToast(err.message, 'error'); } } async function saveWatchEdits(id) { var draft = window._watchCache && window._watchCache[id]; if (!draft) { showToast('Watch not found', 'error'); return; } var fields = document.querySelectorAll('[data-watch-field]'); var updatedData = Object.assign({}, draft.item_data || {}); var newTitle = null; fields.forEach(function(input) { var key = input.getAttribute('data-watch-field'); var val = input.value.trim(); if (key === '_title') { newTitle = val || draft.title; } else { updatedData[key] = val || null; } }); try { var res = await fetch('/api/drafts?action=save', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id, title: newTitle || draft.title, category: draft.category, item_data: updatedData, existing_photo_urls: draft.photo_urls || [], price_watch_enabled: true, }), }); var data = await res.json(); if (!data.success) throw new Error(data.error || 'Could not save'); // Update cache draft.item_data = updatedData; if (newTitle) draft.title = newTitle; window._watchCache[id] = draft; showToast('Item details saved! Comps will update at next check.', 'success'); } catch(err) { showToast(err.message, 'error'); } } async function watchActionSaveSettings(id) { var baseline = parseFloat((document.getElementById('watchEditBaseline') || {}).value) || null; var rise = parseInt((document.getElementById('watchEditRise') || {}).value) || null; var floor = parseFloat((document.getElementById('watchEditFloor') || {}).value) || null; var listPrice = parseFloat((document.getElementById('watchEditListPrice') || {}).value) || null; if (!baseline || baseline <= 0) { showToast('Baseline price is required', 'error'); return; } try { var res = await fetch('/api/drafts?action=watch_update', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: id, baseline_price: baseline, rise_threshold_pct: rise, floor_price: floor, list_price: listPrice, }), }); var data = await res.json(); if (!data.success) throw new Error(data.error); showToast('Settings saved ✓', 'success'); // Update cache if (window._watchCache && window._watchCache[id]) { window._watchCache[id].baseline_price = baseline; window._watchCache[id].rise_threshold_pct = rise; window._watchCache[id].floor_price = floor; window._watchCache[id].list_price = listPrice; } var sheet = document.getElementById('watchDetailSheet'); if (sheet) sheet.remove(); loadWatchlist(); } catch(err) { showToast(err.message, 'error'); } } // ─── Watch Photo Editor (FIX 4) ────────────────────────────────────────────── function editWatchPhotos(id) { var d = window._watchCache && window._watchCache[id]; if (!d) return; var existing = document.getElementById('watchPhotosModal'); if (existing) existing.remove(); var photos = (d.photo_urls || []).slice(); var modal = document.createElement('div'); modal.id = 'watchPhotosModal'; modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.88);z-index:10002;display:flex;align-items:flex-end;justify-content:center;padding:0;'; function renderGrid() { var grid = document.getElementById('watchPhotosGrid'); if (!grid) return; var countEl = document.getElementById('watchPhotoCount'); if (countEl) countEl.textContent = photos.length + '/12'; if (!photos.length) { grid.innerHTML = '
No photos yet
'; return; } grid.innerHTML = photos.map(function(url, i) { return '
' + '' + (i === 0 ? '
MAIN
' : '') + '' + '
'; }).join(''); } modal.innerHTML = '
' + '
' + '
📷 Edit Photos
' + '
' + photos.length + '/12' + '
' + '
' + '' + '' + '
'; document.body.appendChild(modal); modal.addEventListener('click', function(e) { if (e.target === modal) modal.remove(); }); renderGrid(); window._wpPhotos = photos; window._wpId = id; window._wpRender = renderGrid; window._wpRemove = function(i) { photos.splice(i, 1); renderGrid(); }; window._wpAdd = function() { var input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.capture = 'environment'; input.style.display = 'none'; input.onchange = async function(e) { var file = e.target.files && e.target.files[0]; if (!file || photos.length >= 12) return; try { var compressed = await compressImage(file); photos.push(compressed); renderGrid(); } catch(err) { showToast('Could not add photo', 'error'); } input.remove(); }; document.body.appendChild(input); input.click(); }; window._wpSave = async function() { var btn = document.getElementById('watchPhotoSaveBtn'); if (btn) { btn.disabled = true; btn.textContent = 'Saving...'; } var urlPhotos = photos.filter(function(p) { return p.startsWith('http'); }); var b64Photos = photos.filter(function(p) { return !p.startsWith('http'); }).map(function(p) { return p.includes(',') ? p.split(',')[1] : p; }); try { var res = await fetch('/api/drafts?action=save', { method: 'POST', headers: authHeaders(), body: JSON.stringify({ id: window._wpId, existing_photo_urls: urlPhotos, photos: b64Photos, price_watch_enabled: true }) }); var data = await res.json(); if (!data.success) throw new Error(data.error || 'Save failed'); if (window._watchCache && window._watchCache[window._wpId]) { window._watchCache[window._wpId].photo_urls = urlPhotos; } document.getElementById('watchPhotosModal').remove(); showToast('Photos saved!', 'success'); loadWatchlist(); } catch(err) { showToast('Could not save: ' + err.message, 'error'); if (btn) { btn.disabled = false; btn.textContent = '💾 Save Photos'; } } }; }