Admin / Subadmin nav access and bugs fixing

This commit is contained in:
rockyeverlast
2025-09-12 17:49:14 +05:30
parent e5361a5d77
commit 8f19a41c57
19 changed files with 361 additions and 208 deletions

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

@@ -25,9 +25,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 +61,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}>

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, userAccess } = 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 => link.is_active)
.map(link => link.app_resource.app_resource_title) ?? [];
if (res.data) {
setIsAuthenticate(true);
console.log("====================================");
console.log(res.data?.data);
console.log("====================================");
setUserAccess(allowedTitles);
dispatch(setToken(String(loginData["access-token"])));
sessionStorage.setItem('userAccess', JSON.stringify(allowedTitles));
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);
setIsLoading(false);
if (error) {
} catch (error: any) {
console.log('error', error?.response?.data?.message);
toaster.create({
title: error?.response?.data?.message,
// title: "Something Went Wrong",
type: "info",
})
// console.log("Login failed", error?.response?.data?.message);
title: "Error",
description: error?.response?.data?.message || "Login failed",
type: "error",
});
setIsLoading(false);
}
}
});
// console.log("User Access in Context:", userAccess);
return (

View File

@@ -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

View File

@@ -175,10 +175,9 @@ const ManagePost = () => {
),
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),

View File

@@ -18,6 +18,7 @@ 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 = [
@@ -105,9 +106,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

View File

@@ -9,6 +9,8 @@ import { useAgencyMasterToggleMutation, useGetAgencyMasterQuery } from "../../..
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
@@ -65,6 +67,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);
@@ -73,6 +81,11 @@ const AgencyMaster = () => {
agency.id === agencyId ? { ...agency, is_active: currentStatus } : agency
)
);
toaster.create({
title: "Error",
description: "Please try again later",
type: "error",
});
}
};
@@ -199,6 +212,7 @@ const AgencyMaster = () => {
isError={isError}
/>
</Box>
<Toaster />
</MainFrame>
)
}

View File

@@ -8,6 +8,8 @@ import { CountryData, useCountryToggleMutation, useGetCountryMasterQuery } from
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";
@@ -79,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
@@ -173,6 +186,7 @@ const Country = () => {
isError={isError}
/>
</Box>
<Toaster />
</MainFrame>
)
}

View File

@@ -10,6 +10,8 @@ import { useDepartmentToggleMutation, useGetDepartmentMasterQuery } from "../../
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
@@ -58,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
@@ -142,6 +155,7 @@ const DepartmentMasterList = () => {
isError={isError}
/>
</Box>
<Toaster />
</MainFrame>
)
}

View File

@@ -10,6 +10,8 @@ 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
@@ -76,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
@@ -160,6 +173,7 @@ const IndustryMasterList = () => {
isError={isError}
/>
</Box>
<Toaster />
</MainFrame>
)
}

View File

@@ -7,8 +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";
@@ -70,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);
@@ -154,6 +161,7 @@ const JobStatus = () => {
isError={isError}
/>
</Box>
<Toaster />
</MainFrame>
)
}

View File

@@ -9,6 +9,8 @@ import EditJobeModel from "./EditJobModel";
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";
@@ -70,6 +72,12 @@ const JobType = () => {
);
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);
@@ -78,6 +86,11 @@ const JobType = () => {
agency.id === agencyId ? { ...agency, is_active: currentStatus } : agency
)
);
toaster.create({
title: "Error",
description: "Please try again later",
type: "error",
});
}
};
@@ -144,6 +157,7 @@ const JobType = () => {
onPageChange={handlePageChange}
/>
</Box>
<Toaster />
</MainFrame>
)
}

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
@@ -72,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);
@@ -80,6 +88,11 @@ const TemplateMaster = () => {
agency.id === agencyId ? { ...agency, is_active: currentStatus } : agency
)
);
toaster.create({
title: "Error",
description: "Please try again later",
type: "error",
});
}
};
@@ -124,7 +137,8 @@ const TemplateMaster = () => {
</Box>
</HStack>
)
}});
}
});
return (
@@ -182,6 +196,7 @@ const TemplateMaster = () => {
onPageChange={handlePageChange}
/>
</Box>
<Toaster />
</MainFrame>
)
}

View File

@@ -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) =>
@@ -133,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

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

@@ -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";
@@ -13,13 +13,15 @@ export const nav = [
title: "Dashboard",
path: "/",
Icon: MdOutlineDashboard,
type:'single'
type: 'single',
resourceTitle: 'Dashboard'
},
{
title: "Manage Users",
initPath: "/manage-users",
Icon: BiUserPin,
type:'multiple',
type: 'multiple',
resourceTitle: 'Manage Users',
children: [
{
title: "Register Users",
@@ -37,37 +39,43 @@ export const nav = [
title: "Manage Post",
path: "/manage-post",
Icon: HiOutlinePencilSquare,
type:'single'
type: 'single',
resourceTitle: 'Manage Post'
},
{
title: "Manage Sub-Admin",
path: "/sub-admin",
Icon: BiUser,
type:'single'
type: 'single',
resourceTitle: 'Manage Subadmin'
},
{
title: "Manage Jobs",
path: "/manage-jobs",
Icon: LuBriefcaseBusiness,
type:'single'
type: 'single',
resourceTitle: 'Manage Jobs'
},
{
title: "Manage Groups",
path: "/manage-groups",
Icon: PiUsersThree,
type:'single'
type: 'single',
resourceTitle: 'Manage Groups'
},
{
title: "Manage Contact Us",
path: "/manage-contact",
Icon: MdHeadsetMic ,
type:'single'
Icon: MdHeadsetMic,
type: 'single',
resourceTitle: 'Manage Contact Us'
},
{
title: "Manage CMS",
initPath: "/manage-cms",
Icon: AiOutlineFileText,
type:'multiple',
type: 'multiple',
resourceTitle: 'Manage CMS',
children: [
{
title: "FAQs",
@@ -100,13 +108,15 @@ export const nav = [
title: "My Profile",
path: "/profile",
Icon: BsPersonBadge,
type:'single'
type: 'single',
resourceTitle: 'My Profile'
},
{
title: "Master Module",
initPath: "/master-module",
Icon: BsBoxes,
type:'multiple',
type: 'multiple',
resourceTitle: 'Master Module',
children: [
{
title: "Agency Master",
@@ -150,4 +160,4 @@ export const nav = [
},
],
},
];
];

View File

@@ -135,23 +135,29 @@ const DataTable: React.FC<TableProps> = ({
key={index}
bg={index % 2 === 0 ? "#fff" : "#007F3310"}
>
{tableHeadRow.map((heading, colIndex) => (
{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"}
fontSize="xs"
fontWeight={500}
border={"none"}
border="none"
>
{(() => {
const words = item[heading]?.toString().split(" ") || [];
return words.length > 5
? `${words.slice(0, 5).join(" ")}...`
: item[heading];
})()}
{truncated}
</Table.Cell>
))}
);
})}
</Table.Row>
))}
</Table.Body>

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

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