mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-18 06:58:36 +00:00
chore(frontend): changed source code to src folder from root
This commit is contained in:
@@ -0,0 +1,398 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import {
|
||||
faMagnifyingGlass,
|
||||
faPlus,
|
||||
faX,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import { faCheck } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
import Button from '~/components/basic/buttons/Button';
|
||||
import AddIncidentContactDialog from '~/components/basic/dialog/AddIncidentContactDialog';
|
||||
import AddUserDialog from '~/components/basic/dialog/AddUserDialog';
|
||||
import InputField from '~/components/basic/InputField';
|
||||
import UserTable from '~/components/basic/table/UserTable';
|
||||
import NavHeader from '~/components/navigation/NavHeader';
|
||||
import guidGenerator from '~/utilities/randomId';
|
||||
import { getTranslatedServerSideProps } from '~/utilities/withTranslateProps';
|
||||
|
||||
import addUserToOrg from '../../api/organization/addUserToOrg';
|
||||
import deleteIncidentContact from '../../api/organization/deleteIncidentContact';
|
||||
import getIncidentContacts from '../../api/organization/getIncidentContacts';
|
||||
import getOrganization from '../../api/organization/GetOrg';
|
||||
import getOrganizationSubscriptions from '../../api/organization/GetOrgSubscription';
|
||||
import getOrganizationUsers from '../../api/organization/GetOrgUsers';
|
||||
import renameOrg from '../../api/organization/renameOrg';
|
||||
import getUser from '../../api/user/getUser';
|
||||
import deleteWorkspace from '../../api/workspace/deleteWorkspace';
|
||||
import getWorkspaces from '../../api/workspace/getWorkspaces';
|
||||
|
||||
export default function SettingsOrg() {
|
||||
const [buttonReady, setButtonReady] = useState(false);
|
||||
const router = useRouter();
|
||||
const [orgName, setOrgName] = useState('');
|
||||
const [emailUser, setEmailUser] = useState('');
|
||||
const [workspaceToBeDeletedName, setWorkspaceToBeDeletedName] = useState('');
|
||||
const [searchUsers, setSearchUsers] = useState('');
|
||||
const [isAddIncidentContactOpen, setIsAddIncidentContactOpen] =
|
||||
useState(false);
|
||||
const [isAddUserOpen, setIsAddUserOpen] = useState(
|
||||
router.asPath.split('?')[1] == 'invite'
|
||||
);
|
||||
const [incidentContacts, setIncidentContacts] = useState<string[]>([]);
|
||||
const [searchIncidentContact, setSearchIncidentContact] = useState('');
|
||||
const [userList, setUserList] = useState<any[]>([]);
|
||||
const [personalEmail, setPersonalEmail] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
const [currentPlan, setCurrentPlan] = useState('');
|
||||
|
||||
const workspaceId = router.query.id as string;
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const orgId = localStorage.getItem('orgData.id') as string;
|
||||
const org = await getOrganization({
|
||||
orgId,
|
||||
});
|
||||
|
||||
setOrgName(org.name);
|
||||
const incidentContactsData = await getIncidentContacts(
|
||||
localStorage.getItem('orgData.id') as string
|
||||
);
|
||||
|
||||
setIncidentContacts(
|
||||
incidentContactsData?.map((contact) => contact.email)
|
||||
);
|
||||
|
||||
const user = await getUser();
|
||||
setPersonalEmail(user.email);
|
||||
|
||||
const orgUsers = await getOrganizationUsers({
|
||||
orgId,
|
||||
});
|
||||
|
||||
setUserList(
|
||||
orgUsers.map((user) => ({
|
||||
key: guidGenerator(),
|
||||
firstName: user.user?.firstName,
|
||||
lastName: user.user?.lastName,
|
||||
email: user.user?.email == null ? user.inviteEmail : user.user?.email,
|
||||
role: user?.role,
|
||||
status: user?.status,
|
||||
userId: user.user?._id,
|
||||
membershipId: user._id,
|
||||
publicKey: user.user?.publicKey,
|
||||
}))
|
||||
);
|
||||
|
||||
const subscriptions = await getOrganizationSubscriptions({
|
||||
orgId,
|
||||
});
|
||||
setCurrentPlan(subscriptions.data[0].plan.product);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
const modifyOrgName = (newName: string) => {
|
||||
setButtonReady(true);
|
||||
setOrgName(newName);
|
||||
};
|
||||
|
||||
const submitChanges = (newOrgName: string) => {
|
||||
renameOrg(localStorage.getItem('orgData.id') as string, newOrgName);
|
||||
setButtonReady(false);
|
||||
};
|
||||
|
||||
function closeAddUserModal() {
|
||||
setIsAddUserOpen(false);
|
||||
}
|
||||
|
||||
function closeAddIncidentContactModal() {
|
||||
setIsAddIncidentContactOpen(false);
|
||||
}
|
||||
|
||||
function openAddUserModal() {
|
||||
setIsAddUserOpen(true);
|
||||
}
|
||||
|
||||
function openAddIncidentContactModal() {
|
||||
setIsAddIncidentContactOpen(true);
|
||||
}
|
||||
|
||||
async function submitAddUserModal(email: string) {
|
||||
await addUserToOrg(email, localStorage.getItem('orgData.id') as string);
|
||||
setEmail('');
|
||||
setIsAddUserOpen(false);
|
||||
router.reload();
|
||||
}
|
||||
|
||||
const deleteIncidentContactFully = (incidentContact: string) => {
|
||||
setIncidentContacts(
|
||||
incidentContacts.filter((contact) => contact != incidentContact)
|
||||
);
|
||||
deleteIncidentContact(
|
||||
localStorage.getItem('orgData.id') as string,
|
||||
incidentContact
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* This function deleted a workspace.
|
||||
* It first checks if there is more than one workspace aviable. Otherwise, it doesn't delete
|
||||
* It then checks if the name of the workspace to be deleted is correct. Otherwise, it doesn't delete.
|
||||
* It then deletes the workspace and forwards the user to another aviable workspace.
|
||||
*/
|
||||
const executeDeletingWorkspace = async () => {
|
||||
const userWorkspaces = await getWorkspaces();
|
||||
|
||||
if (userWorkspaces.length > 1) {
|
||||
if (
|
||||
userWorkspaces.filter((workspace) => workspace._id == workspaceId)[0]
|
||||
.name == workspaceToBeDeletedName
|
||||
) {
|
||||
await deleteWorkspace(workspaceId);
|
||||
const userWorkspaces = await getWorkspaces();
|
||||
router.push('/dashboard/' + userWorkspaces[0]._id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='bg-bunker-800 max-h-screen flex flex-col justify-between text-white'>
|
||||
<Head>
|
||||
<title>
|
||||
{t('common:head-title', { title: t('settings-org:title') })}
|
||||
</title>
|
||||
<link rel='icon' href='/infisical.ico' />
|
||||
</Head>
|
||||
<div className='flex flex-row'>
|
||||
<div className='w-full max-h-screen pb-2 overflow-y-auto'>
|
||||
<NavHeader pageName={t('settings-org:title')} />
|
||||
<AddIncidentContactDialog
|
||||
isOpen={isAddIncidentContactOpen}
|
||||
closeModal={closeAddIncidentContactModal}
|
||||
workspaceId={workspaceId}
|
||||
incidentContacts={incidentContacts}
|
||||
setIncidentContacts={setIncidentContacts}
|
||||
/>
|
||||
<div className='flex flex-row justify-between items-center ml-6 my-8 text-xl max-w-5xl'>
|
||||
<div className='flex flex-col justify-start items-start text-3xl'>
|
||||
<p className='font-semibold mr-4 text-gray-200'>
|
||||
{t('settings-org:title')}
|
||||
</p>
|
||||
<p className='font-normal mr-4 text-gray-400 text-base'>
|
||||
{t('settings-org:description')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col ml-6 text-mineshaft-50 mr-6 max-w-5xl'>
|
||||
<div className='flex flex-col'>
|
||||
<div className='min-w-md mt-2 flex flex-col items-end pb-4'>
|
||||
<div className='bg-white/5 rounded-md px-6 py-4 flex flex-col items-start w-full mb-6'>
|
||||
<div className='max-h-28 w-full max-w-md mr-auto'>
|
||||
<p className='font-semibold mr-4 text-gray-200 text-xl mb-2'>
|
||||
{t('common:display-name')}
|
||||
</p>
|
||||
<InputField
|
||||
label=''
|
||||
// label="Organization Name"
|
||||
onChangeHandler={modifyOrgName}
|
||||
type='varName'
|
||||
value={orgName}
|
||||
placeholder=''
|
||||
isRequired
|
||||
/>
|
||||
</div>
|
||||
<div className='flex justify-start w-full'>
|
||||
<div className={`flex justify-start max-w-sm mt-4 mb-2`}>
|
||||
<Button
|
||||
text={t('common:save-changes') as string}
|
||||
onButtonPressed={() => submitChanges(orgName)}
|
||||
color='mineshaft'
|
||||
size='md'
|
||||
active={buttonReady}
|
||||
iconDisabled={faCheck}
|
||||
textDisabled={t('common:saved') as string}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='bg-white/5 rounded-md px-6 pt-6 pb-2 flex flex-col items-start w-full mb-6'>
|
||||
<p className='font-semibold mr-4 text-white text-xl'>
|
||||
{t('section-members:org-members')}
|
||||
</p>
|
||||
<p className='mr-4 text-gray-400 mt-2 mb-2'>
|
||||
{t('section-members:org-members-description')}
|
||||
</p>
|
||||
<AddUserDialog
|
||||
isOpen={isAddUserOpen}
|
||||
closeModal={closeAddUserModal}
|
||||
submitModal={submitAddUserModal}
|
||||
email={emailUser}
|
||||
workspaceId={workspaceId}
|
||||
setEmail={setEmailUser}
|
||||
currentPlan={currentPlan}
|
||||
orgName={orgName}
|
||||
/>
|
||||
{/* <DeleteUserDialog isOpen={isDeleteOpen} closeModal={closeDeleteModal} submitModal={deleteMembership} userIdToBeDeleted={userIdToBeDeleted}/> */}
|
||||
<div className='pb-1 w-full flex flex-row items-start max-w-6xl'>
|
||||
<div className='h-10 w-full bg-white/5 mt-2 flex items-center rounded-md flex-row '>
|
||||
<FontAwesomeIcon
|
||||
className='bg-white/5 rounded-l-md py-3 pl-4 pr-2 text-gray-400'
|
||||
icon={faMagnifyingGlass}
|
||||
/>
|
||||
<input
|
||||
className='pl-2 text-gray-400 rounded-r-md bg-white/5 w-full h-full outline-none'
|
||||
value={searchUsers}
|
||||
onChange={(e) => setSearchUsers(e.target.value)}
|
||||
placeholder={t('section-members:search-members') as string}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-2 ml-2 min-w-max flex flex-row items-start justify-start'>
|
||||
<Button
|
||||
text={t('section-members:add-member') as string}
|
||||
onButtonPressed={openAddUserModal}
|
||||
color='mineshaft'
|
||||
size='md'
|
||||
icon={faPlus}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{userList && (
|
||||
<div className='overflow-y-auto max-w-6xl w-full'>
|
||||
<UserTable
|
||||
userData={userList}
|
||||
changeData={setUserList}
|
||||
myUser={personalEmail}
|
||||
filter={searchUsers.toLowerCase()}
|
||||
resendInvite={submitAddUserModal}
|
||||
isOrg={true}
|
||||
// onClick={openDeleteModal}
|
||||
// deleteUser={deleteMembership}
|
||||
// setUserIdToBeDeleted={setUserIdToBeDeleted}
|
||||
className='w-full'
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='bg-white/5 rounded-md px-6 pt-6 pb-6 flex flex-col items-start w-full mb-6 mt-4'>
|
||||
<div className='flex flex-row max-w-5xl justify-between items-center w-full'>
|
||||
<div className='flex flex-col justify-between w-full max-w-3xl'>
|
||||
<p className='text-xl font-semibold mb-3 min-w-max'>
|
||||
{t('section-incident:incident-contacts')}
|
||||
</p>
|
||||
<p className='text-xs text-gray-500 mb-2 min-w-max'>
|
||||
{t('section-incident:incident-contacts-description')}
|
||||
</p>
|
||||
</div>
|
||||
<div className='mt-4 mb-2 min-w-max flex flex-row items-end justify-center'>
|
||||
<Button
|
||||
text={t('section-incident:add-contact') as string}
|
||||
onButtonPressed={openAddIncidentContactModal}
|
||||
color='mineshaft'
|
||||
size='md'
|
||||
icon={faPlus}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='h-12 w-full max-w-5xl bg-white/5 mt-2 flex items-center rounded-t-md flwex-row'>
|
||||
<FontAwesomeIcon
|
||||
className='bg-white/5 rounded-tl-md py-4 pl-4 pr-2 text-gray-400'
|
||||
icon={faMagnifyingGlass}
|
||||
/>
|
||||
<input
|
||||
className='pl-2 text-gray-400 rounded-tr-md bg-white/5 w-full h-full outline-none'
|
||||
value={searchIncidentContact}
|
||||
onChange={(e) => setSearchIncidentContact(e.target.value)}
|
||||
placeholder={t('common:search') as string}
|
||||
/>
|
||||
</div>
|
||||
{incidentContacts?.filter((email) =>
|
||||
email.includes(searchIncidentContact)
|
||||
).length > 0 ? (
|
||||
incidentContacts
|
||||
.filter((email) => email.includes(searchIncidentContact))
|
||||
.map((contact) => (
|
||||
<div
|
||||
key={guidGenerator()}
|
||||
className='flex flex-row items-center justify-between max-w-5xl px-4 py-3 hover:bg-white/5 border-t border-gray-600 w-full'
|
||||
>
|
||||
<p className='text-gray-300'>{contact}</p>
|
||||
<div className='opacity-50 hover:opacity-100 duration-200'>
|
||||
<Button
|
||||
onButtonPressed={() =>
|
||||
deleteIncidentContactFully(contact)
|
||||
}
|
||||
color='red'
|
||||
size='icon-sm'
|
||||
icon={faX}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className='w-full flex flex-row justify-center mt-6 max-w-4xl ml-6'>
|
||||
<p className='text-gray-400'>
|
||||
{t('section-incident:no-incident-contacts')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* <div className="border-l border-red pb-4 pl-6 flex flex-col items-start flex flex-col items-start w-full mb-6 mt-4 pt-2 max-w-6xl">
|
||||
<p className="text-xl font-bold text-red">
|
||||
Danger Zone
|
||||
</p>
|
||||
<p className="mt-4 text-md text-gray-400">
|
||||
As soon as you delete an organization, you will
|
||||
not be able to undo it. This will immediately
|
||||
remove all organization members and cancel your
|
||||
subscription. If you still want to do that,
|
||||
please enter the name of the organization below.
|
||||
</p>
|
||||
<div className="max-h-28 w-full max-w-xl mr-auto mt-8 max-w-xl">
|
||||
<InputField
|
||||
label="Organization to be Deleted"
|
||||
onChangeHandler={
|
||||
setWorkspaceToBeDeletedName
|
||||
}
|
||||
type="varName"
|
||||
value={workspaceToBeDeletedName}
|
||||
placeholder=""
|
||||
isRequired
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="mt-6 w-full max-w-xl inline-flex justify-center rounded-md border border-transparent bg-gray-800 px-4 py-2.5 text-sm font-medium text-gray-400 hover:bg-red hover:text-white hover:font-bold hover:text-semibold duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
onClick={executeDeletingWorkspace}
|
||||
>
|
||||
Delete Project
|
||||
</button>
|
||||
<p className="mt-0.5 ml-1 text-xs text-gray-500">
|
||||
Note: You can only delete a project in case you
|
||||
have more than one.
|
||||
</p>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
SettingsOrg.requireAuth = true;
|
||||
|
||||
export const getServerSideProps = getTranslatedServerSideProps([
|
||||
'settings',
|
||||
'settings-org',
|
||||
'section-incident',
|
||||
'section-members',
|
||||
]);
|
||||
Reference in New Issue
Block a user