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.
-
-
-
-
-
- {t("general_name_label")}
-
-
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.
+
+
-
-
- {t("general_description_label")}{" "}
- ({tCommon("optional")})
-
-
setDescription(e.target.value)}
- className="text-sm md:text-base h-9 md:h-10"
- />
-
- {t("general_description_description")}
-
-
-
-
+ {/* Search Space Details Card */}
+
- );
+
+
+ {t("general_description_label")}{" "}
+
+ ({tCommon("optional")})
+
+
+
setDescription(e.target.value)}
+ className="text-sm md:text-base h-9 md:h-10"
+ />
+
+ {t("general_description_description")}
+
+
+
+
+
+ {/* Action Buttons */}
+
+
+ {saving ? : null}
+ {saving ? t("general_saving") : t("general_save")}
+
+
+
+
+ );
}
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.
-
-
-
-
-
- Your Instructions
-
-
+
+
+
+ System instructions apply to all AI interactions in this search space.
+ They guide how the AI responds, its tone, focus areas, and behavior
+ patterns.
+
+
- {customInstructions.trim().length === 0 && (
-
-
-
- No system instructions are currently set. The AI will use default behavior.
-
-
- )}
-
-
+ {/* System Instructions Card */}
+
- );
+ {customInstructions.trim().length === 0 && (
+
+
+
+ No system instructions are currently set. The AI will use
+ default behavior.
+
+
+ )}
+
+
+
+ {/* Action Buttons */}
+
+
+ {saving ? : null}
+ {saving ? "Saving" : "Save Instructions"}
+
+
+
+
+ );
}