'use client'; import React, { useEffect, useMemo, useState } from 'react'; import { getAccessToken_client } from '@/utils/apiHelper_client'; /** ===== Types from your API ===== */ type StoreFromAPI = { userid: string; store_name: string; store_description: string; store_url: string; store_url_path: string; store_last_opened_time: string | null; store_last_opened_time_raw: string | null; store_logo_url: string; }; type ApiOk = { code: 'STORE_PRESENT'; message: string; store: StoreFromAPI }; type ApiMiss = { code: 'USER_NOT_FOUND_OR_NO_STORE'; message: string }; type ApiResp = ApiOk | ApiMiss | any; /** ===== Helpers ===== */ const read = async (r: Response) => r.headers.get('content-type')?.includes('application/json') ? r.json() : r.text(); function safeDateFormat(input?: string | null) { if (!input) return '—'; const d = new Date(input); return isNaN(d.getTime()) ? '—' : d.toLocaleString(); } /** ===== Page ===== */ export default function EbayAuthPage() { const [status, setStatus] = useState<'loading' | 'connected' | 'disconnected'>('loading'); const [store, setStore] = useState(null); const [toast, setToast] = useState(''); const [connecting, setConnecting] = useState(false); // Build a return URL back to THIS page (works no matter which route you use) const returnUrl = useMemo(() => { if (typeof window === 'undefined') return ''; return `${window.location.origin}${window.location.pathname}`; }, []); // On load: check store status with only the userid useEffect(() => { (async () => { try { // Optional: keep your access token call if you need it for other things await getAccessToken_client().catch(() => null); const userid = sessionStorage.getItem('USERID') || undefined; if (!userid) { setStatus('disconnected'); setToast('No user session found. Please sign in and try again.'); return; } const res = await fetch( 'https://ebay.backend.data4autos.com/api/auth/ebay/store/checkstorestatus', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ userid }), cache: 'no-store', } ); const data: ApiResp = await read(res); if (!res.ok) { console.error('checkstorestatus failed:', res.status, data); setStatus('disconnected'); return; } if (data?.code === 'STORE_PRESENT' && data?.store) { setStore(data.store); setStatus('connected'); setToast('eBay connected successfully'); } else { setStatus('disconnected'); } } catch (err) { console.error('checkstorestatus error:', err); setStatus('disconnected'); } })(); }, []); /** Start OAuth if disconnected */ const OAUTH_ENDPOINT = 'https://ebay.backend.data4autos.com/api/ebay/oauth/login'; const startOauth = () => { try { setConnecting(true); const url = `${OAUTH_ENDPOINT}?return_url=${encodeURIComponent(returnUrl)}`; window.location.href = url; } catch { setConnecting(false); setToast('Failed to start eBay connection. Please try again.'); } }; /** Derive UI fields from API store */ const { title, subtitle, logoUrl, desc, link, lastOpen } = useMemo(() => { if (!store) { return { title: '', subtitle: '', logoUrl: '', desc: '', link: '', lastOpen: '—', }; } return { title: `eBay connected! Store ${store.store_url_path}`, subtitle: store.store_name || store.store_url_path, logoUrl: store.store_logo_url, desc: store.store_description, link: store.store_url, lastOpen: safeDateFormat(store.store_last_opened_time || store.store_last_opened_time_raw), }; }, [store]); return (
{status === 'connected' && }
{toast &&
{toast}
} {status === 'loading' && ( <>
Checking your store status…

Verifying eBay connection…

)} {status === 'connected' && ( <>
Connected

{title || 'eBay connected successfully!'} 🎉

{logoUrl ? ( {subtitle ) : (
{(subtitle || 'S').slice(0, 1)}
)}
{subtitle}
Last opened: {lastOpen}
{desc ?

{desc}

: null}
)} {status === 'disconnected' && (

eBay Settings

Connect your eBay store to enable product sync, inventory updates, and order flow.

You’ll be redirected to eBay to authorize access, then returned here automatically.

)}
); } /** ===== Styles (unchanged from your design) ===== */ const styles: Record = { page: { minHeight: '100svh', background: 'radial-gradient(1200px 600px at 10% -10%, rgba(99,102,241,0.25), transparent 50%), radial-gradient(1000px 500px at 110% 10%, rgba(16,185,129,0.25), transparent 50%), linear-gradient(180deg, #0b1020 0%, #0b0f1a 100%)', padding: '48px 16px', color: 'white', display: 'flex', alignItems: 'center', justifyContent: 'center', }, cardWrap: { width: 'min(920px, 92vw)', textAlign: 'center' }, toast: { marginBottom: 12, padding: '10px 12px', borderRadius: 8, border: '1px solid rgba(34,197,94,0.35)', background: 'rgba(34,197,94,0.15)', color: '#d1fae5', fontSize: 14, }, badge: { display: 'inline-flex', alignItems: 'center', gap: 8, background: 'rgba(34,197,94,0.15)', border: '1px solid rgba(34,197,94,0.35)', padding: '8px 12px', borderRadius: 999, marginBottom: 12, }, badgeEmoji: { fontSize: 18 }, badgeText: { fontSize: 14, letterSpacing: 0.4 }, title: { fontSize: 28, lineHeight: 1.25, margin: '0 0 18px', fontWeight: 700 }, card: { textAlign: 'left', background: 'linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.02))', border: '1px solid rgba(255,255,255,0.08)', borderRadius: 16, boxShadow: '0 20px 60px rgba(0,0,0,0.35)', padding: 20, backdropFilter: 'blur(6px)', }, headerRow: { display: 'grid', gridTemplateColumns: '72px 1fr', gap: 16, alignItems: 'center', marginBottom: 12, }, logo: { width: 72, height: 72, borderRadius: 12, objectFit: 'cover', border: '1px solid rgba(255,255,255,0.15)', background: 'rgba(255,255,255,0.06)', }, logoFallback: { width: 72, height: 72, borderRadius: 12, display: 'grid', placeItems: 'center', background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.15)', fontSize: 28, fontWeight: 700, }, storeName: { fontSize: 22, fontWeight: 700, marginBottom: 4, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', }, storeMeta: { opacity: 0.8, fontSize: 14 }, desc: { marginTop: 10, marginBottom: 16, lineHeight: 1.6, opacity: 0.95 }, actions: { display: 'flex', gap: 12, flexWrap: 'wrap' }, primaryBtn: { background: 'linear-gradient(180deg, #22c55e, #16a34a)', color: 'white', textDecoration: 'none', padding: '10px 14px', borderRadius: 10, fontWeight: 600, border: '1px solid rgba(255,255,255,0.15)', }, secondaryBtn: { background: 'transparent', color: 'white', textDecoration: 'none', padding: '10px 14px', borderRadius: 10, fontWeight: 600, border: '1px solid rgba(255,255,255,0.2)', }, hint: { marginTop: 14, opacity: 0.75, fontSize: 14 }, connectWrap: { textAlign: 'left', background: 'linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.02))', border: '1px solid rgba(255,255,255,0.08)', borderRadius: 16, boxShadow: '0 20px 60px rgba(0,0,0,0.35)', padding: 20, backdropFilter: 'blur(6px)', }, subtleText: { opacity: 0.85, marginBottom: 14 }, buttonBase: { width: '100%', padding: '10px 14px', borderRadius: 10, fontWeight: 700, border: '1px solid rgba(255,255,255,0.2)', cursor: 'pointer', }, btnPrimary: { background: 'linear-gradient(180deg, #2563eb, #1d4ed8)', color: 'white', }, btnDisabled: { opacity: 0.7, color: 'white', background: 'linear-gradient(180deg, #2563eb, #1d4ed8)', cursor: 'not-allowed', }, }; /** ===== Confetti (pure CSS) ===== */ const Confetti = () => { const pieces = Array.from({ length: 24 }); return (
{pieces.map((_, i) => ( ))}
); }; const confettiStyles: Record = { wrap: { position: 'fixed', inset: 0, pointerEvents: 'none', overflow: 'hidden' }, }; const confettiKeyframes = ` @keyframes drop { 0% { transform: translateY(-10vh) rotate(0deg); opacity: 0; } 10% { opacity: 1; } 100% { transform: translateY(110vh) rotate(720deg); opacity: 0; } } `; function pieceStyle(i: number): React.CSSProperties { const left = Math.random() * 100; const size = 6 + Math.random() * 8; const duration = 2.8 + Math.random() * 1.6; const delay = Math.random() * 0.8; const borderRadius = Math.random() > 0.5 ? 2 : 999; return { position: 'absolute', top: '-10vh', left: `${left}vw`, width: size, height: size, background: Math.random() > 0.5 ? 'rgba(99,102,241,0.95)' : Math.random() > 0.5 ? 'rgba(16,185,129,0.95)' : 'rgba(244,114,182,0.95)', borderRadius, animation: `drop ${duration}s ease-in ${delay}s both`, }; }