Skip to content

Commit 6ab9f30

Browse files
authored
add global permission form (#4615)
1 parent f2bf653 commit 6ab9f30

File tree

3 files changed

+251
-2
lines changed

3 files changed

+251
-2
lines changed

frontend/app/src/components/form/object-form.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
ACCOUNT_GROUP_OBJECT,
77
ACCOUNT_OBJECT,
88
ACCOUNT_ROLE_OBJECT,
9+
GLOBAL_PERMISSION_OBJECT,
910
NUMBER_POOL_OBJECT,
1011
OBJECT_PERMISSION_OBJECT,
1112
READONLY_REPOSITORY_KIND,
@@ -18,6 +19,7 @@ import { NumberPoolForm } from "@/screens/resource-manager/number-pool-form";
1819
import { AccountForm } from "@/screens/role-management/account-form";
1920
import { AccountGroupForm } from "@/screens/role-management/account-group-form";
2021
import { AccountRoleForm } from "@/screens/role-management/account-role-form";
22+
import { GlobalPermissionForm } from "@/screens/role-management/global-permissions-form";
2123
import { ObjectPermissionForm } from "@/screens/role-management/object-permissions-form";
2224
import { AttributeType, RelationshipType } from "@/utils/getObjectItemDisplayValue";
2325
import { Suspense, lazy } from "react";
@@ -76,6 +78,10 @@ const ObjectForm = ({ kind, currentProfiles, ...props }: ObjectFormProps) => {
7678
return <AccountRoleForm {...props} />;
7779
}
7880

81+
if (kind === GLOBAL_PERMISSION_OBJECT) {
82+
return <GlobalPermissionForm {...props} />;
83+
}
84+
7985
if (kind === OBJECT_PERMISSION_OBJECT) {
8086
return <ObjectPermissionForm {...props} />;
8187
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import { Button } from "@/components/buttons/button-primitive";
2+
import { NodeFormProps } from "@/components/form/node-form";
3+
import { FormFieldValue } from "@/components/form/type";
4+
import { getCurrentFieldValue } from "@/components/form/utils/getFieldDefaultValue";
5+
import { getCreateMutationFromFormDataOnly } from "@/components/form/utils/mutations/getCreateMutationFromFormData";
6+
import { ALERT_TYPES, Alert } from "@/components/ui/alert";
7+
import { Form, FormSubmit } from "@/components/ui/form";
8+
import { ACCOUNT_ROLE_OBJECT, GLOBAL_PERMISSION_OBJECT } from "@/config/constants";
9+
import graphqlClient from "@/graphql/graphqlClientApollo";
10+
import { createObject } from "@/graphql/mutations/objects/createObject";
11+
import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId";
12+
import { currentBranchAtom } from "@/state/atoms/branches.atom";
13+
import { datetimeAtom } from "@/state/atoms/time.atom";
14+
import { AttributeType, RelationshipType } from "@/utils/getObjectItemDisplayValue";
15+
import { stringifyWithoutQuotes } from "@/utils/string";
16+
import { gql } from "@apollo/client";
17+
import { useAtomValue } from "jotai";
18+
import { FieldValues, useForm } from "react-hook-form";
19+
import { toast } from "react-toastify";
20+
21+
import DropdownField from "@/components/form/fields/dropdown.field";
22+
import InputField from "@/components/form/fields/input.field";
23+
import RelationshipField from "@/components/form/fields/relationship.field";
24+
import { getRelationshipDefaultValue } from "@/components/form/utils/getRelationshipDefaultValue";
25+
import { isRequired } from "@/components/form/utils/validation";
26+
import { useSchema } from "@/hooks/useSchema";
27+
28+
interface NumberPoolFormProps extends Pick<NodeFormProps, "onSuccess"> {
29+
currentObject?: Record<string, AttributeType | RelationshipType>;
30+
onCancel?: () => void;
31+
onUpdateComplete?: () => void;
32+
}
33+
34+
export const GlobalPermissionForm = ({
35+
currentObject,
36+
onSuccess,
37+
onCancel,
38+
onUpdateComplete,
39+
}: NumberPoolFormProps) => {
40+
const branch = useAtomValue(currentBranchAtom);
41+
const date = useAtomValue(datetimeAtom);
42+
const { schema } = useSchema(GLOBAL_PERMISSION_OBJECT);
43+
44+
const roles = getRelationshipDefaultValue({
45+
relationshipData: currentObject?.roles?.value,
46+
});
47+
48+
const defaultValues = {
49+
name: getCurrentFieldValue("name", currentObject),
50+
action: getCurrentFieldValue("action", currentObject),
51+
decision: getCurrentFieldValue("decision", currentObject),
52+
roles,
53+
};
54+
55+
const form = useForm<FieldValues>({
56+
defaultValues,
57+
});
58+
59+
const actionOptions = schema?.attributes
60+
?.find((attribute) => {
61+
return attribute.name === "action";
62+
})
63+
?.choices?.map((choice) => {
64+
return {
65+
...choice,
66+
value: choice.name,
67+
};
68+
});
69+
70+
const decisionOptions = [
71+
{
72+
value: "allow",
73+
label: "Allow",
74+
},
75+
{
76+
value: "deny",
77+
label: "Deny",
78+
},
79+
];
80+
81+
async function handleSubmit(data: Record<string, FormFieldValue>) {
82+
try {
83+
const newObject = getCreateMutationFromFormDataOnly(data, currentObject);
84+
85+
if (!Object.keys(newObject).length) {
86+
return;
87+
}
88+
89+
const mutationString = currentObject
90+
? updateObjectWithId({
91+
kind: GLOBAL_PERMISSION_OBJECT,
92+
data: stringifyWithoutQuotes({
93+
id: currentObject.id,
94+
...newObject,
95+
}),
96+
})
97+
: createObject({
98+
kind: GLOBAL_PERMISSION_OBJECT,
99+
data: stringifyWithoutQuotes({
100+
...newObject,
101+
}),
102+
});
103+
104+
const mutation = gql`
105+
${mutationString}
106+
`;
107+
108+
const result = await graphqlClient.mutate({
109+
mutation,
110+
context: {
111+
branch: branch?.name,
112+
date,
113+
},
114+
});
115+
116+
toast(<Alert type={ALERT_TYPES.SUCCESS} message={"Object permission created"} />, {
117+
toastId: "alert-success-object-permission-created",
118+
});
119+
120+
if (onSuccess) await onSuccess(result?.data?.[`${GLOBAL_PERMISSION_OBJECT}Create`]);
121+
if (onUpdateComplete) await onUpdateComplete();
122+
} catch (error: unknown) {
123+
console.error("An error occurred while creating the object: ", error);
124+
}
125+
}
126+
127+
return (
128+
<div className={"bg-custom-white flex flex-col flex-1 overflow-auto p-4"}>
129+
<Form form={form} onSubmit={handleSubmit}>
130+
<InputField
131+
name="name"
132+
label="Name"
133+
rules={{
134+
required: true,
135+
validate: {
136+
required: isRequired,
137+
},
138+
}}
139+
/>
140+
141+
<DropdownField
142+
name="action"
143+
label="Action"
144+
items={actionOptions}
145+
rules={{ required: true, validate: { required: isRequired } }}
146+
/>
147+
148+
<DropdownField
149+
name="decision"
150+
label="Decision"
151+
items={decisionOptions}
152+
rules={{ required: true, validate: { required: isRequired } }}
153+
/>
154+
155+
<RelationshipField
156+
name="roles"
157+
label="Roles"
158+
relationship={{
159+
name: "roles",
160+
peer: ACCOUNT_ROLE_OBJECT,
161+
cardinality: "many",
162+
}}
163+
options={roles.value}
164+
/>
165+
166+
<div className="text-right">
167+
{onCancel && (
168+
<Button variant="outline" className="mr-2" onClick={onCancel}>
169+
Cancel
170+
</Button>
171+
)}
172+
173+
<FormSubmit>{currentObject ? "Update" : "Create"}</FormSubmit>
174+
</div>
175+
</Form>
176+
</div>
177+
);
178+
};

frontend/app/src/screens/role-management/global-permissions.tsx

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,36 @@
1+
import { Button } from "@/components/buttons/button-primitive";
12
import { Pill } from "@/components/display/pill";
2-
import { Table } from "@/components/table/table";
3+
import SlideOver, { SlideOverTitle } from "@/components/display/slide-over";
4+
import ObjectForm from "@/components/form/object-form";
5+
import ModalDeleteObject from "@/components/modals/modal-delete-object";
6+
import { Table, tRowValue } from "@/components/table/table";
37
import { BadgeCopy } from "@/components/ui/badge-copy";
48
import { Pagination } from "@/components/ui/pagination";
59
import { GLOBAL_PERMISSION_OBJECT } from "@/config/constants";
10+
import graphqlClient from "@/graphql/graphqlClientApollo";
611
import { GET_ROLE_MANAGEMENT_GLOBAL_PERMISSIONS } from "@/graphql/queries/role-management/getGlobalPermissions";
12+
import { useSchema } from "@/hooks/useSchema";
13+
import { schemaKindNameState } from "@/state/atoms/schemaKindName.atom";
714
import { useQuery } from "@apollo/client";
815
import { Icon } from "@iconify-icon/react";
16+
import { useAtomValue } from "jotai";
17+
import { useState } from "react";
918
import ErrorScreen from "../errors/error-screen";
1019
import LoadingScreen from "../loading-screen/loading-screen";
1120

1221
function GlobalPermissions() {
13-
const { loading, data, error } = useQuery(GET_ROLE_MANAGEMENT_GLOBAL_PERMISSIONS);
22+
const schemaKindName = useAtomValue(schemaKindNameState);
23+
const { schema } = useSchema(GLOBAL_PERMISSION_OBJECT);
24+
const { loading, data, error, refetch } = useQuery(GET_ROLE_MANAGEMENT_GLOBAL_PERMISSIONS);
25+
const [rowToDelete, setRowToDelete] = useState<Record<
26+
string,
27+
string | number | tRowValue
28+
> | null>(null);
29+
const [rowToUpdate, setRowToUpdate] = useState<Record<
30+
string,
31+
string | number | tRowValue
32+
> | null>(null);
33+
const [showDrawer, setShowDrawer] = useState(false);
1434

1535
const columns = [
1636
{
@@ -36,6 +56,7 @@ function GlobalPermissions() {
3656
data[GLOBAL_PERMISSION_OBJECT]?.edges.map((edge) => {
3757
return {
3858
values: {
59+
id: { value: edge?.node?.id },
3960
display_label: { value: edge?.node?.display_label },
4061
name: {
4162
display: (
@@ -55,6 +76,11 @@ function GlobalPermissions() {
5576
};
5677
});
5778

79+
const globalRefetch = () => {
80+
graphqlClient.refetchQueries({ include: ["GET_ROLE_MANAGEMENT_COUNTS"] });
81+
refetch();
82+
};
83+
5884
if (error) return <ErrorScreen message="An error occured while retrieving the accounts." />;
5985

6086
if (loading) return <LoadingScreen message="Retrieving accounts..." />;
@@ -64,12 +90,51 @@ function GlobalPermissions() {
6490
<div>
6591
<div className="flex items-center justify-between p-2">
6692
<div>{/* Search input + filter button */}</div>
93+
94+
<div>
95+
<Button variant={"primary"} onClick={() => setShowDrawer(true)} disabled={!schema}>
96+
Create {schema?.label}
97+
</Button>
98+
</div>
6799
</div>
68100

69101
<Table columns={columns} rows={rows ?? []} className="border-0" />
70102

71103
<Pagination count={data && data[GLOBAL_PERMISSION_OBJECT]?.count} />
72104
</div>
105+
106+
<ModalDeleteObject
107+
label={schemaKindName[GLOBAL_PERMISSION_OBJECT]}
108+
rowToDelete={rowToDelete}
109+
open={!!rowToDelete}
110+
close={() => setRowToDelete(null)}
111+
onDelete={() => globalRefetch()}
112+
/>
113+
114+
{schema && (
115+
<SlideOver
116+
title={
117+
<SlideOverTitle
118+
schema={schema}
119+
currentObjectLabel="New"
120+
title={`Create ${schema.label}`}
121+
subtitle={schema.description}
122+
/>
123+
}
124+
open={showDrawer}
125+
setOpen={(value) => setShowDrawer(value)}
126+
>
127+
<ObjectForm
128+
kind={GLOBAL_PERMISSION_OBJECT}
129+
currentObject={rowToUpdate}
130+
onCancel={() => setShowDrawer(false)}
131+
onSuccess={() => {
132+
setShowDrawer(false);
133+
globalRefetch();
134+
}}
135+
/>
136+
</SlideOver>
137+
)}
73138
</>
74139
);
75140
}

0 commit comments

Comments
 (0)