some fixes and refactors

This commit is contained in:
counterweight 2025-12-19 11:08:19 +01:00
parent ead8a566d0
commit 75cfc6c928
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C
16 changed files with 381 additions and 425 deletions

View file

@ -20,6 +20,9 @@ const mockLogout = vi.fn();
const mockHasPermission = vi.fn((permission: string) =>
mockUser?.permissions.includes(permission) ?? false
);
const mockHasRole = vi.fn((role: string) =>
mockUser?.roles.includes(role) ?? false
);
vi.mock("../auth-context", () => ({
useAuth: () => ({
@ -27,6 +30,7 @@ vi.mock("../auth-context", () => ({
isLoading: mockIsLoading,
logout: mockLogout,
hasPermission: mockHasPermission,
hasRole: mockHasRole,
}),
Permission: {
VIEW_COUNTER: "view_counter",
@ -52,6 +56,9 @@ beforeEach(() => {
mockHasPermission.mockImplementation((permission: string) =>
mockUser?.permissions.includes(permission) ?? false
);
mockHasRole.mockImplementation((role: string) =>
mockUser?.roles.includes(role) ?? false
);
// Default: successful empty response
mockFetch.mockResolvedValue({
ok: true,
@ -114,7 +121,8 @@ describe("AuditPage", () => {
render(<AuditPage />);
await waitFor(() => {
expect(screen.getByText("Failed to load counter records")).toBeTruthy();
const errors = screen.getAllByText("Request failed: 500");
expect(errors.length).toBeGreaterThan(0);
});
});

View file

@ -1,10 +1,11 @@
"use client";
import { useEffect, useState, useCallback } from "react";
import { useRouter } from "next/navigation";
import { useAuth, Permission } from "../auth-context";
import { API_URL } from "../config";
import { Permission } from "../auth-context";
import { api } from "../api";
import { sharedStyles } from "../styles/shared";
import { Header } from "../components/Header";
import { useRequireAuth } from "../hooks/useRequireAuth";
interface CounterRecord {
id: number;
@ -38,31 +39,17 @@ export default function AuditPage() {
const [sumError, setSumError] = useState<string | null>(null);
const [counterPage, setCounterPage] = useState(1);
const [sumPage, setSumPage] = useState(1);
const { user, isLoading, logout, hasPermission } = useAuth();
const router = useRouter();
const canViewAudit = hasPermission(Permission.VIEW_AUDIT);
useEffect(() => {
if (!isLoading) {
if (!user) {
router.push("/login");
} else if (!canViewAudit) {
router.push("/");
}
}
}, [isLoading, user, router, canViewAudit]);
const { user, isLoading, isAuthorized } = useRequireAuth({
requiredPermission: Permission.VIEW_AUDIT,
fallbackRedirect: "/",
});
const fetchCounterRecords = useCallback(async (page: number) => {
setCounterError(null);
try {
const res = await fetch(`${API_URL}/api/audit/counter?page=${page}&per_page=10`, {
credentials: "include",
});
if (!res.ok) {
throw new Error("Failed to load counter records");
}
const data = await res.json();
const data = await api.get<PaginatedResponse<CounterRecord>>(
`/api/audit/counter?page=${page}&per_page=10`
);
setCounterData(data);
} catch (err) {
setCounterData(null);
@ -73,13 +60,9 @@ export default function AuditPage() {
const fetchSumRecords = useCallback(async (page: number) => {
setSumError(null);
try {
const res = await fetch(`${API_URL}/api/audit/sum?page=${page}&per_page=10`, {
credentials: "include",
});
if (!res.ok) {
throw new Error("Failed to load sum records");
}
const data = await res.json();
const data = await api.get<PaginatedResponse<SumRecord>>(
`/api/audit/sum?page=${page}&per_page=10`
);
setSumData(data);
} catch (err) {
setSumData(null);
@ -88,21 +71,16 @@ export default function AuditPage() {
}, []);
useEffect(() => {
if (user && canViewAudit) {
if (user && isAuthorized) {
fetchCounterRecords(counterPage);
}
}, [user, counterPage, canViewAudit, fetchCounterRecords]);
}, [user, counterPage, isAuthorized, fetchCounterRecords]);
useEffect(() => {
if (user && canViewAudit) {
if (user && isAuthorized) {
fetchSumRecords(sumPage);
}
}, [user, sumPage, canViewAudit, fetchSumRecords]);
const handleLogout = async () => {
await logout();
router.push("/login");
};
}, [user, sumPage, isAuthorized, fetchSumRecords]);
const formatDate = (dateStr: string) => {
return new Date(dateStr).toLocaleString();
@ -116,23 +94,13 @@ export default function AuditPage() {
);
}
if (!user || !canViewAudit) {
if (!user || !isAuthorized) {
return null;
}
return (
<main style={styles.main}>
<div style={styles.header}>
<div style={styles.nav}>
<span style={styles.navCurrent}>Audit</span>
</div>
<div style={styles.userInfo}>
<span style={styles.userEmail}>{user.email}</span>
<button onClick={handleLogout} style={styles.logoutBtn}>
Sign out
</button>
</div>
</div>
<Header currentPage="audit" />
<div style={styles.content}>
<div style={styles.tablesContainer}>