refactor(blackjack): remove Blackjack game components and actions
refactor(home): update sellableData state to use array type fix(account): improve type safety for user data fetching fix(cart): streamline item removal and purchase handling
This commit is contained in:
parent
eef355a791
commit
992c9a2e63
@ -1,354 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { useRef, useState, useTransition } from "react";
|
|
||||||
import { startGame, hit, stand, revealDealer } from "./actions";
|
|
||||||
import { motion } from "framer-motion";
|
|
||||||
|
|
||||||
type Phase = "idle" | "playing" | "revealing" | "finished";
|
|
||||||
|
|
||||||
export default function BlackjackClient() {
|
|
||||||
const [state, setState] = useState<any>(null);
|
|
||||||
const [gameId, setGameId] = useState<string | null>(null);
|
|
||||||
const [phase, setPhase] = useState<Phase>("idle");
|
|
||||||
const [visibleDealerCount, setVisibleDealerCount] = useState(1);
|
|
||||||
|
|
||||||
const dealerRevealIndex = useRef(0);
|
|
||||||
const MAX_DEALER_CARDS = 5;
|
|
||||||
const CARD_WIDTH = 64; // w-16
|
|
||||||
const GAP = 8; // space-x-2
|
|
||||||
|
|
||||||
const [, startTransition] = useTransition();
|
|
||||||
|
|
||||||
// animation snapshots
|
|
||||||
const lastPlayerLen = useRef(0);
|
|
||||||
|
|
||||||
/* ---------------- actions ---------------- */
|
|
||||||
|
|
||||||
const start = async () => {
|
|
||||||
const res: any = await startGame(10);
|
|
||||||
lastPlayerLen.current = 0;
|
|
||||||
setVisibleDealerCount(1);
|
|
||||||
setGameId(res.gameId);
|
|
||||||
setPhase("playing");
|
|
||||||
setState(res);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getDealerCards = () => {
|
|
||||||
// if we don't yet have any dealer info, nothing to render
|
|
||||||
if (!state?.dealerUpcard && !state?.dealer) return [];
|
|
||||||
|
|
||||||
// ALWAYS start with upcard + hole slot; prefer the authoritative dealer state when available
|
|
||||||
const upcard = state?.dealer?.[0] ?? state.dealerUpcard[0];
|
|
||||||
const base = [upcard, "__HOLE__"];
|
|
||||||
|
|
||||||
// If dealer is revealed, append extra cards ONLY
|
|
||||||
if (state?.dealer && state.dealer.length > 2) {
|
|
||||||
return [...base, ...state.dealer.slice(2)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return base;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onHit = async () => {
|
|
||||||
lastPlayerLen.current = state.player.length;
|
|
||||||
|
|
||||||
const res = await hit(gameId!);
|
|
||||||
setState((s: any) => ({ ...s, ...res }));
|
|
||||||
|
|
||||||
if (res.status === "bust") {
|
|
||||||
const dealerRes = await revealDealer(gameId!);
|
|
||||||
|
|
||||||
setState((s: any) => ({
|
|
||||||
...s,
|
|
||||||
dealer: dealerRes.dealer,
|
|
||||||
dealerTotal: dealerRes.dealerTotal,
|
|
||||||
status: "bust",
|
|
||||||
}));
|
|
||||||
|
|
||||||
setVisibleDealerCount(dealerRes.dealer.length);
|
|
||||||
setPhase("finished");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onStand = async () => {
|
|
||||||
const res = await stand(gameId!);
|
|
||||||
|
|
||||||
setPhase("revealing");
|
|
||||||
// start with only upcard visible; we'll reveal the hole (i=1) then extra cards (i>=2)
|
|
||||||
dealerRevealIndex.current = 0;
|
|
||||||
setVisibleDealerCount(1);
|
|
||||||
|
|
||||||
setState((s: any) => ({
|
|
||||||
...s,
|
|
||||||
dealer: res.dealer,
|
|
||||||
dealerTotal: res.dealerTotal,
|
|
||||||
status: "finished",
|
|
||||||
result: res.result,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// sequential reveal:
|
|
||||||
// - i = 1 -> flip the hole card
|
|
||||||
// - i >= 2 -> reveal each drawn card in order
|
|
||||||
for (let i = 1; i < res.dealer.length; i++) {
|
|
||||||
// wait before revealing next slot
|
|
||||||
await new Promise((r) => setTimeout(r, 1600));
|
|
||||||
// set which dealer index should be animating (1 for hole, 2..n for drawn)
|
|
||||||
dealerRevealIndex.current = i;
|
|
||||||
// make the slot visible (i+1 slots visible: 0..i)
|
|
||||||
setVisibleDealerCount(i + 1);
|
|
||||||
// small pause so the flip animation has time to start/finish before next reveal step
|
|
||||||
await new Promise((r) => setTimeout(r, 600));
|
|
||||||
}
|
|
||||||
|
|
||||||
setPhase("finished");
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ---------------- cards ---------------- */
|
|
||||||
|
|
||||||
const CardStatic = ({ c }: any) => (
|
|
||||||
<div className="flex h-24 w-16 items-center justify-center rounded bg-white text-xl font-bold text-black shadow-xl">
|
|
||||||
{c.label}
|
|
||||||
{c.suit}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const CardBack = () => (
|
|
||||||
<div className="flex h-24 w-16 items-center justify-center rounded bg-zinc-700 shadow-xl">
|
|
||||||
<span className="text-xs tracking-widest text-zinc-300">RUST</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const CardFlip = ({ c }: any) => (
|
|
||||||
<motion.div
|
|
||||||
className="perspective h-24 w-16"
|
|
||||||
initial={{ y: -60, opacity: 0 }}
|
|
||||||
animate={{ y: 0, opacity: 1 }}
|
|
||||||
transition={{ duration: 1.0 }}
|
|
||||||
>
|
|
||||||
<motion.div
|
|
||||||
className="relative h-full w-full"
|
|
||||||
initial={{ rotateY: 0 }}
|
|
||||||
animate={{ rotateY: 180 }}
|
|
||||||
transition={{
|
|
||||||
duration: 1.0,
|
|
||||||
delay: 0.8,
|
|
||||||
ease: "easeInOut",
|
|
||||||
}}
|
|
||||||
style={{ transformStyle: "preserve-3d" }}
|
|
||||||
>
|
|
||||||
{/* BACK */}
|
|
||||||
<div
|
|
||||||
className="absolute inset-0 flex items-center justify-center rounded bg-zinc-700 shadow-xl"
|
|
||||||
style={{ backfaceVisibility: "hidden" }}
|
|
||||||
>
|
|
||||||
<span className="text-xs tracking-widest text-zinc-300">RUST</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* FRONT */}
|
|
||||||
<div
|
|
||||||
className="absolute inset-0 flex items-center justify-center rounded bg-white text-xl font-bold text-black shadow-xl"
|
|
||||||
style={{
|
|
||||||
backfaceVisibility: "hidden",
|
|
||||||
transform: "rotateY(180deg)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{c.label}
|
|
||||||
{c.suit}
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
</motion.div>
|
|
||||||
);
|
|
||||||
|
|
||||||
function isSoftHand(hand: any[], total: number) {
|
|
||||||
const hasAce = hand.some((c) => c.label === "A");
|
|
||||||
if (!hasAce) return false;
|
|
||||||
|
|
||||||
// if counting all aces as 1 would reduce the total, it's soft
|
|
||||||
const minTotal = hand.reduce(
|
|
||||||
(sum, c) => sum + (c.label === "A" ? 1 : c.value),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
return minTotal !== total;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stable key helpers to prevent React remount flashes */
|
|
||||||
const dealerCardKey = (slot: any, index: number) => {
|
|
||||||
// slot can be a card object or "__HOLE__"
|
|
||||||
if (slot === "__HOLE__") {
|
|
||||||
// When hole is hidden, use a stable hidden key so it doesn't briefly mount/unmount
|
|
||||||
if (phase === "playing" || state?.status === "bust") {
|
|
||||||
return "dealer-hole-hidden";
|
|
||||||
}
|
|
||||||
// When revealed, key should reflect the actual dealer card identity
|
|
||||||
const revealedCard = state?.dealer?.[1];
|
|
||||||
if (revealedCard?.code) return `dealer-${revealedCard.code}`;
|
|
||||||
if (revealedCard)
|
|
||||||
return `dealer-${revealedCard.label}${revealedCard.suit}`;
|
|
||||||
return `dealer-hole-${index}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For upcard and other visible cards prefer a unique code if available
|
|
||||||
if (slot?.code) return `dealer-${slot.code}`;
|
|
||||||
if (slot?.label && slot?.suit)
|
|
||||||
return `dealer-${slot.label}${slot.suit}-${index}`;
|
|
||||||
|
|
||||||
// fallback stable index
|
|
||||||
return `dealer-${index}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ---------------- render ---------------- */
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex min-h-screen items-center justify-center bg-zinc-900 text-white">
|
|
||||||
<div className="w-[420px] space-y-4 rounded-lg bg-zinc-800 p-4 shadow-2xl">
|
|
||||||
{/* DEALER */}
|
|
||||||
<div className="flex min-h-[96px] justify-center space-x-2">
|
|
||||||
{getDealerCards().map((c, i) => {
|
|
||||||
// Always render upcard (index 0) regardless of visibleDealerCount
|
|
||||||
if (i === 0 && c && c !== "__HOLE__") {
|
|
||||||
const key = dealerCardKey(c, i);
|
|
||||||
return <CardStatic key={key} c={c} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
// HOLE CARD (index 1)
|
|
||||||
if (i === 1) {
|
|
||||||
const key = dealerCardKey(c, i);
|
|
||||||
|
|
||||||
// Hidden hole card during play or when player busts
|
|
||||||
// Also respect visibleDealerCount: hole is visible only when visibleDealerCount >= 2
|
|
||||||
if (
|
|
||||||
phase === "playing" ||
|
|
||||||
state?.status === "bust" ||
|
|
||||||
visibleDealerCount < 2
|
|
||||||
) {
|
|
||||||
return <CardBack key={key} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
// During reveal phase: flip the hole only when dealerRevealIndex === 1
|
|
||||||
if (phase === "revealing" && state?.dealer) {
|
|
||||||
if (dealerRevealIndex.current === 1) {
|
|
||||||
return <CardFlip key={key} c={state.dealer[1]} />;
|
|
||||||
} else {
|
|
||||||
return <CardStatic key={key} c={state.dealer[1]} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Final static revealed card
|
|
||||||
return <CardStatic key={key} c={state.dealer?.[1]} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
// DRAWN CARDS (index >= 2)
|
|
||||||
if (i >= 2 && state?.dealer) {
|
|
||||||
// only render this slot once it's been made visible by visibleDealerCount
|
|
||||||
if (i >= visibleDealerCount) return null;
|
|
||||||
|
|
||||||
const key = dealerCardKey(state.dealer[i], i);
|
|
||||||
|
|
||||||
// If we're currently revealing this index, play the flip animation.
|
|
||||||
// Otherwise show the static face
|
|
||||||
if (phase === "revealing" && i === dealerRevealIndex.current) {
|
|
||||||
return <CardFlip key={key} c={state.dealer[i]} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <CardStatic key={key} c={state.dealer[i]} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* TERMINAL */}
|
|
||||||
<div className="min-h-[130px] rounded border-2 border-green-500 bg-black p-3 font-mono text-sm text-green-400">
|
|
||||||
{phase === "idle" && <p>> INSERT SCRAP</p>}
|
|
||||||
|
|
||||||
{phase === "playing" && state && (
|
|
||||||
<>
|
|
||||||
<p>
|
|
||||||
> PLAYER TOTAL: {state.playerTotal}
|
|
||||||
{isSoftHand(state.player, state.playerTotal) && " (SOFT)"}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>> DEALER WAITING...</p>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{phase === "revealing" && (
|
|
||||||
<motion.p
|
|
||||||
initial={{ opacity: 0 }}
|
|
||||||
animate={{ opacity: 1 }}
|
|
||||||
transition={{ duration: 1 }}
|
|
||||||
>
|
|
||||||
> DEALER DRAWING...
|
|
||||||
</motion.p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{phase === "finished" && state && (
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0 }}
|
|
||||||
animate={{ opacity: 1 }}
|
|
||||||
transition={{ duration: 1 }}
|
|
||||||
>
|
|
||||||
<p>> PLAYER: {state.playerTotal}</p>
|
|
||||||
|
|
||||||
{state.status === "bust" && (
|
|
||||||
<p className="text-red-500">> PLAYER BUSTED</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Always show dealer score once dealer is revealed */}
|
|
||||||
{state.dealerTotal !== undefined && (
|
|
||||||
<p>> DEALER: {state.dealerTotal}</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{state.result && <p>> RESULT: {state.result.toUpperCase()}</p>}
|
|
||||||
</motion.div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{/* PLAYER */}
|
|
||||||
<div className="flex min-h-[96px] justify-center space-x-2">
|
|
||||||
{state?.player?.map((c: any, i: number) =>
|
|
||||||
phase === "playing" && i >= lastPlayerLen.current ? (
|
|
||||||
<CardFlip
|
|
||||||
key={`player-${c?.code ?? c?.label + c?.suit + i}`}
|
|
||||||
c={c}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<CardStatic
|
|
||||||
key={`player-${c?.code ?? c?.label + c?.suit + i}`}
|
|
||||||
c={c}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{/* CONTROLS */}
|
|
||||||
<div className="flex justify-center space-x-3">
|
|
||||||
{phase === "idle" && (
|
|
||||||
<button
|
|
||||||
onClick={() => startTransition(start)}
|
|
||||||
className="rounded bg-green-700 px-4 py-2 font-bold"
|
|
||||||
>
|
|
||||||
INSERT SCRAP
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{phase === "playing" && state?.status === "playing" && (
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
onClick={() => startTransition(onHit)}
|
|
||||||
className="rounded bg-green-700 px-4 py-2"
|
|
||||||
>
|
|
||||||
HIT
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => startTransition(onStand)}
|
|
||||||
className="rounded bg-red-700 px-4 py-2"
|
|
||||||
>
|
|
||||||
STAND
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,84 +0,0 @@
|
|||||||
"use server";
|
|
||||||
|
|
||||||
import crypto from "crypto";
|
|
||||||
import {
|
|
||||||
createDeck,
|
|
||||||
shuffle,
|
|
||||||
calculateHand,
|
|
||||||
type Card,
|
|
||||||
} from "~/lib/blackjack/engine";
|
|
||||||
|
|
||||||
// In-memory storage (replace with DB/Redis in production)
|
|
||||||
const games = new Map<string, any>();
|
|
||||||
|
|
||||||
export async function startGame(bet: number) {
|
|
||||||
const deck = shuffle(createDeck());
|
|
||||||
|
|
||||||
const player: Card[] = [deck.pop()!, deck.pop()!];
|
|
||||||
const dealer: Card[] = [deck.pop()!, deck.pop()!];
|
|
||||||
|
|
||||||
const gameId = crypto.randomUUID();
|
|
||||||
|
|
||||||
games.set(gameId, { deck, player, dealer, bet, status: "playing" });
|
|
||||||
|
|
||||||
return {
|
|
||||||
gameId,
|
|
||||||
player,
|
|
||||||
dealerUpcard: [dealer[0]],
|
|
||||||
playerTotal: calculateHand(player),
|
|
||||||
status: "playing",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function hit(gameId: string) {
|
|
||||||
const game = games.get(gameId);
|
|
||||||
if (!game) throw new Error("Game not found");
|
|
||||||
|
|
||||||
const card = game.deck.pop();
|
|
||||||
game.player.push(card);
|
|
||||||
|
|
||||||
const playerTotal = calculateHand(game.player);
|
|
||||||
if (playerTotal > 21) game.status = "bust";
|
|
||||||
|
|
||||||
return {
|
|
||||||
player: game.player,
|
|
||||||
playerTotal,
|
|
||||||
status: game.status,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function stand(gameId: string) {
|
|
||||||
const game = games.get(gameId);
|
|
||||||
if (!game) throw new Error("Game not found");
|
|
||||||
|
|
||||||
while (calculateHand(game.dealer) < 17) {
|
|
||||||
game.dealer.push(game.deck.pop());
|
|
||||||
}
|
|
||||||
|
|
||||||
const playerTotal = calculateHand(game.player);
|
|
||||||
const dealerTotal = calculateHand(game.dealer);
|
|
||||||
|
|
||||||
let result: "win" | "lose" | "push" = "lose";
|
|
||||||
if (dealerTotal > 21 || playerTotal > dealerTotal) result = "win";
|
|
||||||
if (playerTotal === dealerTotal) result = "push";
|
|
||||||
|
|
||||||
// TODO: update user balance here
|
|
||||||
|
|
||||||
games.delete(gameId);
|
|
||||||
|
|
||||||
return {
|
|
||||||
dealer: game.dealer,
|
|
||||||
dealerTotal,
|
|
||||||
result,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function revealDealer(gameId: string) {
|
|
||||||
const game = games.get(gameId);
|
|
||||||
if (!game) throw new Error("Game not found");
|
|
||||||
|
|
||||||
return {
|
|
||||||
dealer: game.dealer,
|
|
||||||
dealerTotal: calculateHand(game.dealer),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
import BlackjackClient from "./BlackjackClient";
|
|
||||||
|
|
||||||
export default function BlackjackPage() {
|
|
||||||
return (
|
|
||||||
<div className="space-y-4 p-6">
|
|
||||||
<h1 className="text-3xl font-bold">Blackjack</h1>
|
|
||||||
<BlackjackClient />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -43,9 +43,7 @@ type Props = {
|
|||||||
export default function HomeClient({ session }: Props) {
|
export default function HomeClient({ session }: Props) {
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const [userData, setUserData] = useState<UserResponse | null>(null);
|
const [userData, setUserData] = useState<UserResponse | null>(null);
|
||||||
const [sellableData, setSellableData] = useState<SellableResponse | null>(
|
const [sellableData, setSellableData] = useState<SellableResponse[]>([]);
|
||||||
null,
|
|
||||||
);
|
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
// Fetch /api/user once and store globally here
|
// Fetch /api/user once and store globally here
|
||||||
@ -68,7 +66,7 @@ export default function HomeClient({ session }: Props) {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
const res = await fetch("/api/sellable");
|
const res = await fetch("/api/sellable");
|
||||||
if (!res.ok) throw new Error("Failed to fetch sellable");
|
if (!res.ok) throw new Error("Failed to fetch sellable");
|
||||||
const data = (await res.json()) as SellableResponse;
|
const data = (await res.json()) as SellableResponse[];
|
||||||
setSellableData(data);
|
setSellableData(data);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|||||||
@ -37,7 +37,7 @@ export default function AccountButton({ loading }: Props) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await res.json();
|
const data = (await res.json()) as UserData;
|
||||||
|
|
||||||
setUserData({
|
setUserData({
|
||||||
adresses: data.adresses ?? [],
|
adresses: data.adresses ?? [],
|
||||||
@ -83,7 +83,7 @@ export default function AccountButton({ loading }: Props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const res = await saveAll(next);
|
const res = await saveAll(next);
|
||||||
if (!res || !res.ok) {
|
if (!res?.ok) {
|
||||||
console.error("Failed to add address");
|
console.error("Failed to add address");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -99,7 +99,7 @@ export default function AccountButton({ loading }: Props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const res = await saveAll(next);
|
const res = await saveAll(next);
|
||||||
if (!res || !res.ok) {
|
if (!res?.ok) {
|
||||||
console.error("Failed to remove address");
|
console.error("Failed to remove address");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ export default function AccountButton({ loading }: Props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const res = await saveAll(next);
|
const res = await saveAll(next);
|
||||||
if (!res || !res.ok) {
|
if (!res?.ok) {
|
||||||
console.error("Failed to add shop");
|
console.error("Failed to add shop");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -136,7 +136,7 @@ export default function AccountButton({ loading }: Props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const res = await saveAll(next);
|
const res = await saveAll(next);
|
||||||
if (!res || !res.ok) {
|
if (!res?.ok) {
|
||||||
console.error("Failed to remove shop");
|
console.error("Failed to remove shop");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import type { CartItem } from "generated/prisma";
|
|
||||||
import { IMAGES_MANIFEST } from "next/dist/shared/lib/constants";
|
|
||||||
import { useEffect, useState, useCallback } from "react";
|
import { useEffect, useState, useCallback } from "react";
|
||||||
|
|
||||||
type Cart = {
|
type Cart = {
|
||||||
@ -202,12 +200,12 @@ export default function CartButton({
|
|||||||
[dragging],
|
[dragging],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleMouseRelease = useCallback(async () => {
|
const handleMouseRelease = useCallback(() => {
|
||||||
if (dragging) {
|
if (dragging) {
|
||||||
const { itemId, quantity } = dragging;
|
const { itemId, quantity } = dragging;
|
||||||
setDragging(null);
|
setDragging(null);
|
||||||
setSliderActive(null);
|
setSliderActive(null);
|
||||||
await removeItem(itemId, quantity);
|
void removeItem(itemId, quantity);
|
||||||
}
|
}
|
||||||
}, [dragging]);
|
}, [dragging]);
|
||||||
|
|
||||||
@ -370,7 +368,12 @@ export default function CartButton({
|
|||||||
cartItems.length === 0
|
cartItems.length === 0
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
void buyItems(cartItems, selectedAddress);
|
void buyItems(cartItems, selectedAddress).then(async () => {
|
||||||
|
setCartItems([]);
|
||||||
|
setTotalQuantity(0);
|
||||||
|
setIsOpen(false);
|
||||||
|
await reloadUser();
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
disabled={buyDisabled}
|
disabled={buyDisabled}
|
||||||
>
|
>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user