Fixed Build

This commit is contained in:
Mitchell Magro 2025-11-25 11:39:58 +01:00
parent a62faab6b5
commit 4f05061411
15 changed files with 52 additions and 29 deletions

View File

@ -24,7 +24,14 @@ export async function POST(request: Request) {
let mustChangePassword = false; let mustChangePassword = false;
try { try {
const payload = decodeJwt(token); const payload = decodeJwt(token);
mustChangePassword = payload.MustChangePassword || false; const mustChangeClaim = payload.MustChangePassword;
if (typeof mustChangeClaim === "boolean") {
mustChangePassword = mustChangeClaim;
} else if (typeof mustChangeClaim === "string") {
mustChangePassword = mustChangeClaim.toLowerCase() === "true";
} else {
mustChangePassword = false;
}
} catch (err) { } catch (err) {
console.error("❌ Failed to decode current JWT:", err); console.error("❌ Failed to decode current JWT:", err);
} }

View File

@ -1,15 +1,17 @@
import { NextResponse } from "next/server"; // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { NextResponse, type NextRequest } from "next/server";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
const BE_BASE_URL = process.env.BE_BASE_URL || "http://localhost:8583"; const BE_BASE_URL = process.env.BE_BASE_URL || "http://localhost:8583";
const COOKIE_NAME = "auth_token"; const COOKIE_NAME = "auth_token";
export async function PUT( export async function PUT(
request: Request, request: NextRequest,
{ params }: { params: { id: string } } context: { params: Promise<{ id: string }> }
) { ) {
try { try {
const { id } = params; const { id } = await context.params;
if (!id) { if (!id) {
return NextResponse.json( return NextResponse.json(
@ -39,15 +41,14 @@ export async function PUT(
} }
); );
// Attempt to parse JSON; fall back to status-only response let data;
let data: unknown = null;
try { try {
data = await resp.json(); data = await resp.json();
} catch { } catch {
data = { success: resp.ok }; data = { success: resp.ok };
} }
return NextResponse.json(data ?? { success: resp.ok }, { return NextResponse.json(data, {
status: resp.status, status: resp.status,
}); });
} catch (error: unknown) { } catch (error: unknown) {

View File

@ -63,10 +63,10 @@ function transformUserUpdateData(updates: Record<string, unknown>): {
export async function PUT( export async function PUT(
request: Request, request: Request,
{ params }: { params: { id: string } } context: { params: Promise<{ id: string }> }
) { ) {
try { try {
const { id } = await params; const { id } = await context.params;
const body = await request.json(); const body = await request.json();
// Transform the request body to match backend format // Transform the request body to match backend format
@ -108,10 +108,10 @@ export async function PUT(
export async function DELETE( export async function DELETE(
_request: Request, _request: Request,
{ params }: { params: { id: string } } context: { params: Promise<{ id: string }> }
) { ) {
try { try {
const { id } = await params; const { id } = await context.params;
const { cookies } = await import("next/headers"); const { cookies } = await import("next/headers");
const cookieStore = await cookies(); const cookieStore = await cookies();
const token = cookieStore.get(COOKIE_NAME)?.value; const token = cookieStore.get(COOKIE_NAME)?.value;

View File

@ -5,10 +5,10 @@ const COOKIE_NAME = "auth_token";
export async function PUT( export async function PUT(
request: NextRequest, request: NextRequest,
{ params }: { params: { id: string } } context: { params: Promise<{ id: string }> }
) { ) {
try { try {
const { id } = await params; const { id } = await context.params;
const { cookies } = await import("next/headers"); const { cookies } = await import("next/headers");
const cookieStore = await cookies(); const cookieStore = await cookies();
const token = cookieStore.get(COOKIE_NAME)?.value; const token = cookieStore.get(COOKIE_NAME)?.value;

View File

@ -0,0 +1,13 @@
import { NextResponse } from "next/server";
/**
* Placeholder Settings API route.
* Keeps the module valid while the real implementation
* is being built, and makes the intent obvious to clients.
*/
export async function GET() {
return NextResponse.json(
{ message: "Settings endpoint not implemented" },
{ status: 501 }
);
}

View File

@ -4,7 +4,7 @@ import React from "react";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { Button, Box, Typography, Stack } from "@mui/material"; import { Button, Box, Typography, Stack } from "@mui/material";
import { AppDispatch } from "@/app/redux/types"; import { AppDispatch } from "@/app/redux/types";
import { autoLogout, refreshAuthStatus } from "@/app/redux/auth/authSlice"; import { autoLogout, validateAuth } from "@/app/redux/auth/authSlice";
export default function TestTokenExpiration() { export default function TestTokenExpiration() {
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
@ -14,7 +14,7 @@ export default function TestTokenExpiration() {
}; };
const handleRefreshAuth = () => { const handleRefreshAuth = () => {
dispatch(refreshAuthStatus()); dispatch(validateAuth());
}; };
return ( return (

View File

@ -22,7 +22,6 @@ export default function AllTransactionPage() {
transactions: TransactionRow[]; transactions: TransactionRow[];
total: number; total: number;
}>({ transactions: [], total: 0 }); }>({ transactions: [], total: 0 });
const [totalRows, setRowCount] = useState(0);
const extraColumns: string[] = []; // static for now const extraColumns: string[] = []; // static for now
// Memoize rows to avoid new reference each render // Memoize rows to avoid new reference each render

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const normalizeValue = (input: any): string => { export const normalizeValue = (input: any): string => {
if (input == null) return ""; if (input == null) return "";
if (typeof input === "string" || typeof input === "number") if (typeof input === "string" || typeof input === "number")

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import React, { useState, useEffect, useCallback, useMemo } from "react"; import React, { useState, useCallback, useMemo } from "react";
import { DataGrid, GridPaginationModel } from "@mui/x-data-grid"; import { DataGrid, GridPaginationModel } from "@mui/x-data-grid";
import { Box, Paper, Alert } from "@mui/material"; import { Box, Paper, Alert } from "@mui/material";
import DataTableHeader from "./DataTableHeader"; import DataTableHeader from "./DataTableHeader";

View File

@ -31,6 +31,8 @@ const SettingsAccountSecurity: React.FC = () => {
currentPassword: passwordData.currentPassword, currentPassword: passwordData.currentPassword,
}) })
); );
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) { } catch (err: any) {
// Error handling is now done by the epic // Error handling is now done by the epic
console.error("Password change error:", err); console.error("Password change error:", err);

View File

@ -20,11 +20,9 @@ const SettingsPageClient: React.FC = () => {
<Box sx={{ display: "flex", gap: 3 }}> <Box sx={{ display: "flex", gap: 3 }}>
<SettingsSidebar <SettingsSidebar
active={activeSection} active={activeSection}
onChange={( onChange={(section: "personal" | "account") =>
section: setActiveSection(section)
| string }
| ((prevState: "personal" | "account") => "personal" | "account")
) => setActiveSection(section)}
/> />
<Box sx={{ flex: 1 }}> <Box sx={{ flex: 1 }}>

View File

@ -47,6 +47,7 @@ const DeleteUser: React.FC<DeleteUserProps> = ({ open, onClose, user }) => {
(resultAction.payload as string) || "Failed to delete user" (resultAction.payload as string) || "Failed to delete user"
); );
} }
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) { } catch (error: any) {
toast.error(error.message || "An unexpected error occurred"); toast.error(error.message || "An unexpected error occurred");
} }

View File

@ -2,14 +2,14 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { initializeAuth } from "./auth/authSlice"; import { validateAuth } from "./auth/authSlice";
import { AppDispatch } from "./types"; import { AppDispatch } from "./types";
export function InitializeAuth() { export function InitializeAuth() {
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
useEffect(() => { useEffect(() => {
dispatch(initializeAuth()); dispatch(validateAuth());
}, [dispatch]); }, [dispatch]);
return null; return null;

View File

@ -3,7 +3,7 @@
import React, { useEffect, useRef } from "react"; import React, { useEffect, useRef } from "react";
import { Provider } from "react-redux"; import { Provider } from "react-redux";
import { store } from "./store"; import { store } from "./store";
import { checkAuthStatus } from "./auth/authSlice"; import { validateAuth } from "./auth/authSlice";
export default function ReduxProvider({ export default function ReduxProvider({
children, children,
@ -16,17 +16,17 @@ export default function ReduxProvider({
useEffect(() => { useEffect(() => {
// Check authentication status when the ReduxProvider component mounts on the client. // Check authentication status when the ReduxProvider component mounts on the client.
// This ensures your Redux auth state is synced with the server-side token. // This ensures your Redux auth state is synced with the server-side token.
store.dispatch(checkAuthStatus()); store.dispatch(validateAuth());
// Do an additional check after 2 seconds to ensure we have the latest token info // Do an additional check after 2 seconds to ensure we have the latest token info
initialCheckRef.current = setTimeout(() => { initialCheckRef.current = setTimeout(() => {
store.dispatch(checkAuthStatus()); store.dispatch(validateAuth());
}, 2000); }, 2000);
// Set up periodic token validation every 5 minutes // Set up periodic token validation every 5 minutes
intervalRef.current = setInterval( intervalRef.current = setInterval(
() => { () => {
store.dispatch(checkAuthStatus()); store.dispatch(validateAuth());
}, },
5 * 60 * 1000 5 * 60 * 1000
); // 5 minutes ); // 5 minutes

View File

@ -5,6 +5,7 @@ import toast from "react-hot-toast";
import { filter, switchMap } from "rxjs/operators"; import { filter, switchMap } from "rxjs/operators";
import { of } from "rxjs"; import { of } from "rxjs";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const changePasswordEpic: Epic<any, any, RootState> = action$ => export const changePasswordEpic: Epic<any, any, RootState> = action$ =>
action$.pipe( action$.pipe(
// Listen for any action related to changePassword (pending, fulfilled, rejected) // Listen for any action related to changePassword (pending, fulfilled, rejected)