Feat(webapp) chat AI UI improvements, new task landing pages and side menu#3941
Feat(webapp) chat AI UI improvements, new task landing pages and side menu#3941samejr wants to merge 145 commits into
Conversation
Conflicts: - AgentMessageView.tsx, AgentView.tsx: kept both sides. - schedules/route.tsx: deletion preserved; the purchase modal and usage bar have been moved into the Scheduled Task landing page with a new shared resource action route. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Important Review skippedToo many files! This PR contains 157 files, which is 7 over the limit of 150. To get a review, narrow the scope: ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (157)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Add FINAL + _is_deleted = 0 to all task_runs_v2 queries (ReplacingMergeTree) - Add organization_id + project_id filters to engage the sort-key prefix - Add inserted_at partition filter to llm_metrics_v1 queries Covers UnifiedTaskList, TaskDetail, AgentDetail (runs/sessions/LLM), TasksDashboard. Activity counts will read accurately and queries will prune partitions instead of full-environment scans. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both TaskListPresenter and AgentListPresenter now accept an optional pre-resolved currentWorker; UnifiedTaskListPresenter does the lookup once and passes it down, eliminating one redundant Postgres round-trip per page load. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
getLimit -> getLimits -> client.currentPlan() was duplicating the same upstream HTTP call that getCurrentPlan() also makes. Derive the schedules limit directly from currentPlan.v3Subscription.plan.limits.schedules and drop the redundant getLimit() call. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Without this, a single BackgroundWorker that happens to have both a STANDARD/SCHEDULED task and an AGENT task with the same slug would have its standard running count silently overwritten by the agent state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The JS side builds exactly 24 hour-bucket keys but CH's `now() - INTERVAL 24 HOUR` would return rows in 25 distinct hour buckets after the top of any hour. The oldest bucket was silently discarded. Align lower bound to `toStartOfHour(now() - INTERVAL 23 HOUR)` so CH returns exactly 24 buckets. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Observer + scroll listener now attach once on mount instead of on every streaming chunk (deps `[]`, not `[merged.length]`). Earlier this caused sustained CPU during fast streams. - Auto-scroll write deferred to the next animation frame so it runs after the virtualizer's layout-effect measures `getTotalSize()`. Without rAF the imperative scrollTop write races the virtualizer and the user gets "stuck half a row up" during streaming. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A shareable URL with a typo or stale kind name previously produced an unrecoverable "No tasks match your filters" state — the unrecognised strings became valid Set entries with no UI affordance to remove them. Filter unknown values out at the parse site. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds parseFiniteInt() to webapp searchParams utils. parseInt() accepts garbage-suffixed numbers and returns NaN for non-numeric input, both of which silently nudge time-range and pagination semantics. Apply at the agent, standard, and scheduled task landing-page loaders. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pendingResolutionsRef previously grew unbounded over the lifetime of the component — one entry per tool call, never removed. On chatty long-lived sessions that's a slow leak plus wasted JSON.stringify equality work on every `.out` chunk. Delete the buffered entry once the overlay has landed at a terminal output-available/error/denied state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The intentional omission of snapshotPresignedUrl and scheduleFlush from the dep array is well-commented but had no machine-readable suppression. Future refactors would silently lose the lint signal. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously the bundle stepper only re-synced when extraSchedules/stepSize props changed. Opening the modal, typing a value, cancelling, and reopening left the stale draft visible — confusing because the new total on the summary no longer matched the user's intent. The existing `lastSubmission as any` cast matches the codebase's established pattern (8 other sites) — left as-is rather than diverging. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The hasPrivateConnections feature-flag guard and the isUsingPlugin guard for Roles were commented out in a80dd96 (WIP cleanup) and never re-enabled. Route loaders still defend against unauthorised access (the Private Connections loader redirects, Roles renders an upsell empty state) but exposing both items unconditionally to users without the entitlements is a UX regression. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| const planLimit = currentPlan?.v3Subscription?.plan?.limits.schedules?.number; | ||
| const limit = typeof planLimit === "number" ? planLimit : 100_000_000; | ||
| const extraSchedules = currentPlan?.v3Subscription?.addOns?.schedules?.purchased ?? 0; | ||
| const canPurchaseSchedules = | ||
| currentPlan?.v3Subscription?.plan?.limits.schedules.canExceed === true; |
There was a problem hiding this comment.
🚩 ScheduleListPresenter getLimit replacement is equivalent but relies on platform API contract
The PR removes the getLimit RPC and inlines the same field access (currentPlan?.v3Subscription?.plan?.limits.schedules?.number). I verified that getLimit(orgId, "schedules", fallback) internally calls getLimits(orgId) → client.currentPlan(orgId) → returns result.v3Subscription?.plan?.limits.schedules.number — the exact same field. The downstream formula planScheduleLimit = limit - extraSchedules only makes semantic sense if the platform's plan.limits.schedules.number already includes purchased add-ons (so subtracting extras yields the base). If the platform API ever changes to return only the base plan number (without extras), this formula would silently produce incorrect values and block users from creating schedules they've paid for.
(Refers to lines 116-122)
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Acknowledged but no action — this coupling pre-dates the PR. Before the change, getLimit(orgId, "schedules", fallback) resolved via getLimits(orgId) → client.currentPlan(orgId) and returned result.v3Subscription?.plan?.limits.schedules.number — the exact same field this code now reads inline. The formula planScheduleLimit = limit - extraSchedules was already relying on the same platform contract. The PR only removes a duplicate upstream HTTP call. A future contract break would affect the original code identically.
| @@ -1,869 +0,0 @@ | |||
| import { useForm } from "@conform-to/react"; | |||
There was a problem hiding this comment.
📝 Info: Removed schedules listing route redirects to tasks page with filter
The old standalone /schedules index route was deleted and replaced with a thin redirect at schedules._index/route.tsx:14-22 that sends users to v3EnvironmentPath + ?types=SCHEDULED. This preserves discoverability for users with bookmarks or shared links. However, the redirect drops all original query params (filters, pagination) from the old URL — any bookmarked filtered-schedules URL will lose its filters. This is acceptable for a route migration but worth noting in release notes.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Fixed in de79e44 — added a thin redirect route at schedules._index/route.tsx that sends bookmarked /schedules URLs to the unified Tasks page with ?types=SCHEDULED. Individual schedule routes (/schedules/:id, /schedules/edit/:id, /schedules/new) are siblings and were never affected.
The standalone /schedules listing was removed when the unified Tasks page took over. Existing bookmarks / shared links 404'd. Add a thin redirect that lands users on the Tasks page pre-filtered to Scheduled. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🚩 Scheduled task page search.has() and search.del() rely on hook API surface
The scheduled task page at tasks.scheduled.$taskParam/route.tsx uses search.has("createSchedule") and search.del("schedule") on the return value of useSearchParams(). These methods are not destructured in other files in the diff (which only use value, values, replace, del). The has method in particular is novel in this PR. If useSearchParams doesn't expose a has method, this would be a runtime error. TypeScript should catch this, but it's worth verifying the hook's API surface if typecheck hasn't been run.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
No action — verified. The hook at apps/webapp/app/hooks/useSearchParam.ts:57-63 explicitly returns { value, values, replace, del, has }. has isn't new to this PR — it's used in runs._index/route.tsx:223 and errors/route.tsx:176 on main. Typecheck has been clean throughout, which would have caught any phantom property access.
Major dashboard restructure plus the new task landing pages and self-serve schedules add-on integration.
Side menu
dashboardPreferencesDashboardList.tsx)Tasks (
_index— unified Tasks page)UnifiedTaskListPresentercomposesTaskListPresenter+AgentListPresenter(sharedcurrentWorkerlookup)WORKER_CREATEDso onboardingtrigger devflips the blank state automaticallyAgent landing page (
/agents/$agentParam)AgentDetailPresenterqueries ClickHouse for run activity, session activity (with FINAL onsessions_v1), and LLM cost/token activity fromllm_metrics_v1ai-chat/overviewStandard Task landing page (
/tasks/standard/$taskParam)TaskDetailPresenterfor activity + propertiesScheduled Task landing page (
/tasks/scheduled/$taskParam)/scheduleslisting page during theorigin/mainmerge):grid-rows-[auto_1fr_auto]— progress ring + "X/Y of your schedules" + Purchase / Upgrade / Request CTAPurchaseSchedulesModalextracted as a shared component (apps/webapp/app/components/schedules/PurchaseSchedulesModal.tsx) handling increase / decrease / above-quota / need-to-delete states/resources/orgs/$organizationSlug/schedules-addonSessions
data-*parts grouped under "AI SDK data parts:" labeltoSafeUrlhelper guards rendered URLs from streamed contentPlayground (Test agent)
Dashboards
/dashboards) — Run metrics, AI metrics, Create your own CTAsBuiltInDashboardsupdated; newTasksDashboardPresenterfor the tasks overviewPageHeader / shared primitives
PageTitlegains anaccessoryprop supporting string (auto-wrapped in tooltip) and ReactNodeCardprimitive used for dashboard-style chart panels throughoutCode review fixes (last batch on this branch)
FINAL+_is_deleted = 0ontask_runs_v2(ReplacingMergeTree);organization_id+project_idfilters for sort-key prefix;inserted_atpartition filter onllm_metrics_v1UnifiedTaskListPresenter: sharedcurrentWorkerlookup; slug-collision guard inmergeRunningStates; off-by-one fixed in 24h bucket alignmentScheduleListPresenter: halved platform RPCs by deriving limit fromcurrentPlaninstead of callinggetLimitrequestAnimationFramedeferral on auto-scroll to avoid virtualizer race?types=validated against known kinds; newparseFiniteInthelper applied tofrom/to/pageparamsPurchaseSchedulesModal: bundle state resets on each open instead of persisting stale draftsManual testing
Manual smoke-test plan is tracked under TRI-10883, broken into 20 sub-issues covering onboarding, self-serve schedules, side menu, the four landing pages, sessions, runs, dashboards, regressions and performance.
🤖 Generated with Claude Code