mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 00:29:34 +00:00
Finish basic CRUD groups
This commit is contained in:
@@ -16,6 +16,7 @@ export enum OrgPermissionSubjects {
|
||||
Scim = "scim",
|
||||
Sso = "sso",
|
||||
Ldap = "ldap",
|
||||
Groups = "groups",
|
||||
Billing = "billing",
|
||||
SecretScanning = "secret-scanning",
|
||||
Identity = "identity"
|
||||
@@ -31,6 +32,7 @@ export type OrgPermissionSet =
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Scim]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Sso]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Ldap]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Groups]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.SecretScanning]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Billing]
|
||||
| [OrgPermissionActions, OrgPermissionSubjects.Identity];
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export {
|
||||
useCreateGroup,
|
||||
useDeleteGroup,
|
||||
useUpdateGroup} from "./mutations";
|
||||
@@ -0,0 +1,87 @@
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { organizationKeys } from "../organization/queries";
|
||||
import { TGroup } from "./types";
|
||||
|
||||
export const useCreateGroup = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
name,
|
||||
slug,
|
||||
organizationId,
|
||||
role
|
||||
}: {
|
||||
name: string;
|
||||
slug: string;
|
||||
organizationId: string;
|
||||
role?: string;
|
||||
}) => {
|
||||
const {
|
||||
data: group
|
||||
} = await apiRequest.post<TGroup>("/api/v1/groups", {
|
||||
name,
|
||||
slug,
|
||||
organizationId,
|
||||
role
|
||||
});
|
||||
|
||||
return group;
|
||||
},
|
||||
onSuccess: (_, { organizationId }) => {
|
||||
queryClient.invalidateQueries(organizationKeys.getOrgGroups(organizationId));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateGroup = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
currentSlug,
|
||||
name,
|
||||
slug,
|
||||
role
|
||||
}: {
|
||||
currentSlug: string;
|
||||
name?: string;
|
||||
slug?: string;
|
||||
role?: string;
|
||||
}) => {
|
||||
const {
|
||||
data: group
|
||||
} = await apiRequest.patch<TGroup>(`/api/v1/groups/${currentSlug}`, {
|
||||
name,
|
||||
slug,
|
||||
role
|
||||
});
|
||||
|
||||
return group;
|
||||
},
|
||||
onSuccess: ({ orgId }) => {
|
||||
queryClient.invalidateQueries(organizationKeys.getOrgGroups(orgId));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteGroup = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
slug
|
||||
}: {
|
||||
slug: string;
|
||||
}) => {
|
||||
const {
|
||||
data: group
|
||||
} = await apiRequest.delete<TGroup>(`/api/v1/groups/${slug}`);
|
||||
|
||||
return group;
|
||||
},
|
||||
onSuccess: ({ orgId }) => {
|
||||
queryClient.invalidateQueries(organizationKeys.getOrgGroups(orgId));
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
export type TGroup = {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
orgId: string;
|
||||
createAt: string;
|
||||
updatedAt: string;
|
||||
role: string;
|
||||
};
|
||||
@@ -3,6 +3,7 @@ export * from "./apiKeys";
|
||||
export * from "./auditLogs";
|
||||
export * from "./auth";
|
||||
export * from "./bots";
|
||||
export * from "./groups";
|
||||
export * from "./identities";
|
||||
export * from "./incidentContacts";
|
||||
export * from "./integrationAuth";
|
||||
|
||||
@@ -7,6 +7,7 @@ export {
|
||||
useDeleteOrgPmtMethod,
|
||||
useDeleteOrgTaxId,
|
||||
useGetIdentityMembershipOrgs,
|
||||
useGetOrganizationGroups,
|
||||
useGetOrganizations,
|
||||
useGetOrgBillingDetails,
|
||||
useGetOrgInvoices,
|
||||
@@ -17,6 +18,5 @@ export {
|
||||
useGetOrgPmtMethods,
|
||||
useGetOrgTaxIds,
|
||||
useGetOrgTrialUrl,
|
||||
useUpdateOrg,
|
||||
useUpdateOrgBillingDetails
|
||||
} from "./queries";
|
||||
useUpdateOrg,
|
||||
useUpdateOrgBillingDetails} from "./queries";
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { apiRequest } from "@app/config/request";
|
||||
|
||||
import { TGroup } from "../groups/types";
|
||||
import { IdentityMembershipOrg } from "../identities/types";
|
||||
import {
|
||||
BillingDetails,
|
||||
@@ -27,8 +28,8 @@ export const organizationKeys = {
|
||||
getOrgTaxIds: (orgId: string) => [{ orgId }, "organization-tax-ids"] as const,
|
||||
getOrgInvoices: (orgId: string) => [{ orgId }, "organization-invoices"] as const,
|
||||
getOrgLicenses: (orgId: string) => [{ orgId }, "organization-licenses"] as const,
|
||||
getOrgIdentityMemberships: (orgId: string) =>
|
||||
[{ orgId }, "organization-identity-memberships"] as const
|
||||
getOrgIdentityMemberships: (orgId: string) => [{ orgId }, "organization-identity-memberships"] as const,
|
||||
getOrgGroups: (orgId: string) => [{ orgId }, "organization-groups"] as const
|
||||
};
|
||||
|
||||
export const fetchOrganizations = async () => {
|
||||
@@ -404,3 +405,16 @@ export const useDeleteOrgById = () => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetOrganizationGroups = (organizationId: string) => {
|
||||
return useQuery({
|
||||
queryKey: organizationKeys.getOrgGroups(organizationId),
|
||||
queryFn: async () => {
|
||||
const {
|
||||
data: { groups }
|
||||
} = await apiRequest.get<{ groups: TGroup[] }>(`/api/v1/organization/${organizationId}/groups`);
|
||||
|
||||
return groups;
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -20,6 +20,7 @@ export type SubscriptionPlan = {
|
||||
samlSSO: boolean;
|
||||
scim: boolean;
|
||||
ldap: boolean;
|
||||
groups: boolean;
|
||||
status:
|
||||
| "incomplete"
|
||||
| "incomplete_expired"
|
||||
|
||||
@@ -3,10 +3,16 @@ import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
|
||||
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
|
||||
import { withPermission } from "@app/hoc";
|
||||
|
||||
import { OrgIdentityTab, OrgMembersTab, OrgRoleTabSection } from "./components";
|
||||
import {
|
||||
OrgGroupsTab,
|
||||
OrgIdentityTab,
|
||||
OrgMembersTab,
|
||||
OrgRoleTabSection
|
||||
} from "./components";
|
||||
|
||||
enum TabSections {
|
||||
Member = "members",
|
||||
Groups = "groups",
|
||||
Roles = "roles",
|
||||
Identities = "identities"
|
||||
}
|
||||
@@ -20,6 +26,7 @@ export const MembersPage = withPermission(
|
||||
<Tabs defaultValue={TabSections.Member}>
|
||||
<TabList>
|
||||
<Tab value={TabSections.Member}>People</Tab>
|
||||
<Tab value={TabSections.Groups}>Groups</Tab>
|
||||
<Tab value={TabSections.Identities}>
|
||||
<div className="flex items-center">
|
||||
<p>Machine Identities</p>
|
||||
@@ -33,6 +40,9 @@ export const MembersPage = withPermission(
|
||||
<TabPanel value={TabSections.Member}>
|
||||
<OrgMembersTab />
|
||||
</TabPanel>
|
||||
<TabPanel value={TabSections.Groups}>
|
||||
<OrgGroupsTab />
|
||||
</TabPanel>
|
||||
<TabPanel value={TabSections.Identities}>
|
||||
<OrgIdentityTab />
|
||||
</TabPanel>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
import { OrgGroupsSection } from "./components";
|
||||
|
||||
export const OrgGroupsTab = () => {
|
||||
return (
|
||||
<motion.div
|
||||
key="panel-org-groups"
|
||||
transition={{ duration: 0.15 }}
|
||||
initial={{ opacity: 0, translateX: 30 }}
|
||||
animate={{ opacity: 1, translateX: 0 }}
|
||||
exit={{ opacity: 0, translateX: 30 }}
|
||||
>
|
||||
<OrgGroupsSection />
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
import { useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { Button, FormControl, Input, Modal, ModalContent, Select, SelectItem } from "@app/components/v2";
|
||||
import { useOrganization } from "@app/context";
|
||||
import {
|
||||
useCreateGroup,
|
||||
useGetOrgRoles,
|
||||
useUpdateGroup
|
||||
} from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
const GroupFormSchema = z.object({
|
||||
name: z.string().min(1, "Name cannot be empty").max(50, "Name must be 50 characters or fewer"),
|
||||
slug: z.string().min(1, "Slug cannot be empty").max(36, "Slug must be 36 characters or fewer"),
|
||||
role: z.string()
|
||||
});
|
||||
|
||||
export type TGroupFormData = z.infer<typeof GroupFormSchema>;
|
||||
|
||||
type Props = {
|
||||
popUp: UsePopUpState<["group"]>;
|
||||
handlePopUpClose: (popUpName: keyof UsePopUpState<["group"]>) => void;
|
||||
handlePopUpToggle: (popUpName: keyof UsePopUpState<["group"]>, state?: boolean) => void;
|
||||
};
|
||||
|
||||
export const OrgGroupModal = ({
|
||||
popUp,
|
||||
handlePopUpClose,
|
||||
handlePopUpToggle
|
||||
}: Props) => {
|
||||
const { currentOrg } = useOrganization();
|
||||
const { createNotification } = useNotificationContext();
|
||||
const { data: roles } = useGetOrgRoles(currentOrg?.id || "");
|
||||
const { mutateAsync: createMutateAsync, isLoading: createIsLoading } = useCreateGroup();
|
||||
const { mutateAsync: updateMutateAsync, isLoading: updateIsLoading } = useUpdateGroup();
|
||||
|
||||
const {
|
||||
control,
|
||||
handleSubmit,
|
||||
reset,
|
||||
} = useForm<TGroupFormData>({
|
||||
resolver: zodResolver(GroupFormSchema)
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const group = popUp?.group?.data as {
|
||||
groupId: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
role: string;
|
||||
customRole: {
|
||||
name: string;
|
||||
slug: string;
|
||||
};
|
||||
};
|
||||
|
||||
if (!roles?.length) return;
|
||||
|
||||
if (group) {
|
||||
reset({
|
||||
name: group.name,
|
||||
slug: group.slug,
|
||||
role: group?.customRole?.slug ?? group.role
|
||||
});
|
||||
} else {
|
||||
reset({
|
||||
name: "",
|
||||
slug: "",
|
||||
role: roles[0].slug
|
||||
});
|
||||
}
|
||||
}, [popUp?.group?.data, roles]);
|
||||
|
||||
const onGroupModalSubmit = async ({
|
||||
name,
|
||||
slug,
|
||||
role
|
||||
}: TGroupFormData) => {
|
||||
try {
|
||||
if (!currentOrg?.id) return;
|
||||
|
||||
const group = popUp?.group?.data as {
|
||||
groupId: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
};
|
||||
|
||||
if (group) {
|
||||
await updateMutateAsync({
|
||||
currentSlug: group.slug,
|
||||
name,
|
||||
slug,
|
||||
role: role || undefined
|
||||
});
|
||||
} else {
|
||||
await createMutateAsync({
|
||||
name,
|
||||
slug,
|
||||
organizationId: currentOrg.id,
|
||||
role: role || undefined
|
||||
});
|
||||
}
|
||||
handlePopUpToggle("group", false);
|
||||
|
||||
createNotification({
|
||||
text: `Successfully ${popUp?.group?.data ? "updated" : "created"} group`,
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
createNotification({
|
||||
text: `Failed to ${popUp?.group?.data ? "updated" : "created"} group`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={popUp?.group?.isOpen}
|
||||
onOpenChange={(isOpen) => {
|
||||
handlePopUpToggle("group", isOpen);
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
<ModalContent
|
||||
title={`${popUp?.group?.data ? "Update" : "Create"} Group`}
|
||||
subTitle="Manage users more easily with groups"
|
||||
>
|
||||
<form onSubmit={handleSubmit(onGroupModalSubmit)}>
|
||||
<Controller
|
||||
control={control}
|
||||
name="name"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Name"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Engineering"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="slug"
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label="Slug"
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="engineering"
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="role"
|
||||
defaultValue=""
|
||||
render={({ field: { onChange, ...field }, fieldState: { error } }) => (
|
||||
<FormControl
|
||||
label={`${popUp?.group?.data ? "Update" : ""} Role`}
|
||||
errorText={error?.message}
|
||||
isError={Boolean(error)}
|
||||
className="mt-4"
|
||||
>
|
||||
<Select
|
||||
defaultValue={field.value}
|
||||
{...field}
|
||||
onValueChange={(e) => onChange(e)}
|
||||
className="w-full"
|
||||
>
|
||||
{(roles || []).map(({ name, slug }) => (
|
||||
<SelectItem value={slug} key={`org-group-role-${slug}`}>
|
||||
{name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
/>
|
||||
<div className="mt-8 flex items-center">
|
||||
<Button
|
||||
className="mr-4"
|
||||
size="sm"
|
||||
type="submit"
|
||||
isLoading={createIsLoading || updateIsLoading}
|
||||
>
|
||||
{!popUp?.group?.data ? "Create" : "Update"}
|
||||
</Button>
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
variant="plain"
|
||||
onClick={() => handlePopUpClose("group")}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
);
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
import { faPlus } 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,
|
||||
DeleteActionModal,
|
||||
UpgradePlanModal
|
||||
} from "@app/components/v2";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
useSubscription
|
||||
} from "@app/context";
|
||||
import { useDeleteGroup } from "@app/hooks/api";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
import { OrgGroupModal } from "./OrgGroupModal";
|
||||
import { OrgGroupsTable } from "./OrgGroupsTable";
|
||||
|
||||
export const OrgGroupsSection = () => {
|
||||
const { createNotification } = useNotificationContext();
|
||||
const { subscription } = useSubscription();
|
||||
const { mutateAsync: deleteMutateAsync } = useDeleteGroup();
|
||||
|
||||
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
|
||||
"group",
|
||||
"deleteGroup",
|
||||
"upgradePlan"
|
||||
] as const);
|
||||
|
||||
const handleAddGroupModal = () => {
|
||||
if (!subscription?.groups) {
|
||||
handlePopUpOpen("upgradePlan", {
|
||||
description: "You can manage users more efficiently with groups if you upgrade your Infisical plan."
|
||||
});
|
||||
} else {
|
||||
handlePopUpOpen("group");
|
||||
}
|
||||
}
|
||||
|
||||
const onDeleteGroupSubmit = async ({
|
||||
name,
|
||||
slug
|
||||
}: {
|
||||
name: string;
|
||||
slug: string;
|
||||
}) => {
|
||||
try {
|
||||
await deleteMutateAsync({
|
||||
slug
|
||||
});
|
||||
createNotification({
|
||||
text: `Successfully deleted the group named ${name}`,
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: `Failed to delete the group named ${name}`,
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
|
||||
handlePopUpClose("deleteGroup");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="mb-4 flex justify-between">
|
||||
<p className="text-xl font-semibold text-mineshaft-100">Groups</p>
|
||||
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.Groups}>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
colorSchema="primary"
|
||||
type="submit"
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={() => handleAddGroupModal()}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
Create Group
|
||||
</Button>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</div>
|
||||
<OrgGroupsTable
|
||||
handlePopUpOpen={handlePopUpOpen}
|
||||
/>
|
||||
<OrgGroupModal
|
||||
popUp={popUp}
|
||||
handlePopUpClose={handlePopUpClose}
|
||||
handlePopUpToggle={handlePopUpToggle}
|
||||
/>
|
||||
<DeleteActionModal
|
||||
isOpen={popUp.deleteGroup.isOpen}
|
||||
title={`Are you sure want to delete the group named ${
|
||||
(popUp?.deleteGroup?.data as { name: string })?.name || ""
|
||||
}?`}
|
||||
onChange={(isOpen) => handlePopUpToggle("deleteGroup", isOpen)}
|
||||
deleteKey="confirm"
|
||||
onDeleteApproved={() =>
|
||||
onDeleteGroupSubmit(
|
||||
(popUp?.deleteGroup?.data as { name: string; slug: string })
|
||||
)
|
||||
}
|
||||
/>
|
||||
<UpgradePlanModal
|
||||
isOpen={popUp.upgradePlan.isOpen}
|
||||
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
|
||||
text={(popUp.upgradePlan?.data as { description: string })?.description}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
import { useState } from "react";
|
||||
// import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||
import { faMagnifyingGlass, faPencil, faUsers, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { OrgPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
// Button,
|
||||
EmptyState,
|
||||
IconButton,
|
||||
Input,
|
||||
// Select,
|
||||
// SelectItem,
|
||||
Table,
|
||||
TableContainer,
|
||||
TableSkeleton,
|
||||
TBody,
|
||||
Td,
|
||||
Th,
|
||||
THead,
|
||||
Tooltip,
|
||||
Tr} from "@app/components/v2";
|
||||
import {
|
||||
OrgPermissionActions,
|
||||
OrgPermissionSubjects,
|
||||
useOrganization} from "@app/context";
|
||||
import {
|
||||
useGetOrganizationGroups,
|
||||
// useGetOrgRoles
|
||||
} from "@app/hooks/api";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
type Props = {
|
||||
handlePopUpOpen: (
|
||||
popUpName: keyof UsePopUpState<
|
||||
["group", "deleteGroup"]
|
||||
>,
|
||||
data?: {
|
||||
groupId?: string;
|
||||
name?: string;
|
||||
slug?: string;
|
||||
}
|
||||
) => void;
|
||||
};
|
||||
|
||||
export const OrgGroupsTable = ({
|
||||
handlePopUpOpen
|
||||
}: Props) => {
|
||||
// const { createNotification } = useNotificationContext();
|
||||
const [searchGroupsFilter, setSearchGroupsFilter] = useState("");
|
||||
const { currentOrg } = useOrganization();
|
||||
const orgId = currentOrg?.id || "";
|
||||
const { isLoading, data: groups } = useGetOrganizationGroups(orgId);
|
||||
|
||||
// const { data: roles } = useGetOrgRoles(orgId);
|
||||
|
||||
console.log("OrgGroupsTable groups: ", groups);
|
||||
console.log("OrgGroupsTable roles: ", groups);
|
||||
|
||||
// const handleChangeRole = ({
|
||||
// groupId,
|
||||
// role
|
||||
// }: {
|
||||
// groupId: string;
|
||||
// role: string;
|
||||
// }) => {
|
||||
// try {
|
||||
|
||||
// // TODO
|
||||
|
||||
// createNotification({
|
||||
// text: "Successfully updated group role",
|
||||
// type: "success"
|
||||
// });
|
||||
// } catch (err) {
|
||||
// console.error(err);
|
||||
|
||||
// createNotification({
|
||||
// text: "Failed to update group role",
|
||||
// type: "error"
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Input
|
||||
value={searchGroupsFilter}
|
||||
onChange={(e) => setSearchGroupsFilter(e.target.value)}
|
||||
leftIcon={<FontAwesomeIcon icon={faMagnifyingGlass} />}
|
||||
placeholder="Search groups..."
|
||||
/>
|
||||
<TableContainer className="mt-4">
|
||||
<Table>
|
||||
<THead>
|
||||
<Tr>
|
||||
<Th>Name</Th>
|
||||
<Th>Slug</Th>
|
||||
<Th>Role</Th>
|
||||
<Th className="w-5" />
|
||||
</Tr>
|
||||
</THead>
|
||||
<TBody>
|
||||
{isLoading && <TableSkeleton columns={4} innerKey="org-groups" />}
|
||||
{!isLoading && groups?.map(({ id, name, slug }) => {
|
||||
return (
|
||||
<Tr className="h-10" key={`org-group-${id}`}>
|
||||
<Td>{name}</Td>
|
||||
<Td>{slug}</Td>
|
||||
<Td>N/A</Td>
|
||||
<Td>
|
||||
<div className="flex items-center justify-end">
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Edit}
|
||||
a={OrgPermissionSubjects.Groups}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Tooltip content="Edit group">
|
||||
<IconButton
|
||||
onClick={async () => {
|
||||
handlePopUpOpen("group", {
|
||||
groupId: id,
|
||||
name,
|
||||
slug
|
||||
});
|
||||
}}
|
||||
size="lg"
|
||||
colorSchema="primary"
|
||||
variant="plain"
|
||||
ariaLabel="update"
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
<FontAwesomeIcon icon={faPencil} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
<OrgPermissionCan
|
||||
I={OrgPermissionActions.Delete}
|
||||
a={OrgPermissionSubjects.Groups}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Tooltip content="Delete group">
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
console.log("Delete group");
|
||||
handlePopUpOpen("deleteGroup", {
|
||||
slug,
|
||||
name
|
||||
});
|
||||
}}
|
||||
size="lg"
|
||||
colorSchema="danger"
|
||||
variant="plain"
|
||||
ariaLabel="update"
|
||||
className="ml-4"
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
<FontAwesomeIcon icon={faXmark} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</OrgPermissionCan>
|
||||
</div>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
</TBody>
|
||||
</Table>
|
||||
{groups?.length === 0 && (
|
||||
<EmptyState title="No groups found" icon={faUsers} />
|
||||
)}
|
||||
</TableContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export { OrgGroupsSection } from "./OrgGroupsSection";
|
||||
@@ -0,0 +1 @@
|
||||
export { OrgGroupsSection } from "./OrgGroupsSection";
|
||||
@@ -0,0 +1 @@
|
||||
export { OrgGroupsTab } from "./OrgGroupsTab";
|
||||
+1
-1
@@ -140,7 +140,7 @@ export const IdentityModal = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Pro
|
||||
const error = err as any;
|
||||
const text =
|
||||
error?.response?.data?.message ??
|
||||
`Failed to ${popUp?.identity?.data ? "updated" : "created"} identity`;
|
||||
`Failed to ${popUp?.identity?.data ? "update" : "create"} identity`;
|
||||
|
||||
createNotification({
|
||||
text,
|
||||
|
||||
+3
@@ -52,6 +52,9 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => {
|
||||
const { data, isLoading } = useGetIdentityMembershipOrgs(orgId);
|
||||
|
||||
const { data: roles } = useGetOrgRoles(orgId);
|
||||
|
||||
console.log("IdentityTable data: ", data);
|
||||
console.log("IdentityTable roles: ", roles);
|
||||
|
||||
const handleChangeRole = async ({ identityId, role }: { identityId: string; role: string }) => {
|
||||
try {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { OrgMembersSection } from "./components";
|
||||
export const OrgMembersTab = () => {
|
||||
return (
|
||||
<motion.div
|
||||
key="panel-service-token"
|
||||
key="panel-org-members"
|
||||
transition={{ duration: 0.15 }}
|
||||
initial={{ opacity: 0, translateX: 30 }}
|
||||
animate={{ opacity: 1, translateX: 0 }}
|
||||
|
||||
+3
-1
@@ -62,6 +62,8 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Prop
|
||||
|
||||
const { data: serverDetails } = useFetchServerStatus();
|
||||
const { data: members, isLoading: isMembersLoading } = useGetOrgUsers(orgId);
|
||||
|
||||
console.log("OrgGroupsTable members: ", members);
|
||||
|
||||
const { mutateAsync: addUserMutateAsync } = useAddUserToOrg();
|
||||
const { mutateAsync: updateUserOrgRole } = useUpdateOrgUserRole();
|
||||
@@ -263,7 +265,7 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Prop
|
||||
</TBody>
|
||||
</Table>
|
||||
{!isLoading && filterdUser?.length === 0 && (
|
||||
<EmptyState title="No project members found" icon={faUsers} />
|
||||
<EmptyState title="No organization members found" icon={faUsers} />
|
||||
)}
|
||||
</TableContainer>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export { OrgGroupsTab } from "./OrgGroupsTab";
|
||||
export { OrgIdentityTab } from "./OrgIdentityTab";
|
||||
export { OrgMembersTab } from "./OrgMembersTab";
|
||||
export { OrgRoleTabSection } from "./OrgRoleTabSection";
|
||||
Reference in New Issue
Block a user