From 8e0e82d573083dc59ccc50b239181ca72ea996ec Mon Sep 17 00:00:00 2001 From: iza <59828082+izadoesdev@users.noreply.github.com> Date: Tue, 8 Sep 2026 20:08:36 +0300 Subject: [PATCH] fix(dashboard): match business context to organization settings --- .agents/skills/databuddy-internal/SKILL.md | 1 + .../components/business-context-editor.tsx | 460 ++++++++++-------- .../components/business-context-settings.tsx | 142 +++--- .../regressions/business-context.spec.ts | 9 +- 4 files changed, 338 insertions(+), 274 deletions(-) diff --git a/.agents/skills/databuddy-internal/SKILL.md b/.agents/skills/databuddy-internal/SKILL.md index 0d63c6b03..f9ee0f53e 100644 --- a/.agents/skills/databuddy-internal/SKILL.md +++ b/.agents/skills/databuddy-internal/SKILL.md @@ -132,6 +132,7 @@ Read [codebase-map.md](./references/codebase-map.md) when you need deeper routin ### Dashboard work - Start in `apps/dashboard` +- Organization settings should match `organizations/components/general-settings.tsx`: the same `max-w-2xl` column, shared `Card` headers/content, compact fields, and `TopBar.Actions` for Save. Compare actual neighboring pages visually before claiming design consistency; shared inputs alone are not enough. - The Feature Flags list is a dense data table; keep flag identity metadata clean and place activity telemetry in a dedicated labeled column with its accuracy caveat in the Activity header tooltip. - Keep large page-owned settings sheets and dialogs in adjacent feature files; route components should own page data and layout rather than embedding unrelated form lifecycles. - For dashboard navigation audits, check all route surfaces: `components/layout/navigation/navigation-config.tsx`, `components/ui/command-search.tsx`, and local `PageNavigation` layouts under `app/**/layout.tsx` before calling a page orphaned. diff --git a/apps/dashboard/app/(main)/organizations/components/business-context-editor.tsx b/apps/dashboard/app/(main)/organizations/components/business-context-editor.tsx index 18e9670b2..756341605 100644 --- a/apps/dashboard/app/(main)/organizations/components/business-context-editor.tsx +++ b/apps/dashboard/app/(main)/organizations/components/business-context-editor.tsx @@ -6,10 +6,17 @@ import { type BusinessContextSettings, businessContextIsGenerating, } from "@databuddy/shared/organization-business-context"; -import { Button, Field, Textarea, dayjs } from "@databuddy/ui"; +import { Button, Card, Field, Textarea, dayjs } from "@databuddy/ui"; import { Dialog, DropdownMenu } from "@databuddy/ui/client"; -import { CaretDownIcon, WandSparkleIcon } from "@databuddy/ui/icons"; +import { + ArrowSquareOutIcon, + CaretDownIcon, + FileTextIcon, + FloppyDiskIcon, + WandSparkleIcon, +} from "@databuddy/ui/icons"; import { useEffect, useRef, useState } from "react"; +import { TopBar } from "@/components/layout/top-bar"; interface EditableBrief { content: string; @@ -35,21 +42,40 @@ function Sources({ sources }: { sources: BusinessBrief["sources"] }) { return null; } return ( -
- Sources - {links.map(({ url, title }) => ( - - {title || new URL(url).hostname} - - ))} -
+ + + Sources + Pages used to generate this brief + + + {links.map(({ url, title }) => { + const page = new URL(url); + return ( + + +
+

+ {title || page.hostname} +

+

+ {page.hostname} + {page.pathname === "/" ? "" : page.pathname} +

+
+ +
+ ); + })} +
+
); } @@ -68,6 +94,7 @@ export function BusinessContextEditor({ const [notice, setNotice] = useState(""); const [review, setReview] = useState<"generation" | "conflict" | null>(null); const editorRef = useRef(null); + const savingRef = useRef(false); const revision = profile?.revision ?? 0; const content = draft?.content ?? profile?.content ?? ""; const generationWebsite = websites.find( @@ -144,9 +171,10 @@ export function BusinessContextEditor({ } async function save() { - if (saveDisabled || !draft) { + if (saveDisabled || !draft || savingRef.current) { return; } + savingRef.current = true; setIsSaving(true); setError(undefined); setNotice(""); @@ -166,6 +194,7 @@ export function BusinessContextEditor({ : "Couldn't save the brief. Your edits are still here." ); } finally { + savingRef.current = false; setIsSaving(false); } } @@ -191,211 +220,222 @@ export function BusinessContextEditor({ } return ( -
-
-

- Your business, in your words -

-

- Give your agent the context behind your numbers: what you sell, who - you serve, and what success looks like. -

-
- {canEdit && ( -
- {websites.length > 1 ? ( - - - } - aria-label={`Source website: ${selectedWebsite?.name || selectedWebsite?.domain}`} - > - - {selectedWebsite?.name || selectedWebsite?.domain} - - - - - - - Generate from website - - - {websites.map((site) => ( - - {site.name || site.domain} - - ))} - - - - - ) : ( -

- {selectedWebsite - ? `From ${selectedWebsite.domain}` - : "Add a website to generate a brief, or write your own below."} -

- )} - {selectedWebsite && ( - - )} -
- )} -
- {generating && ( -

- {canEdit - ? "Reading your website and preparing a draft. You can keep writing." - : "An updated brief is being prepared."} -

- )} - {pendingDraft && canEdit && ( -
-

An AI draft is ready. Your current text has been kept.

- -
- )} - {generation?.status === "failed" && canEdit && ( -

- {generation.error || - "AI couldn't finish this draft. Try generating again, or keep editing."} -

- )} - {notice &&

{notice}

} -
- - Business brief - - {canEdit - ? "Edit anything. Your saved brief stays in use until you save changes." - : "This is the context your agent uses. Organization admins can update it."} - -