diff --git a/surfsense_web/components/settings/general-settings-manager.tsx b/surfsense_web/components/settings/general-settings-manager.tsx index a9482001d..8a847b629 100644 --- a/surfsense_web/components/settings/general-settings-manager.tsx +++ b/surfsense_web/components/settings/general-settings-manager.tsx @@ -9,160 +9,190 @@ import { toast } from "sonner"; import { updateSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Skeleton } from "@/components/ui/skeleton"; import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; +import { Spinner } from "../ui/spinner"; interface GeneralSettingsManagerProps { - searchSpaceId: number; + searchSpaceId: number; } -export function GeneralSettingsManager({ searchSpaceId }: GeneralSettingsManagerProps) { - const t = useTranslations("searchSpaceSettings"); - const tCommon = useTranslations("common"); - const { - data: searchSpace, - isLoading: loading, - refetch: fetchSearchSpace, - } = useQuery({ - queryKey: cacheKeys.searchSpaces.detail(searchSpaceId.toString()), - queryFn: () => searchSpacesApiService.getSearchSpace({ id: searchSpaceId }), - enabled: !!searchSpaceId, - }); +export function GeneralSettingsManager({ + searchSpaceId, +}: GeneralSettingsManagerProps) { + const t = useTranslations("searchSpaceSettings"); + const tCommon = useTranslations("common"); + const { + data: searchSpace, + isLoading: loading, + refetch: fetchSearchSpace, + } = useQuery({ + queryKey: cacheKeys.searchSpaces.detail(searchSpaceId.toString()), + queryFn: () => searchSpacesApiService.getSearchSpace({ id: searchSpaceId }), + enabled: !!searchSpaceId, + }); - const { mutateAsync: updateSearchSpace } = useAtomValue(updateSearchSpaceMutationAtom); + const { mutateAsync: updateSearchSpace } = useAtomValue( + updateSearchSpaceMutationAtom, + ); - const [name, setName] = useState(""); - const [description, setDescription] = useState(""); - const [saving, setSaving] = useState(false); - const [hasChanges, setHasChanges] = useState(false); + const [name, setName] = useState(""); + const [description, setDescription] = useState(""); + const [saving, setSaving] = useState(false); + const [hasChanges, setHasChanges] = useState(false); - // Initialize state from fetched search space - useEffect(() => { - if (searchSpace) { - setName(searchSpace.name || ""); - setDescription(searchSpace.description || ""); - setHasChanges(false); - } - }, [searchSpace]); + // Initialize state from fetched search space + useEffect(() => { + if (searchSpace) { + setName(searchSpace.name || ""); + setDescription(searchSpace.description || ""); + setHasChanges(false); + } + }, [searchSpace]); - // Track changes - useEffect(() => { - if (searchSpace) { - const currentName = searchSpace.name || ""; - const currentDescription = searchSpace.description || ""; - const changed = currentName !== name || currentDescription !== description; - setHasChanges(changed); - } - }, [searchSpace, name, description]); + // Track changes + useEffect(() => { + if (searchSpace) { + const currentName = searchSpace.name || ""; + const currentDescription = searchSpace.description || ""; + const changed = + currentName !== name || currentDescription !== description; + setHasChanges(changed); + } + }, [searchSpace, name, description]); - const handleSave = async () => { - try { - setSaving(true); + const handleSave = async () => { + try { + setSaving(true); - await updateSearchSpace({ - id: searchSpaceId, - data: { - name: name.trim(), - description: description.trim() || undefined, - }, - }); + await updateSearchSpace({ + id: searchSpaceId, + data: { + name: name.trim(), + description: description.trim() || undefined, + }, + }); - setHasChanges(false); - await fetchSearchSpace(); - } catch (error: any) { - console.error("Error saving search space details:", error); - toast.error(error.message || "Failed to save search space details"); - } finally { - setSaving(false); - } - }; + setHasChanges(false); + await fetchSearchSpace(); + } catch (error: any) { + console.error("Error saving search space details:", error); + toast.error(error.message || "Failed to save search space details"); + } finally { + setSaving(false); + } + }; - if (loading) { - return ( -
- - - - - - - - - - -
- ); - } + const onSubmit = (e: React.FormEvent) => { + e.preventDefault(); + handleSave(); + }; - return ( -
- - - - Update your search space name and description. These details help identify and organize - your workspace. - - + if (loading) { + return ( +
+ + + + + + + + + + +
+ ); + } - {/* Search Space Details Card */} - - - Search Space Details - - Manage the basic information for this search space. - - - -
- - setName(e.target.value)} - className="text-sm md:text-base h-9 md:h-10" - /> -

- {t("general_name_description")} -

-
+ return ( +
+ + + + Update your search space name and description. These details help + identify and organize your workspace. + + -
- - setDescription(e.target.value)} - className="text-sm md:text-base h-9 md:h-10" - /> -

- {t("general_description_description")} -

-
- - + {/* Search Space Details Card */} +
+ + + + Search Space Details + + + Manage the basic information for this search space. + + + +
+ + setName(e.target.value)} + className="text-sm md:text-base h-9 md:h-10" + /> +

+ {t("general_name_description")} +

+
- {/* Action Buttons */} -
- -
-
- ); +
+ + setDescription(e.target.value)} + className="text-sm md:text-base h-9 md:h-10" + /> +

