161 lines
4.1 KiB
TypeScript
161 lines
4.1 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, useCallback } from "react";
|
|
import Link from "next/link";
|
|
import Items from "~/components/sellable_items";
|
|
import Search from "~/components/search";
|
|
import CartButton from "~/components/cart";
|
|
import type { Session } from "next-auth";
|
|
import AccountButton from "./account";
|
|
import SellableItemsButton from "./new_items";
|
|
import TransferButton from "./transfer";
|
|
|
|
type UserResponse = {
|
|
id: string;
|
|
name: string | null;
|
|
carts: {
|
|
cartItems: {
|
|
itemId: string;
|
|
quantity: number;
|
|
}[];
|
|
}[];
|
|
balance: number;
|
|
adresses: string[];
|
|
};
|
|
|
|
type SellableResponse = {
|
|
id: string;
|
|
item_name: string;
|
|
amount: number;
|
|
price: number;
|
|
enabled: boolean;
|
|
shop: {
|
|
label: string;
|
|
};
|
|
item: {
|
|
stock: number;
|
|
};
|
|
};
|
|
|
|
type Props = {
|
|
session: Session | null;
|
|
};
|
|
|
|
export default function HomeClient({ session }: Props) {
|
|
const [query, setQuery] = useState("");
|
|
const [userData, setUserData] = useState<UserResponse | null>(null);
|
|
const [sellableData, setSellableData] = useState<SellableResponse[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
// Fetch /api/user once and store globally here
|
|
const loadUser = useCallback(async () => {
|
|
try {
|
|
setLoading(true);
|
|
const res = await fetch("/api/user");
|
|
if (!res.ok) throw new Error("Failed to fetch user");
|
|
const data = (await res.json()) as UserResponse;
|
|
setUserData(data);
|
|
} catch (err) {
|
|
console.error(err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
const loadSellable = useCallback(async () => {
|
|
try {
|
|
setLoading(true);
|
|
const res = await fetch("/api/sellable");
|
|
if (!res.ok) throw new Error("Failed to fetch sellable");
|
|
const data = (await res.json()) as SellableResponse[];
|
|
setSellableData(data);
|
|
} catch (err) {
|
|
console.error(err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!session) return;
|
|
|
|
// Load immediately
|
|
void loadUser();
|
|
void loadSellable();
|
|
|
|
// Set intervals to reload every 30s (30000ms)
|
|
const userInterval = setInterval(() => {
|
|
void loadUser();
|
|
}, 30000);
|
|
|
|
const sellableInterval = setInterval(() => {
|
|
void loadSellable();
|
|
}, 30000);
|
|
|
|
// Clear intervals on unmount
|
|
return () => {
|
|
clearInterval(userInterval);
|
|
clearInterval(sellableInterval);
|
|
};
|
|
}, [session, loadUser, loadSellable]);
|
|
|
|
return (
|
|
<main className="flex min-h-screen flex-col items-center justify-center bg-linear-to-b from-[#2e026d] to-[#7f3b3b] text-white">
|
|
<div className="absolute top-4 right-4 flex items-center gap-4">
|
|
{userData && (
|
|
<TransferButton
|
|
balance={userData.balance ?? 0}
|
|
reloadUser={loadUser}
|
|
/>
|
|
)}
|
|
{session?.user && (
|
|
<>
|
|
<CartButton
|
|
userData={userData}
|
|
reloadUser={loadUser}
|
|
loading={loading}
|
|
/>
|
|
<AccountButton loading={loading} />
|
|
<SellableItemsButton
|
|
loading={loading}
|
|
reloadSellable={loadSellable}
|
|
reloadUser={loadUser}
|
|
/>
|
|
</>
|
|
)}
|
|
|
|
<Search query={query} setQuery={setQuery} />
|
|
|
|
{session?.user ? (
|
|
<Link
|
|
href="/api/auth/signout"
|
|
className="rounded-full bg-white/10 px-4 py-2 font-semibold transition hover:bg-white/20"
|
|
>
|
|
Sign out
|
|
</Link>
|
|
) : (
|
|
<Link
|
|
href="/api/auth/signin"
|
|
className="rounded-full bg-white/10 px-4 py-2 font-semibold transition hover:bg-white/20"
|
|
>
|
|
Sign in
|
|
</Link>
|
|
)}
|
|
</div>
|
|
|
|
<div className="container flex flex-col items-center gap-12 px-4 py-16">
|
|
<h1 className="text-5xl font-extrabold tracking-tight">
|
|
Suchodupin <span className="text-[hsl(280,100%,70%)]">MC</span> Shop
|
|
</h1>
|
|
{sellableData && (
|
|
<Items
|
|
query={query}
|
|
sellableData={sellableData}
|
|
reloadUser={loadUser}
|
|
/>
|
|
)}
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|