diff --git a/src/Components/Banner/AddBanner.jsx b/src/Components/Banner/AddBanner.jsx index c8ec5fd..9ec776c 100644 --- a/src/Components/Banner/AddBanner.jsx +++ b/src/Components/Banner/AddBanner.jsx @@ -31,6 +31,7 @@ import Header from "../Header"; import ToastBox from "../ToastBox"; import BannerMainCard from "./BannerMainCard"; + const AddBanner = ({ createApi, navigateLink, title, center }) => { const toast = useToast(); const navigate = useNavigate(); diff --git a/src/Components/FormField.jsx b/src/Components/FormField.jsx index 2c67452..0853ad2 100644 --- a/src/Components/FormField.jsx +++ b/src/Components/FormField.jsx @@ -520,7 +520,7 @@ const FormField = ({ ps={1} {...field} {...props} size='md' colorScheme='forestGreen'> - Is This Sharia Compliant + Is This Shariah Compliant );} else{ diff --git a/src/Components/Pagination.jsx b/src/Components/Pagination.jsx index 488775d..78d914d 100644 --- a/src/Components/Pagination.jsx +++ b/src/Components/Pagination.jsx @@ -49,7 +49,7 @@ const Pagination = ({ value={pageSize} onChange={handlePageSizeChange} > - {[15, 20, 30]?.map((size) => ( + {[15, 20, 30, 500]?.map((size) => ( diff --git a/src/Constants/Paginations.js b/src/Constants/Paginations.js index e657279..8c134dc 100644 --- a/src/Constants/Paginations.js +++ b/src/Constants/Paginations.js @@ -1,2 +1,3 @@ export const TABLE_PAGINATION = { page: 1, size: 20 } -export const IMAGE_URI = import.meta.env.VITE_API_IMAGE_URL \ No newline at end of file +export const IMAGE_URI = import.meta.env.VITE_API_IMAGE_URL +export const INVESTOR_TABLE_PAGINATION = { page: 1, size: 500 } \ No newline at end of file diff --git a/src/Layout/DefaultLayout.jsx b/src/Layout/DefaultLayout.jsx index 23044a7..f615b1e 100644 --- a/src/Layout/DefaultLayout.jsx +++ b/src/Layout/DefaultLayout.jsx @@ -148,7 +148,7 @@ const DashboardLayout = ({ isOnline }) => { case path.startsWith("/email"): return ( - Email Notifiation + Email Notification ); case path.startsWith("/investment-type"): @@ -167,7 +167,7 @@ const DashboardLayout = ({ isOnline }) => { return ( - Echange rate + Exchange rate ); case path.startsWith("/create-io"): @@ -297,7 +297,7 @@ const DashboardLayout = ({ isOnline }) => { return ( - Notification + Push Notification ); case path.startsWith("/contact"): diff --git a/src/Pages/Admin/Notification.jsx b/src/Pages/Admin/Notification.jsx index 19e3d80..874a28a 100644 --- a/src/Pages/Admin/Notification.jsx +++ b/src/Pages/Admin/Notification.jsx @@ -3,11 +3,14 @@ import { Badge, Box, Button, + HStack, + Input, + Select, Text, Tooltip, useToast, } from "@chakra-ui/react"; -import { useForm} from "react-hook-form"; +import { useForm } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; import { useNavigate } from "react-router-dom"; @@ -23,15 +26,13 @@ import ToastBox from "../../Components/ToastBox"; import NormalTable from "../../Components/DataTable/NormalTable"; import GlobalStateContext from "../../Contexts/GlobalStateContext"; import { useGetInvestorsQuery } from "../../Services/investor.details.service"; -import { TABLE_PAGINATION } from "../../Constants/Paginations"; +import { INVESTOR_TABLE_PAGINATION, TABLE_PAGINATION } from "../../Constants/Paginations"; import { formatDate, generateSerialNumber } from "../../Constants/Constants"; import { ViewIcon } from "@chakra-ui/icons"; import { useGetUnbanInvestorQuery } from "../../Services/ban.investor.service"; export const notification = yup.object().shape({ - title: yup - .string() - .required("Investment Name is required"), + title: yup.string().required("Notification Header is required"), ManualDate: yup .date() .required("Manual Date is required") @@ -43,33 +44,26 @@ export const notification = yup.object().shape({ /^([01]\d|2[0-3]):?([0-5]\d)$/, "Invalid time format, must be in HH:mm" ), - expectedReturn: yup - .string() - .required("Expected Return is required"), + expectedReturn: yup.string().required("Expected Return is required"), }); export const notificationNew = yup.object().shape({ - title: yup - .string() - .required("Investment Name is required"), - message: yup - .string() - .required("Message is required"), - + title: yup.string().required("Notification Header is required"), + message: yup.string().required("Message is required"), }); - - const Notification = () => { const toast = useToast(); const navigate = useNavigate(); const [form, setForm] = useState({}); const [isLoading, setIsLoading] = useState(false); - const [ selectedRadio, setSelectedRadio] = useState([]) - const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size); - const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page); + const [selectedRadio, setSelectedRadio] = useState([]); + const [pageSize, setPageSize] = useState(INVESTOR_TABLE_PAGINATION?.size); + const [currentPage, setCurrentPage] = useState(INVESTOR_TABLE_PAGINATION?.page); const [searchTerm, setSearchTerm] = useState(""); const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(""); + const [country, setCountry] = useState(""); + const [kyc, setKyc] = useState(""); const { control, @@ -80,21 +74,20 @@ const Notification = () => { } = useForm({ resolver: yupResolver(notificationNew), - defaultValues: { - title: '', - message: '', - }, + defaultValues: { + title: "", + message: "", + }, }); console.log(errors); - + const { data: contact, isLoading: contactLoading, error, } = useGetContactQuery(); - const formatDate = (date) => { return new Date(date).toLocaleDateString("en-GB", { day: "2-digit", @@ -109,28 +102,42 @@ const Notification = () => { // // error, // } = useGetInvestorsQuery({ page: currentPage, size: pageSize }); + useEffect(() => { + const handler = setTimeout(() => { + setDebouncedSearchTerm(searchTerm.trim()); // Trim to remove leading/trailing spaces + }, 300); + return () => clearTimeout(handler); + }, [searchTerm]); + - const { - data : investorDetails, - isLoading: investorDetailsLoading, - refetch, - } = useGetUnbanInvestorQuery({ - page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search - size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search - search: debouncedSearchTerm, - }, - { - skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request - });; + const { data: investorDetails, isLoading: investorDetailsLoading, refetch } = + useGetUnbanInvestorQuery( + { + page: 1, // Omit pagination for search + size: 10000, // Omit pagination for search + // page: debouncedSearchTerm ? undefined : currentPage, // Disable pagination for search + // size: debouncedSearchTerm ? undefined : 10000 || pageSize || 500, // Disable pagination for search + search: debouncedSearchTerm, // Pass search term + country_xid: country, + KYCStatus: kyc, + }, + { + skip: searchTerm !== "" && debouncedSearchTerm === "", // Skip if search not debounced yet + } + ); + // useEffect(() => { + // console.log("Search Term:", searchTerm); + // console.log("Debounced Search Term:", debouncedSearchTerm); + // console.log("Investor Details:", investorDetails); + // }, [searchTerm, debouncedSearchTerm, investorDetails]); + console.log(investorDetails); - const [sendNotification] = useSendNotificationMutation(); - if (contactLoading) { return ; } @@ -141,9 +148,11 @@ const Notification = () => { placeHolder: " ", name: "title", type: "text", - width:"100%", - maxLength:100, - helperText:`Maximum length should be 100 characters. You have entered ${watch()?.title?.length || 0} characters.`, + width: "100%", + maxLength: 100, + helperText: `Maximum length should be 100 characters. You have entered ${ + watch()?.title?.length || 0 + } characters.`, isRequired: true, section: "Send Custom Push Notification", // value: contact?.phoneNumber || "", @@ -152,15 +161,16 @@ const Notification = () => { label: "Notification Message", placeHolder: " ", name: "message", - width:"100%", + width: "100%", type: "textarea", isRequired: true, - maxLength:200, - helperText:`Maximum length should be 200 characters. You have entered ${watch()?.message?.length || 0} characters.`, + maxLength: 200, + helperText: `Maximum length should be 200 characters. You have entered ${ + watch()?.message?.length || 0 + } characters.`, section: "Send Custom Push Notification", // value: contact?.phoneNumber || "", }, - ]; const groupedFields = formFields.reduce((groups, field) => { @@ -173,55 +183,47 @@ const Notification = () => { }, {}); const onSubmit = async (data) => { - const dataToPass = { ...data, - principal_xid:selectedRadio - } + principal_xid: selectedRadio, + }; setIsLoading(true); try { const res = await sendNotification(dataToPass); console.log(res); - + if (res?.error) { toast({ render: () => ( ), }); - setIsLoading(false) - }else if(res?.data){ + setIsLoading(false); + } else if (res?.data) { toast({ - render: () => ( - - ), + render: () => , }); - setIsLoading(false) - setSelectedRadio([]) + setIsLoading(false); + setSelectedRadio([]); reset({ - title: '', // Resetting specific fields - message: '', + title: "", // Resetting specific fields + message: "", }); // Clears the form fields - }else{ + } else { toast({ render: () => ( - + ), }); - setIsLoading(false) + setIsLoading(false); } } catch (error) { console.log(error); setIsLoading(false); } - - }; - - - // ====================================================[Table Setup]================================================================ const tableHeadRow = [ "Sr N/O", @@ -235,7 +237,6 @@ const Notification = () => { "KYC Status", ]; - const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({ id: item?.principal_xid, "Sr N/O": ( @@ -245,7 +246,7 @@ const Notification = () => { color={"gray.600"} className="d-flex align-items-center fw-bold web-text-small" > - {generateSerialNumber(idx,currentPage, pageSize )} + {generateSerialNumber(idx, currentPage, pageSize)} ), Date: ( @@ -305,13 +306,14 @@ const Notification = () => { color={item?.KYCStatus === false ? "red" : "blue"} px={2} py={0.5} - variant={'ghost'} + variant={"ghost"} > - {item?.KYCStatus === true ? "Completed" : "Incompleted"} + {item?.KYCStatus === true ? "Completed" : "Not Completed"} ), })); + return ( @@ -322,18 +324,77 @@ const Notification = () => { onSubmit={handleSubmit(onSubmit)} btnLoading={isLoading} > - - - + + setSearchTerm(e.target.value)} + /> + + + + + + + + ); diff --git a/src/Pages/EmailNotification/EmailNotification.jsx b/src/Pages/EmailNotification/EmailNotification.jsx index e74981a..22d5c98 100644 --- a/src/Pages/EmailNotification/EmailNotification.jsx +++ b/src/Pages/EmailNotification/EmailNotification.jsx @@ -7,10 +7,11 @@ import { FormLabel, HStack, Input, + Select, Text, useToast, } from "@chakra-ui/react"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { OPACITY_ON_LOAD } from "../../Layout/animations"; import NormalTable from "../../Components/DataTable/NormalTable"; import { useGetUnbanInvestorQuery } from "../../Services/ban.investor.service"; @@ -28,8 +29,11 @@ const EmailNotification = () => { const [subject, setSubject] = useState(""); const [value, setValue] = useState(""); // Quill content (body) const toast = useToast(); - const [sendCustomNotification] = useSendCustomEmailMutation(); + const [searchTerm, setSearchTerm] = useState(""); + const [debouncedSearchTerm, setDebouncedSearchTerm] = useState(""); + const [country, setCountry] = useState(""); + const [kyc, setKyc] = useState(""); // ===========================[Table Setup]============================== const tableHeadRow = [ @@ -47,14 +51,40 @@ const EmailNotification = () => { const [pageSize, setPageSize] = useState(TABLE_PAGINATION?.size); const [currentPage, setCurrentPage] = useState(TABLE_PAGINATION?.page); + // const { + // data: investorDetails, + // isLoading: investorDetailsLoading, + // refetch, + // } = useGetUnbanInvestorQuery({ + // page: currentPage, // Omit pagination for search + // size: 10000, // Omit pagination for search + // }); + + useEffect(() => { + const handler = setTimeout(() => { + setDebouncedSearchTerm(searchTerm.trim()); // Trim to remove leading/trailing spaces + }, 300); + return () => clearTimeout(handler); + }, [searchTerm]); + const { data: investorDetails, isLoading: investorDetailsLoading, refetch, - } = useGetUnbanInvestorQuery({ - page: currentPage, // Omit pagination for search - size: 10000, // Omit pagination for search - }); + } = useGetUnbanInvestorQuery( + { + page: 1, // Omit pagination for search + size: 10000, // Omit pagination for search + // page: debouncedSearchTerm ? undefined : currentPage, // Omit pagination for search + // size: debouncedSearchTerm ? undefined : pageSize, // Omit pagination for search + search: debouncedSearchTerm, + country_xid: country, + KYCStatus: kyc, + }, + { + skip: debouncedSearchTerm === "" && searchTerm !== "", // Skip if search is empty and it's not the initial request + } + ); const extractedArray = investorDetails?.data?.rows?.map((item, idx) => ({ id: item?.principal_xid, @@ -127,7 +157,7 @@ const EmailNotification = () => { py={0.5} variant={"ghost"} > - {item?.KYCStatus === true ? "Completed" : "Incompleted"} + {item?.KYCStatus === true ? "Completed" : "Not Completed"} ), @@ -135,9 +165,9 @@ const EmailNotification = () => { const modules = { toolbar: [ - // [{ header: "1" }, { header: "2" }, - // // { font: [] } - // ], + // [{ header: "1" }, { header: "2" }, + // // { font: [] } + // ], // [{ size: [] }], ["bold", "italic", "underline", "strike", "blockquote"], [{ list: "ordered" }, { list: "bullet" }], @@ -147,12 +177,15 @@ const EmailNotification = () => { // Main submission logic const handleSend = async (e) => { - e.preventDefault(); // Prevent default form submission + e.preventDefault(); // Prevent default form submission if (!subject || !value) { toast({ render: () => ( - + ), }); return; @@ -161,7 +194,10 @@ const EmailNotification = () => { if (selectedRadio.length === 0) { toast({ render: () => ( - + ), }); return; @@ -172,44 +208,36 @@ const EmailNotification = () => { const emailPayload = { subject, body: value, - principal_xid: selectedRadio, + principal_xid: selectedRadio, }; - try { - const res = await sendCustomNotification(emailPayload) - console.log(res); + const res = await sendCustomNotification(emailPayload); + console.log(res); if (res?.error) { toast({ render: () => ( ), }); - setIsLoading(false) - }else if(res?.data){ + setIsLoading(false); + } else if (res?.data) { + toast({ + render: () => , + }); + setIsLoading(false); + setSubject(""); + setValue(""); + setSelectedRadio([]); + } else { toast({ render: () => ( - + ), }); - setIsLoading(false) - setSubject("") - setValue("") - setSelectedRadio([]) - - }else{ - toast({ - render: () => ( - - ), - }); - setIsLoading(false) + setIsLoading(false); } - - } catch (error) { - - } - + } catch (error) {} }; return ( @@ -247,40 +275,92 @@ const EmailNotification = () => { {/* Entered subject will be reflected on emails subject body. */} - Create Custom body - + {/* We'll never share your email. */} - - - - - - - + + setSearchTerm(e.target.value)} + /> + + + + + + + diff --git a/src/Pages/IO_Management/CreateIO/IOArtifactsVideo.jsx b/src/Pages/IO_Management/CreateIO/IOArtifactsVideo.jsx index 0e68193..c95a79b 100644 --- a/src/Pages/IO_Management/CreateIO/IOArtifactsVideo.jsx +++ b/src/Pages/IO_Management/CreateIO/IOArtifactsVideo.jsx @@ -217,7 +217,7 @@ const IOArtifactsAdd = ({ isOpen, onClose, firstField, actionId, setActionId, da isOpen={alert} onClose={() => setAlert(false)} alertHandler={handleSave} - message={"Are you sure you want to update this artifact?"} + message={"Are you sure you want to add this artifact?"} isLoading={isLoading} /> diff --git a/src/Pages/IO_Management/CreateIO/IOCashDetails/IOCashDetails.jsx b/src/Pages/IO_Management/CreateIO/IOCashDetails/IOCashDetails.jsx index 505ba1d..d341972 100644 --- a/src/Pages/IO_Management/CreateIO/IOCashDetails/IOCashDetails.jsx +++ b/src/Pages/IO_Management/CreateIO/IOCashDetails/IOCashDetails.jsx @@ -107,7 +107,8 @@ const IOCashDetails = () => { {IODetails?.isInvestedAmount - ? isMaker() && ( + ? isMaker() && + IODetails?.ioSatatus !== "Exited" && ( - diff --git a/src/Pages/IO_Management/ViewIO/HeaderModal/DistributionInvestor.jsx b/src/Pages/IO_Management/ViewIO/HeaderModal/DistributionInvestor.jsx index 167c686..e5cff96 100644 --- a/src/Pages/IO_Management/ViewIO/HeaderModal/DistributionInvestor.jsx +++ b/src/Pages/IO_Management/ViewIO/HeaderModal/DistributionInvestor.jsx @@ -495,7 +495,7 @@ const DistributionInvestor = ({ isOpen, onClose }) => { > Save - diff --git a/src/Pages/IO_Management/ViewIO/HeaderModal/UpdateIOStatus.jsx b/src/Pages/IO_Management/ViewIO/HeaderModal/UpdateIOStatus.jsx index 6327f52..1db3014 100644 --- a/src/Pages/IO_Management/ViewIO/HeaderModal/UpdateIOStatus.jsx +++ b/src/Pages/IO_Management/ViewIO/HeaderModal/UpdateIOStatus.jsx @@ -17,6 +17,9 @@ import { ModalHeader, ModalOverlay, FormErrorMessage, + Text, + Textarea, + Box, } from "@chakra-ui/react"; import { useGetIOprepopulateDataQuery, @@ -37,6 +40,8 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => { const { data } = useGetIOprepopulateDataQuery(); const [updateStatusIo] = useUpdateStatusIoMutation(); const [updateCancleStatus] = useUpdateCancleStatusToMutation(); + const [message, setMessage] = useState(null); + const [messageError, setMessageError] = useState(null); // useEffect(() => { // setSelectedStatusId(status?.[0]?.id); @@ -47,11 +52,14 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => { setSelectedStatusId(id); }; - const handleSubmit = async () => { + const handleSubmit = async (data) => { if (!selectedStatusId) { setError("Please select status"); return; } + if (!message) { + return setMessageError("message is required"); + } setError(""); setIsLoading(true); try { @@ -60,9 +68,10 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => { // If selectedItem is 'Cancelled', make the updateCancelStatus API call if (selectedItem === import.meta.env.VITE_STATUS_CANCELLED) { res = await updateCancleStatus({ - id + id: id, + data: { comments: message }, }); - } + } // Otherwise, make the updateStatusIo API call else { res = await updateStatusIo({ @@ -72,7 +81,7 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => { id, }); } - + console.log("API Response:", res); setIsLoading(false); handleClose(); @@ -84,6 +93,8 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => { const handleClose = () => { setSelectedItem(null); setSelectedStatusId(null); + setMessage(null); + setMessageError(null); onClose(); setError(""); }; @@ -121,7 +132,8 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => { colorScheme={ selectedItem === import.meta.env.VITE_STATUS_DRAFT ? "gray" - : selectedItem === import.meta.env.VITE_STATUS_PROCESSING + : selectedItem === + import.meta.env.VITE_STATUS_PROCESSING ? "yellow" : selectedItem === import.meta.env.VITE_STATUS_OPEN ? "blue" @@ -154,7 +166,7 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => { { colorScheme={ statusAdmin === import.meta.env.VITE_STATUS_DRAFT ? "gray" - : statusAdmin === import.meta.env.VITE_STATUS_PROCESSING + : statusAdmin === + import.meta.env.VITE_STATUS_PROCESSING ? "yellow" : statusAdmin === import.meta.env.VITE_STATUS_OPEN ? "blue" @@ -171,7 +184,8 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => { ? "green" : statusAdmin === import.meta.env.VITE_STATUS_EXITED ? "red" - : statusAdmin === import.meta.env.VITE_STATUS_CANCELLED + : statusAdmin === + import.meta.env.VITE_STATUS_CANCELLED ? "orange" : "purple" } @@ -191,6 +205,24 @@ const UpdateIOStatus = ({ isOpen, onClose, status }) => { {error} + {selectedItem === import.meta.env.VITE_STATUS_CANCELLED && ( + + + Message + +