mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-09-26 00:10:27 +00:00
feat: add update and remove buttons to the status table
This commit is contained in:
@@ -17,10 +17,13 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
import DeleteIcon from "@mui/icons-material/Delete";
|
||||||
|
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
||||||
import { Box, Link } from "@mui/material";
|
import { Box, Link } from "@mui/material";
|
||||||
import {
|
import {
|
||||||
DataGrid,
|
DataGrid,
|
||||||
GRID_CHECKBOX_SELECTION_COL_DEF,
|
GRID_CHECKBOX_SELECTION_COL_DEF,
|
||||||
|
GridActionsCellItem,
|
||||||
type GridColDef,
|
type GridColDef,
|
||||||
type GridFilterModel,
|
type GridFilterModel,
|
||||||
type GridRowId,
|
type GridRowId,
|
||||||
@@ -56,6 +59,7 @@ function createListColumn(
|
|||||||
|
|
||||||
export default function PackageTable(): React.JSX.Element {
|
export default function PackageTable(): React.JSX.Element {
|
||||||
const table = usePackageTable();
|
const table = usePackageTable();
|
||||||
|
const { handleRemove, handleUpdate, isAuthorized } = table;
|
||||||
const apiRef = useGridApiRef();
|
const apiRef = useGridApiRef();
|
||||||
const debouncedSearch = useDebounce(table.searchText, 300);
|
const debouncedSearch = useDebounce(table.searchText, 300);
|
||||||
|
|
||||||
@@ -96,8 +100,31 @@ export default function PackageTable(): React.JSX.Element {
|
|||||||
<StatusCell isHeld={params.row.isHeld} status={params.row.status} />,
|
<StatusCell isHeld={params.row.isHeld} status={params.row.status} />,
|
||||||
width: 120,
|
width: 120,
|
||||||
},
|
},
|
||||||
|
...isAuthorized ? [{
|
||||||
|
field: "actions",
|
||||||
|
type: "actions",
|
||||||
|
headerName: "actions",
|
||||||
|
width: 100,
|
||||||
|
disableExport: true,
|
||||||
|
getActions: ({ row }) => [
|
||||||
|
<GridActionsCellItem
|
||||||
|
key="update"
|
||||||
|
icon={<PlayArrowIcon />}
|
||||||
|
label={`Update ${row.base}`}
|
||||||
|
title={`Update ${row.base}`}
|
||||||
|
onClick={() => void handleUpdate([row.base])}
|
||||||
|
/>,
|
||||||
|
<GridActionsCellItem
|
||||||
|
key="remove"
|
||||||
|
icon={<DeleteIcon />}
|
||||||
|
label={`Remove ${row.base}`}
|
||||||
|
title={`Remove ${row.base}`}
|
||||||
|
onClick={() => void handleRemove([row.base])}
|
||||||
|
/>,
|
||||||
],
|
],
|
||||||
[],
|
} satisfies GridColDef<PackageRow>] : [],
|
||||||
|
],
|
||||||
|
[handleRemove, handleUpdate, isAuthorized],
|
||||||
);
|
);
|
||||||
|
|
||||||
return <Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
return <Box sx={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||||
@@ -110,8 +137,10 @@ export default function PackageTable(): React.JSX.Element {
|
|||||||
onRebuildClick: () => table.setDialogOpen("rebuild"),
|
onRebuildClick: () => table.setDialogOpen("rebuild"),
|
||||||
onRefreshDatabaseClick: () => void table.handleRefreshDatabase(),
|
onRefreshDatabaseClick: () => void table.handleRefreshDatabase(),
|
||||||
onReloadClick: table.handleReload,
|
onReloadClick: table.handleReload,
|
||||||
onRemoveClick: () => void table.handleRemove(),
|
onRemoveClick: () => void table.handleRemove(table.selectionModel)
|
||||||
onUpdateClick: () => void table.handleUpdate(),
|
.then(() => table.setSelectionModel([])),
|
||||||
|
onUpdateClick: () => void table.handleUpdate(table.selectionModel)
|
||||||
|
.then(() => table.setSelectionModel([])),
|
||||||
}}
|
}}
|
||||||
isAuthorized={table.isAuthorized}
|
isAuthorized={table.isAuthorized}
|
||||||
hasSelection={table.selectionModel.length > 0}
|
hasSelection={table.selectionModel.length > 0}
|
||||||
@@ -123,6 +152,7 @@ export default function PackageTable(): React.JSX.Element {
|
|||||||
<DataGrid
|
<DataGrid
|
||||||
apiRef={apiRef}
|
apiRef={apiRef}
|
||||||
checkboxSelection
|
checkboxSelection
|
||||||
|
localeText={{ checkboxSelectionHeaderName: "selection" }}
|
||||||
columnVisibilityModel={table.columnVisibility}
|
columnVisibilityModel={table.columnVisibility}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
density="compact"
|
density="compact"
|
||||||
@@ -134,8 +164,8 @@ export default function PackageTable(): React.JSX.Element {
|
|||||||
}}
|
}}
|
||||||
loading={table.isLoading}
|
loading={table.isLoading}
|
||||||
onCellClick={(params, event) => {
|
onCellClick={(params, event) => {
|
||||||
// Don't open info dialog when clicking checkbox or link
|
// Don't open info dialog when clicking checkbox, actions, or link
|
||||||
if (params.field === GRID_CHECKBOX_SELECTION_COL_DEF.field) {
|
if (params.field === GRID_CHECKBOX_SELECTION_COL_DEF.field || params.field === "actions") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((event.target as HTMLElement).closest("a")) {
|
if ((event.target as HTMLElement).closest("a")) {
|
||||||
|
|||||||
@@ -24,29 +24,27 @@ import { useClient } from "hooks/useClient";
|
|||||||
import { useNotification } from "hooks/useNotification";
|
import { useNotification } from "hooks/useNotification";
|
||||||
import { useRepository } from "hooks/useRepository";
|
import { useRepository } from "hooks/useRepository";
|
||||||
import type { RepositoryId } from "models/RepositoryId";
|
import type { RepositoryId } from "models/RepositoryId";
|
||||||
|
import { useCallback } from "react";
|
||||||
|
|
||||||
export interface UsePackageActionsResult {
|
export interface UsePackageActionsResult {
|
||||||
handleRefreshDatabase: () => Promise<void>;
|
handleRefreshDatabase: () => Promise<void>;
|
||||||
handleReload: () => void;
|
handleReload: () => void;
|
||||||
handleRemove: () => Promise<void>;
|
handleRemove: (packages: string[]) => Promise<void>;
|
||||||
handleUpdate: () => Promise<void>;
|
handleUpdate: (packages: string[]) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function usePackageActions(
|
export function usePackageActions(): UsePackageActionsResult {
|
||||||
selectionModel: string[],
|
|
||||||
setSelectionModel: (model: string[]) => void,
|
|
||||||
): UsePackageActionsResult {
|
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
const { currentRepository } = useRepository();
|
const { currentRepository } = useRepository();
|
||||||
const { showSuccess, showError } = useNotification();
|
const { showSuccess, showError } = useNotification();
|
||||||
const queryClient = useQueryClient();
|
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.packages(repository) });
|
||||||
void queryClient.invalidateQueries({ queryKey: QueryKeys.status(repository) });
|
void queryClient.invalidateQueries({ queryKey: QueryKeys.status(repository) });
|
||||||
};
|
}, [queryClient]);
|
||||||
|
|
||||||
const performAction = async (
|
const performAction = useCallback(async (
|
||||||
action: (repository: RepositoryId) => Promise<string>,
|
action: (repository: RepositoryId) => Promise<string>,
|
||||||
errorMessage: string,
|
errorMessage: string,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
@@ -57,11 +55,10 @@ export function usePackageActions(
|
|||||||
const successMessage = await action(currentRepository);
|
const successMessage = await action(currentRepository);
|
||||||
showSuccess("Success", successMessage);
|
showSuccess("Success", successMessage);
|
||||||
invalidate(currentRepository);
|
invalidate(currentRepository);
|
||||||
setSelectionModel([]);
|
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
showError("Action failed", `${errorMessage}: ${ApiError.errorDetail(exception)}`);
|
showError("Action failed", `${errorMessage}: ${ApiError.errorDetail(exception)}`);
|
||||||
}
|
}
|
||||||
};
|
}, [currentRepository, invalidate, showError, showSuccess]);
|
||||||
|
|
||||||
const handleReload = (): void => {
|
const handleReload = (): void => {
|
||||||
if (currentRepository !== null) {
|
if (currentRepository !== null) {
|
||||||
@@ -69,14 +66,16 @@ export function usePackageActions(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpdate = (): Promise<void> => performAction(async (repository): Promise<string> => {
|
const handleUpdate = useCallback((packages: string[]): Promise<void> => {
|
||||||
if (selectionModel.length === 0) {
|
return performAction(async (repository): Promise<string> => {
|
||||||
|
if (packages.length === 0) {
|
||||||
await client.service.servicePackageUpdate(repository, { packages: [] });
|
await client.service.servicePackageUpdate(repository, { packages: [] });
|
||||||
return "Repository update has been run";
|
return "Repository update has been run";
|
||||||
}
|
}
|
||||||
await client.service.servicePackageAdd(repository, { packages: selectionModel });
|
await client.service.servicePackageAdd(repository, { packages });
|
||||||
return `Run update for packages ${selectionModel.join(", ")}`;
|
return `Run update for packages ${packages.join(", ")}`;
|
||||||
}, "Packages update failed");
|
}, "Packages update failed");
|
||||||
|
}, [client, performAction]);
|
||||||
|
|
||||||
const handleRefreshDatabase = (): Promise<void> => performAction(async (repository): Promise<string> => {
|
const handleRefreshDatabase = (): Promise<void> => performAction(async (repository): Promise<string> => {
|
||||||
await client.service.servicePackageUpdate(repository, {
|
await client.service.servicePackageUpdate(repository, {
|
||||||
@@ -89,15 +88,15 @@ export function usePackageActions(
|
|||||||
return "Pacman database update has been requested";
|
return "Pacman database update has been requested";
|
||||||
}, "Could not update pacman databases");
|
}, "Could not update pacman databases");
|
||||||
|
|
||||||
const handleRemove = (): Promise<void> => {
|
const handleRemove = useCallback((packages: string[]): Promise<void> => {
|
||||||
if (selectionModel.length === 0) {
|
if (packages.length === 0) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
return performAction(async (repository): Promise<string> => {
|
return performAction(async (repository): Promise<string> => {
|
||||||
await client.service.servicePackageRemove(repository, selectionModel);
|
await client.service.servicePackageRemove(repository, packages);
|
||||||
return `Packages ${selectionModel.join(", ")} have been removed`;
|
return `Packages ${packages.join(", ")} have been removed`;
|
||||||
}, "Could not remove packages");
|
}, "Could not remove packages");
|
||||||
};
|
}, [client, performAction]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleRefreshDatabase,
|
handleRefreshDatabase,
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ export interface UsePackageTableResult {
|
|||||||
filterModel: GridFilterModel;
|
filterModel: GridFilterModel;
|
||||||
handleRefreshDatabase: () => Promise<void>;
|
handleRefreshDatabase: () => Promise<void>;
|
||||||
handleReload: () => void;
|
handleReload: () => void;
|
||||||
handleRemove: () => Promise<void>;
|
handleRemove: (packages: string[]) => Promise<void>;
|
||||||
handleUpdate: () => Promise<void>;
|
handleUpdate: (packages: string[]) => Promise<void>;
|
||||||
isAuthorized: boolean;
|
isAuthorized: boolean;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
paginationModel: { page: number; pageSize: number };
|
paginationModel: { page: number; pageSize: number };
|
||||||
@@ -52,7 +52,7 @@ export interface UsePackageTableResult {
|
|||||||
export function usePackageTable(): UsePackageTableResult {
|
export function usePackageTable(): UsePackageTableResult {
|
||||||
const { rows, isLoading, isAuthorized, status } = usePackageData();
|
const { rows, isLoading, isAuthorized, status } = usePackageData();
|
||||||
const tableState = useTableState();
|
const tableState = useTableState();
|
||||||
const actions = usePackageActions(tableState.selectionModel, tableState.setSelectionModel);
|
const actions = usePackageActions();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isLoading,
|
isLoading,
|
||||||
|
|||||||
Reference in New Issue
Block a user