diff --git a/frontend/src/components/table/PackageTable.tsx b/frontend/src/components/table/PackageTable.tsx
index a573bd54..9d742dfa 100644
--- a/frontend/src/components/table/PackageTable.tsx
+++ b/frontend/src/components/table/PackageTable.tsx
@@ -17,10 +17,13 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
+import DeleteIcon from "@mui/icons-material/Delete";
+import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import { Box, Link } from "@mui/material";
import {
DataGrid,
GRID_CHECKBOX_SELECTION_COL_DEF,
+ GridActionsCellItem,
type GridColDef,
type GridFilterModel,
type GridRowId,
@@ -56,6 +59,7 @@ function createListColumn(
export default function PackageTable(): React.JSX.Element {
const table = usePackageTable();
+ const { handleRemove, handleUpdate, isAuthorized } = table;
const apiRef = useGridApiRef();
const debouncedSearch = useDebounce(table.searchText, 300);
@@ -96,8 +100,31 @@ export default function PackageTable(): React.JSX.Element {
,
width: 120,
},
+ ...isAuthorized ? [{
+ field: "actions",
+ type: "actions",
+ headerName: "actions",
+ width: 100,
+ disableExport: true,
+ getActions: ({ row }) => [
+ }
+ label={`Update ${row.base}`}
+ title={`Update ${row.base}`}
+ onClick={() => void handleUpdate([row.base])}
+ />,
+ }
+ label={`Remove ${row.base}`}
+ title={`Remove ${row.base}`}
+ onClick={() => void handleRemove([row.base])}
+ />,
+ ],
+ } satisfies GridColDef] : [],
],
- [],
+ [handleRemove, handleUpdate, isAuthorized],
);
return
@@ -110,8 +137,10 @@ export default function PackageTable(): React.JSX.Element {
onRebuildClick: () => table.setDialogOpen("rebuild"),
onRefreshDatabaseClick: () => void table.handleRefreshDatabase(),
onReloadClick: table.handleReload,
- onRemoveClick: () => void table.handleRemove(),
- onUpdateClick: () => void table.handleUpdate(),
+ onRemoveClick: () => void table.handleRemove(table.selectionModel)
+ .then(() => table.setSelectionModel([])),
+ onUpdateClick: () => void table.handleUpdate(table.selectionModel)
+ .then(() => table.setSelectionModel([])),
}}
isAuthorized={table.isAuthorized}
hasSelection={table.selectionModel.length > 0}
@@ -123,6 +152,7 @@ export default function PackageTable(): React.JSX.Element {
{
- // Don't open info dialog when clicking checkbox or link
- if (params.field === GRID_CHECKBOX_SELECTION_COL_DEF.field) {
+ // Don't open info dialog when clicking checkbox, actions, or link
+ if (params.field === GRID_CHECKBOX_SELECTION_COL_DEF.field || params.field === "actions") {
return;
}
if ((event.target as HTMLElement).closest("a")) {
diff --git a/frontend/src/hooks/usePackageActions.ts b/frontend/src/hooks/usePackageActions.ts
index 369c2db7..d36ea4b4 100644
--- a/frontend/src/hooks/usePackageActions.ts
+++ b/frontend/src/hooks/usePackageActions.ts
@@ -24,29 +24,27 @@ import { useClient } from "hooks/useClient";
import { useNotification } from "hooks/useNotification";
import { useRepository } from "hooks/useRepository";
import type { RepositoryId } from "models/RepositoryId";
+import { useCallback } from "react";
export interface UsePackageActionsResult {
handleRefreshDatabase: () => Promise;
handleReload: () => void;
- handleRemove: () => Promise;
- handleUpdate: () => Promise;
+ handleRemove: (packages: string[]) => Promise;
+ handleUpdate: (packages: string[]) => Promise;
}
-export function usePackageActions(
- selectionModel: string[],
- setSelectionModel: (model: string[]) => void,
-): UsePackageActionsResult {
+export function usePackageActions(): UsePackageActionsResult {
const client = useClient();
const { currentRepository } = useRepository();
const { showSuccess, showError } = useNotification();
const queryClient = useQueryClient();
- const invalidate = (repository: RepositoryId): void => {
+ const invalidate = useCallback((repository: RepositoryId): void => {
void queryClient.invalidateQueries({ queryKey: QueryKeys.packages(repository) });
void queryClient.invalidateQueries({ queryKey: QueryKeys.status(repository) });
- };
+ }, [queryClient]);
- const performAction = async (
+ const performAction = useCallback(async (
action: (repository: RepositoryId) => Promise,
errorMessage: string,
): Promise => {
@@ -57,11 +55,10 @@ export function usePackageActions(
const successMessage = await action(currentRepository);
showSuccess("Success", successMessage);
invalidate(currentRepository);
- setSelectionModel([]);
} catch (exception) {
showError("Action failed", `${errorMessage}: ${ApiError.errorDetail(exception)}`);
}
- };
+ }, [currentRepository, invalidate, showError, showSuccess]);
const handleReload = (): void => {
if (currentRepository !== null) {
@@ -69,14 +66,16 @@ export function usePackageActions(
}
};
- const handleUpdate = (): Promise => performAction(async (repository): Promise => {
- if (selectionModel.length === 0) {
- await client.service.servicePackageUpdate(repository, { packages: [] });
- return "Repository update has been run";
- }
- await client.service.servicePackageAdd(repository, { packages: selectionModel });
- return `Run update for packages ${selectionModel.join(", ")}`;
- }, "Packages update failed");
+ const handleUpdate = useCallback((packages: string[]): Promise => {
+ return performAction(async (repository): Promise => {
+ if (packages.length === 0) {
+ await client.service.servicePackageUpdate(repository, { packages: [] });
+ return "Repository update has been run";
+ }
+ await client.service.servicePackageAdd(repository, { packages });
+ return `Run update for packages ${packages.join(", ")}`;
+ }, "Packages update failed");
+ }, [client, performAction]);
const handleRefreshDatabase = (): Promise => performAction(async (repository): Promise => {
await client.service.servicePackageUpdate(repository, {
@@ -89,15 +88,15 @@ export function usePackageActions(
return "Pacman database update has been requested";
}, "Could not update pacman databases");
- const handleRemove = (): Promise => {
- if (selectionModel.length === 0) {
+ const handleRemove = useCallback((packages: string[]): Promise => {
+ if (packages.length === 0) {
return Promise.resolve();
}
return performAction(async (repository): Promise => {
- await client.service.servicePackageRemove(repository, selectionModel);
- return `Packages ${selectionModel.join(", ")} have been removed`;
+ await client.service.servicePackageRemove(repository, packages);
+ return `Packages ${packages.join(", ")} have been removed`;
}, "Could not remove packages");
- };
+ }, [client, performAction]);
return {
handleRefreshDatabase,
diff --git a/frontend/src/hooks/usePackageTable.ts b/frontend/src/hooks/usePackageTable.ts
index 0fd94b29..f0104847 100644
--- a/frontend/src/hooks/usePackageTable.ts
+++ b/frontend/src/hooks/usePackageTable.ts
@@ -30,8 +30,8 @@ export interface UsePackageTableResult {
filterModel: GridFilterModel;
handleRefreshDatabase: () => Promise;
handleReload: () => void;
- handleRemove: () => Promise;
- handleUpdate: () => Promise;
+ handleRemove: (packages: string[]) => Promise;
+ handleUpdate: (packages: string[]) => Promise;
isAuthorized: boolean;
isLoading: boolean;
paginationModel: { page: number; pageSize: number };
@@ -52,7 +52,7 @@ export interface UsePackageTableResult {
export function usePackageTable(): UsePackageTableResult {
const { rows, isLoading, isAuthorized, status } = usePackageData();
const tableState = useTableState();
- const actions = usePackageActions(tableState.selectionModel, tableState.setSelectionModel);
+ const actions = usePackageActions();
return {
isLoading,