From fadb36edb8e53e0aaa8a291231c3c1662f5be2a8 Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Mon, 11 Dec 2023 13:27:41 +0530 Subject: [PATCH] feat(infisical-pg): removed _id with id for new backend --- frontend/src/hooks/api/identities/types.ts | 10 +- .../AppLayout/components/NavBar/NavBar.tsx | 2 +- .../cloudflare-pages/authorize.tsx | 2 +- .../cloudflare-workers/create.tsx | 4 +- .../IdentitySection/IdentityModal.tsx | 399 +++++---- .../IdentitySection/IdentitySection.tsx | 213 +++-- .../IdentitySection/IdentityTable.tsx | 485 ++++++----- ...IdentityUniversalAuthClientSecretModal.tsx | 737 +++++++++-------- .../IdentityUniversalAuthForm.tsx | 760 +++++++++--------- .../OrgMembersSection/AddOrgMemberModal.tsx | 285 ++++--- .../OrgMembersSection/OrgMembersSection.tsx | 222 +++-- .../OrgMembersSection/OrgMembersTable.tsx | 479 ++++++----- .../AuditLogsPage/components/LogsTableRow.tsx | 6 +- .../IdentitySection/IdentityModal.tsx | 329 ++++---- .../IdentitySection/IdentitySection.tsx | 134 ++- .../IdentitySection/IdentityTable.tsx | 315 ++++---- .../components/OrgAuthTab/SSOModal.tsx | 2 +- 17 files changed, 2107 insertions(+), 2277 deletions(-) diff --git a/frontend/src/hooks/api/identities/types.ts b/frontend/src/hooks/api/identities/types.ts index 0a9a41fc3..604163032 100644 --- a/frontend/src/hooks/api/identities/types.ts +++ b/frontend/src/hooks/api/identities/types.ts @@ -2,14 +2,14 @@ import { TRole } from "../roles/types"; import { IdentityAuthMethod } from "./enums"; export type IdentityTrustedIp = { - _id: string; + id: string; ipAddress: string; type: "ipv4" | "ipv6"; prefix?: number; } export type Identity = { - _id: string; + id: string; name: string; authMethod?: IdentityAuthMethod; createdAt: string; @@ -17,7 +17,7 @@ export type Identity = { }; export type IdentityMembershipOrg = { - _id: string; + id: string; identity: Identity; organization: string; role: "admin" | "member" | "viewer" | "no-access" | "custom"; @@ -27,7 +27,7 @@ export type IdentityMembershipOrg = { } export type IdentityMembership = { - _id: string; + id: string; identity: Identity; organization: string; role: "admin" | "member" | "viewer" | "no-access" | "custom"; @@ -100,7 +100,7 @@ export type CreateIdentityUniversalAuthClientSecretDTO = { } export type ClientSecretData = { - _id: string; + id: string; identityUniversalAuth: string; isClientSecretRevoked: boolean; description: string; diff --git a/frontend/src/layouts/AppLayout/components/NavBar/NavBar.tsx b/frontend/src/layouts/AppLayout/components/NavBar/NavBar.tsx index 324b19734..979758ba5 100644 --- a/frontend/src/layouts/AppLayout/components/NavBar/NavBar.tsx +++ b/frontend/src/layouts/AppLayout/components/NavBar/NavBar.tsx @@ -324,7 +324,7 @@ export const Navbar = () => { // direct user to start pro trial const url = await mutateAsync({ - orgId: currentOrg._id, + orgId: currentOrg.id, success_url: window.location.href }); diff --git a/frontend/src/pages/integrations/cloudflare-pages/authorize.tsx b/frontend/src/pages/integrations/cloudflare-pages/authorize.tsx index 362b88afe..6337b0d48 100644 --- a/frontend/src/pages/integrations/cloudflare-pages/authorize.tsx +++ b/frontend/src/pages/integrations/cloudflare-pages/authorize.tsx @@ -36,7 +36,7 @@ export default function RenderCreateIntegrationPage() { setIsLoading(false); - router.push(`/integrations/render/create?integrationAuthId=${integrationAuth._id}`); + router.push(`/integrations/render/create?integrationAuthId=${integrationAuth.id}`); } catch (err) { console.error(err); } diff --git a/frontend/src/pages/integrations/cloudflare-workers/create.tsx b/frontend/src/pages/integrations/cloudflare-workers/create.tsx index 7c41f83e8..a977920d2 100644 --- a/frontend/src/pages/integrations/cloudflare-workers/create.tsx +++ b/frontend/src/pages/integrations/cloudflare-workers/create.tsx @@ -46,12 +46,12 @@ export default function CloudflareWorkersIntegrationPage() { const handleButtonClick = async () => { try { - if (!integrationAuth?._id) return; + if (!integrationAuth?.id) return; setIsLoading(true); await mutateAsync({ - integrationAuthId: integrationAuth?._id, + integrationAuthId: integrationAuth?.id, isActive: true, app: targetApp, appId: targetAppId, diff --git a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityModal.tsx b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityModal.tsx index 28a4e4dfe..992cda52b 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityModal.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityModal.tsx @@ -1,31 +1,29 @@ import { useEffect } from "react"; -import { Controller, useForm } from "react-hook-form"; +import { Controller, useForm } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { - Button, - FormControl, - Input, - Modal, - ModalContent, - Select, - SelectItem + Button, + FormControl, + Input, + Modal, + ModalContent, + Select, + SelectItem } from "@app/components/v2"; import { useOrganization } from "@app/context"; -import { - useCreateIdentity, - useGetRoles, - useUpdateIdentity -} from "@app/hooks/api"; +import { useCreateIdentity, useGetRoles, useUpdateIdentity } from "@app/hooks/api"; import { IdentityAuthMethod } from "@app/hooks/api/identities"; import { UsePopUpState } from "@app/hooks/usePopUp"; -const schema = yup.object({ +const schema = yup + .object({ name: yup.string().required("MI name is required"), - role: yup.string(), -}).required(); + role: yup.string() + }) + .required(); export type FormData = yup.InferType; @@ -34,206 +32,191 @@ type Props = { handlePopUpOpen: ( popUpName: keyof UsePopUpState<["identityAuthMethod"]>, data: { - identityId: string; - name: string; - authMethod?: IdentityAuthMethod; + identityId: string; + name: string; + authMethod?: IdentityAuthMethod; } ) => void; handlePopUpToggle: (popUpName: keyof UsePopUpState<["identity"]>, state?: boolean) => void; }; -export const IdentityModal = ({ - popUp, - handlePopUpOpen, - handlePopUpToggle -}: Props) => { - const { createNotification } = useNotificationContext(); +export const IdentityModal = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Props) => { + const { createNotification } = useNotificationContext(); - const { currentOrg } = useOrganization(); - const orgId = currentOrg?._id || ""; + const { currentOrg } = useOrganization(); + const orgId = currentOrg?.id || ""; - const { data: roles } = useGetRoles({ - orgId - }); - - const { mutateAsync: createMutateAsync } = useCreateIdentity(); - const { mutateAsync: updateMutateAsync } = useUpdateIdentity(); - - const { - control, - handleSubmit, - reset, - formState: { isSubmitting } - } = useForm({ - resolver: yupResolver(schema), - defaultValues: { - name: "" - } - }); - - useEffect(() => { - - const identity = popUp?.identity?.data as { - identityId: string; - name: string; - role: string; - customRole: { - name: string; - slug: string; - }; - }; + const { data: roles } = useGetRoles({ + orgId + }); - if (!roles?.length) return; - - if (identity) { - reset({ - name: identity.name, - role: identity?.customRole?.slug ?? identity.role - }); - } else { - reset({ - name: "", - role: roles[0].slug - }); - } - }, [popUp?.identity?.data, roles]); - - const onFormSubmit = async ({ - name, - role, - }: FormData) => { - try { - - const identity = popUp?.identity?.data as { - identityId: string; - name: string; - role: string; - }; - - if (identity) { - // update - - await updateMutateAsync({ - identityId: identity.identityId, - name, - role: role || undefined, - organizationId: orgId - }); - - handlePopUpToggle("identity", false); - } else { - // create - - const { - _id: createdId, - name: createdName, - authMethod - } = await createMutateAsync({ - name, - role: role || undefined, - organizationId: orgId - }); - - handlePopUpToggle("identity", false); - handlePopUpOpen("identityAuthMethod", { - identityId: createdId, - name: createdName, - authMethod - }); - } - - createNotification({ - text: `Successfully ${popUp?.identity?.data ? "updated" : "created"} identity`, - type: "success" - }); + const { mutateAsync: createMutateAsync } = useCreateIdentity(); + const { mutateAsync: updateMutateAsync } = useUpdateIdentity(); - reset(); - } catch (err) { - console.error(err); - const error = err as any; - const text = error?.response?.data?.message - ?? `Failed to ${popUp?.identity?.data ? "updated" : "created"} identity`; - - createNotification({ - text, - type: "error" - }); - } + const { + control, + handleSubmit, + reset, + formState: { isSubmitting } + } = useForm({ + resolver: yupResolver(schema), + defaultValues: { + name: "" } + }); - return ( - { - handlePopUpToggle("identity", isOpen); - reset(); - }} - > - -
- ( - - - - )} - /> - ( - - - - )} - /> -
- - -
- -
-
- ); -} \ No newline at end of file + useEffect(() => { + const identity = popUp?.identity?.data as { + identityId: string; + name: string; + role: string; + customRole: { + name: string; + slug: string; + }; + }; + + if (!roles?.length) return; + + if (identity) { + reset({ + name: identity.name, + role: identity?.customRole?.slug ?? identity.role + }); + } else { + reset({ + name: "", + role: roles[0].slug + }); + } + }, [popUp?.identity?.data, roles]); + + const onFormSubmit = async ({ name, role }: FormData) => { + try { + const identity = popUp?.identity?.data as { + identityId: string; + name: string; + role: string; + }; + + if (identity) { + // update + + await updateMutateAsync({ + identityId: identity.identityId, + name, + role: role || undefined, + organizationId: orgId + }); + + handlePopUpToggle("identity", false); + } else { + // create + + const { + id: createdId, + name: createdName, + authMethod + } = await createMutateAsync({ + name, + role: role || undefined, + organizationId: orgId + }); + + handlePopUpToggle("identity", false); + handlePopUpOpen("identityAuthMethod", { + identityId: createdId, + name: createdName, + authMethod + }); + } + + createNotification({ + text: `Successfully ${popUp?.identity?.data ? "updated" : "created"} identity`, + type: "success" + }); + + reset(); + } catch (err) { + console.error(err); + const error = err as any; + const text = + error?.response?.data?.message ?? + `Failed to ${popUp?.identity?.data ? "updated" : "created"} identity`; + + createNotification({ + text, + type: "error" + }); + } + }; + + return ( + { + handlePopUpToggle("identity", isOpen); + reset(); + }} + > + +
+ ( + + + + )} + /> + ( + + + + )} + /> +
+ + +
+ +
+
+ ); +}; diff --git a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentitySection.tsx b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentitySection.tsx index 2807103c3..1a67504ee 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentitySection.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentitySection.tsx @@ -4,14 +4,8 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { OrgPermissionCan } from "@app/components/permissions"; -import { - Button, - DeleteActionModal -} from "@app/components/v2"; -import { - OrgPermissionActions, - OrgPermissionSubjects, - useOrganization} from "@app/context"; +import { Button, DeleteActionModal } from "@app/components/v2"; +import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context"; import { withPermission } from "@app/hoc"; import { useDeleteIdentity } from "@app/hooks/api"; import { usePopUp } from "@app/hooks/usePopUp"; @@ -22,109 +16,110 @@ import { IdentityTable } from "./IdentityTable"; import { IdentityUniversalAuthClientSecretModal } from "./IdentityUniversalAuthClientSecretModal"; export const IdentitySection = withPermission( - () => { - const { currentOrg } = useOrganization(); - const orgId = currentOrg?._id || ""; - - const { createNotification } = useNotificationContext(); - const { mutateAsync: deleteMutateAsync } = useDeleteIdentity(); - const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ - "identity", - "identityAuthMethod", - "deleteIdentity", - "universalAuthClientSecret", - "deleteUniversalAuthClientSecret", - "upgradePlan" - ] as const); - - const onDeleteIdentitySubmit = async (identityId: string) => { - try { - - await deleteMutateAsync({ - identityId, - organizationId: orgId - }); - - createNotification({ - text: "Successfully deleted identity", - type: "success" - }); - - handlePopUpClose("deleteIdentity"); - } catch (err) { - console.error(err); - const error = err as any; - const text = error?.response?.data?.message - ?? "Failed to delete identity" + () => { + const { currentOrg } = useOrganization(); + const orgId = currentOrg?.id || ""; - createNotification({ - text, - type: "error" - }); - } - } - - return ( -
-
-

