diff --git a/src/components/elements/CurateIndividual/CurateIndividual.tsx b/src/components/elements/CurateIndividual/CurateIndividual.tsx index 7519f485..04787af1 100644 --- a/src/components/elements/CurateIndividual/CurateIndividual.tsx +++ b/src/components/elements/CurateIndividual/CurateIndividual.tsx @@ -1,10 +1,10 @@ -import React, { useEffect, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { useRouter } from 'next/router'; import { useDispatch, useSelector, RootStateOrAny } from "react-redux"; import { identityFetchData, reciterFetchData,reCalcPubMedPubCount } from "../../../redux/actions/actions"; import Loader from "../Common/Loader"; import fullName from "../../../utils/fullName"; -import { Container, Button, Row } from "react-bootstrap"; +import { Container, Button, Row,Toast } from "react-bootstrap"; import appStyles from '../App/App.module.css'; import styles from "./CurateIndividual.module.css"; import InferredKeywords from "./InferredKeywords" @@ -12,6 +12,11 @@ import SuggestionsBanner from "./SuggestionsBanner"; import ReciterTabs from "./ReciterTabs"; import Image from "next/image"; import Profile from "../Profile/Profile"; +import { useSession } from "next-auth/client"; +import { allowedPermissions, toastMessage } from "../../../utils/constants"; +import ToastContainerWrapper from "../ToastContainerWrapper/ToastContainerWrapper"; + + interface PrimaryName { firstInitial?: string, @@ -24,6 +29,7 @@ interface PrimaryName { const CurateIndividual = () => { const router = useRouter() const { id } = router.query + const [newId, setNewId ] = useState(""); const dispatch = useDispatch(); const identityData = useSelector((state: RootStateOrAny) => state.identityData) const identityFetching = useSelector((state: RootStateOrAny) => state.identityFetching) @@ -31,10 +37,29 @@ const CurateIndividual = () => { const reciterFetching = useSelector((state: RootStateOrAny) => state.reciterFetching) const [displayImage, setDisplayImage] = useState(true); const [modalShow, setModalShow] = useState(false); + const [session, loading] = useSession(); useEffect(() => { - dispatch(identityFetchData(id)); - fetchData(); + let userPermissions = JSON.parse(session.data?.userRoles); + let routerUserId = router.query.id ; + + let nextPersonIdentifier = ""; + //Commented as this needs to be worked on later.. + // checking curator_self + /* if (userPermissions.length === 1 && userPermissions.some(role => role.roleLabel === allowedPermissions.Curator_Self && 'aaa2020' != routerUserId) ){ + toastMessage("error", "You do not have access to view page"); + //userPermissions.map((id)=>{ nextPersonIdentifier = id.personIdentifier}) + //router.push(`/curate/${routerUserId}`,`/curate/aaa2020`,{shallow : true}); + }else{ + userPermissions.map((id)=>{ nextPersonIdentifier = id.personIdentifier}) + console.log('nextPersonIdentifier',nextPersonIdentifier); + setNewId('aaa2020'); + dispatch(identityFetchData('aaa2020')); + fetchData(); + }*/ + setNewId('${routerUserId}'); + dispatch(identityFetchData(routerUserId)); + fetchData(); }, []) const fetchData = () => { @@ -59,6 +84,7 @@ const CurateIndividual = () => { return (
+

Curate Publications

{ identityData && @@ -95,7 +121,7 @@ const CurateIndividual = () => { {reciterData.reciterPending && reciterData.reciterPending.length > 0 && } diff --git a/src/components/elements/CurateIndividual/ReciterTabs.tsx b/src/components/elements/CurateIndividual/ReciterTabs.tsx index dd8be1f7..6c82b84b 100644 --- a/src/components/elements/CurateIndividual/ReciterTabs.tsx +++ b/src/components/elements/CurateIndividual/ReciterTabs.tsx @@ -5,6 +5,8 @@ import ReciterTabContent from "./ReciterTabContent"; import styles from "./CurateIndividual.module.css"; import { RootStateOrAny, useSelector } from "react-redux"; import TabAddPublication from "../TabAddPublication/TabAddPublication"; +import { allowedPermissions } from "../../../utils/constants"; +import { useSession } from "next-auth/client"; const ReciterTabs = ({ reciterData, fullName, fetchOriginalData }: { reciterData: any, fullName: string, fetchOriginalData: any }) => { @@ -12,24 +14,25 @@ const ReciterTabs = ({ reciterData, fullName, fetchOriginalData }: { reciterData const [key, setKey] = useState('NULL'); const [filteredData, setFilteredData] = useState([]) const [pubKey, setPubKey] = useState(false); + const [session, loading] = useSession(); const isSearchText = useSelector((state: RootStateOrAny) => state.curateSearchtext) const addnewtabName =

Add New record:

const pubMedTabName =

PubMed

const tabsData = [ - { name: 'Suggested', value: 'NULL' }, - { name: 'Accepted', value: 'ACCEPTED' }, - { name: 'Rejected', value: 'REJECTED' }, - { name: addnewtabName, value: 'addNewRecord' }, - { name: pubMedTabName, value: 'AddPub' }, + { name: 'Suggested', value: 'NULL',allowedRoleNames: ["Superuser", "Curator_All","Curator_Self"] }, + { name: 'Accepted', value: 'ACCEPTED',allowedRoleNames: ["Superuser", "Curator_All","Curator_Self"] }, + { name: 'Rejected', value: 'REJECTED',allowedRoleNames: ["Superuser", "Curator_All","Curator_Self"] }, + { name: addnewtabName, value: 'addNewRecord',allowedRoleNames: ["Superuser","Curator_Self","Curator_All"] }, + { name: pubMedTabName, value: 'AddPub', allowedRoleNames: ["Superuser","Curator_Self","Curator_All"] }, ] useEffect(() => { let publicationsPerTabs = []; tabsData.forEach((tab) => { let filteredReciterData = filterPublications(reciterData, tab.value, ""); - publicationsPerTabs.push({ value: tab.value, name: tab.name, data: filteredReciterData, count: filteredReciterData.length }) + publicationsPerTabs.push({ value: tab.value, name: tab.name, data: filteredReciterData, count: filteredReciterData.length, allowedRoleNames: tab.allowedRoleNames}) }) setFilteredData(publicationsPerTabs); }, []) @@ -96,6 +99,9 @@ const ReciterTabs = ({ reciterData, fullName, fetchOriginalData }: { reciterData className={`${styles.tabsContainer}`} > { filteredData.map((tabData: any, index: number) => { + let userPermissions = JSON.parse(session.data.userRoles); + const matchedRoles = userPermissions.filter(role => tabData.allowedRoleNames.includes(role.roleLabel)); + if(matchedRoles.length >= 1){ return ( ) + } }) } diff --git a/src/components/elements/Dropdown/SplitDropdown.tsx b/src/components/elements/Dropdown/SplitDropdown.tsx index 815b220e..f712c034 100644 --- a/src/components/elements/Dropdown/SplitDropdown.tsx +++ b/src/components/elements/Dropdown/SplitDropdown.tsx @@ -13,9 +13,10 @@ interface DropdownProps { secondary?: boolean, disabled?: boolean, onDropDownClick:any, + isUserRole:any } -const SplitDropdown: React.FC = ({ title, to, onClick, id, listItems, secondary,onDropDownClick,disabled}) => { +const SplitDropdown: React.FC = ({ isUserRole,title, to, onClick, id, listItems, secondary,onDropDownClick,disabled}) => { return (
diff --git a/src/components/elements/Login/Login.js b/src/components/elements/Login/Login.js index a301294f..f935b6ac 100644 --- a/src/components/elements/Login/Login.js +++ b/src/components/elements/Login/Login.js @@ -5,8 +5,10 @@ import { Footer } from "../Footer/Footer"; import ToastContainerWrapper from "../ToastContainerWrapper/ToastContainerWrapper" import Router from "next/router" import Header from "../Header/Header" -import { signIn } from "next-auth/client" +import { signIn,getSession } from "next-auth/client" import { toast } from "react-toastify" +import { allowedPermissions } from "../../../utils/constants"; + const Login = () => { @@ -14,9 +16,10 @@ const Login = () => { const [username, setUsername] = useState("") const [password, setPassword] = useState("") const [isShowButton, setIsShowButton] = useState(true) + const session = getSession(); useEffect(() => { - + }) const validateForm = () => { @@ -48,7 +51,19 @@ const Login = () => { autoClose: 2000, theme: "colored" }); - Router.push(`${window.location.origin}/search`); + //if(session && session.data && session.data.userRoles) + getSession().then((session) => { + if (session) { + let userPermissions = JSON.parse(session.data.userRoles); + let userName = session.data.username; + let personIdentifier = userPermissions && userPermissions.length > 0 ? userPermissions[0].personIdentifier : "" + if((userPermissions.some(role => role.roleLabel === allowedPermissions.Curator_Self)) && userName) + Router.push(`${window.location.origin}/curate/${personIdentifier}`); + else + Router.push(`${window.location.origin}/search`); + } + }); + } else { setInvalidCredentialsFlag(true) toast.error("Invalid credentials", { diff --git a/src/components/elements/Navbar/NestedListItem.tsx b/src/components/elements/Navbar/NestedListItem.tsx index 07d3ac5e..7ce9a96a 100644 --- a/src/components/elements/Navbar/NestedListItem.tsx +++ b/src/components/elements/Navbar/NestedListItem.tsx @@ -10,7 +10,9 @@ import { MenuItem } from "../../../../types/menu"; import Image from 'next/image'; import MenuListItem from "./MenuListItem"; import Box from '@mui/material/Box'; -import styles from './Navbar.module.css' +import styles from './Navbar.module.css'; +import { useSession } from 'next-auth/client'; + type ImageSourcePropType = React.ComponentProps['src'] @@ -22,6 +24,8 @@ type NestedListItemProps = { const NestedListItem: React.FC = ({ header, menuItems, imgUrl}) => { const [open, setOpen] = React.useState(false); + const [session, loading] = useSession(); + const handleClick = () => { setOpen(!open); @@ -31,33 +35,37 @@ const NestedListItem: React.FC = ({ header, menuItems, imgU <> - Menu Icon + Menu Icon {open ? : } - - { - menuItems.map((item: MenuItem, index: number) => { - return ( - - - - ) - }) - } + + { + menuItems.map((item: MenuItem, index: number) => { + let userPermissions = JSON.parse(session.data.userRoles); + const matchedRoles = userPermissions.filter(role => item.allowedRoleNames.includes(role.roleLabel)); + if (matchedRoles.length >= 1) { + return ( + + + + ) + } + }) + } diff --git a/src/components/elements/Navbar/SideNavbar.tsx b/src/components/elements/Navbar/SideNavbar.tsx index 16d8f0d2..8196f466 100644 --- a/src/components/elements/Navbar/SideNavbar.tsx +++ b/src/components/elements/Navbar/SideNavbar.tsx @@ -19,6 +19,7 @@ import settingsIconActive from '../../../../public/images/icon-side-faculty_admi import chartIconActive from '../../../../public/images/icon-side-faculty_report-active.png'; import checkMarkIconActive from '../../../../public/images/icon-side-check_mark-active.png'; import { useSelector, RootStateOrAny } from "react-redux"; +import { useSession } from 'next-auth/client'; type SideNavBarProps = { items: any @@ -135,6 +136,9 @@ const SideNavbar: React.FC = () => { const theme = useTheme(); const [open, setOpen] = React.useState(true); const filters = useSelector((state: RootStateOrAny) => state.filters) + + const [isCurateSelf, setIsCurateSelf] = React.useState(false); + const [session, loading] = useSession(); const menuItems: Array = [ { @@ -143,6 +147,7 @@ const SideNavbar: React.FC = () => { imgUrl: facultyIcon, imgUrlActive: facultyIconActive, disabled: false, + allowedRoleNames: ["Superuser", "Curator_All","Reporter_All"], }, { title: 'Curate Publications', @@ -150,6 +155,7 @@ const SideNavbar: React.FC = () => { imgUrl: settingsIcon, imgUrlActive: settingsIconActive, disabled: (Object.keys(filters).length === 0), + allowedRoleNames: ["Superuser", "Curator_All","Curator_Self"], }, { title: 'Create Reports', @@ -157,19 +163,29 @@ const SideNavbar: React.FC = () => { imgUrl: chartIcon, imgUrlActive: chartIconActive, disabled: false, + allowedRoleNames: ["Superuser","Reporter_All" ], }, - { - title: 'Perform Analysis', - to: '/login', - imgUrl: checkMarkIcon, - imgUrlActive: checkMarkIconActive, - disabled: false, - }, + // { + // title: 'Perform Analysis', + // to: '/login', + // imgUrl: checkMarkIcon, + // imgUrlActive: checkMarkIconActive, + // disabled: false, + // allowedRoleNames: ["Superuser","Reporter_All" ], + // }, { title: 'Manage Module', imgUrl: settingsIcon, imgUrlActive: settingsIconActive, - nestedMenu: [{title: 'Manage Users', to: '/admin/manage/users', imgUrl: facultyIcon, imgUrlActive: facultyIconActive, disabled: false,}] + nestedMenu: [{title: 'Add Users', + to: '/admin/add/users', + imgUrl: facultyIcon, + imgUrlActive: facultyIconActive, + disabled: false, + allowedRoleNames: ["Superuser"], + }], + allowedRoleNames: ["Superuser"], + } ] @@ -192,6 +208,9 @@ const SideNavbar: React.FC = () => { { menuItems.map((item: MenuItem, index: number) => { + let userPermissions = JSON.parse(session.data.userRoles); + const matchedRoles = userPermissions.filter(role => item.allowedRoleNames.includes(role.roleLabel)); + if(matchedRoles.length >= 1){ return item.nestedMenu ? = () => { imgUrlActive={item.imgUrlActive} disabled={item.disabled} /> + } }) } diff --git a/src/components/elements/Search/FilterReview.tsx b/src/components/elements/Search/FilterReview.tsx index 4ccb8341..0bd07a66 100644 --- a/src/components/elements/Search/FilterReview.tsx +++ b/src/components/elements/Search/FilterReview.tsx @@ -8,12 +8,10 @@ import SplitDropdown from "../Dropdown/SplitDropdown"; import { useRouter } from 'next/router'; import { useDispatch } from "react-redux"; import { updatePubFiltersFromSearch } from "../../../redux/actions/actions"; +import { useSession } from "next-auth/client"; +import { allowedPermissions, dropdownItemsReport } from "../../../utils/constants"; +import styles from './Search.module.css' -const dropdownItems: Array = - [ - { title: 'Create Reports', to: '/create-reports'}, - { title: 'Perform Analysis', to: '/perform-analysis'}, - ] const FilterReview = ({ @@ -21,11 +19,13 @@ const FilterReview = ({ count, filterByPending, onCurate, + isUserRole, } : { onToggle: (value: boolean) => void; count: number, filterByPending: boolean, onCurate: any, + isUserRole : any }) => { const [filter, setFilter] = useState(false); const router = useRouter(); @@ -54,6 +54,7 @@ const FilterReview = ({ })); const onDropDownClick = ()=>{ + dispatch(updatePubFiltersFromSearch()); onCurate(); } @@ -67,21 +68,27 @@ const FilterReview = ({ const dropdownItems: Array = [ { title: 'Create Reports', onClick: onClickCurateReports}, - { title: 'Perform Analysis', to: '/perform-analysis'}, + // { title: 'Perform Analysis', to: '/perform-analysis'}, ] return (

{`${count}`} people found using filters

+ { + isUserRole === allowedPermissions.Superuser ? + : + + }
Show only people with
pending suggestions
diff --git a/src/components/elements/Search/Search.js b/src/components/elements/Search/Search.js index 1d41b457..84739126 100644 --- a/src/components/elements/Search/Search.js +++ b/src/components/elements/Search/Search.js @@ -10,16 +10,13 @@ import { useSession } from 'next-auth/client'; import SearchBar from "./SearchBar"; import FilterReview from "./FilterReview"; import fetchWithTimeout from "../../../utils/fetchWithTimeout"; -import { Table } from "react-bootstrap"; +import { Table,Button} from "react-bootstrap"; import SplitDropdown from "../Dropdown/SplitDropdown"; import Loader from "../Common/Loader"; import { reciterConfig } from "../../../../config/local"; import { useHistory } from "react-router-dom"; +import { allowedPermissions, dropdownItemsReport, dropdownItemsSuper } from "../../../utils/constants" -const dropdownItems = [ - { title: 'Create Reports', to: '/create-reports' }, - { title: 'Perform Analysis', to: '/perform-analysis' }, -] const Search = () => { @@ -41,25 +38,35 @@ const Search = () => { const filters = useSelector((state) => state.filters) const errors = useSelector((state) => state.errors) - const auth = useSelector((state) => state.auth) + const [sort, setSort] = useState("0") const [identitySearch, setIdentitySearch] = useState("") const [identityData, setIdentityData] = useState([]) - const [curateIds, setCurateIds] = useSession(""); const [search, setSearch] = useState("") + const [isUserRole, setIsuserRole] = useState("") + const [page, setPage] = useState(1) const [count, setCount] = useState(20) const [filterByPending, setFilterByPending] = useState(false); const [totalCount, setTotalCount] = useState(0); const [countAllData, setCountAllData] = useState(0); const [isCountLoading, setIsCountLoading] = useState(false); - //ref const searchValue = useRef() useEffect(() => { + let userPermissions = JSON.parse(session.data.userRoles); + + if (userPermissions.length === 1 && userPermissions.some(role => role.roleLabel === allowedPermissions.Reporter_All)) { + setIsuserRole(allowedPermissions.Reporter_All) + } else if (userPermissions.length === 1 && userPermissions.some(role => role.roleLabel === allowedPermissions.Curator_All)) { + setIsuserRole(allowedPermissions.Curator_All); + } else { + setIsuserRole(allowedPermissions.Superuser); + } + if (identityAllData.length === 0) { fetchPaginatedData() fetchCount() @@ -256,11 +263,17 @@ const Search = () => { } const redirectToCurate = (isFor, data) => { + + // if() if (isFor === "individual") { router.push({ pathname: `/curate/${data}`, }) - }else { + } else if (isFor === "report") { + router.push({ + pathname: '/report', + }) + } else { dispatch(curateIdsFromSearch(identities.paginatedIdentities)) router.push({ pathname: '/curate', @@ -284,7 +297,7 @@ const Search = () => { tableBody = paginatedIdentities.map(function (identity, identityIndex) { return - onCLickProfile(identity.personIdentifier)}> + onCLickProfile(identity.personIdentifier)}> {identity.primaryOrganizationalUnit &&
{identity.primaryOrganizationalUnit}
} @@ -296,14 +309,19 @@ const Search = () => { {identity.countPendingArticles &&
{identity.countPendingArticles}
} - redirectToCurate("individual", identity.personIdentifier)} - id={`curate-publications_${identity.personIdentifier}`} - listItems={dropdownItems} - secondary={true} - /> + { + isUserRole === allowedPermissions.Superuser ? + redirectToCurate("report") : () => redirectToCurate("individual", identity.personIdentifier)} + id={`curate-publications_${identity.personIdentifier}`} + listItems={isUserRole && isUserRole === allowedPermissions.Superuser ? dropdownItemsSuper : dropdownItemsReport} + secondary={true} + /> + : + + } ; }) @@ -337,7 +355,7 @@ const Search = () => { {totalCount !== undefined &&

{totalCount.toLocaleString("en-US")} people

}
} - {filtersOn && } + {filtersOn && } { await NextAuth(req, res, options); @@ -17,10 +18,13 @@ const options = { async authorize(credentials) { if(credentials.username !== undefined && credentials.password !== undefined) { const apiResponse = await authenticate(credentials); - console.log(apiResponse) if (apiResponse.statusCode == 200) { const adminUser = await findOrCreateAdminUsers(credentials.username) - apiResponse.databaseUser = adminUser + apiResponse.databaseUser = adminUser; + const userRoles = await findUserPermissions(credentials.username); + apiResponse.userRoles = userRoles; + + return apiResponse; } else { return null; @@ -62,20 +66,18 @@ const options = { if (user.attributes && user.attributes.CWID) { cwid = user.attributes.CWID[0]; } - console.log(user) - if (cwid) { const adminUser = await findOrCreateAdminUsers(cwid) adminUser.databaseUser = adminUser adminUser.personIdentifier - console.log(adminUser) + const userRoles = await findUserPermissions(cwid); + adminUser.userRoles = userRoles; return adminUser; } return { cwid, has_access: false }; } catch (error) { - console.log(error); return null; } }, @@ -98,7 +100,11 @@ const options = { if(apiResponse.databaseUser) { if(apiResponse.databaseUser.personIdentifier) token.username = apiResponse.databaseUser.personIdentifier - token.databaseUser = apiResponse.databaseUser + token.databaseUser = apiResponse.databaseUser + } + if(apiResponse.userRoles) { + if(apiResponse.userRoles) + token.userRoles = apiResponse.userRoles } } return token diff --git a/src/pages/index.js b/src/pages/index.js index d05005f6..604374db 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,10 +1,51 @@ import { getSession } from "next-auth/client" +import { useRouter } from 'next/router'; +import { useEffect } from "react"; +import { allowedPermissions } from "../utils/constants"; + + +// function RedirectTo(to){ +// const router = useRouter(); +// useEffect(()=>{ +// router.push(to) +// },[to]) +// } + export async function getServerSideProps(ctx) { const session = await getSession(ctx); - if(process.env.LOGIN_PROVIDER !== "SAML") { + let userPermissions =''; + const personIdentifier = userPermissions && userPermissions.length > 0 ? userPermissions[0].personIdentifier : "" + + if(session && session.data && session.data.userRoles) + userPermissions = JSON.parse(session.data?.userRoles); + + if (process.env.LOGIN_PROVIDER !== "SAML") { //Redirect to search after login - if(session && session.data) { - if(session.data.databaseUser && session.data.databaseUser.status == 0) { + if (session && session.data) { + if (session.data.databaseUser && session.data.databaseUser.status == 0) { + return { + redirect: { + destination: "/noaccess", + permanent: false, + }, + }; + } + + if (userPermissions.some(role => role.roleLabel === allowedPermissions.Curator_All || role.roleLabel === allowedPermissions.Reporter_All || role.roleLabel === allowedPermissions.Superuser)) { + return { + redirect: { + destination: "/search", + permanent: false, + }, + }; + } else if (userPermissions.some(role => role.roleLabel === allowedPermissions.Curator_Self)) { + return { + redirect: { + destination: `/curate/${personIdentifier}`, + permanent: false, + }, + }; + } else { return { redirect: { destination: "/noaccess", @@ -12,13 +53,8 @@ export async function getServerSideProps(ctx) { }, }; } - return { - redirect: { - destination: "/search", - permanent: false, - }, - }; } + return { redirect: { destination: "/login", @@ -27,8 +63,8 @@ export async function getServerSideProps(ctx) { }; } //Redirect to search after login - if(session && session.data) { - if(session.data.databaseUser && session.data.databaseUser.status == 0) { + if (session && session.data) { + if (session.data.databaseUser && session.data.databaseUser.status == 0) { return { redirect: { destination: "/noaccess", @@ -36,14 +72,30 @@ export async function getServerSideProps(ctx) { }, }; } - return { - redirect: { - destination: "/search", - permanent: false, - }, - }; - } + if (userPermissions.some(role => role.roleLabel === allowedPermissions.Curator_All || role.roleLabel === allowedPermissions.Reporter_All || role.roleLabel === allowedPermissions.Superuser)) { + return { + redirect: { + destination: "/search", + permanent: false, + }, + }; + } else if (userPermissions.some(role => role.roleLabel === allowedPermissions.Curator_Self)) { + return { + redirect: { + destination: `/curate/${personIdentifier}`, + permanent: false, + }, + }; + } else { + return { + redirect: { + destination: "/noaccess", + permanent: false, + }, + }; + } + } return { redirect: { destination: "/api/saml/assert?callbackUrl=/search", @@ -53,5 +105,6 @@ export async function getServerSideProps(ctx) { } export default function Home() { + return <>; } \ No newline at end of file diff --git a/styles/globals.css b/styles/globals.css index 78471088..262b630b 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -150,4 +150,7 @@ h1 { .noSpace{ margin: 0 !important; padding: 0 !important; +} +.menuDisplayNone{ + display:none !important; } \ No newline at end of file diff --git a/types/menu.d.ts b/types/menu.d.ts index e8ac7717..41bf5d25 100644 --- a/types/menu.d.ts +++ b/types/menu.d.ts @@ -6,4 +6,5 @@ export type MenuItem = { imgUrl?: string | StaticImport imgUrlActive?: string | StaticImport disabled?: boolean + allowedRoleNames?:Array } \ No newline at end of file diff --git a/types/next-auth.d.ts b/types/next-auth.d.ts index ffc3f704..e3736d81 100644 --- a/types/next-auth.d.ts +++ b/types/next-auth.d.ts @@ -16,6 +16,7 @@ declare module "next-auth" { userID: number, status: number, } + userRoles : Array } } } \ No newline at end of file