From 2ed8a2af15da5ca75438ecc4a86d8da216e50740 Mon Sep 17 00:00:00 2001 From: chihlasm Date: Fri, 20 Mar 2026 04:06:41 +0000 Subject: [PATCH] =?UTF-8?q?fix:=206=20integration=20audit=20fixes=20?= =?UTF-8?q?=E2=80=94=20ticket=20filter,=20admin=20nav,=20FK=20scope,=20deb?= =?UTF-8?q?ounce,=20error=20messages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix AISession.ticket_id → psa_ticket_id in list_sessions filter query - Add Gallery nav item (LayoutGrid icon) to AdminSidebar navItems array - Remove ForeignKey from FileUpload.session_id (Python model) + migration b8d2f4a6c091 to drop DB constraint, allowing column to reference either session type - Add 400ms debounce on AI session search input in SessionHistoryPage (aiSearchInput state + useRef timeout pattern) - Show friendly 503 error message in RichTextInput upload error handler (both initial upload and retry paths) - Add overflow-x-auto to FlowPilotAnalyticsPage tab bar container Co-Authored-By: Claude Opus 4.6 (1M context) --- ...4a6c091_drop_file_uploads_session_id_fk.py | 34 +++++++++++++++++++ backend/app/api/endpoints/ai_sessions.py | 2 +- backend/app/models/file_upload.py | 2 +- .../src/components/admin/AdminSidebar.tsx | 2 ++ .../src/components/common/RichTextInput.tsx | 10 ++++-- frontend/src/pages/FlowPilotAnalyticsPage.tsx | 2 +- frontend/src/pages/SessionHistoryPage.tsx | 29 ++++++++++++---- 7 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 backend/alembic/versions/b8d2f4a6c091_drop_file_uploads_session_id_fk.py diff --git a/backend/alembic/versions/b8d2f4a6c091_drop_file_uploads_session_id_fk.py b/backend/alembic/versions/b8d2f4a6c091_drop_file_uploads_session_id_fk.py new file mode 100644 index 00000000..82b8c674 --- /dev/null +++ b/backend/alembic/versions/b8d2f4a6c091_drop_file_uploads_session_id_fk.py @@ -0,0 +1,34 @@ +"""drop file_uploads session_id foreign key constraint + +Revision ID: b8d2f4a6c091 +Revises: a7c9e3b1f402 +Create Date: 2026-03-20 00:00:00.000000 + +The session_id column on file_uploads previously referenced ai_sessions.id. +Removing the FK allows the column to reference either AI sessions or regular +sessions without a constraint violation, while keeping the index for query +performance. +""" +from typing import Sequence, Union + +from alembic import op + + +# revision identifiers, used by Alembic. +revision: str = 'b8d2f4a6c091' +down_revision: Union[str, None] = 'a7c9e3b1f402' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.drop_constraint('file_uploads_session_id_fkey', 'file_uploads', type_='foreignkey') + + +def downgrade() -> None: + op.create_foreign_key( + 'file_uploads_session_id_fkey', + 'file_uploads', 'ai_sessions', + ['session_id'], ['id'], + ondelete='SET NULL', + ) diff --git a/backend/app/api/endpoints/ai_sessions.py b/backend/app/api/endpoints/ai_sessions.py index d2ee73ca..3fb43e15 100644 --- a/backend/app/api/endpoints/ai_sessions.py +++ b/backend/app/api/endpoints/ai_sessions.py @@ -562,7 +562,7 @@ async def list_sessions( if confidence_tier: query = query.where(AISession.confidence_tier == confidence_tier) if ticket_id: - query = query.where(AISession.ticket_id == ticket_id) + query = query.where(AISession.psa_ticket_id == ticket_id) if date_from: query = query.where(AISession.created_at >= date_from) if date_to: diff --git a/backend/app/models/file_upload.py b/backend/app/models/file_upload.py index 4c447326..6846b1a3 100644 --- a/backend/app/models/file_upload.py +++ b/backend/app/models/file_upload.py @@ -21,7 +21,7 @@ class FileUpload(Base): UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=False ) session_id: Mapped[Optional[uuid.UUID]] = mapped_column( - UUID(as_uuid=True), ForeignKey("ai_sessions.id", ondelete="SET NULL"), nullable=True, index=True + UUID(as_uuid=True), nullable=True, index=True ) filename: Mapped[str] = mapped_column(String(255), nullable=False) content_type: Mapped[str] = mapped_column(String(100), nullable=False) diff --git a/frontend/src/components/admin/AdminSidebar.tsx b/frontend/src/components/admin/AdminSidebar.tsx index ca77d73c..a5a23d08 100644 --- a/frontend/src/components/admin/AdminSidebar.tsx +++ b/frontend/src/components/admin/AdminSidebar.tsx @@ -10,6 +10,7 @@ import { FolderTree, ClipboardList, MessageSquareText, + LayoutGrid, ArrowLeft, } from 'lucide-react' import { cn } from '@/lib/utils' @@ -25,6 +26,7 @@ const navItems = [ { path: '/admin/categories', label: 'Categories', icon: FolderTree }, { path: '/admin/survey-invites', label: 'Survey Invites', icon: ClipboardList }, { path: '/admin/survey-responses', label: 'Survey Responses', icon: MessageSquareText }, + { path: '/admin/gallery', label: 'Gallery', icon: LayoutGrid }, ] interface AdminSidebarProps { diff --git a/frontend/src/components/common/RichTextInput.tsx b/frontend/src/components/common/RichTextInput.tsx index 9d7a23dc..914726cd 100644 --- a/frontend/src/components/common/RichTextInput.tsx +++ b/frontend/src/components/common/RichTextInput.tsx @@ -73,10 +73,13 @@ export function RichTextInput({ }) }) .catch((err) => { + const errorMsg = err?.response?.status === 503 + ? 'File uploads not available — contact your administrator' + : err?.message || 'Upload failed' setPendingUploads((prev) => prev.map((u) => u.id === upload.id - ? { ...u, status: 'error' as const, error: err?.message || 'Upload failed' } + ? { ...u, status: 'error' as const, error: errorMsg } : u ) ) @@ -196,10 +199,13 @@ export function RichTextInput({ }) }) .catch((err) => { + const errorMsg = err?.response?.status === 503 + ? 'File uploads not available — contact your administrator' + : err?.message || 'Upload failed' setPendingUploads((prev) => prev.map((u) => u.id === uploadId - ? { ...u, status: 'error' as const, error: err?.message || 'Upload failed' } + ? { ...u, status: 'error' as const, error: errorMsg } : u ) ) diff --git a/frontend/src/pages/FlowPilotAnalyticsPage.tsx b/frontend/src/pages/FlowPilotAnalyticsPage.tsx index f1bfe2af..fda132f2 100644 --- a/frontend/src/pages/FlowPilotAnalyticsPage.tsx +++ b/frontend/src/pages/FlowPilotAnalyticsPage.tsx @@ -189,7 +189,7 @@ export default function FlowPilotAnalyticsPage() { {/* Tab bar */} -
+
{TABS.map((tab) => (
{/* Clear filters */} - {(aiFilters.q || aiFilters.problem_domain || aiFilters.confidence_tier || aiFilters.date_from || aiFilters.date_to) && ( + {(aiSearchInput || aiFilters.q || aiFilters.problem_domain || aiFilters.confidence_tier || aiFilters.date_from || aiFilters.date_to) && (
) : aiSessions.length === 0 ? ( - (aiFilters.q || aiFilters.problem_domain || aiFilters.confidence_tier || aiFilters.date_from || aiFilters.date_to) ? ( + (aiSearchInput || aiFilters.q || aiFilters.problem_domain || aiFilters.confidence_tier || aiFilters.date_from || aiFilters.date_to) ? ( setAiFilters({ q: '', problem_domain: '', confidence_tier: '', date_from: '', date_to: '' })} + onClick={() => { + setAiSearchInput('') + setAiFilters({ q: '', problem_domain: '', confidence_tier: '', date_from: '', date_to: '' }) + }} className="text-foreground hover:underline text-sm" > Clear all filters