Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ import {
} from "@features/inbox/utils/filterReports";
import { INBOX_REFETCH_INTERVAL_MS } from "@features/inbox/utils/inboxConstants";
import { setPendingInboxOpenMethod } from "@features/inbox/utils/pendingInboxOpenMethod";
import { DiscoveredTaskDetailPane } from "@features/setup/components/DiscoveredTaskDetailPane";
import { RecommendedSetupTasks } from "@features/setup/components/RecommendedSetupTasks";
import { useSetupStore } from "@features/setup/stores/setupStore";
import {
useIntegrations,
useRepositoryIntegration,
Expand Down Expand Up @@ -361,9 +358,6 @@ export function InboxSignalsTab() {
// ── Click handler: plain / cmd / shift ──────────────────────────────────
const handleReportClick = useCallback(
(reportId: string, event: { metaKey: boolean; shiftKey: boolean }) => {
// Selecting a real report clears any discovered-task selection so the
// detail pane can swap to the report.
useSetupStore.getState().selectDiscoveredTask(null);
if (event.shiftKey) {
setPendingInboxOpenMethod("click_shift");
selectRange(
Expand Down Expand Up @@ -444,39 +438,14 @@ export function InboxSignalsTab() {
};
}, [sidebarIsResizing, setSidebarWidth, setSidebarIsResizing]);

// ── Discovered-task suggestions (rendered inline at top of list) ───────
const discoveredTasks = useSetupStore((s) => s.discoveredTasks);
const hasDiscoveredTasks = discoveredTasks.length > 0;
const selectedDiscoveredTaskId = useSetupStore(
(s) => s.selectedDiscoveredTaskId,
);
const selectDiscoveredTask = useSetupStore((s) => s.selectDiscoveredTask);
const selectedDiscoveredTask =
discoveredTasks.find((t) => t.id === selectedDiscoveredTaskId) ?? null;

const handleSelectDiscoveredTask = useCallback(
(taskId: string) => {
selectDiscoveredTask(taskId);
clearSelection();
},
[selectDiscoveredTask, clearSelection],
);

const handleCloseDiscoveredTaskPane = useCallback(() => {
selectDiscoveredTask(null);
}, [selectDiscoveredTask]);

// ── Layout mode (computed early — needed by focus effect below) ────────
const hasReports = allReports.length > 0;
const hasActiveFilters =
sourceProductFilter.length > 0 ||
suggestedReviewerFilter.length > 0 ||
statusFilter.length < 5;
const shouldShowTwoPane =
hasReports ||
!!searchQuery.trim() ||
hasActiveFilters ||
hasDiscoveredTasks;
hasReports || !!searchQuery.trim() || hasActiveFilters;

// Sticky: once we enter two-pane mode, stay there even if a refetch
// momentarily empties the list (e.g. when sort order changes).
Expand Down Expand Up @@ -759,9 +728,6 @@ export function InboxSignalsTab() {
onReportAction={tracker.signalAction}
/>
</Box>
<RecommendedSetupTasks
onSelectTask={handleSelectDiscoveredTask}
/>
<ReportListPane
reports={reports}
allReports={allReports}
Expand Down Expand Up @@ -817,11 +783,6 @@ export function InboxSignalsTab() {
onReportAction={tracker.signalAction}
onScroll={tracker.signalScroll}
/>
) : selectedDiscoveredTask ? (
<DiscoveredTaskDetailPane
task={selectedDiscoveredTask}
onClose={handleCloseDiscoveredTaskPane}
/>
) : (
<SelectReportPane />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ANALYTICS_EVENTS,
type RepositoryProvider,
} from "@shared/types/analytics";
import { useActiveRepoStore } from "@stores/activeRepoStore";
import { track } from "@utils/analytics";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { ONBOARDING_STEPS, type OnboardingStep } from "../types";
Expand Down Expand Up @@ -32,12 +33,8 @@ export interface DetectedRepo {
export function useOnboardingFlow() {
const currentStep = useOnboardingStore((state) => state.currentStep);
const setCurrentStep = useOnboardingStore((state) => state.setCurrentStep);
const selectedDirectory = useOnboardingStore(
(state) => state.selectedDirectory,
);
const setSelectedDirectory = useOnboardingStore(
(state) => state.setSelectedDirectory,
);
const selectedDirectory = useActiveRepoStore((state) => state.path);
const setSelectedDirectory = useActiveRepoStore((state) => state.setPath);
const directionRef = useRef<1 | -1>(1);

const [detectedRepo, setDetectedRepo] = useState<DetectedRepo | null>(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ interface OnboardingStoreState {
currentStep: OnboardingStep;
hasCompletedOnboarding: boolean;
selectedProjectId: number | null;
selectedDirectory: string;
}

interface OnboardingStoreActions {
Expand All @@ -18,7 +17,6 @@ interface OnboardingStoreActions {
resetOnboarding: () => void;
resetSelections: () => void;
selectProjectId: (projectId: number | null) => void;
setSelectedDirectory: (path: string) => void;
}

type OnboardingStore = OnboardingStoreState & OnboardingStoreActions;
Expand All @@ -27,7 +25,6 @@ const initialState: OnboardingStoreState = {
currentStep: "welcome",
hasCompletedOnboarding: false,
selectedProjectId: null,
selectedDirectory: "",
};

export const useOnboardingStore = create<OnboardingStore>()(
Expand All @@ -47,15 +44,13 @@ export const useOnboardingStore = create<OnboardingStore>()(
selectedProjectId: null,
}),
selectProjectId: (selectedProjectId) => set({ selectedProjectId }),
setSelectedDirectory: (selectedDirectory) => set({ selectedDirectory }),
}),
{
name: "onboarding-store",
partialize: (state) => ({
currentStep: state.currentStep,
hasCompletedOnboarding: state.hasCompletedOnboarding,
selectedProjectId: state.selectedProjectId,
selectedDirectory: state.selectedDirectory,
}),
},
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Badge } from "@components/ui/Badge";
import { Button } from "@components/ui/Button";
import { MarkdownRenderer } from "@features/editor/components/MarkdownRenderer";
import { useFolders } from "@features/folders/hooks/useFolders";
import { useOnboardingStore } from "@features/onboarding/stores/onboardingStore";
import { useSetupStore } from "@features/setup/stores/setupStore";
import type { DiscoveredTask } from "@features/setup/types";
import { buildDiscoveredTaskPrompt } from "@features/setup/utils/buildDiscoveredTaskPrompt";
Expand All @@ -10,26 +10,58 @@ import {
FALLBACK_CATEGORY_CONFIG,
} from "@features/setup/utils/categoryConfig";
import { useDetectedCloudRepository } from "@hooks/useDetectedCloudRepository";
import { PlusIcon, SparkleIcon, X as XIcon } from "@phosphor-icons/react";
import { Box, Button, Flex, ScrollArea, Text } from "@radix-ui/themes";
import { PlusIcon, SparkleIcon } from "@phosphor-icons/react";
import {
Box,
Dialog,
Flex,
ScrollArea,
Text,
VisuallyHidden,
} from "@radix-ui/themes";
import { ANALYTICS_EVENTS } from "@shared/types/analytics";
import { useActiveRepoStore } from "@stores/activeRepoStore";
import { useNavigationStore } from "@stores/navigationStore";
import { track } from "@utils/analytics";

interface DiscoveredTaskDetailPaneProps {
task: DiscoveredTask;
interface DiscoveredTaskDetailDialogProps {
task: DiscoveredTask | null;
onClose: () => void;
}

export function DiscoveredTaskDetailPane({
export function DiscoveredTaskDetailDialog({
task,
onClose,
}: DiscoveredTaskDetailDialogProps) {
return (
<Dialog.Root
open={task !== null}
onOpenChange={(open) => {
if (!open) onClose();
}}
>
<Dialog.Content maxWidth="640px">
<VisuallyHidden>
<Dialog.Title>{task?.title ?? "Suggestion"}</Dialog.Title>
</VisuallyHidden>
{task && <DialogBody task={task} onClose={onClose} />}
</Dialog.Content>
</Dialog.Root>
);
}

function DialogBody({
task,
onClose,
}: DiscoveredTaskDetailPaneProps) {
}: {
task: DiscoveredTask;
onClose: () => void;
}) {
const config = CATEGORY_CONFIG[task.category] ?? FALLBACK_CATEGORY_CONFIG;
const CategoryIcon = config.icon;

const tasks = useSetupStore((s) => s.discoveredTasks);
const selectedDirectory = useOnboardingStore((s) => s.selectedDirectory);
const selectedDirectory = useActiveRepoStore((s) => s.path);
const navigateToTaskInput = useNavigationStore((s) => s.navigateToTaskInput);
const { folders } = useFolders();
const detectedCloudRepository = useDetectedCloudRepository(selectedDirectory);
Expand All @@ -45,7 +77,10 @@ export function DiscoveredTaskDetailPane({

const initialPrompt = buildDiscoveredTaskPrompt(task);
const folderId = folders.find((f) => f.path === selectedDirectory)?.id;
useSetupStore.getState().removeDiscoveredTask(task.id);
useSetupStore
.getState()
.removeDiscoveredTask(task.id, task.repoPath ?? null);
onClose();
navigateToTaskInput({
initialPrompt,
folderId,
Expand All @@ -61,48 +96,33 @@ export function DiscoveredTaskDetailPane({
position: position >= 0 ? position : 0,
total_discovered: tasks.length,
});
useSetupStore.getState().removeDiscoveredTask(task.id);
useSetupStore
.getState()
.removeDiscoveredTask(task.id, task.repoPath ?? null);
onClose();
};

return (
<>
<Flex
align="center"
justify="between"
gap="2"
py="2"
className="shrink-0 border-b border-b-(--gray-5) @2xl:px-6 @3xl:px-8 @4xl:px-10 @5xl:px-12 @lg:px-4 @md:px-3 @xl:px-5 px-2"
>
<Flex align="center" gap="2" className="min-w-0">
<Badge
color="violet"
className="!leading-none inline-flex shrink-0 items-center gap-1"
>
<SparkleIcon size={10} weight="fill" />
Suggested
</Badge>
<Text className="block min-w-0 text-balance break-words font-bold text-base">
{task.title}
</Text>
</Flex>
<Flex align="center" gap="1" className="shrink-0">
<button
type="button"
onClick={onClose}
aria-label="Close suggestion"
className="rounded p-0.5 text-gray-11 hover:bg-gray-3 hover:text-gray-12"
>
<XIcon size={14} />
</button>
</Flex>
<Flex direction="column" gap="4">
<Flex align="center" gap="2" wrap="wrap">
<Badge
color="violet"
className="!leading-none inline-flex shrink-0 items-center gap-1"
>
<SparkleIcon size={10} weight="fill" />
Suggested
</Badge>
<Text className="block min-w-0 text-balance break-words font-bold text-base">
{task.title}
</Text>
</Flex>

<ScrollArea type="auto" scrollbars="vertical" className="min-h-0 flex-1">
<Flex
direction="column"
gap="4"
className="@2xl:px-6 @3xl:px-8 @4xl:px-10 @5xl:px-12 @lg:px-4 @md:px-3 @xl:px-5 px-2 py-4"
>
<ScrollArea
type="auto"
scrollbars="vertical"
className="max-h-[60vh] min-h-0"
>
<Flex direction="column" gap="4" pr="3">
<Flex align="center" gap="2" className="text-(--gray-11)">
<span style={{ color: `var(--${config.color}-9)` }}>
<CategoryIcon size={14} weight="duotone" />
Expand Down Expand Up @@ -158,32 +178,16 @@ export function DiscoveredTaskDetailPane({
</Flex>
</ScrollArea>

<Flex
align="center"
justify="end"
gap="2"
className="h-[38px] shrink-0 border-t border-t-(--gray-5) bg-(--gray-1) @2xl:px-6 @3xl:px-8 @4xl:px-10 @5xl:px-12 @lg:px-4 @md:px-3 @xl:px-5 px-2"
>
<Button
size="1"
variant="ghost"
color="gray"
className="gap-1 font-medium text-[11px]"
onClick={handleDismiss}
>
<Flex gap="3" justify="end">
<Button variant="soft" color="gray" onClick={handleDismiss}>
Dismiss
</Button>
<Button
size="1"
variant="solid"
className="gap-1 font-medium text-[11px]"
onClick={handleCreateTask}
>
<PlusIcon size={12} />
<Button variant="solid" onClick={handleCreateTask}>
<PlusIcon size={14} weight="bold" />
Implement as new task
</Button>
</Flex>
</>
</Flex>
);
}

Expand Down
Loading
Loading