Skip to content

Commit

Permalink
feat: allow bulk admin rights modification
Browse files Browse the repository at this point in the history
Fix #754
  • Loading branch information
helakaraa authored Oct 1, 2024
1 parent 1e9a397 commit 20ab1e3
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 35 deletions.
148 changes: 115 additions & 33 deletions izanami-frontend/src/pages/users.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ export function Users() {
const [bulkOperation, setBulkOperation] = useState<string | undefined>(
undefined
);

const BULK_OPERATIONS = ["Delete"] as const;
const BULK_OPERATIONS = ["Delete", "Toggle Admin Role"] as const;
const isAdmin = useAdmin();
const context = useContext(IzanamiContext);
const isTenantAdmin = Boolean(
Expand All @@ -59,7 +58,6 @@ export function Users() {
},
}
);

const userUpdateMutation = useMutation(
(user: { username: string; admin: boolean; rights: TRights }) => {
const { username, ...rest } = user;
Expand Down Expand Up @@ -90,6 +88,115 @@ export function Users() {
createInvitation(data.email, data.admin, data.rights)
);

function OperationToggleForm(props: {
bulkOperation: string;
selectedRows: UserType[];
cancel: () => void;
}) {
const { bulkOperation, selectedRows, cancel } = props;
switch (bulkOperation) {
case "Toggle Admin Role": {
const adminOptions = [
{
label: "Add Admin right",
value: "true",
},
{
label: "Remove Admin right",
value: "false",
},
];

return (
<>
<Form
className={"d-flex align-items-center"}
schema={{
admin: {
className: "form-margin",
label: () => "",
type: "string",
format: "select",
props: { styles: customStyles },
placeholder: "Select Admin Role...",
options: adminOptions,
},
}}
onSubmit={(ctx) => {
return askConfirmation(
`Are you sure you want to change admin role for ${
selectedRows.length
} user${selectedRows.length > 1 ? "s" : ""}?`,
() => {
return Promise.all(
selectedRows.map((row) => {
return fetch(`/api/admin/users/${row.username}`)
.then((response) => {
return response.json();
})
.then((data) => {
return userUpdateMutation.mutateAsync({
username: row.username,
admin: ctx.admin === "true",
rights: data.rights,
});
});
})
).then(cancel);
}
);
}}
footer={({ valid }: { valid: () => void }) => {
return (
<div className="d-flex justify-content-end">
<button
type="button"
className="btn btn-danger-light m-2"
onClick={cancel}
>
Cancel
</button>
<button className="btn btn-primary m-2" onClick={valid}>
Update {selectedRows.length} User
{selectedRows.length > 1 ? "s" : ""}
</button>
</div>
);
}}
/>
</>
);
}
default:
return (
<button
className="ms-2 btn btn-primary"
type="button"
disabled={!hasSelectedRows || !bulkOperation}
onClick={() =>
askConfirmation(
`Are you sure you want to delete ${selectedRows.length} user${
selectedRows.length > 1 ? "s" : ""
} ?`,
() => {
return Promise.all(
selectedRows.map((row) =>
userDeleteMutation
.mutateAsync(row.username)
.then(() => setBulkOperation(undefined))
)
);
}
)
}
>
{bulkOperation} {selectedRows.length} user
{selectedRows.length > 1 ? "s" : ""}
</button>
);
}
}

if (userQuery.isLoading) {
return <Loader message="Loading users..." />;
} else if (userQuery.isSuccess) {
Expand Down Expand Up @@ -254,36 +361,11 @@ export function Users() {
/>
&nbsp;
{bulkOperation && (
<>
<button
className="ms-2 btn btn-primary"
type="button"
disabled={!hasSelectedRows || !bulkOperation}
onClick={() => {
switch (bulkOperation) {
case "Delete":
askConfirmation(
`Are you sure you want to delete ${
selectedRows.length
} user${selectedRows.length > 1 ? "s" : ""} ?`,
() => {
return Promise.all(
selectedRows.map((row) =>
userDeleteMutation
.mutateAsync(row.username)
.then(() => setBulkOperation(undefined))
)
);
}
);
break;
}
}}
>
{bulkOperation} {selectedRows.length} user
{selectedRows.length > 1 ? "s" : ""}
</button>
</>
<OperationToggleForm
bulkOperation={bulkOperation}
selectedRows={selectedRows}
cancel={() => setBulkOperation(undefined)}
/>
)}
</div>
<GenericTable
Expand Down
1 change: 1 addition & 0 deletions izanami-frontend/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export interface ProjectType extends TenantProjectType {
}

export interface UserType {
admin : boolean;
username: string;
}

Expand Down
1 change: 0 additions & 1 deletion test/fr/maif/izanami/api/BaseAPISpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2334,7 +2334,6 @@ object BaseAPISpec extends DefaultAwaitTimeout {

RequestResult(json = Try { response.json }, status = response.status)
}

def updateUserPassword(user: String, oldPassword: String, newPassword: String): RequestResult = {
val response = await(
ws.url(s"${ADMIN_BASE_URL}/users/${user}/password")
Expand Down
1 change: 0 additions & 1 deletion test/fr/maif/izanami/api/UsersAPISpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,6 @@ class UsersAPISpec extends BaseAPISpec {
(situation.fetchUser(user.username).json.get \ "admin").as[Boolean] mustBe true
response.status mustBe NO_CONTENT
}

"Allow to add project right" in {
val user = TestUser(
username = "foo",
Expand Down

0 comments on commit 20ab1e3

Please sign in to comment.