Commit 15bfac34 by Arjun Jhukal

updated the layout for buy credit

parent 18ed2582
import DashboardLayout from '@/components/layouts/DashboardLayout'
import Private from '@/routes/Private' import Private from '@/routes/Private'
import React from 'react' import React from 'react'
export default function AdminLayout({ children }: { children: React.ReactNode }) { export default function AdminLayout({ children }: { children: React.ReactNode }) {
return ( return (
<Private> <Private>
{children} <DashboardLayout> {children}</DashboardLayout>
</Private> </Private>
) )
} }
...@@ -10,8 +10,8 @@ export default function AddGeneralPage() { ...@@ -10,8 +10,8 @@ export default function AddGeneralPage() {
<> <>
<OutlinedInput placeholder='Page Title' /> <OutlinedInput placeholder='Page Title' />
<OutlinedInput placeholder='Slug' /> <OutlinedInput placeholder='Slug' />
{Array.from({ length: pageField }).map((i) => ( {Array.from({ length: pageField }).map((_, i) => (
<div className="grid lg:grid-cols-12 gap-4"> <div className="grid lg:grid-cols-12 gap-4" key={i}>
<div className="col-span-3"> <div className="col-span-3">
<OutlinedInput placeholder='Heading' /> <OutlinedInput placeholder='Heading' />
</div> </div>
......
import BitCoinIcon from '@/icons/BitCoinIcon';
import GoldCoinIcon from '@/icons/GoldCoinIcon';
import { Box, Button } from '@mui/material';
import Image from 'next/image';
import React from 'react'
interface Props {
searchParams: { amount?: string; bonus?: string };
params: { slug: string };
}
export default async function CheckoutPage(props: Promise<Props>) {
const { searchParams, params } = await props;
const amount = searchParams.amount ? Number(searchParams.amount) : 0;
const bonus = searchParams.bonus ? Number(searchParams.bonus) : 0;
const slug = params.slug;
return (
<section className="checkout__root">
<div className="grid grid-cols-12 gap-4 lg:gap-10 xl:gap-12">
<div className="col-span-12 lg:col-span-4 ">
<Box className="coin__card" sx={{
borderRadius: "16px",
background: "linear-gradient(0deg, rgba(0, 0, 0, 0.20) 0%, rgba(0, 0, 0, 0.20) 100%), rgba(255, 255, 255, 0.10)",
padding: "16px"
}}>
<div className="title">
<h2 className='text-[28px]'>${amount}</h2>
</div>
<div className="footer mt-10">
<div className="coin-info flex justify-between items-center py-3 px-4"
style={{
borderRadius: "16px",
background: "linear-gradient(0deg, rgba(234, 47, 231, 0.10) 0%, rgba(234, 47, 231, 0.10) 100%), rgba(255, 255, 255, 0.10)"
}}
>
<div className="coin flex items-center gap-1">
<GoldCoinIcon />
<span className='text-[12px]'>Gold Coins</span>
</div>
<p>
<strong className='text-[16px] block'>500</strong>
</p>
</div>
<div className="coin-info flex justify-between items-center py-3 px-4 mt-1"
style={{
borderRadius: "16px",
background: "linear-gradient(0deg, rgba(234, 47, 231, 0.10) 0%, rgba(234, 47, 231, 0.10) 100%), rgba(255, 255, 255, 0.10)"
}}
>
<div className="coin flex items-center gap-1">
<GoldCoinIcon />
<span className='text-[12px]'>Bonus Coins</span>
</div>
<p>
<strong className='text-[16px] block'>{bonus}</strong>
</p>
</div>
</div>
</Box>
</div>
<div className="col-span-12 lg:col-span-8">
<Box>
<h1 className='mb-2 text-[24px] lg:text-[32px]'>Payment Method</h1>
<p className='text-[11px] lg:text-[13px]'>To start playing and cashing out your winnings, you’ll need a crypto wallet to purchase E-Credits and receive payouts. Don't worry—it’s quick and easy!</p>
<h2 className='text-[20px] lg:text-[24px] mt-8 mb-4'>Select payment method</h2>
<div className="grid sm:grid-cols-2 mb-8">
<div className="col-span-1">
<div className="py-5 px-4 rounded-[8px]" style={{
borderRadius: "8px",
background: "linear-gradient(0deg, rgba(234, 47, 231, 0.10) 0%, rgba(234, 47, 231, 0.10) 100%), rgba(255, 255, 255, 0.10)",
}}>
<span className="text-[14px] flex items-center justify-start gap-2"><BitCoinIcon />Crypto Currency</span>
</div>
</div>
</div>
<Button type='submit' variant='contained' color='primary' className='!mt-3' >Proceed to Payment</Button>
<p className="text-[11px] leading-[120%] mt-8 mb-2 text-center">Powered By</p>
<div className="flex justify-center items-center gap-4">
<Image src="/assets/images/payment-01.png" alt='' width={78} height={24} />
<Image src="/assets/images/payment-02.png" alt='' width={78} height={24} />
<Image src="/assets/images/payment-03.png" alt='' width={78} height={24} />
</div>
</Box>
</div>
</div>
</section>
)
}
import BuyCoinSinlgeGame from '@/components/pages/dashboard/userDashboard/buyCoins/buyCoinSinlgeGame'
import React from 'react' import React from 'react'
export default function SingleGameCoinPacks() { export default async function SingleGameCoinPacks(props: { params: Promise<{ slug: string }> }) {
const { slug } = await props.params;
return ( return (
<div>SingleGameCoinPacks</div> <BuyCoinSinlgeGame slug={slug} />
) )
} }
import BuyCoinGameListPage from '@/components/pages/dashboard/userDashboard/buyCoins'
import { getAllGames, getUserGameBalance } from '@/serverApi/game';
import React from 'react' import React from 'react'
export default function BuyCoins() { export default async function BuyCoins() {
const games = await getAllGames();
const coins = await getUserGameBalance();
return ( return (
<div>BuyCoins</div> <BuyCoinGameListPage games={games} coins={coins} />
) )
} }
import type { Metadata } from "next"; import type { Metadata } from "next";
import { Geist, Geist_Mono, Inter } from "next/font/google"; import { Inter } from "next/font/google";
import "./globals.css"; import "./globals.css";
import ProviderWrapper from "./ProviderWrapper"; import ProviderWrapper from "./ProviderWrapper";
......
...@@ -79,7 +79,7 @@ export default function InputFile({ ...@@ -79,7 +79,7 @@ export default function InputFile({
cursor: "pointer", cursor: "pointer",
}} }}
/> />
<span className=" absolute left-1 top-1/2 translate-y-[-50%] text-[11px] text-title bg-[#D8D8DD] inline-block py-1 px-2 z-[-1] rounded-sm">Choose File</span> <span className=" absolute left-2 top-1/2 translate-y-[-50%] text-[11px] text-title bg-[#D8D8DD] inline-block py-1 px-2 z-[-1] rounded-sm">Choose File</span>
</div> </div>
{/* Hidden file input */} {/* Hidden file input */}
......
"use client";
import GoldCoinIcon from '@/icons/GoldCoinIcon';
import { Box, Button, OutlinedInput } from '@mui/material';
import { Coin } from '@wandersonalwes/iconsax-react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import React, { useState } from 'react';
export default function CoinCalculator() {
const [amount, setAmount] = useState<number | "">("");
const [baseCoins, setBaseCoins] = useState<number | null>(null);
const [bonusCoins, setBonusCoins] = useState<number | null>(null);
const router = useRouter();
const calculateBonus = (amount: number) => {
return Math.max(Math.round(25.56 * amount - 27.78), 0);
};
const handleCalculate = () => {
if (amount && amount > 0) {
const base = amount * 100;
const bonus = calculateBonus(amount);
setBaseCoins(base);
setBonusCoins(bonus);
} else {
setBaseCoins(null);
setBonusCoins(null);
}
};
const handleEdit = () => {
setAmount("");
setBaseCoins(null);
setBonusCoins(null);
};
const handleBuy = () => {
if (baseCoins !== null && bonusCoins !== null) {
router.push(`/checkout/buy-coins/1?amount=${baseCoins}&bonus=${bonusCoins}`);
}
};
return (
<Box className="coin__card" sx={{
borderRadius: "16px",
background: "linear-gradient(0deg, rgba(0, 0, 0, 0.20) 0%, rgba(0, 0, 0, 0.20) 100%), rgba(255, 255, 255, 0.10)",
padding: "16px",
height: "100%",
display: "flex",
flexDirection: "column",
justifyContent: "space-between"
}}>
<div className="title">
<h2 className='text-[28px]'>Custom</h2>
<span className='text-[12px]'>$1 = 100 Gold Coins</span>
</div>
<div className="footer">
{baseCoins && baseCoins > 0 ? (
<>
<div className="coin-info mt-10 flex justify-between items-center py-3 px-4 mb-4"
style={{
borderRadius: "16px",
background: "linear-gradient(0deg, rgba(234, 47, 231, 0.10) 0%, rgba(234, 47, 231, 0.10) 100%), rgba(255, 255, 255, 0.10)"
}}
>
<div className="coin flex gap-1">
<GoldCoinIcon />
<p>
<strong className='text-[16px] block'>{baseCoins}</strong>
<span className='text-[12px]'>Gold Coins</span>
</p>
</div>
<div className="bonus">
<strong className='text-[16px] block'>+{bonusCoins}</strong>
<span className='text-[12px]'>bonus</span>
</div>
</div>
{/* Navigate dynamically using router.push */}
<Button
variant="contained"
color="primary"
className='ss-btn bg-primary-grad !text-white flex justify-center items-center'
onClick={handleBuy}
>
<Coin /> Buy Coins
</Button>
<Button variant="contained" color="secondary" className='!mt-4' onClick={handleEdit}>
Edit Amount
</Button>
</>
) : (
<>
<OutlinedInput
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value === "" ? "" : Number(e.target.value))}
placeholder="Enter amount"
/>
<Button variant="contained" color="primary" className='!mt-4' onClick={handleCalculate}>
Calculate
</Button>
</>
)}
</div>
</Box>
);
}
import React from 'react'
export default function CheckoutPage() {
return (
<div>CheckoutPage</div>
)
}
import GoldCoinIcon from '@/icons/GoldCoinIcon'
import { Box, Button, OutlinedInput } from '@mui/material'
import { Coin } from '@wandersonalwes/iconsax-react'
import Link from 'next/link'
import React from 'react' import React from 'react'
import CoinCalculator from './CoinCalculator'
export default function BuyCoinSinlgeGame() { export default function BuyCoinSinlgeGame({ slug }: { slug: string }) {
const packs = [
{
amount: "5",
label: "Starter Pack",
coin: "500",
bonus: "100",
tag: "popular"
},
{
amount: "20",
label: "Golden Pack",
coin: "2000",
bonus: "450",
},
{
amount: "50",
label: "Legend Pack",
coin: "5000",
bonus: "1250",
},
]
return ( return (
<div>BuyCoinSinlgeGame</div> <section className="buy__coin__root">
<div className="section__title mb-4 lg:mb-8 max-w-[520px]">
<h1 className='mb-2 text-[24px] lg:text-[32px]'>Buy Panda Master Coins</h1>
<p className='text-[11px] lg:text-[13px]'>To start playing and cashing out your winnings, you’ll need a crypto wallet to purchase E-Credits and receive payouts. Don't worry—it’s quick and easy!</p>
</div>
<div className="grid grid-col-1 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-4">
{
packs.map((pack) => (
<div className="col-span-1 h-full" key={pack.label}>
<Box className="coin__card" sx={{
borderRadius: "16px",
background: "linear-gradient(0deg, rgba(0, 0, 0, 0.20) 0%, rgba(0, 0, 0, 0.20) 100%), rgba(255, 255, 255, 0.10)",
padding: "16px"
}}>
<div className="title">
<h2 className='text-[28px]'>${pack.amount}</h2>
<span className='text-[12px]'>{pack.label}</span>
</div>
<div className="footer">
<div className="coin-info mt-10 flex justify-between items-center py-3 px-4 mb-4"
style={{
borderRadius: "16px",
background: "linear-gradient(0deg, rgba(234, 47, 231, 0.10) 0%, rgba(234, 47, 231, 0.10) 100%), rgba(255, 255, 255, 0.10)"
}}
>
<div className="coin flex gap-1">
<GoldCoinIcon />
<p>
<strong className='text-[16px] block'>{pack.coin}</strong>
<span className='text-[12px]'>Gold Coins</span>
</p>
</div>
<div className="bonus">
<strong className='text-[16px] block'>+{pack.bonus}</strong>
<span className='text-[12px]'>bonus</span>
</div>
</div>
<Link href={`/buy-coins/${slug}/checkout?amount=${pack.amount}&bonus=${pack.bonus}`} className='ss-btn bg-primary-grad !text-white flex justify-center items-center'><Coin />Buy Coins</Link>
</div>
</Box>
</div>
))
}
<div className="col-span-1 h-full">
<CoinCalculator />
</div>
</div >
</section >
) )
} }
import GoldCoinIcon from '@/icons/GoldCoinIcon'
import SilverCoinIcon from '@/icons/SilverCoinIcon'
import { GameResponseProps } from '@/types/game'
import { Button } from '@mui/material'
import { Coin } from '@wandersonalwes/iconsax-react'
import Image from 'next/image'
import Link from 'next/link'
import React from 'react' import React from 'react'
export default function BuyCoinGameListPage() { export default function BuyCoinGameListPage({
games,
coins,
}: {
games: GameResponseProps
coins: any
}) {
const gameInfo = coins?.data?.game_information || {}
return ( return (
<div>BuyCoinGameListPage</div> <section className="buy__coin__root">
<div className="section__title mb-4 lg:mb-8 max-w-[520px]">
<h1 className="mb-2 text-[24px] lg:text-[32px]">Buy Coins</h1>
<p className="text-[11px] lg:text-[13px]">
To start playing and cashing out your winnings, you’ll need a crypto wallet to purchase E-Credits and receive payouts. Don't worry—it’s quick and easy!
</p>
</div>
<div className="grid grid-col-1 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-4">
{games.data?.data.map((game) => {
const info = gameInfo[game.provider.toLowerCase()] || { balance: 0, type: 'sc' }
const CoinIcon = info.type === 'gc' ? GoldCoinIcon : SilverCoinIcon
return (
<div key={game.id} className="col-span-1">
<div
className="coin__card px-6 py-4"
style={{
borderRadius: '24px',
background:
'linear-gradient(0deg, rgba(0, 0, 0, 0.20) 0%, rgba(0, 0, 0, 0.20) 100%), rgba(255, 255, 255, 0.10)',
}}
>
<div className="coin__detail">
<div className="flex gap-4 items-center mb-4">
<Image
src={game.thumbnail || '/assets/images/fallback.png'}
alt={game.name}
width={72}
height={72}
className="rounded-full aspect-square"
/>
<div className="game-content">
<strong className="text-[16px] text-white block mb-2">{game?.name}</strong>
<ul className="flex gap-4">
<li className="flex gap-1 items-center">
<CoinIcon />
<span className="text-[12px] font-[600]">{info.balance}</span>
</li>
</ul>
</div>
</div>
</div>
<Link href={`buy-coins/${game.id}`}
className="ss-btn bg-primary-grad !text-white flex justify-center gap-1"
>
<Coin /> Buy Coins
</Link>
</div>
</div>
)
})}
</div>
</section>
) )
} }
...@@ -46,7 +46,9 @@ export default function ExlusiveGameDetail({ game }: { game: SingleGameResponse ...@@ -46,7 +46,9 @@ export default function ExlusiveGameDetail({ game }: { game: SingleGameResponse
borderRadius: "16px" borderRadius: "16px"
}} className="flex justify-center items-center gap-2 py-4 px-6 bg-secondary-grad text-title min-w-[30%] "> }} className="flex justify-center items-center gap-2 py-4 px-6 bg-secondary-grad text-title min-w-[30%] ">
<div className="coins"> <div className="coins">
<strong className="text-[16px] leading-4 font-[600] block mb-1">+ Deposit Coins</strong> <Link href={"/buy-coins"}>
<strong className="text-[16px] leading-4 font-[600] block mb-1">+ Deposit Coins</strong>
</Link>
</div> </div>
</Box> </Box>
<Box sx={{ <Box sx={{
......
import React from 'react'
export default function BitCoinIcon() {
return (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5009_5424)">
<path d="M17.25 24C20.9779 24 24 20.9779 24 17.25C24 13.5221 20.9779 10.5 17.25 10.5C13.5221 10.5 10.5 13.5221 10.5 17.25C10.5 20.9779 13.5221 24 17.25 24Z" fill="#EA9706" />
<path d="M17.25 22.5C20.1495 22.5 22.5 20.1495 22.5 17.25C22.5 14.3505 20.1495 12 17.25 12C14.3505 12 12 14.3505 12 17.25C12 20.1495 14.3505 22.5 17.25 22.5Z" fill="#FFAC00" />
<path d="M17.625 20.625H16.875C16.3777 20.625 15.9008 20.4275 15.5492 20.0758C15.1975 19.7242 15 19.2473 15 18.75C15 18.6505 15.0395 18.5552 15.1098 18.4848C15.1802 18.4145 15.2755 18.375 15.375 18.375C15.4745 18.375 15.5698 18.4145 15.6402 18.4848C15.7105 18.5552 15.75 18.6505 15.75 18.75C15.75 19.0484 15.8685 19.3345 16.0795 19.5455C16.2905 19.7565 16.5766 19.875 16.875 19.875H17.625C17.9234 19.875 18.2095 19.7565 18.4205 19.5455C18.6315 19.3345 18.75 19.0484 18.75 18.75C18.75 18.4516 18.6315 18.1655 18.4205 17.9545C18.2095 17.7435 17.9234 17.625 17.625 17.625H16.875C16.3777 17.625 15.9008 17.4275 15.5492 17.0758C15.1975 16.7242 15 16.2473 15 15.75C15 15.2527 15.1975 14.7758 15.5492 14.4242C15.9008 14.0725 16.3777 13.875 16.875 13.875H17.625C18.1223 13.875 18.5992 14.0725 18.9508 14.4242C19.3025 14.7758 19.5 15.2527 19.5 15.75C19.5 15.8495 19.4605 15.9448 19.3902 16.0152C19.3198 16.0855 19.2245 16.125 19.125 16.125C19.0255 16.125 18.9302 16.0855 18.8598 16.0152C18.7895 15.9448 18.75 15.8495 18.75 15.75C18.75 15.4516 18.6315 15.1655 18.4205 14.9545C18.2095 14.7435 17.9234 14.625 17.625 14.625H16.875C16.5766 14.625 16.2905 14.7435 16.0795 14.9545C15.8685 15.1655 15.75 15.4516 15.75 15.75C15.75 16.0484 15.8685 16.3345 16.0795 16.5455C16.2905 16.7565 16.5766 16.875 16.875 16.875H17.625C18.1223 16.875 18.5992 17.0725 18.9508 17.4242C19.3025 17.7758 19.5 18.2527 19.5 18.75C19.5 19.2473 19.3025 19.7242 18.9508 20.0758C18.5992 20.4275 18.1223 20.625 17.625 20.625Z" fill="#F3F3F3" />
<path d="M17.25 21.375C17.1505 21.375 17.0552 21.3355 16.9848 21.2652C16.9145 21.1948 16.875 21.0995 16.875 21V13.5C16.875 13.4005 16.9145 13.3052 16.9848 13.2348C17.0552 13.1645 17.1505 13.125 17.25 13.125C17.3495 13.125 17.4448 13.1645 17.5152 13.2348C17.5855 13.3052 17.625 13.4005 17.625 13.5V21C17.625 21.0995 17.5855 21.1948 17.5152 21.2652C17.4448 21.3355 17.3495 21.375 17.25 21.375Z" fill="#F3F3F3" />
<path d="M12 18.75C15.7279 18.75 18.75 15.7279 18.75 12C18.75 8.27208 15.7279 5.25 12 5.25C8.27208 5.25 5.25 8.27208 5.25 12C5.25 15.7279 8.27208 18.75 12 18.75Z" fill="#00BEBD" />
<path d="M12 17.25C14.8995 17.25 17.25 14.8995 17.25 12C17.25 9.10051 14.8995 6.75 12 6.75C9.10051 6.75 6.75 9.10051 6.75 12C6.75 14.8995 9.10051 17.25 12 17.25Z" fill="#50D9D7" />
<path d="M11.9999 13.1249C11.9137 13.1249 12.0862 13.1737 9.62992 12.3749C9.57559 12.3564 9.52625 12.3257 9.48568 12.2851C9.44512 12.2445 9.41441 12.1951 9.39594 12.1407C9.37746 12.0864 9.37171 12.0285 9.37911 11.9716C9.38652 11.9147 9.4069 11.8602 9.43867 11.8124L11.6887 8.43744C11.723 8.3864 11.7693 8.34457 11.8236 8.31565C11.8779 8.28673 11.9384 8.27161 11.9999 8.27161C12.0614 8.27161 12.122 8.28673 12.1763 8.31565C12.2305 8.34457 12.2769 8.3864 12.3112 8.43744L14.5612 11.8124C14.5929 11.8602 14.6133 11.9147 14.6207 11.9716C14.6281 12.0285 14.6224 12.0864 14.6039 12.1407C14.5854 12.1951 14.5547 12.2445 14.5142 12.2851C14.4736 12.3257 14.4242 12.3564 14.3699 12.3749C11.9287 13.1699 12.0862 13.1249 11.9999 13.1249ZM10.3349 11.8012L11.9999 12.3749L13.6649 11.8199L11.9999 9.29994L10.3349 11.8012Z" fill="white" />
<path d="M11.6884 15.5813L9.43843 12.2063C9.4057 12.1657 9.38181 12.1187 9.36828 12.0683C9.35476 12.0179 9.3519 11.9652 9.35989 11.9136C9.36788 11.8621 9.38655 11.8128 9.41469 11.7688C9.44283 11.7249 9.47983 11.6873 9.52333 11.6585C9.56682 11.6297 9.61585 11.6102 9.66728 11.6014C9.71872 11.5926 9.77142 11.5947 9.82202 11.6074C9.87261 11.6202 9.92 11.6433 9.96114 11.6754C10.0023 11.7075 10.0363 11.7478 10.0609 11.7938L11.9997 14.7001L13.9384 11.7938C13.9631 11.7478 13.9971 11.7075 14.0382 11.6754C14.0794 11.6433 14.1267 11.6202 14.1773 11.6074C14.2279 11.5947 14.2806 11.5926 14.3321 11.6014C14.3835 11.6102 14.4325 11.6297 14.476 11.6585C14.5195 11.6873 14.5565 11.7249 14.5847 11.7688C14.6128 11.8128 14.6315 11.8621 14.6395 11.9136C14.6475 11.9652 14.6446 12.0179 14.6311 12.0683C14.6176 12.1187 14.5937 12.1657 14.5609 12.2063L12.3109 15.5813C12.2766 15.6324 12.2303 15.6742 12.176 15.7031C12.1217 15.732 12.0612 15.7471 11.9997 15.7471C11.9382 15.7471 11.8776 15.732 11.8233 15.7031C11.7691 15.6742 11.7227 15.6324 11.6884 15.5813Z" fill="white" />
<path d="M6.75 13.5C10.4779 13.5 13.5 10.4779 13.5 6.75C13.5 3.02208 10.4779 0 6.75 0C3.02208 0 0 3.02208 0 6.75C0 10.4779 3.02208 13.5 6.75 13.5Z" fill="#EA9706" />
<path d="M6.75 12C9.6495 12 12 9.6495 12 6.75C12 3.85051 9.6495 1.5 6.75 1.5C3.85051 1.5 1.5 3.85051 1.5 6.75C1.5 9.6495 3.85051 12 6.75 12Z" fill="#FFAC00" />
<path d="M7.3125 7.125H5.25C5.15054 7.125 5.05516 7.08549 4.98484 7.01517C4.91451 6.94484 4.875 6.84946 4.875 6.75V4.125C4.875 4.02554 4.91451 3.93016 4.98484 3.85984C5.05516 3.78951 5.15054 3.75 5.25 3.75H7.3125C7.76005 3.75 8.18928 3.92779 8.50574 4.24426C8.82221 4.56073 9 4.98995 9 5.4375C9 5.88505 8.82221 6.31428 8.50574 6.63074C8.18928 6.94721 7.76005 7.125 7.3125 7.125ZM5.625 6.375H7.3125C7.56114 6.375 7.7996 6.27623 7.97541 6.10041C8.15123 5.9246 8.25 5.68614 8.25 5.4375C8.25 5.18886 8.15123 4.9504 7.97541 4.77459C7.7996 4.59877 7.56114 4.5 7.3125 4.5H5.625V6.375Z" fill="#F3F3F3" />
<path d="M7.6875 9.75H5.25C5.15054 9.75 5.05516 9.71049 4.98484 9.64017C4.91451 9.56984 4.875 9.47446 4.875 9.375V6.75C4.875 6.65054 4.91451 6.55516 4.98484 6.48483C5.05516 6.41451 5.15054 6.375 5.25 6.375H7.6875C8.13505 6.375 8.56428 6.55279 8.88074 6.86926C9.19721 7.18573 9.375 7.61495 9.375 8.0625C9.375 8.51005 9.19721 8.93928 8.88074 9.25574C8.56428 9.57221 8.13505 9.75 7.6875 9.75ZM5.625 9H7.6875C7.93614 9 8.1746 8.90123 8.35041 8.72541C8.52623 8.5496 8.625 8.31114 8.625 8.0625C8.625 7.81386 8.52623 7.5754 8.35041 7.39959C8.1746 7.22377 7.93614 7.125 7.6875 7.125H5.625V9Z" fill="#F3F3F3" />
<path d="M6 4.5C5.90054 4.5 5.80516 4.46049 5.73484 4.39016C5.66451 4.31984 5.625 4.22446 5.625 4.125V3.375C5.625 3.27554 5.66451 3.18016 5.73484 3.10984C5.80516 3.03951 5.90054 3 6 3C6.09946 3 6.19484 3.03951 6.26517 3.10984C6.33549 3.18016 6.375 3.27554 6.375 3.375V4.125C6.375 4.22446 6.33549 4.31984 6.26517 4.39016C6.19484 4.46049 6.09946 4.5 6 4.5Z" fill="#F3F3F3" />
<path d="M7.5 4.5C7.40054 4.5 7.30516 4.46049 7.23483 4.39016C7.16451 4.31984 7.125 4.22446 7.125 4.125V3.375C7.125 3.27554 7.16451 3.18016 7.23483 3.10984C7.30516 3.03951 7.40054 3 7.5 3C7.59946 3 7.69484 3.03951 7.76517 3.10984C7.83549 3.18016 7.875 3.27554 7.875 3.375V4.125C7.875 4.22446 7.83549 4.31984 7.76517 4.39016C7.69484 4.46049 7.59946 4.5 7.5 4.5Z" fill="#F3F3F3" />
<path d="M7.5 10.5C7.40054 10.5 7.30516 10.4605 7.23483 10.3902C7.16451 10.3198 7.125 10.2245 7.125 10.125V9.375C7.125 9.27554 7.16451 9.18016 7.23483 9.10983C7.30516 9.03951 7.40054 9 7.5 9C7.59946 9 7.69484 9.03951 7.76517 9.10983C7.83549 9.18016 7.875 9.27554 7.875 9.375V10.125C7.875 10.2245 7.83549 10.3198 7.76517 10.3902C7.69484 10.4605 7.59946 10.5 7.5 10.5Z" fill="#F3F3F3" />
<path d="M6 10.5C5.90054 10.5 5.80516 10.4605 5.73484 10.3902C5.66451 10.3198 5.625 10.2245 5.625 10.125V9.375C5.625 9.27554 5.66451 9.18016 5.73484 9.10983C5.80516 9.03951 5.90054 9 6 9C6.09946 9 6.19484 9.03951 6.26517 9.10983C6.33549 9.18016 6.375 9.27554 6.375 9.375V10.125C6.375 10.2245 6.33549 10.3198 6.26517 10.3902C6.19484 10.4605 6.09946 10.5 6 10.5Z" fill="#F3F3F3" />
<path d="M5.25 4.5H4.5C4.40054 4.5 4.30516 4.46049 4.23484 4.39016C4.16451 4.31984 4.125 4.22446 4.125 4.125C4.125 4.02554 4.16451 3.93016 4.23484 3.85984C4.30516 3.78951 4.40054 3.75 4.5 3.75H5.25C5.34946 3.75 5.44484 3.78951 5.51516 3.85984C5.58549 3.93016 5.625 4.02554 5.625 4.125C5.625 4.22446 5.58549 4.31984 5.51516 4.39016C5.44484 4.46049 5.34946 4.5 5.25 4.5Z" fill="#F3F3F3" />
<path d="M5.25 9.75H4.5C4.40054 9.75 4.30516 9.71049 4.23484 9.64017C4.16451 9.56984 4.125 9.47446 4.125 9.375C4.125 9.27554 4.16451 9.18016 4.23484 9.10983C4.30516 9.03951 4.40054 9 4.5 9H5.25C5.34946 9 5.44484 9.03951 5.51516 9.10983C5.58549 9.18016 5.625 9.27554 5.625 9.375C5.625 9.47446 5.58549 9.56984 5.51516 9.64017C5.44484 9.71049 5.34946 9.75 5.25 9.75Z" fill="#F3F3F3" />
</g>
<defs>
<clipPath id="clip0_5009_5424">
<rect width="24" height="24" fill="white" />
</clipPath>
</defs>
</svg>
)
}
...@@ -49,3 +49,5 @@ export default function Private({ children }: { children: React.ReactNode }) { ...@@ -49,3 +49,5 @@ export default function Private({ children }: { children: React.ReactNode }) {
return <>{children}</>; return <>{children}</>;
} }
...@@ -11,7 +11,7 @@ interface Props { ...@@ -11,7 +11,7 @@ interface Props {
} }
export default function ProtectedLink({ href, className, children }: Props) { export default function ProtectedLink({ href, className, children }: Props) {
const user = useAppSelector((s) => s.auth.user); const user = useAppSelector((s) => s ? s.auth.user : "");
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const router = useRouter(); const router = useRouter();
......
"use client";
import { useEffect } from "react";
import { useAppDispatch } from "@/hooks/hook";
import { setTokens } from "@/slice/authSlice";
export default function ReduxHydrator({ token, user }: { token: string; user: any }) {
const dispatch = useAppDispatch();
useEffect(() => {
dispatch(setTokens({ access_token: token, user }));
}, [dispatch, token, user]);
return null;
}
// routes/Private.tsx
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import React from "react";
import ReduxHydrator from "./ReduxHydrator";
function decodeJwt(token: string) {
try {
return JSON.parse(atob(token.split(".")[1]));
} catch {
return null;
}
}
export default async function ServerPrivate({ children }: { children: React.ReactNode }) {
// ✅ Read cookie server-side
const cookieStore = await cookies();
const access_token = cookieStore.get("access_token")?.value;
if (!access_token) redirect("/");
const payload = decodeJwt(access_token);
if (!payload || !payload.exp || payload.exp < Math.floor(Date.now() / 1000)) {
redirect("/");
}
// Optionally, you could fetch user data from your API here if you don't store it in JWT
const user = payload; // Or fetch user profile based on token
return (
<>
{/* ✅ Hydrate Redux store on client */}
<ReduxHydrator token={access_token} user={user} />
{children}
</>
);
}
\ No newline at end of file
...@@ -24,4 +24,13 @@ export async function getUserGameCredentials(): Promise<CredentialsResponseProps ...@@ -24,4 +24,13 @@ export async function getUserGameCredentials(): Promise<CredentialsResponseProps
token: access_token, token: access_token,
withAuth: true, withAuth: true,
}); });
}
export async function getUserGameBalance(): Promise<CredentialsResponseProps> {
const cookieStore = await cookies();
const access_token = cookieStore.get("access_token")?.value;
return serverBaseQuery<CredentialsResponseProps>(`/api/detail/get-balance`, {
token: access_token,
withAuth: true,
});
} }
\ No newline at end of file
...@@ -29,7 +29,8 @@ export const transactionApi = createApi({ ...@@ -29,7 +29,8 @@ export const transactionApi = createApi({
} }
}, },
providesTags: ['transaction'] providesTags: ['transaction']
}) }),
}) })
}) })
......
// utils/auth.ts
import { cookies } from "next/headers";
export async function getServerAuth() {
const cookieStore = await cookies();
const token = cookieStore.get("access_token")?.value;
if (!token) return { isAuthenticated: false, user: null };
try {
const payload = JSON.parse(atob(token.split(".")[1]));
const now = Math.floor(Date.now() / 1000);
if (payload.exp < now) return { isAuthenticated: false, user: null };
return { isAuthenticated: true, user: payload };
} catch (err) {
return { isAuthenticated: false, user: null };
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment