"use client"; import { ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Clock, Eye, Filter, Globe, MessageSquare, Search, } from "lucide-react"; import Link from "next/link"; import { useCallback, useEffect, useId, useReducer } from "react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { formatCategory } from "@/lib/format-enums"; import type { ChatSession } from "@/lib/types"; // State types interface FilterState { searchTerm: string; debouncedSearchTerm: string; category: string; language: string; startDate: string; endDate: string; sortKey: string; sortOrder: "asc" | "desc"; options: { categories: string[]; languages: string[] }; } interface PaginationState { currentPage: number; totalPages: number; pageSize: number; } interface UIState { loading: boolean; error: string | null; filtersExpanded: boolean; } interface SessionsState { sessions: ChatSession[]; filters: FilterState; pagination: PaginationState; ui: UIState; } // Action types type SessionsAction = | { type: "SET_SESSIONS"; payload: ChatSession[] } | { type: "SET_FILTER"; field: keyof Omit; value: string; } | { type: "SET_DEBOUNCED_SEARCH"; value: string } | { type: "SET_FILTER_OPTIONS"; payload: { categories: string[]; languages: string[] }; } | { type: "SET_PAGE"; page: number } | { type: "SET_TOTAL_PAGES"; total: number } | { type: "SET_LOADING"; loading: boolean } | { type: "SET_ERROR"; error: string | null } | { type: "TOGGLE_FILTERS" }; const initialState: SessionsState = { sessions: [], filters: { searchTerm: "", debouncedSearchTerm: "", category: "", language: "", startDate: "", endDate: "", sortKey: "startTime", sortOrder: "desc", options: { categories: [], languages: [] }, }, pagination: { currentPage: 1, totalPages: 0, pageSize: 10, }, ui: { loading: true, error: null, filtersExpanded: false, }, }; function sessionsReducer( state: SessionsState, action: SessionsAction ): SessionsState { switch (action.type) { case "SET_SESSIONS": return { ...state, sessions: action.payload }; case "SET_FILTER": return { ...state, filters: { ...state.filters, [action.field]: action.value }, pagination: { ...state.pagination, currentPage: 1 }, // Reset page on filter change }; case "SET_DEBOUNCED_SEARCH": return { ...state, filters: { ...state.filters, debouncedSearchTerm: action.value }, }; case "SET_FILTER_OPTIONS": return { ...state, filters: { ...state.filters, options: action.payload }, }; case "SET_PAGE": return { ...state, pagination: { ...state.pagination, currentPage: action.page }, }; case "SET_TOTAL_PAGES": return { ...state, pagination: { ...state.pagination, totalPages: action.total }, }; case "SET_LOADING": return { ...state, ui: { ...state.ui, loading: action.loading } }; case "SET_ERROR": return { ...state, ui: { ...state.ui, error: action.error } }; case "TOGGLE_FILTERS": return { ...state, ui: { ...state.ui, filtersExpanded: !state.ui.filtersExpanded }, }; default: return state; } } export default function SessionsPage() { const [state, dispatch] = useReducer(sessionsReducer, initialState); const { sessions, filters, pagination, ui } = state; const searchHeadingId = useId(); const filtersHeadingId = useId(); const filterContentId = useId(); const categoryFilterId = useId(); const categoryHelpId = useId(); const languageFilterId = useId(); const languageHelpId = useId(); const sortOrderId = useId(); const sortOrderHelpId = useId(); const resultsHeadingId = useId(); const startDateFilterId = useId(); const startDateHelpId = useId(); const endDateFilterId = useId(); const endDateHelpId = useId(); const sortKeyId = useId(); const sortKeyHelpId = useId(); // Debounce search term useEffect(() => { const timerId = setTimeout(() => { dispatch({ type: "SET_DEBOUNCED_SEARCH", value: filters.searchTerm }); }, 500); return () => clearTimeout(timerId); }, [filters.searchTerm]); const fetchFilterOptions = useCallback(async () => { try { const response = await fetch("/api/dashboard/session-filter-options"); if (!response.ok) throw new Error("Failed to fetch filter options"); const data = await response.json(); dispatch({ type: "SET_FILTER_OPTIONS", payload: data }); } catch (err) { dispatch({ type: "SET_ERROR", error: err instanceof Error ? err.message : "Failed to load filter options", }); } }, []); const fetchSessions = useCallback(async () => { dispatch({ type: "SET_LOADING", loading: true }); dispatch({ type: "SET_ERROR", error: null }); try { const params = new URLSearchParams(); if (filters.debouncedSearchTerm) params.append("searchTerm", filters.debouncedSearchTerm); if (filters.category) params.append("category", filters.category); if (filters.language) params.append("language", filters.language); if (filters.startDate) params.append("startDate", filters.startDate); if (filters.endDate) params.append("endDate", filters.endDate); if (filters.sortKey) params.append("sortKey", filters.sortKey); if (filters.sortOrder) params.append("sortOrder", filters.sortOrder); params.append("page", pagination.currentPage.toString()); params.append("pageSize", pagination.pageSize.toString()); const response = await fetch( `/api/dashboard/sessions?${params.toString()}` ); if (!response.ok) throw new Error(`Failed to fetch sessions: ${response.statusText}`); const data = await response.json(); dispatch({ type: "SET_SESSIONS", payload: data.sessions || [] }); dispatch({ type: "SET_TOTAL_PAGES", total: Math.ceil((data.totalSessions || 0) / pagination.pageSize), }); } catch (err) { dispatch({ type: "SET_ERROR", error: err instanceof Error ? err.message : "An unknown error occurred", }); dispatch({ type: "SET_SESSIONS", payload: [] }); } finally { dispatch({ type: "SET_LOADING", loading: false }); } }, [filters, pagination.currentPage, pagination.pageSize]); useEffect(() => { fetchSessions(); }, [fetchSessions]); useEffect(() => { fetchFilterOptions(); }, [fetchFilterOptions]); return (
{/* Page heading for screen readers */}

