Commit 8515c989 by Arjun Jhukal

initialized the create player layout and game detail page

parent 0af072d4
......@@ -12,10 +12,12 @@
"@emotion/styled": "^11.14.1",
"@mui/material": "^7.3.2",
"@reduxjs/toolkit": "^2.9.0",
"@tanstack/react-table": "^8.21.3",
"@wandersonalwes/iconsax-react": "0.0.10",
"formik": "^2.4.6",
"lightgallery": "^2.9.0-beta.1",
"next": "15.5.3",
"react": "19.1.0",
"react": "^19.1.0",
"react-dom": "19.1.0",
"react-quill-new": "3.4.6",
"react-redux": "^9.2.0",
......@@ -1793,6 +1795,39 @@
"tailwindcss": "4.1.13"
}
},
"node_modules/@tanstack/react-table": {
"version": "8.21.3",
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz",
"integrity": "sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==",
"license": "MIT",
"dependencies": {
"@tanstack/table-core": "8.21.3"
},
"engines": {
"node": ">=12"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/@tanstack/table-core": {
"version": "8.21.3",
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.3.tgz",
"integrity": "sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
}
},
"node_modules/@tybys/wasm-util": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
......@@ -4945,6 +4980,15 @@
"node": ">= 0.8.0"
}
},
"node_modules/lightgallery": {
"version": "2.9.0-beta.1",
"resolved": "https://registry.npmjs.org/lightgallery/-/lightgallery-2.9.0-beta.1.tgz",
"integrity": "sha512-4Bl1vsYGOsI6RsPyephAUFW8bbZuZlLrpxU59cfKYKPDEX5Mhxfo1DUoUIJ+j5QPNrHEqrd+QeQtnGUCCwqK2g==",
"license": "GPLv3",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/lightningcss": {
"version": "1.30.1",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
......
......@@ -13,14 +13,16 @@
"@emotion/styled": "^11.14.1",
"@mui/material": "^7.3.2",
"@reduxjs/toolkit": "^2.9.0",
"@tanstack/react-table": "^8.21.3",
"@wandersonalwes/iconsax-react": "0.0.10",
"formik": "^2.4.6",
"lightgallery": "^2.9.0-beta.1",
"next": "15.5.3",
"react": "19.1.0",
"react": "^19.1.0",
"react-dom": "19.1.0",
"react-quill-new": "3.4.6",
"react-redux": "^9.2.0",
"yup": "^1.7.0",
"@wandersonalwes/iconsax-react": "0.0.10",
"react-quill-new": "3.4.6"
"yup": "^1.7.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
......@@ -33,4 +35,4 @@
"tailwindcss": "^4",
"typescript": "^5"
}
}
\ No newline at end of file
}
import GameDetailPage from '@/components/pages/dashboard/adminDashboard/games/GameDetail'
import React from 'react'
export default function AdminGamePage() {
return (
<div>AdminGamePage</div>
<GameDetailPage />
)
}
"use client";
import PageHeader from '@/components/molecules/PageHeader'
import AddGameForm from '@/components/pages/dashboard/adminDashboard/games/AddGameForm'
import { useParams } from 'next/navigation';
import React from 'react'
export default function EditGame() {
const params = useParams();
const id = params?.id as string;
return (
<>
<PageHeader title="Add Game" />
<AddGameForm />
<AddGameForm id={id} />
</>
)
}
import PageHeader from '@/components/molecules/PageHeader'
import AddPlayerForm from '@/components/pages/dashboard/adminDashboard/players/addPlayerForm'
import React from 'react'
export default function AddPlayer() {
return (
<>
<PageHeader
title='Add Player'
/>
<AddPlayerForm />
</>
)
}
import PageHeader from '@/components/molecules/PageHeader'
import { PATH } from '@/routes/PATH'
import React from 'react'
export default function Players() {
return (
<>
<PageHeader
title='Players'
cta={{ url: PATH.ADMIN.PLAYERS.ADD_PLAYER.ROOT, label: "Add New Player" }}
/>
</>
)
}
......@@ -150,7 +150,14 @@
@apply !bg-white;
}
.MuiInputBase-input {
/* .MuiInputBase-input {
padding: 0 !important;
}
} */
}
.general-content-box p {
font-size: 14px;
font-weight: 400;
line-height: 120%;
color: var(--para-light);
}
\ No newline at end of file
......@@ -10,7 +10,7 @@ interface Option {
interface SelectFieldProps {
name: string;
label: string;
label?: string;
value: string;
onChange: (e: React.ChangeEvent<HTMLSelectElement>) => void;
onBlur?: (e: React.FocusEvent<HTMLSelectElement>) => void;
......
// import React from 'react'
// export default function Pagination() {
// return (
// .pagi
// )
// }
import { Button } from "@mui/material";
import SelectField from "../atom/SelectField";
interface TableHeaderProps {
search: string;
setSearch: (value: string) => void;
filterMethod: string;
setFilterMethod: (value: string) => void;
onDownloadCSV: () => void;
}
export default function TableHeader({
search,
setSearch,
filterMethod,
setFilterMethod,
onDownloadCSV,
}: TableHeaderProps) {
return (
<div className="table__header p-4 mb-4 flex justify-between">
<div className="inpute__field relative">
<input
type="search"
placeholder="Search keywords..."
name="search"
id="search"
value={search}
onChange={(e) => setSearch(e.target.value)}
className="rounded-[8px] border-solid border-[1px] border-gray pl-7 outline-none focus:outline-primary-light text-[14px] focus:border-primary"
/>
</div>
<div className="header-right flex justify-end items-center gap-2">
<SelectField
name="search"
value={filterMethod}
onChange={(e) => setFilterMethod(e.target.value)}
options={[
{ value: "all", label: "All Method" },
{ value: "crypto", label: "Crypto" },
{ value: "paypal", label: "USD/Paypal" },
]}
/>
<Button onClick={onDownloadCSV}>Download CSV</Button>
</div>
</div>
);
}
......@@ -35,7 +35,7 @@ export default function Toast() {
return (
<div
className={`fixed top-4 right-4 flex max-w-sm w-full items-start gap-3 rounded-xl border-l-4 px-4 py-3 shadow-lg transition-all duration-300 animate-in slide-in-from-right
className={`z-[9999] fixed top-4 right-4 flex max-w-sm w-full items-start gap-3 rounded-xl border-l-4 px-4 py-3 shadow-lg transition-all duration-300 animate-in slide-in-from-right
data-[state=closed]:slide-out-to-right data-[state=closed]:fade-out
${variantStyles[currentVariant]}`}
>
......
......@@ -9,7 +9,7 @@ export default function AdminSearchBar() {
<Box>
<div className="inpute__field relative">
<input type="search" placeholder="Ctrl + K" name="search" id="search" className="rounded-[4px] border-solid border-[1px] border-gray pl-5 outline-none focus:outline-primary-light" />
<SearchNormal color="#71717A" size={18} className="absolute left-2 top-1/2 transform -translate-y-1/2" />
<SearchNormal color="#71717A" size={16} className="absolute left-2 top-1/2 transform -translate-y-1/2" />
</div>
</Box>
)
......
"use client";
import Image from "next/image";
import LightGallery from "lightgallery/react";
import lgZoom from "lightgallery/plugins/zoom";
import lgThumbnail from "lightgallery/plugins/thumbnail";
import "lightgallery/css/lightgallery.css";
import "lightgallery/css/lg-zoom.css";
import "lightgallery/css/lg-thumbnail.css";
interface CustomLightGalleryProps {
images: string[];
aspectRatio?: string;
column?: string | number;
}
export default function CustomLightGallery({
images,
aspectRatio,
column
}: CustomLightGalleryProps) {
const baseGrid = "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2";
const gridClass = column
? `${baseGrid} lg:grid-cols-7`
: `${baseGrid} lg:grid-cols-5`;
return (
<LightGallery
speed={500}
plugins={[lgZoom, lgThumbnail]}
elementClassNames={`${gridClass}`}
>
{images.map((img, index) => (
<a
key={index}
href={img}
className={`relative w-full ${aspectRatio || "aspect-[210/120]"} block`}
>
<Image
src={img}
alt={`Screenshot ${index + 1}`}
fill
className="object-cover rounded-lg"
/>
</a>
))}
</LightGallery>
);
}
import { PATH } from '@/routes/PATH';
import { List, ListItem, ListItemButton, ListItemIcon, ListItemText } from '@mui/material'
import { StatusUp } from '@wandersonalwes/iconsax-react'
import { Game, StatusUp, UserSearch } from '@wandersonalwes/iconsax-react'
import { usePathname, useRouter } from 'next/navigation'
import React from 'react'
......@@ -42,7 +42,7 @@ export default function AdminMenu({ open }: { open: boolean }) {
>
<ListItemIcon className={open ? "expanded" : "collapsed"}>
<StatusUp />
<Game />
</ListItemIcon>
<ListItemText
primary="Games"
......@@ -50,6 +50,28 @@ export default function AdminMenu({ open }: { open: boolean }) {
/>
</ListItemButton>
</ListItem>
<ListItem>
<ListItemButton
className={[
open ? "expanded" : "collapsed",
[
PATH.ADMIN.PLAYERS.ROOT,
PATH.ADMIN.PLAYERS.ADD_PLAYER.ROOT,
"/edit-game"
].some(path => pathname.startsWith(path)) ? "active" : ""
].join(" ")}
onClick={() => { router.push(PATH.ADMIN.PLAYERS.ROOT) }}
>
<ListItemIcon className={open ? "expanded" : "collapsed"}>
<UserSearch />
</ListItemIcon>
<ListItemText
primary="Players"
className={open ? "expanded" : "collapsed"}
/>
</ListItemButton>
</ListItem>
</List>
)
}
......@@ -12,7 +12,7 @@ import { useRouter } from "next/navigation";
import * as Yup from "yup";
interface AddGameFormProps {
id?: string | number
id?: string
}
const validationSchema = Yup.object({
......
"use client";
import React, { useMemo, useState } from "react";
import {
useReactTable,
getCoreRowModel,
getPaginationRowModel,
ColumnDef,
flexRender,
getSortedRowModel,
} from "@tanstack/react-table";
import { Button, Pagination } from "@mui/material";
import TableHeader from "@/components/molecules/TableHeader";
import { ArrowDown, ArrowUp } from "@wandersonalwes/iconsax-react";
interface PlayerTransaction {
id: number;
playerName: string;
method: string;
type: string;
amount: number;
sweepcoins: string;
date: string;
}
const data: PlayerTransaction[] = [
{ id: 244, playerName: "Harry Michael", method: "Paypal", type: "Deposit", amount: 56.68, sweepcoins: "56,598", date: "July 23, 2025 [15:69]" },
{ id: 243, playerName: "Harry Michael", method: "Crypto", type: "Withdraw", amount: 56.68, sweepcoins: "56,598", date: "July 23, 2025 [15:69]" },
{ id: 242, playerName: "Harry Michael", method: "Paypal", type: "Deposit", amount: 56.68, sweepcoins: "56,598", date: "July 23, 2025 [15:69]" },
{ id: 241, playerName: "Harry Michael", method: "Paypal", type: "Deposit", amount: 56.68, sweepcoins: "56,598", date: "July 23, 2025 [15:69]" },
{ id: 240, playerName: "Harry Michael", method: "Crypto", type: "Deposit", amount: 56.68, sweepcoins: "56,598", date: "July 23, 2025 [15:69]" },
{ id: 239, playerName: "Harry Michael", method: "Paypal", type: "Deposit", amount: 56.68, sweepcoins: "56,598", date: "July 23, 2025 [15:69]" },
{ id: 238, playerName: "Harry Michael", method: "Crypto", type: "Deposit", amount: 56.68, sweepcoins: "56,598", date: "July 23, 2025" },
];
export default function GameTransactionTable() {
const [search, setSearch] = useState("");
const [filterMethod, setFilterMethod] = useState("all");
const [sorting, setSorting] = useState<{ id: string; desc: boolean }[]>([]);
const columns = useMemo<ColumnDef<PlayerTransaction>[]>(
() => [
{
accessorKey: "id",
header: ({ column }) => (
<button
onClick={() => column.toggleSorting()}
className="flex items-center gap-1"
>
#ID
{{
asc: <ArrowUp size={14} />,
desc: <ArrowDown size={14} />,
}[column.getIsSorted() as string] || null}
</button>
),
},
{ accessorKey: "playerName", header: "Player Name" },
{ accessorKey: "method", header: "Method" },
{ accessorKey: "type", header: "Type" },
{ accessorKey: "amount", header: "Amount USD" },
{ accessorKey: "sweepcoins", header: "Sweepcoins" },
{ accessorKey: "date", header: "Transaction Date" },
],
[]
);
const filteredData = useMemo(() => {
return data.filter((row) => {
const searchMatch =
row.playerName.toLowerCase().includes(search.toLowerCase()) ||
row.type.toLowerCase().includes(search.toLowerCase()) ||
row.date.toLowerCase().includes(search.toLowerCase());
const methodMatch = filterMethod === "all" || row.method.toLowerCase() === filterMethod;
return searchMatch && methodMatch;
});
}, [search, filterMethod]);
const table = useReactTable({
data: filteredData,
columns,
state: { sorting },
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
});
const downloadCSV = () => {
};
return (
<section className="transaction__root">
<div className="section-title mb-4">
<h2 className="text-[20px] leading-[140%] font-[600]">
Games Under Diner Frenzy Spins
</h2>
</div>
<div className="border-gray border-solid border-[1px] rounded-[8px] lg:rounded-[16px]">
<TableHeader
search={search}
setSearch={setSearch}
filterMethod={filterMethod}
setFilterMethod={setFilterMethod}
onDownloadCSV={downloadCSV}
/>
<table className="min-w-full border-collapse border border-gray-200 text-left">
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th
key={header.id}
className="text-[12px] font-[600] text-title p-2 py-6 px-6 bg-light-gray"
>
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map((row) => (
<tr key={row.id} className="odd:bg-white even:bg-gray-50">
{row.getVisibleCells().map((cell) => (
<td key={cell.id} className=" p-2 py-6 px-6">
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
{/* Pagination */}
<div className="flex justify-between items-center mt-4 px-8 py-6">
<div>
<span>Row per page:</span>
<select
value={table.getState().pagination.pageSize}
onChange={(e) => table.setPageSize(Number(e.target.value))}
className="ml-2 border border-gray-300 rounded p-1"
>
{[5, 10, 15, 20].map((size) => (
<option key={size} value={size}>
{size}
</option>
))}
</select>
</div>
<Pagination count={10} variant="outlined" shape="rounded" sx={{ gap: "8px" }} />
</div>
</div>
</section>
);
}
import TableHeader from '@/components/molecules/TableHeader';
import CustomLightGallery from '@/components/organism/LightGallery';
import Image from 'next/image'
import Link from 'next/link'
import React from 'react'
import TransactionBlock from './Transaction';
export default function GameDetailPage() {
const screenshots = Array.from({ length: 12 }).map(
() => "/assets/images/auth-image.png"
);
const relatedGames = Array.from({ length: 8 }).map(
() => "/assets/images/auth-image.png"
);
return (
<>
<section className="game__detail__intro mb-14">
<div className="lg:grid grid-cols-12 gap-8 xl:gap-10">
<div className="lg:col-span-5 xl:col-span-4 game__featured__image relative aspect-[338/338] rounded-[10px] overflow-hidden">
<Image src={"/assets/images/fallback.png"} alt="Game Name" fill className="object-cover" />
</div>
<div className="game__content lg:col-span-7 xl:col-span-8">
<div className="flex items-start justify-between pb-6 mb-6 border-b-[1px] border-solid border-[rgba(0,0,0,0.1)]">
<div className="section__title">
<strong className="text-bold block mb-6 text-[12px]">
Active Players:{" "}
<span className="bg-primary-light rounded-[20px] py-1 px-2">
6985
</span>
</strong>
<h1 className='text-[24px] leading-[133%] mb-4'>Panda Master</h1>
<ul className="flex justify-start items-center gap-12">
<li className='flex gap-2' >
<p className="mb-1 text-[14px] leading-[120%] text-para-light">
Type:
</p>
<strong className="text-[14px] leading-[120%] font-[500] text-title">
Fishing
</strong>
</li>
<li className='flex gap-2' >
<p className="mb-1 text-[14px] leading-[120%] text-para-light">
Provider:
</p>
<strong className="text-[14px] leading-[120%] font-[500] text-title">
PG Soft
</strong>
</li>
</ul>
</div>
<Link href={"/games/1"} className='ss-btn bg-primary-grad max-w-fit text-white'>Edit Game Details</Link>
</div>
<div className="general-content-box content mb-6">
<p>Diner Frenzy Spins is a fast-paced, food-themed slot game where sizzling reels serve up delicious wins. Set in a retro diner, it features wild symbols, bonus rounds, and free spin combos that bring the kitchen chaos to life with every spin. With exciting bonus rounds, wild symbols, and free spin feature.. Read More</p>
</div>
<div className="game__key__highlight flex justify-between lg:flex-row flex-col gap-4 py-5 px-6 bg-[rgba(143,167,226,0.10)] rounded-[8px]">
<div className="highlight">
<p className='text-[12px] leading-[120%] font-[500] text-para-light'>This week total Screentime</p>
<strong className='text-[16px] leading-[120%] font-[600]'>689hr</strong>
</div>
<div className="highlight">
<p className='text-[12px] leading-[120%] font-[500] text-para-light'>Available Credit</p>
<strong className='text-[16px] leading-[120%] font-[600]'>$326,126.05 <span className='text-[12px] leading-[120%] font-[500] text-para-light'>/100,000</span></strong>
</div>
<div className="highlight">
<p className='text-[12px] leading-[120%] font-[500] text-para-light'>Unsettled Credit</p>
<strong className='text-[16px] leading-[120%] font-[600]'>$326,126.05 <span className='text-[12px] leading-[120%] font-[500] text-para-light'>/100,000</span></strong>
</div>
</div>
</div>
</div>
</section>
{screenshots.length ? <section className="game__screenshots mb-14">
<div className="section-title mb-4">
<h2 className="text-[20px] leading-[140%] font-[600]">
Uploaded Game-Screenshots
</h2>
</div>
<CustomLightGallery images={screenshots} />
</section> : null}
{relatedGames.length ? <section className="game__screenshots mb-14">
<div className="section-title mb-4">
<h2 className="text-[20px] leading-[140%] font-[600]">
Games Under Diner Frenzy Spins
</h2>
</div>
<CustomLightGallery images={relatedGames} aspectRatio='aspect-[148/164]' column="7" />
</section> : null}
<TransactionBlock />
</>
)
}
......@@ -102,7 +102,7 @@ export default function AdminGameList() {
{/* CTA */}
<Link
href={`/game/${game.id}`}
href={`/games/${game.id}`}
className="ss-btn bg-primary-grad text-white max-w-fit"
>
Game Overview
......
"use client"
import InputFile from '@/components/atom/InputFile'
import PasswordField from '@/components/molecules/PasswordField'
import { useAppDispatch } from '@/hooks/hook'
import { useCreatePlayerMutation } from '@/services/playerApi'
import { showToast, ToastVariant } from '@/slice/toastSlice'
import { initialPlayerValues } from '@/types/player'
import { Button, Input, InputLabel } from '@mui/material'
import { useFormik } from 'formik'
import { useRouter } from 'next/navigation'
import React from 'react'
import * as Yup from "yup";
export const PlayerValidationSchema = Yup.object().shape({
email: Yup.string()
.email("Invalid email address")
.required("Email is required"),
first_name: Yup.string().required("First name is required"),
last_name: Yup.string().required("Last name is required"),
wallet_address: Yup.string().nullable(),
address: Yup.string().nullable(),
city: Yup.string().nullable(),
phone: Yup.string()
.matches(/^\+?\d{7,15}$/, "Invalid phone number")
.nullable(),
password: Yup.string()
.min(6, "Password must be at least 6 characters")
.required("Password is required"),
password_confirmation: Yup.string()
.oneOf([Yup.ref("password")], "Passwords must match")
.required("Password confirmation is required"),
// profile_image: Yup.mixed().required("Profile is required"),
});
export default function AddPlayerForm({ id }: { id?: string | number }) {
const dispatch = useAppDispatch();
const router = useRouter();
const [createPlayer, { isLoading }] = useCreatePlayerMutation();
const formik = useFormik({
initialValues: initialPlayerValues,
validationSchema: PlayerValidationSchema,
// enableReinitialize,
onSubmit: async (values) => {
if (id) {
try {
console.log("Editing Player")
}
catch (e: any) {
dispatch(
showToast({
message: e.error,
variant: ToastVariant.ERROR
})
)
}
}
else {
try {
const formData = new FormData();
// Required fields
formData.append("email", values.email);
formData.append("first_name", values.first_name);
formData.append("last_name", values.last_name);
formData.append("password", values.password);
formData.append("password_confirmation", values.password_confirmation);
if (values.wallet_address) formData.append("wallet_address", values.wallet_address);
if (values.address) formData.append("address", values.address);
if (values.city) formData.append("city", values.city);
if (values.phone) formData.append("phone", values.phone);
if (values.profile_image) {
if (Array.isArray(values.profile_image)) {
values.profile_image.forEach((file) => formData.append("profile_image", file));
} else {
formData.append("profile_image", values.profile_image);
}
}
const response = await createPlayer(formData).unwrap();
dispatch(
showToast({
message: response.data.message,
variant: ToastVariant.SUCCESS
})
);
router.push("/players");
}
catch (e: any) {
console.log(e);
dispatch(
showToast({
message: e.error,
variant: ToastVariant.ERROR
})
)
}
}
}
})
return (
<form onSubmit={formik.handleSubmit}>
<div className="form__field__wrapper border-solid border-[1px] border-gray rounded-[16px] mb-6 ">
<div className="form__title py-6 px-10 border-b-solid border-b-[1px] border-gray">
<h2 className="text-[20px] leading-[140%] font-bold">Player Details</h2>
</div>
<div className="form__fields p-6 lg:p-10 flex flex-col gap-4 lg:gap-6 lg:grid grid-cols-2">
<div className="input__field">
<InputLabel htmlFor="email">Email<span className="text-red-500">*</span></InputLabel>
<Input
fullWidth
id="email"
name="email"
type="email"
placeholder="Enter email address"
value={formik.values.email}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<span className="error">
{formik.touched.email && formik.errors.email ? formik.errors.email : ""}
</span>
</div>
<div className="input__field">
<InputLabel htmlFor="first_name">First Name<span className="text-red-500">*</span></InputLabel>
<Input
fullWidth
id="first_name"
name="first_name"
placeholder="Enter first name"
value={formik.values.first_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<span className="error">
{formik.touched.first_name && formik.errors.first_name ? formik.errors.first_name : ""}
</span>
</div>
<div className="input__field">
<InputLabel htmlFor="last_name">Last Name<span className="text-red-500">*</span></InputLabel>
<Input
fullWidth
id="last_name"
name="last_name"
placeholder="Enter last name"
value={formik.values.last_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<span className="error">
{formik.touched.last_name && formik.errors.last_name ? formik.errors.last_name : ""}
</span>
</div>
<div className="input__field">
<InputLabel htmlFor="wallet_address">Wallet Address</InputLabel>
<Input
fullWidth
id="wallet_address"
name="wallet_address"
placeholder="Enter wallet address"
value={formik.values.wallet_address}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<span className="error">
{formik.touched.wallet_address && formik.errors.wallet_address ? formik.errors.wallet_address : ""}
</span>
</div>
<div className="input__field">
<InputLabel htmlFor="address">Address</InputLabel>
<Input
fullWidth
id="address"
name="address"
placeholder="Enter address"
value={formik.values.address}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<span className="error">
{formik.touched.address && formik.errors.address ? formik.errors.address : ""}
</span>
</div>
<div className="input__field">
<InputLabel htmlFor="city">City</InputLabel>
<Input
fullWidth
id="city"
name="city"
placeholder="Enter city"
value={formik.values.city}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<span className="error">
{formik.touched.city && formik.errors.city ? formik.errors.city : ""}
</span>
</div>
<div className="input__field">
<InputLabel htmlFor="phone">Phone</InputLabel>
<Input
fullWidth
id="phone"
name="phone"
placeholder="Enter phone number"
value={formik.values.phone}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<span className="error">
{formik.touched.phone && formik.errors.phone ? formik.errors.phone : ""}
</span>
</div>
<div className="input__field">
<PasswordField
name="password"
label="Password*"
placeholder="Enter password"
value={formik.values.password}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.password ? formik.errors.password : undefined}
/>
</div>
<div className="input__field">
<PasswordField
name="password_confirmation"
label="Confirm Password*"
placeholder="Confirm password"
value={formik.values.password_confirmation}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.password_confirmation ? formik.errors.password_confirmation : undefined}
/>
</div>
<div className="input__field">
<InputFile
name="profile_image"
label="Profile Image"
value={formik.values.profile_image || null}
onChange={(file: File | File[] | null) => formik.setFieldValue("profile_image", file)}
onBlur={() => formik.setFieldTouched("profile_image", true)}
/>
<span className="error">
{formik.touched.profile_image && formik.errors.profile_image ? formik.errors.profile_image : ""}
</span>
</div>
</div>
</div>
<div className="text-end mt-8 lg:mt-12 max-w-fit ml-auto">
<Button type="submit" variant="contained" color="primary" sx={{ color: "#fff" }} disabled={!formik.dirty || formik.isSubmitting}>
Confirm {id ? "Player Update" : "Player Addition"}
</Button>
</div>
</form>
)
}
import React from 'react'
export default function PlayerListing() {
return (
<div>PlayerListing</div>
)
}
......@@ -3,6 +3,7 @@ import { configureStore } from "@reduxjs/toolkit";
import auth from "@/slice/authSlice";
import toastSlice from "@/slice/toastSlice";
import { gameApi } from "@/services/gameApi";
import { playerApi } from "@/services/playerApi";
export const store = configureStore({
reducer: {
......@@ -10,11 +11,13 @@ export const store = configureStore({
toastSlice,
[authApi.reducerPath]: authApi.reducer,
[gameApi.reducerPath]: gameApi.reducer,
[playerApi.reducerPath]: playerApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware()
.concat(authApi.middleware)
.concat(gameApi.middleware)
.concat(playerApi.middleware)
})
......
......@@ -23,7 +23,16 @@ export const PATH = {
ROOT: "/games/add-game"
},
EDIT_GAME: {
ROOT: "/games/edit-game/:id"
ROOT: "/games/edit-game/"
}
},
PLAYERS: {
ROOT: "/players",
ADD_PLAYER: {
ROOT: "/players/add-player"
},
EDIT_PLAYER: {
ROOT: "/players/edit-player/"
}
}
},
......
import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./baseQuery";
import { PlayerProps, PlayerResponseProps } from "@/types/player";
import { GlobalResponse } from "@/types/config";
export const playerApi = createApi({
reducerPath: "playerApi",
baseQuery: baseQuery,
tagTypes: ["players"],
endpoints: (builder) => ({
createPlayer: builder.mutation<PlayerResponseProps, FormData>({
query: (body) => ({
url: "/api/admin/add-user",
method: "POST",
body: body
}),
invalidatesTags: ["players"]
}),
getAllPlayer: builder.query<PlayerResponseProps[], null>({
query: () => ({
url: "/api/admin/get-users",
method: "GET"
}),
providesTags: ['players']
}),
getPlayerById: builder.query<PlayerResponseProps, { id: number }>({
query: ({ id }) => ({
url: `/api/admin/get-user/${id}`,
method: "GET"
}),
providesTags: ['players']
}),
getPlayerBalanceById: builder.query<PlayerResponseProps, { id: number }>({
query: ({ id }) => ({
url: `/api/admin/get-balance/${id}`,
method: "GET"
}),
providesTags: ['players']
}),
updatePlayerById: builder.mutation<PlayerResponseProps, { id: number, data: FormData }>({
query: ({ id, data }) => ({
url: `/api/admin/update-user/${id}`,
method: "POST",
body: data
}),
invalidatesTags: ["players"]
}),
deletePlayerById: builder.mutation<GlobalResponse, { id: number }>({
query: ({ id }) => ({
url: `/api/admin/update-user/${id}`,
method: "DELETE",
}),
invalidatesTags: ["players"]
}),
})
})
export const {
useCreatePlayerMutation,
useGetAllPlayerQuery,
useGetPlayerByIdQuery,
useGetPlayerBalanceByIdQuery, useUpdatePlayerByIdMutation,
useDeletePlayerByIdMutation
} = playerApi;
\ No newline at end of file
......@@ -49,6 +49,9 @@ export default function Palette(mode: ThemeMode) {
// Final MUI Theme
return createTheme({
typography: {
fontFamily: "Inter, sans-serif",
},
palette: {
mode: mode as PaletteMode,
common: {
......@@ -127,7 +130,7 @@ export default function Palette(mode: ThemeMode) {
},
},
input: {
p0dding: 0,
padding: 0,
"&::placeholder": {
color: "var(--Gray, #999)",
fontWeight: 400,
......@@ -263,14 +266,6 @@ export default function Palette(mode: ThemeMode) {
}
}
},
typography: {
fontFamily: "Inter, sans-serif",
subtitle1: {
fontSize: "14px",
fontWeight: 500,
lineHeight: "120%",
color: "var(--Title, #0E0E11)"
}
}
});
}
\ No newline at end of file
import { ImageProps } from "./config";
export interface PlayerProps {
name: string;
email: string;
first_name: string;
last_name: string;
wallet_address?: string;
address?: string;
city?: string;
phone?: string;
password: string;
password_confirmation: string;
profile_image: File | null;
}
export const initialPlayerValues: PlayerProps = {
name: "",
email: "",
first_name: "",
last_name: "",
wallet_address: "",
address: "",
city: "",
phone: "",
password: "",
password_confirmation: "",
profile_image: null,
};
export interface PlayerItem extends PlayerProps {
id: string | number
}
export interface PlayerResponseProps {
data: {
data: PlayerItem;
message: string;
status: string;
}
}
\ No newline at end of file
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