diff --git a/frontend/module/admin/layout/AdminDashboardLayout.tsx b/frontend/module/admin/layout/AdminDashboardLayout.tsx
new file mode 100644
index 0000000..94be9bd
--- /dev/null
+++ b/frontend/module/admin/layout/AdminDashboardLayout.tsx
@@ -0,0 +1,155 @@
+"use client";
+
+import Link from "next/link";
+import { usePathname, useRouter } from "next/navigation";
+import { ReactNode, useEffect, useState } from "react";
+import { useAuth } from "../../auth/context/AuthContext";
+
+interface NavItem {
+ href: string;
+ label: string;
+ icon: ReactNode;
+}
+
+const NAV_ITEMS: NavItem[] = [
+ {
+ href: "/admin/users",
+ label: "Users",
+ icon: (
+
+ ),
+ },
+ {
+ href: "/admin/documents",
+ label: "All Documents",
+ icon: (
+
+ ),
+ },
+ {
+ href: "/admin/audit",
+ label: "Audit Log",
+ icon: (
+
+ ),
+ },
+ {
+ href: "/admin/queue",
+ label: "Queue Stats",
+ icon: (
+
+ ),
+ },
+ {
+ href: "/admin/health",
+ label: "System Health",
+ icon: (
+
+ ),
+ },
+];
+
+export default function AdminDashboardLayout({
+ children,
+}: {
+ children: ReactNode;
+}) {
+ const { user, isLoading, logout } = useAuth();
+ const pathname = usePathname();
+ const router = useRouter();
+ const [collapsed, setCollapsed] = useState(false);
+
+ useEffect(() => {
+ if (!isLoading && user?.role !== "admin") {
+ router.replace("/dashboard?unauthorized=1");
+ }
+ }, [isLoading, user, router]);
+
+ useEffect(() => {
+ const mq = window.matchMedia("(max-width: 1023px)");
+ setCollapsed(mq.matches);
+ const handler = (e: MediaQueryListEvent) => setCollapsed(e.matches);
+ mq.addEventListener("change", handler);
+ return () => mq.removeEventListener("change", handler);
+ }, []);
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ if (user?.role !== "admin") return null;
+
+ return (
+
+
+
+
+
+
+ {user.fullName}
+
+
+
+ {children}
+
+
+ );
+}
diff --git a/frontend/module/auth/context/AuthContext.tsx b/frontend/module/auth/context/AuthContext.tsx
new file mode 100644
index 0000000..7fd896b
--- /dev/null
+++ b/frontend/module/auth/context/AuthContext.tsx
@@ -0,0 +1,138 @@
+"use client";
+
+import React, {
+ createContext,
+ useCallback,
+ useContext,
+ useEffect,
+ useState,
+} from "react";
+import { useRouter } from "next/navigation";
+
+interface User {
+ id: string;
+ email: string;
+ fullName: string;
+ role: string;
+ preferredLanguage?: string;
+ twoFactorEnabled?: boolean;
+}
+
+interface AuthContextValue {
+ user: User | null;
+ isAuthenticated: boolean;
+ isLoading: boolean;
+ login: (
+ email: string,
+ password: string
+ ) => Promise<{ success: boolean; error?: string }>;
+ logout: () => void;
+ refreshUser: () => Promise;
+}
+
+const AuthContext = createContext(null);
+
+function getToken(): string {
+ return typeof localStorage !== "undefined"
+ ? (localStorage.getItem("access_token") ?? "")
+ : "";
+}
+
+function setToken(token: string) {
+ localStorage.setItem("access_token", token);
+}
+
+function clearToken() {
+ localStorage.removeItem("access_token");
+}
+
+export function AuthProvider({ children }: { children: React.ReactNode }) {
+ const router = useRouter();
+ const [user, setUser] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+
+ const apiBase = process.env.NEXT_PUBLIC_API_URL ?? "";
+
+ const fetchMe = useCallback(async (): Promise => {
+ const token = getToken();
+ if (!token) {
+ setUser(null);
+ return;
+ }
+ const res = await fetch(`${apiBase}/api/module/users/me`, {
+ headers: { Authorization: `Bearer ${token}` },
+ });
+ if (res.ok) {
+ const data: User = await res.json();
+ setUser(data);
+ } else {
+ clearToken();
+ setUser(null);
+ }
+ }, [apiBase]);
+
+ useEffect(() => {
+ fetchMe().finally(() => setIsLoading(false));
+ }, [fetchMe]);
+
+ const login = useCallback(
+ async (
+ email: string,
+ password: string
+ ): Promise<{ success: boolean; error?: string }> => {
+ try {
+ const res = await fetch(`${apiBase}/api/auth/login`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ email, password }),
+ });
+ if (!res.ok) {
+ const data = await res.json().catch(() => ({}));
+ return { success: false, error: data.message ?? "Login failed." };
+ }
+ const data = await res.json();
+ setToken(data.access_token ?? data.token);
+ await fetchMe();
+ return { success: true };
+ } catch {
+ return { success: false, error: "Network error." };
+ }
+ },
+ [apiBase, fetchMe]
+ );
+
+ const logout = useCallback(() => {
+ clearToken();
+ setUser(null);
+ router.push("/auth/login");
+ }, [router]);
+
+ const refreshUser = useCallback(async () => {
+ await fetchMe();
+ }, [fetchMe]);
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useAuth(): AuthContextValue {
+ const ctx = useContext(AuthContext);
+ if (!ctx) {
+ throw new Error("useAuth must be used inside ");
+ }
+ return ctx;
+}
+
+export default AuthContext;
diff --git a/frontend/module/components/export-button/DocumentExportButton.tsx b/frontend/module/components/export-button/DocumentExportButton.tsx
new file mode 100644
index 0000000..aafabf3
--- /dev/null
+++ b/frontend/module/components/export-button/DocumentExportButton.tsx
@@ -0,0 +1,118 @@
+"use client";
+
+import { useRef, useState } from "react";
+
+interface DocumentExportButtonProps {
+ documentId?: string;
+}
+
+type ExportType = "pdf" | "excel";
+
+function downloadBlob(blob: Blob, filename: string) {
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement("a");
+ a.href = url;
+ a.download = filename;
+ a.click();
+ URL.revokeObjectURL(url);
+}
+
+export default function DocumentExportButton({
+ documentId,
+}: DocumentExportButtonProps) {
+ const [open, setOpen] = useState(false);
+ const [loading, setLoading] = useState(null);
+ const [toastError, setToastError] = useState(null);
+ const menuRef = useRef(null);
+
+ const token = () => localStorage.getItem("access_token") ?? "";
+ const apiBase = process.env.NEXT_PUBLIC_API_URL ?? "";
+
+ async function handleExport(type: ExportType) {
+ setOpen(false);
+ setLoading(type);
+ setToastError(null);
+ try {
+ const url =
+ type === "pdf" && documentId
+ ? `${apiBase}/api/module/documents/${documentId}/export/pdf`
+ : `${apiBase}/api/module/documents/export/excel`;
+
+ const res = await fetch(url, {
+ headers: { Authorization: `Bearer ${token()}` },
+ });
+ if (!res.ok) throw new Error("Export request failed.");
+ const blob = await res.blob();
+ const ext = type === "pdf" ? "pdf" : "xlsx";
+ const name = documentId
+ ? `document-${documentId}.${ext}`
+ : `documents.${ext}`;
+ downloadBlob(blob, name);
+ } catch (err) {
+ setToastError(err instanceof Error ? err.message : "Export failed.");
+ setTimeout(() => setToastError(null), 4000);
+ } finally {
+ setLoading(null);
+ }
+ }
+
+ const showPdf = !!documentId;
+
+ return (
+
+
+
+ {open && (
+
+ {showPdf && (
+
+ )}
+
+
+ )}
+
+ {toastError && (
+
+ {toastError}
+
+ )}
+
+ );
+}
diff --git a/frontend/module/components/verify-button/VerificationRequestButton.tsx b/frontend/module/components/verify-button/VerificationRequestButton.tsx
new file mode 100644
index 0000000..e2b7348
--- /dev/null
+++ b/frontend/module/components/verify-button/VerificationRequestButton.tsx
@@ -0,0 +1,104 @@
+"use client";
+
+import { useState } from "react";
+
+type DocumentStatus = "PENDING" | "FLAGGED" | "ANALYZING" | "VERIFIED" | string;
+
+interface VerificationRequestButtonProps {
+ documentId: string;
+ currentStatus: DocumentStatus;
+ onQueued?: () => void;
+}
+
+interface Toast {
+ message: string;
+ type: "success" | "error";
+}
+
+export default function VerificationRequestButton({
+ documentId,
+ currentStatus,
+ onQueued,
+}: VerificationRequestButtonProps) {
+ const [status, setStatus] = useState(currentStatus);
+ const [loading, setLoading] = useState(false);
+ const [toast, setToast] = useState(null);
+
+ function showToast(message: string, type: Toast["type"]) {
+ setToast({ message, type });
+ setTimeout(() => setToast(null), 4000);
+ }
+
+ async function handleRequest() {
+ setLoading(true);
+ try {
+ const res = await fetch(
+ `${process.env.NEXT_PUBLIC_API_URL}/api/documents/${documentId}/verify`,
+ {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${localStorage.getItem("access_token") ?? ""}`,
+ },
+ }
+ );
+ if (res.status === 202) {
+ setStatus("ANALYZING");
+ showToast("Verification queued", "success");
+ onQueued?.();
+ } else {
+ const data = await res.json().catch(() => ({}));
+ showToast(data.message ?? "Verification request failed.", "error");
+ }
+ } catch {
+ showToast("Network error. Please try again.", "error");
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ const isRequestable = status === "PENDING" || status === "FLAGGED";
+ const isQueued = status === "ANALYZING";
+ const isVerified = status === "VERIFIED";
+
+ return (
+
+ {isVerified ? (
+
+
+ Verified on Stellar
+
+ ) : isQueued ? (
+
+ ) : isRequestable ? (
+
+ ) : null}
+
+ {toast && (
+
+ {toast.message}
+
+ )}
+
+ );
+}