diff --git a/app/api/auth/validate/route.ts b/app/api/auth/validate/route.ts index 3c406b9..ea7d7eb 100644 --- a/app/api/auth/validate/route.ts +++ b/app/api/auth/validate/route.ts @@ -24,6 +24,8 @@ export async function POST() { const data = await resp.json(); + console.log("[DEBUG] [VALIDATE-AUTH][ROUTE][data]: ", data); + return NextResponse.json(data, { status: resp.status }); } catch (err: unknown) { const message = err instanceof Error ? err.message : "Unknown error"; diff --git a/app/api/dashboard/admin/mockData.ts b/app/api/dashboard/admin/mockData.ts deleted file mode 100644 index 194e386..0000000 --- a/app/api/dashboard/admin/mockData.ts +++ /dev/null @@ -1,63 +0,0 @@ -// mockData.ts -export type User = { - merchantId: number; - id: string; - name?: string; - username?: string; - firstName?: string; - lastName?: string; - email?: string; - phone?: string; - jobTitle?: string; - enabled?: boolean; - authorities?: string[]; - allowedMerchantIds?: number[]; - created?: string; - disabledBy?: string | null; - disabledDate?: string | null; - disabledReason?: string | null; - incidentNotes?: boolean; - lastLogin?: string; - lastMandatoryUpdated?: string; - marketingNewsletter?: boolean; - releaseNotes?: boolean; - requiredActions?: string[]; - twoFactorCondition?: string; - twoFactorCredentials?: unknown[]; // narrow if you know the shape -}; - -export const users: User[] = [ - { - merchantId: 100987998, - id: "bc6a8a55-13bc-4538-8255-cd0cec3bb4e9", - name: "Jacob", - username: "lspaddy", - firstName: "Paddy", - lastName: "Man", - email: "patrick@omegasys.eu", - phone: "", - jobTitle: "", - enabled: true, - authorities: [ - "ROLE_IIN", - "ROLE_FIRST_APPROVER", - "ROLE_RULES_ADMIN", - "ROLE_TRANSACTION_VIEWER", - "ROLE_IIN_ADMIN", - "ROLE_USER_PSP_ACCOUNT", - ], - allowedMerchantIds: [100987998], - created: "2025-05-04T15:32:48.432Z", - disabledBy: null, - disabledDate: null, - disabledReason: null, - incidentNotes: false, - lastLogin: "", - lastMandatoryUpdated: "2025-05-04T15:32:48.332Z", - marketingNewsletter: false, - releaseNotes: false, - requiredActions: ["CONFIGURE_TOTP", "UPDATE_PASSWORD"], - twoFactorCondition: "required", - twoFactorCredentials: [], - }, -]; diff --git a/app/api/dashboard/admin/users/route.ts b/app/api/dashboard/admin/users/route.ts new file mode 100644 index 0000000..8c128da --- /dev/null +++ b/app/api/dashboard/admin/users/route.ts @@ -0,0 +1,50 @@ +import { NextResponse } from "next/server"; + +const BE_BASE_URL = process.env.BE_BASE_URL || "http://localhost:5000"; +const COOKIE_NAME = "auth_token"; + +export async function GET(request: Request) { + try { + const { cookies } = await import("next/headers"); + const cookieStore = await cookies(); + const token = cookieStore.get(COOKIE_NAME)?.value; + + if (!token) { + return NextResponse.json( + { message: "Missing Authorization header" }, + { status: 401 } + ); + } + + // Preserve query string when proxying + const url = new URL(request.url); + const queryString = url.search ? url.search : ""; + + console.log( + "[DEBUG] - URL", + `${BE_BASE_URL}/api/v1/users/list${queryString}` + ); + + const response = await fetch( + `${BE_BASE_URL}/api/v1/users/list${queryString}`, + { + method: "GET", + headers: { + Authorization: `Bearer ${token}`, + }, + cache: "no-store", + } + ); + + console.log("[DEBUG] - Response", response); + const data = await response.json(); + return NextResponse.json(data, { status: response.status }); + } catch (err: unknown) { + console.error("Proxy GET /api/v1/users/list error:", err); + const errorMessage = err instanceof Error ? err.message : "Unknown error"; + return NextResponse.json( + { message: "Internal server error", error: errorMessage }, + { status: 500 } + ); + } +} diff --git a/app/dashboard/admin/users/page.tsx b/app/dashboard/admin/users/page.tsx index d72a71b..8b2eb32 100644 --- a/app/dashboard/admin/users/page.tsx +++ b/app/dashboard/admin/users/page.tsx @@ -1,17 +1,60 @@ import Users from "@/app/features/Pages/Admin/Users/users"; +import { cookies } from "next/headers"; -export default async function BackOfficeUsersPage() { - // const baseUrl = process.env.NEXT_PUBLIC_BASE_URL - // ? `${process.env.NEXT_PUBLIC_BASE_URL}` - // : "http://localhost:4000"; - // const res = await fetch(`${baseUrl}/api/dashboard/admin/users`, { - // cache: "no-store", // 👈 disables caching for SSR freshness - // }); - // const users = await res.json(); +export default async function BackOfficeUsersPage({ + searchParams, +}: { + searchParams: Promise>; +}) { + // Await searchParams and extract pagination + const params = await searchParams; + const limit = params.limit || "10"; + const page = params.page || "1"; + + // Build absolute URL for server-side fetch + // In server components, fetch requires absolute URLs + const port = process.env.PORT || "3000"; + const baseUrl = + process.env.NEXT_PUBLIC_BASE_URL || + (process.env.VERCEL_URL + ? `https://${process.env.VERCEL_URL}` + : `http://localhost:${port}`); + + const url = new URL(`${baseUrl}/api/dashboard/admin/users`); + url.searchParams.set("limit", typeof limit === "string" ? limit : limit[0]); + url.searchParams.set("page", typeof page === "string" ? page : page[0]); + + // Forward cookies for auth when calling the internal API route + const cookieStore = await cookies(); + const cookieHeader = cookieStore + .getAll() + .map((c: { name: string; value: string }) => `${c.name}=${c.value}`) + .join("; "); + + const res = await fetch(url.toString(), { + headers: { + Cookie: cookieHeader, + }, + cache: "no-store", + }); + + if (!res.ok) { + console.error("Failed to fetch users:", res.status, res.statusText); + return ( +
+ +
+ ); + } + + const data = await res.json(); + console.log("[DEBUG] - DATA", data); + // Handle different response structures: could be array directly, or wrapped in data/users property + // const users = Array.isArray(data) ? data : data.users || data.data || []; return (
- +
); } diff --git a/app/features/Pages/Admin/Users/interfaces.ts b/app/features/Pages/Admin/Users/interfaces.ts index f99b43e..42c050a 100644 --- a/app/features/Pages/Admin/Users/interfaces.ts +++ b/app/features/Pages/Admin/Users/interfaces.ts @@ -1,27 +1,12 @@ export interface IUser { - merchantId: number; - name?: string; id: string; - username: string; - firstName: string; - lastName: string; email: string; + first_name: string; + last_name: string; + username: string; phone: string; - jobTitle: string; + job_title: string; + groups: string[]; // or a more specific type if you know what’s inside + merchants: string[]; // likewise, refine this if needed enabled: boolean; - authorities: string[]; - allowedMerchantIds: number[]; - created: string; - disabledBy: string | null; - disabledDate: string | null; - disabledReason: string | null; - incidentNotes: boolean; - lastLogin: string; - lastMandatoryUpdated: string; - marketingNewsletter: boolean; - releaseNotes: boolean; - requiredActions: string[]; - twoFactorCondition: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - twoFactorCredentials: any[]; // Assuming this is an array that could contain any type of data } diff --git a/app/features/Pages/Admin/Users/users.tsx b/app/features/Pages/Admin/Users/users.tsx index 03d62ec..68bd008 100644 --- a/app/features/Pages/Admin/Users/users.tsx +++ b/app/features/Pages/Admin/Users/users.tsx @@ -26,13 +26,10 @@ const Users: React.FC = ({ users }) => { }} /> {users?.length > 0 && - users.map((user: IUser) => ( + users?.map((user: IUser) => ( {user.username} - - Merchant ID: {user.merchantId} - { const handleClose = () => { dispatch(clearAddedUser()); - dispatch(setShowNewUserModal(false)); - // Refresh data after closing to update lists without racing rehydrate router.refresh(); + dispatch(setShowNewUserModal(false)); }; return ( diff --git a/app/features/UserRoles/userRoleCard.tsx b/app/features/UserRoles/userRoleCard.tsx index 4c769dc..c128850 100644 --- a/app/features/UserRoles/userRoleCard.tsx +++ b/app/features/UserRoles/userRoleCard.tsx @@ -25,19 +25,11 @@ import { IUser } from "../Pages/Admin/Users/interfaces"; interface Props { user: IUser; - isAdmin: boolean; - lastLogin: string; - merchants: string[]; - extraRolesCount?: number; } -export default function UserRoleCard({ - user, - isAdmin, - extraRolesCount, -}: Props) { +export default function UserRoleCard({ user }: Props) { const [isEditing, setIsEditing] = useState(false); - const { username, name, email, authorities: roles } = user; + const { username, first_name, last_name, email, groups } = user; const handleEditClick = () => { setIsEditing(!isEditing); }; @@ -50,10 +42,12 @@ export default function UserRoleCard({ {username?.slice(0, 2).toUpperCase()} {username} - {name} + + {first_name} {last_name} + {email} - {isAdmin && ( + {true && ( } label="Admin" size="small" /> )} @@ -100,11 +94,11 @@ export default function UserRoleCard({ - {roles.map(role => ( + {groups.map(role => ( ))} - {extraRolesCount && } + {/* {extraRolesCount && } */}
("auth/validate", async (_, { rejectWithValue }) => { +>("auth/validate", async (_, { rejectWithValue, dispatch }) => { try { const res = await fetch("/api/auth/validate", { method: "POST" }); const data = await res.json(); - if (!res.ok || !data.valid) { + if (!res.ok || !data.success) { + toast.error(data.message || "Session invalid or expired"); + dispatch(autoLogout("Session invalid or expired")); return rejectWithValue("Session invalid or expired"); } diff --git a/package.json b/package.json index 182a29d..a44481f 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@types/react-dom": "^19", "@types/react-redux": "^7.1.34", "@types/redux-persist": "^4.3.1", + "cross-env": "^10.1.0", "eslint": "^9", "eslint-config-next": "15.3.3", "husky": "^9.1.7", diff --git a/services/roles.services.ts b/services/roles.services.ts index 093d831..718180a 100644 --- a/services/roles.services.ts +++ b/services/roles.services.ts @@ -1,20 +1,6 @@ import { IEditUserForm } from "@/app/features/UserRoles/User.interfaces"; -// export async function addUser(data: IEditUserForm) { -// const res = await fetch("/api/auth/register", { -// method: "POST", -// headers: { "Content-Type": "application/json" }, -// body: JSON.stringify(data), -// }); - -// if (!res.ok) { -// throw new Error("Failed to create role"); -// } - -// return res.json(); // or return type depending on your backend -// } export async function editUser(id: string, data: IEditUserForm) { - console.log("[editUser] - id", id, data); const res = await fetch(`/api/dashboard/admin/users/${id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, diff --git a/yarn.lock b/yarn.lock index 9c4aaf1..f238341 100644 --- a/yarn.lock +++ b/yarn.lock @@ -233,6 +233,11 @@ resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz" integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== +"@epic-web/invariant@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@epic-web/invariant/-/invariant-1.0.0.tgz#1073e5dee6dd540410784990eb73e4acd25c9813" + integrity sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA== + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.7.0": version "4.7.0" resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz" @@ -1619,6 +1624,14 @@ crc-32@~1.2.0, crc-32@~1.2.1: resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz" integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== +cross-env@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-10.1.0.tgz#cfd2a6200df9ed75bfb9cb3d7ce609c13ea21783" + integrity sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw== + dependencies: + "@epic-web/invariant" "^1.0.0" + cross-spawn "^7.0.6" + cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz"