fix: improve document loading error handling and UI feedback for processing state

This commit is contained in:
Anish Sarkar 2026-04-01 20:31:45 +05:30
parent 9d6d818712
commit d7dd6db1b9
7 changed files with 69 additions and 24 deletions

View file

@ -219,7 +219,7 @@ export const DocumentNode = React.memo(function DocumentNode({
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-40" onClick={(e) => e.stopPropagation()}>
<DropdownMenuItem onClick={() => onPreview(doc)}>
<DropdownMenuItem onClick={() => onPreview(doc)} disabled={isProcessing}>
<Eye className="mr-2 h-4 w-4" />
Open
</DropdownMenuItem>
@ -259,7 +259,7 @@ export const DocumentNode = React.memo(function DocumentNode({
{contextMenuOpen && (
<ContextMenuContent className="w-40" onClick={(e) => e.stopPropagation()}>
<ContextMenuItem onClick={() => onPreview(doc)}>
<ContextMenuItem onClick={() => onPreview(doc)} disabled={isProcessing}>
<Eye className="mr-2 h-4 w-4" />
Open
</ContextMenuItem>

View file

@ -1,7 +1,7 @@
"use client";
import { useAtom } from "jotai";
import { CirclePlus } from "lucide-react";
import { Search } from "lucide-react";
import { useCallback, useMemo, useState } from "react";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
@ -250,8 +250,9 @@ export function FolderTreeView({
if (treeNodes.length === 0 && (activeTypes.length > 0 || searchQuery)) {
return (
<div className="flex flex-1 flex-col items-center justify-center gap-3 px-4 py-12 text-muted-foreground">
<CirclePlus className="h-10 w-10 rotate-45" />
<p className="text-sm">No matching documents</p>
<Search className="h-10 w-10" />
<p className="text-sm text-muted-foreground">No matching documents</p>
<p className="text-xs text-muted-foreground/70 mt-1">Try a different search term</p>
</div>
);
}

View file

@ -1,7 +1,7 @@
"use client";
import { useAtomValue, useSetAtom } from "jotai";
import { AlertCircle, XIcon } from "lucide-react";
import { FileQuestionMark, RefreshCw, XIcon } from "lucide-react";
import dynamic from "next/dynamic";
import { useCallback, useEffect, useRef, useState } from "react";
import { toast } from "sonner";
@ -200,10 +200,22 @@ export function EditorPanelContent({
<EditorPanelSkeleton />
) : error || !editorDoc ? (
<div className="flex flex-1 flex-col items-center justify-center gap-3 p-6 text-center">
<AlertCircle className="size-8 text-destructive" />
<div>
<p className="font-medium text-foreground">Failed to load document</p>
<p className="text-sm text-red-500 mt-1">{error || "An unknown error occurred"}</p>
{error?.toLowerCase().includes("still being processed") ? (
<div className="rounded-full bg-muted/50 p-3">
<RefreshCw className="size-6 text-muted-foreground animate-spin" />
</div>
) : (
<div className="rounded-full bg-muted/50 p-3">
<FileQuestionMark className="size-6 text-muted-foreground" />
</div>
)}
<div className="space-y-1 max-w-xs">
<p className="font-medium text-foreground">
{error?.toLowerCase().includes("still being processed")
? "Document is processing"
: "Document unavailable"}
</p>
<p className="text-sm text-muted-foreground">{error || "An unknown error occurred"}</p>
</div>
</div>
) : isEditableType ? (

View file

@ -1,6 +1,6 @@
"use client";
import { AlertCircle, Pencil } from "lucide-react";
import { FileQuestionMark, PenLine, RefreshCw } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";
import { toast } from "sonner";
import { PlateEditor } from "@/components/editor/plate-editor";
@ -160,15 +160,35 @@ export function DocumentTabContent({ documentId, searchSpaceId, title }: Documen
if (isLoading) return <DocumentSkeleton />;
if (error || !doc) {
const isProcessing = error?.toLowerCase().includes("still being processed");
return (
<div className="flex flex-1 flex-col items-center justify-center gap-3 p-6 text-center">
<AlertCircle className="size-10 text-destructive" />
<div>
<p className="font-medium text-foreground text-lg">Failed to load document</p>
<p className="text-sm text-muted-foreground mt-1">
<div className="flex flex-1 flex-col items-center justify-center gap-4 p-8 text-center">
<div className="rounded-full bg-muted/50 p-4">
{isProcessing ? (
<RefreshCw className="size-8 text-muted-foreground animate-spin" />
) : (
<FileQuestionMark className="size-8 text-muted-foreground" />
)}
</div>
<div className="space-y-1.5 max-w-sm">
<p className="font-semibold text-foreground text-lg">
{isProcessing ? "Document is processing" : "Document unavailable"}
</p>
<p className="text-sm text-muted-foreground">
{error || "An unknown error occurred"}
</p>
</div>
{!isProcessing && (
<Button
variant="outline"
size="sm"
className="mt-1 gap-1.5"
onClick={() => window.location.reload()}
>
<RefreshCw className="size-3.5" />
Retry
</Button>
)}
</div>
);
}
@ -229,7 +249,7 @@ export function DocumentTabContent({ documentId, searchSpaceId, title }: Documen
onClick={() => setIsEditing(true)}
className="gap-1.5"
>
<Pencil className="size-3.5" />
<PenLine className="size-3.5" />
Edit
</Button>
)}

View file

@ -1,7 +1,7 @@
"use client";
import { useQuery } from "@tanstack/react-query";
import { BookOpen, ChevronDown, ExternalLink, FileText, Hash, Sparkles, X } from "lucide-react";
import { BookOpen, ChevronDown, ExternalLink, FileQuestionMark, FileText, Hash, Sparkles, X } from "lucide-react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import { useTranslations } from "next-intl";
import type React from "react";
@ -392,12 +392,12 @@ export function SourceDetailPanel({
animate={{ opacity: 1, scale: 1 }}
className="flex flex-col items-center gap-4 text-center px-6"
>
<div className="w-20 h-20 rounded-full bg-destructive/10 flex items-center justify-center">
<X className="h-10 w-10 text-destructive" />
<div className="w-20 h-20 rounded-full bg-muted/50 flex items-center justify-center">
<FileQuestionMark className="h-10 w-10 text-muted-foreground" />
</div>
<div>
<p className="font-semibold text-destructive text-lg">
Failed to load document
<p className="font-semibold text-foreground text-lg">
Document unavailable
</p>
<p className="text-sm text-muted-foreground mt-2 max-w-md">
{documentByChunkFetchingError.message ||