Resolve PR review issues

This commit is contained in:
Tuan Dang
2024-04-11 20:44:38 -07:00
parent fa1b236f26
commit 747acfe070
14 changed files with 272 additions and 297 deletions
+6 -3
View File
@@ -1,9 +1,12 @@
export {
useAddGroupToWorkspace,
useDeleteGroupFromWorkspace,
useUpdateGroupWorkspaceRole
} from "./mutations";
export {
useAddIdentityToWorkspace,
useCreateWorkspace,
useCreateWsEnvironment,
useDeleteGroupFromWorkspace,
useDeleteIdentityFromWorkspace,
useDeleteUserFromWorkspace,
useDeleteWorkspace,
@@ -22,8 +25,8 @@ export {
useNameWorkspaceSecrets,
useRenameWorkspace,
useToggleAutoCapitalization,
useUpdateGroupWorkspaceRole,
useUpdateIdentityWorkspaceRole,
useUpdateUserWorkspaceRole,
useUpdateWsEnvironment,
useUpgradeProject} from "./queries";
useUpgradeProject
} from "./queries";
@@ -0,0 +1,64 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { workspaceKeys } from "./queries";
import { TUpdateWorkspaceGroupRoleDTO } from "./types";
export const useAddGroupToWorkspace = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
groupSlug,
projectSlug,
role
}: {
groupSlug: string;
projectSlug: string;
role?: string;
}) => {
const {
data: { groupMembership }
} = await apiRequest.post(`/api/v2/workspace/${projectSlug}/groups/${groupSlug}`, {
role
});
return groupMembership;
},
onSuccess: (_, { projectSlug }) => {
queryClient.invalidateQueries(workspaceKeys.getWorkspaceGroupMemberships(projectSlug));
}
});
};
export const useUpdateGroupWorkspaceRole = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ groupSlug, projectSlug, roles }: TUpdateWorkspaceGroupRoleDTO) => {
const {
data: { groupMembership }
} = await apiRequest.patch(`/api/v2/workspace/${projectSlug}/groups/${groupSlug}`, {
roles
});
return groupMembership;
},
onSuccess: (_, { projectSlug }) => {
queryClient.invalidateQueries(workspaceKeys.getWorkspaceGroupMemberships(projectSlug));
}
});
};
export const useDeleteGroupFromWorkspace = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ groupSlug, projectSlug }: { groupSlug: string; projectSlug: string }) => {
const {
data: { groupMembership }
} = await apiRequest.delete(`/api/v2/workspace/${projectSlug}/groups/${groupSlug}`);
return groupMembership;
},
onSuccess: (_, { projectSlug }) => {
queryClient.invalidateQueries(workspaceKeys.getWorkspaceGroupMemberships(projectSlug));
}
});
};
+1 -73
View File
@@ -17,7 +17,6 @@ import {
RenameWorkspaceDTO,
TGetUpgradeProjectStatusDTO,
ToggleAutoCapitalizationDTO,
TUpdateWorkspaceGroupRoleDTO,
TUpdateWorkspaceIdentityRoleDTO,
TUpdateWorkspaceUserRoleDTO,
UpdateEnvironmentDTO,
@@ -455,83 +454,12 @@ export const useGetWorkspaceIdentityMemberships = (workspaceId: string) => {
});
};
export const useAddGroupToWorkspace = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
groupSlug,
projectSlug,
role
}: {
groupSlug: string;
projectSlug: string;
role?: string;
}) => {
const {
data: { groupMembership }
} = await apiRequest.post(
`/api/v2/workspace/${projectSlug}/groups/${groupSlug}`,
{
role
}
);
return groupMembership;
},
onSuccess: (_, { projectSlug }) => {
queryClient.invalidateQueries(workspaceKeys.getWorkspaceGroupMemberships(projectSlug));
}
});
};
export const useUpdateGroupWorkspaceRole = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({ groupSlug, projectSlug, roles }: TUpdateWorkspaceGroupRoleDTO) => {
const {
data: { groupMembership }
} = await apiRequest.patch(
`/api/v2/workspace/${projectSlug}/groups/${groupSlug}`, {
roles
}
);
return groupMembership;
},
onSuccess: (_, { projectSlug }) => {
queryClient.invalidateQueries(workspaceKeys.getWorkspaceGroupMemberships(projectSlug));
}
});
};
export const useDeleteGroupFromWorkspace = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
groupSlug,
projectSlug
}: {
groupSlug: string;
projectSlug: string;
}) => {
const {
data: { groupMembership }
} = await apiRequest.delete(
`/api/v2/workspace/${projectSlug}/groups/${groupSlug}`
);
return groupMembership;
},
onSuccess: (_, { projectSlug }) => {
queryClient.invalidateQueries(workspaceKeys.getWorkspaceGroupMemberships(projectSlug));
}
});
};
export const useListWorkspaceGroups = (projectSlug: string) => {
return useQuery({
queryKey: workspaceKeys.getWorkspaceGroupMemberships(projectSlug),
queryFn: async () => {
const {
data: { groupMemberships }
data: { groupMemberships }
} = await apiRequest.get<{ groupMemberships: TGroupMembership[] }>(
`/api/v2/workspace/${projectSlug}/groups`
);
@@ -4,19 +4,23 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { createNotification } from "@app/components/notifications";
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";
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()
name: z.string().min(1, "Name cannot be empty").max(50, "Name must be 50 characters or fewer"),
slug: z.string().min(5, "Slug cannot be empty").max(36, "Slug must be 36 characters or fewer"),
role: z.string()
});
export type TGroupFormData = z.infer<typeof GroupFormSchema>;
@@ -27,185 +31,158 @@ type Props = {
handlePopUpToggle: (popUpName: keyof UsePopUpState<["group"]>, state?: boolean) => void;
};
export const OrgGroupModal = ({
popUp,
handlePopUpClose,
handlePopUpToggle
}: Props) => {
const { currentOrg } = useOrganization();
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;
};
};
export const OrgGroupModal = ({ popUp, handlePopUpClose, handlePopUpToggle }: Props) => {
const { currentOrg } = useOrganization();
const { data: roles } = useGetOrgRoles(currentOrg?.id || "");
const { mutateAsync: createMutateAsync, isLoading: createIsLoading } = useCreateGroup();
const { mutateAsync: updateMutateAsync, isLoading: updateIsLoading } = useUpdateGroup();
if (!roles?.length) return;
const { control, handleSubmit, reset } = useForm<TGroupFormData>({
resolver: zodResolver(GroupFormSchema)
});
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;
useEffect(() => {
const group = popUp?.group?.data as {
groupId: string;
name: string;
slug: string;
role: string;
customRole: {
name: string;
slug: string;
};
};
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);
reset();
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"
});
}
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
});
}
return (
<Modal
isOpen={popUp?.group?.isOpen}
onOpenChange={(isOpen) => {
handlePopUpToggle("group", isOpen);
reset();
}}
>
<ModalContent title={`${popUp?.group?.data ? "Update" : "Create"} Group`}>
<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>
);
}
}, [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);
reset();
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`}>
<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>
);
};
@@ -5,7 +5,13 @@ import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { withProjectPermission } from "@app/hoc";
import { GroupsTab, IdentityTab, MemberListTab, ProjectRoleListTab, ServiceTokenTab } from "./components";
import {
GroupsTab,
IdentityTab,
MemberListTab,
ProjectRoleListTab,
ServiceTokenTab
} from "./components";
enum TabSections {
Member = "members",
@@ -18,8 +24,6 @@ enum TabSections {
export const MembersPage = withProjectPermission(
() => {
const { currentWorkspace } = useWorkspace();
console.log("currentWorkspace: ", currentWorkspace);
return (
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
<div className="mx-auto mb-6 w-full max-w-7xl py-6 px-6">
@@ -44,7 +48,7 @@ export const MembersPage = withProjectPermission(
{currentWorkspace?.version && currentWorkspace.version > 1 && (
<TabPanel value={TabSections.Groups}>
<motion.div
key="panel-1"
key="panel-groups"
transition={{ duration: 0.15 }}
initial={{ opacity: 0, translateX: 30 }}
animate={{ opacity: 1, translateX: 0 }}