43 Commits

Author SHA1 Message Date
rockyeverlast
ec9448078e fix: stop tracking .env file 2025-09-19 18:29:56 +05:30
rockyeverlast
5b37c9da1e Profile Image editiing api 2025-09-19 18:12:42 +05:30
rockyeverlast
9bfbf5ddb8 dob disabled for recruiter xid2 2025-09-17 14:04:21 +05:30
rockyeverlast
2e8f8d5a77 Fixed language update for user 2025-09-17 14:01:16 +05:30
rockyeverlast
248980d24b Added default language in user edit 2025-09-17 12:23:55 +05:30
rockyeverlast
8966295260 Bugs fixes and updates 2025-09-15 20:47:26 +05:30
rockyeverlast
df62dcda64 Cell title changed to EMP ID 2025-09-12 21:11:15 +05:30
rockyeverlast
44f34490a0 Added Agency status and fixed ResourceTitle for Manage User 2025-09-12 21:09:00 +05:30
rockyeverlast
8efb309fa9 Merge branch 'dev-rohit' of http://git.wdipl.com/Siddhesh.More/SSA-Admin-Panel into testing 2025-09-12 18:15:04 +05:30
rockyeverlast
171a92a3dc Toast updated successfully 2025-09-12 18:14:26 +05:30
rockyeverlast
ec021ee33c Merge branch 'dev-rohit' of http://git.wdipl.com/Siddhesh.More/SSA-Admin-Panel into testing 2025-09-12 17:59:15 +05:30
rockyeverlast
3aa9ebf619 Last name added to user table 2025-09-12 17:50:43 +05:30
rockyeverlast
8f19a41c57 Admin / Subadmin nav access and bugs fixing 2025-09-12 17:49:14 +05:30
rockyeverlast
638a68ff03 Merge branch 'dev-rohit' of http://git.wdipl.com/Siddhesh.More/SSA-Admin-Panel into testing 2025-09-11 20:21:13 +05:30
rockyeverlast
e5361a5d77 Bug fixes and updates 2025-09-11 20:16:37 +05:30
rockyeverlast
71c23a2125 Merge branch 'dev-rohit' of http://git.wdipl.com/Siddhesh.More/SSA-Admin-Panel into testing 2025-09-10 20:44:01 +05:30
rockyeverlast
abaae9603e env added to git ignore 2025-09-10 20:43:42 +05:30
rockyeverlast
7a3e76aa35 env added to git ignore 2025-09-10 20:42:58 +05:30
rockyeverlast
eaf8e77b79 env file updated 2025-09-10 20:42:08 +05:30
rockyeverlast
2488ab98c0 Merge branch 'dev-rohit' of http://git.wdipl.com/Siddhesh.More/SSA-Admin-Panel into dev-rohit 2025-09-10 20:37:36 +05:30
rockyeverlast
6648cd3bf3 fixed newCustomerCount / Recruiter count in dash 2025-09-10 20:36:15 +05:30
d1cc504397 Updated env with beta dev url 2025-09-10 14:53:43 +00:00
rockyeverlast
1e7b7347e4 Dashboard API integration 2025-09-10 20:18:07 +05:30
rockyeverlast
32ccea4917 commented out disable feature on toggle in agency master 2025-09-09 17:52:55 +05:30
rockyeverlast
1fafdfe54d Edit agency master updated 2025-09-09 17:44:39 +05:30
rockyeverlast
20a92a60ab optional chaining done for language in register user 2025-09-09 17:37:45 +05:30
rockyeverlast
284b5f3099 Bug fixes and updates 2025-09-09 17:30:26 +05:30
rockyeverlast
901dfb93a6 Updated subadmin add / ediit function 2025-09-05 16:56:41 +05:30
rockyeverlast
2805528ba3 Search implemented 2025-07-11 12:51:22 +05:30
rockyeverlast
a8a0e2ffef fixed Country uodated 2025-07-10 18:53:44 +05:30
rockyeverlast
897572c1f1 Merge branch 'dev-rohit' of http://git.wdipl.com/Siddhesh.More/SSA-Admin-Panel into testing 2025-07-10 18:25:27 +05:30
rockyeverlast
03ab1dcd6f Bugs fixes 2025-07-10 18:24:27 +05:30
rockyeverlast
9d8278900d Merge branch 'dev-rohit' of http://git.wdipl.com/Siddhesh.More/SSA-Admin-Panel into testing 2025-07-09 16:56:39 +05:30
rockyeverlast
f65b5f9b2d Bug fixes and API integration sub admin 2025-07-09 16:56:06 +05:30
rockyeverlast
ffda4cf04b Merge branch 'dev-rohit' of http://git.wdipl.com/Siddhesh.More/SSA-Admin-Panel into testing 2025-07-03 16:14:55 +05:30
rockyeverlast
f2d101ef16 Bug fixes 2025-07-03 16:08:52 +05:30
rockyeverlast
b91c97c6c2 Edit and view user API integrated 2025-06-23 18:57:56 +05:30
rockyeverlast
2a0517d8dc Remove .env from repo 2025-06-20 12:07:48 +05:30
rockyeverlast
0c6238c843 Merge branch 'yasin' of http://git.wdipl.com/Siddhesh.More/SSA-Admin-Panel into dev-rohit 2025-06-19 13:10:29 +05:30
rockyeverlast
49e8e5531f updated env file 2025-06-09 11:47:33 +05:30
rockyeverlast
15c9cc1d94 updated env file 2025-06-09 11:36:15 +05:30
rockyeverlast
cb7bdcdeb3 updated env file 2025-06-09 11:28:32 +05:30
rockyeverlast
8106d505a0 changes to env testing files 2025-06-09 11:23:27 +05:30
80 changed files with 3147 additions and 1550 deletions

15
.env
View File

@@ -1,15 +0,0 @@
# VITE_API_URL='https://ssa.betadelivery.com/apia/v1'
# # VITE_API_URL='http://192.16.50.44/seo-backend/apia/v1'
# VITE_USER_NAME="Admin"
# VITE_PASSWORD="71%@L%es^bUX94`J9XT*@bh,._WWM{$%^^&&"
# VITE_APP_NAME=MyViteApp
# VITE_IMG_TEMPLATES='https://ssa.betadelivery.com/storage/app/public/uploads/post_templates/'
VITE_API_URL='https://ssa.betadelivery.com/testing/apia/v1'
# VITE_API_URL='https://ssa.betadelivery.com/apia/v1'
VITE_USER_NAME="Admin"
VITE_PASSWORD="71%@L%es^bUX94`J9XT*@bh,._WWM{$%^^&&"
# VITE_PASSWORD="71%@L%es^bUX94`J9XT*%4&^%tUU^%Q^ffgt"
VITE_APP_NAME=MyViteApp
VITE_IMG_TEMPLATES='https://ssa.betadelivery.com/testing/storage/app/public/uploads/post_templates/'
VITE_MAIN_URL='https://ssa.betadelivery.com/testing'
VITE_POST_IMG=`${VITE_MAIN_URL}/storage/app/public/uploads/post/`

View File

@@ -1,5 +1,7 @@
# VITE_API_URL='https://ssa.betadelivery.com/testing/apia/'
# VITE_USER_NAME="Admin"
# VITE_PASSWORD="71%@L%es^bUX94`J9XT*@bh,._WWM{$%^^&&"
# VITE_APP_NAME=MyViteApp
# VITE_IMG_TEMPLATES='https://ssa.betadelivery.com/storage/app/public/uploads/post_templates/'
VITE_API_URL='https://ssa.betadelivery.com/testing/apia/v1'
VITE_USER_NAME="Admin"
VITE_PASSWORD="71%@L%es^bUX94`J9XT*@bh,._WWM{$%^^&&"
VITE_APP_NAME=MyViteApp
VITE_IMG_TEMPLATES='https://ssa.betadelivery.com/storage/app/public/uploads/post_templates/'
VITE_MAIN_URL='https://ssa.betadelivery.com/testing'
VITE_POST_IMG=`${VITE_MAIN_URL}/storage/app/public/uploads/post/`

2
.gitignore vendored
View File

@@ -22,4 +22,4 @@ dist-ssr
*.njsproj
*.sln
*.sw?
.env
*.env

View File

