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;
try {
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) {
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";
const BE_BASE_URL = process.env.BE_BASE_URL || "http://localhost:8583";
const COOKIE_NAME = "auth_token";
export async function PUT(
request: Request,
{ params }: { params: { id: string } }
request: NextRequest,
context: { params: Promise<{ id: string }> }
) {
try {
const { id } = params;
const { id } = await context.params;
if (!id) {
return NextResponse.json(
@ -39,15 +41,14 @@ export async function PUT(
}
);
// Attempt to parse JSON; fall back to status-only response
let data: unknown = null;
let data;
try {
data = await resp.json();
} catch {
data = { success: resp.ok };
}
return NextResponse.json(data ?? { success: resp.ok }, {
return NextResponse.json(data, {
status: resp.status,
});
} catch (error: unknown) {

View File

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

View File

@ -5,10 +5,10 @@ const COOKIE_NAME = "auth_token";
export async function PUT(
request: NextRequest,
{ params }: { params: { id: string } }
context: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params;
const { id } = await context.params;
const { cookies } = await import("next/headers");
const cookieStore = await cookies();
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 { Button, Box, Typography, Stack } from "@mui/material";
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() {
const dispatch = useDispatch<AppDispatch>();
@ -14,7 +14,7 @@ export default function TestTokenExpiration() {
};
const handleRefreshAuth = () => {
dispatch(refreshAuthStatus());
dispatch(validateAuth());
};
return (

View File

@ -22,7 +22,6 @@ export default function AllTransactionPage() {
transactions: TransactionRow[];
total: number;
}>({ transactions: [], total: 0 });
const [totalRows, setRowCount] = useState(0);
const extraColumns: string[] = []; // static for now
// 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 => {
if (input == null) return "";
if (typeof input === "string" || typeof input === "number")

View File

@ -1,6 +1,6 @@
"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 { Box, Paper, Alert } from "@mui/material";
import DataTableHeader from "./DataTableHeader";

View File

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

View File

@ -20,11 +20,9 @@ const SettingsPageClient: React.FC = () => {
<Box sx={{ display: "flex", gap: 3 }}>
<SettingsSidebar
active={activeSection}
onChange={(
section:
| string
| ((prevState: "personal" | "account") => "personal" | "account")
) => setActiveSection(section)}
onChange={(section: "personal" | "account") =>
setActiveSection(section)
}
/>
<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"
);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
toast.error(error.message || "An unexpected error occurred");
}

View File

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

View File

@ -3,7 +3,7 @@
import React, { useEffect, useRef } from "react";
import { Provider } from "react-redux";
import { store } from "./store";
import { checkAuthStatus } from "./auth/authSlice";
import { validateAuth } from "./auth/authSlice";
export default function ReduxProvider({
children,
@ -16,17 +16,17 @@ export default function ReduxProvider({
useEffect(() => {
// Check authentication status when the ReduxProvider component mounts on the client.
// 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
initialCheckRef.current = setTimeout(() => {
store.dispatch(checkAuthStatus());
store.dispatch(validateAuth());
}, 2000);
// Set up periodic token validation every 5 minutes
intervalRef.current = setInterval(
() => {
store.dispatch(checkAuthStatus());
store.dispatch(validateAuth());
},
5 * 60 * 1000
); // 5 minutes

View File

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