+ {t("general_description_description")} +

+
+
+
+ + {/* Action Buttons */} +
+ +
+ +
+ ); } diff --git a/surfsense_web/components/settings/prompt-config-manager.tsx b/surfsense_web/components/settings/prompt-config-manager.tsx index b9c9c2fc8..dc3a15a7d 100644 --- a/surfsense_web/components/settings/prompt-config-manager.tsx +++ b/surfsense_web/components/settings/prompt-config-manager.tsx @@ -6,187 +6,213 @@ import { useEffect, useState } from "react"; import { toast } from "sonner"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; import { Label } from "@/components/ui/label"; import { Skeleton } from "@/components/ui/skeleton"; import { Textarea } from "@/components/ui/textarea"; import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { authenticatedFetch } from "@/lib/auth-utils"; import { cacheKeys } from "@/lib/query-client/cache-keys"; +import { Spinner } from "../ui/spinner"; interface PromptConfigManagerProps { - searchSpaceId: number; + searchSpaceId: number; } -export function PromptConfigManager({ searchSpaceId }: PromptConfigManagerProps) { - const { - data: searchSpace, - isLoading: loading, - refetch: fetchSearchSpace, - } = useQuery({ - queryKey: cacheKeys.searchSpaces.detail(searchSpaceId.toString()), - queryFn: () => searchSpacesApiService.getSearchSpace({ id: searchSpaceId }), - enabled: !!searchSpaceId, - }); +export function PromptConfigManager({ + searchSpaceId, +}: PromptConfigManagerProps) { + const { + data: searchSpace, + isLoading: loading, + refetch: fetchSearchSpace, + } = useQuery({ + queryKey: cacheKeys.searchSpaces.detail(searchSpaceId.toString()), + queryFn: () => searchSpacesApiService.getSearchSpace({ id: searchSpaceId }), + enabled: !!searchSpaceId, + }); - const [customInstructions, setCustomInstructions] = useState(""); - const [saving, setSaving] = useState(false); - const [hasChanges, setHasChanges] = useState(false); + const [customInstructions, setCustomInstructions] = useState(""); + const [saving, setSaving] = useState(false); + const [hasChanges, setHasChanges] = useState(false); - // Initialize state from fetched search space - useEffect(() => { - if (searchSpace) { - setCustomInstructions(searchSpace.qna_custom_instructions || ""); - setHasChanges(false); - } - }, [searchSpace]); + // Initialize state from fetched search space + useEffect(() => { + if (searchSpace) { + setCustomInstructions(searchSpace.qna_custom_instructions || ""); + setHasChanges(false); + } + }, [searchSpace]); - // Track changes - useEffect(() => { - if (searchSpace) { - const currentCustom = searchSpace.qna_custom_instructions || ""; - const changed = currentCustom !== customInstructions; - setHasChanges(changed); - } - }, [searchSpace, customInstructions]); + // Track changes + useEffect(() => { + if (searchSpace) { + const currentCustom = searchSpace.qna_custom_instructions || ""; + const changed = currentCustom !== customInstructions; + setHasChanges(changed); + } + }, [searchSpace, customInstructions]); - const handleSave = async () => { - try { - setSaving(true); + const handleSave = async () => { + try { + setSaving(true); - const payload = { - qna_custom_instructions: customInstructions.trim() || "", - }; + const payload = { + qna_custom_instructions: customInstructions.trim() || "", + }; - const response = await authenticatedFetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${searchSpaceId}`, - { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - } - ); + const response = await authenticatedFetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${searchSpaceId}`, + { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }, + ); - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.detail || "Failed to save system instructions"); - } + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error( + errorData.detail || "Failed to save system instructions", + ); + } - toast.success("System instructions saved successfully"); - setHasChanges(false); - await fetchSearchSpace(); - } catch (error: any) { - console.error("Error saving system instructions:", error); - toast.error(error.message || "Failed to save system instructions"); - } finally { - setSaving(false); - } - }; + toast.success("System instructions saved successfully"); + setHasChanges(false); + await fetchSearchSpace(); + } catch (error: any) { + console.error("Error saving system instructions:", error); + toast.error(error.message || "Failed to save system instructions"); + } finally { + setSaving(false); + } + }; - if (loading) { - return ( -
- - - - - - - - - - -
- ); - } + const onSubmit = (e: React.FormEvent) => { + e.preventDefault(); + handleSave(); + }; - return ( -
- {/* Work in Progress Notice */} - - - - Work in Progress: This functionality is currently - under development and not yet connected to the backend. Your instructions will be saved - but won't affect AI behavior until the feature is fully implemented. - - + if (loading) { + return ( +
+ + + + + + + + + + +
+ ); + } - - - - System instructions apply to all AI interactions in this search space. They guide how the - AI responds, its tone, focus areas, and behavior patterns. - - + return ( +
+ {/* Work in Progress Notice */} + + + + Work in Progress: This + functionality is currently under development and not yet connected to + the backend. Your instructions will be saved but won't affect AI + behavior until the feature is fully implemented. + + - {/* System Instructions Card */} - - - Custom System Instructions - - Provide specific guidelines for how you want the AI to respond. These instructions will - be applied to all answers in this search space. - - - -
- -