Commit 0f9f3779 by Arjun Jhukal

updated the notification listing at admin side

parent 4d2d7d0f
...@@ -5,7 +5,7 @@ import React from 'react' ...@@ -5,7 +5,7 @@ import React from 'react'
export default function DashboardProvider({ children }: { children: React.ReactNode }) { export default function DashboardProvider({ children }: { children: React.ReactNode }) {
const [search, setSearch] = React.useState(""); const [search, setSearch] = React.useState("");
const user = useAppSelector(state => state.auth.user); const user = useAppSelector(state => state?.auth.user);
if (user?.role && user.role.toUpperCase() === "ADMIN") { if (user?.role && user.role.toUpperCase() === "ADMIN") {
return <> return <>
<h1 className="text-[24px] leading-[120%] mb-6">Dashboard</h1> <h1 className="text-[24px] leading-[120%] mb-6">Dashboard</h1>
......
...@@ -7,7 +7,6 @@ import React from 'react' ...@@ -7,7 +7,6 @@ import React from 'react'
export default async function GeneralPage(props: { params: Promise<{ slug: string }> }) { export default async function GeneralPage(props: { params: Promise<{ slug: string }> }) {
const { slug } = await props.params; const { slug } = await props.params;
let pageData = null; let pageData = null;
console.log(pageData);
try { try {
pageData = await getPageDetail(slug); pageData = await getPageDetail(slug);
......
...@@ -5,12 +5,15 @@ import { getAllGames, getSubGames, getUsp } from "@/serverApi/game"; ...@@ -5,12 +5,15 @@ import { getAllGames, getSubGames, getUsp } from "@/serverApi/game";
import { Tooltip } from "@mui/material"; import { Tooltip } from "@mui/material";
import Image from "next/image"; import Image from "next/image";
import DashboardProvider from "./DashboardProvider"; import DashboardProvider from "./DashboardProvider";
import { getBanners, getSubBanners } from "@/serverApi/pages";
import Link from "next/link";
export default async function Home() { export default async function Home() {
let games = null; let games = null;
let usps = null; let usps = null;
let subGames = null; let subGames = null;
let banners = null;
let subBanners = null;
try { try {
games = await getAllGames(); games = await getAllGames();
} catch (err) { } catch (err) {
...@@ -35,15 +38,25 @@ export default async function Home() { ...@@ -35,15 +38,25 @@ export default async function Home() {
console.log("❌ Failed to fetch games:", err); console.log("❌ Failed to fetch games:", err);
return <p className="text-red-500">Failed to load games.</p>; return <p className="text-red-500">Failed to load games.</p>;
} }
try {
banners = await getBanners();
console.log({ subGames: subGames.data, usps }) } catch (err) {
console.log("❌ Failed to fetch banner:", err);
return <p className="text-red-500">Failed to load banner.</p>;
}
try {
subBanners = await getSubBanners();
} catch (err) {
console.log("❌ Failed to fetch sub banner:", err);
return <p className="text-red-500">Failed to load sub banner.</p>;
}
// console.log({ subGames: subGames.data, usps })
return ( return (
<DashboardProvider> <DashboardProvider>
<> <>
<Dashboard /> {banners?.data.length ?
<Dashboard slides={banners.data} /> : ""}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4 2xl:grid-cols-5 mb-8"> <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4 2xl:grid-cols-5 mb-8">
{games?.data?.data.map((game) => ( {games?.data?.data.map((game) => (
...@@ -88,90 +101,123 @@ export default async function Home() { ...@@ -88,90 +101,123 @@ export default async function Home() {
))} ))}
</div> </div>
</section> </section>
<div className="dashboard-card-wrapper grid grid-cols-2 gap-5 justify-center">
<div
className="dashboard-card1 flex px-10 gap-2 rounded-[24px]" {subBanners?.data.length ?
style={{ <div className="dashboard-card-wrapper grid grid-cols-2 gap-5 justify-center">
background: "rgba(255, 255, 255, 0.20)", {subBanners?.data?.length > 0 &&
}}> subBanners.data.map((subBanner, index) => (
<div className="py-7 gap-6"> <div
<h1 key={subBanner.name || index}
className="text-[40px] mb-[8px]" className="dashboard-card1 flex justify-between px-10 gap-2 rounded-[24px]"
style={{ style={{
color: "#FBD230", background:
lineHeight: "96%", index % 2 === 0
letterSpacing: "-0.682px", ? "rgba(255, 255, 255, 0.10)"
}}> : "rgba(255, 255, 255, 0.20)",
Welcome BONUS!! }}
</h1> >
<p <div className="py-7 gap-6">
className="text-[13px] mb-[12px]" {/* ✅ Render title if available */}
style={{ lineHeight: "120%", color: "#FBD230" }}> {subBanner?.name && (
10$ on first play. <h1
</p> className="text-[40px] mb-[8px]"
<a style={{
href="#" color: "#FBD230",
className="px-[18px] py-[11px] rounded-[28px]" lineHeight: "96%",
style={{ letterSpacing: "-0.682px",
background: }}
"linear-gradient(270deg, #F9B901 0.09%, #D09F12 95.19%)", >
}}> {subBanner.name}
Play Now </h1>
</a> )}
</div>
<div className="dashboard-card-img aspect-[245/245]"> {/* ✅ Render description if available */}
<img {subBanner?.description && (
src="/assets/images/card1.png" <p
alt="" className="text-[13px] mb-[12px]"
// className="w-[245px] h-[245px]" style={{ lineHeight: "120%", color: "#FBD230" }}
className="h-auto max-w-full " >
style={{ width: "245px", height: "245px" }} {subBanner.description}
/> </p>
</div> )}
</div>
<div {/* ✅ Render CTA button if link exists */}
className="dashboard-card2 flex px-[45px] gap-2 rounded-[24px]" {subBanner?.cta_link && (
style={{ <Link
background: "rgba(255, 255, 255, 0.10)", href={subBanner.cta_link}
}}> className="px-[18px] py-[11px] rounded-[28px] inline-block"
<div className="py-[45px] gap-6"> style={{
<h1 background:
className="text-[40px] mb-[10px]" index % 2 === 0
style={{ ? "linear-gradient(270deg, #D620D9 0.09%, #B40EF0 95.19%)"
color: "#1AF7FE", : "linear-gradient(270deg, #F9B901 0.09%, #D09F12 95.19%)",
letterSpacing: "-0.682px", }}
lineHeight: "96%", target="_blank"
}}> >
Easy.Set.Play Play Now
</h1> </Link>
<p )}
className="text-[13px] mb-[12px]" </div>
style={{ color: "#E7BCFE", lineHeight: "120%" }}>
Join the Fun today. {/* ✅ Render image only if available */}
</p> {subBanner?.image_url && (
<a <div className="dashboard-card-img aspect-[245/245]">
href="#" <Image
className="px-[18px] py-[11px] rounded-[28px]" src={subBanner.image_url}
style={{ alt={subBanner.name || "Sub Banner"}
background: className="h-auto max-w-full"
"linear-gradient(270deg, #D620D9 0.09%, #B40EF0 95.19%)", width={245}
}}> height={245}
Play Now />
</a> </div>
</div> )}
<div className="dashboard-card-img "> </div>
<img ))}
src="/assets/images/card2.png"
alt="" {/* <div
// className="w-[245px] h-[245px]" className="dashboard-card2 flex px-[45px] gap-2 rounded-[24px]"
className="h-auto max-w-full " style={{
style={{ width: "245px", height: "245px" }} background: "rgba(255, 255, 255, 0.10)",
/> }}>
</div> <div className="py-[45px] gap-6">
</div> <h1
</div> className="text-[40px] mb-[10px]"
style={{
color: "#1AF7FE",
letterSpacing: "-0.682px",
lineHeight: "96%",
}}>
Easy.Set.Play
</h1>
<p
className="text-[13px] mb-[12px]"
style={{ color: "#E7BCFE", lineHeight: "120%" }}>
Join the Fun today.
</p>
<a
href="#"
className="px-[18px] py-[11px] rounded-[28px] inline-block"
style={{
background:
"linear-gradient(270deg, #D620D9 0.09%, #B40EF0 95.19%)",
}}>
Play Now
</a>
</div>
<div className="dashboard-card-img ">
<img
src="/assets/images/card2.png"
alt=""
// className="w-[245px] h-[245px]"
className="h-auto max-w-full "
style={{ width: "245px", height: "245px" }}
/>
</div>
</div> */}
</div> : null}
<UspSlider uspData={usps.data || []} /> <UspSlider uspData={usps.data || []} />
</> </>
</DashboardProvider> </DashboardProvider>
); );
} }
...@@ -209,4 +209,8 @@ ...@@ -209,4 +209,8 @@
.lg-backdrop, .lg-backdrop,
.lg-outer { .lg-outer {
z-index: 9999; z-index: 9999;
}
.banner-desc span {
@apply text-[47px] leading-[50%] text-secondary font-[700];
} }
\ No newline at end of file
...@@ -34,7 +34,6 @@ export default function SelectField({ ...@@ -34,7 +34,6 @@ export default function SelectField({
touched, touched,
}: SelectFieldProps) { }: SelectFieldProps) {
const theme = useThemeContext(); const theme = useThemeContext();
console.log("theme", theme);
return ( return (
<div className="input__field"> <div className="input__field">
<InputLabel className="block text-sm font-semibold mb-2"> <InputLabel className="block text-sm font-semibold mb-2">
......
...@@ -7,20 +7,25 @@ import NotificationPage from '../Notification'; ...@@ -7,20 +7,25 @@ import NotificationPage from '../Notification';
import Profile from '../Profile'; import Profile from '../Profile';
import AdminSearchBar from './AdminSearchBar'; import AdminSearchBar from './AdminSearchBar';
import CreatNewRecord from '../CreatNewRecord'; import CreatNewRecord from '../CreatNewRecord';
import { useGetAllNotificationQuery } from '@/services/notificationApi';
export default function AdminHeader() { export default function AdminHeader() {
const [mounted, setMounted] = React.useState(false); // const [mounted, setMounted] = React.useState(false);
React.useEffect(() => { // React.useEffect(() => {
setMounted(true); // setMounted(true);
}, []); // }, []);
const [page, setPage] = React.useState(1);
const [pageSize, setPageSize] = React.useState(10);
const { data } = useGetAllNotificationQuery({ page: page, per_page: pageSize });
console.log(data);
return ( return (
<Box className='flex items-center gap-4 justify-between w-full'> <Box className='flex items-center gap-4 justify-between w-full'>
<AdminSearchBar /> <AdminSearchBar />
<div className="right flex items-center gap-4"> <div className="right flex items-center gap-4">
<CreatNewRecord /> <CreatNewRecord />
<NotificationPage notifications={[]} /> <NotificationPage notifications={data?.data?.data || []} pagination={data?.data?.pagination}/>
<Profile /> <Profile />
</div> </div>
</Box> </Box>
......
...@@ -15,16 +15,16 @@ import { ...@@ -15,16 +15,16 @@ import {
import Fade from '@mui/material/Fade'; // ✅ Import Fade import Fade from '@mui/material/Fade'; // ✅ Import Fade
import { Notification } from '@wandersonalwes/iconsax-react'; import { Notification } from '@wandersonalwes/iconsax-react';
import Link from 'next/link'; import Link from 'next/link';
import { NotificationProps } from '@/types/notification';
import { Pagination } from '@/types/game';
type Notification = {
label: string;
description: string;
link?: string;
}
export default function NotificationPage({ export default function NotificationPage({
notifications notifications,
pagination
}: { }: {
notifications: Notification[] notifications: NotificationProps[]
pagination: Pagination | undefined
}) { }) {
const anchorRef = useRef<HTMLButtonElement | null>(null); const anchorRef = useRef<HTMLButtonElement | null>(null);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
...@@ -38,6 +38,7 @@ export default function NotificationPage({ ...@@ -38,6 +38,7 @@ export default function NotificationPage({
const id = open ? 'popper' : undefined; const id = open ? 'popper' : undefined;
// const [readNotification,{isLoading}]
return ( return (
<Box> <Box>
<IconButton <IconButton
...@@ -79,18 +80,15 @@ export default function NotificationPage({ ...@@ -79,18 +80,15 @@ export default function NotificationPage({
<Typography variant="h3" > <Typography variant="h3" >
Notifications Notifications
</Typography> </Typography>
<Link href={"#"} className='text-[12px] leading-[120%] hover:text-primary-dark font-medium'>View All</Link> {pagination && pagination?.count > 2 ? <Link href={"#"} className='text-[12px] leading-[120%] hover:text-primary-dark font-medium'>View All</Link> : ""}
</div> </div>
{ {
notifications.length ? ( notifications.length ? (
<List className='max-h-[320px] overflow-auto px-1'> <List className='max-h-[320px] overflow-auto px-1'>
{ {
notifications.map((notification) => ( notifications.map((notification, index) => (
<ListItem className='border-b-solid border-b-gray border-b-[1px] !pb-2 mb-2' key={notification.label}> <ListItem className={`border-b-solid border-b-gray-100 border-b-[1px] rounded-sm !p-2 cursor-pointer ${notification.has_read ? "" : "bg-gray-100"} ${index > 0 ? "mb-2 " : ""}`} key={notification.id}>
<Link href={""}> <p className='text-[12px] lg:text-[14px] leading-[120%] text-title'>{notification.message}</p>
<strong className="text-[12px] leading-[120%] font-[500] block mb-1">New User admin404 registerd</strong>
<p className='text-[10px] leading-[120%] text-para-light'>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Nihil, porro!</p>
</Link>
</ListItem> </ListItem>
)) ))
} }
......
...@@ -3,53 +3,100 @@ ...@@ -3,53 +3,100 @@
import React from "react"; import React from "react";
import { useFormik } from "formik"; import { useFormik } from "formik";
import * as Yup from "yup"; import * as Yup from "yup";
import { Button, InputLabel, OutlinedInput, IconButton } from "@mui/material"; import {
Button,
InputLabel,
OutlinedInput,
IconButton,
FormControlLabel,
Typography,
Switch,
} from "@mui/material";
import InputFile from "@/components/atom/InputFile"; import InputFile from "@/components/atom/InputFile";
import { CloseCircle } from "@wandersonalwes/iconsax-react"; import { CloseCircle } from "@wandersonalwes/iconsax-react";
import { useAppDispatch } from "@/hooks/hook"; import { useAppDispatch } from "@/hooks/hook";
import { showToast, ToastVariant } from "@/slice/toastSlice"; import { showToast, ToastVariant } from "@/slice/toastSlice";
import { useGetAllBannerQuery, useUpdateBannerMutation } from "@/services/settingApi";
const validationSchema = Yup.object({
banners: Yup.array().of(
Yup.object({
name: Yup.string().required("Banner title is required"),
description: Yup.string().required("Banner description is required"),
cta_link: Yup.string().required("CTA link is required"),
// image: Yup.mixed().required("Banner image is required"),
type: Yup.boolean(),
})
),
});
export default function BannerSlider() { export default function BannerSlider() {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { data, isLoading } = useGetAllBannerQuery();
const [updateBanner, { isLoading: updating }] = useUpdateBannerMutation();
console.log("banner data", data);
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
banners: [ banners: data?.data?.length
{ ? data.data.map((item: any) => ({
title: "", name: item.name || "",
description: "", description: item.description || "",
cta_link: "", cta_link: item.cta_link || "",
image: null as File | null, image: null as File | null,
}, type: item.type || false,
], image_url: item.image_url || "",
}))
: [
{
name: "",
description: "",
cta_link: "",
image: null as File | null,
type: false,
image_url: "",
},
],
}, },
validationSchema: Yup.object({ validationSchema,
banners: Yup.array().of( enableReinitialize: true,
Yup.object({ onSubmit: async (values) => {
title: Yup.string().required("Banner title is required"), const formData = new FormData();
description: Yup.string().required("Banner description is required"),
cta_link: Yup.string().required("CTA link is required"),
image: Yup.mixed().required("Banner image is required"),
})
),
}),
onSubmit: (values) => {
try { try {
values.banners.forEach((banner, index) => {
formData.append(`banners[${index}][name]`, banner.name);
formData.append(
`banners[${index}][description]`,
banner.description
);
formData.append(`banners[${index}][cta_link]`, banner.cta_link);
formData.append(
`banners[${index}][type]`,
banner.type ? "true" : "false"
);
if (banner.image) {
formData.append(`banners[${index}][image]`, banner.image);
} else if (banner.image_url) {
formData.append(`banners[${index}][image_url]`, banner.image_url);
}
});
const response = await updateBanner(formData).unwrap();
dispatch( dispatch(
showToast({ showToast({
message: "Banner created successfully", message: response?.message || "Banner created successfully",
variant: ToastVariant.ERROR variant: ToastVariant.SUCCESS,
}) })
) );
} } catch (e: any) {
catch (e: any) {
console.log(e); console.log(e);
dispatch( dispatch(
showToast({ showToast({
message: e.message || "Something went wrong", message: e.message || "Something went wrong",
variant: ToastVariant.ERROR variant: ToastVariant.ERROR,
}) })
) );
} }
}, },
}); });
...@@ -62,6 +109,7 @@ export default function BannerSlider() { ...@@ -62,6 +109,7 @@ export default function BannerSlider() {
description: "", description: "",
cta_link: "", cta_link: "",
image: null, image: null,
isSubBanner: false,
}, },
]); ]);
}; };
...@@ -75,9 +123,9 @@ export default function BannerSlider() { ...@@ -75,9 +123,9 @@ export default function BannerSlider() {
return ( return (
<form <form
onSubmit={formik.handleSubmit} onSubmit={formik.handleSubmit}
className="form__field__wrapper border-solid border-[1px] border-gray rounded-[16px] mb-6" className="form__field__wrapper border border-gray rounded-[16px] mb-6"
> >
<div className="form__title py-6 px-10 border-b-solid border-b-[1px] border-gray"> <div className="form__title py-6 px-10 border-b border-gray">
<h2 className="text-[20px] leading-[140%] font-bold">Banner Slider</h2> <h2 className="text-[20px] leading-[140%] font-bold">Banner Slider</h2>
</div> </div>
...@@ -90,7 +138,7 @@ export default function BannerSlider() { ...@@ -90,7 +138,7 @@ export default function BannerSlider() {
{formik.values.banners.length > 1 && ( {formik.values.banners.length > 1 && (
<IconButton <IconButton
onClick={() => handleRemoveBanner(index)} onClick={() => handleRemoveBanner(index)}
className="!absolute !top-2 !right-2 !text-red-500 !justify-end !z-[9] max-w-fit" className="!absolute !top-2 !right-2 !text-red-500 !z-[9] max-w-fit"
> >
<CloseCircle size={18} /> <CloseCircle size={18} />
</IconButton> </IconButton>
...@@ -103,16 +151,16 @@ export default function BannerSlider() { ...@@ -103,16 +151,16 @@ export default function BannerSlider() {
</InputLabel> </InputLabel>
<OutlinedInput <OutlinedInput
fullWidth fullWidth
name={`banners[${index}].title`} name={`banners[${index}].name`}
placeholder="Enter Banner Title" placeholder="Enter Banner Title"
value={banner.title} value={banner.name}
onChange={formik.handleChange} onChange={formik.handleChange}
onBlur={formik.handleBlur} onBlur={formik.handleBlur}
/> />
<span className="error"> <span className="error">
{formik.touched.banners?.[index]?.title && {formik.touched.banners?.[index]?.name &&
(formik.errors.banners?.[index] as any)?.title (formik.errors.banners?.[index] as any)?.name
? (formik.errors.banners?.[index] as any).title ? (formik.errors.banners?.[index] as any).name
: ""} : ""}
</span> </span>
</div> </div>
...@@ -171,6 +219,7 @@ export default function BannerSlider() { ...@@ -171,6 +219,7 @@ export default function BannerSlider() {
onBlur={() => onBlur={() =>
formik.setFieldTouched(`banners[${index}].image`, true) formik.setFieldTouched(`banners[${index}].image`, true)
} }
serverFile={data?.data[index]?.image_url}
/> />
<span className="error"> <span className="error">
{formik.touched.banners?.[index]?.image && {formik.touched.banners?.[index]?.image &&
...@@ -179,6 +228,20 @@ export default function BannerSlider() { ...@@ -179,6 +228,20 @@ export default function BannerSlider() {
: ""} : ""}
</span> </span>
</div> </div>
{/* ✅ Banner / Sub Banner Switch */}
<div className="input__field">
<FormControlLabel control={<Switch defaultChecked color="primary"
checked={banner.type}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
formik.setFieldValue(
`banners[${index}].type`,
e.target.checked
)
}
name={`banners[${index}].type`}
/>} label="Mark Sub Banner" sx={{ color: "#000", fontSize: "12px" }} />
</div>
</div> </div>
))} ))}
......
...@@ -3,53 +3,87 @@ ...@@ -3,53 +3,87 @@
import Image from "next/image"; import Image from "next/image";
import { motion, AnimatePresence } from "framer-motion"; import { motion, AnimatePresence } from "framer-motion";
import { useState } from "react"; import { useState } from "react";
import { BannerProps } from "@/types/setting";
import { Button } from "@mui/material";
import { renderHTML } from "@/utils/RenderHTML";
const slides = [ export default function Dashboard({ slides }: { slides: BannerProps[] }) {
{
id: 1,
image: "/assets/images/slider1.png",
},
// {
// id: 2,
// image: "/assets/images/slider2.jpg",
// },
// {
// id: 3,
// image: "/assets/images/slider1.png",
// },
];
export default function Dashboard() {
const [current, setCurrent] = useState(0); const [current, setCurrent] = useState(0);
const handleDragEnd = (_: any, info: any) => {
const swipeThreshold = 100; // min distance to trigger slide change
if (info.offset.x < -swipeThreshold && current < slides.length - 1) {
setCurrent((prev) => prev + 1);
} else if (info.offset.x > swipeThreshold && current > 0) {
setCurrent((prev) => prev - 1);
}
};
return ( return (
<div className="dashboard__root relative w-full mx-auto rounded-2xl mb-8"> <div className="dashboard__root relative w-full mx-auto rounded-2xl mb-8">
<div className="relative h-[240px] w-full overflow-hidden rounded-[50px]"> <AnimatePresence mode="wait">
<AnimatePresence mode="wait"> <motion.div
<motion.img key={slides[current]?.name || current}
key={slides[current].id} className="relative aspect-[1105/240] rounded-3xl overflow-hidden flex justify-center items-center text-center cursor-grab active:cursor-grabbing"
src={slides[current].image} initial={{ opacity: 0, x: 100 }}
alt={`slide-${slides[current].id}`} animate={{ opacity: 1, x: 0 }}
initial={{ opacity: 0, x: 100 }} exit={{ opacity: 0, x: -100 }}
animate={{ opacity: 1, x: 0 }} transition={{ duration: 0.6 }}
exit={{ opacity: 0, x: -100 }} drag="x"
transition={{ duration: 0.6 }} dragConstraints={{ left: 0, right: 0 }}
className="absolute inset-0 w-full h-full object-cover rounded-2xl" onDragEnd={handleDragEnd}
>
<Image
src={slides[current].image_url || ""}
alt={slides[current].name}
fill
className="object-cover z-[-1]"
/> />
</AnimatePresence>
</div> <div className="content relative z-10 px-6">
<div className="content relative z-10 px-6 text-center">
{slides[current]?.name && (
<h1 className="text-[48px] leading-[50%] text-[#3A013F] mb-8">
{slides[current].name}
</h1>
)}
{slides[current]?.description && (
<p className="text-[#600167] text-[13px] leading-[120%] font-[700] mb-5 banner-desc">
{renderHTML(slides[current].description)}
</p>
)}
{slides[current]?.cta_link && (
<Button
variant="contained"
sx={{
borderRadius: 27,
background:
"linear-gradient(270deg, #F9B901 0.09%, #D09F12 95.19%)",
}}
onClick={() => window.open(slides[current].cta_link, "_blank")}
>
Play Now
</Button>
)}
</div>
</div>
</motion.div>
</AnimatePresence>
{/* Dots */} {/* Dots */}
<div className="absolute bottom-[-24px] left-1/2 -translate-x-1/2 flex gap-2"> <div className="justify-center flex gap-2 mt-4">
{slides.map((_, i) => ( {slides.map((_, i) => (
<button <button
key={i} key={i}
onClick={() => setCurrent(i)} onClick={() => setCurrent(i)}
className={`h-[8px] p-0 ${i === current ? "bg-white w-[24px]" : "bg-gray-400 w-[8px]" className={`h-[8px] p-0 cursor-pointer ${i === current ? "bg-white w-[24px]" : "bg-gray-400 w-[8px]"
}`} }`}
/> />
))} ))}
</div> </div>
</div> </div>
) );
} }
...@@ -7,7 +7,6 @@ import React from 'react' ...@@ -7,7 +7,6 @@ import React from 'react'
export default function UserCoin({ slug }: { slug: string }) { export default function UserCoin({ slug }: { slug: string }) {
const { data } = useGetUserBalanceBySlugQuery({ slug }); const { data } = useGetUserBalanceBySlugQuery({ slug });
console.log(data);
return ( return (
<Box sx={{ <Box sx={{
background: "linear-gradient(0deg, rgba(234, 47, 231, 0.10) 0%, rgba(234, 47, 231, 0.10) 100%)", background: "linear-gradient(0deg, rgba(234, 47, 231, 0.10) 0%, rgba(234, 47, 231, 0.10) 100%)",
......
...@@ -51,7 +51,6 @@ export default function WithdrawnHistoryPage() { ...@@ -51,7 +51,6 @@ export default function WithdrawnHistoryPage() {
} }
] ]
console.log(data);
const table = useReactTable({ const table = useReactTable({
data: data?.data?.data || [], data: data?.data?.data || [],
......
...@@ -10,6 +10,7 @@ import { transactionApi } from "@/services/transaction"; ...@@ -10,6 +10,7 @@ import { transactionApi } from "@/services/transaction";
import { userApi } from "@/services/userApi"; import { userApi } from "@/services/userApi";
import { settingApi } from "@/services/settingApi"; import { settingApi } from "@/services/settingApi";
import { pageApi } from "@/services/pageApi"; import { pageApi } from "@/services/pageApi";
import { notificationApi } from "@/services/notificationApi";
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
...@@ -24,6 +25,7 @@ export const store = configureStore({ ...@@ -24,6 +25,7 @@ export const store = configureStore({
[userApi.reducerPath]: userApi.reducer, [userApi.reducerPath]: userApi.reducer,
[settingApi.reducerPath]: settingApi.reducer, [settingApi.reducerPath]: settingApi.reducer,
[pageApi.reducerPath]: pageApi.reducer, [pageApi.reducerPath]: pageApi.reducer,
[notificationApi.reducerPath]: notificationApi.reducer,
}, },
middleware: (getDefaultMiddleware) => middleware: (getDefaultMiddleware) =>
getDefaultMiddleware() getDefaultMiddleware()
...@@ -35,6 +37,7 @@ export const store = configureStore({ ...@@ -35,6 +37,7 @@ export const store = configureStore({
.concat(userApi.middleware) .concat(userApi.middleware)
.concat(settingApi.middleware) .concat(settingApi.middleware)
.concat(pageApi.middleware) .concat(pageApi.middleware)
.concat(notificationApi.middleware)
}) })
......
import { PageRequestProps, PageResponseProps } from "@/types/page"; import { PageRequestProps, PageResponseProps } from "@/types/page";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
import { serverBaseQuery } from "./serverBaseQuery"; import { serverBaseQuery } from "./serverBaseQuery";
import { BannerResponseProps } from "@/types/setting";
export async function getPageDetail(slug: string): Promise<PageResponseProps | undefined> { export async function getPageDetail(slug: string): Promise<PageResponseProps | undefined> {
return serverBaseQuery(`/api/general/page/${slug}`); return serverBaseQuery(`/api/general/page/${slug}`);
} }
export async function getBanners(): Promise<BannerResponseProps> {
return serverBaseQuery(`/api/general/home/banner`)
}
export async function getSubBanners(): Promise<BannerResponseProps> {
return serverBaseQuery(`/api/general/home/banner?type=true`)
}
\ No newline at end of file
import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./baseQuery";
import { NotificationResponse } from "@/types/notification";
import { GlobalResponse, QueryParams } from "@/types/config";
export const notificationApi = createApi({
reducerPath: "notificationApi",
baseQuery: baseQuery,
tagTypes: ['Notification'],
endpoints: (builder) => ({
getAllNotification: builder.query<NotificationResponse, QueryParams>({
query: ({ search, page, per_page }) => {
const params = new URLSearchParams();
if (search) params.append('search', search);
if (page) params.append('page', page.toString());
if (per_page) params.append('page_size', per_page.toString());
const queryString = params.toString();
return {
url: `/api/admin/notifications${queryString ? `?${queryString}` : ''}`,
method: "GET"
}
},
providesTags: ["Notification"]
}),
readNotification: builder.mutation<GlobalResponse, { id: string }>({
query: ({ id }) => ({
url: `/api/admin/notification/${id}`,
method: "POST",
})
})
})
})
export const { useGetAllNotificationQuery } = notificationApi
\ No newline at end of file
import { createApi } from "@reduxjs/toolkit/query/react"; import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "./baseQuery"; import { baseQuery } from "./baseQuery";
import { GlobalResponse } from "@/types/config"; import { GlobalResponse } from "@/types/config";
import { SiteSettingResponseProps } from "@/types/setting"; import { BannerResponseProps, SiteSettingResponseProps } from "@/types/setting";
export const settingApi = createApi({ export const settingApi = createApi({
reducerPath: "settingApi", reducerPath: "settingApi",
baseQuery: baseQuery, baseQuery: baseQuery,
tagTypes: ['settings'], tagTypes: ['settings', 'banners'],
endpoints: (builder) => ({ endpoints: (builder) => ({
updateSetting: builder.mutation<GlobalResponse, FormData>({ updateSetting: builder.mutation<GlobalResponse, FormData>({
query: (body) => ({ query: (body) => ({
...@@ -22,8 +22,24 @@ export const settingApi = createApi({ ...@@ -22,8 +22,24 @@ export const settingApi = createApi({
method: "GET", method: "GET",
}), }),
providesTags: ['settings'] providesTags: ['settings']
}) }),
updateBanner: builder.mutation<GlobalResponse, FormData>({
query: (body) => ({
url: "/api/admin/setting/banner",
method: "POST",
body: body,
}),
invalidatesTags: ['banners']
}),
getAllBanner: builder.query<BannerResponseProps, void>({
query: () => ({
url: "/api/admin/setting/banner",
method: "GET",
}),
providesTags: ['banners']
}),
}) })
}) })
export const { useUpdateSettingMutation, useGetSettingsQuery } = settingApi; export const { useUpdateSettingMutation, useGetSettingsQuery, useUpdateBannerMutation, useGetAllBannerQuery } = settingApi;
\ No newline at end of file \ No newline at end of file
import { Pagination } from "./game";
export interface NotificationProps {
id: string;
message: string,
has_read: boolean
}
export interface NotificationResponse {
success: boolean;
message: string;
data: {
data: NotificationProps[]
pagination: Pagination
}
}
\ No newline at end of file
...@@ -43,3 +43,17 @@ export interface UspProps { ...@@ -43,3 +43,17 @@ export interface UspProps {
icon_url?: string; icon_url?: string;
} }
export interface BannerProps {
name: string;
description: string;
cta_link: string;
image?: File | null;
image_url?: string | null;
type: boolean /** TYPE TRUE REPRESENT SUB BANNER */
}
export interface BannerResponseProps {
message: string;
success: boolean;
data: BannerProps[];
}
\ 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