Skip to content

Commit

Permalink
Merge pull request #17141 from guerler/grids_forms_admin
Browse files Browse the repository at this point in the history
Vueify Admin Forms and Quota grids
  • Loading branch information
guerler authored Dec 11, 2023
2 parents 33acf3f + 72a875f commit 224859e
Show file tree
Hide file tree
Showing 16 changed files with 757 additions and 506 deletions.
4 changes: 4 additions & 0 deletions client/src/api/forms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { fetcher } from "@/api/schema";

export const deleteForm = fetcher.path("/api/forms/{id}").method("delete").create();
export const undeleteForm = fetcher.path("/api/forms/{id}/undelete").method("post").create();
5 changes: 5 additions & 0 deletions client/src/api/quotas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { fetcher } from "@/api/schema";

export const deleteQuota = fetcher.path("/api/quotas/{id}").method("delete").create();
export const purgeQuota = fetcher.path("/api/quotas/{id}/purge").method("post").create();
export const undeleteQuota = fetcher.path("/api/quotas/deleted/{id}/undelete").method("post").create();
91 changes: 91 additions & 0 deletions client/src/api/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,14 @@ export interface paths {
*/
post: operations["set_permissions_api_folders__id__permissions_post"];
};
"/api/forms/{id}": {
/** Delete */
delete: operations["delete_api_forms__id__delete"];
};
"/api/forms/{id}/undelete": {
/** Undelete */
post: operations["undelete_api_forms__id__undelete_post"];
};
"/api/ftp_files": {
/**
* Displays remote files available to the user. Please use /api/remote_files instead.
Expand Down Expand Up @@ -1347,6 +1355,10 @@ export interface paths {
*/
delete: operations["delete_api_quotas__id__delete"];
};
"/api/quotas/{id}/purge": {
/** Purges a previously deleted quota. */
post: operations["purge_api_quotas__id__purge_post"];
};
"/api/remote_files": {
/**
* Displays remote files available to the user.
Expand Down Expand Up @@ -11821,6 +11833,58 @@ export interface operations {
};
};
};
delete_api_forms__id__delete: {
/** Delete */
parameters: {
/** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */
header?: {
"run-as"?: string;
};
path: {
id: string;
};
};
responses: {
/** @description Successful Response */
200: {
content: {
"application/json": Record<string, never>;
};
};
/** @description Validation Error */
422: {
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
undelete_api_forms__id__undelete_post: {
/** Undelete */
parameters: {
/** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */
header?: {
"run-as"?: string;
};
path: {
id: string;
};
};
responses: {
/** @description Successful Response */
200: {
content: {
"application/json": Record<string, never>;
};
};
/** @description Validation Error */
422: {
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
index_api_ftp_files_get: {
/**
* Displays remote files available to the user. Please use /api/remote_files instead.
Expand Down Expand Up @@ -17590,6 +17654,33 @@ export interface operations {
};
};
};
purge_api_quotas__id__purge_post: {
/** Purges a previously deleted quota. */
parameters: {
/** @description The user ID that will be used to effectively make this API call. Only admins and designated users can make API calls on behalf of other users. */
header?: {
"run-as"?: string;
};
/** @description The encoded identifier of the Quota. */
path: {
id: string;
};
};
responses: {
/** @description Successful Response */
200: {
content: {
"application/json": string;
};
};
/** @description Validation Error */
422: {
content: {
"application/json": components["schemas"]["HTTPValidationError"];
};
};
};
};
index_api_remote_files_get: {
/**
* Displays remote files available to the user.
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Grid/GridElements/GridText.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ defineProps<Props>();
</script>

<template>
<span v-localize>{{ text ?? "Not available." }}</span>
<span v-localize>{{ text ?? "-" }}</span>
</template>
153 changes: 153 additions & 0 deletions client/src/components/Grid/configs/adminForms.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { faEdit, faPlus, faTrash, faTrashRestore } from "@fortawesome/free-solid-svg-icons";
import { useEventBus } from "@vueuse/core";
import axios from "axios";

import { deleteForm, undeleteForm } from "@/api/forms";
import Filtering, { contains, equals, toBool, type ValidFilter } from "@/utils/filtering";
import _l from "@/utils/localization";
import { withPrefix } from "@/utils/redirect";
import { errorMessageAsString } from "@/utils/simple-error";

import type { ActionArray, FieldArray, GridConfig } from "./types";

const { emit } = useEventBus<string>("grid-router-push");

/**
* Local types
*/
type FormEntry = Record<string, unknown>;

/**
* Request and return data from server
*/
async function getData(offset: number, limit: number, search: string, sort_by: string, sort_desc: boolean) {
const query = {
limit: String(limit),
offset: String(offset),
search: search,
sort_by: sort_by,
sort_desc: String(sort_desc),
};
const queryString = new URLSearchParams(query).toString();
const { data } = await axios.get(withPrefix(`/forms/forms_list?${queryString}`));
return [data.rows, data.rows_total];
}

/**
* Actions are grid-wide operations
*/
const actions: ActionArray = [
{
title: "Create New Form",
icon: faPlus,
handler: () => {
emit("/admin/form/create_form");
},
},
];

/**
* Declare columns to be displayed
*/
const fields: FieldArray = [
{
key: "name",
title: "Name",
type: "operations",
operations: [
{
title: "Edit",
icon: faEdit,
condition: (data: FormEntry) => !data.deleted,
handler: (data: FormEntry) => {
emit(`/admin/form/edit_form?id=${data.id}`);
},
},
{
title: "Delete",
icon: faTrash,
condition: (data: FormEntry) => !data.deleted,
handler: async (data: FormEntry) => {
if (confirm(_l("Are you sure that you want to delete this form?"))) {
try {
await deleteForm({ id: String(data.id) });
return {
status: "success",
message: `'${data.name}' has been deleted.`,
};
} catch (e) {
return {
status: "danger",
message: `Failed to delete '${data.name}': ${errorMessageAsString(e)}`,
};
}
}
},
},
{
title: "Restore",
icon: faTrashRestore,
condition: (data: FormEntry) => !!data.deleted,
handler: async (data: FormEntry) => {
try {
await undeleteForm({ id: String(data.id) });
return {
status: "success",
message: `'${data.name}' has been restored.`,
};
} catch (e) {
return {
status: "danger",
message: `Failed to restore '${data.name}': ${errorMessageAsString(e)}`,
};
}
},
},
],
},
{
key: "desc",
title: "Description",
type: "text",
},
{
key: "type",
title: "Type",
type: "text",
},
{
key: "update_time",
title: "Updated",
type: "date",
},
];

const validFilters: Record<string, ValidFilter<string | boolean | undefined>> = {
name: { placeholder: "name", type: String, handler: contains("name"), menuItem: true },
description: { placeholder: "description", type: String, handler: contains("desc"), menuItem: true },
deleted: {
placeholder: "Filter on deleted entries",
type: Boolean,
boolType: "is",
handler: equals("deleted", "deleted", toBool),
menuItem: true,
},
};

/**
* Grid configuration
*/
const gridConfig: GridConfig = {
id: "forms-grid",
actions: actions,
fields: fields,
filtering: new Filtering(validFilters, undefined, false, false),
getData: getData,
plural: "Forms",
sortBy: "name",
sortDesc: true,
sortKeys: ["name", "update_time"],
title: "Forms",
};

export default gridConfig;
Loading

0 comments on commit 224859e

Please sign in to comment.