- Identities -

- - - {(isAllowed) => ( - - )} - -
- - - - - handlePopUpToggle("deleteIdentity", isOpen)} - deleteKey="confirm" - onDeleteApproved={() => - onDeleteIdentitySubmit( - (popUp?.deleteIdentity?.data as { identityId: string })?.identityId - ) - } + const { createNotification } = useNotificationContext(); + const { mutateAsync: deleteMutateAsync } = useDeleteIdentity(); + const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ + "identity", + "identityAuthMethod", + "deleteIdentity", + "universalAuthClientSecret", + "deleteUniversalAuthClientSecret", + "upgradePlan" + ] as const); + + const onDeleteIdentitySubmit = async (identityId: string) => { + try { + await deleteMutateAsync({ + identityId, + organizationId: orgId + }); + + createNotification({ + text: "Successfully deleted identity", + type: "success" + }); + + handlePopUpClose("deleteIdentity"); + } catch (err) { + console.error(err); + const error = err as any; + const text = error?.response?.data?.message ?? "Failed to delete identity"; + + createNotification({ + text, + type: "error" + }); + } + }; + + return ( +
+
+

Identities

+ + + {(isAllowed) => ( + + )} +
- ); - }, + + + + + handlePopUpToggle("deleteIdentity", isOpen)} + deleteKey="confirm" + onDeleteApproved={() => + onDeleteIdentitySubmit( + (popUp?.deleteIdentity?.data as { identityId: string })?.identityId + ) + } + /> +
+ ); + }, { action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.Identity } ); diff --git a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx index 03b458ad9..2a6d076cb 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx @@ -1,269 +1,248 @@ -import { faKey, faLock,faPencil, faServer, faXmark } from "@fortawesome/free-solid-svg-icons"; +import { faKey, faLock, faPencil, faServer, faXmark } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { OrgPermissionCan } from "@app/components/permissions"; import { - EmptyState, - IconButton, - Select, - SelectItem, - Table, - TableContainer, - TableSkeleton, - TBody, - Td, - Th, - THead, - Tooltip, - Tr + EmptyState, + IconButton, + Select, + SelectItem, + Table, + TableContainer, + TableSkeleton, + TBody, + Td, + Th, + THead, + Tooltip, + Tr } from "@app/components/v2"; -import { - OrgPermissionActions, - OrgPermissionSubjects, - useOrganization} from "@app/context"; -import { - useGetIdentityMembershipOrgs, - useGetRoles, - useUpdateIdentity} from "@app/hooks/api"; +import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context"; +import { useGetIdentityMembershipOrgs, useGetRoles, useUpdateIdentity } from "@app/hooks/api"; import { IdentityAuthMethod, identityAuthToNameMap } from "@app/hooks/api/identities"; import { UsePopUpState } from "@app/hooks/usePopUp"; // TODO: some kind of map type Props = { - handlePopUpOpen: ( - popUpName: keyof UsePopUpState<["deleteIdentity", "identity", "universalAuthClientSecret", "identityAuthMethod"]>, - data?: { - identityId?: string; - name?: string; - authMethod?: string; - role?: string; - customRole?: { - name: string; - slug: string; - }; - } - ) => void; + handlePopUpOpen: ( + popUpName: keyof UsePopUpState< + ["deleteIdentity", "identity", "universalAuthClientSecret", "identityAuthMethod"] + >, + data?: { + identityId?: string; + name?: string; + authMethod?: string; + role?: string; + customRole?: { + name: string; + slug: string; + }; + } + ) => void; +}; + +export const IdentityTable = ({ handlePopUpOpen }: Props) => { + const { createNotification } = useNotificationContext(); + const { currentOrg } = useOrganization(); + const orgId = currentOrg?.id || ""; + + const { mutateAsync: updateMutateAsync } = useUpdateIdentity(); + const { data, isLoading } = useGetIdentityMembershipOrgs(orgId); + + const { data: roles } = useGetRoles({ + orgId + }); + + const handleChangeRole = async ({ identityId, role }: { identityId: string; role: string }) => { + try { + await updateMutateAsync({ + identityId, + role, + organizationId: orgId + }); + + createNotification({ + text: "Successfully updated identity role", + type: "success" + }); + } catch (err) { + console.error(err); + const error = err as any; + const text = error?.response?.data?.message ?? "Failed to update identity role"; + + createNotification({ + text, + type: "error" + }); + } }; -export const IdentityTable = ({ - handlePopUpOpen -}: Props) => { - const { createNotification } = useNotificationContext(); - const { currentOrg } = useOrganization(); - const orgId = currentOrg?._id || ""; - - const { mutateAsync: updateMutateAsync } = useUpdateIdentity(); - const { data, isLoading } = useGetIdentityMembershipOrgs(orgId); - - const { data: roles } = useGetRoles({ - orgId - }); - - const handleChangeRole = async ({ - identityId, - role - }: { - identityId: string; - role: string; - }) => { - try { - - await updateMutateAsync({ - identityId, - role, - organizationId: orgId - }); - - createNotification({ - text: "Successfully updated identity role", - type: "success" - }); - } catch (err) { - console.error(err); - const error = err as any; - const text = error?.response?.data?.message ?? "Failed to update identity role" - - createNotification({ - text, - type: "error" - }); - } - } - - return ( - - - - - - - - - - - - {isLoading && } - {!isLoading && - data && - data.length > 0 && - data.map(({ - identity: { - _id, - name, - authMethod - }, - role, - customRole - }) => { + return ( + +
NameIDRoleAuth Method -
+ + + + + + + + + + {isLoading && } + {!isLoading && + data && + data.length > 0 && + data.map(({ identity: { id, name, authMethod }, role, customRole }) => { + return ( + + + + - - - - - - + ); - })} - {!isLoading && data && data?.length === 0 && ( - - - - )} - -
NameIDRoleAuth Method +
{name}{id} + + {(isAllowed) => { return ( -
{name}{_id} - - {(isAllowed) => { - return ( - - ); - }} - - {authMethod ? identityAuthToNameMap[authMethod] : "Not configured"} -
- {authMethod === IdentityAuthMethod.UNIVERSAL_AUTH && ( - - { - handlePopUpOpen("universalAuthClientSecret", { - identityId: _id, - name - }); - }} - size="lg" - colorSchema="primary" - variant="plain" - ariaLabel="update" - // isDisabled={!isAllowed} - > - - - - )} - - {(isAllowed) => ( - - { - handlePopUpOpen("identityAuthMethod", { - identityId: _id, - name, - authMethod - }); - }} - size="lg" - colorSchema="primary" - variant="plain" - ariaLabel="update" - className="ml-4" - isDisabled={!isAllowed} - > - - - - )} - - - {(isAllowed) => ( - { - handlePopUpOpen("identity", { - identityId: _id, - name, - role, - customRole - }); - }} - size="lg" - colorSchema="primary" - variant="plain" - ariaLabel="update" - className="ml-4" - isDisabled={!isAllowed} - > - - - )} - - - {(isAllowed) => ( - { - handlePopUpOpen("deleteIdentity", { - identityId: _id, - name - }); - }} - size="lg" - colorSchema="danger" - variant="plain" - ariaLabel="update" - className="ml-4" - isDisabled={!isAllowed} - > - - - )} - -
-
- -
-
- ); -} \ No newline at end of file + }} + + + {authMethod ? identityAuthToNameMap[authMethod] : "Not configured"} + +
+ {authMethod === IdentityAuthMethod.UNIVERSAL_AUTH && ( + + { + handlePopUpOpen("universalAuthClientSecret", { + identityId: id, + name + }); + }} + size="lg" + colorSchema="primary" + variant="plain" + ariaLabel="update" + // isDisabled={!isAllowed} + > + + + + )} + + {(isAllowed) => ( + + { + handlePopUpOpen("identityAuthMethod", { + identityId: id, + name, + authMethod + }); + }} + size="lg" + colorSchema="primary" + variant="plain" + ariaLabel="update" + className="ml-4" + isDisabled={!isAllowed} + > + + + + )} + + + {(isAllowed) => ( + { + handlePopUpOpen("identity", { + identityId: id, + name, + role, + customRole + }); + }} + size="lg" + colorSchema="primary" + variant="plain" + ariaLabel="update" + className="ml-4" + isDisabled={!isAllowed} + > + + + )} + + + {(isAllowed) => ( + { + handlePopUpOpen("deleteIdentity", { + identityId: id, + name + }); + }} + size="lg" + colorSchema="danger" + variant="plain" + ariaLabel="update" + className="ml-4" + isDisabled={!isAllowed} + > + + + )} + +
+ + + ); + })} + {!isLoading && data && data?.length === 0 && ( + + + + + + )} + + + + ); +}; diff --git a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthClientSecretModal.tsx b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthClientSecretModal.tsx index 45a921a9f..d10afd6f6 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthClientSecretModal.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthClientSecretModal.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; -import { Controller, useForm } from "react-hook-form"; +import { Controller, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; -import { faCheck, faCopy, faKey,faXmark } from "@fortawesome/free-solid-svg-icons"; +import { faCheck, faCopy, faKey, faXmark } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { yupResolver } from "@hookform/resolvers/yup"; import { format } from "date-fns"; @@ -9,403 +9,394 @@ import * as yup from "yup"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { - Button, - DeleteActionModal, - EmptyState, - FormControl, - IconButton, - Input, - Modal, - ModalContent, - Table, - TableContainer, - TableSkeleton, - TBody, - Td, - Th, - THead, - Tr, + Button, + DeleteActionModal, + EmptyState, + FormControl, + IconButton, + Input, + Modal, + ModalContent, + Table, + TableContainer, + TableSkeleton, + TBody, + Td, + Th, + THead, + Tr } from "@app/components/v2"; import { useToggle } from "@app/hooks"; -import { - useCreateIdentityUniversalAuthClientSecret, - useGetIdentityUniversalAuth, - useGetIdentityUniversalAuthClientSecrets, useRevokeIdentityUniversalAuthClientSecret} from "@app/hooks/api"; +import { + useCreateIdentityUniversalAuthClientSecret, + useGetIdentityUniversalAuth, + useGetIdentityUniversalAuthClientSecrets, + useRevokeIdentityUniversalAuthClientSecret +} from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; const schema = yup.object({ - description: yup.string(), - ttl: yup.string(), - numUsesLimit: yup.string() + description: yup.string(), + ttl: yup.string(), + numUsesLimit: yup.string() }); export type FormData = yup.InferType; type Props = { - popUp: UsePopUpState<["universalAuthClientSecret", "deleteUniversalAuthClientSecret"]>; - handlePopUpOpen: ( - popUpName: keyof UsePopUpState<["deleteUniversalAuthClientSecret"]>, - data?: { - clientSecretPrefix: string; - clientSecretId: string; - } - ) => void; - handlePopUpToggle: (popUpName: keyof UsePopUpState<["universalAuthClientSecret", "deleteUniversalAuthClientSecret"]>, state?: boolean) => void; + popUp: UsePopUpState<["universalAuthClientSecret", "deleteUniversalAuthClientSecret"]>; + handlePopUpOpen: ( + popUpName: keyof UsePopUpState<["deleteUniversalAuthClientSecret"]>, + data?: { + clientSecretPrefix: string; + clientSecretId: string; + } + ) => void; + handlePopUpToggle: ( + popUpName: keyof UsePopUpState< + ["universalAuthClientSecret", "deleteUniversalAuthClientSecret"] + >, + state?: boolean + ) => void; }; export const IdentityUniversalAuthClientSecretModal = ({ - popUp, - handlePopUpOpen, - handlePopUpToggle + popUp, + handlePopUpOpen, + handlePopUpToggle }: Props) => { - const { t } = useTranslation(); - const { createNotification } = useNotificationContext(); - const [token, setToken] = useState(""); - const [isClientSecretCopied, setIsClientSecretCopied] = useToggle(false); - const [isClientIdCopied, setIsClientIdCopied] = useToggle(false); + const { t } = useTranslation(); + const { createNotification } = useNotificationContext(); + const [token, setToken] = useState(""); + const [isClientSecretCopied, setIsClientSecretCopied] = useToggle(false); + const [isClientIdCopied, setIsClientIdCopied] = useToggle(false); - const popUpData = (popUp?.universalAuthClientSecret?.data as { - identityId?: string; - name?: string; - }); - - const { data, isLoading } = useGetIdentityUniversalAuthClientSecrets(popUpData?.identityId ?? ""); - const { data: identityUniversalAuth } = useGetIdentityUniversalAuth(popUpData?.identityId ?? ""); + const popUpData = popUp?.universalAuthClientSecret?.data as { + identityId?: string; + name?: string; + }; - const { mutateAsync: createClientSecretMutateAsync } = useCreateIdentityUniversalAuthClientSecret(); - const { mutateAsync: revokeClientSecretMutateAsync } = useRevokeIdentityUniversalAuthClientSecret(); + const { data, isLoading } = useGetIdentityUniversalAuthClientSecrets(popUpData?.identityId ?? ""); + const { data: identityUniversalAuth } = useGetIdentityUniversalAuth(popUpData?.identityId ?? ""); - const { - control, - handleSubmit, - reset, - formState: { isSubmitting } - } = useForm({ - resolver: yupResolver(schema), - defaultValues: { - description: "", - ttl: "", - numUsesLimit: "" - } - }); - - useEffect(() => { - let timer: NodeJS.Timeout; - if (isClientSecretCopied) { - timer = setTimeout(() => setIsClientSecretCopied.off(), 2000); - } - - return () => clearTimeout(timer); - }, [isClientSecretCopied]); - - useEffect(() => { - let timer: NodeJS.Timeout; - if (isClientIdCopied) { - timer = setTimeout(() => setIsClientIdCopied.off(), 2000); - } - - return () => clearTimeout(timer); - }, [isClientIdCopied]); - - const onFormSubmit = async ({ + const { mutateAsync: createClientSecretMutateAsync } = + useCreateIdentityUniversalAuthClientSecret(); + const { mutateAsync: revokeClientSecretMutateAsync } = + useRevokeIdentityUniversalAuthClientSecret(); + + const { + control, + handleSubmit, + reset, + formState: { isSubmitting } + } = useForm({ + resolver: yupResolver(schema), + defaultValues: { + description: "", + ttl: "", + numUsesLimit: "" + } + }); + + useEffect(() => { + let timer: NodeJS.Timeout; + if (isClientSecretCopied) { + timer = setTimeout(() => setIsClientSecretCopied.off(), 2000); + } + + return () => clearTimeout(timer); + }, [isClientSecretCopied]); + + useEffect(() => { + let timer: NodeJS.Timeout; + if (isClientIdCopied) { + timer = setTimeout(() => setIsClientIdCopied.off(), 2000); + } + + return () => clearTimeout(timer); + }, [isClientIdCopied]); + + const onFormSubmit = async ({ description, ttl, numUsesLimit }: FormData) => { + try { + if (!popUpData?.identityId) return; + + const { clientSecret } = await createClientSecretMutateAsync({ + identityId: popUpData.identityId, description, - ttl, - numUsesLimit - }: FormData) => { - try { - - if (!popUpData?.identityId) return; + ttl: Number(ttl), + numUsesLimit: Number(numUsesLimit) + }); - const { clientSecret } = await createClientSecretMutateAsync({ - identityId: popUpData.identityId, - description, - ttl: Number(ttl), - numUsesLimit: Number(numUsesLimit) - }); + setToken(clientSecret); - setToken(clientSecret); - - createNotification({ - text: "Successfully created client secret", - type: "success" - }); - } catch (err) { - console.error(err); - createNotification({ - text: "Failed to create client secret", - type: "error" - }); - } + createNotification({ + text: "Successfully created client secret", + type: "success" + }); + } catch (err) { + console.error(err); + createNotification({ + text: "Failed to create client secret", + type: "error" + }); } - - const onDeleteClientSecretSubmit = async ({ - clientSecretId, - clientSecretPrefix - }: { - clientSecretId: string; - clientSecretPrefix: string; - }) => { - try { - - if (!popUpData?.identityId) return; + }; - await revokeClientSecretMutateAsync({ - identityId: popUpData.identityId, - clientSecretId - }); - - if (token.startsWith(clientSecretPrefix)) { - reset(); - setToken(""); - } - - handlePopUpToggle("deleteUniversalAuthClientSecret", false); - - createNotification({ - text: "Successfully deleted client secret", - type: "success" - }); - } catch (err) { - console.error(err); - createNotification({ - text: "Failed to delete client secret", - type: "error" - }); - - } + const onDeleteClientSecretSubmit = async ({ + clientSecretId, + clientSecretPrefix + }: { + clientSecretId: string; + clientSecretPrefix: string; + }) => { + try { + if (!popUpData?.identityId) return; + + await revokeClientSecretMutateAsync({ + identityId: popUpData.identityId, + clientSecretId + }); + + if (token.startsWith(clientSecretPrefix)) { + reset(); + setToken(""); + } + + handlePopUpToggle("deleteUniversalAuthClientSecret", false); + + createNotification({ + text: "Successfully deleted client secret", + type: "success" + }); + } catch (err) { + console.error(err); + createNotification({ + text: "Failed to delete client secret", + type: "error" + }); } + }; - const hasToken = Boolean(token); + const hasToken = Boolean(token); - return ( - { - handlePopUpToggle("universalAuthClientSecret", isOpen); - reset(); - setToken(""); + return ( + { + handlePopUpToggle("universalAuthClientSecret", isOpen); + reset(); + setToken(""); + }} + > + +

Client ID

+
+

{identityUniversalAuth?.clientId ?? ""}

+ { + navigator.clipboard.writeText(identityUniversalAuth?.clientId ?? ""); + setIsClientIdCopied.on(); }} - > - -

Client ID

-
-

{identityUniversalAuth?.clientId ?? ""}

- { - navigator.clipboard.writeText(identityUniversalAuth?.clientId ?? "") - setIsClientIdCopied.on(); - }} - > - - - {t("common.click-to-copy")} - - -
-

New Client Secret

- {hasToken ? ( -
-
-

We will only show this secret once

- -
-
-

{token}

- { - navigator.clipboard.writeText(token); - setIsClientSecretCopied.on(); - }} - > - - - {t("common.click-to-copy")} - - -
+ > + + + {t("common.click-to-copy")} + + +
+

New Client Secret

+ {hasToken ? ( +
+
+

We will only show this secret once

+ +
+
+

{token}

+ { + navigator.clipboard.writeText(token); + setIsClientSecretCopied.on(); + }} + > + + + {t("common.click-to-copy")} + + +
+
+ ) : ( +
+ ( + + + + )} + /> +
+ ( + +
+
- ) : ( - - ( - - - - )} - /> -
- ( - -
- - -
-
- )} - /> - ( - -
- - -
-
- )} - /> -
- +
)} -

Client Secrets

- - - - - - - - - - - - {isLoading && } - {!isLoading && - data && - data.length > 0 && - data.map(({ - _id, - description, - clientSecretTTL, + /> + ( + +
+ + +
+
+ )} + /> + + + )} +

Client Secrets

+ +
DescriptionNum UsesExpires AtClient Secret -
+ + + + + + + + + + {isLoading && } + {!isLoading && + data && + data.length > 0 && + data.map( + ({ + id, + description, + clientSecretTTL, + clientSecretPrefix, + clientSecretNumUses, + clientSecretNumUsesLimit, + createdAt + }) => { + let expiresAt; + if (clientSecretTTL > 0) { + expiresAt = new Date(new Date(createdAt).getTime() + clientSecretTTL * 1000); + } + + return ( + + + + + + - - - - - - - ); - })} - {!isLoading && data && data?.length === 0 && ( - - - - )} - -
DescriptionNum UsesExpires AtClient Secret +
{description === "" ? "-" : description}{`${clientSecretNumUses}${ + clientSecretNumUsesLimit ? `/${clientSecretNumUsesLimit}` : "" + }`}{expiresAt ? format(expiresAt, "yyyy-MM-dd") : "-"}{`${clientSecretPrefix}****`} + { + handlePopUpOpen("deleteUniversalAuthClientSecret", { clientSecretPrefix, - clientSecretNumUses, - clientSecretNumUsesLimit, - createdAt - }) => { - let expiresAt; - if (clientSecretTTL > 0) { - expiresAt = new Date(new Date(createdAt).getTime() + clientSecretTTL * 1000); - } - - return ( -
{description === "" ? "-" : description}{`${clientSecretNumUses}${clientSecretNumUsesLimit ? `/${clientSecretNumUsesLimit}` : ""}`}{expiresAt ? format(expiresAt, "yyyy-MM-dd") : "-"}{`${clientSecretPrefix}****`} - { - handlePopUpOpen("deleteUniversalAuthClientSecret", { - clientSecretPrefix, - clientSecretId: _id - }); - }} - size="lg" - colorSchema="primary" - variant="plain" - ariaLabel="update" - > - - -
- -
-
- handlePopUpToggle("deleteUniversalAuthClientSecret", isOpen)} - deleteKey="confirm" - onDeleteApproved={() => { - const deleteClientSecretData = (popUp?.deleteUniversalAuthClientSecret?.data as { - clientSecretId: string; - clientSecretPrefix: string; - }); - - return onDeleteClientSecretSubmit({ - clientSecretId: deleteClientSecretData.clientSecretId, - clientSecretPrefix: deleteClientSecretData.clientSecretPrefix - }); - }} - /> - - - ); -} \ No newline at end of file + clientSecretId: id + }); + }} + size="lg" + colorSchema="primary" + variant="plain" + ariaLabel="update" + > + + + + + ); + } + )} + {!isLoading && data && data?.length === 0 && ( + + + + + + )} + + + + handlePopUpToggle("deleteUniversalAuthClientSecret", isOpen)} + deleteKey="confirm" + onDeleteApproved={() => { + const deleteClientSecretData = popUp?.deleteUniversalAuthClientSecret?.data as { + clientSecretId: string; + clientSecretPrefix: string; + }; + + return onDeleteClientSecretSubmit({ + clientSecretId: deleteClientSecretData.clientSecretId, + clientSecretPrefix: deleteClientSecretData.clientSecretPrefix + }); + }} + /> + + + ); +}; diff --git a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthForm.tsx b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthForm.tsx index 13a2e1bf5..150f13808 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthForm.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityUniversalAuthForm.tsx @@ -1,432 +1,392 @@ import { useEffect } from "react"; -import { Controller, useFieldArray, useForm } from "react-hook-form"; +import { Controller, useFieldArray, useForm } from "react-hook-form"; import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; +import { Button, FormControl, IconButton, Input } from "@app/components/v2"; +import { useOrganization, useSubscription } from "@app/context"; import { - Button, - FormControl, - IconButton, - Input, -} from "@app/components/v2"; -import { - useOrganization, - useSubscription -} from "@app/context"; -import { - useAddIdentityUniversalAuth, - useGetIdentityUniversalAuth, - useUpdateIdentityUniversalAuth + useAddIdentityUniversalAuth, + useGetIdentityUniversalAuth, + useUpdateIdentityUniversalAuth } from "@app/hooks/api"; import { IdentityAuthMethod } from "@app/hooks/api/identities"; import { IdentityTrustedIp } from "@app/hooks/api/identities/types"; import { UsePopUpState } from "@app/hooks/usePopUp"; -const schema = yup.object({ - accessTokenTTL: yup - .string() - .required("Access Token TTL is required"), - accessTokenMaxTTL: yup - .string() - .required("Access Max Token TTL is required"), - accessTokenNumUsesLimit: yup - .string() - .required("Access Token Max Number of Uses is required"), +const schema = yup + .object({ + accessTokenTTL: yup.string().required("Access Token TTL is required"), + accessTokenMaxTTL: yup.string().required("Access Max Token TTL is required"), + accessTokenNumUsesLimit: yup.string().required("Access Token Max Number of Uses is required"), clientSecretTrustedIps: yup - .array( + .array( yup.object({ - ipAddress: yup.string().max(50).required().label("IP Address") + ipAddress: yup.string().max(50).required().label("IP Address") }) - ) - .min(1) - .required() - .label("Client Secret Trusted IP"), + ) + .min(1) + .required() + .label("Client Secret Trusted IP"), accessTokenTrustedIps: yup - .array( + .array( yup.object({ - ipAddress: yup.string().max(50).required().label("IP Address") + ipAddress: yup.string().max(50).required().label("IP Address") }) - ) - .min(1) - .required() - .label("Access Token Trusted IP") -}).required(); + ) + .min(1) + .required() + .label("Access Token Trusted IP") + }) + .required(); export type FormData = yup.InferType; type Props = { - handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void; - handlePopUpToggle: (popUpName: keyof UsePopUpState<["identityAuthMethod"]>, state?: boolean) => void; - identityAuthMethodData: { - identityId: string; - name: string; - authMethod?: IdentityAuthMethod; - } -} + handlePopUpOpen: (popUpName: keyof UsePopUpState<["upgradePlan"]>) => void; + handlePopUpToggle: ( + popUpName: keyof UsePopUpState<["identityAuthMethod"]>, + state?: boolean + ) => void; + identityAuthMethodData: { + identityId: string; + name: string; + authMethod?: IdentityAuthMethod; + }; +}; export const IdentityUniversalAuthForm = ({ - handlePopUpOpen, - handlePopUpToggle, - identityAuthMethodData + handlePopUpOpen, + handlePopUpToggle, + identityAuthMethodData }: Props) => { - const { createNotification } = useNotificationContext(); - const { currentOrg } = useOrganization(); - const orgId = currentOrg?._id || ""; - const { subscription } = useSubscription(); - const { mutateAsync: addMutateAsync } = useAddIdentityUniversalAuth(); - const { mutateAsync: updateMutateAsync } = useUpdateIdentityUniversalAuth(); - const { data } = useGetIdentityUniversalAuth(identityAuthMethodData?.identityId ?? ""); + const { createNotification } = useNotificationContext(); + const { currentOrg } = useOrganization(); + const orgId = currentOrg?.id || ""; + const { subscription } = useSubscription(); + const { mutateAsync: addMutateAsync } = useAddIdentityUniversalAuth(); + const { mutateAsync: updateMutateAsync } = useUpdateIdentityUniversalAuth(); + const { data } = useGetIdentityUniversalAuth(identityAuthMethodData?.identityId ?? ""); - const { - control, - handleSubmit, - reset, - formState: { isSubmitting } - } = useForm({ - resolver: yupResolver(schema), - defaultValues: { - accessTokenTTL: "2592000", - accessTokenMaxTTL: "2592000", - accessTokenNumUsesLimit: "0", - clientSecretTrustedIps: [ - { ipAddress: "0.0.0.0/0" }, - { ipAddress: "::/0" } - ], - accessTokenTrustedIps: [ - { ipAddress: "0.0.0.0/0" }, - { ipAddress: "::/0" } - ], - } - }); - - const { - fields: clientSecretTrustedIpsFields, - append: appendClientSecretTrustedIp, - remove: removeClientSecretTrustedIp - } = useFieldArray({ control, name: "clientSecretTrustedIps" }); - const { - fields: accessTokenTrustedIpsFields, - append: appendAccessTokenTrustedIp, - remove: removeAccessTokenTrustedIp - } = useFieldArray({ control, name: "accessTokenTrustedIps" }); - - useEffect(() => { - if (data) { - reset({ - accessTokenTTL: String(data.accessTokenTTL), - accessTokenMaxTTL: String(data.accessTokenMaxTTL), - accessTokenNumUsesLimit: String(data.accessTokenNumUsesLimit), - clientSecretTrustedIps: data.clientSecretTrustedIps.map(({ - ipAddress, - prefix - }: IdentityTrustedIp) => { - return ({ - ipAddress: `${ipAddress}${prefix !== undefined ? `/${prefix}` : ""}` - }); - }), - accessTokenTrustedIps: data.accessTokenTrustedIps.map(({ - ipAddress, - prefix - }: IdentityTrustedIp) => { - return ({ - ipAddress: `${ipAddress}${prefix !== undefined ? `/${prefix}` : ""}` - }); - }) - }); - } else { - reset({ - accessTokenTTL: "2592000", - accessTokenMaxTTL: "2592000", - accessTokenNumUsesLimit: "0", - clientSecretTrustedIps: [ - { ipAddress: "0.0.0.0/0" }, - { ipAddress: "::/0" } - ], - accessTokenTrustedIps: [ - { ipAddress: "0.0.0.0/0" }, - { ipAddress: "::/0" } - ] - }); - } - }, [data]); - - const onFormSubmit = async ({ - accessTokenTTL, - accessTokenMaxTTL, - accessTokenNumUsesLimit, - clientSecretTrustedIps, - accessTokenTrustedIps - }: FormData) => { - try { - - if (!identityAuthMethodData) return; - - if (data) { - // update universal auth configuration - await updateMutateAsync({ - organizationId: orgId, - identityId: identityAuthMethodData.identityId, - clientSecretTrustedIps, - accessTokenTTL: Number(accessTokenTTL), - accessTokenMaxTTL: Number(accessTokenMaxTTL), - accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit), - accessTokenTrustedIps, - }); - - } else { - // create new universal auth configuration - - await addMutateAsync({ - organizationId: orgId, - identityId: identityAuthMethodData.identityId, - clientSecretTrustedIps, - accessTokenTTL: Number(accessTokenTTL), - accessTokenMaxTTL: Number(accessTokenMaxTTL), - accessTokenNumUsesLimit: Number(accessTokenNumUsesLimit), - accessTokenTrustedIps, - }); - } - - handlePopUpToggle("identityAuthMethod", false); - - createNotification({ - text: `Successfully ${identityAuthMethodData?.authMethod ? "updated" : "configured"} auth method`, - type: "success" - }); - - reset(); - } catch (err) { - console.error(err); - const error = err as any; - const text = error?.response?.data?.message - ?? `Failed to ${identityAuthMethodData?.authMethod ? "update" : "configure"} identity`; - - createNotification({ - text, - type: "error" - }); - } + const { + control, + handleSubmit, + reset, + formState: { isSubmitting } + } = useForm({ + resolver: yupResolver(schema), + defaultValues: { + accessTokenTTL: "2592000", + accessTokenMaxTTL: "2592000", + accessTokenNumUsesLimit: "0", + clientSecretTrustedIps: [{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }], + accessTokenTrustedIps: [{ ipAddress: "0.0.0.0/0" }, { ipAddress: "::/0" }] } - - return ( -
- ( - - - - )} - /> - ( - - - - )} - /> - ( - - - - )} - /> - {clientSecretTrustedIpsFields.map(({ id }, index) => ( -
- { - return ( - - { - if (subscription?.ipAllowlisting) { - field.onChange(e); - return; - } - - handlePopUpOpen("upgradePlan"); - }} - placeholder="123.456.789.0" - /> - - ); - }} - /> - { - if (subscription?.ipAllowlisting) { - removeClientSecretTrustedIp(index); - return; - } - - handlePopUpOpen("upgradePlan"); - }} - size="lg" - colorSchema="danger" - variant="plain" - ariaLabel="update" - className="p-3" - > - - -
- ))} -
- -
- {accessTokenTrustedIpsFields.map(({ id }, index) => ( -
- { - return ( - - { - if (subscription?.ipAllowlisting) { - field.onChange(e); - return; - } - - handlePopUpOpen("upgradePlan"); - }} - placeholder="123.456.789.0" - /> - - ); - }} - /> - { - if (subscription?.ipAllowlisting) { - removeAccessTokenTrustedIp(index); - return; - } - - handlePopUpOpen("upgradePlan"); - }} - size="lg" - colorSchema="danger" - variant="plain" - ariaLabel="update" - className="p-3" - > - - -
- ))} -
- +
+ {accessTokenTrustedIpsFields.map(({ id }, index) => ( +
+ { + return ( + - Add IP Address - -
-
- - -
- - ); -} \ No newline at end of file + { + if (subscription?.ipAllowlisting) { + field.onChange(e); + return; + } + + handlePopUpOpen("upgradePlan"); + }} + placeholder="123.456.789.0" + /> + + ); + }} + /> + { + if (subscription?.ipAllowlisting) { + removeAccessTokenTrustedIp(index); + return; + } + + handlePopUpOpen("upgradePlan"); + }} + size="lg" + colorSchema="danger" + variant="plain" + ariaLabel="update" + className="p-3" + > + + +
+ ))} +
+ +
+
+ + +
+ + ); +}; diff --git a/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/AddOrgMemberModal.tsx b/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/AddOrgMemberModal.tsx index 3465dd0c0..e893998e3 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/AddOrgMemberModal.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/AddOrgMemberModal.tsx @@ -1,176 +1,163 @@ import { Controller, useForm } from "react-hook-form"; -import { - faCheck, - faCopy, -} from "@fortawesome/free-solid-svg-icons"; +import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; -import { - Button, - FormControl, - IconButton, - Input, - Modal, - ModalContent, -} from "@app/components/v2"; +import { Button, FormControl, IconButton, Input, Modal, ModalContent } from "@app/components/v2"; import { useOrganization } from "@app/context"; import { useToggle } from "@app/hooks"; -import { - useAddUserToOrg, - useFetchServerStatus -} from "@app/hooks/api"; +import { useAddUserToOrg, useFetchServerStatus } from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; const addMemberFormSchema = yup.object({ - email: yup.string().email().required().label("Email").trim().lowercase() + email: yup.string().email().required().label("Email").trim().lowercase() }); type TAddMemberForm = yup.InferType; type Props = { - popUp: UsePopUpState<["addMember"]>; - handlePopUpToggle: (popUpName: keyof UsePopUpState<["addMember"]>, state?: boolean) => void; - completeInviteLink: string; - setCompleteInviteLink: (link: string) => void; + popUp: UsePopUpState<["addMember"]>; + handlePopUpToggle: (popUpName: keyof UsePopUpState<["addMember"]>, state?: boolean) => void; + completeInviteLink: string; + setCompleteInviteLink: (link: string) => void; }; export const AddOrgMemberModal = ({ - popUp, - handlePopUpToggle, - completeInviteLink, - setCompleteInviteLink + popUp, + handlePopUpToggle, + completeInviteLink, + setCompleteInviteLink }: Props) => { - const { createNotification } = useNotificationContext(); - const { currentOrg } = useOrganization(); + const { createNotification } = useNotificationContext(); + const { currentOrg } = useOrganization(); - const { data: serverDetails } = useFetchServerStatus(); - const { mutateAsync: addUserMutateAsync } = useAddUserToOrg(); + const { data: serverDetails } = useFetchServerStatus(); + const { mutateAsync: addUserMutateAsync } = useAddUserToOrg(); - const [isInviteLinkCopied, setInviteLinkCopied] = useToggle(false); - - const { - control, - handleSubmit, - reset, - formState: { isSubmitting } - } = useForm({ resolver: yupResolver(addMemberFormSchema) }); + const [isInviteLinkCopied, setInviteLinkCopied] = useToggle(false); - const onAddMember = async ({ email }: TAddMemberForm) => { - if (!currentOrg?._id) return; - - try { - const { data } = await addUserMutateAsync({ - organizationId: currentOrg?._id, - inviteeEmail: email - }); + const { + control, + handleSubmit, + reset, + formState: { isSubmitting } + } = useForm({ resolver: yupResolver(addMemberFormSchema) }); - setCompleteInviteLink(data?.completeInviteLink ??""); + const onAddMember = async ({ email }: TAddMemberForm) => { + if (!currentOrg?.id) return; - // only show this notification when email is configured. - // A [completeInviteLink] will not be sent if smtp is configured + try { + const { data } = await addUserMutateAsync({ + organizationId: currentOrg?.id, + inviteeEmail: email + }); - if (!data.completeInviteLink) { - createNotification({ - text: "Successfully invited user to the organization.", - type: "success" - }); - } - } catch (error) { - console.error(error); - createNotification({ - text: "Failed to invite user to org", - type: "error" - }); + setCompleteInviteLink(data?.completeInviteLink ?? ""); + + // only show this notification when email is configured. + // A [completeInviteLink] will not be sent if smtp is configured + + if (!data.completeInviteLink) { + createNotification({ + text: "Successfully invited user to the organization.", + type: "success" + }); + } + } catch (error) { + console.error(error); + createNotification({ + text: "Failed to invite user to org", + type: "error" + }); + } + + if (serverDetails?.emailConfigured) { + handlePopUpToggle("addMember", false); + } + + reset(); + }; + + const copyTokenToClipboard = () => { + navigator.clipboard.writeText(completeInviteLink as string); + setInviteLinkCopied.on(); + }; + + return ( + { + handlePopUpToggle("addMember", isOpen); + setCompleteInviteLink(""); + }} + > + + {!completeInviteLink && ( +
+ An invite is specific to an email address and expires after 1 day. +
+ For security reasons, you will need to separately add members to projects. +
+ )} + {completeInviteLink && + "This Infisical instance does not have a email provider setup. Please share this invite link with the invitee manually"} +
} - - if (serverDetails?.emailConfigured) { - handlePopUpToggle("addMember", false); - } - - reset(); - }; - - const copyTokenToClipboard = () => { - navigator.clipboard.writeText(completeInviteLink as string); - setInviteLinkCopied.on(); - }; - - return ( - { - handlePopUpToggle("addMember", isOpen); - setCompleteInviteLink(""); - }} - > - - {!completeInviteLink && ( -
- An invite is specific to an email address and expires after 1 day. -
- For security reasons, you will need to separately add members to projects. -
- )} - {completeInviteLink && - "This Infisical instance does not have a email provider setup. Please share this invite link with the invitee manually"} -
- } - > - {!completeInviteLink && ( -
- ( - - - - )} - /> -
- - -
- - )} - {completeInviteLink && ( -
-

{completeInviteLink}

- - - - click to copy - - -
- )} - - - ); -} \ No newline at end of file + > + {!completeInviteLink && ( +
+ ( + + + + )} + /> +
+ + +
+ + )} + {completeInviteLink && ( +
+

{completeInviteLink}

+ + + + click to copy + + +
+ )} + + + ); +}; diff --git a/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersSection.tsx b/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersSection.tsx index a7f96d272..3f2a8cc15 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersSection.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersSection.tsx @@ -14,134 +14,126 @@ import { OrgPermissionActions, OrgPermissionSubjects, useOrganization, - useSubscription, + useSubscription } from "@app/context"; -import { - useDeleteOrgMembership, - useGetSSOConfig, -} from "@app/hooks/api"; +import { useDeleteOrgMembership, useGetSSOConfig } from "@app/hooks/api"; import { usePopUp } from "@app/hooks/usePopUp"; import { AddOrgMemberModal } from "./AddOrgMemberModal"; import { OrgMembersTable } from "./OrgMembersTable"; export const OrgMembersSection = () => { - const { createNotification } = useNotificationContext(); - const { subscription } = useSubscription(); - const { currentOrg } = useOrganization(); - const orgId = currentOrg?._id ?? ""; - - const [completeInviteLink, setCompleteInviteLink] = useState(""); + const { createNotification } = useNotificationContext(); + const { subscription } = useSubscription(); + const { currentOrg } = useOrganization(); + const orgId = currentOrg?.id ?? ""; - const { data: ssoConfig, isLoading: isLoadingSSOConfig } = useGetSSOConfig(orgId); - const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ - "addMember", - "removeMember", - "upgradePlan", - "setUpEmail" - ] as const); + const [completeInviteLink, setCompleteInviteLink] = useState(""); - const { mutateAsync: deleteMutateAsync } = useDeleteOrgMembership(); + const { data: ssoConfig, isLoading: isLoadingSSOConfig } = useGetSSOConfig(orgId); + const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ + "addMember", + "removeMember", + "upgradePlan", + "setUpEmail" + ] as const); - const isMoreUsersNotAllowed = subscription?.memberLimit - ? subscription.membersUsed >= subscription.memberLimit - : false; + const { mutateAsync: deleteMutateAsync } = useDeleteOrgMembership(); - const handleAddMemberModal = () => { - if (!isLoadingSSOConfig && ssoConfig && ssoConfig.isActive) { - createNotification({ - text: "You cannot invite users when SAML SSO is configured for your organization", - type: "error" - }); - return; - } + const isMoreUsersNotAllowed = subscription?.memberLimit + ? subscription.membersUsed >= subscription.memberLimit + : false; - if (isMoreUsersNotAllowed) { - handlePopUpOpen("upgradePlan", { - description: "You can add more members if you upgrade your Infisical plan." - }); - } else { - handlePopUpOpen("addMember"); - } + const handleAddMemberModal = () => { + if (!isLoadingSSOConfig && ssoConfig && ssoConfig.isActive) { + createNotification({ + text: "You cannot invite users when SAML SSO is configured for your organization", + type: "error" + }); + return; } - const onRemoveMemberSubmit = async (orgMembershipId: string) => { - try { - await deleteMutateAsync({ - orgId, - membershipId: orgMembershipId - }); - - createNotification({ - text: "Successfully removed user from org", - type: "success" - }); - } catch (err) { - console.error(err); - createNotification({ - text: "Failed to remove user from the organization", - type: "error" - }); - } - - handlePopUpClose("removeMember"); + if (isMoreUsersNotAllowed) { + handlePopUpOpen("upgradePlan", { + description: "You can add more members if you upgrade your Infisical plan." + }); + } else { + handlePopUpOpen("addMember"); + } + }; + + const onRemoveMemberSubmit = async (orgMembershipId: string) => { + try { + await deleteMutateAsync({ + orgId, + membershipId: orgMembershipId + }); + + createNotification({ + text: "Successfully removed user from org", + type: "success" + }); + } catch (err) { + console.error(err); + createNotification({ + text: "Failed to remove user from the organization", + type: "error" + }); } - return ( -
-
-

- Members -

- - {(isAllowed) => ( - - )} - -
- - - handlePopUpToggle("removeMember", isOpen)} - deleteKey="confirm" - onDeleteApproved={() => - onRemoveMemberSubmit( - (popUp?.removeMember?.data as { orgMembershipId: string })?.orgMembershipId - ) - } - /> - handlePopUpToggle("upgradePlan", isOpen)} - text={(popUp.upgradePlan?.data as { description: string })?.description} - /> - handlePopUpToggle("setUpEmail", isOpen)} - /> -
- ); -} \ No newline at end of file + handlePopUpClose("removeMember"); + }; + + return ( +
+
+

Members

+ + {(isAllowed) => ( + + )} + +
+ + + handlePopUpToggle("removeMember", isOpen)} + deleteKey="confirm" + onDeleteApproved={() => + onRemoveMemberSubmit( + (popUp?.removeMember?.data as { orgMembershipId: string })?.orgMembershipId + ) + } + /> + handlePopUpToggle("upgradePlan", isOpen)} + text={(popUp.upgradePlan?.data as { description: string })?.description} + /> + handlePopUpToggle("setUpEmail", isOpen)} + /> +
+ ); +}; diff --git a/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx b/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx index dfd6169cd..7647aa7de 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx @@ -1,272 +1,265 @@ -import { useCallback,useMemo, useState } from "react"; -import { - faMagnifyingGlass, - faUsers, - faXmark -} from "@fortawesome/free-solid-svg-icons"; +import { useCallback, useMemo, useState } from "react"; +import { faMagnifyingGlass, faUsers, faXmark } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { OrgPermissionCan } from "@app/components/permissions"; import { - Button, - EmptyState, - IconButton, - Input, - Select, - SelectItem, - Table, - TableContainer, - TableSkeleton, - TBody, - Td, - Th, - THead, - Tr, + Button, + EmptyState, + IconButton, + Input, + Select, + SelectItem, + Table, + TableContainer, + TableSkeleton, + TBody, + Td, + Th, + THead, + Tr } from "@app/components/v2"; -import { - OrgPermissionActions, - OrgPermissionSubjects, - useOrganization, - useSubscription, - useUser} from "@app/context"; import { - useAddUserToOrg, - useFetchServerStatus, - useGetOrgUsers, - useGetRoles, - useUpdateOrgUserRole + OrgPermissionActions, + OrgPermissionSubjects, + useOrganization, + useSubscription, + useUser +} from "@app/context"; +import { + useAddUserToOrg, + useFetchServerStatus, + useGetOrgUsers, + useGetRoles, + useUpdateOrgUserRole } from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; type Props = { - handlePopUpOpen: ( - popUpName: keyof UsePopUpState<["removeMember", "upgradePlan"]>, - data?: { - orgMembershipId?: string; - email?: string; - description?: string; - } - ) => void; - setCompleteInviteLink: (link: string) => void; + handlePopUpOpen: ( + popUpName: keyof UsePopUpState<["removeMember", "upgradePlan"]>, + data?: { + orgMembershipId?: string; + email?: string; + description?: string; + } + ) => void; + setCompleteInviteLink: (link: string) => void; }; -export const OrgMembersTable = ({ - handlePopUpOpen, - setCompleteInviteLink -}: Props) => { - const { createNotification } = useNotificationContext(); - const { subscription } = useSubscription(); - const { currentOrg } = useOrganization(); - const { user } = useUser(); - const userId = user?._id || ""; - const orgId = currentOrg?._id || ""; +export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Props) => { + const { createNotification } = useNotificationContext(); + const { subscription } = useSubscription(); + const { currentOrg } = useOrganization(); + const { user } = useUser(); + const userId = user?.id || ""; + const orgId = currentOrg?.id || ""; - const { data: roles, isLoading: isRolesLoading } = useGetRoles({ - orgId - }); - - const [searchMemberFilter, setSearchMemberFilter] = useState(""); + const { data: roles, isLoading: isRolesLoading } = useGetRoles({ + orgId + }); - const { data: serverDetails } = useFetchServerStatus(); - const { data: members, isLoading: isMembersLoading } = useGetOrgUsers(orgId); + const [searchMemberFilter, setSearchMemberFilter] = useState(""); - const { mutateAsync: addUserMutateAsync } = useAddUserToOrg(); - const { mutateAsync: updateUserOrgRole } = useUpdateOrgUserRole(); + const { data: serverDetails } = useFetchServerStatus(); + const { data: members, isLoading: isMembersLoading } = useGetOrgUsers(orgId); - const onRoleChange = async (membershipId: string, role: string) => { - if (!currentOrg?._id) return; - - try { - // TODO: replace hardcoding default role - const isCustomRole = !["admin", "member"].includes(role); - - if (isCustomRole && subscription && !subscription?.rbac) { - handlePopUpOpen("upgradePlan", { - description: "You can assign custom roles to members if you upgrade your Infisical plan." - }); - return; - } - - await updateUserOrgRole({ - organizationId: currentOrg?._id, - membershipId, role - }); + const { mutateAsync: addUserMutateAsync } = useAddUserToOrg(); + const { mutateAsync: updateUserOrgRole } = useUpdateOrgUserRole(); - createNotification({ - text: "Successfully updated user role", - type: "success" - }); - } catch (error) { - console.error(error); - createNotification({ - text: "Failed to update user role", - type: "error" - }); - } - }; - - const onResendInvite = async (email: string) => { - try { - - const { data } = await addUserMutateAsync({ - organizationId: orgId, - inviteeEmail: email - }); + const onRoleChange = async (membershipId: string, role: string) => { + if (!currentOrg?.id) return; - setCompleteInviteLink(data?.completeInviteLink || ""); - - if (!data.completeInviteLink) { - createNotification({ - text: `Successfully resent invite to ${email}`, - type: "success" - }); - } - } catch (err) { - console.error(err); - createNotification({ - text: `Failed to resend invite to ${email}`, - type: "error" - }); - } + try { + // TODO: replace hardcoding default role + const isCustomRole = !["admin", "member"].includes(role); + + if (isCustomRole && subscription && !subscription?.rbac) { + handlePopUpOpen("upgradePlan", { + description: "You can assign custom roles to members if you upgrade your Infisical plan." + }); + return; + } + + await updateUserOrgRole({ + organizationId: currentOrg?.id, + membershipId, + role + }); + + createNotification({ + text: "Successfully updated user role", + type: "success" + }); + } catch (error) { + console.error(error); + createNotification({ + text: "Failed to update user role", + type: "error" + }); } + }; - const isLoading = isMembersLoading || isRolesLoading; + const onResendInvite = async (email: string) => { + try { + const { data } = await addUserMutateAsync({ + organizationId: orgId, + inviteeEmail: email + }); - const isIamOwner = useMemo( - () => members?.find(({ user: u }) => userId === u?._id)?.role === "owner", - [userId, members] - ); - - const findRoleFromId = useCallback( - (roleId: string) => { - return (roles || []).find(({ _id: id }) => id === roleId); - }, - [roles] - ); + setCompleteInviteLink(data?.completeInviteLink || ""); - const filterdUser = useMemo( - () => - members?.filter( - ({ user: u, inviteEmail }) => - u?.firstName?.toLowerCase().includes(searchMemberFilter) || - u?.lastName?.toLowerCase().includes(searchMemberFilter) || - u?.email?.toLowerCase().includes(searchMemberFilter) || - inviteEmail?.includes(searchMemberFilter) - ), - [members, searchMemberFilter] - ); - - return ( -
- setSearchMemberFilter(e.target.value)} - leftIcon={} - placeholder="Search members..." - /> - - - - - - - - - - - {isLoading && } - {!isLoading && - filterdUser?.map( - ({ user: u, inviteEmail, role, customRole, _id: orgMembershipId, status }) => { - const name = u ? `${u.firstName} ${u.lastName}` : "-"; - const email = u?.email || inviteEmail; - return ( - - - - + + ); + } + )} + +
NameEmailRole -
{name}{email} + if (!data.completeInviteLink) { + createNotification({ + text: `Successfully resent invite to ${email}`, + type: "success" + }); + } + } catch (err) { + console.error(err); + createNotification({ + text: `Failed to resend invite to ${email}`, + type: "error" + }); + } + }; + + const isLoading = isMembersLoading || isRolesLoading; + + const isIamOwner = useMemo( + () => members?.find(({ user: u }) => userId === u?.id)?.role === "owner", + [userId, members] + ); + + const findRoleFromId = useCallback( + (roleId: string) => { + return (roles || []).find(({ id }) => id === roleId); + }, + [roles] + ); + + const filterdUser = useMemo( + () => + members?.filter( + ({ user: u, inviteEmail }) => + u?.firstName?.toLowerCase().includes(searchMemberFilter) || + u?.lastName?.toLowerCase().includes(searchMemberFilter) || + u?.email?.toLowerCase().includes(searchMemberFilter) || + inviteEmail?.includes(searchMemberFilter) + ), + [members, searchMemberFilter] + ); + + return ( +
+ setSearchMemberFilter(e.target.value)} + leftIcon={} + placeholder="Search members..." + /> + + + + + + + + + + + {isLoading && } + {!isLoading && + filterdUser?.map( + ({ user: u, inviteEmail, role, customRole, id: orgMembershipId, status }) => { + const name = u ? `${u.firstName} ${u.lastName}` : "-"; + const email = u?.email || inviteEmail; + return ( + + + + + - - - ); - } - )} - -
NameEmailRole +
{name}{email} + + {(isAllowed) => ( + <> + {status === "accepted" && ( + + )} + {(status === "invited" || status === "verified") && + serverDetails?.emailConfigured && ( + + )} + + )} + + + {userId !== u?.id && ( {(isAllowed) => ( - <> - {status === "accepted" && ( - - )} - {(status === "invited" || status === "verified") && - serverDetails?.emailConfigured && ( - - )} - + { + handlePopUpOpen("removeMember", { orgMembershipId, email }); + }} + size="lg" + colorSchema="danger" + variant="plain" + ariaLabel="update" + className="ml-4" + isDisabled={!isAllowed} + > + + )} - - {userId !== u?._id && ( - - {(isAllowed) => ( - { - handlePopUpOpen("removeMember", { orgMembershipId, email }) - }} - size="lg" - colorSchema="danger" - variant="plain" - ariaLabel="update" - className="ml-4" - isDisabled={!isAllowed} - > - - - )} - - )} -
- {!isLoading && filterdUser?.length === 0 && ( - - )} -
- -
- ); -} \ No newline at end of file + )} +
+ {!isLoading && filterdUser?.length === 0 && ( + + )} +
+
+ ); +}; diff --git a/frontend/src/views/Project/AuditLogsPage/components/LogsTableRow.tsx b/frontend/src/views/Project/AuditLogsPage/components/LogsTableRow.tsx index 7fc69400e..78f2f8245 100644 --- a/frontend/src/views/Project/AuditLogsPage/components/LogsTableRow.tsx +++ b/frontend/src/views/Project/AuditLogsPage/components/LogsTableRow.tsx @@ -158,21 +158,21 @@ export const LogsTableRow = ({ auditLog }: Props) => {

{`Name: ${event.metadata.name}`}

); - case EventType.CREATE_IDENTITY: + case EventType.CREATEidENTITY: return (

{`ID: ${event.metadata.identityId}`}

{`Name: ${event.metadata.name}`}

); - case EventType.UPDATE_IDENTITY: + case EventType.UPDATEidENTITY: return (

{`ID: ${event.metadata.identityId}`}

{`Name: ${event.metadata.name}`}

); - case EventType.DELETE_IDENTITY: + case EventType.DELETEidENTITY: return (

{`ID: ${event.metadata.identityId}`}

diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityModal.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityModal.tsx index 64252f096..c61ebefe2 100644 --- a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityModal.tsx +++ b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityModal.tsx @@ -1,34 +1,26 @@ import { useMemo } from "react"; -import { Controller, useForm } from "react-hook-form"; +import { Controller, useForm } from "react-hook-form"; import Link from "next/link"; import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; +import { Button, FormControl, Modal, ModalContent, Select, SelectItem } from "@app/components/v2"; +import { useOrganization, useWorkspace } from "@app/context"; import { - Button, - FormControl, - Modal, - ModalContent, - Select, - SelectItem, -} from "@app/components/v2"; -import { - useOrganization, - useWorkspace -} from "@app/context"; -import { - useAddIdentityToWorkspace, - useGetIdentityMembershipOrgs, - useGetRoles, - useGetWorkspaceIdentityMemberships + useAddIdentityToWorkspace, + useGetIdentityMembershipOrgs, + useGetRoles, + useGetWorkspaceIdentityMemberships } from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; -const schema = yup.object({ +const schema = yup + .object({ identityId: yup.string().required("Identity id is required"), role: yup.string() -}).required(); + }) + .required(); export type FormData = yup.InferType; @@ -37,167 +29,154 @@ type Props = { handlePopUpToggle: (popUpName: keyof UsePopUpState<["identity"]>, state?: boolean) => void; }; -export const IdentityModal = ({ - popUp, - handlePopUpToggle -}: Props) => { - - const { createNotification } = useNotificationContext(); - const { currentOrg } = useOrganization(); - const { currentWorkspace } = useWorkspace(); +export const IdentityModal = ({ popUp, handlePopUpToggle }: Props) => { + const { createNotification } = useNotificationContext(); + const { currentOrg } = useOrganization(); + const { currentWorkspace } = useWorkspace(); - const orgId = currentOrg?._id || ""; - const workspaceId = currentWorkspace?._id || ""; - - const { data: identityMembershipOrgs } = useGetIdentityMembershipOrgs(orgId); - const { data: identityMemberships } = useGetWorkspaceIdentityMemberships(workspaceId); + const orgId = currentOrg?.id || ""; + const workspaceId = currentWorkspace?.id || ""; - const { data: roles } = useGetRoles({ - orgId, - workspaceId + const { data: identityMembershipOrgs } = useGetIdentityMembershipOrgs(orgId); + const { data: identityMemberships } = useGetWorkspaceIdentityMemberships(workspaceId); + + const { data: roles } = useGetRoles({ + orgId, + workspaceId + }); + + const { mutateAsync: addIdentityToWorkspaceMutateAsync } = useAddIdentityToWorkspace(); + + const filteredIdentityMembershipOrgs = useMemo(() => { + const wsIdentityIds = new Map(); + + identityMemberships?.forEach((identityMembership) => { + wsIdentityIds.set(identityMembership.identity.id, true); }); - const { mutateAsync: addIdentityToWorkspaceMutateAsync } = useAddIdentityToWorkspace(); - - const filteredIdentityMembershipOrgs = useMemo(() => { - const wsIdentityIds = new Map(); - - identityMemberships?.forEach((identityMembership) => { - wsIdentityIds.set(identityMembership.identity._id, true); - }); - - return (identityMembershipOrgs || []).filter( - ({ identity: i }) => !wsIdentityIds.has(i._id) - ); - }, [identityMembershipOrgs, identityMemberships]); - - const { - control, - handleSubmit, - reset, - formState: { isSubmitting } - } = useForm({ - resolver: yupResolver(schema) - }); - - const onFormSubmit = async ({ + return (identityMembershipOrgs || []).filter(({ identity: i }) => !wsIdentityIds.has(i.id)); + }, [identityMembershipOrgs, identityMemberships]); + + const { + control, + handleSubmit, + reset, + formState: { isSubmitting } + } = useForm({ + resolver: yupResolver(schema) + }); + + const onFormSubmit = async ({ identityId, role }: FormData) => { + try { + await addIdentityToWorkspaceMutateAsync({ + workspaceId, identityId, - role - }: FormData) => { - try { + role: role || undefined + }); - await addIdentityToWorkspaceMutateAsync({ - workspaceId, - identityId, - role: role || undefined - }); - - createNotification({ - text: "Successfully added identity to project", - type: "success" - }); + createNotification({ + text: "Successfully added identity to project", + type: "success" + }); - reset(); - handlePopUpToggle("identity", false); - } catch (err) { - console.error(err); - const error = err as any; - const text = error?.response?.data?.message - ?? "Failed to add identity to project"; - - createNotification({ - text, - type: "error" - }); - } + reset(); + handlePopUpToggle("identity", false); + } catch (err) { + console.error(err); + const error = err as any; + const text = error?.response?.data?.message ?? "Failed to add identity to project"; + + createNotification({ + text, + type: "error" + }); } - - return ( - { - handlePopUpToggle("identity", isOpen); - reset(); - }} - > - - {filteredIdentityMembershipOrgs.length ? ( -
- ( - - - - )} - /> - ( - - - - )} - /> -
- - -
- - ) : ( -
-
All identities in your organization have already been added to this project.
- - - -
- )} -
-
- ); -} \ No newline at end of file + }; + + return ( + { + handlePopUpToggle("identity", isOpen); + reset(); + }} + > + + {filteredIdentityMembershipOrgs.length ? ( +
+ ( + + + + )} + /> + ( + + + + )} + /> +
+ + +
+ + ) : ( +
+
+ All identities in your organization have already been added to this project. +
+ + + +
+ )} +
+
+ ); +}; diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentitySection.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentitySection.tsx index 3381560ff..e0770ad9a 100644 --- a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentitySection.tsx +++ b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentitySection.tsx @@ -4,14 +4,8 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { ProjectPermissionCan } from "@app/components/permissions"; -import { - Button, - DeleteActionModal -} from "@app/components/v2"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - useWorkspace} from "@app/context"; +import { Button, DeleteActionModal } from "@app/components/v2"; +import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; import { withProjectPermission } from "@app/hoc"; import { useDeleteIdentityFromWorkspace } from "@app/hooks/api"; import { usePopUp } from "@app/hooks/usePopUp"; @@ -24,94 +18,90 @@ export const IdentitySection = withProjectPermission( const { createNotification } = useNotificationContext(); const { currentWorkspace } = useWorkspace(); - const workspaceId = currentWorkspace?._id ?? ""; + const workspaceId = currentWorkspace?.id ?? ""; const { mutateAsync: deleteMutateAsync } = useDeleteIdentityFromWorkspace(); - - const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ + + const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ "identity", "deleteIdentity", "upgradePlan" ] as const); - + const onRemoveIdentitySubmit = async (identityId: string) => { try { - await deleteMutateAsync({ - identityId, - workspaceId + identityId, + workspaceId }); createNotification({ - text: "Successfully removed identity from project", - type: "success" + text: "Successfully removed identity from project", + type: "success" }); - + handlePopUpClose("deleteIdentity"); } catch (err) { console.error(err); const error = err as any; - const text = error?.response?.data?.message ?? "Failed to remove identity from project" - + const text = error?.response?.data?.message ?? "Failed to remove identity from project"; + createNotification({ - text, - type: "error" + text, + type: "error" }); } - } + }; return ( -
-
-

- Identities -

-
- - - Documentation - - -
- - {(isAllowed) => ( - - )} - +
+
+

Identities

+
+ + + Documentation{" "} + + +
- - - handlePopUpToggle("deleteIdentity", isOpen)} - deleteKey="confirm" - onDeleteApproved={() => - onRemoveIdentitySubmit( - (popUp?.deleteIdentity?.data as { identityId: string })?.identityId - ) - } - /> + + {(isAllowed) => ( + + )} +
+ + + handlePopUpToggle("deleteIdentity", isOpen)} + deleteKey="confirm" + onDeleteApproved={() => + onRemoveIdentitySubmit( + (popUp?.deleteIdentity?.data as { identityId: string })?.identityId + ) + } + /> +
); }, { action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.Identity } -); \ No newline at end of file +); diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityTable.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityTable.tsx index 5ce979e65..91b41f153 100644 --- a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityTable.tsx +++ b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityTable.tsx @@ -5,187 +5,168 @@ import { format } from "date-fns"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { ProjectPermissionCan } from "@app/components/permissions"; import { - EmptyState, - IconButton, - Select, - SelectItem, - Table, - TableContainer, - TableSkeleton, - TBody, - Td, - Th, - THead, - Tr + EmptyState, + IconButton, + Select, + SelectItem, + Table, + TableContainer, + TableSkeleton, + TBody, + Td, + Th, + THead, + Tr } from "@app/components/v2"; import { - ProjectPermissionActions, - ProjectPermissionSub, - useOrganization, - useWorkspace, + ProjectPermissionActions, + ProjectPermissionSub, + useOrganization, + useWorkspace } from "@app/context"; import { - useGetRoles, - useGetWorkspaceIdentityMemberships, - useUpdateIdentityWorkspaceRole} from "@app/hooks/api"; + useGetRoles, + useGetWorkspaceIdentityMemberships, + useUpdateIdentityWorkspaceRole +} from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; type Props = { - handlePopUpOpen: ( - popUpName: keyof UsePopUpState<["deleteIdentity", "identity"]>, - data?: { - identityId?: string; - name?: string; - } - ) => void; + handlePopUpOpen: ( + popUpName: keyof UsePopUpState<["deleteIdentity", "identity"]>, + data?: { + identityId?: string; + name?: string; + } + ) => void; }; -export const IdentityTable = ({ - handlePopUpOpen -}: Props) => { - const { createNotification } = useNotificationContext(); - const { currentOrg } = useOrganization(); - const { currentWorkspace } = useWorkspace(); - const orgId = currentOrg?._id || ""; - const workspaceId = currentWorkspace?._id || ""; - const { data, isLoading } = useGetWorkspaceIdentityMemberships(currentWorkspace?._id || ""); - - const { data: roles } = useGetRoles({ - orgId, - workspaceId - }); +export const IdentityTable = ({ handlePopUpOpen }: Props) => { + const { createNotification } = useNotificationContext(); + const { currentOrg } = useOrganization(); + const { currentWorkspace } = useWorkspace(); + const orgId = currentOrg?.id || ""; + const workspaceId = currentWorkspace?.id || ""; + const { data, isLoading } = useGetWorkspaceIdentityMemberships(currentWorkspace?.id || ""); - const { mutateAsync: updateMutateAsync } = useUpdateIdentityWorkspaceRole(); + const { data: roles } = useGetRoles({ + orgId, + workspaceId + }); - const handleChangeRole = async ({ + const { mutateAsync: updateMutateAsync } = useUpdateIdentityWorkspaceRole(); + + const handleChangeRole = async ({ identityId, role }: { identityId: string; role: string }) => { + try { + await updateMutateAsync({ identityId, + workspaceId, role - }: { - identityId: string; - role: string; - }) => { - try { + }); - await updateMutateAsync({ - identityId, - workspaceId, - role - }); - - createNotification({ - text: "Successfully updated identity role", - type: "success" - }); - } catch (err) { - console.error(err); - const error = err as any; - const text = error?.response?.data?.message ?? "Failed to update identity role" - - createNotification({ - text, - type: "error" - }); - } + createNotification({ + text: "Successfully updated identity role", + type: "success" + }); + } catch (err) { + console.error(err); + const error = err as any; + const text = error?.response?.data?.message ?? "Failed to update identity role"; + + createNotification({ + text, + type: "error" + }); } - - return ( - - - - - - - - - - - {isLoading && } - {!isLoading && - data && - data.length > 0 && - data.map(({ - identity: { - _id, - name - }, - role, - customRole, - createdAt - }) => { + }; + + return ( + +
NameRoleAdded on -
+ + + + + + + + + {isLoading && } + {!isLoading && + data && + data.length > 0 && + data.map(({ identity: { id, name }, role, customRole, createdAt }) => { + return ( + + + - - - - - + ); - })} - {!isLoading && data && data?.length === 0 && ( - - - - )} - -
NameRoleAdded on +
{name} + + {(isAllowed) => { return ( -
{name} - - {(isAllowed) => { - return ( - - ); - }} - - {format(new Date(createdAt), "yyyy-MM-dd")} - - {(isAllowed) => ( - { - handlePopUpOpen("deleteIdentity", { - identityId: _id, - name - }); - }} - size="lg" - colorSchema="danger" - variant="plain" - ariaLabel="update" - className="ml-4" - isDisabled={!isAllowed} - > - - - )} - -
- -
-
- ); -} \ No newline at end of file + }} + + + {format(new Date(createdAt), "yyyy-MM-dd")} + + + {(isAllowed) => ( + { + handlePopUpOpen("deleteIdentity", { + identityId: id, + name + }); + }} + size="lg" + colorSchema="danger" + variant="plain" + ariaLabel="update" + className="ml-4" + isDisabled={!isAllowed} + > + + + )} + + + + ); + })} + {!isLoading && data && data?.length === 0 && ( + + + + + + )} + + + + ); +}; diff --git a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/SSOModal.tsx b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/SSOModal.tsx index 6901e4589..0716a5cda 100644 --- a/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/SSOModal.tsx +++ b/frontend/src/views/Settings/OrgSettingsPage/components/OrgAuthTab/SSOModal.tsx @@ -190,7 +190,7 @@ export const SSOModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props) <>

{renderLabels(authProvider).acsUrl}

-

{`${window.origin}/api/v1/sso/saml2/${data._id}`}

+

{`${window.origin}/api/v1/sso/saml2/${data.id}`}