mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-06 20:15:17 +02:00
refactor: integrate global loading effect into onboarding page and streamline LLMConfigForm usage for improved user experience
This commit is contained in:
parent
32ff5f085c
commit
ba926bbcc9
4 changed files with 67 additions and 144 deletions
|
|
@ -16,6 +16,7 @@ import { Logo } from "@/components/Logo";
|
||||||
import { LLMConfigForm, type LLMConfigFormData } from "@/components/shared/llm-config-form";
|
import { LLMConfigForm, type LLMConfigFormData } from "@/components/shared/llm-config-form";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Spinner } from "@/components/ui/spinner";
|
import { Spinner } from "@/components/ui/spinner";
|
||||||
|
import { useGlobalLoadingEffect } from "@/hooks/use-global-loading";
|
||||||
import { getBearerToken, redirectToLogin } from "@/lib/auth-utils";
|
import { getBearerToken, redirectToLogin } from "@/lib/auth-utils";
|
||||||
|
|
||||||
export default function OnboardPage() {
|
export default function OnboardPage() {
|
||||||
|
|
@ -138,17 +139,11 @@ export default function OnboardPage() {
|
||||||
|
|
||||||
const isSubmitting = isCreating || isUpdatingPreferences;
|
const isSubmitting = isCreating || isUpdatingPreferences;
|
||||||
|
|
||||||
if (globalConfigsLoading || preferencesLoading || isAutoConfiguring) {
|
const isLoading = globalConfigsLoading || preferencesLoading || isAutoConfiguring;
|
||||||
return (
|
useGlobalLoadingEffect(isLoading);
|
||||||
<div className="h-screen flex items-center justify-center bg-background dark:bg-neutral-900">
|
|
||||||
<div className="text-center space-y-3">
|
if (isLoading) {
|
||||||
<Spinner size="lg" className="mx-auto text-muted-foreground" />
|
return null;
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{isAutoConfiguring ? "Setting up your AI..." : "Loading..."}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (globalConfigs.length > 0 && !isAutoConfiguring) {
|
if (globalConfigs.length > 0 && !isAutoConfiguring) {
|
||||||
|
|
@ -171,15 +166,13 @@ export default function OnboardPage() {
|
||||||
|
|
||||||
{/* Form card */}
|
{/* Form card */}
|
||||||
<div className="rounded-xl border bg-background dark:bg-neutral-900 flex-1 min-h-0 overflow-y-auto px-6 py-6">
|
<div className="rounded-xl border bg-background dark:bg-neutral-900 flex-1 min-h-0 overflow-y-auto px-6 py-6">
|
||||||
<LLMConfigForm
|
<LLMConfigForm
|
||||||
searchSpaceId={searchSpaceId}
|
searchSpaceId={searchSpaceId}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
isSubmitting={isSubmitting}
|
mode="create"
|
||||||
mode="create"
|
showAdvanced={true}
|
||||||
showAdvanced={true}
|
formId="onboard-config-form"
|
||||||
formId="onboard-config-form"
|
initialData={{
|
||||||
hideActions
|
|
||||||
initialData={{
|
|
||||||
citations_enabled: true,
|
citations_enabled: true,
|
||||||
use_default_system_instructions: true,
|
use_default_system_instructions: true,
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -498,7 +498,7 @@ export function ModelSelector({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Plus className="size-4 text-primary" />
|
<Plus className="size-4 text-primary" />
|
||||||
<span className="text-sm font-medium">Add New Configuration</span>
|
<span className="text-sm font-medium">Add LLM Model</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</CommandList>
|
</CommandList>
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,8 @@
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useAtomValue } from "jotai";
|
import { useAtomValue } from "jotai";
|
||||||
import { Check, ChevronDown, ChevronsUpDown } from "lucide-react";
|
import { Check, ChevronDown, ChevronsUpDown } from "lucide-react";
|
||||||
import { AnimatePresence, motion } from "motion/react";
|
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm, type Resolver } from "react-hook-form";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import {
|
import {
|
||||||
defaultSystemInstructionsAtom,
|
defaultSystemInstructionsAtom,
|
||||||
|
|
@ -41,7 +40,6 @@ import {
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Spinner } from "@/components/ui/spinner";
|
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { LLM_PROVIDERS } from "@/contracts/enums/llm-providers";
|
import { LLM_PROVIDERS } from "@/contracts/enums/llm-providers";
|
||||||
|
|
@ -73,28 +71,18 @@ interface LLMConfigFormProps {
|
||||||
initialData?: Partial<LLMConfigFormData>;
|
initialData?: Partial<LLMConfigFormData>;
|
||||||
searchSpaceId: number;
|
searchSpaceId: number;
|
||||||
onSubmit: (data: LLMConfigFormData) => Promise<void>;
|
onSubmit: (data: LLMConfigFormData) => Promise<void>;
|
||||||
onCancel?: () => void;
|
|
||||||
isSubmitting?: boolean;
|
|
||||||
mode?: "create" | "edit";
|
mode?: "create" | "edit";
|
||||||
submitLabel?: string;
|
|
||||||
showAdvanced?: boolean;
|
showAdvanced?: boolean;
|
||||||
compact?: boolean;
|
|
||||||
formId?: string;
|
formId?: string;
|
||||||
hideActions?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LLMConfigForm({
|
export function LLMConfigForm({
|
||||||
initialData,
|
initialData,
|
||||||
searchSpaceId,
|
searchSpaceId,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onCancel,
|
|
||||||
isSubmitting = false,
|
|
||||||
mode = "create",
|
mode = "create",
|
||||||
submitLabel,
|
|
||||||
showAdvanced = true,
|
showAdvanced = true,
|
||||||
compact = false,
|
|
||||||
formId,
|
formId,
|
||||||
hideActions = false,
|
|
||||||
}: LLMConfigFormProps) {
|
}: LLMConfigFormProps) {
|
||||||
const { data: defaultInstructions, isSuccess: defaultInstructionsLoaded } = useAtomValue(
|
const { data: defaultInstructions, isSuccess: defaultInstructionsLoaded } = useAtomValue(
|
||||||
defaultSystemInstructionsAtom
|
defaultSystemInstructionsAtom
|
||||||
|
|
@ -105,8 +93,7 @@ export function LLMConfigForm({
|
||||||
const [systemInstructionsOpen, setSystemInstructionsOpen] = useState(false);
|
const [systemInstructionsOpen, setSystemInstructionsOpen] = useState(false);
|
||||||
|
|
||||||
const form = useForm<FormValues>({
|
const form = useForm<FormValues>({
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
resolver: zodResolver(formSchema) as Resolver<FormValues>,
|
||||||
resolver: zodResolver(formSchema) as any,
|
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: initialData?.name ?? "",
|
name: initialData?.name ?? "",
|
||||||
description: initialData?.description ?? "",
|
description: initialData?.description ?? "",
|
||||||
|
|
@ -232,34 +219,26 @@ export function LLMConfigForm({
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Custom Provider (conditional) */}
|
{/* Custom Provider (conditional) */}
|
||||||
<AnimatePresence>
|
{watchProvider === "CUSTOM" && (
|
||||||
{watchProvider === "CUSTOM" && (
|
<FormField
|
||||||
<motion.div
|
control={form.control}
|
||||||
initial={{ opacity: 0, height: 0 }}
|
name="custom_provider"
|
||||||
animate={{ opacity: 1, height: "auto" }}
|
render={({ field }) => (
|
||||||
exit={{ opacity: 0, height: 0 }}
|
<FormItem>
|
||||||
>
|
<FormLabel className="text-xs sm:text-sm">Custom Provider Name</FormLabel>
|
||||||
<FormField
|
<FormControl>
|
||||||
control={form.control}
|
<Input
|
||||||
name="custom_provider"
|
placeholder="my-custom-provider"
|
||||||
render={({ field }) => (
|
{...field}
|
||||||
<FormItem>
|
value={field.value ?? ""}
|
||||||
<FormLabel className="text-xs sm:text-sm">Custom Provider Name</FormLabel>
|
/>
|
||||||
<FormControl>
|
</FormControl>
|
||||||
<Input
|
<FormMessage />
|
||||||
placeholder="my-custom-provider"
|
</FormItem>
|
||||||
{...field}
|
|
||||||
value={field.value ?? ""}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</motion.div>
|
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Model Name with Combobox */}
|
{/* Model Name with Combobox */}
|
||||||
<FormField
|
<FormField
|
||||||
|
|
@ -404,36 +383,29 @@ export function LLMConfigForm({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Ollama Quick Actions */}
|
{/* Ollama Quick Actions */}
|
||||||
<AnimatePresence>
|
{watchProvider === "OLLAMA" && (
|
||||||
{watchProvider === "OLLAMA" && (
|
<div className="flex flex-wrap gap-2">
|
||||||
<motion.div
|
<Button
|
||||||
initial={{ opacity: 0, height: 0 }}
|
type="button"
|
||||||
animate={{ opacity: 1, height: "auto" }}
|
variant="outline"
|
||||||
exit={{ opacity: 0, height: 0 }}
|
size="sm"
|
||||||
className="flex flex-wrap gap-2"
|
className="h-7 text-xs"
|
||||||
>
|
onClick={() => form.setValue("api_base", "http://localhost:11434")}
|
||||||
<Button
|
>
|
||||||
type="button"
|
localhost:11434
|
||||||
variant="outline"
|
</Button>
|
||||||
size="sm"
|
<Button
|
||||||
className="h-7 text-xs"
|
type="button"
|
||||||
onClick={() => form.setValue("api_base", "http://localhost:11434")}
|
variant="outline"
|
||||||
>
|
size="sm"
|
||||||
localhost:11434
|
className="h-7 text-xs"
|
||||||
</Button>
|
onClick={() => form.setValue("api_base", "http://host.docker.internal:11434")}
|
||||||
<Button
|
>
|
||||||
type="button"
|
Docker
|
||||||
variant="outline"
|
</Button>
|
||||||
size="sm"
|
</div>
|
||||||
className="h-7 text-xs"
|
)}
|
||||||
onClick={() => form.setValue("api_base", "http://host.docker.internal:11434")}
|
|
||||||
>
|
|
||||||
Docker
|
|
||||||
</Button>
|
|
||||||
</motion.div>
|
|
||||||
)}
|
|
||||||
</AnimatePresence>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Advanced Parameters */}
|
{/* Advanced Parameters */}
|
||||||
|
|
@ -554,44 +526,6 @@ export function LLMConfigForm({
|
||||||
/>
|
/>
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
|
|
||||||
{!hideActions && (
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"flex gap-3 pt-4",
|
|
||||||
compact ? "justify-end" : "justify-center sm:justify-end"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{onCancel && (
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="secondary"
|
|
||||||
onClick={onCancel}
|
|
||||||
disabled={isSubmitting}
|
|
||||||
className="text-xs sm:text-sm h-9 sm:h-10"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
disabled={isSubmitting}
|
|
||||||
className="gap-2 min-w-[140px] sm:min-w-[160px] text-xs sm:text-sm h-9 sm:h-10"
|
|
||||||
>
|
|
||||||
{isSubmitting ? (
|
|
||||||
<>
|
|
||||||
<Spinner size="sm" />
|
|
||||||
{mode === "edit" ? "Updating..." : "Creating"}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{submitLabel ??
|
|
||||||
(mode === "edit" ? "Update Configuration" : "Create Configuration")}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -213,14 +213,12 @@ export function ModelConfigDialog({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{mode === "create" ? (
|
{mode === "create" ? (
|
||||||
<LLMConfigForm
|
<LLMConfigForm
|
||||||
searchSpaceId={searchSpaceId}
|
searchSpaceId={searchSpaceId}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
isSubmitting={isSubmitting}
|
mode="create"
|
||||||
mode="create"
|
formId="model-config-form"
|
||||||
formId="model-config-form"
|
/>
|
||||||
hideActions
|
|
||||||
/>
|
|
||||||
) : isAutoMode && config ? (
|
) : isAutoMode && config ? (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
|
@ -362,11 +360,9 @@ export function ModelConfigDialog({
|
||||||
citations_enabled: config.citations_enabled,
|
citations_enabled: config.citations_enabled,
|
||||||
search_space_id: searchSpaceId,
|
search_space_id: searchSpaceId,
|
||||||
}}
|
}}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
isSubmitting={isSubmitting}
|
mode="edit"
|
||||||
mode="edit"
|
formId="model-config-form"
|
||||||
formId="model-config-form"
|
|
||||||
hideActions
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue