Commit e5b9fdf0 by Arjun Jhukal

updated the new including update the game

parent e19bc8d3
"use client";
import { InputLabel } from "@mui/material";
import { InputLabel, Tooltip } from "@mui/material";
import { CloseCircle } from "@wandersonalwes/iconsax-react";
import React from "react";
interface InputFileProps {
......@@ -14,6 +15,7 @@ interface InputFileProps {
error?: string | boolean;
touched?: boolean;
multiple?: boolean;
serverFile?: string | string[] | null
}
export default function InputFile({
......@@ -27,6 +29,7 @@ export default function InputFile({
error,
touched,
multiple = false,
serverFile
}: InputFileProps) {
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const files = e.target.files;
......@@ -38,6 +41,13 @@ export default function InputFile({
}
};
const handleRemoveFile = (fileToRemove: File) => {
if (!Array.isArray(value)) return;
const updatedFiles = value.filter((f) => f !== fileToRemove);
onChange(updatedFiles.length > 0 ? updatedFiles : null);
};
return (
<div className="input__field">
<InputLabel className="block text-sm font-semibold mb-2">
......@@ -63,6 +73,56 @@ export default function InputFile({
onBlur={onBlur}
/>
</label>
{Array.isArray(value) && value.length > 0 && (
<div className="flex gap-3 flex-wrap mt-2">
{value.map((f) => (
<div
key={f.name}
className="flex items-center gap-2 rounded-[20px] py-[2px] px-3 bg-primary-light text-white"
>
<Tooltip title={f.name}>
<span className="text-[12px]">
{f?.name.length > 5
? f.name.slice(0, 5) + "..."
: f.name}
</span>
</Tooltip>
<CloseCircle
size={14}
className="cursor-pointer hover:text-red-500"
onClick={() => handleRemoveFile(f)}
/>
</div>
))}
</div>
)}
{
Array.isArray(serverFile) && serverFile.length > 0 ? (
serverFile.map((f) => (
<div
key={f}
className="flex items-center gap-2 rounded-[20px] py-[2px] px-3 bg-primary-light text-white"
>
<Tooltip title={f}>
<span className="text-[12px]">
{f.length > 5
? f.slice(0, 5) + "..."
: f}
</span>
</Tooltip>
{/* <CloseCircle
size={14}
className="cursor-pointer hover:text-red-500"
onClick={() => handleRemoveFile(f)}
/> */}
</div>
))
) : (<span className="text-[12px]">
{serverFile && serverFile?.length > 5
? serverFile?.slice(0, 5) + "..."
: serverFile}
</span>)
}
{touched && error && (
<span className="text-red-500 text-xs mt-1">{error}</span>
......
......@@ -43,7 +43,7 @@ export default function SelectField({
value={value}
onChange={onChange}
onBlur={onBlur}
className="w-full border text-[14px] border-gray-300 rounded-lg px-3 py-[10px] focus:border-primary outline-0"
className="w-full border text-[14px] border-gray-300 rounded-lg px-3 py-2 focus:border-primary outline-0"
>
<option value="">Select the Type of Game</option>
{options.map((option) => (
......
......@@ -8,6 +8,7 @@ import lgThumbnail from "lightgallery/plugins/thumbnail";
import "lightgallery/css/lightgallery.css";
import "lightgallery/css/lg-zoom.css";
import "lightgallery/css/lg-thumbnail.css";
import { FileResponse } from "@/types/game";
interface CustomLightGalleryProps {
images: string[];
......
......@@ -3,7 +3,8 @@ import InputFile from "@/components/atom/InputFile";
import SelectField from "@/components/atom/SelectField";
import ReactQuillEditor from "@/components/molecules/ReactQuill";
import { useAppDispatch } from "@/hooks/hook";
import { useAddGameMutation } from "@/services/gameApi";
import { PATH } from "@/routes/PATH";
import { useAddGameMutation, useGetGameByIdQuery, useUpdateGameByIdMutation } from "@/services/gameApi";
import { useGetAllProviderQuery } from "@/services/providerApi";
import { showToast, ToastVariant } from "@/slice/toastSlice";
import { gameInitialValues, GameProps } from "@/types/game";
......@@ -20,7 +21,7 @@ const validationSchema = Yup.object({
name: Yup.string().required("Name is required"),
category: Yup.string().required("Category is required"),
description: Yup.string().required("Description is required"),
thumbnail: Yup.mixed().required("Thumbnail is required"),
// thumbnail: Yup.mixed().required("Thumbnail is required"),
api: Yup.string().required("API is required"),
provider: Yup.string().required("Provider is required"),
});
......@@ -30,16 +31,23 @@ export default function AddGameForm({ id }: AddGameFormProps) {
const router = useRouter();
const { data: gameProviders, isLoading } = useGetAllProviderQuery();
const [addGame, { isLoading: addingGame }] = useAddGameMutation();
const { data, isLoading: loadingGameData } = useGetGameByIdQuery({ id: Number(id) }, { skip: !id })
const [updateGame, { isLoading: editing }] = useUpdateGameByIdMutation();
const formik = useFormik<GameProps>({
initialValues: gameInitialValues,
initialValues: data ? {
name: data.data?.name,
category: data.data?.category,
description: data.data?.description,
thumbnail: null,
screenshots: [],
subgames: [],
api: data.data?.api,
provider: data.data?.provider,
profit: data.data?.profit,
} : gameInitialValues,
validationSchema,
enableReinitialize: true,
onSubmit: async (values) => {
if (id) {
console.log("Editing")
}
else {
try {
console.log(values);
const formData = new FormData();
formData.append("name", values.name);
......@@ -49,25 +57,48 @@ export default function AddGameForm({ id }: AddGameFormProps) {
formData.append("provider", values.provider);
if (values.profit) formData.append("profit", values.profit);
// Thumbnail (single file)
if (values.thumbnail instanceof File) {
formData.append("thumbnail", values.thumbnail);
}
// Screenshots as indexed array
if (values.screenshots && Array.isArray(values.screenshots)) {
values.screenshots.forEach((file, index) => {
formData.append(`screenshots[${index}]`, file);
});
}
// Subgames as indexed array
if (values.subgames && Array.isArray(values.subgames)) {
values.subgames.forEach((file, index) => {
formData.append(`subgames[${index}]`, file);
});
}
if (id) {
if (data?.data.subgames && Array.isArray(data?.data.subgames)) {
data?.data.subgames.forEach((file, index) => {
formData.append(`subgames_files[${index}]`, file);
});
}
}
if (id) {
if (data?.data.screenshots && Array.isArray(data?.data.screenshots)) {
data?.data.screenshots.forEach((file, index) => {
formData.append(`screenshots_files[${index}]`, file);
});
}
}
if (id) {
const response = await updateGame({ id: id, body: formData }).unwrap();
dispatch(
showToast({
message: response.message,
variant: ToastVariant.SUCCESS
})
)
}
else {
try {
const response = await addGame(formData).unwrap();
dispatch(
showToast({
......@@ -75,6 +106,7 @@ export default function AddGameForm({ id }: AddGameFormProps) {
variant: ToastVariant.SUCCESS
})
)
router.push(PATH.ADMIN.GAMES.ROOT)
}
catch (e: any) {
console.log(e);
......@@ -122,6 +154,7 @@ export default function AddGameForm({ id }: AddGameFormProps) {
value={formik.values.thumbnail || null}
onChange={(file: File | File[] | null) => formik.setFieldValue("thumbnail", file)}
onBlur={() => formik.setFieldTouched("thumbnail", true)}
serverFile={data?.data?.thumbnail}
/>
<span className="error">
{formik.touched.thumbnail && formik.errors.thumbnail ? formik.errors.thumbnail : ""}
......@@ -179,6 +212,7 @@ export default function AddGameForm({ id }: AddGameFormProps) {
}
}
onBlur={() => formik.setFieldTouched("screenshots", true)}
serverFile={data?.data?.screenshots}
/>
</div>
......@@ -200,6 +234,7 @@ export default function AddGameForm({ id }: AddGameFormProps) {
}
}
onBlur={() => formik.setFieldTouched("subgames", true)}
serverFile={data?.data?.subgames}
/>
</div>
</div>
......
"use client"
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';
import { useParams } from 'next/navigation';
import { useGetGameByIdQuery } from '@/services/gameApi';
import { PATH } from '@/routes/PATH';
import { renderHTML } from '@/utils/RenderHTML';
export default function GameDetailPage() {
const screenshots = Array.from({ length: 12 }).map(
() => "/assets/images/auth-image.png"
);
const params = useParams();
const id = params.slug;
const { data, isLoading } = useGetGameByIdQuery({ id: Number(id) })
const screenshots = data?.data?.screenshots || [];
const relatedGames = data?.data?.subgames || [];
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>
{data?.data?.thumbnail ? <div className="lg:col-span-5 xl:col-span-4 game__featured__image relative aspect-[338/338] rounded-[10px] overflow-hidden">
<Image src={data?.data?.thumbnail || ""} alt="Game Name" fill className="object-cover" />
</div> : null}
<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">
{
data?.data?.active_users ?
<strong className="text-bold block mb-6 text-[12px]">
Active Players:{" "}
<span className="bg-primary-light rounded-[20px] py-1 px-2">
6985
{data.data.active_users}
</span>
</strong>
<h1 className='text-[24px] leading-[133%] mb-4'>Panda Master</h1>
</strong> : ""
}
{data?.data?.name ? <h1 className='text-[24px] leading-[133%] mb-4'>{data?.data?.name}</h1> : ""}
<ul className="flex justify-start items-center gap-12">
<li className='flex gap-2' >
{data?.data?.category ? <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
{data?.data?.category}
</strong>
</li>
</li> : ""}
{data?.data?.category ?
<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
{data?.data?.provider}
</strong>
</li>
</li> : ""}
</ul>
</div>
<Link href={"/games/1"} className='ss-btn bg-primary-grad max-w-fit text-white'>Edit Game Details</Link>
</div>
<Link
href={`${PATH.ADMIN.GAMES.EDIT_GAME.ROOT}/${data?.data?.id}`}
className='ss-btn bg-primary-grad max-w-fit text-white'
>
Edit Game Details
</Link>
<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>
{data?.data?.description ? <div className="general-content-box content mb-6">
{renderHTML(data.data.description)}
</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>
......
......@@ -38,7 +38,6 @@ function GameSkeleton() {
}
export default function AdminGameList() {
const { data, isLoading } = useGetAllGamesQuery();
console.log(data);
return (
<div className="admin__games grid md:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-6">
{isLoading &&
......@@ -77,11 +76,11 @@ export default function AdminGameList() {
</ul>
{/* Active Players */}
{game.activePlayers && (
{game.active_users && (
<strong className="text-bold block mb-4 text-[12px]">
Active Players:{" "}
<span className="bg-primary-light rounded-[20px] py-1 px-2">
{game.activePlayers}
{game.active_users}
</span>
</strong>
)}
......
......@@ -61,7 +61,6 @@ export default function AddPlayerForm({ id }: { id?: string | number }) {
else {
try {
console.log(values);
const formData = new FormData();
// Required fields
......
......@@ -23,7 +23,7 @@ export const PATH = {
ROOT: "/games/add-game"
},
EDIT_GAME: {
ROOT: "/games/edit-game/"
ROOT: "/games/edit-game"
}
},
PLAYERS: {
......@@ -32,7 +32,7 @@ export const PATH = {
ROOT: "/players/add-player"
},
EDIT_PLAYER: {
ROOT: "/players/edit-player/"
ROOT: "/players/edit-player"
}
}
},
......
import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./baseQuery";
import { GameProps, GameResponseProps } from "@/types/game";
import { GameProps, GameResponseProps, SingleGameResponse } from "@/types/game";
import { GlobalResponse } from "@/types/config";
export const gameApi = createApi({
......@@ -23,25 +23,26 @@ export const gameApi = createApi({
}),
providesTags: ['games']
}),
getGameById: builder.query<GameProps[], { id: number | string }>({
getGameById: builder.query<SingleGameResponse, { id: string | number }>({
query: ({ id }) => ({
url: `/api/admin/games/${id}`,
url: `/api/admin/game/${id}`,
method: 'GET',
}),
}),
updateGameById: builder.mutation<GameProps[], { id: number }>({
query: ({ id }) => ({
url: `/api/admin/games/${id}`,
method: "POST"
updateGameById: builder.mutation<SingleGameResponse, { id: string | number, body: FormData }>({
query: ({ id, body }) => ({
url: `/api/admin/game/${id}`,
method: "POST",
body: body
})
}),
deleteGameById: builder.mutation<GlobalResponse, { id: number }>({
deleteGameById: builder.mutation<GlobalResponse, { id: string | number }>({
query: ({ id }) => ({
url: `/api/admin/games/${id}`,
url: `/api/admin/game/${id}`,
method: "DELETE"
})
})
})
})
export const { useAddGameMutation, useGetAllGamesQuery, useLazyGetGameByIdQuery, useUpdateGameByIdMutation, useDeleteGameByIdMutation } = gameApi;
\ No newline at end of file
export const { useAddGameMutation, useGetAllGamesQuery, useGetGameByIdQuery, useUpdateGameByIdMutation, useDeleteGameByIdMutation } = gameApi;
\ No newline at end of file
export interface GameProps {
export interface FileResponse {
file_name: string;
mime_type: string;
size: number;
contents: string;
}
export interface CommonGameProps {
name: string;
category?: string;
description: string;
thumbnail?: File | null;
screenshots?: File | File[] | null;
subgames?: File | File[] | null;
api: string;
provider: string;
profit?: string;
}
export interface GameProps extends CommonGameProps {
thumbnail?: File | null;
screenshots?: File | File[] | null;
subgames?: File | File[] | null;
screenshots_files?: string[];
subgames_files?: string[];
}
export interface GameItem extends GameProps {
export interface GameItem extends CommonGameProps {
id: number;
activePlayers?: string | number
active_users?: string | number
thumbnail?: string | null;
screenshots?: string[] | null;
subgames?: string[] | null;
}
export interface Pagination {
......@@ -27,7 +40,14 @@ export interface GameResponseProps {
data: {
data: GameItem[];
pagination: Pagination;
message: string;
success: boolean;
}
}
export interface SingleGameResponse {
data: GameItem;
pagination: Pagination;
message: string;
success: boolean;
}
......
export function renderHTML(content: string) {
return (
<div
dangerouslySetInnerHTML={{ __html: content }
}
/>
);
}
......@@ -22,6 +22,6 @@
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "src/utils/RenderHTML.tss"],
"exclude": ["node_modules"]
}
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