Move operation condition from backend to client

This commit is contained in:
guerler
2023-12-02 10:03:45 +03:00
parent dd222f69b1
commit 1dde4ffa7f
5 changed files with 112 additions and 26 deletions
@@ -10,6 +10,7 @@ library.add(faCaretDown);
interface Props {
rowData: RowData;
operations: Array<Operation>;
title: string;
}
const props = defineProps<Props>();
@@ -35,7 +36,7 @@ function hasCondition(conditionHandler: (rowData: RowData) => Boolean) {
aria-expanded="false"
class="ui-link font-weight-bold">
<FontAwesomeIcon icon="caret-down" class="fa-lg" />
<span class="font-weight-bold">{{ rowData.title }}</span>
<span class="font-weight-bold">{{ title }}</span>
</button>
<div v-if="operations" class="dropdown-menu" aria-labelledby="dataset-dropdown">
<span v-for="(operation, operationIndex) in operations" :key="operationIndex">
+1
View File
@@ -254,6 +254,7 @@ watch(operationMessage, () => {
v-if="fieldEntry.type == 'operations' && fieldEntry.operations"
:operations="fieldEntry.operations"
:row-data="rowData"
:title="rowData[fieldEntry.key]"
@execute="onOperation($event, rowData)" />
<GridText v-else-if="fieldEntry.type == 'text'" :text="rowData[fieldEntry.key]" />
<GridLink
@@ -1,7 +1,9 @@
import axios from "axios";
import type Router from "vue-router";
import Filtering, { contains, equals, toBool, type ValidFilter } from "@/utils/filtering";
import { withPrefix } from "@/utils/redirect";
import { errorMessageAsString } from "@/utils/simple-error";
/**
* Local types
@@ -29,13 +31,112 @@ async function getData(offset: number, limit: number, search: string, sort_by: s
*/
const fields = [
{
title: "Email",
key: "email",
type: "text",
width: "30%",
handler: (data: UserEntry) => {
window.location.href = withPrefix(`/plugins/visualizations/${data.type}/saved?id=${data.id}`);
},
title: "Email",
type: "operations",
condition: (data: UserEntry) => !data.deleted,
operations: [
{
title: "Manage Information",
icon: "user",
condition: (data: UserEntry) => !data.deleted,
handler: (data: UserEntry, router: Router) => {
router.push(`/user/information?id=${data.id}`);
},
},
{
title: "Manage Roles and Groups",
icon: "users",
condition: (data: UserEntry) => !data.deleted,
handler: (data: UserEntry, router: Router) => {
router.push(`/admin/form/manage_roles_and_groups_for_user?id=${data.id}`);
},
},
{
title: "Reset Password",
icon: "unlock",
condition: (data: UserEntry) => !data.deleted,
handler: (data: UserEntry, router: Router) => {
router.push(`/admin/form/reset_user_password?id=${data.id}`);
},
},
{
title: "Recalculate Disk Usage",
icon: "calculator",
condition: (data: UserEntry) => !data.deleted,
handler: async (data: UserEntry) => {
try {
await axios.put(`/api/visualizations/${data.id}`, { deleted: true });
return {
status: "success",
message: `Disk usage of '${data.title}' has been recalculated.`,
};
} catch (e) {
return {
status: "danger",
message: `Failed to recalculate disk usage of '${data.title}': ${errorMessageAsString(e)}.`,
};
}
},
},
{
title: "Generate New API Key",
icon: "key",
condition: (data: UserEntry) => !data.deleted,
handler: async (data: UserEntry) => {
try {
await axios.put(`/api/visualizations/${data.id}`, { deleted: true });
return {
status: "success",
message: `New API Key for '${data.title}' has been generated.`,
};
} catch (e) {
return {
status: "danger",
message: `Failed to generate new API Key for '${data.title}': ${errorMessageAsString(e)}.`,
};
}
},
},
{
title: "Delete",
icon: "trash",
condition: (data: UserEntry) => !data.deleted /* && config.allow_user_deletion */,
handler: async (data: UserEntry) => {
try {
await axios.delete(`/api/users/${data.id}`);
return {
status: "success",
message: `'${data.username}' has been deleted.`,
};
} catch (e) {
return {
status: "danger",
message: `Failed to delete '${data.username}': ${errorMessageAsString(e)}`,
};
}
},
},
{
title: "Restore",
icon: "trash-restore",
condition: (data: UserEntry) => data.deleted,
handler: async (data: UserEntry) => {
try {
await axios.post(`/api/users/deleted/${data.id}/undelete`);
return {
status: "success",
message: `'${data.username}' has been restored.`,
};
} catch (e) {
return {
status: "danger",
message: `Failed to restore '${data.username}': ${errorMessageAsString(e)}`,
};
}
},
},
],
},
{
key: "username",
+2
View File
@@ -4,6 +4,7 @@ import {
faBell,
faBolt,
faBurn,
faCalculator,
faChartArea,
faChartBar,
faCheckSquare,
@@ -57,6 +58,7 @@ library.add(
faBell,
faBolt,
faBurn,
faCalculator,
faChartArea,
faChartBar,
faCheckSquare,
@@ -616,8 +616,6 @@ class AdminGalaxy(controller.JSAppLauncher):
@web.json
@web.require_admin
def users_list(self, trans, **kwd):
message = kwd.get("message", "")
status = kwd.get("status", "")
if "operation" in kwd:
id = kwd.get("id")
if not id:
@@ -638,23 +636,6 @@ class AdminGalaxy(controller.JSAppLauncher):
message, status = self._activate_user(trans, id)
elif operation == "resend activation email":
message, status = self._resend_activation_email(trans, id)
if message and status:
kwd["message"] = util.sanitize_text(message)
kwd["status"] = status
if trans.app.config.allow_user_deletion:
if self.delete_operation not in self.user_list_grid.operations:
self.user_list_grid.operations.append(self.delete_operation)
if self.undelete_operation not in self.user_list_grid.operations:
self.user_list_grid.operations.append(self.undelete_operation)
if self.purge_operation not in self.user_list_grid.operations:
self.user_list_grid.operations.append(self.purge_operation)
if trans.app.config.allow_user_impersonation:
if self.impersonate_operation not in self.user_list_grid.operations:
self.user_list_grid.operations.append(self.impersonate_operation)
if trans.app.config.user_activation_on:
if self.activate_operation not in self.user_list_grid.operations:
self.user_list_grid.operations.append(self.activate_operation)
self.user_list_grid.operations.append(self.resend_activation_email)
return self.user_list_grid(trans, **kwd)
@web.legacy_expose_api