Skip to content
Merged
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
147 changes: 147 additions & 0 deletions packages/ui/src/features/home/components/HomeArchivedSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import {
Archive,
CaretDown,
CaretRight,
Cloud as CloudIcon,
GitBranch as GitBranchIcon,
Laptop as LaptopIcon,
} from "@phosphor-icons/react";
import {
type ArchivedTaskWithDetails,
formatRelativeDate,
getRepoName,
} from "@posthog/core/archive/archiveListView";
import type { WorkspaceMode } from "@posthog/shared";
import { useHomeUiStore } from "@posthog/ui/features/home/stores/homeUiStore";
import { navigateToArchived } from "@posthog/ui/router/navigationBridge";
import { openTask } from "@posthog/ui/router/useOpenTask";
import { Box, Flex, Text } from "@radix-ui/themes";

// Cap rows shown inline; the full searchable list lives in ArchivedTasksView.
const INLINE_LIMIT = 8;

function ModeGlyph({ mode }: { mode: WorkspaceMode }) {
const Icon =
mode === "cloud"
? CloudIcon
: mode === "worktree"
? GitBranchIcon
: LaptopIcon;
return <Icon size={14} className="shrink-0 text-gray-10" />;
}

interface HomeArchivedSectionProps {
items: ArchivedTaskWithDetails[];
}

export function HomeArchivedSection({ items }: HomeArchivedSectionProps) {
const expanded = useHomeUiStore((s) => s.archivedExpanded);
const toggleExpanded = useHomeUiStore((s) => s.toggleArchivedExpanded);

if (items.length === 0) return null;

const visible = items.slice(0, INLINE_LIMIT);
const hiddenCount = items.length - visible.length;

return (
<Box>
<Flex
align="center"
gap="2"
className="sticky top-0 z-10 border-(--gray-3) border-b bg-(--color-panel-solid) px-4 py-2"
>
<button
type="button"
onClick={toggleExpanded}
className="flex min-w-0 items-center gap-2 bg-transparent text-left"
aria-expanded={expanded}
>
{expanded ? (
<CaretDown size={11} weight="bold" className="text-(--gray-10)" />
) : (
<CaretRight size={11} weight="bold" className="text-(--gray-10)" />
)}
<Archive size={13} weight="fill" className="text-(--gray-11)" />
<Text className="font-semibold text-[12px] text-gray-12">
Archived
</Text>
<Text className="rounded-full bg-(--gray-a3) px-1.5 py-px font-medium text-(--gray-11) text-[10.5px] tabular-nums">
{items.length}
</Text>
</button>
<button
type="button"
onClick={navigateToArchived}
className="ml-auto bg-transparent text-(--gray-10) text-[11px] transition-colors hover:text-(--gray-12)"
>
View all
</button>
</Flex>

{expanded ? (
<>
{visible.map((item) => (
<HomeArchivedRow key={item.archived.taskId} item={item} />
))}
{hiddenCount > 0 ? (
<button
type="button"
onClick={navigateToArchived}
className="w-full bg-transparent px-4 py-2 text-left text-(--gray-10) text-[12px] transition-colors hover:bg-(--gray-2) hover:text-(--gray-12)"
>
View {hiddenCount} more in Archive
</button>
) : null}
</>
) : null}
</Box>
);
}

