From a10a228ff13c1d7d63b3205be3fd52798d400dcd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 7 Dec 2025 04:15:46 +0000 Subject: [PATCH 1/3] Initial plan From 51501cd2aae5ad3362972d84c0d0e6822aa5d56b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 7 Dec 2025 04:25:57 +0000 Subject: [PATCH 2/3] Fix critical bugs in adjudication queue page - Fixed Tabs component to actually switch tabs and show/hide content - Added status filter parameter to API request (was missing) - Enhanced backend schema to include subject_name and triggered_rules - Updated adjudication queue endpoint to load subject relationship and populate computed fields Co-authored-by: teoat <68715844+teoat@users.noreply.github.com> --- backend/app/api/v1/endpoints/adjudication.py | 40 ++++++++++++--- backend/app/schemas/mens_rea.py | 7 +++ frontend/src/components/ui/Tabs.tsx | 51 +++++++++++++++++--- frontend/src/pages/AdjudicationQueue.tsx | 5 ++ 4 files changed, 90 insertions(+), 13 deletions(-) diff --git a/backend/app/api/v1/endpoints/adjudication.py b/backend/app/api/v1/endpoints/adjudication.py index 13f36c0..75ae38f 100644 --- a/backend/app/api/v1/endpoints/adjudication.py +++ b/backend/app/api/v1/endpoints/adjudication.py @@ -24,15 +24,20 @@ class AdjudicationDecision(BaseModel): async def get_adjudication_queue( page: int = 1, limit: int = 100, + status: Optional[str] = None, db: AsyncSession = Depends(deps.get_db), current_user=Depends(require_analyst), ): """ Fetch cases that require adjudication (status='flagged' or 'pending'). - Supports pagination. + Supports pagination and status filtering. """ - # Base query + # Base query - load subject relationship for name query = select(models.AnalysisResult).where(models.AnalysisResult.decision is None) + + # Apply status filter if provided + if status and status != 'all': + query = query.where(models.AnalysisResult.adjudication_status == status) # Get total count from sqlalchemy import func @@ -41,20 +46,43 @@ async def get_adjudication_queue( total_result = await db.execute(count_query) total = total_result.scalar_one() - # Apply pagination and fetch items + # Apply pagination and fetch items with relationships query = ( - query.options(selectinload(models.AnalysisResult.indicators)) + query.options( + selectinload(models.AnalysisResult.indicators), + selectinload(models.AnalysisResult.subject) + ) .offset((page - 1) * limit) .limit(limit) ) result = await db.execute(query) cases = result.scalars().all() - + + # Transform to include subject_name and triggered_rules import math + items = [] + for case in cases: + case_dict = { + "id": str(case.id), + "subject_id": str(case.subject_id), + "status": case.status, + "risk_score": float(case.risk_score), + "created_at": case.created_at, + "updated_at": case.updated_at, + "adjudication_status": case.adjudication_status or "pending", + "decision": case.decision, + "reviewer_notes": case.reviewer_notes, + "indicators": case.indicators, + # Extract subject name from encrypted PII + "subject_name": case.subject.encrypted_pii.get("name", "Unknown") if case.subject and case.subject.encrypted_pii else "Unknown", + # Extract triggered rules from indicators + "triggered_rules": [ind.type for ind in case.indicators] if case.indicators else [] + } + items.append(case_dict) return { - "items": cases, + "items": items, "total": total, "page": page, "pages": math.ceil(total / limit) if limit > 0 else 1, diff --git a/backend/app/schemas/mens_rea.py b/backend/app/schemas/mens_rea.py index f492695..d092274 100644 --- a/backend/app/schemas/mens_rea.py +++ b/backend/app/schemas/mens_rea.py @@ -37,6 +37,13 @@ class AnalysisResult(AnalysisResultBase): created_at: datetime updated_at: Optional[datetime] = None indicators: List[Indicator] = [] + adjudication_status: Optional[str] = "pending" + decision: Optional[str] = None + reviewer_notes: Optional[str] = None + + # Computed fields for frontend compatibility + subject_name: Optional[str] = None + triggered_rules: List[str] = [] class PaginatedAnalysisResult(BaseModel): diff --git a/frontend/src/components/ui/Tabs.tsx b/frontend/src/components/ui/Tabs.tsx index 886e35a..589b771 100644 --- a/frontend/src/components/ui/Tabs.tsx +++ b/frontend/src/components/ui/Tabs.tsx @@ -1,4 +1,4 @@ -import { FC, ReactNode } from 'react'; +import { FC, ReactNode, createContext, useContext } from 'react'; interface TabsProps { value: string; @@ -24,30 +24,54 @@ interface TabsContentProps { className?: string; } +interface TabsContextType { + activeTab: string; + setActiveTab: (value: string) => void; +} + +const TabsContext = createContext(undefined); + +const useTabsContext = () => { + const context = useContext(TabsContext); + if (!context) { + throw new Error('Tabs compound components must be used within a Tabs component'); + } + return context; +}; + export const Tabs: FC & { List: FC; Trigger: FC; Content: FC; } = ({ value, onValueChange, children, className }) => { return ( -
- {children} -
+ +
+ {children} +
+
); }; export const TabsList: FC = ({ children, className }) => { return ( -
+
{children}
); }; export const TabsTrigger: FC = ({ value, children, className }) => { + const { activeTab, setActiveTab } = useTabsContext(); + const isActive = activeTab === value; + return (