fix: update create org to properly handle tx (super admin), remove sub org incidents, remove clipping animation container for project templates ui

This commit is contained in:
Scott Wilson
2025-10-20 16:36:46 -07:00
parent 339e00dedf
commit 0b5c823390
6 changed files with 185 additions and 235 deletions
@@ -284,7 +284,7 @@ export const licenseServiceFactory = ({
}; };
const updateSubscriptionOrgMemberCount = async (orgId: string, tx?: Knex) => { const updateSubscriptionOrgMemberCount = async (orgId: string, tx?: Knex) => {
const org = await orgDAL.findRootOrgDetails(orgId); const org = await orgDAL.findRootOrgDetails(orgId, tx);
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` }); if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
const rootOrgId = org.id; const rootOrgId = org.id;
+2 -3
View File
@@ -705,10 +705,9 @@ export const orgDALFactory = (db: TDbClient) => {
} }
}; };
const findRootOrgDetails = async (orgId: string): Promise<TOrganizations | undefined> => { const findRootOrgDetails = async (orgId: string, tx?: Knex): Promise<TOrganizations | undefined> => {
try { try {
const org = await db const org = await (tx ?? db.replicaNode())(TableName.Organization)
.replicaNode()(TableName.Organization)
.select(selectAllTableCols(TableName.Organization)) .select(selectAllTableCols(TableName.Organization))
.where( .where(
"id", "id",
+1 -1
View File
@@ -679,7 +679,7 @@ export const orgServiceFactory = ({
const organization = await (trx ? createOrg(trx) : orgDAL.transaction(createOrg)); const organization = await (trx ? createOrg(trx) : orgDAL.transaction(createOrg));
await licenseService.updateSubscriptionOrgMemberCount(organization.id); await licenseService.updateSubscriptionOrgMemberCount(organization.id, trx);
return organization; return organization;
}; };
@@ -11,7 +11,7 @@ export const OrgGeneralTab = () => {
return ( return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-6"> <div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-6">
{isSubOrganization ? <SubOrgNameChangeSection /> : <OrgNameChangeSection />} {isSubOrganization ? <SubOrgNameChangeSection /> : <OrgNameChangeSection />}
<OrgIncidentContactsSection /> {!isSubOrganization && <OrgIncidentContactsSection />}
{hasOrgRole(OrgMembershipRole.Admin) && <OrgDeleteSection />} {hasOrgRole(OrgMembershipRole.Admin) && <OrgDeleteSection />}
</div> </div>
); );
@@ -70,154 +70,129 @@ export const ProjectTemplateRolesSection = ({ projectTemplate, isInfisicalTempla
return ( return (
<div className="relative"> <div className="relative">
<AnimatePresence> {popUp?.editRole.isOpen ? (
{popUp?.editRole.isOpen ? ( <ProjectTemplateEditRoleForm
<motion.div onGoBack={() => handlePopUpClose("editRole")}
key="edit-role" projectTemplate={projectTemplate}
transition={{ duration: 0.3 }} role={editRole}
initial={{ opacity: 0, translateX: 30 }} isDisabled={
animate={{ opacity: 1, translateX: 0 }} permission.cannot(OrgPermissionActions.Edit, OrgPermissionSubjects.ProjectTemplates) ||
exit={{ opacity: 0, translateX: 30 }} (editRole && !isCustomProjectRole(editRole.slug))
className="absolute min-h-40 w-full" }
> />
<ProjectTemplateEditRoleForm ) : (
onGoBack={() => handlePopUpClose("editRole")} <div className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
projectTemplate={projectTemplate} <div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
role={editRole} <div>
isDisabled={ <h2 className="text-lg font-medium">Project Roles</h2>
permission.cannot( <p className="text-sm text-mineshaft-400">
OrgPermissionActions.Edit, {isInfisicalTemplate
OrgPermissionSubjects.ProjectTemplates ? "Click a role to view the associated permissions"
) || : "Add, edit and remove roles for this project template"}
(editRole && !isCustomProjectRole(editRole.slug)) </p>
} </div>
/> {!isInfisicalTemplate && (
<div className="h-4 w-full" /> <OrgPermissionCan
</motion.div> I={OrgPermissionActions.Edit}
) : ( a={OrgPermissionSubjects.ProjectTemplates}
<motion.div >
key="role-list" {(isAllowed) => (
transition={{ duration: 0.3 }} <Button
initial={{ opacity: 0, translateX: 0 }} onClick={() => {
animate={{ opacity: 1, translateX: 0 }} handlePopUpOpen("editRole");
exit={{ opacity: 0, translateX: -30 }} }}
className="absolute w-full" colorSchema="primary"
> className="ml-auto"
<div className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"> variant="outline_bg"
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4"> leftIcon={<FontAwesomeIcon icon={faPlus} />}
<div> isDisabled={!isAllowed}
<h2 className="text-lg font-medium">Project Roles</h2>
<p className="text-sm text-mineshaft-400">
{isInfisicalTemplate
? "Click a role to view the associated permissions"
: "Add, edit and remove roles for this project template"}
</p>
</div>
{!isInfisicalTemplate && (
<OrgPermissionCan
I={OrgPermissionActions.Edit}
a={OrgPermissionSubjects.ProjectTemplates}
> >
{(isAllowed) => ( Add Role
<Button </Button>
onClick={() => {
handlePopUpOpen("editRole");
}}
colorSchema="primary"
className="ml-auto"
variant="outline_bg"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
isDisabled={!isAllowed}
>
Add Role
</Button>
)}
</OrgPermissionCan>
)} )}
</div> </OrgPermissionCan>
<div className="py-4"> )}
<TableContainer> </div>
<Table> <div className="py-4">
<THead> <TableContainer>
<Tr> <Table>
<Th>Name</Th> <THead>
<Th>Slug</Th> <Tr>
<Th className="w-5" /> <Th>Name</Th>
</Tr> <Th>Slug</Th>
</THead> <Th className="w-5" />
<TBody> </Tr>
{roles.length ? ( </THead>
roles.map((role) => { <TBody>
return ( {roles.length ? (
<Tr roles.map((role) => {
key={role.slug} return (
className="group w-full cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700" <Tr
role="button" key={role.slug}
tabIndex={0} className="group w-full cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
onKeyDown={(evt) => { role="button"
if (evt.key === "Enter") { tabIndex={0}
handlePopUpOpen("editRole", role); onKeyDown={(evt) => {
} if (evt.key === "Enter") {
}} handlePopUpOpen("editRole", role);
onClick={() => handlePopUpOpen("editRole", role)} }
> }}
<Td>{role.name}</Td> onClick={() => handlePopUpOpen("editRole", role)}
<Td>{role.slug}</Td> >
<Td> <Td>{role.name}</Td>
{isCustomProjectRole(role.slug) && ( <Td>{role.slug}</Td>
<div className="flex space-x-2 opacity-0 transition-opacity duration-300 group-hover:opacity-100"> <Td>
<OrgPermissionCan {isCustomProjectRole(role.slug) && (
I={OrgPermissionActions.Edit} <div className="flex space-x-2 opacity-0 transition-opacity duration-300 group-hover:opacity-100">
a={OrgPermissionSubjects.ProjectTemplates} <OrgPermissionCan
renderTooltip I={OrgPermissionActions.Edit}
allowedLabel="Remove Role" a={OrgPermissionSubjects.ProjectTemplates}
renderTooltip
allowedLabel="Remove Role"
>
{(isAllowed) => (
<IconButton
colorSchema="danger"
ariaLabel="delete-icon"
variant="plain"
className="group relative"
isDisabled={!isAllowed}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
handlePopUpOpen("removeRole", role);
}}
> >
{(isAllowed) => ( <FontAwesomeIcon icon={faTrash} />
<IconButton </IconButton>
colorSchema="danger" )}
ariaLabel="delete-icon" </OrgPermissionCan>
variant="plain" </div>
className="group relative" )}
isDisabled={!isAllowed}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
handlePopUpOpen("removeRole", role);
}}
>
<FontAwesomeIcon icon={faTrash} />
</IconButton>
)}
</OrgPermissionCan>
</div>
)}
</Td>
</Tr>
);
})
) : (
<Tr>
<Td colSpan={2}>
<EmptyState title="No roles assigned to template" icon={faUnlock} />
</Td> </Td>
</Tr> </Tr>
)} );
</TBody> })
</Table> ) : (
</TableContainer> <Tr>
</div> <Td colSpan={2}>
<DeleteActionModal <EmptyState title="No roles assigned to template" icon={faUnlock} />
isOpen={popUp.removeRole.isOpen} </Td>
deleteKey="remove" </Tr>
title={`Are you sure you want to remove the role ${roleToDelete?.slug}?`} )}
onChange={(isOpen) => handlePopUpToggle("removeRole", isOpen)} </TBody>
onDeleteApproved={() => handleRemoveRole(roleToDelete?.slug)} </Table>
/> </TableContainer>
</div> </div>
<div className="h-4 w-full" /> <DeleteActionModal
</motion.div> isOpen={popUp.removeRole.isOpen}
)} deleteKey="remove"
</AnimatePresence> title={`Are you sure you want to remove the role ${roleToDelete?.slug}?`}
onChange={(isOpen) => handlePopUpToggle("removeRole", isOpen)}
onDeleteApproved={() => handleRemoveRole(roleToDelete?.slug)}
/>
</div>
)}
</div> </div>
); );
}; };
@@ -1,7 +1,6 @@
import { useState } from "react"; import { useState } from "react";
import { faArrowUpRightFromSquare, faBookOpen, faPlus } from "@fortawesome/free-solid-svg-icons"; import { faArrowUpRightFromSquare, faBookOpen, faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { AnimatePresence, motion } from "framer-motion";
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal"; import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
import { OrgPermissionCan } from "@app/components/permissions"; import { OrgPermissionCan } from "@app/components/permissions";
@@ -25,93 +24,70 @@ export const ProjectTemplatesSection = () => {
return ( return (
<div className="relative"> <div className="relative">
<AnimatePresence> {editTemplate ? (
{editTemplate ? ( <EditProjectTemplateSection template={editTemplate} onBack={() => setEditTemplate(null)} />
<motion.div ) : (
key="edit-project-template" <div>
transition={{ duration: 0.3 }} <p className="mb-6 font-inter text-bunker-300">
initial={{ opacity: 0, translateX: 30 }} Create and configure templates with predefined roles and environments to streamline
animate={{ opacity: 1, translateX: 0 }} project setup
exit={{ opacity: 0, translateX: 30 }} </p>
className="absolute min-h-40 w-full" <div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
> <div className="mb-4 flex items-start">
<EditProjectTemplateSection <p className="text-xl font-medium text-mineshaft-100">Project Templates</p>
template={editTemplate} <a
onBack={() => setEditTemplate(null)} target="_blank"
/> rel="noopener noreferrer"
</motion.div> href="https://infisical.com/docs/documentation/platform/project-templates"
) : ( >
<motion.div <div className="mt-[0.32rem] ml-2 inline-block rounded-md bg-yellow/20 px-1.5 text-sm text-yellow opacity-80 hover:opacity-100">
key="project-templates-list" <FontAwesomeIcon icon={faBookOpen} className="mr-1.5" />
transition={{ duration: 0.3 }} <span>Docs</span>
initial={{ opacity: 0, translateX: 30 }} <FontAwesomeIcon
animate={{ opacity: 1, translateX: 0 }} icon={faArrowUpRightFromSquare}
exit={{ opacity: 0, translateX: 30 }} className="mb-[0.07rem] ml-1.5 text-[10px]"
className="absolute min-h-40 w-full" />
>
<div>
<p className="mb-6 font-inter text-bunker-300">
Create and configure templates with predefined roles and environments to streamline
project setup
</p>
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-4 flex items-start">
<p className="text-xl font-medium text-mineshaft-100">Project Templates</p>
<a
target="_blank"
rel="noopener noreferrer"
href="https://infisical.com/docs/documentation/platform/project-templates"
>
<div className="mt-[0.32rem] ml-2 inline-block rounded-md bg-yellow/20 px-1.5 text-sm text-yellow opacity-80 hover:opacity-100">
<FontAwesomeIcon icon={faBookOpen} className="mr-1.5" />
<span>Docs</span>
<FontAwesomeIcon
icon={faArrowUpRightFromSquare}
className="mb-[0.07rem] ml-1.5 text-[10px]"
/>
</div>
</a>
<OrgPermissionCan
I={OrgPermissionActions.Create}
a={OrgPermissionSubjects.ProjectTemplates}
>
{(isAllowed) => (
<Button
colorSchema="secondary"
type="submit"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => {
if (!subscription?.projectTemplates) {
handlePopUpOpen("upgradePlan");
return;
}
handlePopUpOpen("addTemplate");
}}
isDisabled={!isAllowed}
className="ml-auto"
>
Add Template
</Button>
)}
</OrgPermissionCan>
</div> </div>
<ProjectTemplatesTable onEdit={setEditTemplate} /> </a>
<ProjectTemplateDetailsModal <OrgPermissionCan
onComplete={(template) => setEditTemplate(template)} I={OrgPermissionActions.Create}
isOpen={popUp.addTemplate.isOpen} a={OrgPermissionSubjects.ProjectTemplates}
onOpenChange={(isOpen) => handlePopUpToggle("addTemplate", isOpen)} >
/> {(isAllowed) => (
<UpgradePlanModal <Button
isOpen={popUp.upgradePlan.isOpen} colorSchema="secondary"
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)} type="submit"
text="You can create project templates if you switch to Infisical's Enterprise plan." leftIcon={<FontAwesomeIcon icon={faPlus} />}
/> onClick={() => {
</div> if (!subscription?.projectTemplates) {
handlePopUpOpen("upgradePlan");
return;
}
handlePopUpOpen("addTemplate");
}}
isDisabled={!isAllowed}
className="ml-auto"
>
Add Template
</Button>
)}
</OrgPermissionCan>
</div> </div>
</motion.div> <ProjectTemplatesTable onEdit={setEditTemplate} />
)} <ProjectTemplateDetailsModal
</AnimatePresence> onComplete={(template) => setEditTemplate(template)}
isOpen={popUp.addTemplate.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("addTemplate", isOpen)}
/>
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text="You can create project templates if you switch to Infisical's Enterprise plan."
/>
</div>
</div>
)}
</div> </div>
); );
}; };