// screens.jsx — the 4 main screens: Home, Detail, Auth, Dashboard
const { useState: _uS, useEffect: _uE, useMemo: _uM, useRef: _uR } = React;
// ─────────────────────────────────────────────────────────────
// HOME — hero + filter + grid of listings
// ─────────────────────────────────────────────────────────────
function HomeScreen({ L, lang, setLang, theme, layout, filters, setFilters, favs, setFavs, onView, onAuth, sort, setSort, onLayout }) {
const filtered = _uM(() => {
let out = LISTINGS.slice();
if (filters.age && filters.age !== 'any') {
const min = +filters.age * 12;
out = out.filter(l => l.age >= min);
}
if (filters.niche) out = out.filter(l => l.niche === filters.niche);
if (filters.followers === '10k') out = out.filter(l => l.followers < 10000);
else if (filters.followers === '100k') out = out.filter(l => l.followers >= 10000 && l.followers < 100000);
else if (filters.followers === '1m') out = out.filter(l => l.followers >= 100000);
if (filters.priceMax) out = out.filter(l => l.price <= filters.priceMax);
if (sort === 'price_low') out.sort((a,b) => a.price - b.price);
else if (sort === 'price_high') out.sort((a,b) => b.price - a.price);
else if (sort === 'followers') out.sort((a,b) => b.followers - a.followers);
else if (sort === 'age') out.sort((a,b) => b.age - a.age);
return out;
}, [filters, sort]);
const toggleFav = (id) => setFavs(f => f.includes(id) ? f.filter(x => x !== id) : [...f, id]);
return (
{/* HERO — animated gradient + floating post mosaic */}
{L.hero_kicker}
{L.hero_title.split(',')[0]},
{L.hero_title.split(',')[1]?.trim()}
{L.hero_sub}
{/* Social proof: rating + sales */}
{[0,1,2,3,4].map(i => )}
4.9
· 312 yorum
1.847
hesap teslim edildi
{/* Right: floating listing previews */}
{/* Stats strip */}
{[
{ n: '2.847', l: L.stat_listings },
{ n: '14.2K', l: L.stat_sold },
{ n: '99.6%', l: L.stat_uptime },
].map((s, i) => (
))}
{/* Marketplace grid */}
{filtered.map((l, i) => (
))}
);
}
function FloatingPreview({ listing, L, lang, layout = 'grid', style }) {
return (
{}} onView={() => {}}/>
);
}
// ─────────────────────────────────────────────────────────────
// DETAIL screen
// ─────────────────────────────────────────────────────────────
function DetailScreen({ L, lang, setLang, theme, listing, onBack, onAuth, favs, setFavs }) {
const l = listing || LISTINGS[0];
const isFav = favs.includes(l.id);
const [contactOpen, setContactOpen] = _uS(false);
const niche = L[`niche_${l.niche}`] || l.niche;
const toggleFav = () => setFavs(f => f.includes(l.id) ? f.filter(x => x !== l.id) : [...f, l.id]);
// SEO: Product JSON-LD + dynamic title/meta per listing
React.useEffect(() => {
if (typeof document === 'undefined') return;
document.title = `${l.handle} — ${niche} · ${fmtNum(l.followers)} takipçi · InstaSatış`;
let descTag = document.querySelector('meta[name="description"]');
if (!descTag) { descTag = document.createElement('meta'); descTag.setAttribute('name', 'description'); document.head.appendChild(descTag); }
descTag.setAttribute('content', `${l.handle} — ${niche} kategorisinde ${fmtNum(l.followers)} takipçili Instagram hesabı. ${l.age} aylık, %${l.er} etkileşim. ${fmtPrice(l.price, lang)}.`);
// Inject Product schema
const oldLd = document.getElementById('__listing_jsonld');
if (oldLd) oldLd.remove();
const ld = document.createElement('script');
ld.id = '__listing_jsonld';
ld.type = 'application/ld+json';
ld.textContent = JSON.stringify({
"@context": "https://schema.org",
"@type": "Product",
"name": `${l.handle} — Instagram ${niche} hesabı`,
"description": `${fmtNum(l.followers)} takipçili, ${l.age} aylık, ${niche} kategorisinde Instagram hesabı.`,
"category": niche,
"brand": { "@type": "Brand", "name": "InstaSatış" },
"offers": {
"@type": "Offer",
"price": l.price,
"priceCurrency": "TRY",
"availability": l.status === 'sold' ? "https://schema.org/SoldOut" : "https://schema.org/InStock",
"url": `https://instasatis.com/ilan.html?id=${l.id}`,
"seller": { "@type": "Organization", "name": "InstaSatış" }
},
"aggregateRating": l.er >= 3 ? {
"@type": "AggregateRating",
"ratingValue": Math.min(5, 3 + l.er / 5).toFixed(1),
"reviewCount": Math.floor(l.followers / 1000)
} : undefined,
});
document.head.appendChild(ld);
return () => { const el = document.getElementById('__listing_jsonld'); if (el) el.remove(); };
}, [l.id, l.handle, l.followers, l.price, niche, lang]);
return (
{L.nav_explore}
{/* LEFT — profile, posts, metrics */}
{/* Profile card */}
{l.handle}
{l.verified && }
{niche} · {l.country} · listed 2 days ago
{fmtNum(l.followers)} {L.card_followers}
{fmtAge(l.age, lang, L)}
{l.er.toFixed(1)}% ER
{/* Metrics — novel: radial chart + bars */}
{/* Post samples */}
{L.detail_content}
12 of 428 posts
{Array.from({ length: 12 }).map((_, i) => (
))}
{/* Account age "rings" — novel visualization */}
{/* Reviews / yorumlar */}
{/* RIGHT — purchase card sticky */}
{contactOpen && typeof ContactModal !== 'undefined' && (
setContactOpen(false)}/>
)}
);
}
function MetricRadial({ er }) {
const pct = Math.min(er / 10, 1);
const r = 56;
const c = 2 * Math.PI * r;
return (
{er.toFixed(1)}%
ENGAGEMENT
);
}
function MetricBar({ label, value, max, fmt }) {
const pct = Math.min(value / max, 1);
return (
);
}
function AgeTimeline({ months }) {
const years = Math.ceil(months / 12);
const now = new Date(); const start = new Date(now); start.setMonth(start.getMonth() - months);
return (
{Array.from({ length: months }).map((_, i) => {
const h = 20 + ((i * 37) % 20);
return
;
})}
{start.toLocaleDateString('tr-TR', { month: 'short', year: 'numeric' })}
{years} year{years > 1 ? 's' : ''} active · {months} posts/month avg
Today
);
}
// ─────────────────────────────────────────────────────────────
// AUTH screen
// ─────────────────────────────────────────────────────────────
function AuthScreen({ L, lang, setLang, theme, mode = 'login', onMode, onBack }) {
const [m, setM] = _uS(mode);
const [name, setName] = _uS('');
const [email, setEmail] = _uS('');
const [pass, setPass] = _uS('');
const [phone, setPhone] = _uS('');
const [loading, setLoading] = _uS(false);
const [error, setError] = _uS('');
// 2FA aşaması
const [needs2FA, setNeeds2FA] = _uS(false);
const [totpCode, setTotpCode] = _uS('');
_uE(() => setM(mode), [mode]);
const handleSubmit = async (e) => {
e?.preventDefault?.();
setError('');
if (!email || !pass) return setError(lang === 'tr' ? 'E-posta ve şifre gerekli' : 'Email and password required');
if (m === 'signup' && !name) return setError(lang === 'tr' ? 'İsim gerekli' : 'Name required');
if (m === 'signup' && pass.length < 8) return setError(lang === 'tr' ? 'Şifre en az 8 karakter' : 'Password min 8 chars');
if (needs2FA && totpCode.length !== 6) return setError(lang === 'tr' ? '6 haneli kodu gir' : 'Enter 6-digit code');
if (!window.api) {
// API yüklü değil → demo modu (localStorage)
localStorage.setItem('instasatis_user', JSON.stringify({ email, name: name || email.split('@')[0] }));
window.location.href = 'panelim.html';
return;
}
setLoading(true);
try {
if (m === 'login') {
const res = await window.api.auth.login(email, pass, needs2FA ? totpCode : undefined);
// 2FA gerekiyorsa ikinci aşamaya geç
if (res && res.needs2FA) {
setNeeds2FA(true);
setLoading(false);
return;
}
} else {
await window.api.auth.register(email, pass, name, phone);
}
window.location.href = 'panelim.html';
} catch (err) {
setError(err.message || (lang === 'tr' ? 'Bir hata oluştu' : 'An error occurred'));
} finally {
setLoading(false);
}
};
return (
{/* LEFT — brand panel */}
instasatis.com
{L.hero_title.split(',')[0]},
{L.hero_title.split(',')[1]?.trim()}
{[L.trust_ssl, L.trust_escrow, L.trust_refund, L.trust_verified].map((t,i) => (
{t}
))}
{/* decorative post mosaic */}
{/* RIGHT — form */}
{L.nav_explore}
{L.auth_welcome}
{m === 'login' ? L.auth_login : L.auth_signup}
{L.auth_sub}
{/* Tabs */}
{['login','signup'].map(t => (
))}
);
}
function Field({ icon, placeholder, type = 'text', defaultValue, value, onChange }) {
const isControlled = value !== undefined && onChange;
return (
{isControlled ? (
onChange(e.target.value)} style={{ paddingLeft: 40 }}/>
) : (
)}
);
}
// ─────────────────────────────────────────────────────────────
// DASHBOARD — buyer side
// ─────────────────────────────────────────────────────────────
function DashboardScreen({ L, lang, setLang, theme, favs, setFavs, onView, onAuth }) {
const [tab, setTab] = _uS('fav');
const favItems = LISTINGS.filter(l => favs.includes(l.id));
return (
{/* Sidebar */}
{/* Main */}
{tab === 'fav' ? L.dash_fav : tab === 'watch' ? L.dash_watch : tab === 'offers' ? L.dash_offers : tab === 'security' ? (lang === 'tr' ? 'Güvenlik' : 'Security') : L.dash_purchases}
{tab === 'fav' ? favs.length : tab === 'watch' ? 3 : tab === 'offers' ? 1 : 2} items
{tab === 'fav' && (
favItems.length === 0 ? (
) : (
{favItems.map(l => (
setFavs(f => f.filter(x => x !== id))}
onView={onView}/>
))}
)
)}
{tab === 'offers' && (
)}
{tab === 'watch' && (
{[LISTINGS[3], LISTINGS[6], LISTINGS[11]].map(l => (
setFavs(f => f.includes(id) ? f.filter(x => x !== id) : [...f, id])}
onView={onView}/>
))}
)}
{tab === 'purchases' && (
{[
{ l: LISTINGS[2], date: '12 Nis 2026', status: 'delivered' },
{ l: LISTINGS[5], date: '28 Mar 2026', status: 'delivered' },
].map((p, i) => (
))}
)}
{tab === 'security' &&
}
);
}
function OfferRow({ L, lang, listing, status, amount }) {
return (
{listing.handle}
{L[`niche_${listing.niche}`]} · {fmtNum(listing.followers)} {L.card_followers}
Teklifin
{fmtPrice(amount, lang)}
Bekliyor
);
}
function PurchaseRow({ L, lang, l, date, status, isFirst }) {
return (
{l.handle}
{date} · Teslim edildi
{fmtPrice(l.price, lang)}
Teslim
);
}
// ─────────────────────────────────────────────────────────────
// Satıcı kartı — listing aside'da gösterilen mini profil
// ─────────────────────────────────────────────────────────────
function SellerCard({ listing, lang, L }) {
const l = listing;
// Mock satıcı verisi (API olunca gerçek gelecek)
const seller = {
name: l.seller_name || (l.handle.replace('@','').split('.')[0].charAt(0).toUpperCase() + l.handle.replace('@','').split('.')[0].slice(1) + ' Y.'),
handle: '@seller_' + l.handle.replace('@','').slice(0, 8),
rating: 4.8,
review_count: 23,
listings_count: 7,
response_time: lang === 'tr' ? '~2 saat' : '~2h',
member_since: 2023,
verified_seller: true,
};
return (
{lang === 'tr' ? 'Satıcı' : 'Seller'}
{seller.name.charAt(0)}
{seller.name}
{seller.verified_seller &&
}
{lang === 'tr' ? 'Üye' : 'Member'} {seller.member_since}
{/* Rating */}
{seller.rating}
({seller.review_count} {lang === 'tr' ? 'yorum' : 'reviews'})
{/* İstatistikler */}
{lang === 'tr' ? 'Toplam İlan' : 'Listings'}
{seller.listings_count}
{lang === 'tr' ? 'Cevap Süresi' : 'Response'}
{seller.response_time}
{lang === 'tr' ? 'Tüm ilanlarını gör →' : 'View all listings →'}
);
}
// Yıldız puanlama
function StarRating({ value, size = 16, interactive = false, onChange }) {
const stars = [1, 2, 3, 4, 5];
return (
{stars.map(s => {
const filled = value >= s;
const half = !filled && value >= s - 0.5;
return (
);
})}
);
}
// ─────────────────────────────────────────────────────────────
// Yorumlar bölümü
// ─────────────────────────────────────────────────────────────
function ReviewsSection({ listing, lang, L }) {
// Mock yorumlar (API'den gelecek)
const reviews = [
{ id: 1, name: 'Ahmet K.', initials: 'AK', rating: 5, date: '2 hafta önce', text: lang === 'tr' ? 'Süreç çok pürüzsüzdü, hesap aynen ilanda yazdığı gibiydi. Komisyon karşılığında bu güvenliği almak çok değerli.' : 'Smooth process, account exactly as listed. Worth the commission for the security.', verified_purchase: true },
{ id: 2, name: 'Selin Ö.', initials: 'SÖ', rating: 5, date: '1 ay önce', text: lang === 'tr' ? 'Satıcı çok hızlı yanıt verdi ve devir 24 saat içinde tamamlandı. Tavsiye ederim.' : 'Seller responded quickly, transfer completed within 24 hours. Recommended.', verified_purchase: true },
{ id: 3, name: 'Mert D.', initials: 'MD', rating: 4, date: '2 ay önce', text: lang === 'tr' ? 'Hesap iyi, takipçi sayısı doğru. Tek eksik birkaç eski post silinmesi gerekti.' : 'Good account, follower count accurate. Only minor: had to delete some old posts.', verified_purchase: true },
];
const avg = reviews.reduce((s, r) => s + r.rating, 0) / reviews.length;
const distribution = [5,4,3,2,1].map(n => ({
n, count: reviews.filter(r => r.rating === n).length,
pct: (reviews.filter(r => r.rating === n).length / reviews.length) * 100,
}));
return (
{lang === 'tr' ? 'Yorumlar & Değerlendirmeler' : 'Reviews & Ratings'}
{reviews.length} {lang === 'tr' ? 'yorum' : 'reviews'}
{/* Özet */}
{avg.toFixed(1)}
{reviews.length} {lang === 'tr' ? 'değerlendirme' : 'ratings'}
{distribution.map(d => (
))}
{/* Yorum listesi */}
{reviews.map(r => (
{r.initials}
{r.name}
{r.verified_purchase && (
{lang === 'tr' ? 'DOĞRULANMIŞ ALIM' : 'VERIFIED'}
)}
{r.date}
{r.text}
))}
);
}
Object.assign(window, { HomeScreen, DetailScreen, AuthScreen, DashboardScreen, SellerCard, StarRating, ReviewsSection });