2025-12-26 13:12:37 +00:00

202 lines
6.1 KiB
TypeScript

"use client";
import { getAccessToken_client } from "@/utils/apiHelper_client";
import React, { useEffect, useMemo, useState } from "react";
type Store = {
description?: string;
lastOpenedTime?: string;
logo?: { url?: string };
name?: string;
url?: string;
urlPath?: string;
};
type Payload = {
userid?: string;
message?: string;
store?: Store;
storeId?: string;
};
function tryParseData(raw: string | null): Payload | null {
if (!raw) return null;
try {
return JSON.parse(decodeURIComponent(raw));
} catch {
return null;
}
}
function formatDate(iso?: string) {
if (!iso) return "—";
const d = new Date(iso);
return isNaN(d.getTime()) ? "—" : d.toLocaleString();
}
const read = async (r: Response) =>
r.headers.get("content-type")?.includes("application/json")
? r.json()
: r.text();
export default function AuthCompleteClient() {
const [payload, setPayload] = useState<Payload | null>(null);
useEffect(() => {
const url = new URL(window.location.href);
const hash = window.location.hash;
const hashVal = hash.startsWith("#data=") ? hash.slice(6) : null;
const hashPayload = tryParseData(hashVal);
const qpPayload = tryParseData(url.searchParams.get("data"));
const storeIdOnly = url.searchParams.get("storeId");
setPayload(
hashPayload || qpPayload || (storeIdOnly ? { storeId: storeIdOnly } : null)
);
}, []);
const { title, subtitle, logoUrl, desc, link, lastOpen } = useMemo(() => {
const store = payload?.store ?? {};
const id = payload?.storeId || store.urlPath || "—";
return {
title:
payload?.message ||
(store.name
? `eBay connected! Tokens saved for Store ${store.urlPath}.`
: `eBay connected! Store ${id}`),
subtitle: store.name || id,
logoUrl: store.logo?.url,
desc: store.description,
link: store.url,
lastOpen: formatDate(store.lastOpenedTime),
};
}, [payload]);
useEffect(() => {
if (payload) {
(async () => {
const accessToken = await getAccessToken_client();
payload.userid = sessionStorage.getItem("USERID") || undefined;
if (!payload.userid) return;
const saveRes = await fetch(
"https://ebay.backend.data4autos.com/api/motorstate/auth/ebay/store/save",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
}
);
const saveData = await read(saveRes);
if (!saveRes.ok) {
console.error("Save failed:", saveData);
}
})();
}
}, [payload]);
return (
<div className="min-h-[83vh] flex flex-col items-center justify-center bg-gradient-to-br from-[#00d1ff]/10 via-white to-[#00d1ff]/20 text-gray-800 px-6 py-12">
{/* 🎉 Confetti Animation */}
<Confetti />
{/* Card */}
<div className="w-full max-w-2xl bg-white shadow-xl border border-[#00d1ff]/30 rounded-3xl p-8 text-center relative">
<div className="absolute top-0 left-0 w-full h-2 bg-[#00d1ff]" />
<div className="inline-flex items-center gap-2 bg-[#00d1ff]/10 border border-[#00d1ff]/30 text-[#00d1ff] px-4 py-1 rounded-full mb-6 font-medium">
Connected
</div>
<h1 className="text-2xl md:text-3xl font-bold text-[#00d1ff] mb-4">
{title || "eBay connected successfully!"} 🎉
</h1>
<div className="flex items-center justify-center gap-4 mb-6">
{logoUrl ? (
<img
src={logoUrl}
alt={subtitle || "Store Logo"}
className="w-20 h-20 rounded-xl border border-[#00d1ff]/30 object-cover"
/>
) : (
<div className="w-20 h-20 rounded-xl bg-[#00d1ff]/10 flex items-center justify-center text-2xl font-bold text-[#00d1ff] border border-[#00d1ff]/20">
{subtitle?.[0] || "S"}
</div>
)}
<div className="text-left">
<div className="text-lg font-semibold text-gray-700">{subtitle}</div>
<div className="text-sm text-gray-500">
Last opened: <strong>{lastOpen}</strong>
</div>
</div>
</div>
{desc && <p className="text-gray-600 mb-6">{desc}</p>}
<div className="flex flex-wrap justify-center gap-3">
{link && (
<a
href={link}
target="_blank"
rel="noopener noreferrer"
className="bg-[#00d1ff] hover:bg-[#00b0e6] text-white px-5 py-2 rounded-lg font-medium transition"
>
Visit eBay Store
</a>
)}
<a
href="/"
className="border border-[#00d1ff] text-[#00d1ff] hover:bg-[#00d1ff]/10 px-5 py-2 rounded-lg font-medium transition"
>
Go to Dashboard
</a>
</div>
{!payload && (
<p className="text-gray-500 text-sm mt-6">
Waiting for data redirect with <code>?data=...</code>
</p>
)}
</div>
</div>
);
}
/* ===== Tiny Confetti ===== */
const Confetti = () => {
const pieces = Array.from({ length: 24 });
return (
<div className="absolute inset-0 overflow-hidden pointer-events-none">
<style>{`
@keyframes drop {
0% { transform: translateY(-10vh) rotate(0deg); opacity: 0; }
10% { opacity: 1; }
100% { transform: translateY(110vh) rotate(720deg); opacity: 0; }
}
`}</style>
{pieces.map((_, i) => {
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 color = ["#00d1ff", "#ff69b4", "#16a34a"][Math.floor(Math.random() * 3)];
return (
<span
key={i}
className="absolute top-[-10vh] rounded-full"
style={{
left: `${left}vw`,
width: size,
height: size,
backgroundColor: color,
animation: `drop ${duration}s ease-in ${delay}s both`,
}}
/>
);
})}
</div>
);
};