diff --git a/frontend/package.json b/frontend/package.json
index 8bba20d7..81b367a7 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,8 +1,34 @@
{
+ "dependencies": {
+ "@emotion/react": ">=11.14.0 <11.15.0",
+ "@emotion/styled": ">=11.14.0 <11.15.0",
+ "@mui/icons-material": ">=7.3.0 <7.4.0",
+ "@mui/material": ">=7.3.0 <7.4.0",
+ "@mui/x-data-grid": ">=8.28.0 <8.29.0",
+ "@tanstack/react-query": ">=5.94.0 <5.95.0",
+ "chart.js": ">=4.5.0 <4.6.0",
+ "react": ">=19.2.0 <19.3.0",
+ "react-chartjs-2": ">=5.3.0 <5.4.0",
+ "react-dom": ">=19.2.0 <19.3.0",
+ "react-syntax-highlighter": ">=16.1.0 <16.2.0"
+ },
+ "devDependencies": {
+ "@eslint/js": ">=9.39.0 <9.40.0",
+ "@stylistic/eslint-plugin": ">=5.10.0 <5.11.0",
+ "@types/react": ">=19.2.0 <19.3.0",
+ "@types/react-dom": ">=19.2.0 <19.3.0",
+ "@types/react-syntax-highlighter": ">=15.5.0 <15.6.0",
+ "@vitejs/plugin-react": ">=6.0.0 <6.1.0",
+ "eslint": ">=9.39.0 <9.40.0",
+ "eslint-plugin-react-hooks": ">=7.0.0 <7.1.0",
+ "eslint-plugin-react-refresh": ">=0.5.0 <0.6.0",
+ "eslint-plugin-simple-import-sort": ">=12.1.0 <12.2.0",
+ "typescript": ">=5.9.0 <5.10.0",
+ "typescript-eslint": ">=8.57.0 <8.58.0",
+ "vite": ">=8.0.0 <8.1.0"
+ },
"name": "ahriman-frontend",
"private": true,
- "type": "module",
- "version": "2.20.0",
"scripts": {
"build": "tsc && vite build",
"dev": "vite",
@@ -10,32 +36,6 @@
"lint:fix": "eslint --fix src/",
"preview": "vite preview"
},
- "dependencies": {
- "@emotion/react": "^11.14.0",
- "@emotion/styled": "^11.14.1",
- "@mui/icons-material": "^7.3.9",
- "@mui/material": "^7.3.9",
- "@mui/x-data-grid": "^8.27.4",
- "@tanstack/react-query": "^5.91.3",
- "chart.js": "^4.5.1",
- "react": "^19.2.4",
- "react-chartjs-2": "^5.3.1",
- "react-dom": "^19.2.4",
- "react-syntax-highlighter": "^16.1.1"
- },
- "devDependencies": {
- "@eslint/js": "^9.39.3",
- "@stylistic/eslint-plugin": "^5.10.0",
- "@types/react": "^19.2.14",
- "@types/react-dom": "^19.2.3",
- "@types/react-syntax-highlighter": "^15.5.13",
- "@vitejs/plugin-react": "^6.0.1",
- "eslint": "^9.39.3",
- "eslint-plugin-react-hooks": "^7.0.1",
- "eslint-plugin-react-refresh": "^0.5.2",
- "eslint-plugin-simple-import-sort": "^12.1.1",
- "typescript": "^5.9.3",
- "typescript-eslint": "^8.56.1",
- "vite": "^8.0.0"
- }
+ "type": "module",
+ "version": "2.20.0"
}
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index da1db045..431485a7 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -29,8 +29,8 @@ import type React from "react";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
- staleTime: 30_000,
retry: 1,
+ staleTime: 30_000,
},
},
});
diff --git a/frontend/src/api/client/ApiError.ts b/frontend/src/api/client/ApiError.ts
index 1462d48b..539de669 100644
--- a/frontend/src/api/client/ApiError.ts
+++ b/frontend/src/api/client/ApiError.ts
@@ -18,9 +18,10 @@
* along with this program. If not, see .
*/
export class ApiError extends Error {
+
+ body: string;
status: number;
statusText: string;
- body: string;
constructor(status: number, statusText: string, body: string) {
super(`${status} ${statusText}`);
diff --git a/frontend/src/api/client/FetchClient.ts b/frontend/src/api/client/FetchClient.ts
index d5e1c0eb..3c857d3e 100644
--- a/frontend/src/api/client/FetchClient.ts
+++ b/frontend/src/api/client/FetchClient.ts
@@ -37,14 +37,14 @@ export class FetchClient {
this.client = client;
}
- async fetchPackageArtifacts(packageBase: string, repository: RepositoryId): Promise {
- return this.client.request(`/api/v1/packages/${encodeURIComponent(packageBase)}/archives`, {
+ async fetchPackage(packageBase: string, repository: RepositoryId): Promise {
+ return this.client.request(`/api/v1/packages/${encodeURIComponent(packageBase)}`, {
query: repository.toQuery(),
});
}
- async fetchPackage(packageBase: string, repository: RepositoryId): Promise {
- return this.client.request(`/api/v1/packages/${encodeURIComponent(packageBase)}`, {
+ async fetchPackageArtifacts(packageBase: string, repository: RepositoryId): Promise {
+ return this.client.request(`/api/v1/packages/${encodeURIComponent(packageBase)}/archives`, {
query: repository.toQuery(),
});
}
diff --git a/frontend/src/api/client/RequestOptions.ts b/frontend/src/api/client/RequestOptions.ts
index fca0e4fe..8060c246 100644
--- a/frontend/src/api/client/RequestOptions.ts
+++ b/frontend/src/api/client/RequestOptions.ts
@@ -18,8 +18,8 @@
* along with this program. If not, see .
*/
export interface RequestOptions {
+ json?: unknown;
method?: string;
query?: Record;
- json?: unknown;
timeout?: number;
}
diff --git a/frontend/src/api/client/ServiceClient.ts b/frontend/src/api/client/ServiceClient.ts
index 6181a06b..6ccd3392 100644
--- a/frontend/src/api/client/ServiceClient.ts
+++ b/frontend/src/api/client/ServiceClient.ts
@@ -37,17 +37,17 @@ export class ServiceClient {
return this.client.request("/api/v1/service/add", { method: "POST", query: repository.toQuery(), json: data });
}
- async servicePackagePatchRemove(packageBase: string, key: string): Promise {
- return this.client.request(`/api/v1/packages/${encodeURIComponent(packageBase)}/patches/${encodeURIComponent(key)}`, {
- method: "DELETE",
+ async servicePackageHold(packageBase: string, repository: RepositoryId, isHeld: boolean): Promise {
+ return this.client.request(`/api/v1/packages/${encodeURIComponent(packageBase)}/hold`, {
+ method: "POST",
+ query: repository.toQuery(),
+ json: { is_held: isHeld },
});
}
- async servicePackageRollback(repository: RepositoryId, data: RollbackRequest): Promise {
- return this.client.request("/api/v1/service/rollback", {
- method: "POST",
- query: repository.toQuery(),
- json: data,
+ async servicePackagePatchRemove(packageBase: string, key: string): Promise {
+ return this.client.request(`/api/v1/packages/${encodeURIComponent(packageBase)}/patches/${encodeURIComponent(key)}`, {
+ method: "DELETE",
});
}
@@ -67,6 +67,14 @@ export class ServiceClient {
});
}
+ async servicePackageRollback(repository: RepositoryId, data: RollbackRequest): Promise {
+ return this.client.request("/api/v1/service/rollback", {
+ method: "POST",
+ query: repository.toQuery(),
+ json: data,
+ });
+ }
+
async servicePackageSearch(query: string): Promise {
return this.client.request("/api/v1/service/search", { query: { for: query } });
}
@@ -87,14 +95,6 @@ export class ServiceClient {
return this.client.request("/api/v1/service/pgp", { method: "POST", json: data });
}
- async servicePackageHoldUpdate(packageBase: string, repository: RepositoryId, isHeld: boolean): Promise {
- return this.client.request(`/api/v1/packages/${encodeURIComponent(packageBase)}/hold`, {
- method: "POST",
- query: repository.toQuery(),
- json: { is_held: isHeld },
- });
- }
-
async serviceRebuild(repository: RepositoryId, packages: string[]): Promise {
return this.client.request("/api/v1/service/rebuild", {
method: "POST",
diff --git a/frontend/src/components/charts/EventDurationLineChart.tsx b/frontend/src/components/charts/EventDurationLineChart.tsx
index 0c1744bb..952950eb 100644
--- a/frontend/src/components/charts/EventDurationLineChart.tsx
+++ b/frontend/src/components/charts/EventDurationLineChart.tsx
@@ -32,11 +32,11 @@ export default function EventDurationLineChart({ events }: EventDurationLineChar
labels: updateEvents.map(event => new Date(event.created * 1000).toISOStringShort()),
datasets: [
{
- label: "update duration, s",
- data: updateEvents.map(event => event.data?.took ?? 0),
- borderColor: blue[500],
backgroundColor: blue[200],
+ borderColor: blue[500],
cubicInterpolationMode: "monotone" as const,
+ data: updateEvents.map(event => event.data?.took ?? 0),
+ label: "update duration, s",
tension: 0.4,
},
],
diff --git a/frontend/src/components/charts/PackageCountBarChart.tsx b/frontend/src/components/charts/PackageCountBarChart.tsx
index 7a0ca8e0..76c97f8c 100644
--- a/frontend/src/components/charts/PackageCountBarChart.tsx
+++ b/frontend/src/components/charts/PackageCountBarChart.tsx
@@ -29,19 +29,19 @@ interface PackageCountBarChartProps {
export default function PackageCountBarChart({ stats }: PackageCountBarChartProps): React.JSX.Element {
return counters[label]),
backgroundColor: labels.map(label => StatusColors[label]),
+ data: labels.map(label => counters[label]),
+ label: "packages in status",
},
],
+ labels: labels,
};
return ;
diff --git a/frontend/src/components/common/AutoRefreshControl.tsx b/frontend/src/components/common/AutoRefreshControl.tsx
index bf125a2d..479281c4 100644
--- a/frontend/src/components/common/AutoRefreshControl.tsx
+++ b/frontend/src/components/common/AutoRefreshControl.tsx
@@ -25,14 +25,14 @@ import type { AutoRefreshInterval } from "models/AutoRefreshInterval";
import React, { useState } from "react";
interface AutoRefreshControlProps {
- intervals: AutoRefreshInterval[];
currentInterval: number;
+ intervals: AutoRefreshInterval[];
onIntervalChange: (interval: number) => void;
}
export default function AutoRefreshControl({
- intervals,
currentInterval,
+ intervals,
onIntervalChange,
}: AutoRefreshControlProps): React.JSX.Element | null {
const [anchorEl, setAnchorEl] = useState(null);
@@ -46,25 +46,25 @@ export default function AutoRefreshControl({
return <>
setAnchorEl(event.currentTarget)}
color={enabled ? "primary" : "default"}
+ onClick={event => setAnchorEl(event.currentTarget)}
+ size="small"
>
{enabled ? : }