mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 03:29:35 +00:00
chore(frontend): fixed all eslint errors
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
import { useNotificationContext } from '@app/components/context/Notifications/NotificationProvider';
|
||||
import { faX } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { useNotificationContext } from '~/components/context/Notifications/NotificationProvider';
|
||||
|
||||
import deleteAPIKey from "../../../pages/api/apiKey/deleteAPIKey";
|
||||
import deleteAPIKey from '../../../pages/api/apiKey/deleteAPIKey';
|
||||
import guidGenerator from '../../utilities/randomId';
|
||||
import Button from '../buttons/Button';
|
||||
|
||||
@@ -28,49 +27,47 @@ const ApiKeyTable = ({ data, setApiKeys }: ServiceTokensProps) => {
|
||||
const { createNotification } = useNotificationContext();
|
||||
return (
|
||||
<div className="table-container w-full bg-bunker rounded-md mb-6 border border-mineshaft-700 relative mt-1">
|
||||
<div className="absolute rounded-t-md w-full h-12 bg-white/5"></div>
|
||||
<div className="absolute rounded-t-md w-full h-12 bg-white/5" />
|
||||
<table className="w-full my-1">
|
||||
<thead className="text-bunker-300 text-sm font-light">
|
||||
<tr>
|
||||
<th className="text-left pl-6 pt-2.5 pb-2">API KEY NAME</th>
|
||||
<th className="text-left pl-6 pt-2.5 pb-2">VALID UNTIL</th>
|
||||
<th></th>
|
||||
<th aria-label="button" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data?.length > 0 ? (
|
||||
data?.map((row) => {
|
||||
return (
|
||||
<tr
|
||||
key={guidGenerator()}
|
||||
className="bg-bunker-800 hover:bg-bunker-800/5 duration-100"
|
||||
>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.name}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{new Date(row.expiresAt).toUTCString()}
|
||||
</td>
|
||||
<td className="py-2 border-mineshaft-700 border-t">
|
||||
<div className="opacity-50 hover:opacity-100 duration-200 flex items-center">
|
||||
<Button
|
||||
onButtonPressed={() => {
|
||||
deleteAPIKey({ apiKeyId: row._id} );
|
||||
setApiKeys(data.filter(token => token._id != row._id));
|
||||
createNotification({
|
||||
text: `'${row.name}' API key has been revoked.`,
|
||||
type: 'error'
|
||||
});
|
||||
}}
|
||||
color="red"
|
||||
size="icon-sm"
|
||||
icon={faX}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
data?.map((row) => (
|
||||
<tr
|
||||
key={guidGenerator()}
|
||||
className="bg-bunker-800 hover:bg-bunker-800/5 duration-100"
|
||||
>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.name}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{new Date(row.expiresAt).toUTCString()}
|
||||
</td>
|
||||
<td className="py-2 border-mineshaft-700 border-t">
|
||||
<div className="opacity-50 hover:opacity-100 duration-200 flex items-center">
|
||||
<Button
|
||||
onButtonPressed={() => {
|
||||
deleteAPIKey({ apiKeyId: row._id });
|
||||
setApiKeys(data.filter((token) => token._id !== row._id));
|
||||
createNotification({
|
||||
text: `'${row.name}' API key has been revoked.`,
|
||||
type: 'error'
|
||||
});
|
||||
}}
|
||||
color="red"
|
||||
size="icon-sm"
|
||||
icon={faX}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={4} className="text-center pt-7 pb-5 text-bunker-300 text-sm">
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
/* eslint-disable jsx-a11y/label-has-associated-control */
|
||||
type Props = {
|
||||
addAllUsers: boolean;
|
||||
setAddAllUsers: (arg: boolean) => void;
|
||||
};
|
||||
|
||||
export const Checkbox = ({ addAllUsers, setAddAllUsers }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<div className='flex flex-row items-center'>
|
||||
{addAllUsers == true ? (
|
||||
<input
|
||||
type='checkbox'
|
||||
className='accent-primary h-4 w-4'
|
||||
checked
|
||||
readOnly
|
||||
onClick={() => setAddAllUsers(!addAllUsers)}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className='h-4 w-4 bg-bunker border border-gray-600 rounded-sm'
|
||||
onClick={() => setAddAllUsers(!addAllUsers)}
|
||||
></div>
|
||||
)}
|
||||
export const Checkbox = ({ addAllUsers, setAddAllUsers }: Props) => (
|
||||
<div className="flex flex-row items-center">
|
||||
{addAllUsers === true ? (
|
||||
<input
|
||||
type="checkbox"
|
||||
className="accent-primary h-4 w-4"
|
||||
checked
|
||||
readOnly
|
||||
onClick={() => setAddAllUsers(!addAllUsers)}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
onKeyDown={() => setAddAllUsers(!addAllUsers)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-label="add all users"
|
||||
className="h-4 w-4 bg-bunker border border-gray-600 rounded-sm"
|
||||
onClick={() => setAddAllUsers(!addAllUsers)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<label className='ml-2 text-gray-500 text-sm'>
|
||||
Add all members of my organization to this project.
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
<label className="ml-2 text-gray-500 text-sm">
|
||||
Add all members of my organization to this project.
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -14,15 +14,10 @@ type Props = {
|
||||
onDeleteEnv: (slug: string) => Promise<void>;
|
||||
};
|
||||
|
||||
const EnvironmentTable = ({
|
||||
data = [],
|
||||
onCreateEnv,
|
||||
onDeleteEnv,
|
||||
onUpdateEnv,
|
||||
}: Props) => {
|
||||
const EnvironmentTable = ({ data = [], onCreateEnv, onDeleteEnv, onUpdateEnv }: Props) => {
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
|
||||
'createUpdateEnv',
|
||||
'deleteEnv',
|
||||
'deleteEnv'
|
||||
] as const);
|
||||
|
||||
const onEnvCreateCB = async (env: Env) => {
|
||||
@@ -36,10 +31,7 @@ const EnvironmentTable = ({
|
||||
|
||||
const onEnvUpdateCB = async (env: Env) => {
|
||||
try {
|
||||
await onUpdateEnv(
|
||||
(popUp.createUpdateEnv?.data as Pick<Env, 'slug'>)?.slug,
|
||||
env
|
||||
);
|
||||
await onUpdateEnv((popUp.createUpdateEnv?.data as Pick<Env, 'slug'>)?.slug, env);
|
||||
handlePopUpClose('createUpdateEnv');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -48,9 +40,7 @@ const EnvironmentTable = ({
|
||||
|
||||
const onEnvDeleteCB = async () => {
|
||||
try {
|
||||
await onDeleteEnv(
|
||||
(popUp.deleteEnv?.data as Pick<Env, 'slug'>)?.slug
|
||||
);
|
||||
await onDeleteEnv((popUp.deleteEnv?.data as Pick<Env, 'slug'>)?.slug);
|
||||
handlePopUpClose('deleteEnv');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -59,83 +49,68 @@ const EnvironmentTable = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='flex flex-row justify-between w-full'>
|
||||
<div className='flex flex-col w-full'>
|
||||
<p className='text-xl font-semibold mb-3'>Project Environments</p>
|
||||
<p className='text-base text-gray-400 mb-4'>
|
||||
Choose which environments will show up in your dashboard like
|
||||
development, staging, production
|
||||
<div className="flex flex-row justify-between w-full">
|
||||
<div className="flex flex-col w-full">
|
||||
<p className="text-xl font-semibold mb-3">Project Environments</p>
|
||||
<p className="text-base text-gray-400 mb-4">
|
||||
Choose which environments will show up in your dashboard like development, staging,
|
||||
production
|
||||
</p>
|
||||
<p className='text-sm mr-1 text-gray-500 self-start'>
|
||||
Note: the text in slugs shows how these environmant should be
|
||||
accessed in CLI.
|
||||
<p className="text-sm mr-1 text-gray-500 self-start">
|
||||
Note: the text in slugs shows how these environmant should be accessed in CLI.
|
||||
</p>
|
||||
</div>
|
||||
<div className='w-48'>
|
||||
<div className="w-48">
|
||||
<Button
|
||||
text='Add New Env'
|
||||
text="Add New Env"
|
||||
onButtonPressed={() => handlePopUpOpen('createUpdateEnv')}
|
||||
color='mineshaft'
|
||||
color="mineshaft"
|
||||
icon={faPlus}
|
||||
size='md'
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='table-container w-full bg-bunker rounded-md mb-6 border border-mineshaft-700 relative mt-1'>
|
||||
<div className='absolute rounded-t-md w-full h-12 bg-white/5'></div>
|
||||
<table className='w-full my-1'>
|
||||
<thead className='text-bunker-300'>
|
||||
<div className="table-container w-full bg-bunker rounded-md mb-6 border border-mineshaft-700 relative mt-1">
|
||||
<div className="absolute rounded-t-md w-full h-12 bg-white/5" />
|
||||
<table className="w-full my-1">
|
||||
<thead className="text-bunker-300">
|
||||
<tr>
|
||||
<th className='text-left pl-6 pt-2.5 pb-2'>Name</th>
|
||||
<th className='text-left pl-6 pt-2.5 pb-2'>Slug</th>
|
||||
<th></th>
|
||||
<th className="text-left pl-6 pt-2.5 pb-2">Name</th>
|
||||
<th className="text-left pl-6 pt-2.5 pb-2">Slug</th>
|
||||
<th aria-label="buttons" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data?.length > 0 ? (
|
||||
data.map(({ name, slug }) => {
|
||||
return (
|
||||
<tr
|
||||
key={name}
|
||||
className='bg-bunker-800 hover:bg-bunker-800/5 duration-100'
|
||||
>
|
||||
<td className='pl-6 py-2 border-mineshaft-700 border-t text-gray-300 capitalize'>
|
||||
{name}
|
||||
</td>
|
||||
<td className='pl-6 py-2 border-mineshaft-700 border-t text-gray-300'>
|
||||
{slug}
|
||||
</td>
|
||||
<td className='py-2 border-mineshaft-700 border-t flex'>
|
||||
<div className='opacity-50 hover:opacity-100 duration-200 flex items-center mr-8'>
|
||||
<Button
|
||||
onButtonPressed={() =>
|
||||
handlePopUpOpen('createUpdateEnv', { name, slug })
|
||||
}
|
||||
color='red'
|
||||
size='icon-sm'
|
||||
icon={faPencil}
|
||||
/>
|
||||
</div>
|
||||
<div className='opacity-50 hover:opacity-100 duration-200 flex items-center'>
|
||||
<Button
|
||||
onButtonPressed={() =>
|
||||
handlePopUpOpen('deleteEnv', { name, slug })
|
||||
}
|
||||
color='red'
|
||||
size='icon-sm'
|
||||
icon={faX}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
data.map(({ name, slug }) => (
|
||||
<tr key={name} className="bg-bunker-800 hover:bg-bunker-800/5 duration-100">
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300 capitalize">
|
||||
{name}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">{slug}</td>
|
||||
<td className="py-2 border-mineshaft-700 border-t flex">
|
||||
<div className="opacity-50 hover:opacity-100 duration-200 flex items-center mr-8">
|
||||
<Button
|
||||
onButtonPressed={() => handlePopUpOpen('createUpdateEnv', { name, slug })}
|
||||
color="red"
|
||||
size="icon-sm"
|
||||
icon={faPencil}
|
||||
/>
|
||||
</div>
|
||||
<div className="opacity-50 hover:opacity-100 duration-200 flex items-center">
|
||||
<Button
|
||||
onButtonPressed={() => handlePopUpOpen('deleteEnv', { name, slug })}
|
||||
color="red"
|
||||
size="icon-sm"
|
||||
icon={faX}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={4}
|
||||
className='text-center pt-7 pb-4 text-bunker-400'
|
||||
>
|
||||
<td colSpan={4} className="text-center pt-7 pb-4 text-bunker-400">
|
||||
No environmants found
|
||||
</td>
|
||||
</tr>
|
||||
@@ -143,7 +118,7 @@ const EnvironmentTable = ({
|
||||
</tbody>
|
||||
</table>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp['deleteEnv'].isOpen}
|
||||
isOpen={popUp.deleteEnv.isOpen}
|
||||
title={`Are you sure want to delete ${
|
||||
(popUp?.deleteEnv?.data as { name: string })?.name || ' '
|
||||
}?`}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useNotificationContext } from '@app/components/context/Notifications/NotificationProvider';
|
||||
import { faX } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { useNotificationContext } from '~/components/context/Notifications/NotificationProvider';
|
||||
|
||||
import deleteServiceToken from "../../../pages/api/serviceToken/deleteServiceToken";
|
||||
import deleteServiceToken from '../../../pages/api/serviceToken/deleteServiceToken';
|
||||
import guidGenerator from '../../utilities/randomId';
|
||||
import Button from '../buttons/Button';
|
||||
|
||||
@@ -33,7 +32,7 @@ const ServiceTokenTable = ({ data, workspaceName, setServiceTokens }: ServiceTok
|
||||
|
||||
return (
|
||||
<div className="table-container w-full bg-bunker rounded-md mb-6 border border-mineshaft-700 relative mt-1">
|
||||
<div className="absolute rounded-t-md w-full h-12 bg-white/5"></div>
|
||||
<div className="absolute rounded-t-md w-full h-12 bg-white/5" />
|
||||
<table className="w-full my-1">
|
||||
<thead className="text-bunker-300 text-sm font-light">
|
||||
<tr>
|
||||
@@ -41,49 +40,47 @@ const ServiceTokenTable = ({ data, workspaceName, setServiceTokens }: ServiceTok
|
||||
<th className="text-left pl-6 pt-2.5 pb-2">PROJECT</th>
|
||||
<th className="text-left pl-6 pt-2.5 pb-2">ENVIRONMENT</th>
|
||||
<th className="text-left pl-6 pt-2.5 pb-2">VAILD UNTIL</th>
|
||||
<th></th>
|
||||
<th aria-label="button" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data?.length > 0 ? (
|
||||
data?.map((row) => {
|
||||
return (
|
||||
<tr
|
||||
key={guidGenerator()}
|
||||
className="bg-bunker-800 hover:bg-bunker-800/5 duration-100"
|
||||
>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.name}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{workspaceName}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.environment}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{new Date(row.expiresAt).toUTCString()}
|
||||
</td>
|
||||
<td className="py-2 border-mineshaft-700 border-t">
|
||||
<div className="opacity-50 hover:opacity-100 duration-200 flex items-center">
|
||||
<Button
|
||||
onButtonPressed={() => {
|
||||
deleteServiceToken({ serviceTokenId: row._id} );
|
||||
setServiceTokens(data.filter(token => token._id != row._id));
|
||||
createNotification({
|
||||
text: `'${row.name}' token has been revoked.`,
|
||||
type: 'error'
|
||||
});
|
||||
}}
|
||||
color="red"
|
||||
size="icon-sm"
|
||||
icon={faX}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
data?.map((row) => (
|
||||
<tr
|
||||
key={guidGenerator()}
|
||||
className="bg-bunker-800 hover:bg-bunker-800/5 duration-100"
|
||||
>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.name}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{workspaceName}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.environment}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{new Date(row.expiresAt).toUTCString()}
|
||||
</td>
|
||||
<td className="py-2 border-mineshaft-700 border-t">
|
||||
<div className="opacity-50 hover:opacity-100 duration-200 flex items-center">
|
||||
<Button
|
||||
onButtonPressed={() => {
|
||||
deleteServiceToken({ serviceTokenId: row._id });
|
||||
setServiceTokens(data.filter((token) => token._id !== row._id));
|
||||
createNotification({
|
||||
text: `'${row.name}' token has been revoked.`,
|
||||
type: 'error'
|
||||
});
|
||||
}}
|
||||
color="red"
|
||||
size="icon-sm"
|
||||
icon={faX}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr>
|
||||
<td colSpan={4} className="text-center pt-7 pb-5 text-bunker-300 text-sm">
|
||||
|
||||
@@ -1,241 +0,0 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { faX } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import deleteUserFromOrganization from '~/pages/api/organization/deleteUserFromOrganization';
|
||||
import changeUserRoleInWorkspace from '~/pages/api/workspace/changeUserRoleInWorkspace';
|
||||
import deleteUserFromWorkspace from '~/pages/api/workspace/deleteUserFromWorkspace';
|
||||
import getLatestFileKey from '~/pages/api/workspace/getLatestFileKey';
|
||||
import uploadKeys from '~/pages/api/workspace/uploadKeys';
|
||||
|
||||
import guidGenerator from '../../utilities/randomId';
|
||||
import Button from '../buttons/Button';
|
||||
import Listbox from '../Listbox';
|
||||
|
||||
const {
|
||||
decryptAssymmetric,
|
||||
encryptAssymmetric
|
||||
} = require('../../utilities/cryptography/crypto');
|
||||
const nacl = require('tweetnacl');
|
||||
nacl.util = require('tweetnacl-util');
|
||||
|
||||
const roles = ['admin', 'user'];
|
||||
|
||||
/**
|
||||
* This is the component that we utilize for the user table - in future, can reuse it for some other purposes too.
|
||||
* #TODO: add the possibility of choosing and doing operations on multiple users.
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
const UserTable = ({
|
||||
userData,
|
||||
changeData,
|
||||
myUser,
|
||||
filter,
|
||||
resendInvite,
|
||||
isOrg,
|
||||
onClick,
|
||||
deleteUser,
|
||||
setUserIdToBeDeleted
|
||||
}) => {
|
||||
const [roleSelected, setRoleSelected] = useState(
|
||||
Array(userData?.length).fill(userData.map((user) => user.role))
|
||||
);
|
||||
const router = useRouter();
|
||||
const [myRole, setMyRole] = useState('member');
|
||||
|
||||
// Delete the row in the table (e.g. a user)
|
||||
// #TODO: Add a pop-up that warns you that the user is going to be deleted.
|
||||
const handleDelete = (membershipId, index, e) => {
|
||||
// setUserIdToBeDeleted(userId);
|
||||
// onClick();
|
||||
if (isOrg) {
|
||||
deleteUserFromOrganization(membershipId);
|
||||
} else {
|
||||
deleteUserFromWorkspace(membershipId);
|
||||
}
|
||||
changeData(userData.filter((v, i) => i !== index));
|
||||
setRoleSelected([
|
||||
...roleSelected.slice(0, index),
|
||||
...roleSelected.slice(index + 1, userData?.length)
|
||||
]);
|
||||
};
|
||||
|
||||
// Update the rold of a certain user
|
||||
const handleRoleUpdate = (index, e) => {
|
||||
changeUserRoleInWorkspace(userData[index].membershipId, e);
|
||||
changeData([
|
||||
...userData.slice(0, index),
|
||||
...[
|
||||
{
|
||||
key: userData[index].key,
|
||||
firstName: userData[index].firstName,
|
||||
lastName: userData[index].lastName,
|
||||
email: userData[index].email,
|
||||
role: e,
|
||||
status: userData[index].status,
|
||||
userId: userData[index].userId,
|
||||
membershipId: userData[index].membershipId,
|
||||
publicKey: userData[index].publicKey
|
||||
}
|
||||
],
|
||||
...userData.slice(index + 1, userData?.length)
|
||||
]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setMyRole(userData.filter((user) => user.email == myUser)[0]?.role);
|
||||
}, [userData, myUser]);
|
||||
|
||||
const grantAccess = async (id, publicKey) => {
|
||||
let result = await getLatestFileKey({ workspaceId: router.query.id });
|
||||
|
||||
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY');
|
||||
|
||||
// assymmetrically decrypt symmetric key with local private key
|
||||
const key = decryptAssymmetric({
|
||||
ciphertext: result.latestKey.encryptedKey,
|
||||
nonce: result.latestKey.nonce,
|
||||
publicKey: result.latestKey.sender.publicKey,
|
||||
privateKey: PRIVATE_KEY
|
||||
});
|
||||
|
||||
const { ciphertext, nonce } = encryptAssymmetric({
|
||||
plaintext: key,
|
||||
publicKey: publicKey,
|
||||
privateKey: PRIVATE_KEY
|
||||
});
|
||||
|
||||
uploadKeys(router.query.id, id, ciphertext, nonce);
|
||||
router.reload();
|
||||
};
|
||||
|
||||
const deleteMembershipAndResendInvite = (email, membershipId) => {
|
||||
// deleteUserFromWorkspace(membershipId);
|
||||
resendInvite(email);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="table-container bg-bunker rounded-md mb-6 border border-mineshaft-700 relative mt-1">
|
||||
<div className="absolute rounded-t-md w-full h-[3.25rem] bg-white/5"></div>
|
||||
<table className="w-full my-0.5">
|
||||
<thead className="text-gray-400 text-sm font-light">
|
||||
<tr>
|
||||
<th className="text-left pl-6 py-3.5">FIRST NAME</th>
|
||||
<th className="text-left pl-6 py-3.5">LAST NAME</th>
|
||||
<th className="text-left pl-6 py-3.5">EMAIL</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{userData?.filter(
|
||||
(user) =>
|
||||
user.firstName?.toLowerCase().includes(filter) ||
|
||||
user.lastName?.toLowerCase().includes(filter) ||
|
||||
user.email?.toLowerCase().includes(filter)
|
||||
).length > 0 &&
|
||||
userData
|
||||
?.filter(
|
||||
(user) =>
|
||||
user.firstName?.toLowerCase().includes(filter) ||
|
||||
user.lastName?.toLowerCase().includes(filter) ||
|
||||
user.email?.toLowerCase().includes(filter)
|
||||
)
|
||||
.map((row, index) => {
|
||||
return (
|
||||
<tr
|
||||
key={guidGenerator()}
|
||||
className="bg-bunker-800 hover:bg-bunker-800/5"
|
||||
>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.firstName}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.lastName}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.email}
|
||||
</td>
|
||||
<td className="flex flex-row justify-end pr-8 py-2 border-t border-0.5 border-mineshaft-700">
|
||||
<div className="flex justify-end mr-6 w-3/4 mx-2 w-full h-full flex flex-row items-center">
|
||||
{row.status == 'granted' &&
|
||||
((myRole == 'admin' && row.role != 'owner') ||
|
||||
myRole == 'owner') &&
|
||||
myUser !== row.email ? (
|
||||
<Listbox
|
||||
selected={row.role}
|
||||
onChange={(e) => handleRoleUpdate(index, e)}
|
||||
data={
|
||||
myRole == 'owner'
|
||||
? ['owner', 'admin', 'member']
|
||||
: ['admin', 'member']
|
||||
}
|
||||
text="Role: "
|
||||
membershipId={row.membershipId}
|
||||
/>
|
||||
) : (
|
||||
row.status != 'invited' &&
|
||||
row.status != 'verified' && (
|
||||
<Listbox
|
||||
selected={row.role}
|
||||
text="Role: "
|
||||
membershipId={row.membershipId}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
{(row.status == 'invited' ||
|
||||
row.status == 'verified') && (
|
||||
<div className="w-full pl-9">
|
||||
<Button
|
||||
onButtonPressed={() =>
|
||||
deleteMembershipAndResendInvite(
|
||||
row.email,
|
||||
row.membershipId
|
||||
)
|
||||
}
|
||||
color="mineshaft"
|
||||
text="Resend Invite"
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{row.status == 'completed' && myUser !== row.email && (
|
||||
<div className="border border-mineshaft-700 rounded-md bg-white/5 hover:bg-primary text-white hover:text-black duration-200">
|
||||
<Button
|
||||
onButtonPressed={() =>
|
||||
grantAccess(row.userId, row.publicKey)
|
||||
}
|
||||
color="mineshaft"
|
||||
text="Grant Access"
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{myUser !== row.email &&
|
||||
// row.role != "admin" &&
|
||||
myRole != 'member' ? (
|
||||
<div className="opacity-50 hover:opacity-100 flex items-center">
|
||||
<Button
|
||||
onButtonPressed={(e) =>
|
||||
handleDelete(row.membershipId, index, e)
|
||||
}
|
||||
color="red"
|
||||
size="icon-sm"
|
||||
icon={faX}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-9 h-9"></div>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserTable;
|
||||
@@ -0,0 +1,218 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import deleteUserFromOrganization from '@app/pages/api/organization/deleteUserFromOrganization';
|
||||
import changeUserRoleInWorkspace from '@app/pages/api/workspace/changeUserRoleInWorkspace';
|
||||
import deleteUserFromWorkspace from '@app/pages/api/workspace/deleteUserFromWorkspace';
|
||||
import getLatestFileKey from '@app/pages/api/workspace/getLatestFileKey';
|
||||
import uploadKeys from '@app/pages/api/workspace/uploadKeys';
|
||||
import { faX } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { decryptAssymmetric, encryptAssymmetric } from '../../utilities/cryptography/crypto';
|
||||
import guidGenerator from '../../utilities/randomId';
|
||||
import Button from '../buttons/Button';
|
||||
import Listbox from '../Listbox';
|
||||
|
||||
// const roles = ['admin', 'user'];
|
||||
// TODO: Set type for this
|
||||
type Props = {
|
||||
userData: any[];
|
||||
changeData: (users: any[]) => void;
|
||||
myUser: string;
|
||||
filter: string;
|
||||
resendInvite: (email: string) => void;
|
||||
isOrg: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the component that we utilize for the user table - in future, can reuse it for some other purposes too.
|
||||
* #TODO: add the possibility of choosing and doing operations on multiple users.
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
const UserTable = ({ userData, changeData, myUser, filter, resendInvite, isOrg }: Props) => {
|
||||
const [roleSelected, setRoleSelected] = useState(
|
||||
Array(userData?.length).fill(userData.map((user) => user.role))
|
||||
);
|
||||
const router = useRouter();
|
||||
const [myRole, setMyRole] = useState('member');
|
||||
|
||||
const workspaceId = router.query.id as string;
|
||||
// Delete the row in the table (e.g. a user)
|
||||
// #TODO: Add a pop-up that warns you that the user is going to be deleted.
|
||||
const handleDelete = (membershipId: string, index: number) => {
|
||||
// setUserIdToBeDeleted(userId);
|
||||
// onClick();
|
||||
if (isOrg) {
|
||||
deleteUserFromOrganization(membershipId);
|
||||
} else {
|
||||
deleteUserFromWorkspace(membershipId);
|
||||
}
|
||||
changeData(userData.filter((v, i) => i !== index));
|
||||
setRoleSelected([
|
||||
...roleSelected.slice(0, index),
|
||||
...roleSelected.slice(index + 1, userData?.length)
|
||||
]);
|
||||
};
|
||||
|
||||
// Update the rold of a certain user
|
||||
const handleRoleUpdate = (index: number, e: string) => {
|
||||
changeUserRoleInWorkspace(userData[index].membershipId, e);
|
||||
changeData([
|
||||
...userData.slice(0, index),
|
||||
...[
|
||||
{
|
||||
key: userData[index].key,
|
||||
firstName: userData[index].firstName,
|
||||
lastName: userData[index].lastName,
|
||||
email: userData[index].email,
|
||||
role: e,
|
||||
status: userData[index].status,
|
||||
userId: userData[index].userId,
|
||||
membershipId: userData[index].membershipId,
|
||||
publicKey: userData[index].publicKey
|
||||
}
|
||||
],
|
||||
...userData.slice(index + 1, userData?.length)
|
||||
]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setMyRole(userData.filter((user) => user.email === myUser)[0]?.role);
|
||||
}, [userData, myUser]);
|
||||
|
||||
const grantAccess = async (id: string, publicKey: string) => {
|
||||
const result = await getLatestFileKey({ workspaceId });
|
||||
|
||||
const PRIVATE_KEY = localStorage.getItem('PRIVATE_KEY') as string;
|
||||
|
||||
// assymmetrically decrypt symmetric key with local private key
|
||||
const key = decryptAssymmetric({
|
||||
ciphertext: result.latestKey.encryptedKey,
|
||||
nonce: result.latestKey.nonce,
|
||||
publicKey: result.latestKey.sender.publicKey,
|
||||
privateKey: PRIVATE_KEY
|
||||
});
|
||||
|
||||
const { ciphertext, nonce } = encryptAssymmetric({
|
||||
plaintext: key,
|
||||
publicKey,
|
||||
privateKey: PRIVATE_KEY
|
||||
});
|
||||
|
||||
uploadKeys(workspaceId, id, ciphertext, nonce);
|
||||
router.reload();
|
||||
};
|
||||
|
||||
const deleteMembershipAndResendInvite = (email: string) => {
|
||||
// deleteUserFromWorkspace(membershipId);
|
||||
resendInvite(email);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="table-container bg-bunker rounded-md mb-6 border border-mineshaft-700 relative mt-1">
|
||||
<div className="absolute rounded-t-md w-full h-[3.25rem] bg-white/5" />
|
||||
<table className="w-full my-0.5">
|
||||
<thead className="text-gray-400 text-sm font-light">
|
||||
<tr>
|
||||
<th className="text-left pl-6 py-3.5">FIRST NAME</th>
|
||||
<th className="text-left pl-6 py-3.5">LAST NAME</th>
|
||||
<th className="text-left pl-6 py-3.5">EMAIL</th>
|
||||
<th aria-label="buttons" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{userData?.filter(
|
||||
(user) =>
|
||||
user.firstName?.toLowerCase().includes(filter) ||
|
||||
user.lastName?.toLowerCase().includes(filter) ||
|
||||
user.email?.toLowerCase().includes(filter)
|
||||
).length > 0 &&
|
||||
userData
|
||||
?.filter(
|
||||
(user) =>
|
||||
user.firstName?.toLowerCase().includes(filter) ||
|
||||
user.lastName?.toLowerCase().includes(filter) ||
|
||||
user.email?.toLowerCase().includes(filter)
|
||||
)
|
||||
.map((row, index) => (
|
||||
<tr key={guidGenerator()} className="bg-bunker-800 hover:bg-bunker-800/5">
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.firstName}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.lastName}
|
||||
</td>
|
||||
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
|
||||
{row.email}
|
||||
</td>
|
||||
<td className="flex flex-row justify-end pr-8 py-2 border-t border-0.5 border-mineshaft-700">
|
||||
<div className="justify-end mr-6 mx-2 w-full h-full flex flex-row items-center">
|
||||
{row.status === 'granted' &&
|
||||
((myRole === 'admin' && row.role !== 'owner') || myRole === 'owner') &&
|
||||
myUser !== row.email ? (
|
||||
<Listbox
|
||||
selected={row.role}
|
||||
onChange={(e) => handleRoleUpdate(index, e)}
|
||||
data={
|
||||
myRole === 'owner' ? ['owner', 'admin', 'member'] : ['admin', 'member']
|
||||
}
|
||||
text="Role: "
|
||||
/>
|
||||
) : (
|
||||
row.status !== 'invited' &&
|
||||
row.status !== 'verified' && (
|
||||
<Listbox
|
||||
selected={row.role}
|
||||
text="Role: "
|
||||
onChange={() => {
|
||||
throw new Error('Function not implemented.');
|
||||
}}
|
||||
data={null}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
{(row.status === 'invited' || row.status === 'verified') && (
|
||||
<div className="w-full pl-9">
|
||||
<Button
|
||||
onButtonPressed={() => deleteMembershipAndResendInvite(row.email)}
|
||||
color="mineshaft"
|
||||
text="Resend Invite"
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{row.status === 'completed' && myUser !== row.email && (
|
||||
<div className="border border-mineshaft-700 rounded-md bg-white/5 hover:bg-primary text-white hover:text-black duration-200">
|
||||
<Button
|
||||
onButtonPressed={() => grantAccess(row.userId, row.publicKey)}
|
||||
color="mineshaft"
|
||||
text="Grant Access"
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{myUser !== row.email &&
|
||||
// row.role !== "admin" &&
|
||||
myRole !== 'member' ? (
|
||||
<div className="opacity-50 hover:opacity-100 flex items-center">
|
||||
<Button
|
||||
onButtonPressed={() => handleDelete(row.membershipId, index)}
|
||||
color="red"
|
||||
size="icon-sm"
|
||||
icon={faX}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-9 h-9" />
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserTable;
|
||||
Reference in New Issue
Block a user