feat: view customize on member's role
This commit is contained in:
@@ -4,16 +4,17 @@ import { FolderType, FoldersMessages } from '@/types/folder';
|
||||
|
||||
type Props = {
|
||||
folder: FolderType;
|
||||
isDisabled: boolean;
|
||||
onEditClick: (folder: FolderType) => void;
|
||||
onDeleteClick: (deleteFolderId: number) => void;
|
||||
messages: FoldersMessages;
|
||||
};
|
||||
|
||||
export default function FolderEditMenu({ folder, onEditClick, onDeleteClick, messages }: Props) {
|
||||
export default function FolderEditMenu({ folder, isDisabled, onEditClick, onDeleteClick, messages }: Props) {
|
||||
return (
|
||||
<Dropdown>
|
||||
<DropdownTrigger>
|
||||
<Button isIconOnly size="sm" className="bg-transparent rounded-full">
|
||||
<Button isIconOnly isDisabled={isDisabled} size="sm" className="bg-transparent rounded-full">
|
||||
<MoreVertical size={16} />
|
||||
</Button>
|
||||
</DropdownTrigger>
|
||||
|
||||
@@ -118,6 +118,7 @@ export default function FoldersPane({ projectId, messages, locale }: Props) {
|
||||
size="sm"
|
||||
variant="bordered"
|
||||
className="m-2"
|
||||
isDisabled={!context.isProjectEditable(Number(projectId))}
|
||||
onClick={openDialogForCreate}
|
||||
>
|
||||
{messages.newFolder}
|
||||
@@ -132,6 +133,7 @@ export default function FoldersPane({ projectId, messages, locale }: Props) {
|
||||
endContent={
|
||||
<FolderEditMenu
|
||||
folder={folder}
|
||||
isDisabled={!context.isProjectEditable(Number(projectId))}
|
||||
onEditClick={onEditClick}
|
||||
onDeleteClick={onDeleteClick}
|
||||
messages={messages}
|
||||
|
||||
@@ -28,11 +28,19 @@ export type TokenContextType = {
|
||||
};
|
||||
isSignedIn: () => Boolean;
|
||||
isAdmin: () => Boolean;
|
||||
isProjectEditable: (projectId: number) => Boolean;
|
||||
setToken: (token: TokenType) => {};
|
||||
storeTokenToLocalStorage: (token: TokenType) => {};
|
||||
removeTokenFromLocalStorage: () => {};
|
||||
};
|
||||
|
||||
export type ProjectRoleType = {
|
||||
projectId: number;
|
||||
isOwner: boolean;
|
||||
isMember: boolean;
|
||||
role: number;
|
||||
};
|
||||
|
||||
export type AuthMessages = {
|
||||
title: string;
|
||||
linkTitle: string;
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
'use client';
|
||||
import { createContext, useState, useEffect, useContext } from 'react';
|
||||
import { TokenContextType, TokenType } from '@/types/user';
|
||||
import { ProjectRoleType, TokenContextType, TokenType } from '@/types/user';
|
||||
import { TokenProps } from '@/types/user';
|
||||
import { useRouter, usePathname } from '@/src/navigation';
|
||||
import {
|
||||
isSignedIn as tokenIsSinedIn,
|
||||
isAdmin as tokenIsAdmin,
|
||||
isProjectEditable as tokenIsProjectEditable,
|
||||
checkSignInPage as tokenCheckSignInPage,
|
||||
fetchMyRoles,
|
||||
} from './token';
|
||||
import { ToastContext } from './ToastProvider';
|
||||
const LOCAL_STORAGE_KEY = 'testplat-auth-token';
|
||||
@@ -43,6 +45,7 @@ const TokenProvider = ({ toastMessages, locale, children }: TokenProps) => {
|
||||
expires_at: 0,
|
||||
user: null,
|
||||
});
|
||||
const [projectRoles, setProjectRoles] = useState<ProjectRoleType[]>([]);
|
||||
|
||||
const isSignedIn = () => {
|
||||
return tokenIsSinedIn(token);
|
||||
@@ -52,10 +55,16 @@ const TokenProvider = ({ toastMessages, locale, children }: TokenProps) => {
|
||||
return tokenIsAdmin(token);
|
||||
};
|
||||
|
||||
const isProjectEditable = (projectId: number) => {
|
||||
return tokenIsProjectEditable(projectRoles, projectId);
|
||||
};
|
||||
|
||||
const tokenContext = {
|
||||
token,
|
||||
projectRoles,
|
||||
isSignedIn,
|
||||
isAdmin,
|
||||
isProjectEditable,
|
||||
setToken,
|
||||
storeTokenToLocalStorage,
|
||||
removeTokenFromLocalStorage,
|
||||
@@ -91,6 +100,23 @@ const TokenProvider = ({ toastMessages, locale, children }: TokenProps) => {
|
||||
}
|
||||
}, [pathname, hasRestoreFinished]);
|
||||
|
||||
useEffect(() => {
|
||||
async function refreshProjectRoles() {
|
||||
if (!hasRestoreFinished || !token || !token.access_token) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await fetchMyRoles(token.access_token);
|
||||
setProjectRoles(data);
|
||||
} catch (error: any) {
|
||||
console.error('Error in effect:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
refreshProjectRoles();
|
||||
}, [hasRestoreFinished, token]);
|
||||
|
||||
return <TokenContext.Provider value={tokenContext}>{children}</TokenContext.Provider>;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { TokenType } from '@/types/user';
|
||||
import { roles } from '@/config/selection';
|
||||
import { ProjectRoleType, TokenType } from '@/types/user';
|
||||
import { roles, memberRoles } from '@/config/selection';
|
||||
import Config from '@/config/config';
|
||||
const apiServer = Config.apiServer;
|
||||
|
||||
function tokenExists(token: TokenType) {
|
||||
if (token && token.user && token.user.username) {
|
||||
@@ -36,7 +38,52 @@ function isAdmin(token: TokenType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// pravate paths are '/account', '/admin', '/projects/*'
|
||||
async function fetchMyRoles(jwt: string) {
|
||||
const fetchOptions = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
},
|
||||
};
|
||||
|
||||
const url = `${apiServer}/members/check`;
|
||||
|
||||
try {
|
||||
const response = await fetch(url, fetchOptions);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error: any) {
|
||||
console.error('Error fetching data:', error.message);
|
||||
}
|
||||
}
|
||||
|
||||
function isProjectEditable(projectRoles: ProjectRoleType[], projectId: number) {
|
||||
const found = projectRoles.find((role) => {
|
||||
return role.projectId === projectId;
|
||||
});
|
||||
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (found.isOwner === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const managerRoleIndex = memberRoles.findIndex((entry) => entry.uid === 'manager');
|
||||
const developerRoleIndex = memberRoles.findIndex((entry) => entry.uid === 'developer');
|
||||
if (found.role === managerRoleIndex || found.role === developerRoleIndex) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// private paths are '/account', '/admin', '/projects/*'
|
||||
const isPrivatePath = (pathname: string) => {
|
||||
return /^\/account(\/)?$/.test(pathname) || /^\/admin(\/.*)?$/.test(pathname) || /^\/projects(\/.*)?$/.test(pathname);
|
||||
};
|
||||
@@ -66,4 +113,4 @@ function checkSignInPage(token: TokenType, pathname: string) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
export { isSignedIn, isAdmin, isPrivatePath, checkSignInPage };
|
||||
export { isSignedIn, isAdmin, isProjectEditable, isPrivatePath, checkSignInPage, fetchMyRoles };
|
||||
|
||||
Reference in New Issue
Block a user