@@ -82,7 +82,7 @@ define(['./workbox-54d0af47'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812"
}, {
"url": "index.html",
"revision": "0.p75md0vraj"
"revision": "0.73grfmd27h8"
}], {});
workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {

View File

@@ -7,6 +7,8 @@ export interface GlobalStateContextType {
setIsAuthenticate: Dispatch<SetStateAction<boolean>>;
isBarLoading: boolean;
setIsBarLoading: Dispatch<SetStateAction<boolean>>;
userAccess: string[];
setUserAccess: Dispatch<SetStateAction<string[]>>;
}
// Create the context with a default value of `undefined`
const GlobalStateContext = createContext<GlobalStateContextType | undefined>(undefined);

View File

@@ -6,6 +6,10 @@ import GlobalStateContext from "./GlobalStateContext";
const GlobalStateProvider = ({ children }: { children: ReactNode }) => {
const [isAuthenticate, setIsAuthenticate] = useState<boolean>(true);
const [isBarLoading, setIsBarLoading] = useState<boolean>(false); // ✅ Fixed typo
const [userAccess, setUserAccess] = useState<string[]>(() => {
const stored = sessionStorage.getItem('userAccess');
return stored ? JSON.parse(stored) : [];
});
@@ -15,6 +19,8 @@ const GlobalStateProvider = ({ children }: { children: ReactNode }) => {
setIsAuthenticate,
isBarLoading,
setIsBarLoading, // ✅ Fixed typo
userAccess,
setUserAccess
}}>
{children}
</GlobalStateContext.Provider>

View File

@@ -1,6 +1,6 @@
import { HStack, Image, Text, VStack } from "@chakra-ui/react";
import React, { FC, useContext } from "react";
import { RiNotificationLine } from "react-icons/ri";
// import { RiNotificationLine } from "react-icons/ri";
import { NavLink, useLocation, useNavigate } from "react-router-dom";
import { nav } from "../Routes/Nav";
import logo from '../assets/logo.svg';
@@ -14,6 +14,8 @@ import { useLogOutMutation } from "../Redux/Service/apiSlice";
import ProgressBar from "../components/ProgressBar/ProgressBar";
import { useGetProfileQuery } from "../Redux/Service/profile.password";
const PROFILEIMGURL = import.meta.env.VITE_IMG_PROFILE
const DefaultLayout: FC<{ children: React.ReactNode }> = ({ children }) => {
const { data } = useGetProfileQuery()
const dispatch = useDispatch()
@@ -25,9 +27,13 @@ const DefaultLayout: FC<{ children: React.ReactNode }> = ({ children }) => {
if (!context) {
throw new Error('App must be used within a GlobalStateProvider');
}
const { setIsAuthenticate, isBarLoading } = context;
const { setIsAuthenticate, isBarLoading, userAccess } = context;
const [logOutAdmin] = useLogOutMutation()
const filteredNav = nav.filter(item =>
userAccess.includes('*') || !item.resourceTitle || userAccess.includes(item.resourceTitle)
);
// Logout function
const handleLogout = async () => {
@@ -57,7 +63,7 @@ const DefaultLayout: FC<{ children: React.ReactNode }> = ({ children }) => {
<Image w={55} src={logo} />
</HStack>
<VStack w={'100%'} p={2} pt={0}>
{nav?.map(({ title, path, Icon, type, children, initPath }, index) => type === 'single' ?
{filteredNav?.map(({ title, path, Icon, type, children, initPath }, index) => type === 'single' ?
<NavLink className="link" key={index} to={path || ''} style={{ cursor: 'pointer', borderRadius: '8px', padding: '6px', width: '100%', display: 'flex', alignItems: 'center', gap: 6, border: '1px solid #ffffff', backgroundColor: '#fff', color: '#000', boxShadow: 'rgba(99, 99, 99, 0.2) 0px 2px 8px 0px' }} ><Icon style={{ fontSize: '20px' }} /> <Text fontSize={'xs'} w={'100%'}>{title}</Text></NavLink> :
<AccordionRoot border={location?.pathname.startsWith(initPath ?? path) ? "1px solid #02A0A0" : '1px'} key={index} bg={'#fff'} rounded={'lg'} collapsible>
<AccordionItem rounded={'lg'} bg={'#fff'} boxShadow={'rgba(99, 99, 99, 0.2) 0px 2px 8px 0px'} borderBottom={'none'} p={0} key={index} value={title}>
@@ -74,12 +80,12 @@ const DefaultLayout: FC<{ children: React.ReactNode }> = ({ children }) => {
<HStack h={'11%'} w={'100%'} justifyContent={'flex-end'} pe={3} gap={6}>
<NavLink to={'/manage-notification'}><RiNotificationLine color="#013e3e" cursor={'pointer'} style={{ fontSize: '22px' }} /></NavLink>
{/* <NavLink to={'/manage-notification'}><RiNotificationLine color="#013e3e" cursor={'pointer'} style={{ fontSize: '22px' }} /></NavLink> */}
<HStack cursor={'pointer'} onClick={() => navigate('/profile')} >
<Avatar size={'sm'} src="https://i.pinimg.com/736x/d6/cd/0f/d6cd0ffd4634b0763d3958a7325ce26e.jpg" />
<Avatar size={'sm'} src={`${PROFILEIMGURL}${data?.data.profile_photo}`} />
<VStack color={'#013e3e'} gap={0} alignItems={'flex-start'}>
<Text fontSize={'sm'} fontWeight={'bold'}>{`${data?.data?.first_name.charAt(0).toUpperCase()}${data?.data.first_name.slice(1)}`}</Text>
<Text fontSize={'xs'} >{data?.data?.phone_number}</Text>
<Text fontSize={'sm'} fontWeight={'bold'}>{data?.data?.first_name ? `${data?.data?.first_name.charAt(0).toUpperCase()}${data?.data.first_name.slice(1)}` : ''}</Text>
<Text fontSize={'xs'} >{data?.data?.phone_number ? data?.data?.phone_number : ''}</Text>
</VStack>
</HStack>
</HStack>

View File

@@ -1,61 +1,68 @@
import { Box, HStack, Image, Input, Stack, Text } from "@chakra-ui/react";
import React, { useState, useEffect } from "react";
import {
Box,
HStack,
// Image, Input, Stack,
Text
} from "@chakra-ui/react";
// import React, { useState, useEffect } from "react";
import { Button } from "../../components/ui/button";
import { IoAddSharp } from "react-icons/io5";
import delateIcon from "../../assets/deleteIcon.png";
import { FaClockRotateLeft } from "react-icons/fa6";
// import { IoAddSharp } from "react-icons/io5";
// import delateIcon from "../../assets/deleteIcon.png";
// import { FaClockRotateLeft } from "react-icons/fa6";
import { Link } from "react-router-dom";
import { AgencyList } from "../../Redux/Service/dashBoard";
interface Todo {
id: number;
text: string;
completed: boolean;
timestamp: string;
}
// interface Todo {
// id: number;
// text: string;
// completed: boolean;
// timestamp: string;
// }
const AgencyName: React.FC = () => {
const [todos, setTodos] = useState<Todo[]>([]);
const [input, setInput] = useState<string>("");
const getCurrentTime = () => {
const now = new Date();
return now.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
};
const AgencyName = ({ agencyList }: { agencyList: AgencyList }) => {
// const [todos, setTodos] = useState<Todo[]>([]);
// const [input, setInput] = useState<string>("");
const addTodo = () => {
if (input.trim() === "") return;
setTodos([...todos, { id: Date.now(), text: input, completed: false, timestamp: getCurrentTime() }]);
setInput("");
};
// const getCurrentTime = () => {
// const now = new Date();
// return now.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
// };
// const addTodo = () => {
// if (input.trim() === "") return;
// setTodos([...todos, { id: Date.now(), text: input, completed: false, timestamp: getCurrentTime() }]);
// setInput("");
// };
// Delete a task
const deleteTodo = (id: number) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
// const deleteTodo = (id: number) => {
// setTodos(todos.filter((todo) => todo.id !== id));
// };
useEffect(() => {
const savedTodos = localStorage.getItem("todos");
if (savedTodos) {
setTodos(JSON.parse(savedTodos));
}
}, []); // Runs only on mount
// useEffect(() => {
// const savedTodos = localStorage.getItem("todos");
// if (savedTodos) {
// setTodos(JSON.parse(savedTodos));
// }
// }, []); // Runs only on mount
// 🔹 Save todos to localStorage whenever they change
useEffect(() => {
if (todos.length > 0) {
localStorage.setItem("todos", JSON.stringify(todos));
}
}, [todos]); // Runs when `todos` changes
// // 🔹 Save todos to localStorage whenever they change
// useEffect(() => {
// if (todos.length > 0) {
// localStorage.setItem("todos", JSON.stringify(todos));
// }
// }, [todos]); // Runs when `todos` changes
return (
<Box p={"10px"}>
<HStack justifyContent={"space-between"} mb={5}>
<Text fontSize={"xs"} fontWeight={500}>
Add Agency Name
Agency List
</Text>
<Button
{/* <Button
bg={"#fff"}
color={"#222222CC"}
px={3}
@@ -64,9 +71,18 @@ const AgencyName: React.FC = () => {
onClick={addTodo}
>
<IoAddSharp /> Add
</Button> */}
<Button
bg={"#fff"}
color={"#222222CC"}
px={3}
fontSize={"12px"}
h={"28px"}
>
<Link to="/master-module/agency-master">View ALL</Link>
</Button>
</HStack>
<Input
{/* <Input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
@@ -76,14 +92,14 @@ const AgencyName: React.FC = () => {
w={"100%"}
p={2}
mb={4}
/>
{todos.map((todo) => (
/> */}
{agencyList?.data.map((todo) => (
<HStack key={todo.id} backgroundColor={"#fff"} rounded={5} mb={3} p={4} justifyContent={'space-between'} alignItems={'inherit'}>
<Text fontSize={'sm'} color={'#222222bd'} fontWeight={400}>{todo.text}</Text>
<Stack display={'flex'} alignItems={'end'} w={'130px'}>
<Text fontSize={'sm'} color={'#222222bd'} fontWeight={400}>{todo.name}</Text>
{/* <Stack display={'flex'} alignItems={'end'} w={'130px'}>
<Box display={'flex'} alignItems={'center'} gap={2}>
<FaClockRotateLeft fontSize={'10px'} style={{fontSize:'13px',color:'#222222bd'}} />
<Text fontSize={"xs"} color={"#222222bd"}>{todo.timestamp}</Text>
<FaClockRotateLeft fontSize={'10px'} style={{ fontSize: '13px', color: '#222222bd' }} />
<Text fontSize={"xs"} color={"#222222bd"}>{todo.timestamp}</Text>
</Box>
<Box
onClick={() => deleteTodo(todo.id)}
@@ -93,7 +109,7 @@ const AgencyName: React.FC = () => {
>
<Image w={"16px"} src={delateIcon} />
</Box>
</Stack>
</Stack> */}
</HStack>
))}
</Box>

View File

@@ -26,8 +26,31 @@ import {
SelectValueText
} from "../../components/ui/select";
import AgencyName from "./AgencyName";
import { useGetAgencyListQuery, useGetFaqListQuery, useGetNewUserQuery, useGetPastUserQuery, useGetTotalUserQuery } from "../../Redux/Service/dashBoard";
import { useEffect, useState } from "react";
import { Link } from "react-router-dom";
const Dashboard = () => {
const { data: totalUsers } = useGetTotalUserQuery()
const { data: pastUsers } = useGetPastUserQuery()
const { data: newUsers } = useGetNewUserQuery()
const {data: agencyList} = useGetAgencyListQuery()
const { data: faqList } = useGetFaqListQuery()
const [activeTab, setActiveTab] = useState("tab-1");
const [totalUser, setTotalUser] = useState<any>(null);
console.log("data", totalUser)
useEffect(() => {
if (activeTab === "tab-1") {
setTotalUser( pastUsers?.data)
} else if (activeTab === "tab-2") {
setTotalUser(totalUsers?.data)
} else if (activeTab === "tab-3") {
setTotalUser(newUsers?.data)
}
}, [totalUsers?.data, pastUsers?.data, newUsers?.data, activeTab]);
const frameworks = createListCollection({
items: [
{ label: "Today", value: "Today" },
@@ -37,35 +60,35 @@ const Dashboard = () => {
],
});
const accItems = [
{
value: "1",
title: "How to create new account?",
text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since. Lorem Ipsum has been the industry's standard dummy text ever since.",
},
{
value: "2",
title: "How to create new account?",
text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since. Lorem Ipsum has been the industry's standard dummy text ever since.",
},
{
value: "3",
title: "How to create new account?",
text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since. Lorem Ipsum has been the industry's standard dummy text ever since.",
},
{
value: "4",
title: "How to create new account?",
text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since. Lorem Ipsum has been the industry's standard dummy text ever since.",
},
];
// const accItems = [
// {
// value: "1",
// title: "How to create new account?",
// text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since. Lorem Ipsum has been the industry's standard dummy text ever since.",
// },
// {
// value: "2",
// title: "How to create new account?",
// text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since. Lorem Ipsum has been the industry's standard dummy text ever since.",
// },
// {
// value: "3",
// title: "How to create new account?",
// text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since. Lorem Ipsum has been the industry's standard dummy text ever since.",
// },
// {
// value: "4",
// title: "How to create new account?",
// text: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since. Lorem Ipsum has been the industry's standard dummy text ever since.",
// },
// ];
return (
<MainFrame>
<Box display={"flex"} p={"20px"} pe={'20px'} gap={5}>
<Box display={"flex"} p={"20px"} pe={'20px'} gap={5}>
<Box rounded={'lg'} w={"30%"} boxShadow={"rgba(99, 99, 99, 0.2) 0px 2px 8px 0px"}>
<Heading fontSize={"sm"} p={2}>
Total Users
Total Users : {totalUsers?.data?.totalUserCount ?? 0}
</Heading>
<Tabs.Root
size={"sm"}
@@ -74,6 +97,8 @@ const Dashboard = () => {
variant="enclosed"
fitted
defaultValue={"tab-1"}
value={activeTab}
onValueChange={(details) => setActiveTab(details.value)}
mb={6}
>
<Tabs.List>
@@ -89,7 +114,7 @@ const Dashboard = () => {
</Tabs.List>
</Tabs.Root>
<Box>
<SemiDoughnutChart />
{totalUser && <SemiDoughnutChart totalUser={totalUser} />}
</Box>
<Box
@@ -101,11 +126,11 @@ const Dashboard = () => {
>
<Status.Root colorPalette="blue">
<Status.Indicator />
Recruiter <Text fontWeight={500}>2554</Text>
Recruiter <Text fontWeight={500}>{totalUser?.past24hourRecruiterCount ?? totalUser?.totalRecruiterCount ?? totalUser?.newRecuiterCount }</Text>
</Status.Root>
<Status.Root colorPalette="blue">
<Status.Indicator />
Customer <Text fontWeight={500}>1224</Text>
Customer <Text fontWeight={500}>{totalUser?.past24hourCustomercount ?? totalUser?.totalCustomerCount ?? totalUser?.newCustomerCount}</Text>
</Status.Root>
</Box>
</Box>
@@ -142,8 +167,8 @@ const Dashboard = () => {
<CircularApp />
</Box>
</Box>
<Box p={"20px"} pt={0} display={"flex"} gap={5}>
<Box w={"50%"} rounded={'lg'} bg={"#f2f2f2"} h={'100%'} p={"10px"} overflow={'auto'}>
<Box p={"20px"} pt={0} display={"flex"} gap={5}>
<Box w={"50%"} rounded={'lg'} bg={"#f2f2f2"} h={'100%'} p={"10px"} overflowY={'scroll'} height={'292px'}>
<HStack justifyContent={"space-between"} mb={5}>
<Text fontSize={"xs"} fontWeight={500}>Faqs</Text>
<Button
@@ -153,15 +178,15 @@ const Dashboard = () => {
fontSize={"12px"}
h={"28px"}
>
View ALL
<Link to="/manage-cms/faq">View ALL</Link>
</Button>
</HStack>
<AccordionRoot collapsible defaultValue={["b"]}>
{accItems.map((item, index) => (
{faqList?.data.map((item) => (
<AccordionItem
boxShadow={'rgba(99, 99, 99, 0.2) 0px 2px 8px 0px'}
key={index}
value={item.value}
boxShadow={'rgba(99, 99, 99, 0.2) 0px 2px 8px 0px'}
key={item.id}
value={item.faqs_xid.toString()}
bg={"#fff"}
mb={2}
p={"12px"}
@@ -169,17 +194,17 @@ const Dashboard = () => {
borderBottom={0}
>
<AccordionItemTrigger fontSize={"sm"} >
{item.title}
{item.question}
</AccordionItemTrigger>
<AccordionItemContent fontSize={"xs"} color={'#222222CC'} pt={2}>
{item.text}
{item.answer}
</AccordionItemContent>
</AccordionItem>
))}
</AccordionRoot>
</Box>
<Box w={"50%"} rounded={'lg'} bg={"#f2f2f2"} h={'100%'} overflow={'auto'}>
<AgencyName />
<Box w={"50%"} rounded={'lg'} bg={"#f2f2f2"} alignItems={'flex-start'} overflowY={'scroll'} height={'292px'}>
{agencyList && <AgencyName agencyList={agencyList}/>}
</Box>
</Box>
</MainFrame>

View File

@@ -34,7 +34,7 @@ const Login = () => {
if (!context) {
throw new Error("App must be used within a GlobalStateProvider");
}
const { setIsAuthenticate } = context;
const { setIsAuthenticate, setUserAccess } = context;
const {
register,
handleSubmit,
@@ -43,10 +43,10 @@ const Login = () => {
const onSubmit = handleSubmit(async (data) => {
setIsLoading(true);
// Encode Basic Auth Credentials
const username = import.meta.env.VITE_USER_NAME || ""; // Replace with actual username
const password = import.meta.env.VITE_PASSWORD || ""; // Replace with actual password
const basicAuth = `${username}:${password}`; // Encode to Base64
const username = import.meta.env.VITE_USER_NAME || "";
const password = import.meta.env.VITE_PASSWORD || "";
const basicAuth = `${username}:${password}`;
try {
const res = await axios.post(
@@ -62,35 +62,36 @@ const Login = () => {
},
}
);
console.log("============", res);
const loginData = res.data?.data;
const { principal_type_xid, role_permission } = loginData;
const isAdmin = principal_type_xid === 1;
const allowedTitles = isAdmin
? ['*']
: role_permission?.get_resource_action_link
?.filter((link: unknown) => (link as any).is_active)
.map((link: unknown) => (link as any).app_resource.app_resource_title) ?? [];
setIsAuthenticate(true);
setUserAccess(allowedTitles);
dispatch(setToken(String(loginData["access-token"])));
sessionStorage.setItem('userAccess', JSON.stringify(allowedTitles));
navigate("/dashboard");
setIsLoading(false);
if (res.data) {
setIsAuthenticate(true);
console.log("====================================");
console.log(res.data?.data);
console.log("====================================");
navigate("/dashboard");
dispatch(setToken(String(res.data?.data["access-token"])));
} else {
console.log("====================================");
console.log(res);
console.log("====================================");
}
} catch (error: any) {
console.log('error', error.response.data.message);
if (error) {
toaster.create({
title: error?.response?.data?.message,
// title: "Something Went Wrong",
type: "info",
})
// console.log("Login failed", error?.response?.data?.message);
setIsLoading(false);
}
console.log('error', error?.response?.data?.message);
toaster.create({
title: "Error",
description: error?.response?.data?.message || "Login failed",
type: "error",
});
setIsLoading(false);
}
});
// console.log("User Access in Context:", userAccess);
return (

View File

@@ -16,12 +16,13 @@ import "react-quill/dist/quill.snow.css"; // Import the styles
import { useState } from "react";
import { useUpdateAboutUsMutation } from "../../../Redux/Service/manage.aboutus.service";
import { useForm, Controller } from "react-hook-form"; // Import React Hook Form
import { toaster, Toaster } from "../../../components/ui/toaster";
function AboutUsAddModel({ aboutUsData }: { aboutUsData: any }) {
const [isOpen, setIsOpen] = useState(false);
// RTK Query Mutation Hook
const [updateAboutUs] = useUpdateAboutUsMutation();
const [updateAboutUs, { isLoading }] = useUpdateAboutUsMutation();
// React Hook Form
const {
@@ -55,87 +56,96 @@ function AboutUsAddModel({ aboutUsData }: { aboutUsData: any }) {
}).unwrap();
setIsOpen(false); // Close dialog on success
reset(); // Reset the form
} catch (error) {
} catch (error: any) {
console.error("Update failed:", error);
toaster.create({
title: "Error",
description: `${error.data.message || "Failed to update"}`,
type: "error",
});
}
};
return (
<DialogRoot placement="center" open={isOpen}>
<DialogTrigger asChild>
<Button
bgColor="#EEEEEE"
pl={3}
pr={3}
size="xs"
color="#000"
onClick={() => handleEditClick(aboutUsData)} // Set content before opening modal
>
<FaRegEdit color="#000" style={{ height: "14px", width: "14px" }} />
<Text color="#000" mt={1}>Edit</Text>
</Button>
</DialogTrigger>
<DialogContent bg="#fff" w={{ base: "90%", md: "1200px" }} height="auto" p={3}>
<DialogHeader bg="white">
<DialogTitle alignSelf="center" color="black" fontSize="14px">
Edit About Us
</DialogTitle>
</DialogHeader>
<DialogBody bg="white">
<Stack py={3} mb={8}>
<Field.Root>
<Field.Label color="black" pt={1} fontSize="12px">
About Us Content
</Field.Label>
{/* Use Controller to integrate ReactQuill with React Hook Form */}
<Controller
name="content"
control={control}
render={({ field }) => (
<ReactQuill
value={field.value}
onChange={field.onChange}
placeholder="Enter About Us content"
modules={{
toolbar: [
[{ 'header': [1, 2, false] }],
['bold', 'italic', 'underline', 'strike'],
['link', 'image'],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
['clean']
],
}}
formats={[
'header',
'bold', 'italic', 'underline', 'strike',
'list', 'bullet',
'link', 'image'
]}
style={{ color: "black", border: "none", fontSize: "12px", height: "170px", width: "100%" }}
/>
)}
/>
</Field.Root>
</Stack>
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt="2">
<>
<DialogRoot placement="center" open={isOpen}>
<DialogTrigger asChild>
<Button
w="100%"
bg="#02A0A0"
color="#fff"
// isLoading={isLoading}
onClick={handleSubmit(onSubmit)} // Use handleSubmit to trigger form submission
bgColor="#EEEEEE"
pl={3}
pr={3}
size="xs"
color="#000"
onClick={() => handleEditClick(aboutUsData)} // Set content before opening modal
>
Save
<FaRegEdit color="#000" style={{ height: "14px", width: "14px" }} />
<Text color="#000" mt={1}>Edit</Text>
</Button>
</DialogFooter>
</DialogTrigger>
<DialogCloseTrigger color="black" onClick={() => setIsOpen(false)} />
</DialogContent>
</DialogRoot>
<DialogContent bg="#fff" w={{ base: "90%", md: "1200px" }} height="auto" p={3}>
<DialogHeader bg="white">
<DialogTitle alignSelf="center" color="black" fontSize="14px">
Edit About Us
</DialogTitle>
</DialogHeader>
<DialogBody bg="white">
<Stack py={3} mb={8}>
<Field.Root>
<Field.Label color="black" pt={1} fontSize="12px">
About Us Content
</Field.Label>
{/* Use Controller to integrate ReactQuill with React Hook Form */}
<Controller
name="content"
control={control}
render={({ field }) => (
<ReactQuill
value={field.value}
onChange={field.onChange}
placeholder="Enter About Us content"
modules={{
toolbar: [
[{ 'header': [1, 2, false] }],
['bold', 'italic', 'underline', 'strike'],
['link', 'image'],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
['clean']
],
}}
formats={[
'header',
'bold', 'italic', 'underline', 'strike',
'list', 'bullet',
'link', 'image'
]}
style={{ color: "black", border: "none", fontSize: "12px", height: "170px", width: "100%" }}
/>
)}
/>
</Field.Root>
</Stack>
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt="2">
<Button
w="100%"
bg="#02A0A0"
color="#fff"
// isLoading={isLoading}
disabled={isLoading}
onClick={handleSubmit(onSubmit)} // Use handleSubmit to trigger form submission
>
Save
</Button>
</DialogFooter>
<DialogCloseTrigger color="black" onClick={() => setIsOpen(false)} />
</DialogContent>
</DialogRoot>
<Toaster />
</>
);
}

View File

@@ -26,7 +26,7 @@ function EditDetails({ rowData, refetch }: {rowData: RowData, refetch: VoidFunct
const [faqQuestion, setFaqQuestion] = useState(rowData?.question);
const [faqAnswer, setFaqAnswer] = useState(rowData?.answer);
const [isOpen, setIsOpen] = useState(false);
const [updateFaq] = useUpdateFaqMutation()
const [updateFaq, { isLoading }] = useUpdateFaqMutation()
// console.log('ROWDATA', rowData);
@@ -146,7 +146,7 @@ function EditDetails({ rowData, refetch }: {rowData: RowData, refetch: VoidFunct
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -54,7 +54,7 @@ const tableHeadRow = [
const FAQ = () => {
const [currentPage, setCurrentPage] = useState(1);
const { data, refetch, isLoading, isFetching } = useGetFaqQuery(currentPage)
const { data, refetch, isLoading, isFetching, isError } = useGetFaqQuery(currentPage)
const [localData, setLocalData] = useState<any[]>([]);
const [faqToggle] = useFaqToggleMutation()
const [deleteFaqPost] = useDeleteFaqPostMutation()
@@ -85,9 +85,21 @@ const FAQ = () => {
try {
await faqToggle({ id: agencyId, is_active: newStatus }).unwrap();
refetch()
toaster.create({
title: "Success",
description: "Status updated successfully",
type: "success",
});
setTimeout(() => {
refetch();
}, 500);
} catch (error) {
console.error("Error updating privacy policy:", error);
toaster.create({
title: "Error",
description: "Someting went wrong.",
type: "error",
});
setLocalData((prevData) =>
prevData.map((agency) =>
agency.id === agencyId ? { ...agency, is_active: currentStatus } : agency
@@ -160,7 +172,7 @@ const FAQ = () => {
handleDeleteFaq(selectedFaqId);
}
}}
/>
/>
<Box>
<Switch
colorPalette={'teal'}
@@ -225,6 +237,8 @@ const FAQ = () => {
total: data?.data.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>)}
</Box>
<Toaster />

View File

@@ -20,7 +20,7 @@ function FaqAddModel({ refetch }: { refetch: VoidFunction }) {
const [faqAnswer, setFaqAnswer] = useState('');
const [userType, setUserType] = useState("");
const [isOpen, setIsOpen] = useState(false);
const [createFaqPost] = useCreateFaqPostMutation()
const [createFaqPost, { isLoading }] = useCreateFaqPostMutation()
const handleOpenModal = () => {
setIsOpen(true); // Open modal when clicking "Add"
@@ -159,7 +159,7 @@ function FaqAddModel({ refetch }: { refetch: VoidFunction }) {
</Stack>
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -16,7 +16,7 @@ import ReactQuill from "react-quill";
function PrivacyPolicyAddModel({ policyData, refetch }: { policyData: any, refetch: VoidFunction }) {
const [isOpen, setIsOpen] = useState(false);
const [updatePrivacyPolicy] = useUpdatePrivacyPolicyMutation()
const [updatePrivacyPolicy, { isLoading }] = useUpdatePrivacyPolicyMutation()
const {
control,
handleSubmit,
@@ -139,7 +139,7 @@ function PrivacyPolicyAddModel({ policyData, refetch }: { policyData: any, refet
</Stack>
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} mt={'4'} onClick={handleSubmit(onSubmit)}>
<Button w="100%" bg="#02A0A0" color={"#fff"} mt={'4'} onClick={handleSubmit(onSubmit)} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -18,7 +18,7 @@ import { useUpdateTermsMutation } from "../../../Redux/Service/terms.and.conditi
function TermsAndConditionsAddModel({ termsData, refetch }: { termsData: any, refetch: VoidFunction }) {
const [isOpen, setIsOpen] = useState(false);
const [updateTerms] = useUpdateTermsMutation()
const [updateTerms, { isLoading }] = useUpdateTermsMutation()
const {
control,
handleSubmit,
@@ -136,7 +136,7 @@ function TermsAndConditionsAddModel({ termsData, refetch }: { termsData: any, re
</Stack>
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} mt={'4'} onClick={handleSubmit(onSubmit)}>
<Button w="100%" bg="#02A0A0" color={"#fff"} mt={'4'} onClick={handleSubmit(onSubmit)} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -1,19 +1,26 @@
import { Box, HStack, Input, Text } from "@chakra-ui/react";
import { Box, HStack, Text } from "@chakra-ui/react";
import MainFrame from "../../components/MainFrame";
import PendingRequests from "../../Pages/ManageContact/PendingRequests";
import { InputGroup } from "../../components/ui/input-group";
import { LuSearch } from "react-icons/lu";
// import { InputGroup } from "../../components/ui/input-group";
// import { LuSearch } from "react-icons/lu";
import DataTable from "../../components/DataTable";
import { useGetContactQuery } from "../../Redux/Service/manage.contactus.service";
import { useEffect, useState } from "react";
import { Spinner } from "../../components/Sipnner/Spinner";
import { useDebounce } from "../../components/Hooks/useDebounce";
import SearchComponent from "../../components/SearchComponent";
// table data
const tableHeadRow = ["Sr. No", "Email id", "Name", "Date", "Action"];
const ManageContact = () => {
const { data, isLoading, isError } = useGetContactQuery();
const [localData, setLocalData] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [localData, setLocalData] = useState<any[]>([]);
const [searchTerm, setSearchTerm] = useState("");
const debouncedSearchTerm = useDebounce(searchTerm, 500);
const queryArgs = debouncedSearchTerm ? { page: currentPage, search: debouncedSearchTerm } : { page: currentPage };
const { data, isLoading, isError, refetch, isFetching } = useGetContactQuery(queryArgs);
useEffect(() => {
if (data) {
@@ -21,6 +28,20 @@ const ManageContact = () => {
}
}, [data]);
const handlePageChange = (page: number) => {
setCurrentPage(page);
};
const handleSearchChange = (value: string) => {
setSearchTerm(value);
setCurrentPage(1);
};
const filteredData = localData?.filter((agency) =>
agency?.first_name.toLowerCase().includes(searchTerm.toLowerCase()) ||
agency?.email.toLowerCase().includes(searchTerm.toLowerCase())
);
const formatDateOfBirth = (dob: string): string => {
return new Date(dob).toLocaleDateString("en-GB", {
day: "2-digit",
@@ -29,14 +50,15 @@ const ManageContact = () => {
});
};
const managepost = localData?.map((agency: any, index: number) => ({
const managepost = filteredData?.map((agency: any, index: number) => ({
"Sr. No": index + 1, // Typically Sr. No starts from 1, not using id which might not be sequential
"Email id": agency?.email || "-",
Name: agency?.first_name || "-",
Date: formatDateOfBirth(agency?.created_at) || "-",
Action: (
<HStack justifyContent="center">
<PendingRequests />
Action: (
<HStack justifyContent="center" cursor={agency.response_status === 0 ? 'pointer' : 'default'}>
{agency.response_status === 0 ? <PendingRequests data={agency} refetch={refetch} /> : 'Resolved'}
{/* <PendingRequests data={agency} refetch={refetch} /> */}
</HStack>
),
}));
@@ -86,35 +108,25 @@ const ManageContact = () => {
</Text>
<HStack>
<InputGroup
startElement={
<LuSearch
fontSize={"xs"}
style={{ position: "relative", left: "10px" }}
/>
}
color={"#000"}
>
<Input
p={3}
w={300}
bg={"#fff"}
colorPalette={"blue"}
_focus={{ border: "1px solid #02A0A0" }}
rounded={"md"}
size={"xs"}
fontSize={"sm"}
placeholder="Search..."
bgColor={"#EEEEEE"}
ps={8}
/>
</InputGroup>
<SearchComponent
value={searchTerm}
onChange={handleSearchChange}
/>
</HStack>
</HStack>
<DataTable
sortableColumns={["Name", "Registration Date "]}
tableHeadRow={tableHeadRow}
data={managepost || []} // Ensure an empty array is passed if managepost is undefined
paginationData={{
current_page: (data?.data as any)?.current_page || 1,
last_page: (data?.data as any)?.last_page || 1,
per_page: (data?.data as any)?.per_page || 10,
total: (data?.data as any)?.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>
</Box>
</MainFrame>

View File

@@ -10,95 +10,160 @@ import {
DialogTrigger,
} from "../../components/ui/dialog";
import { Badge, Field, HStack, Input, Stack, Textarea } from "@chakra-ui/react";
function PendingRequests() {
import { usePendingRequestMutation } from "../../Redux/Service/manage.contactus.service";
import { useState } from "react";
import { Toaster, toaster } from "../../components/ui/toaster";
function PendingRequests({ data, refetch }: { data: any, refetch: VoidFunction }) {
const [isOpen, setIsOpen] = useState(false);
const [res, setRes] = useState({
contact_us_xid: data.id,
message: data.message,
solution: '',
})
const [pendingRequest, { isLoading }] = usePendingRequestMutation()
const handleOpenModal = () => {
setIsOpen(true);
};
const handleSubmit = async (status: string) => {
const payload = { ...res, response_status: status };
if(res.solution === ''){
toaster.create({
title: "Error",
description: "All fields are required",
type: "error",
});
return;
}
try {
const response = await pendingRequest(payload).unwrap()
if (response?.status === "success") {
toaster.create({
title: "Success",
description: "Country updated successfully",
type: "success",
});
setIsOpen(false);
refetch()
} else {
toaster.create({
title: "Error",
description: "Failed to update Country",
type: "error",
});
}
} catch (error: any) {
toaster.create({
title: "Error",
description: error?.data?.message || "Something went wrong",
type: "error",
});
}
}
return (
<DialogRoot placement="center">
<DialogTrigger asChild>
<Badge fontSize={"xs"} px={2} bg={'#02a0a01f'}>
Answer request
</Badge>
</DialogTrigger>
<>
<DialogRoot placement="center" key={data.id} open={isOpen} onOpenChange={({ open }) => setIsOpen(open)}>
<DialogTrigger asChild>
<Badge fontSize={"xs"} px={2} bg={'#02a0a01f'} onClick={handleOpenModal}>
Answer request
</Badge>
</DialogTrigger>
<DialogContent
bg={"#fff"}
w={{ base: "90%", md: "400px" }}
height={"auto"}
p={3} // Reduced padding
bgSize={"md"}
>
<DialogHeader bg="white">
<DialogTitle alignSelf="center" color="black" fontSize="14px">
Pending Requests
</DialogTitle>
</DialogHeader>
<DialogBody bg="white">
<Stack py={3}>
<Field.Root>
<Field.Label color="black" pt={1} fontSize="12px">
Request Type
</Field.Label>
<Input
placeholder="Message"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
<Field.Label color="black" pt={1} fontSize="12px">
Solution
</Field.Label>
<Textarea
placeholder=""
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="80px"
pt={1.5}
/>
</Field.Root>
</Stack>
</DialogBody>
<DialogFooter
display={{ base: "block", md: "flex" }}
justifyContent="center"
gap={1}
pt={2}
<DialogContent
bg={"#fff"}
w={{ base: "90%", md: "400px" }}
height={"auto"}
p={3} // Reduced padding
bgSize={"md"}
>
<HStack mt={2} mb={3} width={"100%"} justifyContent={"space-between"}>
<Button
width={"48%"}
color="black"
_hover={{ bgColor: "white" }}
variant="outline"
borderRadius="sm"
border="1px solid #02A0A0"
size={"xs"}
>
Unresolved
</Button>
<Button
width={"48%"}
borderRadius="sm"
// bgColor="#007F33"
bgColor={"#02A0A0"}
color="white"
// colorPalette="#007F33"
size={"xs"}
>
Resolved
</Button>
</HStack>
</DialogFooter>
<DialogHeader bg="white">
<DialogTitle alignSelf="center" color="black" fontSize="14px">
Pending Requests
</DialogTitle>
</DialogHeader>
<DialogCloseTrigger color="black" />
</DialogContent>
</DialogRoot>
<DialogBody bg="white">
<Stack py={3}>
<Field.Root>
<Field.Label color="black" pt={1} fontSize="12px">
Request Type
</Field.Label>
<Input
placeholder="Message"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={res.message}
disabled
// onChange={(e) => setRes({ ...res, message: e.target.value })}
/>
<Field.Label color="black" pt={1} fontSize="12px">
Solution
</Field.Label>
<Textarea
placeholder=""
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="80px"
pt={1.5}
onChange={(e) => setRes({ ...res, solution: e.target.value })}
/>
</Field.Root>
</Stack>
</DialogBody>
<DialogFooter
display={{ base: "block", md: "flex" }}
justifyContent="center"
gap={1}
pt={2}
>
<HStack mt={2} mb={3} width={"100%"} justifyContent={"space-between"}>
<Button
width={"48%"}
color="black"
_hover={{ bgColor: "white" }}
variant="outline"
borderRadius="sm"
border="1px solid #02A0A0"
size={"xs"}
onClick={() => handleSubmit('1')}
disabled={isLoading}
>
Unresolved
</Button>
<Button
width={"48%"}
borderRadius="sm"
// bgColor="#007F33"
bgColor={"#02A0A0"}
color="white"
// colorPalette="#007F33"
size={"xs"}
onClick={() => handleSubmit('0')}
disabled={isLoading}
>
Resolved
</Button>
</HStack>
</DialogFooter>
<DialogCloseTrigger color="black" />
</DialogContent>
</DialogRoot>
<Toaster />
</>
);
}

View File

@@ -3,15 +3,14 @@ import {
HStack,
Image,
// Image,
Input,
Text,
} from "@chakra-ui/react";
import { LuSearch } from "react-icons/lu";
// import { LuSearch } from "react-icons/lu";
// import { RiDeleteBin5Line } from "react-icons/ri";
// import AlertDailog from "../../components/AlertDailog";
import DataTable from "../../components/DataTable";
import MainFrame from "../../components/MainFrame";
import { InputGroup } from "../../components/ui/input-group";
// import { InputGroup } from "../../components/ui/input-group";
import ManageJobsAdd from "./ManageJobsAdd";
import ViewManageJob from "./ViewManageJob";
import {
@@ -21,8 +20,10 @@ import {
import { useEffect, useState } from "react";
import { Spinner } from "../../components/Sipnner/Spinner";
import Delete from "../../components/ActionIcons/Delete";
import { toaster } from "../../components/ui/toaster";
import { Toaster, toaster } from "../../components/ui/toaster";
import AlertDailog from "../../components/AlertDailog";
import { useDebounce } from "../../components/Hooks/useDebounce";
import SearchComponent from "../../components/SearchComponent";
// import { useState } from "react";
// import { useGetManageJobsQuery } from "../../Redux/Service/manage.jobs.service";
// import Delete from "../../components/ActionIcons/Delete";
@@ -40,9 +41,12 @@ const tableHeadRow = [
];
const ManageJobs = () => {
const [currentPage] = useState(1);
const [currentPage, setCurrentPage] = useState(1);
const [localData, setLocalData] = useState([]);
const { data, refetch, isLoading } = useGetManageJobsQuery(currentPage);
const [searchTerm, setSearchTerm] = useState("");
const debouncedSearchTerm = useDebounce(searchTerm, 500);
const queryArgs = debouncedSearchTerm ? { page: currentPage, search: debouncedSearchTerm } : { page: currentPage };
const { data, refetch, isLoading, isError, isFetching } = useGetManageJobsQuery(queryArgs);
const [deleteJobsPost] = useDeleteJobsPostMutation();
const [deleteModal, setDeleteModal] = useState(false);
const [selectedJobsId, setSelectedJobsId] = useState<number | null>(null);
@@ -52,7 +56,16 @@ const ManageJobs = () => {
setLocalData((data as any)?.data?.data || []);
}
}, [data]);
console.log(data?.data.data);
// console.log(data?.data.data);
const handleSearchChange = (value: string) => {
setSearchTerm(value);
setCurrentPage(1);
};
const handlePageChange = (page: number) => {
setCurrentPage(page);
};
const handleDeleteJobs = async (jobsId: number) => {
try {
@@ -84,8 +97,8 @@ const ManageJobs = () => {
Salary: agency?.ctc_amount,
Action: (
<HStack justifyContent="center">
<ViewManageJob />
<ManageJobsAdd />
<ViewManageJob data={agency} />
<ManageJobsAdd data={agency} refetch={refetch}/>
<AlertDailog
isOpen={deleteModal}
AltertTiggerIcon={() => (
@@ -142,29 +155,10 @@ const ManageJobs = () => {
</Text>
<HStack>
<InputGroup
startElement={
<LuSearch
fontSize={"xs"}
style={{ position: "relative", left: "10px" }}
/>
}
color={"#000"}
>
<Input
p={3}
w={300}
bg={"#fff"}
colorPalette={"blue"}
_focus={{ border: "1px solid #02A0A0" }}
rounded={"md"}
size={"xs"}
fontSize={"sm"}
placeholder="Search..."
bgColor={"#EEEEEE"}
ps={8}
/>
</InputGroup>
<SearchComponent
value={searchTerm}
onChange={handleSearchChange}
/>
{/* <Button bgColor={'#EEEEEE'} pl={3} pr={3}><IoMdAdd /> <Text>Add</Text></Button> */}
</HStack>
</HStack>
@@ -172,8 +166,18 @@ const ManageJobs = () => {
sortableColumns={["Name", "Registration Date "]}
tableHeadRow={tableHeadRow}
data={managepost}
paginationData={{
current_page: data?.data.current_page || 1,
last_page: data?.data.last_page || 1,
per_page: data?.data.per_page || 10,
total: data?.data.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>
</Box>
<Toaster />
</MainFrame>
);
};

View File

@@ -1,10 +1,8 @@
import {
Field,
Input,
SelectValueText,
Span,
Stack,
createListCollection,
} from "@chakra-ui/react";
import { Button } from "../../components/ui/button";
import {
@@ -19,24 +17,151 @@ import {
} from "../../components/ui/dialog";
// import { TbEdit } from "react-icons/tb";
import {
SelectContent,
SelectItem,
SelectLabel,
SelectRoot,
SelectTrigger,
} from "../../components/ui/select";
import Edit from "../../components/ActionIcons/Edit";
import { JobStatusData, useGetCountryQuery, useGetDepartmentQuery, useGetIndustryQuery, useGetManageJobTypeQuery, useGetWorkspaceModesQuery, useUpdateJobsMutation, WorkSpace } from "../../Redux/Service/manage.jobs.service";
import { useReducer, useRef } from "react";
import { Toaster, toaster } from "../../components/ui/toaster";
const reducerFunction = (state: any, action: any) => {
switch (action.type) {
case "SET_JOB_TITLE":
return { ...state, jobTitle: action.payload };
case "SET_WORKSPACE_MODE":
return { ...state, workspaceMode: action.payload };
case "SET_CATEGORY":
return { ...state, category: action.payload };
case "SET_SUB_CATEGORY":
return { ...state, subCategory: action.payload };
case "SET_SALARY":
return { ...state, salary: action.payload };
case "SET_EXPERIENCE":
return { ...state, experience: action.payload };
case "SET_JOB_LOCATION":
return { ...state, jobLocation: action.payload };
case "SET_COUNTRY_SELECTION":
return { ...state, countrySelection: action.payload };
case "SET_JOB_TYPE":
return { ...state, jobType: action.payload };
case "SET_SKILLS_REQUIRED":
return { ...state, skillsRequired: action.payload };
case "SET_JOB_DESCRIPTION":
return { ...state, jobDescription: action.payload };
default:
return state;
}
};
function ManageJobsAdd({ data, refetch }: { data: JobStatusData, refetch: () => void }) {
const { data: workspaceModes } = useGetWorkspaceModesQuery({});
const { data: industryData } = useGetIndustryQuery({});
const { data: departmentData } = useGetDepartmentQuery({});
const { data: countryData } = useGetCountryQuery({});
const { data: jobTypeData } = useGetManageJobTypeQuery({});
const [updateJobs, { isLoading}] = useUpdateJobsMutation();
// console.log('Modes:', jobTypeData?.data.data);
const initialState = {
jobTitle: data?.job_title || "",
workspaceMode: data?.workspace_mode_xid || "",
category: data?.industry_xid || "",
subCategory: data?.department_xid || "",
salary: data?.ctc_amount || "",
experience: data?.experience || "",
jobLocation: data?.job_location || "",
countrySelection: data?.country_xid || "",
jobType: data?.job_type_xid || "",
skillsDescription: data?.skill_description || "",
jobDescription: data?.job_description || "",
};
const [state, dispatch] = useReducer(reducerFunction, initialState);
const closeRef = useRef<HTMLButtonElement>(null);
const getDisplayName = (name: string, maxLength = 30) =>
name.length > maxLength ? name.slice(0, maxLength) + "..." : name;
const handleSubmit = async () => {
const {
jobTitle,
workspaceMode,
category,
subCategory,
salary,
experience,
jobLocation,
countrySelection,
jobType,
skillsDescription,
jobDescription,
} = state;
if (
!jobTitle ||
!workspaceMode ||
!category ||
!subCategory ||
!salary ||
!experience ||
!jobLocation ||
!countrySelection ||
!jobType ||
!skillsDescription ||
!jobDescription
) {
toaster.create({
title: "Missing Fields",
description: "Please fill in all required fields before submitting.",
type: "error",
});
return;
}
// Only en_name is editable, so we only need to send that in the payload.
const payload = {
id: data?.id,
job_title: state.jobTitle,
workspace_mode_xid: state.workspaceMode,
industry_xid: state.category,
department_xid: state.subCategory,
ctc_amount: state.salary,
experience: state.experience,
job_location: state.jobLocation,
country_xid: state.countrySelection,
job_type_xid: state.jobType,
skill_description: state.skillsDescription,
job_description: state.jobDescription,
};
// console.log('payload', payload)
try {
const response = await updateJobs(payload).unwrap();
if (response?.status === "success") {
toaster.create({
title: "Success",
description: "Country updated successfully",
type: "success",
});
closeRef.current?.click();
refetch()
} else {
toaster.create({
title: "Error",
description: "Failed to update Country",
type: "error",
});
}
} catch (error) {
console.error("Error updating template:", error);
// alert("Failed to update template");
toaster.create({
title: "Error",
description: "Something went wrong",
type: "error",
});
}
};
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
});
function ManageJobsAdd() {
return (
<DialogRoot placement="center">
<DialogTrigger asChild>
@@ -73,50 +198,182 @@ function ManageJobsAdd() {
pl={1}
fontSize="12px"
height="30px"
value={state.jobTitle}
onChange={(e) =>
dispatch({
type: "SET_JOB_TITLE",
payload: e.target.value,
})
}
/>
</Field.Root>
<Field.Root>
<Field.Label color="black" pt={1} fontSize="12px">
Workspace mode
</Field.Label>
<Input
placeholder="Enter the Workspace Mode"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
<select
style={{
backgroundColor: "#EEEEEE",
color: "black",
border: "none",
height: "30px",
fontSize: "12px",
padding: "4px",
borderRadius: "4px",
width: "100%",
}}
value={state.workspace_mode_xid}
onChange={(e) =>
dispatch({
type: "SET_WORKSPACE_MODE",
payload: Number(e.target.value),
})
}
>
<option value="" disabled>
Select country
</option>
{workspaceModes && workspaceModes?.data.map((mode: WorkSpace) => (
<option key={mode.id} value={mode.id}>
{getDisplayName(mode.en_name)}
</option>
))}
</select>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Category
</Field.Label>
<Input
placeholder="Enter the Category"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
<select
style={{
backgroundColor: "#EEEEEE",
color: "black",
border: "none",
height: "30px",
fontSize: "12px",
padding: "4px",
borderRadius: "4px",
width: "100%",
}}
value={state.industry_xid}
onChange={(e) =>
dispatch({
type: "SET_INDUSTRY",
payload: Number(e.target.value),
})
}
>
<option value="" disabled>
Select department
</option>
{industryData && industryData?.data.map((mode: WorkSpace) => (
<option key={mode.id} value={mode.id}>
{getDisplayName(mode.en_name)}
</option>
))}
</select>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Sub-Category
</Field.Label>
<Input
placeholder="Enter the Sub-Category"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
<select
style={{
backgroundColor: "#EEEEEE",
color: "black",
border: "none",
height: "30px",
fontSize: "12px",
padding: "4px",
borderRadius: "4px",
width: "100%",
}}
value={state.department_xid}
onChange={(e) =>
dispatch({
type: "SET_DEPARTMENT",
payload: Number(e.target.value),
})
}
>
<option value="" disabled>
Select department
</option>
{departmentData && departmentData?.data.map((mode: WorkSpace) => (
<option key={mode.id} value={mode.id}>
{getDisplayName(mode.en_name)}
</option>
))}
</select>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Country
</Field.Label>
<select
style={{
backgroundColor: "#EEEEEE",
color: "black",
border: "none",
height: "30px",
fontSize: "12px",
padding: "4px",
borderRadius: "4px",
width: "100%",
}}
value={state.country_xid}
onChange={(e) =>
dispatch({
type: "SET_COUNTRY",
payload: Number(e.target.value),
})
}
>
<option value="" disabled>
Select country
</option>
{countryData && countryData?.data.map((mode: WorkSpace) => (
<option key={mode.id} value={mode.id}>
{getDisplayName(mode.en_name)}
</option>
))}
</select>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Job type
</Field.Label>
<select
style={{
backgroundColor: "#EEEEEE",
color: "black",
border: "none",
height: "30px",
fontSize: "12px",
padding: "4px",
borderRadius: "4px",
width: "100%",
}}
value={state.job_type_xid}
onChange={(e) =>
dispatch({
type: "SET_JOB_TYPE",
payload: Number(e.target.value),
})
}
>
<option value="" disabled>
Select Job Type
</option>
{jobTypeData && jobTypeData?.data.data.map((mode: WorkSpace) => (
<option key={mode.id} value={mode.id}>
{getDisplayName(mode.en_name)}
</option>
))}
</select>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Salary
@@ -129,6 +386,13 @@ function ManageJobsAdd() {
pl={1}
fontSize="12px"
height="30px"
value={state.salary}
onChange={(e) =>
dispatch({
type: "SET_SALARY",
payload: e.target.value,
})
}
/>
</Field.Root>
<Field.Root>
@@ -136,13 +400,20 @@ function ManageJobsAdd() {
Experience
</Field.Label>
<Input
placeholder="Enter the Experience"
placeholder="Enter the Experience in years"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={state.experience}
onChange={(e) =>
dispatch({
type: "SET_EXPERIENCE",
payload: e.target.value,
})
}
/>
</Field.Root>
<Field.Root>
@@ -157,71 +428,34 @@ function ManageJobsAdd() {
pl={1}
fontSize="12px"
height="30px"
/>
</Field.Root>
{/* <Field.Label pt={1} color="black" fontSize="12px">Country Selection</Field.Label>
<Input placeholder="Enter the Country Selection" /> */}
<SelectRoot collection={frameworks} size="sm" w={"100%"}>
<SelectLabel pt={1} color="black" fontSize="12px">
Country Selection
</SelectLabel>
<SelectTrigger
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
borderRadius={"5px"}
>
<SelectValueText
placeholder="Enter the Country Selection"
pb={"6px"}
fontSize={"12px"}
/>
</SelectTrigger>
<SelectContent position={"relative"} zIndex={"9999"} bg={"#fff"}>
{frameworks.items.map((movie) => (
<SelectItem
item={movie}
key={movie.value}
color={"black"}
pl={2}
p={1}
_hover={{ bg: "#F0F0F0" }} // Light grey background on hover
fontSize="12px"
>
{movie.label}
</SelectItem>
))}
</SelectContent>
</SelectRoot>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Job type
</Field.Label>
<Input
placeholder="Enter the Job Type"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={state.jobLocation}
onChange={(e) =>
dispatch({
type: "SET_JOB_LOCATION",
payload: e.target.value,
})
}
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Skills required
Skills description
</Field.Label>
<Input
placeholder="Enter the Skills Required"
placeholder="Enter the Skills description"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={state.skillsDescription}
onChange={(e) =>
dispatch({
type: "SET_SKILLS_DESCRIPTION",
payload: e.target.value,
})
}
/>
</Field.Root>
<Field.Root>
@@ -236,6 +470,13 @@ function ManageJobsAdd() {
pl={1}
fontSize="12px"
height="30px"
value={state.jobDescription}
onChange={(e) =>
dispatch({
type: "SET_JOB_DESCRIPTION",
payload: e.target.value,
})
}
/>
</Field.Root>
</Stack>
@@ -247,13 +488,16 @@ function ManageJobsAdd() {
color={"#fff"}
fontSize="12px"
height="30px"
onClick={handleSubmit}
disabled={isLoading}
>
Save
</Button>
</DialogFooter>
<DialogCloseTrigger color="black" />
<DialogCloseTrigger color="black" ref={closeRef} />
</DialogContent>
<Toaster />
</DialogRoot>
);
}

View File

@@ -1,17 +1,13 @@
import {
Field,
Input,
SelectValueText,
Span,
Stack,
createListCollection,
} from "@chakra-ui/react";
import { Button } from "../../components/ui/button";
import {
DialogBody,
DialogCloseTrigger,
DialogContent,
DialogFooter,
DialogHeader,
DialogRoot,
DialogTitle,
@@ -19,276 +15,290 @@ import {
} from "../../components/ui/dialog";
// import { MdOutlineRemoveRedEye } from "react-icons/md";
import {
SelectContent,
SelectItem,
SelectLabel,
SelectRoot,
SelectTrigger,
} from "../../components/ui/select";
import View from "../../components/ActionIcons/View";
import { useLazyViewJobsQuery } from "../../Redux/Service/manage.jobs.service";
import { JobStatusData, useLazyViewJobsQuery } from "../../Redux/Service/manage.jobs.service";
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
});
function ViewManageJob() {
const [trigger, { data }] = useLazyViewJobsQuery();
function ViewManageJob({ data }: { data: JobStatusData }) {
const [trigger] = useLazyViewJobsQuery();
console.log(data);
// const handleView = () => {
// trigger(id);
// };
const handleView = () => {
trigger(data.id);
};
const viewJobs = data;
// const viewJobs = data;
console.log();
return (
<DialogRoot placement="center">
<DialogTrigger asChild>
<Span>
<Button
onClick={handleView}
bg={'transparent'}
color={"black"}
>
<View />
</Span>
</Button>
</DialogTrigger>
{/* {viewJobs?.map((data: any) => ( */}
<DialogContent
bg={"#fff"}
// w={{ lg: "60%", md: "230px" }}
w={{ base: "90%", md: "400px" }}
height={"80vh"}
overflow={"scroll"}
overflowX="hidden"
p={3} // Reduced padding
bgSize={"md"}
>
<DialogHeader bg="white">
<DialogTitle alignSelf="center" color="black" fontSize="14px">
Add Details
</DialogTitle>
</DialogHeader>
<DialogContent
bg={"#fff"}
// w={{ lg: "60%", md: "230px" }}
w={{ base: "90%", md: "400px" }}
height={"80vh"}
overflow={"scroll"}
overflowX="hidden"
p={3} // Reduced padding
bgSize={"md"}
>
<DialogHeader bg="white">
<DialogTitle alignSelf="center" color="black" fontSize="14px">
Add Details
</DialogTitle>
</DialogHeader>
<DialogBody bg="white">
<Stack py={3}>
<Field.Root>
<Field.Label color="black" pt={1} fontSize="12px">
Job title
</Field.Label>
<Input
placeholder="Enter the Job Title"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
</Field.Root>
<Field.Root>
<Field.Label color="black" pt={1} fontSize="12px">
Workspace mode
</Field.Label>
<Input
placeholder="Enter the Workspace Mode"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Category
</Field.Label>
<Input
placeholder="Enter the Category"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Sub-Category
</Field.Label>
<Input
placeholder="Enter the Sub-Category"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Salary
</Field.Label>
<Input
placeholder="Enter the Salary"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Experience
</Field.Label>
<Input
placeholder="Enter the Experience"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Job Location
</Field.Label>
<Input
placeholder="Enter the Job Location"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
</Field.Root>
{/* <Field.Label pt={1} color="black" fontSize="12px">Country Selection</Field.Label>
<Input placeholder="Enter the Country Selection" /> */}
<SelectRoot collection={frameworks} size="sm" w={"100%"}>
<SelectLabel pt={1} color="black" fontSize="12px">
Country Selection
</SelectLabel>
<SelectTrigger
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
borderRadius={"5px"}
>
<SelectValueText
placeholder="Enter the Country Selection"
pb={"6px"}
fontSize={"12px"}
/>
</SelectTrigger>
<SelectContent
position={"relative"}
zIndex={"9999"}
bg={"#fff"}
>
{frameworks.items.map((movie) => (
<SelectItem
item={movie}
key={movie.value}
color={"black"}
pl={2}
p={1}
_hover={{ bg: "#F0F0F0" }} // Light grey background on hover
fontSize="12px"
>
{movie.label}
</SelectItem>
))}
</SelectContent>
</SelectRoot>
<DialogBody bg="white">
<Stack py={3}>
<Field.Root>
<Field.Label color="black" pt={1} fontSize="12px">
Job title
</Field.Label>
<Input
placeholder="Enter the Job Title"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={data?.job_title}
/>
</Field.Root>
<Field.Root>
<Field.Label color="black" pt={1} fontSize="12px">
Workspace mode
</Field.Label>
<Input
placeholder="Enter the Workspace Mode"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={data?.workspace?.en_name}
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Category
</Field.Label>
<Input
placeholder="Enter the Category"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={data?.industry?.en_name}
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Sub-Category
</Field.Label>
<Input
placeholder="Enter the Sub-Category"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={data?.department?.en_name}
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Salary
</Field.Label>
<Input
placeholder="Enter the Salary"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={data?.ctc_amount}
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Experience
</Field.Label>
<Input
placeholder="Enter the Experience"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={data?.experience}
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Job Location
</Field.Label>
<Input
placeholder="Enter the Job Location"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={data?.job_location}
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Job type
</Field.Label>
<Input
placeholder="Enter the Job Type"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Skills required
</Field.Label>
<Input
placeholder="Enter the Skills Required"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Job Description*
</Field.Label>
<Input
placeholder="Enter the Job Description"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Upload Image
</Field.Label>
<Input
placeholder="Upload Image"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
</Field.Root>
</Stack>
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button
w="100%"
bg="#02A0A0"
color={"#fff"}
fontSize="12px"
height="30px"
>
Save
</Button>
</DialogFooter>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Country
</Field.Label>
<Input
placeholder="Enter the Country"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={data?.country.en_name}
/>
</Field.Root>
<DialogCloseTrigger color="black" />
</DialogContent>
{/* <SelectRoot collection={frameworks} size="sm" w={"100%"}>
<SelectLabel pt={1} color="black" fontSize="12px">
Country Selection
</SelectLabel>
<SelectTrigger
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
borderRadius={"5px"}
>
<SelectValueText
placeholder="Enter the Country Selection"
pb={"6px"}
fontSize={"12px"}
/>
</SelectTrigger>
<SelectContent
position={"relative"}
zIndex={"9999"}
bg={"#fff"}
>
{frameworks.items.map((movie) => (
<SelectItem
item={movie}
key={movie.value}
color={"black"}
pl={2}
p={1}
_hover={{ bg: "#F0F0F0" }} // Light grey background on hover
fontSize="12px"
>
{movie.label}
</SelectItem>
))}
</SelectContent>
</SelectRoot> */}
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Job type
</Field.Label>
<Input
placeholder="Enter the Job Type"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={data?.job_type?.en_name}
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Skills required
</Field.Label>
<Input
placeholder="Enter the Skills Required"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={data?.skill_description}
/>
</Field.Root>
<Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Job Description*
</Field.Label>
<Input
placeholder="Enter the Job Description"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={data?.job_description}
/>
</Field.Root>
{/* <Field.Root>
<Field.Label pt={1} color="black" fontSize="12px">
Upload Image
</Field.Label>
<Input
placeholder="Upload Image"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
</Field.Root> */}
</Stack>
</DialogBody>
{/* <DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button
w="100%"
bg="#02A0A0"
color={"#fff"}
fontSize="12px"
height="30px"
>
Save
</Button>
</DialogFooter> */}
<DialogCloseTrigger color="black" />
</DialogContent>
{/* ))} */}
</DialogRoot>
);

View File

@@ -14,9 +14,10 @@ import { Switch } from "../../components/ui/switch";
import ViewDailog from "./ViewDailog";
import { useGetManagePostsQuery, usePostStatusToggleMutation } from "../../Redux/Service/manage.post.service";
import { useEffect, useState } from "react";
import { toaster } from "../../components/ui/toaster";
import { Toaster, toaster } from "../../components/ui/toaster";
import { FaVideo } from "react-icons/fa";
import SearchComponent from "../../components/SearchComponent";
import { delay } from "../../components/Utils";
// import Delete from "../../components/ActionIcons/Delete";
// import ViewDailog from './ViewDailog'
@@ -71,7 +72,7 @@ const tableHeadRow = [
const ManagePost = () => {
const [currentPage, setCurrentPage] = useState(1);
const { data, refetch } = useGetManagePostsQuery(currentPage)
const { data, refetch, isFetching, isError } = useGetManagePostsQuery(currentPage)
const [localData, setLocalData] = useState<any[]>([]);
const [searchTerm, setSearchTerm] = useState("");
const [postStatusToggle] = usePostStatusToggleMutation()
@@ -96,6 +97,12 @@ const ManagePost = () => {
);
try {
await postStatusToggle({ id: agencyId, is_active: newStatus }).unwrap();
toaster.create({
title: "Success",
description: "Status updated successfully",
type: "success",
});
await delay(500);
refetch()
} catch (error) {
console.error("Error updating:", error);
@@ -149,36 +156,35 @@ const ManagePost = () => {
h={50}
src={`${APIURL}${agency.images[0].image_name}`}
/>
<Text fontSize="xs" color={'lightgray'}>
<Text fontSize="xs" color={'lightgray'}>
{`${Number(agency.images.length) > 1 ? '+' + (Number(agency.images.length) - 1) : ''}`}
</Text>
</Text>
</HStack>
) : (
<HStack>
<Box
rounded={"lg"}
w={100}
h={50}
bg="gray.200"
display="flex"
alignItems="center"
justifyContent="center"
>
<Icon as={FaVideo} color="gray.500" />
</Box>
<Text fontSize="xs" color={'lightgray'}>
{`${Number(agency.images.length) > 1 ? '+' + (Number(agency.images.length) - 1) : ''}`}
</Text>
</HStack>
<HStack>
<Box
rounded={"lg"}
w={100}
h={50}
bg="gray.200"
display="flex"
alignItems="center"
justifyContent="center"
>
<Icon as={FaVideo} color="gray.500" />
</Box>
<Text fontSize="xs" color={'lightgray'}>
{`${Number(agency.images.length) > 1 ? '+' + (Number(agency.images.length) - 1) : ''}`}
</Text>
</HStack>
) : ''
// <Image rounded={"lg"} w={100} h={50} src={img} />
),
Description: (
<Text>
{`${translation?.content}`.slice(
0,
30
) + "..."}
{translation?.content?.length > 30
? `${translation.content.slice(0, 30)}...`
: translation?.content}
</Text>
),
"Publish Data": formatAPIDate(agency.created_at),
@@ -236,8 +242,11 @@ const ManagePost = () => {
total: data?.data.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>
</Box>
<Toaster />
</MainFrame>
);
};

View File

@@ -1,12 +1,16 @@
import { Box, HStack, Input, Text } from "@chakra-ui/react";
import { Box, HStack, Text } from "@chakra-ui/react";
import MainFrame from "../../../components/MainFrame";
import DataTable from "../../../components/DataTable";
import { Switch } from "../../../components/ui/switch";
import { InputGroup } from "../../../components/ui/input-group";
import { LuSearch } from "react-icons/lu";
import { useGetContactQuery } from "../../../Redux/Service/deactivated.account.service";
// import { InputGroup } from "../../../components/ui/input-group";
// import { LuSearch } from "react-icons/lu";
// import { useGetContactQuery } from "../../../Redux/Service/deactivated.account.service";
import { useEffect, useState } from "react";
import { Spinner } from "../../../components/Sipnner/Spinner";
import { useGetDeactivateUserQuery, useUserDeactivateToggleMutation } from "../../../Redux/Service/manage.user";
import SearchComponent from "../../../components/SearchComponent";
import { Toaster, toaster } from "../../../components/ui/toaster";
import { delay } from "../../../components/Utils";
const tableHeadRow = [
"Sr. No",
@@ -17,8 +21,11 @@ const tableHeadRow = [
];
const DeactivatedAccounts = () => {
const { data ,isLoading} = useGetContactQuery();
const [localData, setLocalData] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const { data, isLoading, refetch, isError, isFetching } = useGetDeactivateUserQuery(currentPage);
const [localData, setLocalData] = useState<any[]>([]);
const [searchTerm, setSearchTerm] = useState("");
const [userDeactivateToggle] = useUserDeactivateToggleMutation()
useEffect(() => {
if (data) {
@@ -26,8 +33,47 @@ const DeactivatedAccounts = () => {
}
}, [data]);
const handlePageChange = (page: number) => {
setCurrentPage(page);
}
const manageUser = localData?.map((agency: any, index: number) => ({
const filteredData = localData?.filter((agency) => {
const searchLower = searchTerm.toLowerCase();
const firstName = agency.first_name?.toLowerCase().includes(searchLower);
const lastName = agency.last_name?.toLowerCase().includes(searchLower);
// const email = agency.capital?.toLowerCase().includes(searchLower);
return firstName || lastName;
});
const handleToggle = async (agencyId: number, currentStatus: string) => {
const newStatus = currentStatus === '1' ? '0' : '1';
setLocalData((prevData) =>
prevData.map((agency) =>
agency.id === agencyId ? { ...agency, is_active: newStatus } : agency
)
);
try {
await userDeactivateToggle({ id: agencyId, is_active: newStatus }).unwrap();
toaster.create({
title: "Success",
description: "Status updated successfully",
type: "success",
});
await delay(500);
refetch()
} catch (error) {
console.error("Error updating privacy policy:", error);
setLocalData((prevData) =>
prevData.map((agency) =>
agency.id === agencyId ? { ...agency, is_active: currentStatus } : agency
)
);
}
}
const manageUser = filteredData?.map((agency: any, index: number) => ({
"Sr. No": index + 1,
"First Name": agency?.first_name,
"Last Name": agency?.last_name,
@@ -38,13 +84,13 @@ const DeactivatedAccounts = () => {
size={"sm"}
colorPalette={"teal"}
checked={agency.is_active === true}
// onChange={() => handleToggle(agency.id, agency.is_active ? "1" : "0")}
onChange={() => handleToggle(agency.id, agency.is_active ? "1" : "0")}
/>
</Box>
),
}));
if (isLoading) {
if (isLoading) {
return (
<MainFrame>
<Box
@@ -89,37 +135,32 @@ const DeactivatedAccounts = () => {
</Text>
<HStack>
<InputGroup
startElement={
<LuSearch
fontSize={"xs"}
style={{ position: "relative", left: "10px" }}
/>
}
color={"#000"}
>
<Input
p={4}
w={300}
bg={"#fff"}
colorPalette={"blue"}
_focus={{ border: "1px solid #02A0A0" }}
rounded={"md"}
size={"2xs"}
fontSize={"sm"}
placeholder="Search..."
bgColor={"#EEEEEE"}
ps={8}
/>
</InputGroup>
<SearchComponent
value={searchTerm}
onChange={(value) => {
setSearchTerm(value);
// setCurrentPage(1);
refetch()
}}
/>
</HStack>
</HStack>
<DataTable
sortableColumns={["Name", "Registration Date "]}
tableHeadRow={tableHeadRow}
data={manageUser}
paginationData={{
current_page: data?.data.current_page || 1,
last_page: data?.data.last_page || 1,
per_page: data?.data.per_page || 10,
total: data?.data.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>
</Box>
<Toaster />
</MainFrame>
);
};

View File

@@ -1,4 +1,4 @@
import { Field, Input, Stack } from "@chakra-ui/react";
import { Box, Field, Input, Stack } from "@chakra-ui/react";
import {
DialogActionTrigger,
DialogBody,
@@ -12,8 +12,32 @@ import {
} from "../../../components/ui/dialog";
import { Button } from "../../../components/ui/button";
import { IoMdAdd } from "react-icons/io";
import { useState } from "react";
// import { useCreateUserMutation } from "../../../Redux/Service/manage.user";
// import { useState } from "react";
function AddRegisterUsers() {
// const [createUser] = useCreateUserMutation();
const [userType, setUserType] = useState<number | "">("");
const [user, setUser] = useState<{
principal_type_xid: number;
principal_source_xid: number | "";
first_name: string;
last_name: string;
gender: string;
date_of_birth: string;
language_name: string[];
}>({
principal_type_xid: 1,
principal_source_xid: userType,
first_name: '',
last_name: '',
gender: '',
date_of_birth: '',
language_name: [],
});
return (
<DialogRoot placement="center">
<DialogTrigger asChild>
@@ -23,13 +47,13 @@ function AddRegisterUsers() {
</DialogTrigger>
<DialogContent
bg={"#fff"}
w={{ base: '90%', md: '400px' }}
height={'80vh'}
overflow={'scroll'}
overflowX="hidden"
p={3} // Reduced padding
bgSize={'md'}
bg={"#fff"}
w={{ base: '90%', md: '400px' }}
height={'80vh'}
overflow={'scroll'}
overflowX="hidden"
p={3} // Reduced padding
bgSize={'md'}
>
<DialogHeader bg="white" >
<DialogTitle alignSelf="center" color="black" fontSize="14px">
@@ -44,42 +68,110 @@ function AddRegisterUsers() {
First Name
</Field.Label>
<Input
bgColor="#EEEEEE" color="black" border="none" pl={1} fontSize="12px" height="30px"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={user.first_name}
onChange={(e) => setUser({ ...user, first_name: e.target.value })}
/>
<Field.Label color="black" pt={1} fontSize="12px">
Last Name
</Field.Label>
<Input
bgColor="#EEEEEE" color="black" border="none" pl={1} fontSize="12px" height="30px"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={user.last_name}
onChange={(e) => setUser({ ...user, last_name: e.target.value })}
/>
<Field.Label color="black" pt={1} fontSize="12px">
Gender
</Field.Label>
<Input
bgColor="#EEEEEE" color="black" border="none" pl={1} fontSize="12px" height="30px"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={user.gender}
onChange={(e) => setUser({ ...user, gender: e.target.value })}
/>
<Field.Label color="black" pt={1} fontSize="12px">
DOB
</Field.Label>
<Input
bgColor="#EEEEEE" color="black" border="none" pl={1} fontSize="12px" height="30px"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
type="date"
value={user.date_of_birth}
onChange={(e) => setUser({ ...user, date_of_birth: e.target.value })}
/>
<Field.Label color="black" pt={1} fontSize="12px">
<Field.Root>
<Field.Label color="black" pt={1} fontSize="12px">Select User Type</Field.Label>
<Box bgColor="#EEEEEE" borderRadius="md" p={1}>
<select
style={{
width: "100%",
background: "transparent",
color: "black",
border: "none",
fontSize: "12px",
height: "30px",
outline: "none",
}}
value={userType}
onChange={(e) => setUserType(Number(e.target.value))}
>
<option value="">Select User Type</option>
<option value="2">Recruiter</option>
<option value="3">Jobseeker</option>
</select>
</Box>
</Field.Root>
{/* <Field.Label color="black" pt={1} fontSize="12px">
OTP Verified
</Field.Label>
<Input
bgColor="#EEEEEE" color="black" border="none" pl={1} fontSize="12px" height="30px"
/>
/> */}
<Field.Label color="black" pt={1} fontSize="12px">
Language
</Field.Label>
<Input
bgColor="#EEEEEE" color="black" border="none" pl={1} fontSize="12px" height="30px"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={user.language_name.join(", ")} // display as comma-separated string
onChange={(e) =>
setUser({
...user,
language_name: e.target.value
.split(",")
.map(lang => lang.trim())
.filter(Boolean), // remove empty strings
})
}
/>
</Field.Root>
</Stack>

View File

@@ -1,7 +1,6 @@
// import { MdOutlineRemoveRedEye } from "react-icons/md";
import { Field, Input, Span, Stack } from "@chakra-ui/react";
import { Box, Field, HStack, Input, Stack } from "@chakra-ui/react";
import {
DialogActionTrigger,
DialogBody,
DialogCloseTrigger,
DialogContent,
@@ -15,118 +14,332 @@ import {
import { Button } from "../../../components/ui/button";
// import { TbEdit } from "react-icons/tb";
import Edit from "../../../components/ActionIcons/Edit";
import { UserData, useUpdateUserMutation } from "../../../Redux/Service/manage.user";
import { useState } from "react";
import { Toaster, toaster } from "../../../components/ui/toaster";
interface UserPayload {
id: number;
principal_type_xid: number;
principal_source_xid: number;
first_name: string;
last_name: string;
gender: string;
date_of_birth: string | null;
language_xid: number; // ✅ Always an array
}
export interface UserFormData {
id: number;
principal_type_xid: number;
first_name: string;
last_name: string;
phone_number: string;
gender: string;
date_of_birth: string;
is_active: boolean;
principal_type: {
id: number;
principal_type_title: string;
};
principle_language_linkss: {
id?: number;
iam_principal_xid?: number;
language_xid: number;
};
}
function EditRegisterUsers({ data, refetch }: { data: UserData, refetch: () => void }) {
const [isOpen, setIsOpen] = useState(false);
const transformToFormData = (data: UserData): UserFormData => ({
...data,
principle_language_linkss: {
...data.principle_language_linkss,
language_xid: data.principle_language_linkss.language_xid, // wrap in array
},
});
const [formData, setFormData] = useState<UserFormData>(transformToFormData(data));
// const [formData, setFormData] = useState<UserData>({
// id: data?.id,
// first_name: data?.first_name || '',
// last_name: data?.last_name || '',
// principal_type_xid: data?.principal_type_xid,
// phone_number: data?.phone_number || '',
// gender: data?.gender || '',
// date_of_birth: data?.date_of_birth || '',
// principal_type: data?.principal_type,
// is_active: data?.is_active ?? true,
// principle_language_linkss: {
// ...data?.principle_language_linkss,
// language_xid: Array.isArray(data?.principle_language_linkss?.language_xid)
// ? data.principle_language_linkss.language_xid
// : [data?.principle_language_linkss?.language_xid].filter(Boolean),
// },
// // principle_language_linkss: data?.principle_language_linkss ?? [],
// });
const [updateUser, { isLoading }] = useUpdateUserMutation();
const handleOpenModal = () => {
setIsOpen(true);
};
const handleSubmit = async (event: React.FormEvent) => {
event.preventDefault();
if (formData.first_name === '' || formData.last_name === '' || formData.phone_number === '' || formData.gender === '' || formData.date_of_birth === '') {
toaster.create({
title: "Error",
description: "Input fields cannot be empty",
type: "error",
});
return;
}
// const languageData = formData?.principle_language_linkss?.language_xid;
const payload: UserPayload = {
id: formData?.id,
principal_type_xid: formData?.principal_type_xid,
principal_source_xid: formData?.id,
first_name: formData?.first_name,
last_name: formData?.last_name,
gender: formData?.gender,
date_of_birth: formData?.date_of_birth,
// language_xid: languageData,
language_xid: formData.principle_language_linkss?.language_xid
? formData.principle_language_linkss.language_xid
: formData.principle_language_linkss?.language_xid,
};
// console.log('payload', payload)
try {
const response = await updateUser(payload).unwrap();
if (response?.status === "success") {
toaster.create({
title: "Success",
description: "Country updated successfully",
type: "success",
});
setIsOpen(false);
refetch()
} else {
toaster.create({
title: "Error",
description: "Failed to update Country",
type: "error",
});
}
} catch (error: any) {
console.error("Error updating template:", error);
// alert("Failed to update template");
toaster.create({
title: "Error",
description: error ? `${error?.data.message}` : "Something went wrong",
type: "error",
});
}
};
function EditRegisterUsers() {
return (
<DialogRoot placement="center">
<DialogTrigger asChild>
<Span>
<>
<DialogRoot placement="center" key={formData.id} open={isOpen} onOpenChange={({ open }) => setIsOpen(open)}>
<DialogTrigger asChild>
{/* <Span>
<Edit />
</Span>
</DialogTrigger>
</Span> */}
<Button bg="transparent" color={"black"} h={"18px"} onClick={handleOpenModal}><Edit /></Button>
</DialogTrigger>
<DialogContent
bg={"#fff"}
w={{ base: "90%", md: "400px" }}
height={"80vh"}
overflow={"scroll"}
overflowX="hidden"
p={3} // Reduced padding
bgSize={"md"}
>
<DialogHeader bg="white" p={0}>
<DialogTitle alignSelf="center" color="black" fontSize="14px">
Edit user Accounts
</DialogTitle>
</DialogHeader>
<DialogContent
bg={"#fff"}
w={{ base: "90%", md: "400px" }}
height={"80vh"}
overflow={"scroll"}
overflowX="hidden"
p={3} // Reduced padding
bgSize={"md"}
>
<DialogHeader bg="white" p={0}>
<DialogTitle alignSelf="center" color="black" fontSize="14px">
Edit user Accounts
</DialogTitle>
</DialogHeader>
<DialogBody bg="white">
<Stack py={3}>
<Field.Root>
<Field.Label color="black" pt={1} fontSize="12px">
First Name
</Field.Label>
<Input
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
<DialogBody bg="white">
<Stack py={3}>
<Field.Root>
<Field.Label color="black" pt={1} fontSize="12px">
First Name
</Field.Label>
<Input
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={formData.first_name}
onChange={(e) => setFormData({ ...formData, first_name: e.target.value })}
/>
<Field.Label color="black" pt={1} fontSize="12px">
Last Name
</Field.Label>
<Input
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
<Field.Label color="black" pt={1} fontSize="12px">
Last Name
</Field.Label>
<Input
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={formData.last_name}
onChange={(e) => setFormData({ ...formData, last_name: e.target.value })}
/>
<Field.Label color="black" pt={1} fontSize="12px">
Gender
</Field.Label>
<Input
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
<Field.Label color="black" pt={1} fontSize="12px">
Gender
</Field.Label>
<Input
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={formData.gender}
onChange={(e) => setFormData({ ...formData, gender: e.target.value })}
/>
<Field.Label color="black" pt={1} fontSize="12px">
DOB
</Field.Label>
<Input
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
<Field.Label color="black" pt={1} fontSize="12px">
DOB
</Field.Label>
<Input
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={formData.date_of_birth ? new Date(formData.date_of_birth).toLocaleDateString('en-GB').replace(/\//g, '-') : 'N/A'}
onChange={(e) => setFormData({ ...formData, date_of_birth: e.target.value })}
disabled={formData.principal_type_xid === 2 ? true : false}
/>
<Field.Label color="black" pt={1} fontSize="12px">
OTP Verified
</Field.Label>
<Input
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
<Field.Label color="black" pt={1} fontSize="12px">
Mobile Number
</Field.Label>
<Input
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={formData.phone_number || ''}
onChange={(e) => setFormData({ ...formData, phone_number: e.target.value })}
/>
<Field.Label color="black" pt={1} fontSize="12px">
Language
</Field.Label>
<Input
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
/>
</Field.Root>
</Stack>
</DialogBody>
<DialogFooter mt={5}>
<DialogActionTrigger asChild>
<Button rounded={"md"} w={"100%"} size={"sm"} bg={"#02A0A0"}>
<Field.Label color="black" pt={1} fontSize="12px">
Type Of User
</Field.Label>
{/* <Input
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={formData.principal_type?.principal_type_title || 'N/A'}
onChange={(e) => setFormData({ ...formData, principal_type: { ...formData.principal_type, principal_type_title: e.target.value } })}
/> */}
<Box>
<select
style={{
width: "100%",
background: "transparent",
color: "black",
border: "none",
fontSize: "12px",
height: "30px",
outline: "none",
}}
value={formData.principal_type_xid?.toString() || 'N/A'}
onChange={(e) => setFormData({ ...formData, principal_type_xid: Number(e.target.value) })}
>
{/* <option value="">Select User Type</option> */}
<option value="2">Recruiter</option>
<option value="3">Jobseeker</option>
</select>
</Box>
<Field.Label color="black" pt={1} fontSize="12px">
Default Language
</Field.Label>
{/* <Input
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={formData?.principle_language_linkss?.language_xid || 'N/A'}
onChange={(e) => setFormData({
...formData, principle_language_linkss: {
...formData?.principle_language_linkss, language_xid: e.target.value
.split(",")
.map(lang => lang.trim())
.filter(Boolean)
}
})}
/> */}
<HStack>
<select
style={{
width: "100%",
background: "transparent",
color: "black",
border: "none",
fontSize: "12px",
height: "30px",
outline: "none",
}}
value={formData?.principle_language_linkss?.language_xid || ""}
onChange={(e) => {
const value = Number(e.target.value);
setFormData({
...formData,
principle_language_linkss: {
...formData?.principle_language_linkss,
language_xid: value,
},
});
}}
>
<option value="1">English</option>
<option value="2">Hindi</option>
<option value="3">Marathi</option>
<option value="4">Telgu</option>
<option value="5">Tamil</option>
<option value="6">Bengali</option>
<option value="7">Odia</option>
</select>
</HStack>
</Field.Root>
</Stack>
</DialogBody>
<DialogFooter mt={5}>
<Button rounded={"md"} w={"100%"} size={"sm"} bg={"#02A0A0"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogActionTrigger>
</DialogFooter>
<DialogCloseTrigger color="black" />
</DialogContent>
</DialogRoot>
</DialogFooter>
<DialogCloseTrigger color="black" />
</DialogContent>
</DialogRoot >
<Toaster />
</>
);
}

View File

@@ -1,7 +1,8 @@
import {
Box, HStack,
Image,
// Image,
Text
Text,
} from "@chakra-ui/react";
import MainFrame from "../../../components/MainFrame";
// import AlertDailog from "../../../components/AlertDailog";
@@ -10,21 +11,26 @@ import DataTable from "../../../components/DataTable";
import { Switch } from "../../../components/ui/switch";
import ViewRegisterUsers from "./ViewRegisterUsers";
import EditRegisterUsers from "./EditRegisterUsers";
import AddRegisterUsers from "./AddRegisterUsers";
// import AddRegisterUsers from "./AddRegisterUsers";
import { useEffect, useState } from "react";
import { useGetManageUserQuery, UserData, useUserToggleMutation } from "../../../Redux/Service/manage.user";
import { useDeleteUserMutation, useGetManageUserQuery, UserData, useUserToggleMutation } from "../../../Redux/Service/manage.user";
import SearchComponent from "../../../components/SearchComponent";
import AlertDailog from "../../../components/AlertDailog";
import { toaster } from "../../../components/ui/toaster";
import Delete from "../../../components/ActionIcons/Delete";
import { delay } from "../../../components/Utils";
// import Delete from "../../../components/ActionIcons/Delete";
const tableHeadRow = [
"Sr. No",
"First Name",
"Last Name",
"Mobile Number",
"Gender",
"DOB",
"Type Of User",
"Language",
"Activate/Deactivate",
"Default Language",
"Active/Deactive",
"Action",
];
@@ -63,10 +69,14 @@ const tableHeadRow = [
const RegisterUsers = () => {
const [currentPage, setCurrentPage] = useState(1);
const { data, refetch } = useGetManageUserQuery(currentPage)
const { data, refetch, isFetching, isError } = useGetManageUserQuery(currentPage)
const [localData, setLocalData] = useState<any[]>([]);
const [searchTerm, setSearchTerm] = useState("");
const [userToggle] = useUserToggleMutation()
const [deleteFaqPost] = useDeleteUserMutation()
const [deleteModal, setDeleteModal] = useState(false)
const [selectedFaqId, setSelectedFaqId] = useState<number | null>(null);
console.log("Register Users Data", data?.data.data);
useEffect(() => {
@@ -97,9 +107,20 @@ const RegisterUsers = () => {
try {
await userToggle({ id: agencyId, is_active: newStatus }).unwrap();
toaster.create({
title: "Success",
description: "Status updated successfully",
type: "success",
});
await delay(500);
refetch()
} catch (error) {
console.error("Error updating privacy policy:", error);
toaster.create({
title: "Error",
description: "Someting went wrong.",
type: "error",
});
setLocalData((prevData) =>
prevData.map((agency) =>
agency.id === agencyId ? { ...agency, is_active: currentStatus } : agency
@@ -108,21 +129,46 @@ const RegisterUsers = () => {
}
}
const handleDeleteFaq = async (faqId: number) => {
try {
const response = await deleteFaqPost({ id: faqId }).unwrap();
if (response?.status === "success") {
toaster.create({
title: "Success",
description: "User deleted successfully",
type: "success",
});
refetch()
console.log("User deleted successfully:", response);
}
// Optionally, refetch data or update state after deletion
} catch (error) {
console.error("Error deleting User:", error);
toaster.create({
title: "Error",
description: "Something went wrong",
type: "error",
});
}
};
const managepost = filteredData?.flatMap((agency: UserData, index: number) => ({
"Sr. No": (currentPage - 1) * (data?.data.per_page ?? 0) + index + 1,
"First Name": agency.first_name,
"Last Name": agency.last_name,
"Mobile Number": agency.phone_number,
"Gender": agency.gender,
"DOB": agency.date_of_birth ? new Date(agency.date_of_birth).toLocaleDateString('en-GB').replace(/\//g, '-') : 'N/A',
"Type Of User": agency.principal_type?.principal_type_title || 'N/A',
// "Language": agency.principle_language_links.map(lang => lang.language_name).join(', ') || 'N/A',
"Action": (
"Default Language": agency?.principle_language_linkss?.language?.language_name,
"Active/Deactive": agency.is_active === true ? 'Active' : 'Inactive',
"Action": (
<HStack justifyContent="center">
<EditRegisterUsers
// rowData={{ id: agency.id, en_name: agency.en_name, country_code: agency.country_code, phonecode: agency.phonecode, capital: agency.capital, currency: agency.currency, currency_name: agency.currency_name, currency_symbol: agency.currency_symbol }}
// refetch={refetch}
data={agency}
refetch={refetch}
/>
<ViewRegisterUsers />
<ViewRegisterUsers data={agency} />
<Box>
<Switch
colorPalette={'teal'}
@@ -131,6 +177,24 @@ const RegisterUsers = () => {
onChange={() => handleToggle(agency.id, agency.is_active ? '1' : '0')}
/>
</Box>
<AlertDailog
isOpen={deleteModal}
AltertTiggerIcon={() => <Delete onClick={() => {
setSelectedFaqId(agency.id);
setDeleteModal(true)
}} />}
alertText="Do you want to delete user?"
alertIcon={<Image src={"DeleteIcon"} h={"39px"} />}
alertCaption="are you sure you want to delete ?"
onClose={() => setDeleteModal(false)}
onConfirm={() => {
// console.log("Deleting FAQ with ID:", selectedFaqId); // Correct ID
if (selectedFaqId) {
setDeleteModal(false);
handleDeleteFaq(selectedFaqId);
}
}}
/>
</HStack>
),
}))
@@ -158,7 +222,7 @@ const RegisterUsers = () => {
refetch()
}}
/>
<AddRegisterUsers />
{/* <AddRegisterUsers /> */}
</HStack>
</HStack>
<DataTable
@@ -172,6 +236,8 @@ const RegisterUsers = () => {
total: data?.data.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>
</Box>
</MainFrame>

View File

@@ -9,8 +9,9 @@ import {
DialogTitle,
DialogTrigger,
} from "../../../components/ui/dialog";
import { UserData } from "../../../Redux/Service/manage.user";
function ViewRegisterUsers() {
function ViewRegisterUsers({ data }: { data: UserData }) {
return (
<DialogRoot placement="center">
<DialogTrigger asChild>
@@ -18,13 +19,13 @@ function ViewRegisterUsers() {
</DialogTrigger>
<DialogContent
bg={"#fff"}
w={{ base: '90%', md: '400px' }}
height={'80vh'}
overflow={'scroll'}
overflowX="hidden"
p={3} // Reduced padding
bgSize={'md'}
bg={"#fff"}
w={{ base: '90%', md: '400px' }}
height={'80vh'}
overflow={'scroll'}
overflowX="hidden"
p={3} // Reduced padding
bgSize={'md'}
>
<DialogHeader bg="white">
<DialogTitle alignSelf="center" color="black" fontSize="14px">
@@ -39,14 +40,24 @@ function ViewRegisterUsers() {
First Name
</Field.Label>
<Input
bgColor="#EEEEEE" color="black" border="none" pl={1} fontSize="12px" height="30px"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={data?.first_name || ''}
/>
<Field.Label color="black" pt={1} fontSize="12px">
Last Name
</Field.Label>
<Input
bgColor="#EEEEEE" color="black" border="none" pl={1} fontSize="12px" height="30px"
bgColor="#EEEEEE"
color="black"
border="none" pl={1}
fontSize="12px" height="30px"
value={data?.last_name || ''}
/>
<Field.Label color="black" pt={1} fontSize="12px">
@@ -54,6 +65,7 @@ function ViewRegisterUsers() {
</Field.Label>
<Input
bgColor="#EEEEEE" color="black" border="none" pl={1} fontSize="12px" height="30px"
value={data?.gender || ''}
/>
<Field.Label color="black" pt={1} fontSize="12px">
@@ -61,20 +73,41 @@ function ViewRegisterUsers() {
</Field.Label>
<Input
bgColor="#EEEEEE" color="black" border="none" pl={1} fontSize="12px" height="30px"
value={data?.date_of_birth ? new Date(data.date_of_birth).toLocaleDateString('en-GB').replace(/\//g, '-') : 'N/A'}
/>
<Field.Label color="black" pt={1} fontSize="12px">
OTP Verified
Mobile Number
</Field.Label>
<Input
bgColor="#EEEEEE" color="black" border="none" pl={1} fontSize="12px" height="30px"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={data?.phone_number || ''}
/>
<Field.Label color="black" pt={1} fontSize="12px">
Language
Type Of User
</Field.Label>
<Input
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
fontSize="12px"
height="30px"
value={data?.principal_type?.principal_type_title || 'N/A'}
/>
<Field.Label color="black" pt={1} fontSize="12px">
Default Language
</Field.Label>
<Input
bgColor="#EEEEEE" color="black" border="none" pl={1} fontSize="12px" height="30px"
value={data?.principle_language_linkss?.language?.language_name || 'N/A'}
/>
</Field.Root>
</Stack>

View File

@@ -2,12 +2,15 @@ import { Box, HStack, Text } from "@chakra-ui/react";
import MainFrame from "../../../components/MainFrame"
import DataTable from "../../../components/DataTable";
import { Switch } from "../../../components/ui/switch";
// import EditAgencyMaster from "./EditAgencyMaster";
import EditAgencyMaster from "./EditAgencyMaster";
// import ViewAgencyAddModel from "./ViewAgencyAddModel";
import ViewAgencyMaster from "./ViewAgencyMaster";
import { useAgencyMasterToggleMutation, useGetAgencyMasterQuery } from "../../../Redux/Service/agency.master.module.service";
import { useEffect, useState } from "react";
import SearchComponent from "../../../components/SearchComponent";
import { useDebounce } from "../../../components/Hooks/useDebounce";
import { toaster, Toaster } from "../../../components/ui/toaster";
import { delay } from "../../../components/Utils";
// table data
@@ -20,6 +23,7 @@ const tableHeadRow = [
"Registered Office Address",
"Website/Domain",
"GST no.",
"Agency Status",
"Action"
];
@@ -48,10 +52,12 @@ const tableHeadRow = [
const AgencyMaster = () => {
const [currentPage, setCurrentPage] = useState(1);
const { data, refetch } = useGetAgencyMasterQuery(currentPage)
const [agencyMasterToggle] = useAgencyMasterToggleMutation()
const [localData, setLocalData] = useState<any[]>([]);
const [searchTerm, setSearchTerm] = useState("");
const debouncedSearchTerm = useDebounce(searchTerm, 500);
const queryArgs = debouncedSearchTerm ? { page: currentPage, search: debouncedSearchTerm } : { page: currentPage };
const { data, refetch, isError, isFetching } = useGetAgencyMasterQuery(queryArgs)
const handleToggle = async (agencyId: string, currentStatus: number) => {
const newStatus = currentStatus ? 0 : 1;
@@ -62,6 +68,12 @@ const AgencyMaster = () => {
);
try {
await agencyMasterToggle({ id: agencyId, is_active: newStatus }).unwrap();
toaster.create({
title: "Success",
description: "Status updated successfully",
type: "success",
});
await delay(500);
refetch()
} catch (error) {
console.error("Error updating privacy policy:", error);
@@ -70,6 +82,11 @@ const AgencyMaster = () => {
agency.id === agencyId ? { ...agency, is_active: currentStatus } : agency
)
);
toaster.create({
title: "Error",
description: "Please try again later",
type: "error",
});
}
};
@@ -77,36 +94,55 @@ const AgencyMaster = () => {
setCurrentPage(page);
};
const handleSearchChange = (value: string) => {
setSearchTerm(value);
setCurrentPage(1);
};
const filteredData = localData?.filter((agency) =>
agency?.name.toLowerCase().includes(searchTerm.toLowerCase())
agency?.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
agency?.rc_number.toLowerCase().includes(searchTerm.toLowerCase()) ||
agency?.state.toLowerCase().includes(searchTerm.toLowerCase()) ||
agency?.registered_office.toLowerCase().includes(searchTerm.toLowerCase()) ||
agency?.domain_name.toLowerCase().includes(searchTerm.toLowerCase()) ||
agency?.gst_number.toLowerCase().includes(searchTerm.toLowerCase())
);
const managepost = filteredData?.map((agency: any, index: number) => ({
'id': agency.id,
"Sr. No": (currentPage - 1) * (data?.data.per_page ?? 0) + index + 1,
"Agency Name": agency.name,
"RC no.": agency.rc_number,
"State": agency.state,
"RC Status": agency.rc_status,
"Registered Office Address": agency.registered_office,
"Website/Domain": agency.domain_name,
"GST no.": agency.gst_number,
"is_active": agency.is_active,
"Action": (
<HStack justifyContent="center">
<ViewAgencyMaster agency={localData} id={agency.id} />
{/* <EditAgencyMaster editData={{ id: agency.id, name: agency.name, domain_name: agency.domain_name, gst_number: agency.gst_number, rc_number: agency.rc_number, rc_status: agency.rc_status, registered_office: agency.registered_office, state: agency.state }} refetch={refetch} /> */}
<Box>
<Switch
colorPalette={"teal"}
size={"xs"}
onChange={() => handleToggle(agency.id.toString(), Number(agency.is_active))}
checked={Boolean(Number(agency.is_active))}
/>
</Box>
</HStack>
),
}));
// const activeCount = filteredData?.filter((a: any) => a.is_active === 1).length ?? 0;
const managepost = filteredData?.map((agency: any, index: number) => {
// const isOnlyActive = activeCount === 1 && agency.is_active === 1;
return {
id: agency.id,
"Sr. No": (currentPage - 1) * (data?.data.per_page ?? 0) + index + 1,
"Agency Name": agency.name,
"RC no.": agency.rc_number,
"State": agency.state,
"RC Status": agency.rc_status,
"Registered Office Address": agency.registered_office,
"Website/Domain": agency.domain_name,
"GST no.": agency.gst_number,
"Agency Status": agency.is_domain_verified ? "Verified" : "Unverified",
"is_active": agency.is_active,
Action: (
<HStack justifyContent="center">
<ViewAgencyMaster agency={localData} id={agency.id} />
<EditAgencyMaster editData={agency} refetch={refetch} />
<Box>
<Switch
colorPalette={"teal"}
size={"xs"}
onChange={() => handleToggle(agency.id.toString(), Number(agency.is_active))}
checked={Boolean(Number(agency.is_active))}
// disabled={isOnlyActive}
/>
</Box>
</HStack>
),
};
});
useEffect(() => {
if (data?.data?.data) {
@@ -158,7 +194,7 @@ const AgencyMaster = () => {
</InputGroup> */}
<SearchComponent
value={searchTerm}
onChange={setSearchTerm}
onChange={handleSearchChange}
/>
{/* <ViewAgencyAddModel refetch={refetch} /> */}
</HStack>
@@ -174,8 +210,11 @@ const AgencyMaster = () => {
total: data?.data.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>
</Box>
</Box>
<Toaster />
</MainFrame>
)
}

View File

@@ -15,34 +15,47 @@ import { useEffect, useState } from "react";
import { useUpdateAgencyMasterMutation } from "../../../Redux/Service/agency.master.module.service";
import { Toaster, toaster } from "../../../components/ui/toaster";
interface Organization {
id: number;
raid?: string;
// interface Organization {
// id: number;
// raid?: string;
// name: string;
// auth_signatory?: string;
// state: string;
// district?: string;
// rc_number: number;
// contact_details?: string;
// registered_office: string;
// branch_office?: string;
// registered_email?: string;
// other_email?: string;
// registered_contact?: string;
// website?: string;
// domain_name: string;
// staff_domain_name?: string;
// gst_number: string;
// rc_status?: "Active" | "Inactive"; // Assuming it's a status with limited values
// }
type AgencyFormData = {
id: string;
name: string;
auth_signatory?: string;
state: string;
district?: string;
rc_number: number;
contact_details?: string;
rc_number: string;
registered_office: string;
branch_office?: string;
registered_email?: string;
other_email?: string;
registered_contact?: string;
website?: string;
domain_name: string;
staff_domain_name?: string;
gst_number: string;
rc_status?: "Active" | "Inactive"; // Assuming it's a status with limited values
}
};
function EditAgencyMaster({ editData, refetch }: { editData: Organization, refetch: VoidFunction }) {
function EditAgencyMaster<T extends AgencyFormData>({ editData, refetch }: { editData: T, refetch: VoidFunction }) {
const [formData, setFormData] = useState(editData);
const [isOpen, setIsOpen] = useState(false);
const [updateAgencyMaster] = useUpdateAgencyMasterMutation()
const [updateAgencyMaster, { isLoading }] = useUpdateAgencyMasterMutation()
console.log("Edit Data", editData);
useEffect(() => {
setFormData(editData);
}, [editData]);
@@ -104,11 +117,11 @@ function EditAgencyMaster({ editData, refetch }: { editData: Organization, refet
type: "error",
});
}
} catch (error) {
} catch (error: any) {
console.error("Error updating template:", error);
toaster.create({
title: "Error",
description: "Something went wrong.",
description: `${error.data.message || "Failed to update"}`,
type: "error",
});
// alert("Failed to update template");
@@ -254,6 +267,7 @@ function EditAgencyMaster({ editData, refetch }: { editData: Organization, refet
fontSize="12px"
height="30px"
onClick={handleSubmit}
disabled={isLoading}
>
Save
</Button>

View File

@@ -70,7 +70,7 @@ function ViewAgencyAddModel({ refetch }: { refetch: VoidFunction }) {
console.error("Error updating template:", error);
toaster.create({
title: "Error",
description: "Something went wrong",
description: "Please try again later",
type: "error",
});

View File

@@ -7,6 +7,9 @@ import EditCountryModel from "./EditCountryModel";
import { CountryData, useCountryToggleMutation, useGetCountryMasterQuery } from "../../../Redux/Service/country.master";
import { useEffect, useState } from "react";
import SearchComponent from "../../../components/SearchComponent";
import { useDebounce } from "../../../components/Hooks/useDebounce";
import { toaster, Toaster } from "../../../components/ui/toaster";
import { delay } from "../../../components/Utils";
@@ -36,10 +39,13 @@ const tableHeadRow = [
const Country = () => {
const [currentPage, setCurrentPage] = useState(1);
const { data, refetch } = useGetCountryMasterQuery(currentPage)
// const { data, refetch } = useGetCountryMasterQuery(currentPage)
const [countryToggle] = useCountryToggleMutation()
const [localData, setLocalData] = useState<any[]>([]);
const [searchTerm, setSearchTerm] = useState("");
const debouncedSearchTerm = useDebounce(searchTerm, 500);
const queryArgs = debouncedSearchTerm ? { page: currentPage, search: debouncedSearchTerm } : { page: currentPage };
const { data, refetch, isError, isFetching } = useGetCountryMasterQuery(queryArgs);
console.log("Country Data", data?.data.data)
useEffect(() => {
@@ -52,6 +58,11 @@ const Country = () => {
setCurrentPage(page);
};
const handleSearchChange = (value: string) => {
setSearchTerm(value);
setCurrentPage(1);
};
const filteredData = localData?.filter((agency) => {
const searchLower = searchTerm.toLowerCase();
const countryName = agency.en_name?.toLowerCase().includes(searchLower);
@@ -70,9 +81,20 @@ const Country = () => {
try {
await countryToggle({ id: agencyId, is_active: newStatus }).unwrap();
toaster.create({
title: "Success",
description: "Status updated successfully",
type: "success",
});
await delay(500);
refetch()
} catch (error) {
console.error("Error updating privacy policy:", error);
toaster.create({
title: "Error",
description: "Please try again later.",
type: "error",
});
setLocalData((prevData) =>
prevData.map((agency) =>
agency.id === agencyId ? { ...agency, is_active: currentStatus } : agency
@@ -99,6 +121,7 @@ const Country = () => {
),
}))
return (
<MainFrame>
@@ -137,14 +160,15 @@ const Country = () => {
</InputGroup> */}
<SearchComponent
value={searchTerm}
onChange={(value) => {
setSearchTerm(value);
// setCurrentPage(1);
refetch()
}}
// onChange={(value) => {
// setSearchTerm(value);
// // setCurrentPage(1);
// refetch()
// }}
onChange={handleSearchChange}
/>
{/* <Button bgColor={'#EEEEEE'} pl={3} pr={3}><IoMdAdd /> <Text>Add</Text></Button> */}
<CountryAddModel />
<CountryAddModel refetch={refetch} />
</HStack>
</HStack>
<DataTable
@@ -158,8 +182,11 @@ const Country = () => {
total: data?.data.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>
</Box>
<Toaster />
</MainFrame>
)
}

View File

@@ -2,12 +2,12 @@ import { DialogBody, DialogCloseTrigger, DialogContent, DialogFooter, DialogHead
import { Field, Input, Stack, Text } from "@chakra-ui/react"
import { IoMdAdd } from "react-icons/io"
import { Button } from "../../../components/ui/button"
import { useState } from "react";
import { useEffect, useState } from "react";
import { PostCountry, useCreateCountryPostMutation } from "../../../Redux/Service/country.master";
import { Toaster, toaster } from "../../../components/ui/toaster";
function CountryAddModel() {
const [createCountryPost] = useCreateCountryPostMutation()
function CountryAddModel({refetch}: { refetch: VoidFunction }) {
const [createCountryPost, { isLoading }] = useCreateCountryPostMutation()
const [isOpen, setIsOpen] = useState(false);
const [countryName, setCountryName] = useState<PostCountry>({
en_name: '',
@@ -19,12 +19,26 @@ function CountryAddModel() {
currency_symbol: '',
});
useEffect(() => {
if (!isOpen) {
setCountryName({
en_name: '',
country_code: '',
phonecode: '',
capital: '',
currency: '',
currency_name: '',
currency_symbol: '',
});
}
}, [isOpen]);
const handleOpenModal = () => {
setIsOpen(true); // Open modal when clicking "Add"
};
const handleSubmit = async () => {
if (countryName.en_name === "") {
if (countryName.en_name === "" || countryName.country_code === "" || countryName.phonecode === "" || countryName.capital === "" || countryName.currency === "" || countryName.currency_name === "" || countryName.currency_symbol === "") {
toaster.create({
title: "Error",
description: "Input fields cannot be empty",
@@ -52,6 +66,7 @@ function CountryAddModel() {
type: "success",
});
setIsOpen(false);
refetch();
} else {
toaster.create({
title: "Error",
@@ -98,7 +113,7 @@ function CountryAddModel() {
<Field.Root>
<Field.Label color="black" pt={1} fontSize="12px">Country</Field.Label>
<Input
placeholder=""
placeholder="Enter Country Name"
bgColor="#EEEEEE"
color="black"
border="none"
@@ -111,7 +126,7 @@ function CountryAddModel() {
<Field.Label color="black" pt={1} fontSize="12px">Country Code</Field.Label>
<Input
placeholder=""
placeholder="Please enter country code ex: IN, US"
bgColor="#EEEEEE"
color="black"
border="none"
@@ -124,7 +139,7 @@ function CountryAddModel() {
<Field.Label color="black" pt={1} fontSize="12px">Phone Code</Field.Label>
<Input
placeholder=""
placeholder="Please enter phone code ex: +91, +1"
bgColor="#EEEEEE"
color="black"
border="none"
@@ -136,7 +151,7 @@ function CountryAddModel() {
/>
<Field.Label color="black" pt={1} fontSize="12px">Capital</Field.Label>
<Input
placeholder=""
placeholder="Enter Capital City"
bgColor="#EEEEEE"
color="black"
border="none"
@@ -148,7 +163,7 @@ function CountryAddModel() {
/>
<Field.Label color="black" pt={1} fontSize="12px">Currency</Field.Label>
<Input
placeholder=""
placeholder="Enter Currency"
bgColor="#EEEEEE"
color="black"
border="none"
@@ -160,7 +175,7 @@ function CountryAddModel() {
/>
<Field.Label color="black" pt={1} fontSize="12px">Currency name</Field.Label>
<Input
placeholder=""
placeholder="Enter Currency Name"
bgColor="#EEEEEE"
color="black"
border="none"
@@ -172,7 +187,7 @@ function CountryAddModel() {
/>
<Field.Label color="black" pt={1} fontSize="12px">Currency Symbol</Field.Label>
<Input
placeholder=""
placeholder="Enter Currency Symbol"
bgColor="#EEEEEE"
color="black"
border="none"
@@ -187,7 +202,7 @@ function CountryAddModel() {
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -36,7 +36,7 @@ export interface EditCountryModelProps {
function EditCountryModel({ rowData, refetch }: { rowData: EditCountryModelProps, refetch: VoidFunction }) {
const [updateCountry] = useUpdateCountryMutation()
const [updateCountry, { isLoading }] = useUpdateCountryMutation()
const [editData, setEditData] = useState(rowData)
const [isOpen, setIsOpen] = useState(false);
@@ -145,7 +145,7 @@ function EditCountryModel({ rowData, refetch }: { rowData: EditCountryModelProps
</Stack>
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -9,7 +9,7 @@ import { useCreateDepartmentPostMutation, useGetDepartmentMasterDropDownQuery }
function AddDepartmentMaster({ refetch }: { refetch: VoidFunction }) {
const [jobType, setJobType] = useState("");
const [isOpen, setIsOpen] = useState(false);
const [createDepartmentPost] = useCreateDepartmentPostMutation()
const [createDepartmentPost, { isLoading }] = useCreateDepartmentPostMutation()
const { data } = useGetDepartmentMasterDropDownQuery()
const [selectdDep, setSelectdDep] = useState<any>({
id: '',
@@ -140,7 +140,7 @@ function AddDepartmentMaster({ refetch }: { refetch: VoidFunction }) {
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -9,6 +9,9 @@ import SearchComponent from "../../../components/SearchComponent";
import { useDepartmentToggleMutation, useGetDepartmentMasterQuery } from "../../../Redux/Service/department.master";
import AddDepartmentMaster from "./AddDepartmentMaster";
import EditDepartmentMaster from "./EditDepartmentMaster";
import { useDebounce } from "../../../components/Hooks/useDebounce";
import { Toaster, toaster } from "../../../components/ui/toaster";
import { delay } from "../../../components/Utils";
// table data
@@ -24,12 +27,14 @@ const tableHeadRow = [
const DepartmentMasterList = () => {
const [currentPage, setCurrentPage] = useState(1);
const { data, refetch } = useGetDepartmentMasterQuery(currentPage)
const [departmentToggle] = useDepartmentToggleMutation()
const [localData, setLocalData] = useState<any[]>([]);
const [searchTerm, setSearchTerm] = useState("");
const [searchTerm, setSearchTerm] = useState("");
const debouncedSearchTerm = useDebounce(searchTerm, 500);
const queryArgs = debouncedSearchTerm ? { page: currentPage, search: debouncedSearchTerm } : { page: currentPage };
const { data, refetch, isError, isFetching } = useGetDepartmentMasterQuery(queryArgs)
console.log("Department Data", data?.data.data)
console.log("Department Data", data?.data.data)
useEffect(() => {
if (data?.data?.data) {
@@ -37,6 +42,11 @@ const DepartmentMasterList = () => {
}
}, [data]);
const handleSearchChange = (value: string) => {
setSearchTerm(value);
setCurrentPage(1);
};
const handlePageChange = (page: number) => {
setCurrentPage(page);
};
@@ -50,9 +60,20 @@ const DepartmentMasterList = () => {
);
try {
await departmentToggle({ id: agencyId, is_active: newStatus }).unwrap();
toaster.create({
title: "Success",
description: "Status updated successfully",
type: "success",
});
await delay(500);
refetch()
} catch (error) {
console.error("Error updating privacy policy:", error);
toaster.create({
title: "Error",
description: "Someting went wrong.",
type: "error",
});
setLocalData((prevData) =>
prevData.map((agency) =>
agency.id === agencyId ? { ...agency, is_active: currentStatus } : agency
@@ -112,11 +133,7 @@ const DepartmentMasterList = () => {
<HStack >
<SearchComponent
value={searchTerm}
onChange={(value) => {
setSearchTerm(value);
// setCurrentPage(1);
refetch()
}}
onChange={handleSearchChange}
/>
{/* <Button bgColor={'#EEEEEE'} pl={3} pr={3}><IoMdAdd /> <Text>Add</Text></Button> */}
{/* <ViewAgencyAddModel /> */}
@@ -134,8 +151,11 @@ const DepartmentMasterList = () => {
total: data?.data.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>
</Box>
<Toaster />
</MainFrame>
)
}

View File

@@ -17,7 +17,7 @@ import { useGetDepartmentMasterDropDownQuery, useUpdateDepartmentMutation } from
function EditDepartmentMaster({ localData, refetch }: { localData: any, refetch: VoidFunction }) {
const [jobtype, setJobType] = useState("");
const [updateDepartment] = useUpdateDepartmentMutation()
const [updateDepartment, { isLoading }] = useUpdateDepartmentMutation()
const { data } = useGetDepartmentMasterDropDownQuery()
const [isOpen, setIsOpen] = useState(false);
const [selectdDep, setSelectdDep] = useState<any>({
@@ -145,7 +145,7 @@ function EditDepartmentMaster({ localData, refetch }: { localData: any, refetch:
</Stack>
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -9,7 +9,7 @@ import { useCreateIndustryMasterPostMutation } from "../../../Redux/Service/indu
function AddIndustryMaster({ refetch }: { refetch: VoidFunction }) {
const [jobType, setJobType] = useState("");
const [isOpen, setIsOpen] = useState(false);
const [createIndustryMasterPost] = useCreateIndustryMasterPostMutation()
const [createIndustryMasterPost, { isLoading }] = useCreateIndustryMasterPostMutation()
const handleOpenModal = () => {
setIsOpen(true); // Open modal when clicking "Add"
@@ -88,7 +88,7 @@ function AddIndustryMaster({ refetch }: { refetch: VoidFunction }) {
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -17,7 +17,7 @@ import { useUpdateIndustryMasterMutation } from "../../../Redux/Service/industry
function EditIndustryMaster({ id, localData, refetch, categories }: { id: number, localData: any, refetch: VoidFunction, categories: any }) {
const [jobtype, setJobType] = useState("");
const [updateIndustryMaster] = useUpdateIndustryMasterMutation()
const [updateIndustryMaster, { isLoading }] = useUpdateIndustryMasterMutation()
const [isOpen, setIsOpen] = useState(false);
const handleOpenModal = () => {
@@ -98,7 +98,7 @@ function EditIndustryMaster({ id, localData, refetch, categories }: { id: number
</Stack>
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -9,6 +9,9 @@ import { useGetIndustryMasterQuery, useIndustryMasterToggleMutation } from "../.
import EditIndustryMaster from "./EditIndustryMaster";
import AddIndustryMaster from "./AddIndustryMaster";
import SearchComponent from "../../../components/SearchComponent";
import { useDebounce } from "../../../components/Hooks/useDebounce";
import { Toaster, toaster } from "../../../components/ui/toaster";
import { delay } from "../../../components/Utils";
// table data
@@ -44,10 +47,12 @@ const tableHeadRow = [
const IndustryMasterList = () => {
const [currentPage, setCurrentPage] = useState(1);
const { data, refetch } = useGetIndustryMasterQuery(currentPage)
const [industryMasterToggle] = useIndustryMasterToggleMutation()
const [localData, setLocalData] = useState<any[]>([]);
const [searchTerm, setSearchTerm] = useState("");
const [searchTerm, setSearchTerm] = useState("");
const debouncedSearchTerm = useDebounce(searchTerm, 500);
const queryArgs = debouncedSearchTerm ? { page: currentPage, search: debouncedSearchTerm } : { page: currentPage };
const { data, refetch, isError, isFetching } = useGetIndustryMasterQuery(queryArgs)
useEffect(() => {
if (data?.data?.data) {
@@ -55,6 +60,11 @@ const IndustryMasterList = () => {
}
}, [data]);
const handleSearchChange = (value: string) => {
setSearchTerm(value);
setCurrentPage(1);
};
const handlePageChange = (page: number) => {
setCurrentPage(page);
};
@@ -68,9 +78,20 @@ const IndustryMasterList = () => {
);
try {
await industryMasterToggle({ id: agencyId, is_active: newStatus }).unwrap();
toaster.create({
title: "Success",
description: "Status updated successfully",
type: "success",
});
await delay(500);
refetch()
} catch (error) {
console.error("Error updating privacy policy:", error);
toaster.create({
title: "Error",
description: "Someting went wrong.",
type: "error",
});
setLocalData((prevData) =>
prevData.map((agency) =>
agency.id === agencyId ? { ...agency, is_active: currentStatus } : agency
@@ -130,11 +151,7 @@ const IndustryMasterList = () => {
<HStack >
<SearchComponent
value={searchTerm}
onChange={(value) => {
setSearchTerm(value);
// setCurrentPage(1);
refetch()
}}
onChange={handleSearchChange}
/>
{/* <Button bgColor={'#EEEEEE'} pl={3} pr={3}><IoMdAdd /> <Text>Add</Text></Button> */}
{/* <ViewAgencyAddModel /> */}
@@ -152,8 +169,11 @@ const IndustryMasterList = () => {
total: data?.data.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>
</Box>
<Toaster />
</MainFrame>
)
}

View File

@@ -9,7 +9,7 @@ import { Toaster, toaster } from "../../../components/ui/toaster";
function EditJobStatusModel({ localData, refetch }: { localData: any, refetch: VoidFunction }) {
const [updateJobStatus] = useUpdateJobStatusMutation()
const [updateJobStatus, { isLoading }] = useUpdateJobStatusMutation()
const [title, setTitle] = useState(localData.translation.title);
const [isOpen, setIsOpen] = useState(false);
@@ -89,7 +89,7 @@ function EditJobStatusModel({ localData, refetch }: { localData: any, refetch: V
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -7,7 +7,9 @@ import EditJobStatusModel from "./EditJobStatusModel";
import { useGetJobStatusQuery, useJobStatusToggleMutation } from "../../../Redux/Service/job.status";
import { useEffect, useState } from "react";
import SearchComponent from "../../../components/SearchComponent";
import { toaster } from "../../../components/ui/toaster";
import { toaster, Toaster } from "../../../components/ui/toaster";
import { useDebounce } from "../../../components/Hooks/useDebounce";
import { delay } from "../../../components/Utils";
@@ -37,9 +39,11 @@ const tableHeadRow = [
const JobStatus = () => {
const [currentPage, setCurrentPage] = useState(1);
const { data, refetch } = useGetJobStatusQuery(currentPage)
const [localData, setLocalData] = useState<any[]>([]);
const [searchTerm, setSearchTerm] = useState("");
const debouncedSearchTerm = useDebounce(searchTerm, 500);
const queryArgs = debouncedSearchTerm ? { page: currentPage, search: debouncedSearchTerm } : { page: currentPage };
const { data, refetch, isError, isFetching } = useGetJobStatusQuery(queryArgs)
const [localData, setLocalData] = useState<any[]>([]);
const [jobStatusToggle] = useJobStatusToggleMutation()
console.log(data?.data.data)
@@ -53,6 +57,11 @@ const JobStatus = () => {
setCurrentPage(page);
};
const handleSearchChange = (value: string) => {
setSearchTerm(value);
setCurrentPage(1);
};
const handleToggle = async (agencyId: string, currentStatus: number) => {
const newStatus = currentStatus ? 0 : 1;
setLocalData((prevData) =>
@@ -62,6 +71,12 @@ const JobStatus = () => {
);
try {
await jobStatusToggle({ id: agencyId, is_active: newStatus }).unwrap();
toaster.create({
title: "Success",
description: "Status updated successfully",
type: "success",
});
await delay(500);
refetch()
} catch (error) {
console.error("Error updating:", error);
@@ -125,11 +140,7 @@ const JobStatus = () => {
<HStack >
<SearchComponent
value={searchTerm}
onChange={(value) => {
setSearchTerm(value);
// setCurrentPage(1);
refetch()
}}
onChange={handleSearchChange}
/>
{/* <Button bgColor={'#EEEEEE'} pl={3} pr={3}><IoMdAdd /> <Text>Add</Text></Button> */}
<JobStatusAddModel refetch={refetch} />
@@ -146,8 +157,11 @@ const JobStatus = () => {
total: data?.data.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>
</Box>
<Toaster />
</MainFrame>
)
}

View File

@@ -9,7 +9,7 @@ import { useState } from "react"
function JobStatusAddModel({ refetch }: { refetch: VoidFunction }) {
const [title, setTitle] = useState('')
const [isOpen, setIsOpen] = useState(false);
const [createJobStatusPost] = useCreateJobStatusPostMutation()
const [createJobStatusPost, { isLoading }] = useCreateJobStatusPostMutation()
const handleOpenModal = () => {
setIsOpen(true);
@@ -88,7 +88,7 @@ function JobStatusAddModel({ refetch }: { refetch: VoidFunction }) {
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -17,7 +17,7 @@ import { toaster } from "../../../components/ui/toaster";
function EditJobeModel({ id, localData, refetch }: { id: number, localData: any, refetch: VoidFunction }) {
const [jobtype, setJobType] = useState("");
const [updateJobType] = useUpdateJobTypeMutation()
const [updateJobType, { isLoading }] = useUpdateJobTypeMutation()
const [isOpen, setIsOpen] = useState(false);
const handleOpenModal = () => {
@@ -97,7 +97,7 @@ function EditJobeModel({ id, localData, refetch }: { id: number, localData: any,
</Stack>
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -9,7 +9,7 @@ import { toaster } from "../../../components/ui/toaster";
function JobAddModel({ refetch }: { refetch: VoidFunction }) {
const [jobType, setJobType] = useState("");
const [isOpen, setIsOpen] = useState(false);
const [createJobTypePost] = useCreateJobTypePostMutation()
const [createJobTypePost, { isLoading }] = useCreateJobTypePostMutation()
const handleOpenModal = () => {
setIsOpen(true); // Open modal when clicking "Add"
@@ -87,7 +87,7 @@ function JobAddModel({ refetch }: { refetch: VoidFunction }) {
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -6,9 +6,11 @@ import DataTable from "../../../components/DataTable";
import { Switch } from "../../../components/ui/switch";
import JobAddModel from "./JobAddModel";
import EditJobeModel from "./EditJobModel";
import { JobTypeData, useGetJobTypeQuery } from "../../../Redux/Service/job.type.service";
import { JobTypeData, useGetJobTypeQuery, useJobTypeToggleMutation } from "../../../Redux/Service/job.type.service";
import { useEffect, useState } from "react";
import SearchComponent from "../../../components/SearchComponent";
import { toaster, Toaster } from "../../../components/ui/toaster";
import { delay } from "../../../components/Utils";
@@ -38,10 +40,10 @@ const tableHeadRow = [
const JobType = () => {
const [currentPage, setCurrentPage] = useState(1);
const { data, refetch } = useGetJobTypeQuery(currentPage)
const { data, refetch, isFetching, isError } = useGetJobTypeQuery(currentPage)
const [localData, setLocalData] = useState<any[]>([]);
const [searchTerm, setSearchTerm] = useState("");
// const [templateMasterToggle] = useTemplateMasterToggleMutation()
const [jobTypeToggle] = useJobTypeToggleMutation()
console.log('DATA', data?.data.data);
@@ -61,25 +63,36 @@ const JobType = () => {
return title;
});
// const handleToggle = async (agencyId: string, currentStatus: number) => {
// const newStatus = currentStatus ? 0 : 1;
// setLocalData((prevData) =>
// prevData.map((agency) =>
// agency.id === agencyId ? { ...agency, is_active: newStatus } : agency
// )
// );
// try {
// await templateMasterToggle({ id: agencyId, is_active: newStatus }).unwrap();
// refetch()
// } catch (error) {
// console.error("Error updating privacy policy:", error);
// setLocalData((prevData) =>
// prevData.map((agency) =>
// agency.id === agencyId ? { ...agency, is_active: currentStatus } : agency
// )
// );
// }
// };
const handleToggle = async (agencyId: string, currentStatus: number) => {
const newStatus = currentStatus ? 0 : 1;
setLocalData((prevData) =>
prevData.map((agency) =>
agency.id === agencyId ? { ...agency, is_active: newStatus } : agency
)
);
try {
await jobTypeToggle({ id: agencyId, is_active: newStatus }).unwrap();
toaster.create({
title: "Success",
description: "Status updated successfully",
type: "success",
});
await delay(500);
refetch()
} catch (error) {
console.error("Error updating privacy policy:", error);
setLocalData((prevData) =>
prevData.map((agency) =>
agency.id === agencyId ? { ...agency, is_active: currentStatus } : agency
)
);
toaster.create({
title: "Error",
description: "Please try again later",
type: "error",
});
}
};
const managepost = filteredData?.map((agency: JobTypeData, index: number) => ({
@@ -94,7 +107,7 @@ const JobType = () => {
<Switch
colorPalette={'teal'}
size={"xs"}
// onChange={() => handleToggle(agency.id, Number(agency.is_active))}
onChange={() => handleToggle(agency.id.toString(), Number(agency.is_active))}
checked={Boolean(Number(agency.is_active))}
/>
</Box>
@@ -142,8 +155,11 @@ const JobType = () => {
total: data?.data.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>
</Box>
<Toaster />
</MainFrame>
)
}

View File

@@ -19,7 +19,7 @@ import { useState } from "react";
// import { FaRegEdit } from "react-icons/fa";
import Edit from "../../../components/ActionIcons/Edit";
import { Toaster, toaster } from "../../../components/ui/toaster";
import { Template } from "../../../Redux/Service/template.master.service";
// import { Template } from "../../../Redux/Service/template.master.service";
import axios from "axios";
const IMGURL = import.meta.env.VITE_IMG_TEMPLATES
@@ -30,13 +30,14 @@ function EditTemplateModel({ id, localData, refetch }: { id: number, localData:
const [subTitle, setSubTitle] = useState("");
const [userType, setUserType] = useState<number | "">("");
const [images, setImages] = useState<(File | string)[]>([]);
const [loading, setLoading] = useState(false);
// const [objectURLs, setObjectURLs] = useState<string[]>([]); // Store object URLs separately
// const [updateTemplateMaster] = useUpdateTemplateMasterMutation()
const [isOpen, setIsOpen] = useState(false);
const [selectedTemplate, setSelectedTemplate] = useState<Template | null>(null);
// const [selectedTemplate, setSelectedTemplate] = useState<Template | null>(null);
const token = localStorage.getItem("token");
console.log(selectedTemplate);
console.log(images);
const handleImageChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
@@ -50,14 +51,17 @@ function EditTemplateModel({ id, localData, refetch }: { id: number, localData:
return;
}
setImages((prevImages) => [...prevImages, file]);
// setImages((prevImages) => [...prevImages, file]);
if (file) {
setImages([file])
}
}
};
const handleOpenModal = () => {
const template = localData?.find((item: any) => item.id === id);
if (template) {
setSelectedTemplate(template);
// setSelectedTemplate(template);
setTitle(template.post_template_translate.length > 0 ? template.post_template_translate[0].title : "");
setSubTitle(template.post_template_translate.length > 0 ? template.post_template_translate[0].sub_title : "");
setUserType(template.principle_type_xid?.toString() || "");
@@ -90,9 +94,9 @@ function EditTemplateModel({ id, localData, refetch }: { id: number, localData:
return;
}
const newImages = images.filter((image) => image instanceof File);
// const newImages = images.filter((image) => image instanceof File);
if (newImages.length === 0) {
if (images.length === 0) {
toaster.create({
title: "Error",
description: "Please upload at least one image.",
@@ -101,14 +105,18 @@ function EditTemplateModel({ id, localData, refetch }: { id: number, localData:
return;
}
setLoading(true);
const formData = new FormData();
formData.append("id", `${id}`);
formData.append("principle_type_xid", `${userType}`);
formData.append("title", title);
formData.append("sub_title", subTitle);
newImages.forEach((image, index) => {
formData.append(`image_name[${index}]`, image, image.name);
images.forEach((image, index) => {
if (image instanceof File) {
formData.append(`image_name[${index}]`, image, image.name);
}
});
try {
@@ -123,12 +131,14 @@ function EditTemplateModel({ id, localData, refetch }: { id: number, localData:
});
}
setIsOpen(false);
setLoading(false);
refetch()
} catch (error) {
} catch (error: any) {
console.error("Error updating template:", error);
setLoading(false);
toaster.create({
title: "Error",
description: "Failed to update template. Please try again.",
description: `${error.response?.data?.message || "Please try again later."}`,
type: "error",
});
}
@@ -294,7 +304,7 @@ function EditTemplateModel({ id, localData, refetch }: { id: number, localData:
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={loading}>
Save
</Button>
</DialogFooter>

View File

@@ -3,7 +3,7 @@ import { Box, Field, Input, Stack, Text } from "@chakra-ui/react"
import { IoMdAdd } from "react-icons/io"
import { Button } from "../../../components/ui/button"
import { FiUpload } from "react-icons/fi";
import { useState } from "react";
import { useEffect, useState } from "react";
// import { useCreateTemplatePostMutation } from "../../../Redux/Service/template.master.service";
import { Toaster, toaster } from "../../../components/ui/toaster"
import axios from "axios";
@@ -17,12 +17,23 @@ function TemplateAddModel({ refetch }: { refetch: VoidFunction }) {
const [images, setImages] = useState<(File | string)[]>([]);
// const [createTemplatePost] = useCreateTemplatePostMutation()
const [isOpen, setIsOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const token = localStorage.getItem("token");
useEffect(() => {
if (!isOpen) {
setTitle("");
setSubTitle("");
setUserType("");
setImages([]);
}
}, [isOpen]);
if (!token) {
console.error("No token found in localStorage!");
return;
return null;
}
const handleOpenModal = () => {
setIsOpen(true); // Open modal when clicking "Add"
};
@@ -40,7 +51,10 @@ function TemplateAddModel({ refetch }: { refetch: VoidFunction }) {
return;
}
setImages((prevImages) => [...prevImages, file]);
// setImages((prevImages) => [...prevImages, file]);
if(file){
setImages([file])
}
}
};
@@ -72,6 +86,7 @@ function TemplateAddModel({ refetch }: { refetch: VoidFunction }) {
// image_name: images.filter((img) => typeof img === "string"), // Send only Base64 strings
// };
setIsLoading(true);
const formData = new FormData();
formData.append("principle_type_xid", `${userType}`);
@@ -106,13 +121,19 @@ function TemplateAddModel({ refetch }: { refetch: VoidFunction }) {
setUserType("");
setImages([]);
setIsOpen(false)
} catch (error) {
setIsLoading(false);
} catch (error: any) {
console.error("Error creating template:", error);
// alert("Failed to create template");
toaster.create({
title: "Error",
description: `${error.response?.data?.message || "Please try again later."}`,
type: "error",
});
}
};
// console.log("Token stored:", window.localStorage.getItem("token"));
// console.log("Token stored:", window.localStorage.getItem("token"))
return (
@@ -226,7 +247,7 @@ function TemplateAddModel({ refetch }: { refetch: VoidFunction }) {
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -9,6 +9,8 @@ import EditTemplateModel from "./EditTemplateModel";
import { Template, useGetTemplateMasterQuery, useTemplateMasterToggleMutation } from "../../../Redux/Service/template.master.service";
import { useEffect, useState } from "react";
import SearchComponent from "../../../components/SearchComponent";
import { toaster, Toaster } from "../../../components/ui/toaster";
import { delay } from "../../../components/Utils";
const APIURL = import.meta.env.VITE_IMG_TEMPLATES
@@ -17,6 +19,7 @@ const APIURL = import.meta.env.VITE_IMG_TEMPLATES
const tableHeadRow = [
"Sr. No",
"Title",
"User Type",
"Images",
"Action"
];
@@ -46,7 +49,7 @@ const tableHeadRow = [
const TemplateMaster = () => {
const [currentPage, setCurrentPage] = useState(1);
const { data, refetch } = useGetTemplateMasterQuery(currentPage)
const { data, refetch, isFetching, isError } = useGetTemplateMasterQuery(currentPage)
const [localData, setLocalData] = useState<any[]>([]);
const [templateMasterToggle] = useTemplateMasterToggleMutation();
const [searchTerm, setSearchTerm] = useState("");
@@ -71,6 +74,12 @@ const TemplateMaster = () => {
);
try {
await templateMasterToggle({ id: agencyId, is_active: newStatus }).unwrap();
toaster.create({
title: "Success",
description: "Status updated successfully",
type: "success",
});
await delay(500);
refetch()
} catch (error) {
console.error("Error updating privacy policy:", error);
@@ -79,6 +88,11 @@ const TemplateMaster = () => {
agency.id === agencyId ? { ...agency, is_active: currentStatus } : agency
)
);
toaster.create({
title: "Error",
description: "Please try again later",
type: "error",
});
}
};
@@ -86,37 +100,45 @@ const TemplateMaster = () => {
agency.post_template_translate[0].title.toLowerCase().includes(searchTerm.toLowerCase())
);
const managepost = filteredData?.map((agency: Template, index: number) => ({
'id': agency.id,
"Sr. No": (currentPage - 1) * (data?.data.per_page ?? 0) + index + 1,
"Title": agency.post_template_translate.length > 0
? agency.post_template_translate[0].title
: "N/A",
"Images": (
// <Image w={50} src={img} />
<HStack key={agency.id}>
{agency.post_template_image.map((img) => (
<Image rounded={'lg'} w={100} h={50} src={`${APIURL}${img.image_name}`} />
))}
const activeCount = filteredData?.filter((a: any) => a.is_active === 1).length ?? 0;
{/* <Image rounded={'lg'} w={100} h={50} src={Templateimg} /> */}
</HStack>
),
const managepost = filteredData?.map((agency: Template, index: number) => {
const isOnlyActive = activeCount === 1 && Number(agency.is_active) === 1;
"Action": (
<HStack justifyContent="center">
<EditTemplateModel id={agency.id} localData={localData} refetch={refetch} />
<Box>
<Switch
colorPalette={'teal'}
size={"xs"}
onChange={() => handleToggle(agency.id.toString(), Number(agency.is_active ?? 0))}
checked={Boolean(Number(agency.is_active))}
/>
</Box>
</HStack>
),
}));
return {
'id': agency.id,
"Sr. No": (currentPage - 1) * (data?.data.per_page ?? 0) + index + 1,
"Title": agency.post_template_translate.length > 0
? agency.post_template_translate[0].title
: "N/A",
"User Type": agency.principle_type_xid === 2 ? 'Recruiter' : 'Job Seeker',
"Images": (
// <Image w={50} src={img} />
<HStack>
{agency.post_template_image.map((img) => (
<Image key={img.id} rounded={'lg'} w={100} h={50} src={`${APIURL}${img.image_name}`} />
))}
{/* <Image rounded={'lg'} w={100} h={50} src={Templateimg} /> */}
</HStack>
),
"Action": (
<HStack justifyContent="center">
<EditTemplateModel id={agency.id} localData={localData} refetch={refetch} />
<Box>
<Switch
colorPalette={'teal'}
size={"xs"}
onChange={() => handleToggle(agency.id.toString(), Number(agency.is_active ?? 0))}
checked={Boolean(Number(agency.is_active))}
disabled={isOnlyActive}
/>
</Box>
</HStack>
)
}
});
return (
@@ -172,8 +194,11 @@ const TemplateMaster = () => {
total: data?.data.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>
</Box>
<Toaster />
</MainFrame>
)
}

View File

@@ -24,7 +24,7 @@ import { useUpdateWorkSpaceMutation } from "../../../Redux/Service/workspace.mod
function EditWorkModel({ localData, refetch }: {localData: any, refetch: VoidFunction}) {
const [title, setTitle] = useState(localData?.en_name);
const [isOpen, setIsOpen] = useState(false);
const [updateWorkSpace] = useUpdateWorkSpaceMutation()
const [updateWorkSpace, { isLoading }] = useUpdateWorkSpaceMutation()
console.log("localData", localData)
const handleOpenModal = () => {
@@ -104,7 +104,7 @@ function EditWorkModel({ localData, refetch }: {localData: any, refetch: VoidFun
</Stack>
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -2,14 +2,14 @@ import { DialogBody, DialogCloseTrigger, DialogContent, DialogFooter, DialogHead
import { Field, Input, Stack, Text } from "@chakra-ui/react"
import { IoMdAdd } from "react-icons/io"
import { Button } from "../../../components/ui/button"
import { useState } from "react";
import { useEffect, useState } from "react";
import { useCreateWorkspacePostMutation } from "../../../Redux/Service/workspace.mode";
import { Toaster, toaster } from "../../../components/ui/toaster";
function WorkAddModel({ refetch }: { refetch: VoidFunction }) {
const [title, setTitle] = useState('')
const [isOpen, setIsOpen] = useState(false);
const [createWorkspacePost] = useCreateWorkspacePostMutation()
const [createWorkspacePost, { isLoading }] = useCreateWorkspacePostMutation()
const handleOpenModal = () => {
setIsOpen(true);
@@ -40,6 +40,12 @@ function WorkAddModel({ refetch }: { refetch: VoidFunction }) {
}
};
useEffect(() => {
if (!isOpen) {
setTitle("");
}
}, [isOpen]);
return (
<DialogRoot placement="center" open={isOpen}>
@@ -88,7 +94,7 @@ function WorkAddModel({ refetch }: { refetch: VoidFunction }) {
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>

View File

@@ -37,7 +37,7 @@ const tableHeadRow = [
const WorkspaceMode = () => {
const [currentPage, setCurrentPage] = useState(1);
const { data, refetch } = useGetWorkSpaceModeQuery(currentPage)
const { data, refetch, isError, isFetching } = useGetWorkSpaceModeQuery(currentPage)
const [localData, setLocalData] = useState<any[]>([]);
const [searchTerm, setSearchTerm] = useState("");
const [workspaceToggle] = useWorkspaceToggleMutation()
@@ -63,12 +63,17 @@ const WorkspaceMode = () => {
);
try {
await workspaceToggle({ id: agencyId, is_active: newStatus }).unwrap();
toaster.create({
title: "Success",
description: "Status updated successfully",
type: "success",
});
refetch()
} catch (error) {
console.error("Error updating privacy policy:", error);
toaster.create({
title: "Error",
description: "Someting went wrong.",
description: "Please try again later.",
type: "error",
});
setLocalData((prevData) =>
@@ -88,7 +93,9 @@ const WorkspaceMode = () => {
const managepost = filteredData?.map((agency: any, index: number) => ({
'id': agency.id,
"Sr. No": (currentPage - 1) * (data?.data.per_page ?? 0) + index + 1,
"Title": agency.en_name,
"Title": agency.en_name.length > 12
? agency.en_name.slice(0, 12) + '...'
: agency.en_name,
"is_active": agency.is_active,
"Action": (
<HStack justifyContent="center">
@@ -131,7 +138,7 @@ const WorkspaceMode = () => {
}}
/>
{/* <Button bgColor={'#EEEEEE'} pl={3} pr={3}><IoMdAdd /> <Text>Add</Text></Button> */}
<WorkAddModel refetch={refetch}/>
<WorkAddModel refetch={refetch} />
</HStack>
</HStack>
<DataTable
@@ -145,6 +152,8 @@ const WorkspaceMode = () => {
total: data?.data.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>
</Box>
<Toaster />

View File

@@ -1,11 +1,11 @@
import { DialogCloseTrigger, Field, IconButton, Input, Stack, Text } from "@chakra-ui/react";
import { Button } from "../../components/ui/button";
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot, DialogTitle, DialogTrigger } from "../../components/ui/dialog";
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot, DialogTitle } from "../../components/ui/dialog";
// import EnterPassword from "./EnterPassword";
import { useForm } from "react-hook-form";
import { useEffect, useState } from "react";
import { toaster } from "../../components/ui/toaster";
import { toaster, Toaster } from "../../components/ui/toaster";
import { useNewPasswordSetMutation } from "../../Redux/Service/profile.password";
import { LuEye, LuEyeOff } from "react-icons/lu";
import { InputGroup } from "../../components/ui/input-group";
@@ -114,11 +114,11 @@ function Changepassword({ onClose, isOpen }: EnterPasswordProps) {
onOpenChange={(open) => !open && onClose()}
>
<DialogTrigger asChild>
{/* <DialogTrigger asChild>
<Button bg="#02A0A0" size={'2xs'} color={"#fff"} px={2} >
Change Password
</Button>
</DialogTrigger>
</DialogTrigger> */}
<DialogContent
bg={"#fff"}
@@ -203,7 +203,7 @@ function Changepassword({ onClose, isOpen }: EnterPasswordProps) {
pl={1}
fontSize="12px"
height="30px"
type= {showNewPassword ? "password" : "Text"}
type={showNewPassword ? "text" : "password"}
border={errors.confirm_password ? "1px solid red" : "1px solid grey"}
{...register("confirm_password", {
required: "Please confirm your password",
@@ -240,6 +240,7 @@ function Changepassword({ onClose, isOpen }: EnterPasswordProps) {
<DialogCloseTrigger color="black" />
</DialogContent>
</DialogRoot >
<Toaster />
</>
)
}

View File

@@ -6,9 +6,78 @@ import { Field } from "../../components/ui/field";
// import Changepassword from "./ChangePassword";
import EnterPassword from "./EnterPassword";
import { useGetProfileQuery } from "../../Redux/Service/profile.password";
// import { useUpdateImageMutation } from "../../Redux/Service/myprofie.service";
import { useEffect, useRef, useState } from "react";
import { Toaster, toaster } from "../../components/ui/toaster";
import axios from "axios";
const APIURL = import.meta.env.VITE_API_URL
const PROFILEIMGURL = import.meta.env.VITE_IMG_PROFILE
const Profile = () => {
const { data } = useGetProfileQuery()
const { data, refetch } = useGetProfileQuery()
const fileInputRef = useRef<HTMLInputElement>(null);
const [avatarSrc, setAvatarSrc] = useState<string>("");
// const [updateImage] = useUpdateImageMutation();
useEffect(() => {
if (data?.data.profile_photo) {
setAvatarSrc(data.data.profile_photo);
}
}, [data?.data.profile_photo]);
const handleClick = () => {
fileInputRef.current?.click();
};
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
if (file.size > 2 * 1024 * 1024) {
alert("File size exceeds 2MB limit.");
return;
}
// Preview the image
const previewUrl = URL.createObjectURL(file);
setAvatarSrc(previewUrl);
// Prepare FormData
const formData = new FormData();
formData.append("profile_photo", file, file.name);
const token = localStorage.getItem("token");
try {
if (token) {
await axios.post(`${APIURL}/profile-image-edit`, formData, {
headers: {
'Content-Type': 'multipart/form-data',
'access-token': `${token}`,
},
// withCredentials: true,
});
toaster.create({
title: "Success",
description: "Updated successfully",
type: "success",
});
refetch();
}
} catch (error) {
console.error("Error updating image:", error);
toaster.create({
title: "Error",
description: "Something went wrong",
type: "error",
});
}
};
console.log('PROFILE DATA:', data?.data);
return (
@@ -20,10 +89,11 @@ const Profile = () => {
<HStack shadow={'md'} rounded={'lg'} justifyContent={'space-between'} alignItems={'flex-end'} w={'100%'} px={3} py={3} >
<VStack w={'100%'} alignItems={'flex-start'} gap={0}>
<Box mb={2} position="relative" width="fit-content"
cursor="pointer" onClick={() => alert("Avatar clicked!")}>
<Avatar.Root size={"2xl"} style={{ display: "inline-block", width: "auto" }}>
cursor="pointer" onClick={handleClick}>
<Avatar.Root size={"2xl"} style={{ display: "flex", width: "50px", height: '50px', justifyContent: 'center' }}>
<Avatar.Fallback />
<Avatar.Image src="https://i.pinimg.com/736x/d6/cd/0f/d6cd0ffd4634b0763d3958a7325ce26e.jpg" />
{/* <Avatar.Image src="https://i.pinimg.com/736x/d6/cd/0f/d6cd0ffd4634b0763d3958a7325ce26e.jpg" /> */}
{avatarSrc && <Avatar.Image src={`${PROFILEIMGURL}${avatarSrc}`} />}
</Avatar.Root>
<Box
position="absolute"
@@ -33,6 +103,13 @@ const Profile = () => {
>
<FaCamera color="#ccc" size={16} />
</Box>
<input
type="file"
accept="image/*"
ref={fileInputRef}
style={{ display: "none" }}
onChange={handleFileChange}
/>
</Box>
<Text color={"black"} as={'span'} fontSize={'sm'} fontWeight={"bold"}>{`${data?.data?.first_name.charAt(0).toUpperCase()}${data?.data.first_name.slice(1)}`}
</Text>
@@ -63,7 +140,7 @@ const Profile = () => {
</VStack>
</HStack>
<Toaster />
</MainFrame >
)
}

View File

@@ -123,7 +123,7 @@ const SetNewPassword = () => {
height={"fit-content"}
mr={2}
>
{showOldPassword ? <LuEye /> : <LuEyeOff />}
{showOldPassword ? <LuEyeOff /> : <LuEye />}
</IconButton>
}
>
@@ -158,7 +158,7 @@ const SetNewPassword = () => {
color={"#000"}
mr={2}
>
{showNewPassword ? <LuEye /> : <LuEyeOff />}
{showNewPassword ? <LuEyeOff /> : <LuEye />}
</IconButton>
}
>

View File

@@ -12,26 +12,54 @@ import {
import { Field, Grid, Heading, Input, Stack, Text } from "@chakra-ui/react";
import { IoMdAdd } from "react-icons/io";
import { Checkbox } from "../../components/ui/checkbox";
import { useCreateSubAdminPostMutation } from "../../Redux/Service/manage.subadmin.service";
import { toaster } from "../../components/ui/toaster";
import { useState } from "react";
import { PermissionResponse, useCreateSubAdminPostMutation } from "../../Redux/Service/manage.subadmin.service";
import { toaster, Toaster } from "../../components/ui/toaster";
import { useEffect, useState } from "react";
function AddModel({ refetch }: { refetch: VoidFunction }) {
const [createSubAdminPost] = useCreateSubAdminPostMutation();
function AddModel({ refetch, allPermissions }: { refetch: VoidFunction, allPermissions?: PermissionResponse }) {
const [createSubAdminPost, { isLoading }] = useCreateSubAdminPostMutation();
// State fields
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [userName, setUserName] = useState("");
// const [userName, setUserName] = useState("");
const [dateOfBirth, setDateOfBirth] = useState("");
const [gender, setGender] = useState("");
const [ setIsOpen] = useState(false);
const [email, setEmail] = useState("");
const [phonenumber, setPhonenumber] = useState("");
const [permissions, setPermission] = useState<number[]>([]);
const [isOpen, setIsOpen] = useState(false);
// const [ setIsOpen] = useState(false);
const handleOpenModal = () => {
setIsOpen(true);
};
const handleCheckboxToggle = (permissionId: number) => {
setPermission((prevData) =>
prevData.includes(permissionId)
? prevData.filter((id) => id !== permissionId)
: [...prevData, permissionId]
);
};
useEffect(() => {
if (!isOpen) {
setFirstName("");
setLastName("");
// setUserName("");
setDateOfBirth("");
setGender("");
setEmail("");
setPhonenumber("");
setPermission([]);
}
}, [isOpen]);
const handleSubmit = async () => {
if (
!firstName.trim() ||
!lastName.trim() ||
!userName.trim() ||
!dateOfBirth.trim() ||
!gender.trim()
) {
@@ -43,17 +71,35 @@ function AddModel({ refetch }: { refetch: VoidFunction }) {
return;
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
toaster.create({
title: "Invalid Email",
description: "Please enter a valid email address",
type: "error",
});
return;
}
if (phonenumber.length !== 10) {
toaster.create({
title: "Invalid Phone Number",
description: "Phone number must be exactly 10 digits",
type: "error",
});
return;
}
const payload = {
principal_type_xid: 4,
principal_source_xid: 1,
user_name: userName,
// user_name: userName,
first_name: firstName,
last_name: lastName,
date_of_birth: dateOfBirth,
gender: gender,
email_address: "example@yopmail.com", // Hardcoded
phone_number: "9876543210", // Hardcoded
created_by: 1,
email_address: email,
phone_number: phonenumber,
permission: permissions.filter((id) => typeof id === "number"),
// created_by: 1,
};
try {
@@ -65,27 +111,33 @@ function AddModel({ refetch }: { refetch: VoidFunction }) {
type: "success",
});
refetch();
setIsOpen(false);
setFirstName("");
setLastName("");
setUserName("");
// setUserName("");
setDateOfBirth("");
setGender("");
setIsOpen(false);
}
} catch (error) {
} catch (error:any) {
console.error("Error creating sub-admin:", error);
toaster.create({
title: "Error",
description: "Failed to create sub-admin",
description: error ? error.data.message : "Failed to create sub-admin",
type: "error",
});
}
};
return (
<DialogRoot placement="center">
<DialogRoot placement="center" open={isOpen} onOpenChange={({ open }) => setIsOpen(open)}>
<DialogTrigger asChild>
<Button rounded={"md"} px={4} py={2} size={"xs"} bg={"#02A0A0"}>
<Button
rounded={"md"}
px={4} py={2}
size={"xs"}
bg={"#02A0A0"}
onClick={handleOpenModal}>
<IoMdAdd /> Add
</Button>
</DialogTrigger>
@@ -138,7 +190,7 @@ function AddModel({ refetch }: { refetch: VoidFunction }) {
onChange={(e) => setLastName(e.target.value)}
/>
<Field.Label color="black" pt={1} fontSize="12px">
{/* <Field.Label color="black" pt={1} fontSize="12px">
Username
</Field.Label>
<Input
@@ -151,7 +203,7 @@ function AddModel({ refetch }: { refetch: VoidFunction }) {
height="30px"
value={userName}
onChange={(e) => setUserName(e.target.value)}
/>
/> */}
<Field.Label color="black" pt={1} fontSize="12px">
DOB
@@ -184,30 +236,63 @@ function AddModel({ refetch }: { refetch: VoidFunction }) {
onChange={(e) => setGender(e.target.value)}
/>
<Field.Label color="black" pt={1} fontSize="12px">
Email Address
</Field.Label>
<Input
placeholder="Enter Email address"
bgColor="#EEEEEE"
color="black"
border="none"
pl={1}
type="email"
fontSize="12px"
height="30px"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<Field.Label color="black" pt={1} fontSize="12px">
Phone Number
</Field.Label>
<Input
placeholder="Enter phone number"
bgColor="#EEEEEE"
color="black"
border="none"
type="tel"
pl={1}
fontSize="12px"
height="30px"
value={phonenumber}
onChange={(e) => {
const value = e.target.value;
if (/^\d*$/.test(value)) { // Only allow digits
setPhonenumber(value);
}
}}
/>
<Heading mt={5} color={"#000"} fontSize={"sm"}>
Permissions
</Heading>
</Field.Root>
<Grid templateColumns="repeat(2, 1fr)" gap={4}>
{[
"Dashboard",
"Manage contact us",
"manage User",
"Manage CMS",
"Manage Post",
"Manage Reports",
"manage Sub-Admin",
"My profile",
"Manage Jobs",
"manage feedbacks",
"Manage community",
"Notification",
].map((permission) => (
<Checkbox size="sm" color="black" key={permission}>
<Text fontSize={12}>{permission}</Text>
</Checkbox>
))}
{Array.isArray(allPermissions?.data?.permission)
? allPermissions.data.permission.map((permission: any) => (
<Checkbox
size="sm"
color="black"
key={permission.id}
checked={permissions.includes(permission.id)}
onChange={() => handleCheckboxToggle(permission.id)}
>
<Text fontSize={12}>{permission.app_resource_title}</Text>
</Checkbox>
))
: <Text fontSize={12} color="gray.500">Loading permissions...</Text>
}
</Grid>
</Stack>
</DialogBody>
@@ -219,13 +304,14 @@ function AddModel({ refetch }: { refetch: VoidFunction }) {
bg="#02A0A0"
color={"#fff"}
onClick={handleSubmit}
disabled={isLoading}
>
Save
</Button>
</DialogFooter>
<DialogCloseTrigger color="black" />
<DialogCloseTrigger color="black" onClick={() => setIsOpen(false)} />
</DialogContent>
<Toaster />
</DialogRoot>
);
}

View File

@@ -1,7 +1,7 @@
import { Field, Grid, Heading, Input, Stack, Text } from "@chakra-ui/react";
// import { TbEdit } from "react-icons/tb";
import { Button } from "./ui/button";
import { Checkbox } from "./ui/checkbox";
import { Button } from "../../components/ui/button";
import { Checkbox } from "../../components/ui/checkbox";
import {
DialogBody,
DialogCloseTrigger,
@@ -11,25 +11,23 @@ import {
DialogRoot,
DialogTitle,
DialogTrigger,
} from "./ui/dialog";
import Edit from "./ActionIcons/Edit";
import { useLazyViewSubAdminQuery, useUpdateSubAdminMutation } from "../Redux/Service/manage.subadmin.service";
} from "../../components/ui/dialog";
import Edit from "../../components/ActionIcons/Edit";
import { PermissionResponse, useLazyViewSubAdminQuery, useUpdateSubAdminMutation } from "../../Redux/Service/manage.subadmin.service";
import { useEffect, useState } from "react";
import { toaster } from "./ui/toaster";
import { Toaster, toaster } from "../../components/ui/toaster";
const resourceIdToLabel: { [key: number]: string } = {
1: 'Dashboard',
2: 'Manage contact us',
3: 'Manage User',
4: 'Manage CMS',
5: 'Manage Post',
6: 'Manage Reports',
7: 'Manage Sub-Admin',
8: 'My profile',
9: 'Manage Jobs',
10: 'Manage feedbacks',
11: 'Manage community',
12: 'Notification',
2: 'Manage User',
3: 'Manage Post',
4: 'Manage Subadmin',
5: 'Manage Jobs',
6: 'Manage Groups',
7: 'Manage Contact Us',
8: 'Manage CMS',
9: 'My Profile',
10: 'Master Module',
};
@@ -37,11 +35,15 @@ interface ResourceActionLink {
id: number;
app_resource_xid: number;
is_active: boolean;
app_resource: {
id: number,
app_resource_title: string
}
}
function EditSubAdmin({ id, refetch }: { id: number, refetch: VoidFunction }) {
function EditSubAdmin({ id, refetch, allPermissions }: { id: number, refetch: VoidFunction, allPermissions?: PermissionResponse }) {
const [trigger, { data }] = useLazyViewSubAdminQuery();
const [updateSubAdmin] = useUpdateSubAdminMutation()
const [updateSubAdmin, {isLoading}] = useUpdateSubAdminMutation()
const [isOpen, setIsOpen] = useState(false);
const [editData, setEditData] = useState<{
id: string;
@@ -62,8 +64,23 @@ function EditSubAdmin({ id, refetch }: { id: number, refetch: VoidFunction }) {
})
useEffect(() => {
if (data?.data && data.data.length > 0) {
const subAdmin = data.data[0]; // Extract the first item from the array
if (data?.data?.length && allPermissions?.data?.permission?.length) {
const subAdmin = data.data[0];
const activePermissionIds = subAdmin.get_resource_action_link
.filter((perm: any) => perm.is_active)
.map((perm: any) => perm.app_resource_xid);
const mergedPermissions: ResourceActionLink[] = allPermissions.data.permission.map((perm) => ({
id: perm.id,
app_resource_xid: perm.id,
is_active: activePermissionIds.includes(perm.id),
app_resource: {
id: perm.id,
app_resource_title: perm.app_resource_title,
},
}));
// Map the API response to editData
setEditData({
@@ -73,10 +90,10 @@ function EditSubAdmin({ id, refetch }: { id: number, refetch: VoidFunction }) {
last_name: subAdmin.last_name,
date_of_birth: formatDateOfBirth(subAdmin.date_of_birth),
gender: subAdmin.gender,
permission: subAdmin.get_resource_action_link,
permission: mergedPermissions,
});
}
}, [data]);
}, [data, allPermissions]);
const formatDateOfBirth = (dob: string): string => {
// Convert the date to the desired format with slashes
@@ -99,7 +116,7 @@ function EditSubAdmin({ id, refetch }: { id: number, refetch: VoidFunction }) {
setEditData((prevData) => ({
...prevData,
permission: prevData.permission.map((permission) =>
permission.id === permissionId
permission.app_resource_xid === permissionId
? { ...permission, is_active: !permission.is_active }
: permission
),
@@ -113,12 +130,14 @@ function EditSubAdmin({ id, refetch }: { id: number, refetch: VoidFunction }) {
const payload = {
id: Number(editData.id),
unique_id: editData.unique_id,
// unique_id: editData.unique_id,
first_name: editData.first_name,
last_name: editData.last_name,
date_of_birth: editData.date_of_birth,
gender: editData.gender,
permission: editData.permission
.filter((p) => p.is_active)
.map((p) => p.app_resource_xid),
};
try {
@@ -171,7 +190,7 @@ function EditSubAdmin({ id, refetch }: { id: number, refetch: VoidFunction }) {
<DialogBody bg="white">
<Stack py={3}>
<Field.Root>
<Field.Label color="black" pt={1} fontSize="12px">
{/* <Field.Label color="black" pt={1} fontSize="12px">
ID
</Field.Label>
<Input
@@ -184,7 +203,7 @@ function EditSubAdmin({ id, refetch }: { id: number, refetch: VoidFunction }) {
height="30px"
value={editData.unique_id}
onChange={(e) => setEditData({ ...editData, unique_id: e.target.value })}
/>
/> */}
<Field.Label color="black" pt={1} fontSize="12px">
First Name
@@ -248,44 +267,6 @@ function EditSubAdmin({ id, refetch }: { id: number, refetch: VoidFunction }) {
Permissions
</Heading>
</Field.Root>
{/* <Grid templateColumns="repeat(2, 1fr)" gap={4}>
<Checkbox size={"sm"} color={"black"}>
<Text fontSize={12}>Dashboard</Text>
</Checkbox>
<Checkbox size={"sm"} color={"black"}>
<Text fontSize={12}>Manage contact us</Text>
</Checkbox>
<Checkbox size={"sm"} color={"black"}>
<Text fontSize={12}>manage User</Text>
</Checkbox>
<Checkbox size={"sm"} color={"black"}>
<Text fontSize={12}>Manage CMS</Text>
</Checkbox>
<Checkbox size={"sm"} color={"black"}>
<Text fontSize={12}>Manage Post</Text>
</Checkbox>
<Checkbox size={"sm"} color={"black"}>
<Text fontSize={12}>Manage Reports</Text>
</Checkbox>
<Checkbox size={"sm"} color={"black"}>
<Text fontSize={12}>manage Sub-Admin</Text>
</Checkbox>
<Checkbox size={"sm"} color={"black"}>
<Text fontSize={12}>My profile</Text>
</Checkbox>
<Checkbox size={"sm"} color={"black"}>
<Text fontSize={12}>Manage Jobs</Text>{" "}
</Checkbox>
<Checkbox size={"sm"} color={"black"}>
<Text fontSize={12}> manage feedbacks</Text>
</Checkbox>
<Checkbox size={"sm"} color={"black"}>
<Text fontSize={12}>Manage community</Text>{" "}
</Checkbox>
<Checkbox size={"sm"} color={"black"}>
<Text fontSize={12}> Notification</Text>
</Checkbox>
</Grid> */}
<Grid templateColumns="repeat(2, 1fr)" gap={4}>
{editData.permission.map((permission) => {
const label = resourceIdToLabel[permission.app_resource_xid];
@@ -295,7 +276,7 @@ function EditSubAdmin({ id, refetch }: { id: number, refetch: VoidFunction }) {
size="sm"
color="black"
checked={permission.is_active}
onChange={() => handleCheckboxToggle(permission.id)}
onChange={() => handleCheckboxToggle(permission.app_resource_xid)}
cursor={'pointer'}
>
<Text fontSize={12}>{label}</Text>
@@ -306,12 +287,13 @@ function EditSubAdmin({ id, refetch }: { id: number, refetch: VoidFunction }) {
</Stack>
</DialogBody>
<DialogFooter display="flex" justifyContent="center" pt={"2"}>
<Button size={"xs"} w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit}>
<Button size={"xs"} w="100%" bg="#02A0A0" color={"#fff"} onClick={handleSubmit} disabled={isLoading}>
Save
</Button>
</DialogFooter>
<DialogCloseTrigger color="black" onClick={() => setIsOpen(false)} />
</DialogContent>
<Toaster />
</DialogRoot>
);
}

View File

@@ -1,24 +1,26 @@
import { Box, HStack, Image, Input, Text } from "@chakra-ui/react"
import { Box, HStack, Image, Text } from "@chakra-ui/react"
import MainFrame from "../../components/MainFrame"
import { InputGroup } from "../../components/ui/input-group"
import { LuSearch } from "react-icons/lu"
// import { InputGroup } from "../../components/ui/input-group"
// import { LuSearch } from "react-icons/lu"
import DataTable from "../../components/DataTable"
import AlertDailog from "../../components/AlertDailog";
// import { RiDeleteBin5Line } from "react-icons/ri";
import AddModel from "./AddModel"
import EditSubAdmin from "../../components/EditSubAdmin"
import EditSubAdmin from "./EditSubAdmin"
import ViewSubAdmin from "./ViewSubAdmin"
import Delete from "../../components/ActionIcons/Delete"
import { useDeleteSubAdminPostMutation, useGetSubAdminQuery } from "../../Redux/Service/manage.subadmin.service"
import { PermissionResponse, useDeleteSubAdminPostMutation, useGetPermissionQuery, useGetSubAdminQuery } from "../../Redux/Service/manage.subadmin.service"
import { useEffect, useState } from "react"
import { toaster } from "../../components/ui/toaster"
import { Toaster, toaster } from "../../components/ui/toaster"
import { useDebounce } from "../../components/Hooks/useDebounce"
import SearchComponent from "../../components/SearchComponent"
// table data
const tableHeadRow = [
"Sr. No",
"Id",
"Emp ID",
"First Name",
"last Name",
"DOB",
@@ -55,37 +57,52 @@ const tableHeadRow = [
// ];
const SubAdmin = () => {
const { data, refetch } = useGetSubAdminQuery()
const [currentPage, setCurrentPage] = useState(1);
// const { data, refetch } = useGetSubAdminQuery()
const { data: permissions } = useGetPermissionQuery()
const [localData, setLocalData] = useState<any[]>([]);
const [allPermissions, setAllPermissions] = useState<PermissionResponse>();
const [deleteModal, setDeleteModal] = useState(false)
const [deleteSubAdminPost] = useDeleteSubAdminPostMutation()
console.log("============================",data);
const [searchTerm, setSearchTerm] = useState("");
const debouncedSearchTerm = useDebounce(searchTerm, 500);
const queryArgs = debouncedSearchTerm ? { page: currentPage, search: debouncedSearchTerm } : { page: currentPage };
const { data, refetch, isError, isFetching } = useGetSubAdminQuery(queryArgs);
useEffect(() => {
if (data?.data.data) {
setLocalData(data?.data.data);
setAllPermissions(permissions);
}
}, [data]);
}, [data, permissions]);
console.log("============================", allPermissions);
console.log('localData', localData);
const handleDeleteFaq = async (faqId: number) => {
const handlePageChange = (page: number) => {
setCurrentPage(page);
};
const handleSearchChange = (value: string) => {
setSearchTerm(value);
setCurrentPage(1);
};
const handleDeleteAdmin = async (faqId: number) => {
try {
const response = await deleteSubAdminPost(faqId).unwrap();
if (response.success) {
toaster.create({
title: "Success",
description: "FAQ deleted successfully",
description: "Sub Admin deleted successfully",
type: "success",
});
refetch()
console.log("FAQ deleted successfully:", response);
console.log("Sub Admin deleted successfully:", response);
}
// Optionally, refetch data or update state after deletion
} catch (error) {
console.error("Error deleting FAQ:", error);
console.error("Error deleting Sub Admin:", error);
toaster.create({
title: "Error",
description: "Something went wrong",
@@ -102,10 +119,18 @@ const SubAdmin = () => {
});
};
const managepost = localData?.map((agency: any, index: number) => ({
const filteredData = localData?.filter((agency) => {
const searchLower = searchTerm.toLowerCase();
const userName = agency.user_name?.toLowerCase().includes(searchLower);
const firstName = agency.first_name?.toLowerCase().includes(searchLower);
const lastName = agency.last_name?.toLowerCase().includes(searchLower);
return userName || firstName || lastName;
});
const managepost = filteredData?.map((agency: any, index: number) => ({
'id': agency.id,
"Sr. No": index + 1,
"Id": agency.unique_id,
"Emp ID": agency.unique_id,
"First Name": agency.first_name,
"last Name": agency.last_name,
"DOB": formatDateOfBirth(agency.date_of_birth),
@@ -114,20 +139,20 @@ const SubAdmin = () => {
"Action": (
<HStack justifyContent="center">
{/* <EditDetails rowData={{ id: agency.id, question: agency.question, answer: agency.answer }} refetch={refetch} /> */}
<ViewSubAdmin id={agency.id}/>
<EditSubAdmin id={agency.id} refetch={refetch} />
<ViewSubAdmin id={agency.id} />
<EditSubAdmin id={agency.id} refetch={refetch} allPermissions={permissions} />
<AlertDailog
isOpen={deleteModal}
AltertTiggerIcon={() => <Delete onClick={() => setDeleteModal(prev => !prev)} />}
alertText="Delete FAQ"
alertText="Delete sub admin"
alertIcon={<Image src={"DeleteIcon"} h={"39px"} />}
alertCaption="are you sure you want to delete ?"
onClose={() => setDeleteModal(false)}
onConfirm={() => {
// console.log("User deleted:", index + 1);
setDeleteModal(false);
handleDeleteFaq(agency.id)
handleDeleteAdmin(agency.id)
}}
/>
</HStack>
@@ -150,38 +175,31 @@ const SubAdmin = () => {
</Text>
<HStack>
<InputGroup
startElement={
<LuSearch fontSize={"xs"} style={{ position: 'relative', left: '10px' }} />
}
color={"#000"}
>
<Input
p={3}
w={300}
bg={"#fff"}
colorPalette={"cyan"}
_focus={{ border: "1px solid #02A0A0" }}
rounded={"md"}
size={"xs"}
fontSize={"sm"}
placeholder="Search..."
bgColor={'#EEEEEE'}
ps={8}
/>
</InputGroup>
<SearchComponent
value={searchTerm}
onChange={handleSearchChange}
/>
{/* <Button bgColor={'#EEEEEE'} pl={3} pr={3}><IoMdAdd /> <Text>Add</Text></Button> */}
<AddModel />
<AddModel refetch={refetch} allPermissions={permissions} />
</HStack>
</HStack>
<DataTable
sortableColumns={["Name", "Registration Date "]}
tableHeadRow={tableHeadRow}
data={managepost}
// paginationData={data?.data}
// refetch={refetch}
paginationData={{
current_page: data?.data.current_page || 1,
last_page: data?.data.last_page || 1,
per_page: data?.data.per_page || 10,
total: data?.data.total || 0
}}
onPageChange={handlePageChange}
isLoading={isFetching}
isError={isError}
/>
</Box> </MainFrame>
</Box>
<Toaster />
</MainFrame>
)
}
export default SubAdmin

View File

@@ -22,9 +22,9 @@ import { Checkbox } from "../../components/ui/checkbox";
// import { FaRegEdit } from "react-icons/fa";
import View from "../../components/ActionIcons/View";
import { Button } from "../../components/ui/button";
import { useLazyViewSubAdminQuery } from "../../Redux/Service/manage.subadmin.service";
import {useLazyViewSubAdminQuery } from "../../Redux/Service/manage.subadmin.service";
function ViewSubAdmin({ id }: { id: number }) {
function ViewSubAdmin({ id, }: { id: number}) {
const [trigger, { data }] = useLazyViewSubAdminQuery();
const handleView = () => {
@@ -64,6 +64,7 @@ function ViewSubAdmin({ id }: { id: number }) {
overflowX="hidden"
p={3} // Reduced padding
bgSize={"md"}
key={data.id}
>
<DialogHeader bg="white">
<DialogTitle alignSelf="center" color="black" fontSize="14px">
@@ -149,8 +150,8 @@ function ViewSubAdmin({ id }: { id: number }) {
</Field.Root>
<Grid templateColumns="repeat(2, 1fr)" gap={4}>
{data.get_resource_action_link.map((check: any) => (
<Checkbox size={"sm"} color={"black"} checked={check.is_active}>
<Text fontSize={12}>{check.app_resource_xid}</Text>
<Checkbox size={"sm"} color={"black"} checked={check.is_active} key={check.id}>
<Text fontSize={12}>{check?.app_resource.app_resource_title}</Text>
</Checkbox>
// <>
// <Checkbox size={"sm"} color={"black"}>

View File

@@ -70,8 +70,18 @@ export const agencyMasterModule = createApi({
}),
}),
getAgencyMaster: builder.query<AgencyResponse, number>({
query: (page = 1) => `/agency-master?page=${page}`
// getAgencyMaster: builder.query<AgencyResponse, number>({
// query: (page = 1) => `/agency-master?page=${page}`
// }),
getAgencyMaster: builder.query<AgencyResponse, { page?: number; search?: string }>({
query: ({ page, search }) => {
const params = new URLSearchParams();
if (page) params.append("page", page.toString());
if (search) params.append("search", search);
return `/agency-master?${params.toString()}`;
},
}),
agencyMasterToggle: builder.mutation({

View File

@@ -65,8 +65,18 @@ export const countryMaster = createApi({
}),
}),
// 🔹 GET: Fetch all posts
getCountryMaster: builder.query<ApiResponse, number>({
query: (page = 1) => `/country-list?page=${page}`,
// getCountryMaster: builder.query<ApiResponse, number>({
// query: (page = 1) => `/country-list?page=${page}`,
// }),
getCountryMaster: builder.query<ApiResponse, { page?: number; search?: string }>({
query: ({ page, search }) => {
const params = new URLSearchParams();
if (page) params.append("page", page.toString());
if (search) params.append("search", search);
return `/country-list?${params.toString()}`;
},
}),
getCountryMasterEdit: builder.query<CountryEdit, number>({

View File

@@ -0,0 +1,77 @@
import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQueryWithReauth } from "./apiSlice";
export type TotalUser = {
data: {
totalRecruiterCount: number;
totalCustomerCount: string;
totalUserCount: string;
recruitersByMonth: Record<string, number>;
customersByMonth: Record<string, number>;
};
};
export type PastUser = {
data: {
past24hourRecruiterCount: number;
past24hourCustomercount: number;
};
};
export type NewUser = {
data: {
newRecuiterCount: number;
newCustomerCount: number;
newTotalUserCount: number;
};
};
export type AgencyList = {
data: [
{
id: number;
name: string;
created_at: string;
is_active: boolean
}
];
};
export type FaqList = {
data: [
{
id: number;
faqs_xid: number;
language_master_xid: number;
question: string;
answer: string;
is_active: boolean
}
];
};
export const dashBoard = createApi({
reducerPath: "dashBoard",
baseQuery: baseQueryWithReauth, // Use enhanced baseQuery with error handling
endpoints: (builder) => ({
getTotalUser: builder.query<TotalUser, void>({ query: () => "/dashboard-total-user" }),
getPastUser: builder.query<PastUser, void>({ query: () => "/dashboard-past-user" }),
getNewUser: builder.query<NewUser, void>({ query: () => "/dashboard-new-user" }),
getAgencyList: builder.query<AgencyList, void>({ query: () => "/dashboard-agency-list" }),
getFaqList: builder.query<FaqList, void>({ query: () => "/dashboard-faq-list" }),
}),
});
export const {
useGetTotalUserQuery,
useGetPastUserQuery,
useGetNewUserQuery,
useGetAgencyListQuery,
useGetFaqListQuery,
} = dashBoard;

View File

@@ -64,8 +64,18 @@ export const departmentMaster = createApi({
}),
}),
// 🔹 GET: Fetch all posts
getDepartmentMaster: builder.query<ApiResponse, number>({
query: (page = 1) => `/department-master-list?page=${page}`,
// getDepartmentMaster: builder.query<ApiResponse, number>({
// query: (page = 1) => `/department-master-list?page=${page}`,
// }),
getDepartmentMaster: builder.query<ApiResponse, { page?: number; search?: string }>({
query: ({ page, search }) => {
const params = new URLSearchParams();
if (page) params.append("page", page.toString());
if (search) params.append("search", search);
return `/department-master-list?${params.toString()}`;
},
}),
getDepartmentMasterDropDown: builder.query<DropDown, void>({

View File

@@ -61,8 +61,18 @@ export const industryMaster = createApi({
}),
}),
// 🔹 GET: Fetch all posts
getIndustryMaster: builder.query<IndustryMasterResponse, number>({
query: (page = 1) => `/industry-master-list?page=${page}`,
// getIndustryMaster: builder.query<IndustryMasterResponse, number>({
// query: (page = 1) => `/industry-master-list?page=${page}`,
// }),
getIndustryMaster: builder.query<IndustryMasterResponse, { page?: number; search?: string }>({
query: ({ page, search }) => {
const params = new URLSearchParams();
if (page) params.append("page", page.toString());
if (search) params.append("search", search);
return `/industry-master-list?${params.toString()}`;
},
}),
updateIndustryMaster: builder.mutation({

View File

@@ -51,8 +51,18 @@ export const jobStatus = createApi({
}),
}),
// 🔹 GET: Fetch all posts
getJobStatus: builder.query<ApiResponse, number>({
query: (page = 1) => `/job-status-list?page=${page}`,
// getJobStatus: builder.query<ApiResponse, number>({
// query: (page = 1) => `/job-status-list?page=${page}`,
// }),
getJobStatus: builder.query<ApiResponse, { page?: number; search?: string }>({
query: ({ page, search }) => {
const params = new URLSearchParams();
if (page) params.append("page", page.toString());
if (search) params.append("search", search);
return `/job-status-list?${params.toString()}`;
},
}),
updateJobStatus: builder.mutation({

View File

@@ -71,9 +71,9 @@ export const jobType = createApi({
}),
}),
templateMasterToggle: builder.mutation({
jobTypeToggle: builder.mutation({
query: ({ id, is_active }) => ({
url: `/template-status`,
url: `/job-type-status`,
method: "POST",
body: { id, is_active },
}),
@@ -86,5 +86,5 @@ export const {
useGetJobTypeQuery,
useCreateJobTypePostMutation,
useUpdateJobTypeMutation,
useTemplateMasterToggleMutation,
useJobTypeToggleMutation,
} = jobType;

View File

@@ -20,14 +20,26 @@ export const manageContactUs = createApi({
tagTypes: ["Contact"],
endpoints: (builder) => ({
getContact: builder.query<ApiResponse, void>({
query: () => "/contact-us",
getContact: builder.query<ApiResponse, { page?: number; search?: string }>({
query: ({ page, search }) => {
const params = new URLSearchParams();
if (page) params.append("page", page.toString());
if (search) params.append("search", search);
return `/contact-us?${params.toString()}`;
},
providesTags: ["Contact"],
}),
pendingRequest: builder.mutation({
query: (body) => ({
url: `/contact-us-response`,
method: "POST",
body,
}),
invalidatesTags: ["Contact"],
}),
}),
});
export const { useGetContactQuery } = manageContactUs;
export const { useGetContactQuery, usePendingRequestMutation } = manageContactUs;

View File

@@ -66,17 +66,40 @@ export type PostJobStatus = {
title: string
};
export type WorkSpace = {
id: number;
en_name: string;
};
export const manageJobs = createApi({
reducerPath: "manageJobs",
baseQuery: baseQueryWithReauth, // Use enhanced baseQuery with error handling
endpoints: (builder) => ({
getManageJobs: builder.query<ApiResponse, number>({
query: (page = 1) => `/manage-jobs-list?page=${page}`,
// getManageJobs: builder.query<ApiResponse, number>({
// query: (page = 1) => `/manage-jobs-list?page=${page}`,
// }),
getManageJobs: builder.query<ApiResponse, { page?: number; search?: string }>({
query: ({ page, search }) => {
const params = new URLSearchParams();
if (page) params.append("page", page.toString());
if (search) params.append("search", search);
return `/manage-jobs-list?${params.toString()}`;
},
}),
updateJobs: builder.mutation({
query: (updatedData) => ({
url: "/manage-jobs-update",
method: "POST",
body: updatedData,
}),
}),
viewJobs: builder.query<ApiResponse, number>({
query: (id) => `/manage-jobs-list/${id}`,
query: () => `/manage-jobs-list`,
}),
deleteJobsPost: builder.mutation<{ status: string; message: string }, { id: number }>({
@@ -87,10 +110,38 @@ export const manageJobs = createApi({
}),
}),
// Modes
getWorkspaceModes: builder.query({
query: () => `/manage-jobs-get-workspace`,
}),
getIndustry: builder.query({
query: () => `/manage-jobs-get-industry`,
}),
getDepartment: builder.query({
query: () => `/manage-jobs-get-department`,
}),
getCountry: builder.query({
query: () => `/manage-jobs-get-country`,
}),
getManageJobType: builder.query({
query: () => `/job-type`,
}),
}),
});
export const { useGetManageJobsQuery,useLazyViewJobsQuery,useDeleteJobsPostMutation } = manageJobs;
export const {
useGetManageJobsQuery,
useLazyViewJobsQuery,
useDeleteJobsPostMutation,
useUpdateJobsMutation,
useGetWorkspaceModesQuery,
useGetIndustryQuery,
useGetDepartmentQuery,
useGetCountryQuery,
useGetManageJobTypeQuery
} = manageJobs;

View File

@@ -40,6 +40,25 @@ interface ApiResponse {
data: PaginatedData;
}
export type Permission = {
id: number;
app_resource_title: string;
is_active: boolean;
created_by: string | null;
modified_by: string | null;
deleted_at: string | null;
created_at: string;
updated_at: string;
};
export type PermissionResponse = {
status: string;
status_code: number;
message: string;
data: {
permission: Permission[];
};
};
// export type SubAdminPost = {
// id: number;
// first_name: string,
@@ -60,6 +79,10 @@ interface ResourceActionLink {
deleted_at: string | null;
created_at: string;
updated_at: string;
app_resource: {
id: number;
app_resource_title: string
}
}
interface SubAdmin {
@@ -80,16 +103,14 @@ interface SubAdminView {
}
interface CreateSubAdminPayload {
principal_type_xid: number;
principal_source_xid: number;
user_name: string;
// user_name: string;
first_name: string;
last_name: string;
date_of_birth: string;
gender: string;
email_address: string;
phone_number: string;
created_by: number;
// created_by: number;
}
interface CreateSubAdminResponse {
@@ -112,9 +133,18 @@ export const manageSubAdmin = createApi({
body: data,
}),
}),
getSubAdmin: builder.query<ApiResponse, void>({
query: () => `/sub-admin`,
getSubAdmin: builder.query<ApiResponse, { page?: number; search?: string }>({
query: ({ page, search }) => {
const params = new URLSearchParams();
if (page) params.append("page", page.toString());
if (search) params.append("search", search);
return `/sub-admin?${params.toString()}`
}
}),
getPermission: builder.query<PermissionResponse, void>({
query: () => `/resources`,
}),
viewSubAdmin: builder.query<SubAdminView, number>({
@@ -151,8 +181,9 @@ export const manageSubAdmin = createApi({
deleteSubAdminPost: builder.mutation<{ success: boolean }, number>({
query: (id) => ({
url: `/faq-delete/${id}`,
method: "DELETE",
url: `/sub-admin-delete`,
method: "POST",
body: { id },
}),
}),
}),
@@ -160,6 +191,7 @@ export const manageSubAdmin = createApi({
export const {
useGetSubAdminQuery,
useGetPermissionQuery,
useLazyViewSubAdminQuery,
useUpdateSubAdminMutation,
useDeleteSubAdminPostMutation,

View File

@@ -10,11 +10,19 @@ export interface UserData {
gender: string;
date_of_birth: string;
is_active: boolean;
principal_type:{
principal_type: {
id: number;
principal_type_title: string;
},
principle_language_links:[]
principle_language_linkss: {
id: number;
language_xid: number;
iam_principal_xid: number;
language: {
id: number,
language_name: string;
}
}
}
interface ApiResponse {
@@ -55,9 +63,9 @@ export const registerUser = createApi({
reducerPath: "registerUser",
baseQuery: baseQueryWithReauth, // Use enhanced baseQuery with error handling
endpoints: (builder) => ({
createCountryPost: builder.mutation<PostCountry, Partial<PostCountry>>({
createUser: builder.mutation<PostCountry, Partial<PostCountry>>({
query: (data) => ({
url: "/country-add",
url: "/manage-user-store",
method: "POST",
body: data,
}),
@@ -67,13 +75,13 @@ export const registerUser = createApi({
query: (page = 1) => `/manage-user-list?page=${page}`,
}),
getCountryMasterEdit: builder.query<CountryEdit, number>({
query: (id) => `/country-edit/${id}`,
getDeactivateUser: builder.query<ApiResponse, number>({
query: (page = 1) => `/manage-user-deactivate-list?page=${page}`,
}),
updateCountry: builder.mutation({
updateUser: builder.mutation({
query: (updatedData) => ({
url: "/country-update",
url: "/manage-user-update",
method: "POST",
body: updatedData,
}),
@@ -87,22 +95,31 @@ export const registerUser = createApi({
}),
}),
// deleteFaqPost: builder.mutation<{ status: string; message: string }, { id: number }>({
// query: ({ id }) => ({
// url: `/faq-delete`,
// method: "POST",
// body: { id },
// }),
// }),
userDeactivateToggle: builder.mutation({
query: ({ id, is_active }) => ({
url: `/manage-user-deactivate-status`,
method: "POST",
body: { id, is_active },
}),
}),
deleteUser: builder.mutation({
query: ({ id }) => ({
url: `/manage-user-delete`,
method: "POST",
body: { id },
}),
}),
}),
});
export const {
useGetManageUserQuery,
useGetCountryMasterEditQuery,
useCreateCountryPostMutation,
useUpdateCountryMutation,
useCreateUserMutation,
useUpdateUserMutation,
useUserToggleMutation,
// useDeleteFaqPostMutation
useGetDeactivateUserQuery,
useUserDeactivateToggleMutation,
useDeleteUserMutation
} = registerUser;

View File

@@ -1,26 +1,37 @@
import { createApi } from "@reduxjs/toolkit/query";
import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQueryWithReauth } from "./apiSlice";
export const myProfile = createApi({
reducerPath: "myProfile",
baseQuery: baseQueryWithReauth, // Use enhanced baseQuery with error handling
endpoints: (builder) => ({
getPosts: builder.query<Post[], void>({ query: () => "/posts" }),
}),
});
export const { } = myProfile;
export type Post = {
id: number;
title: string;
body: string;
};
reducerPath: "myProfile",
baseQuery: baseQueryWithReauth, // Use enhanced baseQuery with error handling
endpoints: (builder) => ({
// getPosts: builder.query<Post[], void>({ query: () => "/profile-image-edit" }),
updateImage: builder.mutation({
query: (formData: FormData) => {
const token = localStorage.getItem("token");
return {
url: "/profile-image-edit",
method: "POST",
body: formData,
headers: {
"access-token": `${token}`,
},
};
},
}),
}),
});
export const {
useUpdateImageMutation,
} = myProfile;

View File

@@ -27,6 +27,7 @@ interface PaginationData {
export interface Template {
id: number;
is_active: boolean;
principle_type_xid: number;
post_template_translate: PostTemplateTranslate[];
post_template_image: PostTemplateImage[];
}

View File

@@ -50,7 +50,7 @@ export const workspaceMode = createApi({
endpoints: (builder) => ({
createWorkspacePost: builder.mutation<PostDepartment, Partial<PostDepartment>>({
query: (data) => ({
url: "/department-master-store",
url: "/workspace-create",
method: "POST",
body: data,
}),

View File

@@ -23,6 +23,7 @@ import { workspaceMode } from "./Service/workspace.mode";
import { jobStatus } from "./Service/job.status";
import { managePosts } from "./Service/manage.post.service";
import { registerUser } from "./Service/manage.user";
import { dashBoard } from "./Service/dashBoard";
export const store = configureStore({
reducer: {
@@ -49,6 +50,7 @@ export const store = configureStore({
[jobStatus.reducerPath]: jobStatus.reducer,
[managePosts.reducerPath]: managePosts.reducer,
[registerUser.reducerPath]: registerUser.reducer,
[dashBoard.reducerPath]: dashBoard.reducer,
auth: authReducer,
},
middleware: (getDefaultMiddleware) =>
@@ -76,6 +78,7 @@ export const store = configureStore({
jobStatus.middleware,
managePosts.middleware,
registerUser.middleware,
dashBoard.middleware,
),
});

View File

@@ -1,5 +1,5 @@
import { LuBriefcaseBusiness} from "react-icons/lu";
import { MdHeadsetMic, MdOutlineDashboard} from "react-icons/md";
import { LuBriefcaseBusiness } from "react-icons/lu";
import { MdHeadsetMic, MdOutlineDashboard } from "react-icons/md";
import { GoDotFill } from "react-icons/go";
import { HiOutlinePencilSquare } from "react-icons/hi2";
import { BiUser, BiUserPin } from "react-icons/bi";
@@ -8,146 +8,156 @@ import { BsBoxes, BsPersonBadge } from "react-icons/bs";
import { AiOutlineFileText } from "react-icons/ai";
export const nav = [
{
title: "Dashboard",
path: "/",
Icon: MdOutlineDashboard,
type:'single'
},
{
title: "Manage Users",
initPath: "/manage-users",
Icon: BiUserPin,
type:'multiple',
children: [
{
title: "Register Users",
path: "/manage-users/register-users",
Icon: GoDotFill,
},
{
title: "Deactivated Accounts",
path: "/manage-users/deactivated-accounts",
Icon: GoDotFill,
},
],
},
{
title: "Manage Post",
path: "/manage-post",
Icon: HiOutlinePencilSquare,
type:'single'
},
{
title: "Manage Sub-Admin",
path: "/sub-admin",
Icon: BiUser,
type:'single'
},
{
title: "Manage Jobs",
path: "/manage-jobs",
Icon: LuBriefcaseBusiness,
type:'single'
},
{
title: "Manage Groups",
path: "/manage-groups",
Icon: PiUsersThree,
type:'single'
},
{
title: "Manage Contact Us",
path: "/manage-contact",
Icon: MdHeadsetMic ,
type:'single'
},
{
title: "Manage CMS",
initPath: "/manage-cms",
Icon: AiOutlineFileText,
type:'multiple',
children: [
{
title: "FAQs",
path: "/manage-cms/faq",
Icon: GoDotFill,
},
{
title: "About Us",
path: "/manage-cms/about-us",
Icon: GoDotFill,
},
{
title: "Privacy Policy",
path: "/manage-cms/privacy-policy",
Icon: GoDotFill,
},
{
title: "Terms And Conditions",
path: "/manage-cms/terms-conditions",
Icon: GoDotFill,
},
// {
// title: "Privacy",
// path: "/manage-cms/privacy",
// Icon: GoDotFill,
// },
],
},
{
title: "My Profile",
path: "/profile",
Icon: BsPersonBadge,
type:'single'
},
{
title: "Master Module",
initPath: "/master-module",
Icon: BsBoxes,
type:'multiple',
children: [
{
title: "Agency Master",
path: "/master-module/agency-master",
Icon: GoDotFill,
},
{
title: "Template Master",
path: "/master-module/template-master",
Icon: GoDotFill,
},
{
title: "Job Type",
path: "/master-module/job-type",
Icon: GoDotFill,
},
{
title: "Workspace Mode",
path: "/master-module/workspace-mode",
Icon: GoDotFill,
},
{
title: "Country",
path: "/master-module/country",
Icon: GoDotFill,
},
{
title: "Job Status",
path: "/master-module/job-status",
Icon: GoDotFill,
},
{
title: "Industry Master",
path: "/master-module/industry-master",
Icon: GoDotFill,
},
{
title: "Department Master",
path: "/master-module/department-master",
Icon: GoDotFill,
},
],
},
];
{
title: "Dashboard",
path: "/",
Icon: MdOutlineDashboard,
type: 'single',
resourceTitle: 'Dashboard'
},
{
title: "Manage Users",
initPath: "/manage-users",
Icon: BiUserPin,
type: 'multiple',
resourceTitle: 'Manage User',
children: [
{
title: "Register Users",
path: "/manage-users/register-users",
Icon: GoDotFill,
},
{
title: "Deactivated Accounts",
path: "/manage-users/deactivated-accounts",
Icon: GoDotFill,
},
],
},
{
title: "Manage Post",
path: "/manage-post",
Icon: HiOutlinePencilSquare,
type: 'single',
resourceTitle: 'Manage Post'
},
{
title: "Manage Sub-Admin",
path: "/sub-admin",
Icon: BiUser,
type: 'single',
resourceTitle: 'Manage Subadmin'
},
{
title: "Manage Jobs",
path: "/manage-jobs",
Icon: LuBriefcaseBusiness,
type: 'single',
resourceTitle: 'Manage Jobs'
},
{
title: "Manage Groups",
path: "/manage-groups",
Icon: PiUsersThree,
type: 'single',
resourceTitle: 'Manage Groups'
},
{
title: "Manage Contact Us",
path: "/manage-contact",
Icon: MdHeadsetMic,
type: 'single',
resourceTitle: 'Manage Contact Us'
},
{
title: "Manage CMS",
initPath: "/manage-cms",
Icon: AiOutlineFileText,
type: 'multiple',
resourceTitle: 'Manage CMS',
children: [
{
title: "FAQs",
path: "/manage-cms/faq",
Icon: GoDotFill,
},
{
title: "About Us",
path: "/manage-cms/about-us",
Icon: GoDotFill,
},
{
title: "Privacy Policy",
path: "/manage-cms/privacy-policy",
Icon: GoDotFill,
},
{
title: "Terms And Conditions",
path: "/manage-cms/terms-conditions",
Icon: GoDotFill,
},
// {
// title: "Privacy",
// path: "/manage-cms/privacy",
// Icon: GoDotFill,
// },
],
},
{
title: "My Profile",
path: "/profile",
Icon: BsPersonBadge,
type: 'single',
resourceTitle: 'My Profile'
},
{
title: "Master Module",
initPath: "/master-module",
Icon: BsBoxes,
type: 'multiple',
resourceTitle: 'Master Module',
children: [
{
title: "Agency Master",
path: "/master-module/agency-master",
Icon: GoDotFill,
},
{
title: "Template Master",
path: "/master-module/template-master",
Icon: GoDotFill,
},
{
title: "Job Type",
path: "/master-module/job-type",
Icon: GoDotFill,
},
{
title: "Workspace Mode",
path: "/master-module/workspace-mode",
Icon: GoDotFill,
},
{
title: "Country",
path: "/master-module/country",
Icon: GoDotFill,
},
{
title: "Job Status",
path: "/master-module/job-status",
Icon: GoDotFill,
},
{
title: "Industry Master",
path: "/master-module/industry-master",
Icon: GoDotFill,
},
{
title: "Department Master",
path: "/master-module/department-master",
Icon: GoDotFill,
},
],
},
];

View File

@@ -11,19 +11,35 @@ import { FaUser } from "react-icons/fa";
// ✅ Register required components
ChartJS.register(ArcElement, Tooltip, Legend);
const SemiDoughnutChart = () => {
const SemiDoughnutChart = ({ totalUser }: { totalUser: any }) => {
const dataSource = totalUser ?? {}
const recruiterCount =
dataSource.past24hourRecruiterCount ??
dataSource.totalRecruiterCount ??
dataSource.newRecuiterCount ??
0;
const customerCount =
dataSource.past24hourCustomercount ??
dataSource.totalCustomerCount ??
dataSource.newCustomerCount ??
0;
// 📊 Chart Data
const data = {
labels: ["Recruiter", "Customer"],
datasets: [
{
data: [2554, 2800], // Values
data: [recruiterCount, customerCount], // Values
backgroundColor: ["#E0E0E0", "#3D5AFE"], // Grey and Blue
borderWidth: 0, // No border
cutout: "90%", // Makes it a doughnut shape
circumference: 270, // Semi-circle
rotation: 225, // Starts from the top
},
],
};
@@ -50,9 +66,9 @@ const SemiDoughnutChart = () => {
fontSize: "20px",
fontWeight: "bold",
color: "#3D5AFE",
backgroundColor:'#ECEAF8',
padding:'15px',
borderRadius:'50%'
backgroundColor: '#ECEAF8',
padding: '15px',
borderRadius: '50%'
}}
>
<FaUser />

View File

@@ -25,6 +25,8 @@ interface TableProps {
total: number;
};
onPageChange?: (page: number) => void;
isLoading?: boolean;
isError?: boolean;
}
const DataTable: React.FC<TableProps> = ({
@@ -33,6 +35,8 @@ const DataTable: React.FC<TableProps> = ({
sortableColumns = [],
paginationData,
onPageChange,
isLoading,
isError
}: TableProps) => {
const { current_page = 1, last_page = 1 } = paginationData || {};
const [sortConfig, setSortConfig] = useState<{
@@ -103,47 +107,65 @@ const DataTable: React.FC<TableProps> = ({
))}
</Table.Row>
</Table.Header>
{data?.length === 0 ? (
<Box>
<Box textAlign={"center"} py={20} position={'absolute'} w={'84%'}>
<Image src={EmptyFile} alt="No data" maxW="65px" mx="auto" />
<Text fontSize={"18px"} fontWeight={500} mt={2}>
We do not have any records
</Text>
</Box>
{isLoading ? (
<Box textAlign={"center"} py={20} position="absolute" w="84%">
<Text fontSize={"18px"} fontWeight={500} mt={2}>
Loading...
</Text>
</Box>
) : isError ? (
<Box textAlign={"center"} py={20} position="absolute" w="84%">
<Image src={EmptyFile} alt="No data" maxW="65px" mx="auto" />
<Text fontSize={"18px"} fontWeight={500} mt={2}>
No records found theyll appear here if available.
</Text>
</Box>
) : (
<Table.Body h={"100%"}>
<Table.Body h="100%">
{data.length === 0 && (
<Box textAlign={"center"} py={20} position="absolute" w="84%">
<Image src={EmptyFile} alt="No data" maxW="65px" mx="auto" />
<Text fontSize={"18px"} fontWeight={500} mt={2}>
No records found theyll appear here if available.
</Text>
</Box>
)}
{data.map((item: any, index) => (
<Table.Row
key={index}
bg={index % 2 === 0 ? "#fff" : "#007F3310"}
>
{tableHeadRow.map((heading, colIndex) => (
<Table.Cell
px={4}
p={2}
key={`${index}-${colIndex}`}
fontSize={"xs"}
fontWeight={500}
border={"none"}
>
{(() => {
const words =
item[heading]?.toString().split(" ") || [];
return words.length > 5
? `${words.slice(0, 5).join(" ")}...`
: item[heading];
})()}
</Table.Cell>
))}
{tableHeadRow.map((heading, colIndex) => {
const cellContent = item[heading];
const words = typeof cellContent === 'string'
? cellContent.split(" ")
: cellContent?.toString().split(" ") || [];
const truncated = words.length > 15
? `${words.slice(0, 15).join(" ")}...`
: cellContent;
return (
<Table.Cell
px={4}
p={2}
key={`${index}-${colIndex}`}
fontSize="xs"
fontWeight={500}
border="none"
>
{truncated}
</Table.Cell>
);
})}
</Table.Row>
))}
</Table.Body>
)}
</Table.Root>
</Table.ScrollArea>
{last_page > 1 && (
{last_page > 1 && !isLoading && !isError && (
<PaginationRoot
count={paginationData?.total ?? 0}
pageSize={paginationData?.per_page ?? 0}

View File

@@ -0,0 +1,16 @@
// hooks/useDebounce.ts
import { useEffect, useState } from "react";
export function useDebounce<T>(value: T, delay = 500): T {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebounced(value);
}, delay);
return () => clearTimeout(handler); // cleanup on unmount or value change
}, [value, delay]);
return debounced;
}

3
src/components/Utils.ts Normal file
View File

@@ -0,0 +1,3 @@
export const delay = (ms: number) => new Promise(res => setTimeout(res, ms));

View File

@@ -44,7 +44,7 @@ export const DialogCloseTrigger = React.forwardRef<
{...props}
asChild
>
<CloseButton size="sm" ref={ref}>
<CloseButton size="sm" ref={ref} _hover={{ color: "gray.200" }}>
{props.children}
</CloseButton>
</ChakraDialog.CloseTrigger>

View File

@@ -183,4 +183,23 @@ input:focus-visible {
.css-1ilznyv {
border: none;
}
.custom-checkbox {
width: 16px;
height: 16px;
border: 2px solid black;
background-color: white;
appearance: none;
cursor: pointer;
position: relative;
}
.custom-checkbox:checked::after {
content: "✔";
color: black;
font-size: 16px;
position: absolute;
top: -2px;
left: 0px;
}