feat(FN-2154): show dismissible setup warning on project page

- Render SetupWarningBanner from App only in project view when setup readiness reports warnings
- Persist dismiss state per project via scoped storage and restore it when switching projects
- Add optional dismiss action to SetupWarningBanner with close button UI and responsive styles
- Remove inline banner from QuickEntryBox and add tests covering dismiss button behavior
This commit is contained in:
Fusion
2026-04-19 13:49:18 -07:00
committed by gsxdsm
parent 1e8bb8345d
commit b052f17bd1
5 changed files with 174 additions and 17 deletions

View File

@@ -19,6 +19,7 @@ import { AppModals } from "./components/AppModals";
import { DashboardLoader, type DashboardLoaderStage } from "./components/DashboardLoader";
import { ExecutorStatusBar } from "./components/ExecutorStatusBar";
import { SessionNotificationBanner } from "./components/SessionNotificationBanner";
import { SetupWarningBanner } from "./components/SetupWarningBanner";
import { OnboardingResumeCard } from "./components/OnboardingResumeCard";
import { PostOnboardingRecommendations } from "./components/PostOnboardingRecommendations";
import {
@@ -42,6 +43,7 @@ import { useAppSettings } from "./hooks/useAppSettings";
import { useDeepLink } from "./hooks/useDeepLink";
import { useFavorites } from "./hooks/useFavorites";
import { useAuthOnboarding } from "./hooks/useAuthOnboarding";
import { useSetupReadiness } from "./hooks/useSetupReadiness";
import { useViewState, type TaskView } from "./hooks/useViewState";
import { useProjectActions } from "./hooks/useProjectActions";
import { useTaskHandlers } from "./hooks/useTaskHandlers";
@@ -50,6 +52,9 @@ import { useRemoteNodeEvents } from "./hooks/useRemoteNodeEvents";
import { NodeProvider, useNodeContext } from "./context/NodeContext";
import type { AiSessionSummary } from "./api";
import { fetchAiSession, fetchUnreadCount, reportDashboardPerf } from "./api";
import { getScopedItem, setScopedItem } from "./utils/projectStorage";
const SETUP_WARNING_DISMISSED_KEY = "kb-setup-warning-dismissed";
function AppInner() {
const { toasts, addToast, removeToast } = useToast();
@@ -59,7 +64,13 @@ function AppInner() {
const { projects, loading: projectsLoading, error: projectsError, refresh: refreshProjects, register: registerProject, update: updateProjectHook, unregister: unregisterProjectHook } = useProjects();
const { nodes } = useNodes();
const { currentProject, setCurrentProject, clearCurrentProject, loading: currentProjectLoading } = useCurrentProject(projects);
const {
hasAiProvider,
hasGithub,
loading: setupReadinessLoading,
hasWarnings,
} = useSetupReadiness(currentProject?.id);
// Node context for local/remote node switching
const { currentNode, currentNodeId, isRemote, setCurrentNode, clearCurrentNode } = useNodeContext();
@@ -203,6 +214,20 @@ function AppInner() {
const [missionTargetId, setMissionTargetId] = useState<string | undefined>(undefined);
const [milestoneSliceResumeSessionId, setMilestoneSliceResumeSessionId] = useState<string | undefined>(undefined);
const [quickChatOpen, setQuickChatOpen] = useState(false);
const [setupWarningDismissed, setSetupWarningDismissed] = useState(
() => getScopedItem(SETUP_WARNING_DISMISSED_KEY, currentProject?.id) === "true",
);
useEffect(() => {
setSetupWarningDismissed(
getScopedItem(SETUP_WARNING_DISMISSED_KEY, currentProject?.id) === "true",
);
}, [currentProject?.id]);
const handleDismissSetupWarning = useCallback(() => {
setScopedItem(SETUP_WARNING_DISMISSED_KEY, "true", currentProject?.id);
setSetupWarningDismissed(true);
}, [currentProject?.id]);
// Settings state
const {
@@ -665,6 +690,13 @@ function AppInner() {
onOpenSettings={(section) => modalManager.openSettings(section as SectionId)}
/>
)}
{viewMode === "project" && currentProject && !setupReadinessLoading && hasWarnings && !setupWarningDismissed && (
<SetupWarningBanner
hasAiProvider={hasAiProvider}
hasGithub={hasGithub}
onDismiss={handleDismissSetupWarning}
/>
)}
<div
className={`project-content${viewMode === "project" && currentProject ? " project-content--with-footer" : ""}${isMobile ? " project-content--with-mobile-nav" : ""}`}
>