function HomeArchivedRow({ item }: { item: ArchivedTaskWithDetails }) {
const { task, archived } = item;
const title = task?.title ?? "Unknown task";
const repoName = getRepoName(task?.repository);

const onOpen = () => {
if (task) {
void openTask(task);
} else {
navigateToArchived();
}
};

return (
<Box
onClick={onOpen}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
onOpen();
}
}}
role="button"
tabIndex={0}
aria-label={`Open ${title}`}
className="group flex cursor-pointer items-center gap-3 border-(--gray-3) border-b py-2 pr-3 pl-4 transition-colors hover:bg-(--gray-2)"
>
<ModeGlyph mode={archived.mode} />
<div className="flex min-w-0 flex-1 flex-col">
<span
className="truncate text-[13px] text-gray-11 group-hover:text-gray-12"
title={title}
>
{title}
</span>
{repoName !== "—" ? (
<span className="truncate text-(--gray-9) text-[11px]">
{repoName}
</span>
) : null}
</div>
<Text className="shrink-0 text-(--gray-9) text-[11px]">
{formatRelativeDate(archived.archivedAt)}
</Text>
</Box>
);
}
30 changes: 22 additions & 8 deletions packages/ui/src/features/home/components/HomeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
Warning,
} from "@phosphor-icons/react";
import { Button } from "@posthog/quill";
import { useHomeArchivedTasks } from "@posthog/ui/features/home/hooks/useHomeArchivedTasks";
import { useHomeSnapshot } from "@posthog/ui/features/home/hooks/useHomeSnapshot";
import {
type HomeViewMode,
Expand All @@ -18,6 +19,7 @@ import { Box, Flex, ScrollArea, Text } from "@radix-ui/themes";
import { useEffect, useMemo } from "react";
import { ConfigMap } from "../config/ConfigMap";
import { HomeActiveAgentsStrip } from "./HomeActiveAgentsStrip";
import { HomeArchivedSection } from "./HomeArchivedSection";
import { HomeBoardView } from "./HomeBoardView";
import { HomeEmptyState } from "./HomeEmptyState";
import { HomeWorkstreamDetailPanel } from "./HomeWorkstreamDetailPanel";
Expand All @@ -39,6 +41,8 @@ const HEADER_CONTENT = (

export function HomeView() {
const { snapshot, isLoading } = useHomeSnapshot();
const { items: archivedItems, isLoading: archivedLoading } =
useHomeArchivedTasks();
const viewMode = useHomeUiStore((s) => s.viewMode);
const setViewMode = useHomeUiStore((s) => s.setViewMode);
const selectedWorkstreamId = useHomeUiStore((s) => s.selectedWorkstreamId);
Expand Down Expand Up @@ -88,7 +92,7 @@ export function HomeView() {
}

const totalRows = needsAttention.length + inProgress.length;
const hasContent = activeAgents.length > 0 || totalRows > 0;
const activeHasContent = activeAgents.length > 0 || totalRows > 0;

return (
<Flex direction="column" className="h-full">
Expand All @@ -100,7 +104,7 @@ export function HomeView() {
Home
</Text>
</Flex>
{hasContent ? (
{activeHasContent ? (
<Flex align="center" gap="5" className="text-[12px]">
{needsAttention.length > 0 ? (
<Stat
Expand Down Expand Up @@ -143,12 +147,14 @@ export function HomeView() {
<HomeActiveAgentsStrip agents={activeAgents} />
<Flex className="min-h-0 flex-1">
<Box className="min-w-0 flex-1">
{!hasContent ? (
<HomeEmptyState hasRunningAgents={activeAgents.length > 0} />
) : viewMode === "board" ? (
<Box className="h-full min-h-0">
<HomeBoardView snapshot={snapshot} />
</Box>
{viewMode === "board" ? (
activeHasContent ? (
<Box className="h-full min-h-0">
<HomeBoardView snapshot={snapshot} />
</Box>
) : (
<HomeEmptyState hasRunningAgents={activeAgents.length > 0} />
)
) : (
<ScrollArea scrollbars="vertical">
{needsAttention.length > 0 ? (
Expand Down Expand Up @@ -190,6 +196,14 @@ export function HomeView() {
{totalRows === 0 && activeAgents.length > 0 ? (
<HomeEmptyState hasRunningAgents />
) : null}

<HomeArchivedSection items={archivedItems} />

{!activeHasContent &&
archivedItems.length === 0 &&
!archivedLoading ? (
<HomeEmptyState hasRunningAgents={false} />
) : null}
</ScrollArea>
)}
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@ export function HomeWorkstreamCard({ workstream }: HomeWorkstreamCardProps) {
primaryIsTask,
showPrInMenu,
showTaskInMenu,
canArchive,
hasMenu,
runAction,
isRunningAction,
openTask,
openPr,
archive,
} = useWorkstreamPresentation(workstream);
const setSelectedWorkstreamId = useHomeUiStore(
(s) => s.setSelectedWorkstreamId,
Expand Down Expand Up @@ -196,9 +198,11 @@ export function HomeWorkstreamCard({ workstream }: HomeWorkstreamCardProps) {
restBound={restBound}
showPrInMenu={showPrInMenu}
showTaskInMenu={showTaskInMenu}
showArchive={canArchive}
onRun={runAction}
onOpenPr={openPr}
onOpenTask={openTask}
onArchive={archive}
size="xs"
runDisabled={isRunningAction}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,13 @@ export function HomeWorkstreamRow({ workstream }: HomeWorkstreamRowProps) {
primaryIsTask,
showPrInMenu,
showTaskInMenu,
canArchive,
hasMenu,
runAction,
isRunningAction,
openTask,
openPr,
archive,
} = useWorkstreamPresentation(workstream);
const setSelectedWorkstreamId = useHomeUiStore(
(s) => s.setSelectedWorkstreamId,
Expand Down Expand Up @@ -204,9 +206,11 @@ export function HomeWorkstreamRow({ workstream }: HomeWorkstreamRowProps) {
restBound={restBound}
showPrInMenu={showPrInMenu}
showTaskInMenu={showTaskInMenu}
showArchive={canArchive}
onRun={runAction}
onOpenPr={openPr}
onOpenTask={openTask}
onArchive={archive}
size="sm"
runDisabled={isRunningAction}
/>
Expand Down
17 changes: 17 additions & 0 deletions packages/ui/src/features/home/components/WorkstreamBits.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Archive,
ArrowSquareOut,
CheckCircle,
CircleNotch,
Expand Down Expand Up @@ -132,18 +133,23 @@ export function WorkstreamOverflowMenu({
restBound,
showPrInMenu,
showTaskInMenu,
showArchive = false,
onRun,
onOpenPr,
onOpenTask,
onArchive,
size = "sm",
runDisabled = false,
}: {
restBound: BoundAction[];
showPrInMenu: boolean;
showTaskInMenu: boolean;
/** Whether to offer "Archive" in the menu. */
showArchive?: boolean;
onRun: (action: BoundAction) => void;
onOpenPr: () => void;
onOpenTask: () => void;
onArchive?: () => void;
size?: "sm" | "xs";
/** Disables the task-starting actions while one is already in flight. */
runDisabled?: boolean;
Expand Down Expand Up @@ -184,6 +190,17 @@ export function WorkstreamOverflowMenu({
{showTaskInMenu ? (
<DropdownMenu.Item onSelect={onOpenTask}>Open task</DropdownMenu.Item>
) : null}
{showArchive && onArchive ? (
<>
{restBound.length > 0 || showPrInMenu || showTaskInMenu ? (
<DropdownMenu.Separator />
) : null}
<DropdownMenu.Item onSelect={onArchive}>
<Archive size={12} />
Archive
</DropdownMenu.Item>
</>
) : null}
</DropdownMenu.Content>
</DropdownMenu.Root>
);
Expand Down
35 changes: 35 additions & 0 deletions packages/ui/src/features/home/hooks/useHomeArchivedTasks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {
type ArchivedTaskWithDetails,
mergeArchivedWithTasks,
} from "@posthog/core/archive/archiveListView";
import { useHostTRPC } from "@posthog/host-router/react";
import { useTasks } from "@posthog/ui/features/tasks/useTasks";
import { useQuery } from "@tanstack/react-query";
import { useMemo } from "react";

export interface HomeArchivedTasks {
items: ArchivedTaskWithDetails[];
isLoading: boolean;
}

// Window into archived tasks for the Home list view. Reuses the same data
// pipeline as the dedicated ArchivedTasksView (archive.list joined with tasks),
// sorted most-recently-archived first so the Home section shows the latest work.
export function useHomeArchivedTasks(): HomeArchivedTasks {
const trpc = useHostTRPC();
const { data: archivedTasks = [], isLoading: isLoadingArchived } = useQuery(
trpc.archive.list.queryOptions(),
);
const { data: tasks = [], isLoading: isLoadingTasks } = useTasks();

const items = useMemo(() => {
const merged = mergeArchivedWithTasks(archivedTasks, tasks);
return [...merged].sort(
(a, b) =>
new Date(b.archived.archivedAt).getTime() -
new Date(a.archived.archivedAt).getTime(),
);
}, [archivedTasks, tasks]);

return { items, isLoading: isLoadingArchived || isLoadingTasks };
}
Loading
Loading