From 562e721539d5d35a6cd174ebc46b1489d1815443 Mon Sep 17 00:00:00 2001 From: Vorflux AI <249966464+vorflux[bot]@users.noreply.github.com> Date: Tue, 21 Jul 2026 16:26:29 +0000 Subject: [PATCH] fix(web): align workspace prompt settings contract --- .../components/settings/workspace-prompt.tsx | 69 ++++++++----------- apps/web/hooks/use-org-settings.ts | 13 +--- packages/lib/api.ts | 18 ++--- 3 files changed, 39 insertions(+), 61 deletions(-) diff --git a/apps/web/components/settings/workspace-prompt.tsx b/apps/web/components/settings/workspace-prompt.tsx index 64d82f7b..30ed8a55 100644 --- a/apps/web/components/settings/workspace-prompt.tsx +++ b/apps/web/components/settings/workspace-prompt.tsx @@ -1,11 +1,10 @@ "use client" import { LoaderIcon } from "lucide-react" -import { useEffect, useRef, useState } from "react" +import { useState } from "react" import { useOrgSettings, useUpdateOrgSettings } from "@/hooks/use-org-settings" import { dmSans125ClassName, dmSansClassName } from "@/lib/fonts" import { cn } from "@lib/utils" -import { useAuth } from "@lib/auth-context" const DESCRIPTION_ID = "workspace-prompt-description" const COUNTER_ID = "workspace-prompt-counter" @@ -36,65 +35,54 @@ function PromptHeader() { "text-[13px] tracking-[-0.13px] text-[#737373]", )} > - Set persistent guidance for operating preferences, priorities, source - and tool choices, workflows, terminology, formatting, and communication - style. This guidance cannot override fixed safety, access, or approval - constraints. + Set persistent guidance for how Company Brain works across your + workspace.

) } export function WorkspacePrompt() { - const { org } = useAuth() const settingsQuery = useOrgSettings() const updateSettings = useUpdateOrgSettings() - const [prompt, setPrompt] = useState("") - const syncedPrompt = useRef<{ orgId: string; value: string } | null>(null) + const [draft, setDraft] = useState(null) const savedPrompt = settingsQuery.data?.workspacePrompt ?? "" - const dirty = prompt.trim() !== savedPrompt.trim() - - useEffect(() => { - if (!settingsQuery.data) return - - const nextPrompt = settingsQuery.data.workspacePrompt ?? "" - const orgId = org?.id ?? "" - const previous = syncedPrompt.current - - setPrompt((current) => { - if (!previous || previous.orgId !== orgId) return nextPrompt - return current === previous.value ? nextPrompt : current - }) - syncedPrompt.current = { orgId, value: nextPrompt } - }, [org?.id, settingsQuery.data]) + const prompt = draft ?? savedPrompt + const dirty = draft !== null && draft.trim() !== savedPrompt.trim() const handleSave = () => { - updateSettings.mutate({ - workspacePrompt: prompt.trim() ? prompt.trim() : null, - }) + updateSettings.mutate( + { + workspacePrompt: prompt.trim() ? prompt.trim() : null, + }, + { onSuccess: () => setDraft(null) }, + ) } return (
{settingsQuery.isLoading ? ( -
- +
+ ) : settingsQuery.isError ? (

@@ -104,10 +92,10 @@ export function WorkspacePrompt() { type="button" onClick={() => void settingsQuery.refetch()} disabled={settingsQuery.isFetching} - className="inline-flex h-7 items-center gap-1.5 rounded-full bg-[#0D121A] px-3 text-[12px] font-semibold text-[#FAFAFA] shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.7)] transition-opacity hover:opacity-80 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50" + className="inline-flex h-7 items-center gap-1.5 rounded-full bg-[#0D121A] px-3 text-[12px] font-semibold text-[#FAFAFA] shadow-[inset_1.5px_1.5px_4.5px_rgba(0,0,0,0.7)] transition-opacity hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#4BA0FA] focus-visible:ring-offset-2 focus-visible:ring-offset-[#1B1F24] cursor-pointer disabled:cursor-not-allowed disabled:opacity-50" > {settingsQuery.isFetching && ( - +

diff --git a/apps/web/hooks/use-org-settings.ts b/apps/web/hooks/use-org-settings.ts index cc643fc9..fa65b19f 100644 --- a/apps/web/hooks/use-org-settings.ts +++ b/apps/web/hooks/use-org-settings.ts @@ -11,10 +11,6 @@ export type OrgSettings = { excludeItems?: string[] | null } -type OrgSettingsResponse = { - settings?: Partial -} & Partial - export function useOrgSettings() { const { org } = useAuth() const orgId = org?.id ?? "" @@ -22,14 +18,11 @@ export function useOrgSettings() { return useQuery({ queryKey: ["settings", "org", orgId], queryFn: async (): Promise => { - const response = await $fetch("@get/settings", { - disableValidation: true, - }) + const response = await $fetch("@get/settings") if (response.error) { throw new Error(response.error.message || "Failed to load settings") } - const data = response.data as OrgSettingsResponse | null - const settings = data?.settings ?? data ?? {} + const settings = response.data ?? {} return { shouldLLMFilter: settings.shouldLLMFilter ?? false, filterPrompt: settings.filterPrompt ?? null, @@ -63,7 +56,7 @@ export function useUpdateOrgSettings() { onMutate: () => ({ orgId }), onSuccess: async (data, _settings, mutationContext) => { const queryKey = ["settings", "org", mutationContext.orgId] as const - const canonicalSettings = data?.settings + const canonicalSettings = data?.updated if (canonicalSettings) { queryClient.setQueryData(queryKey, (current) => current ? { ...current, ...canonicalSettings } : current, diff --git a/packages/lib/api.ts b/packages/lib/api.ts index 6a3ed049..7cf8755f 100644 --- a/packages/lib/api.ts +++ b/packages/lib/api.ts @@ -29,16 +29,10 @@ import { UpdateContainerTagSettingsRequestSchema, } from "../validation/api" -// Settings response schema - this is custom to console (not in shared validation) -const SettingsResponseSchema = z.object({ - message: z.string(), - settings: z.object({ - excludeItems: z.array(z.string().min(1).max(20)).nullable().optional(), - filterPrompt: z.string().max(750).nullable().optional(), - includeItems: z.array(z.string().min(1).max(20)).nullable().optional(), - shouldLLMFilter: z.boolean().optional(), - workspacePrompt: z.string().max(1500).nullable().optional(), - }), +const UpdateSettingsResponseSchema = z.object({ + orgId: z.string(), + orgSlug: z.string(), + updated: SettingsRequestSchema, }) // Analytics request schema - custom to console @@ -196,11 +190,11 @@ export const apiSchema = createSchema({ // Settings operations "@get/settings": { - output: z.object({}).passthrough(), + output: SettingsRequestSchema, }, "@patch/settings": { input: SettingsRequestSchema, - output: SettingsResponseSchema, + output: UpdateSettingsResponseSchema, }, "@post/settings/reset": { input: z.object({ confirmation: z.string() }),