mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 00:36:31 +02:00
refactor(editor): replace Loader2 with Spinner component and enhance save button visibility
This commit is contained in:
parent
06b509213c
commit
0381632bc2
2 changed files with 76 additions and 12 deletions
|
|
@ -7,7 +7,6 @@ import {
|
|||
Download,
|
||||
FileQuestionMark,
|
||||
FileText,
|
||||
Loader2,
|
||||
Pencil,
|
||||
RefreshCw,
|
||||
XIcon,
|
||||
|
|
@ -22,6 +21,7 @@ import { MarkdownViewer } from "@/components/markdown-viewer";
|
|||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Drawer, DrawerContent, DrawerHandle, DrawerTitle } from "@/components/ui/drawer";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import { useMediaQuery } from "@/hooks/use-media-query";
|
||||
import { useElectronAPI } from "@/hooks/use-platform";
|
||||
import { authenticatedFetch, getBearerToken, redirectToLogin } from "@/lib/auth-utils";
|
||||
|
|
@ -346,8 +346,8 @@ export function EditorPanelContent({
|
|||
}}
|
||||
disabled={saving || !hasUnsavedChanges}
|
||||
>
|
||||
<span className={saving ? "invisible" : ""}>Save</span>
|
||||
{saving && <Loader2 className="absolute size-3 animate-spin" />}
|
||||
<span className={saving ? "opacity-0" : ""}>Save</span>
|
||||
{saving && <Spinner size="xs" className="absolute" />}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
|
|
@ -416,8 +416,8 @@ export function EditorPanelContent({
|
|||
}}
|
||||
disabled={saving || !hasUnsavedChanges}
|
||||
>
|
||||
<span className={saving ? "invisible" : ""}>Save</span>
|
||||
{saving && <Loader2 className="absolute size-3 animate-spin" />}
|
||||
<span className={saving ? "opacity-0" : ""}>Save</span>
|
||||
{saving && <Spinner size="xs" className="absolute" />}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
|
|
@ -534,7 +534,7 @@ export function EditorPanelContent({
|
|||
}}
|
||||
>
|
||||
{downloading ? (
|
||||
<Loader2 className="size-3.5 animate-spin" />
|
||||
<Spinner size="xs" />
|
||||
) : (
|
||||
<Download className="size-3.5" />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { useAtomValue, useSetAtom } from "jotai";
|
||||
import { ChevronDownIcon, XIcon } from "lucide-react";
|
||||
import { ChevronDownIcon, Pencil, XIcon } from "lucide-react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
|
@ -125,6 +125,7 @@ export function ReportPanelContent({
|
|||
|
||||
// Editor state — tracks the latest markdown from the Plate editor
|
||||
const [editedMarkdown, setEditedMarkdown] = useState<string | null>(null);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
|
||||
// Read-only when public (shareToken) OR shared (SEARCH_SPACE visibility)
|
||||
const currentThreadState = useAtomValue(currentThreadAtom);
|
||||
|
|
@ -188,6 +189,7 @@ export function ReportPanelContent({
|
|||
// Reset edited markdown when switching versions or reports
|
||||
useEffect(() => {
|
||||
setEditedMarkdown(null);
|
||||
setIsEditing(false);
|
||||
}, [activeReportId]);
|
||||
|
||||
// Copy markdown content (uses latest editor content)
|
||||
|
|
@ -257,7 +259,7 @@ export function ReportPanelContent({
|
|||
|
||||
// Save edited report content
|
||||
const handleSave = useCallback(async () => {
|
||||
if (!currentMarkdown || !activeReportId) return;
|
||||
if (!currentMarkdown || !activeReportId) return false;
|
||||
setSaving(true);
|
||||
try {
|
||||
const response = await authenticatedFetch(
|
||||
|
|
@ -278,9 +280,11 @@ export function ReportPanelContent({
|
|||
setReportContent((prev) => (prev ? { ...prev, content: currentMarkdown } : prev));
|
||||
setEditedMarkdown(null);
|
||||
toast.success("Report saved successfully");
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error("Error saving report:", err);
|
||||
toast.error(err instanceof Error ? err.message : "Failed to save report");
|
||||
return false;
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
|
|
@ -289,6 +293,14 @@ export function ReportPanelContent({
|
|||
const activeVersionIndex = versions.findIndex((v) => v.id === activeReportId);
|
||||
const isPublic = !!shareToken;
|
||||
const btnBg = isPublic ? "bg-main-panel" : "bg-sidebar";
|
||||
const isResume = reportContent?.content_type === "typst";
|
||||
const showReportEditingTier = !isResume;
|
||||
const hasUnsavedChanges = editedMarkdown !== null;
|
||||
|
||||
const handleCancelEditing = useCallback(() => {
|
||||
setEditedMarkdown(null);
|
||||
setIsEditing(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -383,6 +395,58 @@ export function ReportPanelContent({
|
|||
)}
|
||||
</div>
|
||||
|
||||
{showReportEditingTier && (
|
||||
<div className="flex h-10 items-center justify-between gap-2 border-t border-b px-4 shrink-0">
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm text-muted-foreground">
|
||||
{reportContent?.title || title}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
{!isReadOnly &&
|
||||
(isEditing ? (
|
||||
<>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-6 px-2 text-xs"
|
||||
onClick={handleCancelEditing}
|
||||
disabled={saving}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="relative h-6 w-[56px] px-0 text-xs"
|
||||
onClick={async () => {
|
||||
const saveSucceeded = await handleSave();
|
||||
if (saveSucceeded) setIsEditing(false);
|
||||
}}
|
||||
disabled={saving || !hasUnsavedChanges}
|
||||
>
|
||||
<span className={saving ? "opacity-0" : ""}>Save</span>
|
||||
{saving && <Spinner size="xs" className="absolute" />}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="size-6"
|
||||
onClick={() => {
|
||||
setEditedMarkdown(null);
|
||||
setIsEditing(true);
|
||||
}}
|
||||
>
|
||||
<Pencil className="size-3.5" />
|
||||
<span className="sr-only">Edit report</span>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Report content — skeleton/error/viewer/editor shown only in this area */}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
{isLoading ? (
|
||||
|
|
@ -406,15 +470,15 @@ export function ReportPanelContent({
|
|||
</div>
|
||||
) : (
|
||||
<PlateEditor
|
||||
key={`report-${activeReportId}-${isEditing ? "editing" : "viewing"}`}
|
||||
preset="full"
|
||||
markdown={reportContent.content}
|
||||
onMarkdownChange={setEditedMarkdown}
|
||||
readOnly={false}
|
||||
readOnly={!isEditing}
|
||||
placeholder="Report content..."
|
||||
editorVariant="default"
|
||||
onSave={handleSave}
|
||||
hasUnsavedChanges={editedMarkdown !== null}
|
||||
isSaving={saving}
|
||||
allowModeToggle={false}
|
||||
defaultEditing={isEditing}
|
||||
className="[&_[role=toolbar]]:!bg-sidebar"
|
||||
/>
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue