Skip to content

feat(channels): add "Recent tasks" nav option#3023

Closed
raquelmsmith wants to merge 6 commits into
mainfrom
posthog-code/recent-tasks-nav
Closed

feat(channels): add "Recent tasks" nav option#3023
raquelmsmith wants to merge 6 commits into
mainfrom
posthog-code/recent-tasks-nav

Conversation

@raquelmsmith

Copy link
Copy Markdown
Member

Problem

In the Channels (project-bluebird) space, there was no single place to see every task across all channels at a glance. This adds a Recent tasks nav option (below Files) that lists all tasks, most recent first, so you can jump to any task without first navigating into its channel.

Changes

  • New Recent tasks item in the Channels-space nav (clock icon), opening a /website/recent-tasks view.
  • Each row shows the task's channel, its last-updated time in human-readable form ("3 minutes ago", "2 days ago", or a date for older), and the status icon — reusing the same TaskIcon component the sidebar and command palette use (cloud run states, Slack origin, etc.).
  • Clicking a row opens the task in its channel when filed, otherwise the default task detail.
  • Instrumented with a view_recent_tasks action on a new recent_tasks surface; the nav click reuses the existing nav_click tracking.

Gated by project-bluebird since the whole /website space only renders when the flag is on.

How did you test this?

  • pnpm --filter @posthog/ui typecheck — passes (after building upstream package types).
  • pnpm --filter @posthog/shared typecheck and test time — pass.
  • Biome check on all touched files — clean.
  • Route tree regenerated via the repo's generate-routes script (not hand-edited).
  • Did not run the app or e2e tests.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Created with PostHog Code

Add a "Recent tasks" item to the Channels-space nav (below "Files") that
opens a cross-channel list of every task, most recent first. Each row shows
the task's channel, its last-updated time (human-readable), and the same
status icon the sidebar and command palette render (TaskIcon).

Gated by project-bluebird via the /website space.

Generated-By: PostHog Code
Task-Id: 51e058ce-6cca-43fa-b056-95ae0ff5f5b6
@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit eff1210.

@greptile-apps

greptile-apps Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "feat(channels): add "Recent tasks" nav o..." | Re-trigger Greptile

Comment on lines +28 to +50
export function RecentTasksView() {
const { data: tasks } = useTasks();
const { channels } = useChannels();
const taskChannelMap = useTaskChannelMap(channels);
const archivedTaskIds = useArchivedTaskIds();

useEffect(() => {
track(ANALYTICS_EVENTS.CHANNEL_ACTION, {
action_type: "view_recent_tasks",
surface: "recent_tasks",
});
}, []);

const rows = useMemo<RecentTaskRow[]>(() => {
return (tasks ?? [])
.filter((task) => !archivedTaskIds.has(task.id))
.map((task) => ({
task,
channel: taskChannelMap.get(task.id),
updatedAt: Date.parse(task.updated_at) || 0,
}))
.sort((a, b) => b.updatedAt - a.updatedAt);
}, [tasks, taskChannelMap, archivedTaskIds]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Premature empty state during data load

useTasks() returns undefined while the network request is in flight. Because the component uses tasks ?? [], rows is always an empty array during loading, so "No tasks yet" is shown immediately on every mount before the API responds. The fix is to also destructure isLoading (or isPending) and guard the empty-state branch with it.

Per the codebase rule, conditions for loading vs. empty must be kept distinct to avoid prematurely rendering empty states.

Rule Used: In cases where there are multiple states to handle... (source)

Learned From
PostHog/posthog#32610

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Agent (PostHog Code) posting on @raquelmsmith's behalf at her request.

Already handled in the current revision: RecentTasksView destructures isLoading from useTasks() and only renders the "No tasks yet" empty state once loading has finished (isLoading ? null : <empty>), so it no longer flashes during the in-flight request.

Comment on lines +44 to +48
.map((task) => ({
task,
channel: taskChannelMap.get(task.id),
updatedAt: Date.parse(task.updated_at) || 0,
}))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Epoch fallback date shown for unparseable updated_at

When Date.parse(task.updated_at) returns NaN, the || 0 fallback silently assigns epoch 0 (Jan 1, 1970). formatRelativeTimeLong(0) will then render a very old absolute date in the subtitle row instead of something user-friendly. If updated_at can ever be missing or malformed, a null-style guard (skipping or showing "–") would be less confusing than a 1970 timestamp.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Agent (PostHog Code) posting on @raquelmsmith's behalf at her request.

Already handled in the current revision: an unparseable updated_at now maps to updatedAt: null (not epoch 0), the row omits the relative-time segment entirely when it's null, and such rows sort last. No more 1970 dates.

Address code-review feedback on the Recent tasks view:

- Don't flash the "No tasks yet" empty state while the task list is still
  loading (useTasks() returns undefined in flight) — guard it with isLoading.
- Omit the relative time when updated_at can't be parsed, instead of showing
  a misleading epoch (1970) date.
- Build the rows in a single filter+map pass before sorting.

Generated-By: PostHog Code
Task-Id: 51e058ce-6cca-43fa-b056-95ae0ff5f5b6
- Prefix the channel with "#" and keep the channel + last-updated metadata on
  the left of each row.
- Show small clickable icons on the right for artifacts a task produced:
  canvases it generated (via the dashboard's durable generationTaskId) and the
  pull request it opened (latest_run.output.pr_url). Clicking a canvas opens it
  in its channel; clicking the PR opens it in the browser.

No PostHog data-model change was needed — both associations already exist.

Generated-By: PostHog Code
Task-Id: 51e058ce-6cca-43fa-b056-95ae0ff5f5b6
Title now occupies a single line on the left; #channel · time moves to the
right edge next to the artifact icons, balancing the row's visual weight.

Generated-By: PostHog Code
Task-Id: 86f54a5c-8435-47e9-b5e0-d94b6cc286ba
… icons

Recent tasks was a route the tab system couldn't represent — tabs are keyed
only on canvas/task/channel, so /website/recent-tasks fell through to a "New
tab" label and was dropped when switching tabs and back. Add a `routeId` to the
tab identity (schema + DB column/migration + nav decision) so a standalone
Channels-space route is a real tab: labelled "Recent tasks", deduped, and
restored across switches.

Also on the recent-tasks rows: move the canvas/PR artifact icons next to the
task name and give them colour-coded chips matching the channel
artifacts/history rows (canvas = violet, PR = green) so they read as artifacts
at a glance.

Generated-By: PostHog Code
Task-Id: 86f54a5c-8435-47e9-b5e0-d94b6cc286ba
The generated drizzle snapshot had multiline arrays that biome's formatter collapses to single lines, failing `biome ci` (quality check). Reformat to match biome's output.

Generated-By: PostHog Code
Task-Id: 087e1c07-151d-4eee-9de9-5a63ae6acaa5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant