Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion surfsense_web/components/editor-panel/editor-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
"use client";

import dynamic from "next/dynamic";
import { useAtomValue, useSetAtom } from "jotai";
import { AlertCircle, XIcon } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";
import { toast } from "sonner";
import { closeEditorPanelAtom, editorPanelAtom } from "@/atoms/editor/editor-panel.atom";
import { PlateEditor } from "@/components/editor/plate-editor";
import { MarkdownViewer } from "@/components/markdown-viewer";
import { Button } from "@/components/ui/button";
import { Drawer, DrawerContent, DrawerHandle, DrawerTitle } from "@/components/ui/drawer";
import { Skeleton } from "@/components/ui/skeleton";
import { useMediaQuery } from "@/hooks/use-media-query";
import { authenticatedFetch, getBearerToken, redirectToLogin } from "@/lib/auth-utils";

const PlateEditor = dynamic(
() => import("@/components/editor/plate-editor").then((m) => ({ default: m.PlateEditor })),
{ ssr: false, loading: () => <Skeleton className="h-64 w-full" /> }
);

interface EditorContent {
document_id: number;
title: string;
Expand Down
8 changes: 7 additions & 1 deletion surfsense_web/components/hitl-edit-panel/hitl-edit-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
"use client";

import dynamic from "next/dynamic";
import { format } from "date-fns";
import { TagInput, type Tag as TagType } from "emblor";
import { useAtomValue, useSetAtom } from "jotai";
import { CalendarIcon, XIcon } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import type { ExtraField } from "@/atoms/chat/hitl-edit-panel.atom";
import { closeHitlEditPanelAtom, hitlEditPanelAtom } from "@/atoms/chat/hitl-edit-panel.atom";
import { PlateEditor } from "@/components/editor/plate-editor";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Drawer, DrawerContent, DrawerHandle, DrawerTitle } from "@/components/ui/drawer";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Skeleton } from "@/components/ui/skeleton";
import { Textarea } from "@/components/ui/textarea";
import { useMediaQuery } from "@/hooks/use-media-query";

const PlateEditor = dynamic(
() => import("@/components/editor/plate-editor").then((m) => ({ default: m.PlateEditor })),
{ ssr: false, loading: () => <Skeleton className="h-64 w-full" /> }
);

function parseEmailsToTags(value: string): TagType[] {
if (!value.trim()) return [];
return value
Expand Down
25 changes: 7 additions & 18 deletions surfsense_web/components/layout/providers/LayoutDataProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,6 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
const resetTabs = useSetAtom(resetTabsAtom);
const removeChatTab = useSetAtom(removeChatTabAtom);

// State for handling new chat navigation when router is out of sync
const [pendingNewChat, setPendingNewChat] = useState(false);

// Key used to force-remount the page component (e.g. after deleting the active chat
// when the router is out of sync due to replaceState)
const [chatResetKey, setChatResetKey] = useState(0);
Expand Down Expand Up @@ -262,17 +259,6 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
const [isDeletingSearchSpace, setIsDeletingSearchSpace] = useState(false);
const [isLeavingSearchSpace, setIsLeavingSearchSpace] = useState(false);

// Effect to complete new chat navigation after router syncs
// This runs when handleNewChat detected an out-of-sync state and triggered a sync
useEffect(() => {
if (pendingNewChat && params?.chat_id) {
// Router is now synced (chat_id is in params), complete navigation to new-chat
resetCurrentThread();
router.push(`/dashboard/${searchSpaceId}/new-chat`);
setPendingNewChat(false);
}
}, [pendingNewChat, params?.chat_id, router, searchSpaceId, resetCurrentThread]);

// Reset transient slide-out panels and tabs when switching search spaces.
// Use a ref to skip the initial mount — only reset when the space actually changes.
const prevSearchSpaceIdRef = useRef(searchSpaceId);
Expand Down Expand Up @@ -555,14 +541,17 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
if (isOutOfSync) {
// First sync Next.js router by navigating to the current chat's actual URL
// This updates the router's internal state to match the browser URL
resetCurrentThread();
router.replace(`/dashboard/${searchSpaceId}/new-chat/${currentThreadState.id}`);
// Set flag to trigger navigation to new-chat after params update
setPendingNewChat(true);
// Allow router to sync, then navigate to fresh new-chat
setTimeout(() => {
router.push(`/dashboard/${searchSpaceId}/new-chat`);
}, 0);
} else {
// Normal navigation - router is in sync
router.push(`/dashboard/${searchSpaceId}/new-chat`);
}
}, [router, searchSpaceId, currentThreadState.id, params?.chat_id]);
}, [router, searchSpaceId, currentThreadState.id, params?.chat_id, resetCurrentThread]);

