Commit 0e1f8ba7 by Arjun Jhukal

updated the suspended user functionality

parent 557a3ffc
// components/Loading.tsx
"use client";
import React from "react"; import React from "react";
import LotifyLoading from "@/components/atom/LotifyLoading";
import GlobalLoading from "@/components/organism/GlobalLoading"; import GlobalLoading from "@/components/organism/GlobalLoading";
export default function Loading() { export default function Loading() {
......
import { Switch } from '@mui/material'
import React from 'react'
export default function CustomSwitch({ isSuspended, onClick }: { isSuspended?: boolean; onClick?: () => void }) {
return (
<div className="flex items-center gap-2">
<Switch
checked={isSuspended}
sx={{
"& .MuiSwitch-switchBase.Mui-checked": {
color: "var(--primary)",
},
"& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track": {
backgroundColor: "var(--primary)",
},
"& .MuiSwitch-track": {
backgroundColor: "var(--gray)",
},
}}
onClick={onClick}
/>
<span
className={`text-[12px] font-[500] ${isSuspended ? "text-[var(--para-light)]" : "text-[var(--primary)]"
}`}
>
{isSuspended ? "Activate" : "Suspend"}
</span>
</div>
)
}
...@@ -46,11 +46,11 @@ export default function PasswordField({ ...@@ -46,11 +46,11 @@ export default function PasswordField({
onMouseDown={handleMouseDownPassword} onMouseDown={handleMouseDownPassword}
edge="end" edge="end"
color="secondary" color="secondary"
sx={{ // sx={{
padding: 0, // padding: 0,
}} // }}
> >
{showPassword ? <Eye /> : <EyeSlash />} {showPassword ? <Eye size={16} /> : <EyeSlash size={16} />}
</IconButton> </IconButton>
</InputAdornment > </InputAdornment >
} }
......
...@@ -29,7 +29,7 @@ export default function TableHeader({ ...@@ -29,7 +29,7 @@ export default function TableHeader({
startAdornment={ startAdornment={
<InputAdornment position="start"> <InputAdornment position="start">
<IconButton edge="start"> <IconButton edge="start">
<SearchNormal size={32} /> <SearchNormal size={20} />
</IconButton> </IconButton>
</InputAdornment> </InputAdornment>
} }
...@@ -55,7 +55,8 @@ export default function TableHeader({ ...@@ -55,7 +55,8 @@ export default function TableHeader({
borderRadius: "8px", borderRadius: "8px",
border: "1px solid var(--Gray, #E0E0E3)", border: "1px solid var(--Gray, #E0E0E3)",
padding: "8px 16px", padding: "8px 16px",
color: "#0E0E11" color: "#0E0E11",
maxWidth: "fit-content",
}}>Download CSV</Button> }}>Download CSV</Button>
</div> </div>
); );
......
...@@ -29,9 +29,9 @@ export default function ProfileBlock() { ...@@ -29,9 +29,9 @@ export default function ProfileBlock() {
const id = open ? 'profile-dropdown' : "" const id = open ? 'profile-dropdown' : ""
const handleLogout = (e: React.MouseEvent) => { const handleLogout = (e: React.MouseEvent) => {
router.replace(PATH.AUTH.LOGIN.ROOT);
e.preventDefault(); e.preventDefault();
dispatch(clearTokens()); dispatch(clearTokens());
router.replace(PATH.AUTH.LOGIN.ROOT);
}; };
const menuItems = [ const menuItems = [
{ {
...@@ -162,7 +162,7 @@ export default function ProfileBlock() { ...@@ -162,7 +162,7 @@ export default function ProfileBlock() {
border: "1px solid rgba(255, 255, 255, 0.25)", border: "1px solid rgba(255, 255, 255, 0.25)",
borderRadius: "8px", borderRadius: "8px",
boxShadow: ` boxShadow: `
0 8px 32px 0 rgba(0, 0, 0, 0.37), 0 8px 32px 0 #ddd,
inset 0 1px 0 0 rgba(255, 255, 255, 0.4), inset 0 1px 0 0 rgba(255, 255, 255, 0.4),
0 0 20px rgba(255, 255, 255, 0.1) 0 0 20px rgba(255, 255, 255, 0.1)
`, `,
......
...@@ -122,7 +122,7 @@ export default function AdminMenu({ open }: { open: boolean }) { ...@@ -122,7 +122,7 @@ export default function AdminMenu({ open }: { open: boolean }) {
border: "1px solid rgba(255, 255, 255, 0.25)", border: "1px solid rgba(255, 255, 255, 0.25)",
borderRadius: "8px", borderRadius: "8px",
boxShadow: ` boxShadow: `
0 8px 32px 0 rgba(0, 0, 0, 0.37), 0 8px 32px 0 #ddd,
inset 0 1px 0 0 rgba(255, 255, 255, 0.4), inset 0 1px 0 0 rgba(255, 255, 255, 0.4),
0 0 20px rgba(255, 255, 255, 0.1) 0 0 20px rgba(255, 255, 255, 0.1)
`, `,
......
"use client"; "use client";
import CustomSwitch from '@/components/atom/Switch';
import ActionGroup from '@/components/molecules/Action'; import ActionGroup from '@/components/molecules/Action';
import TabController from '@/components/molecules/TabController';
import TableHeader from '@/components/molecules/TableHeader' import TableHeader from '@/components/molecules/TableHeader'
import CustomTable from '@/components/organism/Table'; import CustomTable from '@/components/organism/Table';
import { useAppDispatch } from '@/hooks/hook'; import { useAppDispatch } from '@/hooks/hook';
import { PATH } from '@/routes/PATH'; import { PATH } from '@/routes/PATH';
import { useDeletePlayerByIdMutation, useGetAllPlayerQuery } from '@/services/playerApi'; import { useDeletePlayerByIdMutation, useGetAllPlayerQuery, useSuspendPlayerByIdMutation } from '@/services/playerApi';
import { showToast, ToastVariant } from '@/slice/toastSlice'; import { showToast, ToastVariant } from '@/slice/toastSlice';
import { PlayerItem, PlayerProps } from '@/types/player'; import { PlayerItem, PlayerProps } from '@/types/player';
import { formatDateTime } from '@/utils/formatDateTime'; import { formatDateTime } from '@/utils/formatDateTime';
...@@ -22,16 +24,38 @@ export default function PlayerListing() { ...@@ -22,16 +24,38 @@ export default function PlayerListing() {
const [sorting, setSorting] = useState<{ id: string; desc: boolean }[]>([]); const [sorting, setSorting] = useState<{ id: string; desc: boolean }[]>([]);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10); const [pageSize, setPageSize] = useState(10);
const [currentTab, setCurrentTab] = React.useState("");
const { data, isLoading: loadingPlayer } = useGetAllPlayerQuery({ const { data, isLoading: loadingPlayer } = useGetAllPlayerQuery({
page, page,
per_page: pageSize, per_page: pageSize,
search: search || "" search: search || "",
status: currentTab || ""
}); });
const filteredData = useMemo(() => data?.data?.data || [], [data]); const filteredData = useMemo(() => data?.data?.data || [], [data, currentTab, pageSize]);
const [deletePlayer, { isLoading: deletingPlayer }] = useDeletePlayerByIdMutation(); const [deletePlayer, { isLoading: deletingPlayer }] = useDeletePlayerByIdMutation();
const [suspendPlayer, { isLoading: suspendingPlayer }] = useSuspendPlayerByIdMutation();
const handlePlayerSuspend = async (id: string) => {
try {
const response = await suspendPlayer({ id }).unwrap();
dispatch(
showToast({
message: response.message || "Player status updated successfully",
variant: ToastVariant.SUCCESS
})
)
}
catch (err: any) {
dispatch(
showToast({
message: err?.data?.message || "Failed to update player status",
variant: ToastVariant.ERROR
})
)
}
}
const columns = useMemo<ColumnDef<PlayerItem>[]>(() => [ const columns = useMemo<ColumnDef<PlayerItem>[]>(() => [
{ {
id: 'select', id: 'select',
...@@ -88,6 +112,17 @@ export default function PlayerListing() { ...@@ -88,6 +112,17 @@ export default function PlayerListing() {
) )
}, },
{ {
accessorKey: "status",
header: "Account Status",
cell: ({ row }) => {
const isSuspended = row.original.is_suspended; // true or false
return (
<CustomSwitch isSuspended={isSuspended} onClick={() => handlePlayerSuspend(row.original.id)} />
);
},
},
{
accessorKey: 'registeredDate', accessorKey: 'registeredDate',
header: 'Registered Date', header: 'Registered Date',
cell: ({ row }) => { cell: ({ row }) => {
...@@ -123,27 +158,65 @@ export default function PlayerListing() { ...@@ -123,27 +158,65 @@ export default function PlayerListing() {
onView={`${PATH.ADMIN.PLAYERS.ROOT}/${row.original.id}`} onView={`${PATH.ADMIN.PLAYERS.ROOT}/${row.original.id}`}
onEdit={`${PATH.ADMIN.PLAYERS.EDIT_PLAYER.ROOT}/${row.original.id}`} onEdit={`${PATH.ADMIN.PLAYERS.EDIT_PLAYER.ROOT}/${row.original.id}`}
onDelete={async () => { onDelete={async () => {
const response = await deletePlayer({ id: row.original.id }).unwrap(); try {
dispatch( const response = await deletePlayer({ id: row.original.id }).unwrap();
showToast({ dispatch(
message: response.message, showToast({
variant: ToastVariant.SUCCESS message: response.message || "Player deleted successfully",
}) variant: ToastVariant.SUCCESS
) })
)
}
catch (err: any) {
dispatch(
showToast({
message: err?.data?.message || "Failed to delete player",
variant: ToastVariant.ERROR
})
)
}
}} }}
/> />
), ),
}, },
], []); ], []);
// const table = useReactTable({
// data: data?.data?.data || [],
// columns,
// state: {
// sorting,
// },
// onSortingChange: setSorting,
// getCoreRowModel: getCoreRowModel(),
// getPaginationRowModel: getPaginationRowModel(),
// getSortedRowModel: getSortedRowModel(),
// })
const table = useReactTable({ const table = useReactTable({
data: filteredData || [], data: data?.data?.data || [],
columns, columns,
state: { sorting }, state: {
sorting,
pagination: {
pageIndex: page - 1,
pageSize: pageSize,
},
},
onSortingChange: setSorting, onSortingChange: setSorting,
onPaginationChange: (updater) => {
const newState = typeof updater === "function" ? updater({ pageIndex: page - 1, pageSize }) : updater;
setPage(newState.pageIndex + 1);
setPageSize(newState.pageSize);
},
getCoreRowModel: getCoreRowModel(), getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(), getSortedRowModel: getSortedRowModel(),
}) getPaginationRowModel: getPaginationRowModel(),
});
const handleTabChange = (tab: string) => {
setCurrentTab(tab);
};
return ( return (
<section className="player__listing_root"> <section className="player__listing_root">
...@@ -154,6 +227,11 @@ export default function PlayerListing() { ...@@ -154,6 +227,11 @@ export default function PlayerListing() {
setSearch={setSearch} setSearch={setSearch}
onDownloadCSV={() => { }} onDownloadCSV={() => { }}
/> />
<TabController
links={[{ label: "All", value: "" }, { label: "Subspended", value: "suspend" }]}
currentTab={currentTab}
onTabChange={handleTabChange}
/>
<CustomTable <CustomTable
table={table} table={table}
loading={loadingPlayer} loading={loadingPlayer}
......
...@@ -42,8 +42,8 @@ export default function ExclusiveGameDetail({ game }: { game: SingleGameResponse ...@@ -42,8 +42,8 @@ export default function ExclusiveGameDetail({ game }: { game: SingleGameResponse
<Box sx={{ <Box sx={{
borderRadius: "16px" borderRadius: "16px"
}} className="flex justify-center items-center gap-2 py-4 px-6 bg-secondary-grad text-title"> }} className="flex justify-center items-center gap-2 py-4 px-6 bg-secondary-grad text-title ss-btn">
<div className="coins"> <div className="coins ">
<Link href={`/buy-coins/${game?.data?.id}`}> <Link href={`/buy-coins/${game?.data?.id}`}>
<strong className="text-[16px] leading-4 font-[600] block mb-1">+ Deposit Coins</strong> <strong className="text-[16px] leading-4 font-[600] block mb-1">+ Deposit Coins</strong>
</Link> </Link>
...@@ -51,7 +51,7 @@ export default function ExclusiveGameDetail({ game }: { game: SingleGameResponse ...@@ -51,7 +51,7 @@ export default function ExclusiveGameDetail({ game }: { game: SingleGameResponse
</Box> </Box>
{game?.data?.provider == "goldcoincity" ? "" : <Box sx={{ {game?.data?.provider == "goldcoincity" ? "" : <Box sx={{
borderRadius: "16px" borderRadius: "16px"
}} className="flex justify-center items-center gap-2 py-4 px-6 border border-secondary"> }} className="flex justify-center items-center gap-2 py-4 px-6 border border-secondary ss-btn">
<div className="coins"> <div className="coins">
<Link href={`/withdrawl`}> <Link href={`/withdrawl`}>
<strong className="text-[16px] leading-4 font-[600] block mb-1 text-secondary">Withdraw Coins</strong> <strong className="text-[16px] leading-4 font-[600] block mb-1 text-secondary">Withdraw Coins</strong>
......
// services/playerApi.ts
import { createApi } from "@reduxjs/toolkit/query/react"; import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./baseQuery"; import { baseQuery } from "./baseQuery";
import { PlayerListResponse, PlayerProps, SinlgePlayerResponseProps, } from "@/types/player"; import {
PlayerListResponse,
PlayerProps,
SinlgePlayerResponseProps,
} from "@/types/player";
import { GlobalResponse, QueryParams } from "@/types/config"; import { GlobalResponse, QueryParams } from "@/types/config";
export const playerApi = createApi({ export const playerApi = createApi({
reducerPath: "playerApi", reducerPath: "playerApi",
baseQuery: baseQuery, baseQuery: baseQuery,
tagTypes: ["players"], tagTypes: ["Players"],
endpoints: (builder) => ({ endpoints: (builder) => ({
// CREATE PLAYER
createPlayer: builder.mutation<PlayerListResponse, FormData>({ createPlayer: builder.mutation<PlayerListResponse, FormData>({
query: (body) => ({ query: (body) => ({
url: "/api/admin/add-user", url: "/api/admin/add-user",
method: "POST", method: "POST",
body: body body,
}), }),
invalidatesTags: ["players"] invalidatesTags: [{ type: "Players", id: "LIST" }],
}), }),
getAllPlayer: builder.query<PlayerListResponse, QueryParams>({
query: ({ search, page, per_page }) => { // GET ALL Players
getAllPlayer: builder.query<PlayerListResponse, QueryParams>({
query: ({ search, page, per_page, status }) => {
const params = new URLSearchParams(); const params = new URLSearchParams();
if (search) params.append('search', search); if (search) params.append("search", search);
if (page) params.append('page', page.toString()); if (page) params.append("page", page.toString());
if (per_page) params.append('page_size', per_page.toString()); if (per_page) params.append("page_size", per_page.toString());
if (status) params.append("status", status.toString());
const queryString = params.toString(); const queryString = params.toString();
return { return {
url: `/api/admin/get-users${queryString ? `?${queryString}` : ''}`, url: `/api/admin/get-users${queryString ? `?${queryString}` : ""}`,
method: "GET" method: "GET",
} };
}, },
providesTags: ['players'] providesTags: (result) =>
result?.data
? [
...result.data.data.map((player) => ({
type: "Players" as const,
id: player.id,
})),
{ type: "Players", id: "LIST" },
]
: [{ type: "Players", id: "LIST" }],
}), }),
// GET SINGLE PLAYER
getPlayerById: builder.query<SinlgePlayerResponseProps, { id: number }>({ getPlayerById: builder.query<SinlgePlayerResponseProps, { id: number }>({
query: ({ id }) => ({ query: ({ id }) => ({
url: `/api/admin/get-user/${id}`, url: `/api/admin/get-user/${id}`,
method: "GET" method: "GET",
}), }),
providesTags: ['players'] providesTags: (result, error, { id }) => [{ type: "Players", id }],
}), }),
// GET PLAYER BALANCE BY ID
getPlayerBalanceById: builder.query<SinlgePlayerResponseProps, { id: string }>({ getPlayerBalanceById: builder.query<SinlgePlayerResponseProps, { id: string }>({
query: ({ id }) => ({ query: ({ id }) => ({
url: `/api/admin/get-balance/${id}`, url: `/api/admin/get-balance/${id}`,
method: "GET" method: "GET",
}), }),
providesTags: ['players'] providesTags: (result, error, { id }) => [{ type: "Players", id }],
}), }),
updatePlayerById: builder.mutation<SinlgePlayerResponseProps, { id: string, body: FormData }>({
// UPDATE PLAYER
updatePlayerById: builder.mutation<SinlgePlayerResponseProps, { id: string; body: FormData }>({
query: ({ id, body }) => ({ query: ({ id, body }) => ({
url: `/api/admin/update-user/${id}`, url: `/api/admin/update-user/${id}`,
method: "POST", method: "POST",
body: body body,
}), }),
invalidatesTags: ["players"] invalidatesTags: (result, error, { id }) => [{ type: "Players", id }],
}), }),
// DELETE PLAYER
deletePlayerById: builder.mutation<GlobalResponse, { id: string }>({ deletePlayerById: builder.mutation<GlobalResponse, { id: string }>({
query: ({ id }) => ({ query: ({ id }) => ({
url: `/api/admin/user/${id}`, url: `/api/admin/user/${id}`,
method: "DELETE", method: "DELETE",
}), }),
invalidatesTags: ["players"] invalidatesTags: (result, error, { id }) => [
{ type: "Players", id },
{ type: "Players", id: "LIST" },
],
}),
// SUSPEND PLAYER
suspendPlayerById: builder.mutation<GlobalResponse, { id: string }>({
query: ({ id }) => ({
url: `/api/admin/user/suspend/${id}`,
method: "POST",
}),
invalidatesTags: (result, error, { id }) => [
{ type: "Players", id },
{ type: "Players", id: "LIST" },
],
}), }),
}) }),
}) });
export const { export const {
useCreatePlayerMutation, useCreatePlayerMutation,
useGetAllPlayerQuery, useGetAllPlayerQuery,
useGetPlayerByIdQuery, useGetPlayerByIdQuery,
useGetPlayerBalanceByIdQuery, useUpdatePlayerByIdMutation, useGetPlayerBalanceByIdQuery,
useDeletePlayerByIdMutation useUpdatePlayerByIdMutation,
} = playerApi; useDeletePlayerByIdMutation,
\ No newline at end of file useSuspendPlayerByIdMutation,
} = playerApi;
...@@ -37,4 +37,5 @@ export interface QueryParams { ...@@ -37,4 +37,5 @@ export interface QueryParams {
page?: number; page?: number;
per_page?: number; per_page?: number;
search?: string; search?: string;
status?: string;
} }
\ No newline at end of file
...@@ -47,6 +47,7 @@ export interface PlayerItem extends CommonPlayerProps { ...@@ -47,6 +47,7 @@ export interface PlayerItem extends CommonPlayerProps {
total_withdrawl?: string, total_withdrawl?: string,
total_deposited?: string total_deposited?: string
profile_image_file?: string; profile_image_file?: string;
is_suspended?: boolean;
} }
export interface PlayerListResponse { export interface PlayerListResponse {
......
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