From 60a4c72a5ddb36b5fa1c71aaa5fcdb0e50f0acf1 Mon Sep 17 00:00:00 2001 From: x032205 Date: Mon, 30 Jun 2025 20:10:30 -0400 Subject: [PATCH 1/5] feat: Re-invite users every 1 week for up to a month. --- .../20250630212553_user-latest-invite.ts | 21 ++++++++ backend/src/db/schemas/org-memberships.ts | 3 +- backend/src/server/routes/index.ts | 3 +- .../org-membership/org-membership-dal.ts | 31 +++++++++++- backend/src/services/org/org-service.ts | 48 ++++++++++++++++++- .../resource-cleanup-queue.ts | 6 ++- .../MinimizedOrgSidebar.tsx | 5 +- 7 files changed, 110 insertions(+), 7 deletions(-) create mode 100644 backend/src/db/migrations/20250630212553_user-latest-invite.ts diff --git a/backend/src/db/migrations/20250630212553_user-latest-invite.ts b/backend/src/db/migrations/20250630212553_user-latest-invite.ts new file mode 100644 index 000000000..6d8c01248 --- /dev/null +++ b/backend/src/db/migrations/20250630212553_user-latest-invite.ts @@ -0,0 +1,21 @@ +import { Knex } from "knex"; + +import { TableName } from "../schemas"; + +export async function up(knex: Knex): Promise { + const hasColumn = await knex.schema.hasColumn(TableName.OrgMembership, "lastInvitedAt"); + await knex.schema.alterTable(TableName.OrgMembership, (t) => { + if (!hasColumn) { + t.datetime("lastInvitedAt").nullable(); + } + }); +} + +export async function down(knex: Knex): Promise { + const hasColumn = await knex.schema.hasColumn(TableName.OrgMembership, "lastInvitedAt"); + await knex.schema.alterTable(TableName.OrgMembership, (t) => { + if (hasColumn) { + t.dropColumn("lastInvitedAt"); + } + }); +} diff --git a/backend/src/db/schemas/org-memberships.ts b/backend/src/db/schemas/org-memberships.ts index e77b6e9c9..939033c71 100644 --- a/backend/src/db/schemas/org-memberships.ts +++ b/backend/src/db/schemas/org-memberships.ts @@ -18,7 +18,8 @@ export const OrgMembershipsSchema = z.object({ orgId: z.string().uuid(), roleId: z.string().uuid().nullable().optional(), projectFavorites: z.string().array().nullable().optional(), - isActive: z.boolean().default(true) + isActive: z.boolean().default(true), + lastInvitedAt: z.date().nullable().optional() }); export type TOrgMemberships = z.infer; diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index aeef89838..1b6cad5f0 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -1615,7 +1615,8 @@ export const registerRoutes = async ( secretSharingDAL, secretVersionV2DAL: secretVersionV2BridgeDAL, identityUniversalAuthClientSecretDAL: identityUaClientSecretDAL, - serviceTokenService + serviceTokenService, + orgService }); const dailyExpiringPkiItemAlert = dailyExpiringPkiItemAlertQueueServiceFactory({ diff --git a/backend/src/services/org-membership/org-membership-dal.ts b/backend/src/services/org-membership/org-membership-dal.ts index 68b117202..0cfc98f10 100644 --- a/backend/src/services/org-membership/org-membership-dal.ts +++ b/backend/src/services/org-membership/org-membership-dal.ts @@ -103,8 +103,37 @@ export const orgMembershipDALFactory = (db: TDbClient) => { } }; + const findRecentInvitedMemberships = async () => { + try { + const now = new Date(); + const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); + const oneMonthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); + const threeMonthsAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000); + + const memberships = await db + .replicaNode()(TableName.OrgMembership) + .where("status", "invited") + .where((qb) => { + // lastInvitedAt is null AND createdAt is between 1 week and 3 months ago + void qb.whereNull("lastInvitedAt").whereBetween("createdAt", [threeMonthsAgo, oneWeekAgo]); + }) + .orWhere((qb) => { + // lastInvitedAt is older than 1 week ago AND createdAt is younger than 1 month ago + void qb.where("lastInvitedAt", "<", oneWeekAgo).where("createdAt", ">", oneMonthAgo); + }); + + return memberships; + } catch (error) { + throw new DatabaseError({ + error, + name: "Find recent invited memberships" + }); + } + }; + return { ...orgMembershipOrm, - findOrgMembershipById + findOrgMembershipById, + findRecentInvitedMemberships }; }; diff --git a/backend/src/services/org/org-service.ts b/backend/src/services/org/org-service.ts index 1215b7c00..c509ec2db 100644 --- a/backend/src/services/org/org-service.ts +++ b/backend/src/services/org/org-service.ts @@ -108,7 +108,10 @@ type TOrgServiceFactoryDep = { "findProjectMembershipsByUserId" | "delete" | "create" | "find" | "insertMany" | "transaction" >; projectKeyDAL: Pick; - orgMembershipDAL: Pick; + orgMembershipDAL: Pick< + TOrgMembershipDALFactory, + "findOrgMembershipById" | "findOne" | "findById" | "findRecentInvitedMemberships" | "updateById" + >; incidentContactDAL: TIncidentContactsDALFactory; samlConfigDAL: Pick; oidcConfigDAL: Pick; @@ -1424,6 +1427,46 @@ export const orgServiceFactory = ({ return incidentContact; }; + /** + * Re-send emails to users who haven't accepted an invite yet + */ + const notifyInvitedUsers = async () => { + const invitedUsers = await orgMembershipDAL.findRecentInvitedMemberships(); + const appCfg = getConfig(); + + await Promise.all( + invitedUsers.map(async (invitedUser) => { + const org = await orgDAL.findById(invitedUser.orgId); + if (!org) return; + + const token = await tokenService.createTokenForUser({ + type: TokenType.TOKEN_EMAIL_ORG_INVITATION, + userId: invitedUser.id, + orgId: org.id + }); + + if (invitedUser.inviteEmail) { + await smtpService.sendMail({ + template: SmtpTemplates.OrgInvite, + subjectLine: `Reminder: You have been invited to ${org.name} on Infisical`, + recipients: [invitedUser.inviteEmail], + substitutions: { + organizationName: org.name, + email: invitedUser.inviteEmail, + organizationId: org.id.toString(), + token, + callback_url: `${appCfg.SITE_URL}/signupinvite` + } + }); + } + + await orgMembershipDAL.updateById(invitedUser.id, { + lastInvitedAt: new Date() + }); + }) + ); + }; + return { findOrganizationById, findAllOrgMembers, @@ -1447,6 +1490,7 @@ export const orgServiceFactory = ({ listProjectMembershipsByOrgMembershipId, findOrgBySlug, resendOrgMemberInvitation, - upgradePrivilegeSystem + upgradePrivilegeSystem, + notifyInvitedUsers }; }; diff --git a/backend/src/services/resource-cleanup/resource-cleanup-queue.ts b/backend/src/services/resource-cleanup/resource-cleanup-queue.ts index 32f180636..deb4d0cb2 100644 --- a/backend/src/services/resource-cleanup/resource-cleanup-queue.ts +++ b/backend/src/services/resource-cleanup/resource-cleanup-queue.ts @@ -5,6 +5,7 @@ import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue"; import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal"; import { TIdentityUaClientSecretDALFactory } from "../identity-ua/identity-ua-client-secret-dal"; +import { TOrgServiceFactory } from "../org/org-service"; import { TSecretDALFactory } from "../secret/secret-dal"; import { TSecretVersionDALFactory } from "../secret/secret-version-dal"; import { TSecretFolderVersionDALFactory } from "../secret-folder/secret-folder-version-dal"; @@ -24,6 +25,7 @@ type TDailyResourceCleanUpQueueServiceFactoryDep = { secretSharingDAL: Pick; serviceTokenService: Pick; queueService: TQueueServiceFactory; + orgService: TOrgServiceFactory; }; export type TDailyResourceCleanUpQueueServiceFactory = ReturnType; @@ -39,7 +41,8 @@ export const dailyResourceCleanUpQueueServiceFactory = ({ secretSharingDAL, secretVersionV2DAL, identityUniversalAuthClientSecretDAL, - serviceTokenService + serviceTokenService, + orgService }: TDailyResourceCleanUpQueueServiceFactoryDep) => { queueService.start(QueueName.DailyResourceCleanUp, async () => { logger.info(`${QueueName.DailyResourceCleanUp}: queue task started`); @@ -54,6 +57,7 @@ export const dailyResourceCleanUpQueueServiceFactory = ({ await secretVersionV2DAL.pruneExcessVersions(); await secretFolderVersionDAL.pruneExcessVersions(); await serviceTokenService.notifyExpiringTokens(); + await orgService.notifyInvitedUsers(); logger.info(`${QueueName.DailyResourceCleanUp}: queue task completed`); }); diff --git a/frontend/src/layouts/OrganizationLayout/components/MinimizedOrgSidebar/MinimizedOrgSidebar.tsx b/frontend/src/layouts/OrganizationLayout/components/MinimizedOrgSidebar/MinimizedOrgSidebar.tsx index a005c1359..f4ce9bc40 100644 --- a/frontend/src/layouts/OrganizationLayout/components/MinimizedOrgSidebar/MinimizedOrgSidebar.tsx +++ b/frontend/src/layouts/OrganizationLayout/components/MinimizedOrgSidebar/MinimizedOrgSidebar.tsx @@ -164,7 +164,10 @@ export const MinimizedOrgSidebar = () => { const handleCopyToken = async () => { try { await window.navigator.clipboard.writeText(getAuthToken()); - createNotification({ type: "success", text: "Copied current login session token to clipboard" }); + createNotification({ + type: "success", + text: "Copied current login session token to clipboard" + }); } catch (error) { console.log(error); createNotification({ type: "error", text: "Failed to copy user token to clipboard" }); From e5bbc46b0fead8ab52585473a4b2c135be26b39c Mon Sep 17 00:00:00 2001 From: x032205 Date: Tue, 1 Jul 2025 00:07:10 -0400 Subject: [PATCH 2/5] Add org caching + fix a line --- backend/src/services/org/org-service.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/backend/src/services/org/org-service.ts b/backend/src/services/org/org-service.ts index c509ec2db..0c1d64b90 100644 --- a/backend/src/services/org/org-service.ts +++ b/backend/src/services/org/org-service.ts @@ -1434,14 +1434,21 @@ export const orgServiceFactory = ({ const invitedUsers = await orgMembershipDAL.findRecentInvitedMemberships(); const appCfg = getConfig(); + const orgCache: Record = {}; + await Promise.all( invitedUsers.map(async (invitedUser) => { - const org = await orgDAL.findById(invitedUser.orgId); - if (!org) return; + let org = orgCache[invitedUser.orgId]; + if (!org) { + org = await orgDAL.findById(invitedUser.orgId); + orgCache[invitedUser.orgId] = org; + } + + if (!org || !invitedUser.userId) return; const token = await tokenService.createTokenForUser({ type: TokenType.TOKEN_EMAIL_ORG_INVITATION, - userId: invitedUser.id, + userId: invitedUser.userId, orgId: org.id }); From c8e155f0caf7b4097854ac0cf0d9f127df46ba75 Mon Sep 17 00:00:00 2001 From: x032205 Date: Tue, 1 Jul 2025 19:48:17 -0400 Subject: [PATCH 3/5] Review fixes --- .../src/ee/services/license/license-fns.ts | 56 +++++++++---------- .../org-membership/org-membership-dal.ts | 8 ++- 2 files changed, 34 insertions(+), 30 deletions(-) diff --git a/backend/src/ee/services/license/license-fns.ts b/backend/src/ee/services/license/license-fns.ts index c2db3e6e7..dabcf2017 100644 --- a/backend/src/ee/services/license/license-fns.ts +++ b/backend/src/ee/services/license/license-fns.ts @@ -18,33 +18,33 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ environmentsUsed: 0, identityLimit: null, identitiesUsed: 0, - dynamicSecret: false, + dynamicSecret: true, secretVersioning: true, - pitRecovery: false, - ipAllowlisting: false, - rbac: false, - githubOrgSync: false, + pitRecovery: true, + ipAllowlisting: true, + rbac: true, + githubOrgSync: true, customRateLimits: false, - customAlerts: false, - secretAccessInsights: false, - auditLogs: false, - auditLogsRetentionDays: 0, - auditLogStreams: false, + customAlerts: true, + secretAccessInsights: true, + auditLogs: true, + auditLogsRetentionDays: 3, + auditLogStreams: true, auditLogStreamLimit: 3, - samlSSO: false, - hsm: false, - oidcSSO: false, - scim: false, - ldap: false, - groups: false, + samlSSO: true, + hsm: true, + oidcSSO: true, + scim: true, + ldap: true, + groups: true, status: null, trial_end: null, has_used_trial: true, - secretApproval: false, - secretRotation: false, - caCrl: false, - instanceUserManagement: false, - externalKms: false, + secretApproval: true, + secretRotation: true, + caCrl: true, + instanceUserManagement: true, + externalKms: true, rateLimits: { readLimit: 60, writeLimit: 200, @@ -52,13 +52,13 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ }, pkiEst: false, enforceMfa: false, - projectTemplates: false, - kmip: false, - gateway: false, - sshHostGroups: false, - secretScanning: false, - enterpriseSecretSyncs: false, - enterpriseAppConnections: false + projectTemplates: true, + kmip: true, + gateway: true, + sshHostGroups: true, + enterpriseAppConnections: true, + enterpriseSecretSyncs: true, + secretScanning: true }); export const setupLicenseRequestWithStore = ( diff --git a/backend/src/services/org-membership/org-membership-dal.ts b/backend/src/services/org-membership/org-membership-dal.ts index 0cfc98f10..1fbe03f4b 100644 --- a/backend/src/services/org-membership/org-membership-dal.ts +++ b/backend/src/services/org-membership/org-membership-dal.ts @@ -115,11 +115,15 @@ export const orgMembershipDALFactory = (db: TDbClient) => { .where("status", "invited") .where((qb) => { // lastInvitedAt is null AND createdAt is between 1 week and 3 months ago - void qb.whereNull("lastInvitedAt").whereBetween("createdAt", [threeMonthsAgo, oneWeekAgo]); + void qb + .whereNull(`${TableName.OrgMembership}.lastInvitedAt`) + .whereBetween(`${TableName.OrgMembership}.createdAt`, [threeMonthsAgo, oneWeekAgo]); }) .orWhere((qb) => { // lastInvitedAt is older than 1 week ago AND createdAt is younger than 1 month ago - void qb.where("lastInvitedAt", "<", oneWeekAgo).where("createdAt", ">", oneMonthAgo); + void qb + .where(`${TableName.OrgMembership}.lastInvitedAt`, "<", oneMonthAgo) + .where(`${TableName.OrgMembership}.createdAt`, ">", oneWeekAgo); }); return memberships; From c2ce1aa5aa3036b0dbdc7de700bf52d188c92975 Mon Sep 17 00:00:00 2001 From: x032205 Date: Tue, 1 Jul 2025 20:06:51 -0400 Subject: [PATCH 4/5] Fix license fns --- .../src/ee/services/license/license-fns.ts | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/backend/src/ee/services/license/license-fns.ts b/backend/src/ee/services/license/license-fns.ts index dabcf2017..c2db3e6e7 100644 --- a/backend/src/ee/services/license/license-fns.ts +++ b/backend/src/ee/services/license/license-fns.ts @@ -18,33 +18,33 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ environmentsUsed: 0, identityLimit: null, identitiesUsed: 0, - dynamicSecret: true, + dynamicSecret: false, secretVersioning: true, - pitRecovery: true, - ipAllowlisting: true, - rbac: true, - githubOrgSync: true, + pitRecovery: false, + ipAllowlisting: false, + rbac: false, + githubOrgSync: false, customRateLimits: false, - customAlerts: true, - secretAccessInsights: true, - auditLogs: true, - auditLogsRetentionDays: 3, - auditLogStreams: true, + customAlerts: false, + secretAccessInsights: false, + auditLogs: false, + auditLogsRetentionDays: 0, + auditLogStreams: false, auditLogStreamLimit: 3, - samlSSO: true, - hsm: true, - oidcSSO: true, - scim: true, - ldap: true, - groups: true, + samlSSO: false, + hsm: false, + oidcSSO: false, + scim: false, + ldap: false, + groups: false, status: null, trial_end: null, has_used_trial: true, - secretApproval: true, - secretRotation: true, - caCrl: true, - instanceUserManagement: true, - externalKms: true, + secretApproval: false, + secretRotation: false, + caCrl: false, + instanceUserManagement: false, + externalKms: false, rateLimits: { readLimit: 60, writeLimit: 200, @@ -52,13 +52,13 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ }, pkiEst: false, enforceMfa: false, - projectTemplates: true, - kmip: true, - gateway: true, - sshHostGroups: true, - enterpriseAppConnections: true, - enterpriseSecretSyncs: true, - secretScanning: true + projectTemplates: false, + kmip: false, + gateway: false, + sshHostGroups: false, + secretScanning: false, + enterpriseSecretSyncs: false, + enterpriseAppConnections: false }); export const setupLicenseRequestWithStore = ( From bb59bb18681606af20ab4eeadb2b3742ebcd4b36 Mon Sep 17 00:00:00 2001 From: x032205 Date: Tue, 1 Jul 2025 22:46:16 -0400 Subject: [PATCH 5/5] Remove file --- .../MinimizedOrgSidebar.tsx | 644 ------------------ 1 file changed, 644 deletions(-) delete mode 100644 frontend/src/layouts/OrganizationLayout/components/MinimizedOrgSidebar/MinimizedOrgSidebar.tsx diff --git a/frontend/src/layouts/OrganizationLayout/components/MinimizedOrgSidebar/MinimizedOrgSidebar.tsx b/frontend/src/layouts/OrganizationLayout/components/MinimizedOrgSidebar/MinimizedOrgSidebar.tsx deleted file mode 100644 index f4ce9bc40..000000000 --- a/frontend/src/layouts/OrganizationLayout/components/MinimizedOrgSidebar/MinimizedOrgSidebar.tsx +++ /dev/null @@ -1,644 +0,0 @@ -import { useState } from "react"; -import { faGithub, faSlack } from "@fortawesome/free-brands-svg-icons"; -import { - faArrowUpRightFromSquare, - faBook, - faCheck, - faCheckCircle, - faCog, - faDoorClosed, - faEnvelope, - faInfinity, - faInfo, - faInfoCircle, - faMoneyBill, - faPlug, - faSignOut, - faUser, - faUserCog, - faUsers -} from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { useQueryClient } from "@tanstack/react-query"; -import { Link, linkOptions, useLocation, useNavigate, useRouter } from "@tanstack/react-router"; - -import { Mfa } from "@app/components/auth/Mfa"; -import { createNotification } from "@app/components/notifications"; -import { CreateOrgModal } from "@app/components/organization/CreateOrgModal"; -import SecurityClient from "@app/components/utilities/SecurityClient"; -import { - Button, - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuTrigger, - Modal, - ModalContent, - Tooltip -} from "@app/components/v2"; -import { envConfig } from "@app/config/env"; -import { useOrganization, useSubscription, useUser } from "@app/context"; -import { isInfisicalCloud } from "@app/helpers/platform"; -import { usePopUp, useToggle } from "@app/hooks"; -import { - useGetOrganizations, - useGetOrgTrialUrl, - useLogoutUser, - useSelectOrganization, - workspaceKeys -} from "@app/hooks/api"; -import { authKeys } from "@app/hooks/api/auth/queries"; -import { MfaMethod } from "@app/hooks/api/auth/types"; -import { getAuthToken } from "@app/hooks/api/reactQuery"; -import { SubscriptionPlan } from "@app/hooks/api/types"; -import { AuthMethod } from "@app/hooks/api/users/types"; -import { ProjectType } from "@app/hooks/api/workspace/types"; -import { navigateUserToOrg } from "@app/pages/auth/LoginPage/Login.utils"; - -import { MenuIconButton } from "../MenuIconButton"; -import { ServerAdminsPanel } from "../ServerAdminsPanel/ServerAdminsPanel"; - -const getPlan = (subscription: SubscriptionPlan) => { - if (subscription.groups) return "Enterprise Plan"; - if (subscription.pitRecovery) return "Pro Plan"; - return "Free Plan"; -}; - -export const INFISICAL_SUPPORT_OPTIONS = [ - [ - , - "Support Forum", - "https://infisical.com/slack" - ], - [ - , - "Read Docs", - "https://infisical.com/docs/documentation/getting-started/introduction" - ], - [ - , - "GitHub Issues", - "https://github.com/Infisical/infisical/issues" - ], - [ - , - "Email Support", - "mailto:support@infisical.com" - ], - [ - , - "Instance Admins", - "server-admins" - ] -]; - -export const MinimizedOrgSidebar = () => { - const [shouldShowMfa, toggleShowMfa] = useToggle(false); - const [requiredMfaMethod, setRequiredMfaMethod] = useState(MfaMethod.EMAIL); - const [mfaSuccessCallback, setMfaSuccessCallback] = useState<() => void>(() => {}); - const { subscription } = useSubscription(); - const [open, setOpen] = useState(false); - const [openSupport, setOpenSupport] = useState(false); - const [openUser, setOpenUser] = useState(false); - const [openOrg, setOpenOrg] = useState(false); - const [showAdminsModal, setShowAdminsModal] = useState(false); - - const { user } = useUser(); - const { mutateAsync } = useGetOrgTrialUrl(); - - const { currentOrg } = useOrganization(); - const { data: orgs } = useGetOrganizations(); - - const { popUp, handlePopUpToggle } = usePopUp(["createOrg"] as const); - const { mutateAsync: selectOrganization } = useSelectOrganization(); - const navigate = useNavigate(); - const router = useRouter(); - const location = useLocation(); - const queryClient = useQueryClient(); - - const isMoreSelected = ( - [ - linkOptions({ to: "/organization/access-management" }).to, - linkOptions({ to: "/organization/app-connections" }).to, - linkOptions({ to: "/organization/billing" }).to, - linkOptions({ to: "/organization/sso" }).to, - linkOptions({ to: "/organization/gateways" }).to, - linkOptions({ to: "/organization/settings" }).to, - linkOptions({ to: "/organization/audit-logs" }).to - ] as string[] - ).includes(location.pathname); - - const handleOrgChange = async (orgId: string) => { - queryClient.removeQueries({ queryKey: authKeys.getAuthToken }); - queryClient.removeQueries({ queryKey: workspaceKeys.getAllUserWorkspace() }); - - const { token, isMfaEnabled, mfaMethod } = await selectOrganization({ - organizationId: orgId - }); - - if (isMfaEnabled) { - SecurityClient.setMfaToken(token); - if (mfaMethod) { - setRequiredMfaMethod(mfaMethod); - } - toggleShowMfa.on(); - setMfaSuccessCallback(() => () => handleOrgChange(orgId)); - return; - } - await router.invalidate(); - await navigateUserToOrg(navigate, orgId); - }; - - const logout = useLogoutUser(); - const logOutUser = async () => { - try { - console.log("Logging out..."); - await logout.mutateAsync(); - navigate({ to: "/login" }); - } catch (error) { - console.error(error); - } - }; - - const handleCopyToken = async () => { - try { - await window.navigator.clipboard.writeText(getAuthToken()); - createNotification({ - type: "success", - text: "Copied current login session token to clipboard" - }); - } catch (error) { - console.log(error); - createNotification({ type: "error", text: "Failed to copy user token to clipboard" }); - } - }; - - if (shouldShowMfa) { - return ( -
- toggleShowMfa.off()} - /> -
- ); - } - - return ( - <> - - - -
- -
-
-
- handlePopUpToggle("createOrg", false)} - /> - - ); -};