Sessions Management

{/* Header */}
Chat Sessions
{/* Search Input */}

Search Sessions

{/* Filter and Sort Controls */}
{ui.filtersExpanded && (
Session Filters and Sorting Options
{/* Category Filter */}
Filter sessions by category type
{/* Language Filter */}
Filter sessions by language
{/* Start Date Filter */}
dispatch({ type: "SET_FILTER", field: "startDate", value: e.target.value, }) } aria-describedby={startDateHelpId} />
Filter sessions from this date onwards
{/* End Date Filter */}
dispatch({ type: "SET_FILTER", field: "endDate", value: e.target.value, }) } aria-describedby={endDateHelpId} />
Filter sessions up to this date
{/* Sort Key */}
Choose field to sort sessions by
{/* Sort Order */}
Choose ascending or descending order
)}
{/* Results section */}

Session Results

{/* Live region for screen reader announcements */} {ui.loading && "Loading sessions..."} {ui.error && `Error loading sessions: ${ui.error}`} {!ui.loading && !ui.error && sessions.length > 0 && `Found ${sessions.length} sessions`} {!ui.loading && !ui.error && sessions.length === 0 && "No sessions found"} {/* Loading State */} {ui.loading && ( )} {/* Error State */} {ui.error && ( )} {/* Empty State */} {!ui.loading && !ui.error && sessions.length === 0 && (
{filters.debouncedSearchTerm ? `No sessions found for "${filters.debouncedSearchTerm}".` : "No sessions found."}
)} {/* Sessions List */} {!ui.loading && !ui.error && sessions.length > 0 && (
    {sessions.map((session) => (
  • Session {session.sessionId || session.id} from{" "} {new Date(session.startTime).toLocaleDateString()}

    ID {session.sessionId || session.id}
    {new Date(session.startTime).toLocaleTimeString()}
    {session.category && ( )} {session.language && ( )}
    {session.summary ? (

    {session.summary}

    ) : session.initialMsg ? (

    {session.initialMsg}

    ) : null}
  • ))}
)} {/* Pagination */} {pagination.totalPages > 0 && (
Page {pagination.currentPage} of {pagination.totalPages}
)}
); }