const handleChatSelect = useCallback(
(chat: ChatItem) => {
Expand Down Expand Up @@ -848,7 +837,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
</Button>
<Button
onClick={confirmRenameChat}
disabled={isRenamingChat || !newChatTitle.trim()}
disabled={isRenamingChat || !newChatTitle.trim() || newChatTitle.trim() === chatToRename?.name}
className="relative"
>
<span className={isRenamingChat ? "opacity-0" : ""}>
Expand Down
20 changes: 17 additions & 3 deletions surfsense_web/components/layout/ui/right-panel/RightPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import dynamic from "next/dynamic";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { PanelRight, PanelRightClose } from "lucide-react";
import { startTransition, useEffect } from "react";
Expand All @@ -8,13 +9,26 @@ import { closeReportPanelAtom, reportPanelAtom } from "@/atoms/chat/report-panel
import { documentsSidebarOpenAtom } from "@/atoms/documents/ui.atoms";
import { closeEditorPanelAtom, editorPanelAtom } from "@/atoms/editor/editor-panel.atom";
import { rightPanelCollapsedAtom, rightPanelTabAtom } from "@/atoms/layout/right-panel.atom";
import { EditorPanelContent } from "@/components/editor-panel/editor-panel";
import { HitlEditPanelContent } from "@/components/hitl-edit-panel/hitl-edit-panel";
import { ReportPanelContent } from "@/components/report-panel/report-panel";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { DocumentsSidebar } from "../sidebar";

const EditorPanelContent = dynamic(
() => import("@/components/editor-panel/editor-panel").then((m) => ({ default: m.EditorPanelContent })),
{ ssr: false, loading: () => <Skeleton className="h-96 w-full" /> }
);

const HitlEditPanelContent = dynamic(
() => import("@/components/hitl-edit-panel/hitl-edit-panel").then((m) => ({ default: m.HitlEditPanelContent })),
{ ssr: false, loading: () => <Skeleton className="h-96 w-full" /> }
);

const ReportPanelContent = dynamic(
() => import("@/components/report-panel/report-panel").then((m) => ({ default: m.ReportPanelContent })),
{ ssr: false, loading: () => <Skeleton className="h-96 w-full" /> }
);

interface RightPanelProps {
documentsPanel?: {
open: boolean;
Expand Down
8 changes: 7 additions & 1 deletion surfsense_web/components/report-panel/report-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
"use client";

import dynamic from "next/dynamic";
import { useAtomValue, useSetAtom } from "jotai";
import { ChevronDownIcon, XIcon } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";
import { toast } from "sonner";
import { z } from "zod";
import { currentThreadAtom } from "@/atoms/chat/current-thread.atom";
import { closeReportPanelAtom, reportPanelAtom } from "@/atoms/chat/report-panel.atom";
import { PlateEditor } from "@/components/editor/plate-editor";
import { MarkdownViewer } from "@/components/markdown-viewer";
import { EXPORT_FILE_EXTENSIONS, ExportDropdownItems } from "@/components/shared/ExportMenuItems";
import { Button } from "@/components/ui/button";
import { Drawer, DrawerContent, DrawerHandle, DrawerTitle } from "@/components/ui/drawer";
import { Skeleton } from "@/components/ui/skeleton";
import {
DropdownMenu,
DropdownMenuContent,
Expand All @@ -22,6 +23,11 @@ import { useMediaQuery } from "@/hooks/use-media-query";
import { baseApiService } from "@/lib/apis/base-api.service";
import { authenticatedFetch } from "@/lib/auth-utils";

const PlateEditor = dynamic(
() => import("@/components/editor/plate-editor").then((m) => ({ default: m.PlateEditor })),
{ ssr: false, loading: () => <Skeleton className="h-64 w-full" /> }
);

/**
* Zod schema for a single version entry
*/
Expand Down
Loading