-
Notifications
You must be signed in to change notification settings - Fork 56
Build out task list view #717
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📝 WalkthroughWalkthroughThis PR introduces a normalized task status data model, refactors the desktop TasksView from card-based to table-based layout, adds Linear workflow state synchronization, updates API routes to work with the new status structure, and includes a comprehensive semantic search planning document for future implementation. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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 |
🧹 Preview Cleanup CompleteThe following preview resources have been cleaned up:
Thank you for your contribution! 🎉 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 16
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/components/MemberActions/MemberActions.tsx (1)
21-30: Fix type mismatch:joinedAtis declared asDatebut receives a string from the database.The
created_atfield in theorganizationMemberstable is stored astext, not a Date object. InTeamSettings.tsx(line 39), this string value is assigned directly tojoinedAtwithout parsing:joinedAt: members.createdAt. While theformatDate()function accepts both types and masks the issue, the type contract is broken—MemberDetailsexportsjoinedAt: Date, but actual data is a string.Parse the date at the boundary when constructing
MemberDetailsobjects inTeamSettings.tsx:joinedAt: new Date(members.createdAt),Or revert
joinedAttostringin the interface if string dates are preferred throughout the codebase.apps/api/src/app/api/integrations/linear/webhook/route.ts (1)
114-119: Avoid logging user email.Per coding guidelines, logging user identifiers like emails is a compliance/privacy risk. Consider logging only the presence/absence of a match.
Proposed fix
let assigneeId: string | null = null; if (issue.assignee?.email) { const matchedUser = await db.query.users.findFirst({ where: eq(users.email, issue.assignee.email), }); assigneeId = matchedUser?.id ?? null; + if (!matchedUser) { + console.warn("[webhook] No matching user found for issue assignee"); + } }
🤖 Fix all issues with AI agents
In @apps/api/src/app/api/integrations/linear/callback/route.ts:
- Around line 52-54: The redirect_uri is hardcoded to a temporary ngrok URL
which will break OAuth in production; replace that literal in the Linear
callback handler (the redirect_uri property in the token exchange call in
route.ts, inside the callback handler function such as the POST/GET request
handler) with a dynamic value derived from your public base URL (e.g.,
process.env.NEXT_PUBLIC_API_URL or an existing getBaseUrl/getPublicUrl helper)
concatenated with "/api/integrations/linear/callback" so the URI matches the
environment and the registered Linear app.
- Around line 103-104: The code hardcodes qstashBaseUrl to a temporary ngrok URL
which will break QStash job execution; replace the hardcoded value in route.ts
by reading the real public API base URL from the environment (use
NEXT_PUBLIC_API_URL or the existing env accessor used elsewhere) when
constructing qstashBaseUrl, falling back to a sensible error or throw if the env
var is missing, so published QStash callbacks point to the stable production
host rather than the ngrok address.
In @apps/api/src/app/api/integrations/linear/connect/route.ts:
- Around line 48-49: Replace the hardcoded ngrok redirect URL in this Linear
connect route with the environment-driven URL referenced by NEXT_PUBLIC_API_URL
(as noted by the TODO) so the OAuth redirect uses the correct host in non-local
environments; update the redirect string construction in connect route.ts to
read process.env.NEXT_PUBLIC_API_URL (or the existing env accessor used in the
repo) and concatenate the "/api/integrations/linear/callback" path, and make the
same change in the corresponding callback/route.ts and sync-task/route.ts files
to remove any other hardcoded ngrok URLs.
In @apps/api/src/app/api/integrations/linear/jobs/initial-sync/route.ts:
- Around line 91-93: The loop uses a non-null assertion on status.externalId
which can insert null/undefined as a key or throw; update the logic around the
statuses iteration (where statusByExternalId is populated) to guard against
missing externalId by either filtering statuses beforehand or checking each
status (e.g., skip if status.externalId is null/undefined and optionally log or
collect invalid entries), then only call
statusByExternalId.set(status.externalId, status.id) when externalId is present;
reference the statuses collection and statusByExternalId population code to
apply this check.
- Around line 37-38: Replace the hardcoded ngrok URL in qstashBaseUrl with the
environment variable; remove the temporary string assignment and set
qstashBaseUrl = process.env.NEXT_PUBLIC_API_URL (or use your app config
accessor) so the code reads the deploy-specific base URL at runtime instead of
"https://b02ef5887783.ngrok-free.app"; keep the TODO removed/updated and ensure
any callers of qstashBaseUrl still work with the environment value.
In @apps/api/src/app/api/integrations/linear/jobs/sync-task/route.ts:
- Around line 176-177: Replace the hardcoded ngrok URL in the qstashBaseUrl
assignment with the environment-driven value (use NEXT_PUBLIC_API_URL via
process.env.NEXT_PUBLIC_API_URL or the existing env accessor used elsewhere in
this codebase) so QStash signature verification works in staging/production;
update the qstashBaseUrl variable in route.ts (and remove the TODO comment) to
read from the env and add a safe fallback or throw an error if the env var is
missing to avoid silent misconfiguration.
In
@apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx:
- Around line 36-47: handleSelectUser currently calls collections.tasks.update
without error handling; wrap the update call in a try/catch inside
handleSelectUser so failures are caught, ensure setOpen(false) still runs
appropriately (e.g., before or in finally), log the error (console.error or
existing logger) and surface a user-visible error (toast/notification) if
available; reference handleSelectUser, collections.tasks.update, task.id, and
assigneeId when making this change.
In
@apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsx:
- Around line 37-39: sortedStatuses currently uses statuses.sort(...) which
mutates the original statuses array (which may be a live-query result), causing
unexpected side effects; change the implementation in the useMemo for
sortedStatuses to sort a shallow copy instead (e.g., copy statuses first then
call sort with compareStatusesForDropdown) so the original statuses remains
unchanged, keeping the dependency on [statuses] intact and ensuring no in-place
mutation occurs.
- Around line 14-17: Remove the duplicate TaskWithStatus type declared in
StatusCell.tsx and import the canonical TaskWithStatus from where it's exported
(useTasksTable) instead; replace the local type declaration with an import like
`import { TaskWithStatus } from '.../useTasksTable'` and update any usage in
StatusCell (component, props, etc.) to reference the imported symbol so there’s
a single source of truth.
In
@apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/AssigneeMenuItems.tsx:
- Around line 19-26: The getInitials function can fail on empty or
whitespace-only names and may access undefined when parts are empty; update
getInitials to trim the input, return an empty string or a safe fallback for
falsy/empty inputs, split on whitespace and filter out empty segments, map each
segment to its first character only when present (guarding against
single-character segments), join, upper-case, and then slice to two characters
so it never reads undefined or produces unexpected results.
In @packages/db/drizzle/0008_add_full_task_status_table.sql:
- Around line 1-2: The migration contains a destructive statement DELETE FROM
"tasks" which will wipe all rows in the tasks table; either remove this
statement or guard it so it only runs in non-production environments and/or when
an explicit flag is set, and document the requirement to re-sync tasks from
Linear or warn about losing local-only tasks; locate and update the migration
containing the DELETE FROM "tasks" statement so it is conditional (e.g., check
an env var or use a migration parameter) or add a clear comment in the PR and
migration header describing that the delete is intentional and that tasks must
be re-synced from Linear and that any local-only tasks will be lost.
In @packages/db/src/schema/schema.ts:
- Around line 109-111: The foreign key definition for statusId
(uuid("status_id").notNull().references(() => taskStatuses.id)) is missing an
explicit onDelete action; update that FK to include an onDelete policy (e.g.,
.onDelete("cascade") if tasks should be removed when a status is removed, or
.onDelete("set null") if you prefer to preserve tasks with a null status) —
modify the statusId definition to append the chosen .onDelete(...) call so it
matches the pattern used by the other FKs.
In @packages/trpc/src/router/task/schema.ts:
- Line 8: The schema now requires statusId: z.string().uuid() so update all
create-task call sites to pass a valid UUID from the task_statuses collection;
specifically, modify UI task creation forms, API clients, import scripts, tests,
and the server-side createTask mutation invocations to include a statusId
(lookup or select a default statusId from task_statuses when rendering the form
or before sending requests). If you intended a fallback instead, change the
schema field or the createTask handler to accept an optional statusId and assign
the correct default by querying task_statuses (use the same UUID symbol expected
by the schema) before persisting.
🧹 Nitpick comments (33)
apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/LabelsCell/LabelsCell.tsx (1)
9-31: Consider extracting the magic number for visible labels limit.The value
2is repeated three times. Per coding guidelines, extracting it to a named constant improves readability and maintainability.✨ Optional improvement
+const MAX_VISIBLE_LABELS = 2; + export function LabelsCell({ info }: LabelsCellProps) { const currentLabels = info.getValue() || []; // Don't render anything if there are no labels if (currentLabels.length === 0) { return null; } return ( <div className="flex gap-1 flex-shrink-0"> - {currentLabels.slice(0, 2).map((label) => ( + {currentLabels.slice(0, MAX_VISIBLE_LABELS).map((label) => ( <Badge key={label} variant="outline" className="text-xs"> {label} </Badge> ))} - {currentLabels.length > 2 && ( + {currentLabels.length > MAX_VISIBLE_LABELS && ( <Badge variant="outline" className="text-xs"> - +{currentLabels.length - 2} + +{currentLabels.length - MAX_VISIBLE_LABELS} </Badge> )} </div> ); }apps/api/src/app/api/electric/[...path]/route.ts (1)
45-45: Add error handling for the upstream fetch.The
fetch()call can throw on network failures (DNS resolution, connection refused, timeouts). Without a try-catch, these errors propagate as generic 500s without helpful context.Per coding guidelines: never swallow errors silently—at minimum log them with context.
Proposed fix
- let response = await fetch(originUrl.toString()); + let response: Response; + try { + response = await fetch(originUrl.toString()); + } catch (error) { + console.error("[electric/proxy] Upstream fetch failed:", error); + return new Response("Failed to connect to Electric", { status: 502 }); + }apps/desktop/src/renderer/hooks/useHybridSearch.ts (1)
51-57: Consider using a distinct match type for empty query results.When the query is empty, returning
matchType: "exact"is semantically misleading since no actual matching occurred. Consider using a separate type like"all"or"none"to clarify these are unfiltered results.+type MatchType = "exact" | "fuzzy" | "all"; + interface SearchResult<T extends SearchableTask> { item: T; score: number; - matchType: "exact" | "fuzzy"; + matchType: MatchType; }Then update the empty query return:
return tasks.map((item) => ({ item, score: 1, - matchType: "exact" as const, + matchType: "all" as const, }));packages/ui/src/atoms/Avatar/Avatar.tsx (1)
57-59: Consider removing the passthrough wrapper.
AvatarImageis a pure passthrough that adds no functionality—consumers could importAvatarImageBasedirectly or use the base component. If this wrapper exists for API consistency or future extensibility, consider adding a brief comment explaining the intent.Option A: Remove if not needed
-function AvatarImage(props: React.ComponentProps<typeof AvatarImageBase>) { - return <AvatarImageBase {...props} />; -}Then re-export directly from the base:
// In index.ts export { AvatarImage } from "../../components/ui/avatar";Option B: Add explanatory comment if intentional
+// Re-exported for API consistency with Avatar and AvatarFallback function AvatarImage(props: React.ComponentProps<typeof AvatarImageBase>) { return <AvatarImageBase {...props} />; }apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTopBar/TasksTopBar.tsx (1)
44-47: Consider type-safe tab value handling.The cast
value as TabValueassumesonValueChangeonly receives valid tab values. SinceTABSdefines the allowed values, this should be safe, but you could add runtime validation for defense-in-depth:♻️ Suggested improvement
<Tabs value={currentTab} - onValueChange={(value) => onTabChange(value as TabValue)} + onValueChange={(value) => { + if (value === "all" || value === "active" || value === "backlog") { + onTabChange(value); + } + }} >apps/desktop/src/renderer/screens/main/components/ComponentsShowcase/index.tsx (2)
4-17: Consider extracting color constants for consistency.The hardcoded hex colors (e.g.,
#6B7280,#3B82F6) are repeated throughout this file and elsewhere in the codebase. For a showcase component this is acceptable, but extracting these to named constants at module top would improve maintainability and ensure consistency with actual status colors used in production.Example extraction
const STATUS_COLORS = { gray: "#6B7280", blue: "#3B82F6", red: "#EF4444", orange: "#F59E0B", green: "#10B981", purple: "#8B5CF6", } as const;
68-141: Repetitive type breakdown blocks could be simplified.The five type breakdown panels follow an identical structure. While this is presentational code and readability is acceptable, you could reduce duplication by mapping over a config array. This is a minor stylistic consideration for a showcase component.
apps/desktop/src/renderer/screens/main/index.tsx (1)
35-35: Import placement could follow alphabetical ordering.The
ComponentsShowcaseimport is placed betweenResizablePanelandSettingsView. For consistency, consider grouping alphabetically with other view components or placing it near the other view imports (SettingsView,StartView,TasksView, etc.).apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/constants.ts (1)
1-7: Consider stronger typing for priority keys.Using
Record<string, string>allows any string key, losing type safety. A const assertion or explicit union type would provide better autocomplete and catch typos at compile time.♻️ Suggested improvement for type safety
-export const PRIORITY_COLORS: Record<string, string> = { +export const PRIORITY_COLORS = { urgent: "bg-red-500", high: "bg-orange-500", medium: "bg-yellow-500", low: "bg-blue-500", none: "bg-gray-400", -}; +} as const; + +export type Priority = keyof typeof PRIORITY_COLORS;apps/api/src/app/api/integrations/linear/callback/route.ts (1)
111-112: Consider using prefixed logging pattern.Per coding guidelines, use the
[domain/operation]prefix pattern for console logging.💡 Suggested improvement
- console.error("Failed to queue initial sync job:", error); + console.error("[linear/callback] Failed to queue initial sync job:", error);apps/desktop/src/renderer/screens/main/components/TasksView/components/StatusIcon/constants.ts (1)
1-10: Consider adding a default fallback for unknown statuses.The mapping looks good and follows the guideline of extracting hardcoded values to named constants. However, if a status name doesn't match any key in this map,
undefinedwill be returned, which could cause missing styles.Consider adding a helper function or documenting the expected fallback behavior for consumers:
💡 Optional: Add a helper with fallback
export const STATUS_COLORS: Record<string, string> = { backlog: "text-gray-400", todo: "text-blue-400", planning: "text-purple-400", working: "text-yellow-400", "needs-feedback": "text-orange-400", "ready-to-merge": "text-green-400", completed: "text-green-600", canceled: "text-red-400", }; export const DEFAULT_STATUS_COLOR = "text-gray-400"; export function getStatusColor(status: string): string { return STATUS_COLORS[status] ?? DEFAULT_STATUS_COLOR; }apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx (1)
49-56: ExtractgetInitialsoutside component and handle edge cases.This utility is recreated on every render. Additionally, it may return unexpected results for empty names or names with extra spaces.
Proposed refactor
+// Module-level utility - no component dependencies +function getInitials(name: string): string { + return name + .trim() + .split(/\s+/) + .filter(Boolean) + .map((n) => n[0]) + .join("") + .toUpperCase() + .slice(0, 2) || "?"; +} + export function AssigneeCell({ info }: AssigneeCellProps) { // ... - const getInitials = (name: string) => { - return name - .split(" ") - .map((n) => n[0]) - .join("") - .toUpperCase() - .slice(0, 2); - };apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx (2)
5-9: Consider extracting sharedMenuItemPropsinterface.This interface is duplicated in
PriorityMenuItems.tsx. Extract to a shared types file to avoid drift.
34-38: Sameas anytype casting issue.Consider defining a proper type for status types to avoid bypassing type safety.
apps/desktop/src/renderer/screens/main/components/TasksView/components/TaskContextMenu/TaskContextMenu.tsx (3)
85-91: Clipboard operations may fail without feedback.
navigator.clipboard.writeTextreturns a Promise that can reject (e.g., permission denied). Consider handling errors and providing user feedback.Proposed fix
-const handleCopyId = () => { - navigator.clipboard.writeText(task.slug); -}; +const handleCopyId = async () => { + try { + await navigator.clipboard.writeText(task.slug); + } catch (error) { + console.error("[TaskContextMenu] Failed to copy ID:", error); + } +}; -const handleCopyTitle = () => { - navigator.clipboard.writeText(task.title); -}; +const handleCopyTitle = async () => { + try { + await navigator.clipboard.writeText(task.title); + } catch (error) { + console.error("[TaskContextMenu] Failed to copy title:", error); + } +};
158-164: UseonSelectinstead ofonClickfor consistency.Other
ContextMenuItemusages in this codebase useonSelect. This maintains consistency and ensures proper menu behavior (auto-close on select).Proposed fix
-<ContextMenuItem onClick={handleCopyId}> +<ContextMenuItem onSelect={handleCopyId}> <span>Copy ID</span> </ContextMenuItem> -<ContextMenuItem onClick={handleCopyTitle}> +<ContextMenuItem onSelect={handleCopyTitle}> <span>Copy Title</span> </ContextMenuItem>
169-175: Consider guarding against missingonDeletecallback.The delete button renders unconditionally but
onDeleteis optional. Consider hiding the button or disabling it when no handler is provided.Proposed fix
+{onDelete && ( + <> + <ContextMenuSeparator /> <ContextMenuItem onClick={onDelete} className="text-destructive focus:text-destructive" > <HiOutlineTrash className="text-destructive size-4" /> <span>Delete</span> </ContextMenuItem> + </> +)}apps/desktop/src/renderer/screens/main/components/TasksView/TasksView.tsx (1)
26-29: Add accessibility attributes to the loading spinner.The loading spinner should have appropriate ARIA attributes for screen reader users.
♿ Accessibility improvement
{isLoading ? ( <div className="flex-1 flex items-center justify-center"> - <div className="h-5 w-5 animate-spin rounded-full border-2 border-muted-foreground border-t-transparent" /> + <div + className="h-5 w-5 animate-spin rounded-full border-2 border-muted-foreground border-t-transparent" + role="status" + aria-label="Loading tasks" + /> </div>apps/api/src/app/api/integrations/linear/jobs/initial-sync/syncWorkflowStates.ts (1)
2-2: Remove unused imports.The
andandeqimports fromdrizzle-ormare not used in this file.🧹 Remove unused imports
import type { LinearClient } from "@linear/sdk"; -import { and, eq } from "drizzle-orm"; import { db } from "@superset/db/client";apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/PriorityCell.tsx (2)
41-48: Consider closing dropdown on error and providing user feedback.When the update fails, the dropdown remains open and the user has no indication that the change didn't apply. Consider closing the dropdown and potentially showing an error toast.
💡 Improved error handling
try { collections.tasks.update(task.id, (draft) => { draft.priority = newPriority; }); setOpen(false); } catch (error) { console.error("[PriorityCell] Failed to update priority:", error); + setOpen(false); + // Consider: show error toast to user }
54-56: Add explicittype="button"to prevent form submission.Buttons default to
type="submit"which can cause unexpected form submissions if this component is used within a form context.🔧 Add button type
<button + type="button" className="group p-0 cursor-pointer border-0 transition-all" title={PRIORITY_LABELS[currentPriority]} >apps/desktop/src/renderer/screens/main/components/TasksView/components/PriorityIcon/PriorityIcon.tsx (1)
56-56: Extract hardcoded color to constant for consistency.The hardcoded
#F97316could be replaced with a Tailwind color reference for consistency with the rest of the component.🎨 Use Tailwind color constant
+import colors from "tailwindcss/colors"; + +const URGENT_ACTIVE_COLOR = colors.orange[500]; // #F97316 + // In component: - const fillColor = color || (isActive ? "#F97316" : colors.neutral[500]); + const fillColor = color || (isActive ? URGENT_ACTIVE_COLOR : colors.neutral[500]);apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/TasksTableView.tsx (3)
43-46: Track the TODO for delete implementation.The delete handler is currently a placeholder. Consider creating an issue to track this work.
Would you like me to help implement the delete functionality or open an issue to track this task?
54-55: Document the assumption about column ordering.The code assumes the status column is always at index 0. If column definitions change, this could silently break the rendering.
📝 Add clarifying comment or use named lookup
- {/* Skip status column (index 0), render checkbox, priority, id, title+labels, assignee, created */} - {cells.slice(1).map((cell) => ( + {/* Skip status column (index 0) - this assumes useTasksTable defines status as the first column */} + {/* Column order: [status], checkbox, priority, id, title+labels, assignee, created */} + {cells.slice(1).map((cell) => (
48-52: Consider adding keyboard navigation for accessibility.Table rows should be keyboard navigable. The current implementation relies on hover styles but lacks focus indicators and keyboard event handlers.
♿ Add keyboard accessibility
<div className="grid items-center gap-3 px-4 h-9 hover:bg-accent/50 cursor-pointer border-b border-border/50" + tabIndex={0} + role="row" + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + // Handle row activation + } + }} style={{ gridTemplateColumns: `auto auto ${slugColumnWidth} 1fr auto auto`, }} >apps/desktop/src/renderer/screens/main/components/TasksView/utils/taskSorting.ts (1)
110-120: JSDoc comment slightly inconsistent with PRIORITY_ORDER.The comment states "urgent → high → medium → low → none" which matches PRIORITY_ORDER, but
ALL_PRIORITIES(used in dropdowns) has a different order starting with "none". This is intentional per line 30 comment, but worth clarifying.📝 Clarify the ordering difference
/** * Compare two priorities for dropdown sorting. - * Sort order: urgent → high → medium → low → none - * (Same order as task sorting) + * Sort order: urgent → high → medium → low → none (same as task sorting) + * Note: This differs from ALL_PRIORITIES display order which starts with "none" */ export function comparePrioritiesForDropdown(packages/db/drizzle/meta/0008_snapshot.json (1)
1631-1644: Consider removing the orphanedtask_statusenum in a follow-up migration.The
task_statusenum is no longer actively used after migration 0008, which introduced thetask_statusestable. The tasks table now uses astatusIdforeign key to referencetask_statuses.idinstead. The enum remains defined in the schema but is orphaned. Removing it would clean up technical debt and reduce schema confusion.apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (2)
105-120: Extract magic numbers to named constants.The width calculation uses hardcoded values that should be extracted for clarity and maintainability.
Suggested improvement
+// Monospace font-mono at text-xs (0.75rem = 12px) +const FONT_SIZE_REM = 0.75; +const REM_PER_CHAR = 0.5 * FONT_SIZE_REM; // ~0.375rem per character +const SLUG_COLUMN_PADDING_REM = 0.5; +const DEFAULT_SLUG_WIDTH = "5rem"; + // Calculate optimal slug column width based on longest slug const slugColumnWidth = useMemo(() => { - if (!data || data.length === 0) return "5rem"; // Default fallback + if (!data || data.length === 0) return DEFAULT_SLUG_WIDTH; const longestSlug = data.reduce((longest, task) => { return task.slug.length > longest.length ? task.slug : longest; }, ""); - // Monospace font-mono at text-xs (0.75rem = 12px) - // Each character is ~0.5em of the font size = 0.5 * 0.75rem = 0.375rem per char - const remPerChar = 0.5 * 0.75; // 0.375rem per character - const padding = 0.5; // rem for horizontal padding - const width = longestSlug.length * remPerChar + padding; + const width = longestSlug.length * REM_PER_CHAR + SLUG_COLUMN_PADDING_REM; return `${Math.ceil(width * 10) / 10}rem`; // Round to 1 decimal }, [data]);
164-169: Type assertionas anybypasses type safety.The
status.typeis cast toanywhen passed toStatusIcon. Consider using theStatusTypeunion directly to maintain type safety.Suggested fix
+import type { StatusType } from "../../components/StatusIcon"; + <StatusIcon - type={status.type as any} + type={status.type as StatusType} color={status.color} progress={status.progressPercent ?? undefined} />Alternatively, ensure the
status.typecolumn in the database is properly typed to match theStatusTypeunion, which would eliminate the need for the cast entirely.apps/desktop/src/renderer/screens/main/components/TasksView/components/StatusIcon/StatusIcon.tsx (1)
10-16: Consider exportingStatusIconPropsinterface for consumer components.The interface is currently private but may be useful for type-checking in components that compose or wrap
StatusIcon.Suggested change
-interface StatusIconProps { +export interface StatusIconProps { type: StatusType; color: string; showHover?: boolean; className?: string; progress?: number; }apps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.ts (1)
120-129: Extract the three-month filter to a named constant.The hardcoded filter duration should be a constant for maintainability and clarity.
Suggested improvement
+const SYNC_LOOKBACK_MONTHS = 3; + export async function fetchAllIssues( client: LinearClient, ): Promise<LinearIssue[]> { const allIssues: LinearIssue[] = []; let cursor: string | undefined; - const threeMonthsAgo = subMonths(new Date(), 3); + const cutoffDate = subMonths(new Date(), SYNC_LOOKBACK_MONTHS); do { const response = await client.client.request< IssuesQueryResponse, { first: number; after?: string; filter: object } >(ISSUES_QUERY, { first: 100, after: cursor, - filter: { updatedAt: { gte: threeMonthsAgo.toISOString() } }, + filter: { updatedAt: { gte: cutoffDate.toISOString() } }, });packages/db/src/schema/schema.ts (1)
69-69: Consider using an enum for thetypecolumn.The
typefield usestext()but has a fixed set of valid values ("backlog" | "unstarted" | "started" | "completed" | "cancelled"). Using apgEnumwould provide database-level validation.Suggested improvement
+export const taskStatusType = pgEnum("task_status_type", [ + "backlog", + "unstarted", + "started", + "completed", + "cancelled", +]); + export const taskStatuses = pgTable( "task_statuses", { // ... other fields - type: text().notNull(), // "backlog" | "unstarted" | "started" | "completed" | "canceled" + type: taskStatusType().notNull(), // ... },This would catch invalid values at the database level rather than relying on application-level validation.
apps/desktop/docs/SEMANTIC_SEARCH_PLAN.md (1)
11-11: Add language specifier to fenced code block.The code block on line 11 lacks a language specifier. Since it's a text-based architecture diagram, you can use
textorplaintext.Suggested fix
-``` +```text Server (Linear Sync): Linear task → generate embedding → store in DB
| const teams = await client.teams(); | ||
|
|
||
| for (const team of teams.nodes) { | ||
| console.log(`[syncWorkflowStates] Processing team: ${team.name}`); | ||
|
|
||
| const states = await team.states(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for Linear API calls.
The client.teams() and team.states() calls can fail due to network issues, rate limits, or API errors. Without try-catch, failures will propagate as unhandled rejections and could abort the entire sync process.
🔧 Proposed fix with error handling
console.log("[syncWorkflowStates] Fetching teams");
- const teams = await client.teams();
+ let teams;
+ try {
+ teams = await client.teams();
+ } catch (error) {
+ console.error("[syncWorkflowStates] Failed to fetch teams:", error);
+ throw error;
+ }
for (const team of teams.nodes) {
console.log(`[syncWorkflowStates] Processing team: ${team.name}`);
- const states = await team.states();
+ let states;
+ try {
+ states = await team.states();
+ } catch (error) {
+ console.error(`[syncWorkflowStates] Failed to fetch states for team ${team.name}:`, error);
+ continue; // Skip this team and continue with others
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const teams = await client.teams(); | |
| for (const team of teams.nodes) { | |
| console.log(`[syncWorkflowStates] Processing team: ${team.name}`); | |
| const states = await team.states(); | |
| let teams; | |
| try { | |
| teams = await client.teams(); | |
| } catch (error) { | |
| console.error("[syncWorkflowStates] Failed to fetch teams:", error); | |
| throw error; | |
| } | |
| for (const team of teams.nodes) { | |
| console.log(`[syncWorkflowStates] Processing team: ${team.name}`); | |
| let states; | |
| try { | |
| states = await team.states(); | |
| } catch (error) { | |
| console.error(`[syncWorkflowStates] Failed to fetch states for team ${team.name}:`, error); | |
| continue; // Skip this team and continue with others | |
| } |
apps/api/src/app/api/integrations/linear/jobs/sync-task/route.ts
Outdated
Show resolved
Hide resolved
...r/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsx
Outdated
Show resolved
Hide resolved
...r/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsx
Show resolved
Hide resolved
...sktop/src/renderer/screens/main/components/TasksView/components/shared/AssigneeMenuItems.tsx
Outdated
Show resolved
Hide resolved
| title: z.string().min(1), | ||
| description: z.string().nullish(), | ||
| status: z.string().min(1).default("Backlog"), | ||
| statusId: z.string().uuid(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cat packages/trpc/src/router/task/schema.tsRepository: superset-sh/superset
Length of output: 1164
🏁 Script executed:
# Search for usages of createTask/create mutations to see if they now provide statusId
rg -n 'createTask|create\.mutate' --type ts --type tsx -A 3 -B 1Repository: superset-sh/superset
Length of output: 91
🏁 Script executed:
# Also search for any default status values or status field assignments
rg -n 'status.*=|default.*status|statusId' --type ts --type tsx packages/trpc/src/router/task/ -A 2 -B 1Repository: superset-sh/superset
Length of output: 91
🏁 Script executed:
# Search for task creation/mutation handlers and usages without tsx flag
rg -n 'createTask|mutate.*create' --type ts -A 5 -B 1 | head -100Repository: superset-sh/superset
Length of output: 3310
🏁 Script executed:
# Look for task router mutations that would use createTaskSchema
fd . packages/trpc/src/router/task/ --type f | head -20Repository: superset-sh/superset
Length of output: 180
🏁 Script executed:
# Check the task router to see how createTaskSchema is used
cat packages/trpc/src/router/task/index.ts 2>/dev/null || cat packages/trpc/src/router/task.ts 2>/dev/null || find packages/trpc/src/router/task -name '*.ts' | xargs catRepository: superset-sh/superset
Length of output: 100
🏁 Script executed:
# Get the full create mutation implementation
sed -n '74,120p' packages/trpc/src/router/task/task.tsRepository: superset-sh/superset
Length of output: 1007
🏁 Script executed:
# Search for places where task.create is called to verify statusId is provided
rg -n '\.create\.mutate|task\.create' --type ts -B 2 -A 5 | head -150Repository: superset-sh/superset
Length of output: 1960
🏁 Script executed:
# Get more context about the CollectionsProvider and task initialization
sed -n '1,100p' apps/desktop/src/renderer/contexts/CollectionsProvider/collections.tsRepository: superset-sh/superset
Length of output: 2861
🏁 Script executed:
# Look for task status defaults or initial state definitions
rg -n 'status|Status' apps/desktop/src/renderer/contexts/CollectionsProvider/ --type ts -B 2 -A 2Repository: superset-sh/superset
Length of output: 2559
🏁 Script executed:
# Search for where CollectionsProvider is used and tasks are created in the UI
rg -n 'task\|Task' apps/desktop/src/renderer/ --type ts -l | head -20Repository: superset-sh/superset
Length of output: 46
Breaking change: statusId is now required with no default value.
Previously status had implicit handling. Now statusId requires a valid UUID from callers. The schema enforces this at the tRPC level—any request without a valid statusId will be rejected. Verify that all create task flows (UI forms, imports, etc.) supply a valid statusId from the task_statuses collection.
🤖 Prompt for AI Agents
In @packages/trpc/src/router/task/schema.ts at line 8, The schema now requires
statusId: z.string().uuid() so update all create-task call sites to pass a valid
UUID from the task_statuses collection; specifically, modify UI task creation
forms, API clients, import scripts, tests, and the server-side createTask
mutation invocations to include a statusId (lookup or select a default statusId
from task_statuses when rendering the form or before sending requests). If you
intended a fallback instead, change the schema field or the createTask handler
to accept an optional statusId and assign the correct default by querying
task_statuses (use the same UUID symbol expected by the schema) before
persisting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/api/src/app/api/integrations/linear/callback/route.ts (1)
33-35: Unhandled exception risk:JSON.parsecan throw on malformed state.If the base64-decoded
stateis not valid JSON,JSON.parsewill throw beforesafeParsecan catch it, resulting in a 500 error instead of a graceful redirect.🐛 Proposed fix
- const parsed = stateSchema.safeParse( - JSON.parse(Buffer.from(state, "base64url").toString("utf-8")), - ); + let parsed: z.SafeParseReturnType<z.infer<typeof stateSchema>, z.infer<typeof stateSchema>>; + try { + parsed = stateSchema.safeParse( + JSON.parse(Buffer.from(state, "base64url").toString("utf-8")), + ); + } catch { + return Response.redirect( + `${env.NEXT_PUBLIC_WEB_URL}/integrations/linear?error=invalid_state`, + ); + }
🤖 Fix all issues with AI agents
In @apps/api/src/app/api/integrations/linear/callback/route.ts:
- Around line 52-54: Replace the hardcoded ngrok redirect_uri with a build from
the NEXT_PUBLIC_API_URL environment variable: use
process.env.NEXT_PUBLIC_API_URL + "/api/integrations/linear/callback" (or the
equivalent runtime env accessor in this code) in place of the literal string for
redirect_uri so the OAuth token exchange works across environments; locate the
redirect_uri assignment in route.ts and remove the ngrok URL, constructing the
redirect URI from NEXT_PUBLIC_API_URL instead.
- Around line 104-105: The qstashBaseUrl is hardcoded to a temporary ngrok URL;
revert this by replacing the literal "https://b02ef5887783.ngrok-free.app" in
the callback route where qstashBaseUrl is defined with the environment-backed
value (use process.env.NEXT_PUBLIC_API_URL or the existing env accessor used
elsewhere for redirect_uri) so QStash jobs use the correct runtime API URL
across environments; ensure the variable qstashBaseUrl derives from that
environment variable and falls back to a safe default only if explicitly
intended for local testing.
In @apps/api/src/app/api/integrations/linear/jobs/initial-sync/route.ts:
- Around line 37-38: The qstashBaseUrl is hardcoded to a temporary ngrok URL;
change it to read from the environment (use NEXT_PUBLIC_API_URL /
process.env.NEXT_PUBLIC_API_URL or the same env variable used in
sync-task/route.ts) so production doesn't depend on ngrok, and remove or update
the TODO comment accordingly; update the variable named qstashBaseUrl in
route.ts and any matching usage so it uses the env value with a safe fallback if
needed.
In
@apps/api/src/app/api/integrations/linear/jobs/initial-sync/syncWorkflowStates.ts:
- Around line 10-15: The normalizeStateType function currently converts Linear's
"canceled" to "cancelled" (UK spelling) but the surrounding comment and the
schema at packages/db/src/schema/schema.ts:68 claim US spelling; update the
misleading comment to state that the function normalizes Linear's "canceled" to
the stored UK spelling ("cancelled") or, alternatively, change the function to
preserve US spelling and return "canceled" consistently; then update the schema
documentation at packages/db/src/schema/schema.ts to match the chosen canonical
spelling so code, comments, and schema are consistent (refer to
normalizeStateType).
In @apps/api/src/app/api/integrations/linear/jobs/sync-task/route.ts:
- Around line 180-181: The qstashBaseUrl variable is hardcoded to an ngrok URL
which will break production; replace the hardcoded string assigned to
qstashBaseUrl with the environment-backed value (use NEXT_PUBLIC_API_URL or the
intended env var referenced in the TODO) so the code reads qstashBaseUrl =
process.env.NEXT_PUBLIC_API_URL (or the configured env var) and remove the
temporary ngrok literal; ensure the code handles a missing env value (fail fast
or throw a clear error) so deployment won’t silently use an incorrect URL.
In
@apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx:
- Around line 36-47: The update call in handleSelectUser doesn't handle errors;
wrap the collections.tasks.update(task.id, ...) call in a try/catch (inside
handleSelectUser) and on error call the same error reporting used elsewhere
(e.g., the logger or UI error handler you use in PriorityCell) while still
calling setOpen(false) as appropriate; ensure you reference handleSelectUser,
collections.tasks.update, draft.assigneeId and task.id so the change is applied
to the correct update and do not swallow the exception.
- Around line 49-56: The getInitials function can throw when name is empty or
has consecutive spaces; move getInitials to module scope and update it to
defensively handle falsy or malformed input by trimming the string, splitting on
whitespace, filtering out empty segments, taking the first character of each
valid segment, defaulting to an empty string when no valid characters exist,
joining, uppercasing, and slicing to two chars; ensure callers of getInitials in
AssigneeCell use this new module-level helper.
In
@apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsx:
- Around line 37-39: The sortedStatuses useMemo is calling statuses.sort which
mutates the original array; change the memo to sort a copy instead (e.g., create
a shallow copy of statuses before sorting) so the original statuses/allStatuses
array is not mutated; update the useMemo that creates sortedStatuses
(referencing sortedStatuses, useMemo, statuses, and compareStatusesForDropdown)
to spread or otherwise clone statuses before calling sort.
In
@apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx:
- Around line 34-38: Replace the unsafe "as any" cast on the StatusIcon props by
importing and using the correct StatusType: import StatusType from the
StatusIcon module (or named export) and change type={status.type as any} to
type={status.type as StatusType}; ensure the import matches how StatusType is
exported from StatusIcon and remove the "as any" cast so the prop uses the real
StatusType type.
In
@apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/TasksTableView.tsx:
- Around line 39-41: The inline comment in TasksTableView.tsx is misleading: it
lists the column as "Due" but the table (from useTasksTable) actually uses the
createdAt/Created column; update the comment text in TasksTableView (near the
leaf row/grid layout comment) to replace "Due" with "Created" (or "Created At")
so it accurately reflects the createdAt column used by useTasksTable and avoids
confusion.
In
@apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx:
- Around line 163-167: Remove the unsafe cast and use the actual union type:
stop casting status.type with "as any" in the StatusIcon props; import the
exported StatusType union from the StatusIcon module and type status.type as
StatusType (or update SelectTaskStatus.type in the schema so it already matches
StatusType), then pass status.type directly to the StatusIcon component along
with color and progress.
🧹 Nitpick comments (12)
apps/desktop/src/renderer/screens/main/components/ComponentsShowcase/index.tsx (1)
132-132: Consider responsive grid for narrow viewports.Unlike the "All Variants" section that uses responsive column classes, the Hover States (line 132) and Progress Variants (line 197) grids use fixed
grid-cols-5. On narrow viewports, this may cause cramped layouts.♻️ Optional: Add responsive breakpoints
-<div className="grid grid-cols-5 gap-4 p-4 rounded-lg border border-border bg-card"> +<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-4 p-4 rounded-lg border border-border bg-card">Also applies to: 197-197
apps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsx (1)
52-58: Consider extractingformatDateoutside the component.The function is recreated on each render. Since it's a pure utility with no dependencies on component state or props, extracting it outside the component improves performance slightly and makes it reusable.
+const formatDate = (date: Date | string) => { + const d = date instanceof Date ? date : new Date(date); + return d.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + }); +}; + export function TeamSettings() { const { session } = useAuth(); const collections = useCollections(); // ... - const formatDate = (date: Date | string) => { - const d = date instanceof Date ? date : new Date(date); - return d.toLocaleDateString("en-US", { - month: "short", - day: "numeric", - }); - };apps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.ts (3)
35-38: ExportWorkflowStateWithPositioninterface.This interface is used by
syncWorkflowStates.ts(line 45) but is not exported. While TypeScript may infer the type, explicit export improves API clarity.♻️ Proposed fix
-interface WorkflowStateWithPosition { +export interface WorkflowStateWithPosition { name: string; position: number; }
158-161: Consider graceful handling instead of throwing.Throwing here will abort the entire sync if any issue references an unknown state. This could happen if a state was created between
syncWorkflowStatesandfetchAllIssues. Consider logging and skipping the issue, or returning a sentinel value.♻️ Alternative: skip issues with missing status
const statusId = statusByExternalId.get(issue.state.id); if (!statusId) { - throw new Error(`Status not found for state ${issue.state.id}`); + console.warn(`[mapIssueToTask] Status not found for state ${issue.state.id}, skipping issue ${issue.identifier}`); + return null; }Then filter nulls in the caller:
const taskValues = issues .map((issue) => mapIssueToTask(...)) .filter((t): t is NonNullable<typeof t> => t !== null);
126-135: Good addition of time-based filtering.Filtering issues to the last 3 months reduces sync load. Consider extracting
3as a named constant for clarity.const SYNC_LOOKBACK_MONTHS = 3; const threeMonthsAgo = subMonths(new Date(), SYNC_LOOKBACK_MONTHS);apps/desktop/src/renderer/hooks/useHybridSearch.ts (2)
49-80: Remove redundanttasksfrom the dependency array.The
tasksdependency is redundant sinceexactFuseandfuzzyFusealready depend ontasks. This won't cause bugs but creates unnecessary callback recreation.Proposed fix
const search = useCallback( (query: string): SearchResult<T>[] => { // ... implementation }, - [exactFuse, fuzzyFuse, tasks], + [exactFuse, fuzzyFuse], );
51-57: Consider a distinctmatchTypefor unfiltered results.Labeling all tasks as
"exact"when there's no query is semantically misleading. Consider adding an"all"or"unfiltered"match type, or documenting this behavior.apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsx (2)
14-17: Remove duplicate type definition – import fromuseTasksTable.
TaskWithStatusis already exported from../../../hooks/useTasksTable. Importing it avoids duplication and ensures consistency.Proposed fix
import type { SelectTask, SelectTaskStatus } from "@superset/db/schema"; +import type { TaskWithStatus } from "../../../hooks/useTasksTable"; // ... -// Task with joined status data -type TaskWithStatus = SelectTask & { - status: SelectTaskStatus; -};
61-64: Avoidas anycasts forstatus.type.Same issue as in
StatusMenuItems.tsx. Use the properStatusTypefrom the schema/enums to maintain type safety. As per coding guidelines, avoid using theanytype.Also applies to: 77-81
apps/desktop/src/renderer/screens/main/components/TasksView/components/TaskContextMenu/TaskContextMenu.tsx (1)
89-95: Add error handling for clipboard operations.
navigator.clipboard.writeTextreturns a Promise and can fail (e.g., permissions denied, non-secure context). Consider adding error handling for consistency with other operations.Proposed fix
- const handleCopyId = () => { - navigator.clipboard.writeText(task.slug); + const handleCopyId = async () => { + try { + await navigator.clipboard.writeText(task.slug); + } catch (error) { + console.error("[TaskContextMenu] Failed to copy ID:", error); + } }; - const handleCopyTitle = () => { - navigator.clipboard.writeText(task.title); + const handleCopyTitle = async () => { + try { + await navigator.clipboard.writeText(task.title); + } catch (error) { + console.error("[TaskContextMenu] Failed to copy title:", error); + } };apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/TasksTableView.tsx (1)
49-52: Use prefixed console logging with context pattern.As per the coding guidelines, console logs should follow the
[domain/operation] messagepattern for traceability.Suggested fix
onDelete={() => { // TODO: Implement delete - console.log("Delete task:", task.id); + console.log("[TasksTableView/delete] Task:", task.id); }}apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (1)
103-118: Extract magic numbers to named constants.Per coding guidelines, magic numbers should be extracted to named constants at module top for maintainability.
Suggested refactor
+// Slug column width calculation constants +const SLUG_FONT_SIZE_REM = 0.75; // text-xs = 0.75rem = 12px +const MONOSPACE_CHAR_WIDTH_EM = 0.5; // Each monospace char ~0.5em +const SLUG_PADDING_REM = 0.5; +const DEFAULT_SLUG_WIDTH = "5rem"; + // Calculate optimal slug column width based on longest slug const slugColumnWidth = useMemo(() => { - if (!data || data.length === 0) return "5rem"; // Default fallback + if (!data || data.length === 0) return DEFAULT_SLUG_WIDTH; const longestSlug = data.reduce((longest, task) => { return task.slug.length > longest.length ? task.slug : longest; }, ""); - // Monospace font-mono at text-xs (0.75rem = 12px) - // Each character is ~0.5em of the font size = 0.5 * 0.75rem = 0.375rem per char - const remPerChar = 0.5 * 0.75; // 0.375rem per character - const padding = 0.5; // rem for horizontal padding + const remPerChar = MONOSPACE_CHAR_WIDTH_EM * SLUG_FONT_SIZE_REM; - const width = longestSlug.length * remPerChar + padding; + const width = longestSlug.length * remPerChar + SLUG_PADDING_REM; return `${Math.ceil(width * 10) / 10}rem`; // Round to 1 decimal }, [data]);
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (35)
apps/api/src/app/api/electric/[...path]/utils.tsapps/api/src/app/api/integrations/linear/callback/route.tsapps/api/src/app/api/integrations/linear/jobs/initial-sync/route.tsapps/api/src/app/api/integrations/linear/jobs/initial-sync/syncWorkflowStates.tsapps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.tsapps/api/src/app/api/integrations/linear/jobs/sync-task/route.tsapps/api/src/app/api/integrations/linear/webhook/route.tsapps/desktop/src/renderer/hooks/useHybridSearch.tsapps/desktop/src/renderer/screens/main/components/ComponentsShowcase/index.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsxapps/desktop/src/renderer/screens/main/components/TasksView/TasksView.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/PriorityIcon/PriorityIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/StatusIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/TaskContextMenu/TaskContextMenu.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/TasksTableView.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTopBar/TasksTopBar.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTopBar/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/LabelsCell/LabelsCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/icons/ActiveIcon/ActiveIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/icons/AllIssuesIcon/AllIssuesIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/icons/AssigneeMenuIcon/AssigneeMenuIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/icons/BacklogIcon/BacklogIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/AssigneeMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/index.tsxapps/desktop/src/renderer/stores/app-state.tspackages/db/src/schema/relations.tspackages/ui/src/atoms/Avatar/Avatar.tsxpackages/ui/src/atoms/Avatar/index.ts
🚧 Files skipped from review as they are similar to previous changes (16)
- apps/desktop/src/renderer/screens/main/components/TasksView/components/StatusIcon/index.ts
- apps/desktop/src/renderer/screens/main/components/TasksView/components/icons/ActiveIcon/ActiveIcon.tsx
- apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/AssigneeMenuItems.tsx
- apps/desktop/src/renderer/screens/main/components/TasksView/components/icons/AllIssuesIcon/AllIssuesIcon.tsx
- packages/ui/src/atoms/Avatar/index.ts
- apps/desktop/src/renderer/screens/main/components/TasksView/components/PriorityIcon/PriorityIcon.tsx
- apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityMenuItems.tsx
- apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTopBar/index.ts
- apps/desktop/src/renderer/screens/main/components/TasksView/components/icons/AssigneeMenuIcon/AssigneeMenuIcon.tsx
- apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTopBar/TasksTopBar.tsx
- apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/index.ts
- packages/ui/src/atoms/Avatar/Avatar.tsx
- apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/LabelsCell/LabelsCell.tsx
- apps/desktop/src/renderer/screens/main/index.tsx
- apps/api/src/app/api/integrations/linear/webhook/route.ts
- apps/api/src/app/api/electric/[...path]/utils.ts
🧰 Additional context used
📓 Path-based instructions (8)
apps/desktop/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)
apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined insrc/lib/trpc
Use alias as defined intsconfig.jsonwhen possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from@trpc/server/observableinstead of async generators, as the library explicitly checksisObservable(result)and throws an error otherwise
Files:
apps/desktop/src/renderer/stores/app-state.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/icons/BacklogIcon/BacklogIcon.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TaskContextMenu/TaskContextMenu.tsxapps/desktop/src/renderer/screens/main/components/TasksView/TasksView.tsxapps/desktop/src/renderer/hooks/useHybridSearch.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/TasksTableView.tsxapps/desktop/src/renderer/screens/main/components/ComponentsShowcase/index.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use object parameters for functions with 2+ parameters instead of positional arguments
Functions with 2+ parameters should accept a single params object with named properties for self-documentation and extensibility
Use prefixed console logging with context pattern: [domain/operation] message
Extract magic numbers and hardcoded values to named constants at module top
Use lookup objects/maps instead of repeated if (type === ...) conditionals
Avoid usinganytype - maintain type safety in TypeScript code
Never swallow errors silently - at minimum log them with context
Import from concrete files directly when possible - avoid barrel file abuse that creates circular dependencies
Avoid deep nesting (4+ levels) - use early returns, extract functions, and invert conditions
Use named properties in options objects instead of boolean parameters to avoid boolean blindness
Files:
apps/desktop/src/renderer/stores/app-state.tsapps/api/src/app/api/integrations/linear/callback/route.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/icons/BacklogIcon/BacklogIcon.tsxapps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.tspackages/db/src/schema/relations.tsapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TaskContextMenu/TaskContextMenu.tsxapps/desktop/src/renderer/screens/main/components/TasksView/TasksView.tsxapps/api/src/app/api/integrations/linear/jobs/initial-sync/syncWorkflowStates.tsapps/desktop/src/renderer/hooks/useHybridSearch.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/api/src/app/api/integrations/linear/jobs/sync-task/route.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/TasksTableView.tsxapps/desktop/src/renderer/screens/main/components/ComponentsShowcase/index.tsxapps/api/src/app/api/integrations/linear/jobs/initial-sync/route.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx
apps/desktop/src/renderer/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility
Files:
apps/desktop/src/renderer/stores/app-state.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/icons/BacklogIcon/BacklogIcon.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TaskContextMenu/TaskContextMenu.tsxapps/desktop/src/renderer/screens/main/components/TasksView/TasksView.tsxapps/desktop/src/renderer/hooks/useHybridSearch.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/TasksTableView.tsxapps/desktop/src/renderer/screens/main/components/ComponentsShowcase/index.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx
apps/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Drizzle ORM for all database operations - never use raw SQL
Files:
apps/desktop/src/renderer/stores/app-state.tsapps/api/src/app/api/integrations/linear/callback/route.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/icons/BacklogIcon/BacklogIcon.tsxapps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.tsapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TaskContextMenu/TaskContextMenu.tsxapps/desktop/src/renderer/screens/main/components/TasksView/TasksView.tsxapps/api/src/app/api/integrations/linear/jobs/initial-sync/syncWorkflowStates.tsapps/desktop/src/renderer/hooks/useHybridSearch.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/api/src/app/api/integrations/linear/jobs/sync-task/route.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/TasksTableView.tsxapps/desktop/src/renderer/screens/main/components/ComponentsShowcase/index.tsxapps/api/src/app/api/integrations/linear/jobs/initial-sync/route.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Biome for formatting and linting - run at root level with
bun run lint:fixorbiome check --write
Files:
apps/desktop/src/renderer/stores/app-state.tsapps/api/src/app/api/integrations/linear/callback/route.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/icons/BacklogIcon/BacklogIcon.tsxapps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.tspackages/db/src/schema/relations.tsapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TaskContextMenu/TaskContextMenu.tsxapps/desktop/src/renderer/screens/main/components/TasksView/TasksView.tsxapps/api/src/app/api/integrations/linear/jobs/initial-sync/syncWorkflowStates.tsapps/desktop/src/renderer/hooks/useHybridSearch.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/api/src/app/api/integrations/linear/jobs/sync-task/route.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/TasksTableView.tsxapps/desktop/src/renderer/screens/main/components/ComponentsShowcase/index.tsxapps/api/src/app/api/integrations/linear/jobs/initial-sync/route.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx
apps/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/api/**/*.{ts,tsx}: Use TRPCError with appropriate error codes: NOT_FOUND, UNAUTHORIZED, FORBIDDEN, BAD_REQUEST, INTERNAL_SERVER_ERROR, NOT_IMPLEMENTED
tRPC procedures and API route handlers should validate and delegate; keep orchestrators thin
Extract business logic from tRPC procedures into utility functions when logic exceeds ~50 lines, is used by multiple procedures, or needs independent testing
Validate at boundaries using Zod schemas for tRPC inputs and API route bodies
Files:
apps/api/src/app/api/integrations/linear/callback/route.tsapps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.tsapps/api/src/app/api/integrations/linear/jobs/initial-sync/syncWorkflowStates.tsapps/api/src/app/api/integrations/linear/jobs/sync-task/route.tsapps/api/src/app/api/integrations/linear/jobs/initial-sync/route.ts
**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
One component per file - do not create multi-component files
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/icons/BacklogIcon/BacklogIcon.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TaskContextMenu/TaskContextMenu.tsxapps/desktop/src/renderer/screens/main/components/TasksView/TasksView.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/TasksTableView.tsxapps/desktop/src/renderer/screens/main/components/ComponentsShowcase/index.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx
packages/db/src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Schema definitions must be in packages/db/src/ using Drizzle ORM
Files:
packages/db/src/schema/relations.ts
🧠 Learnings (5)
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
Applied to files:
apps/desktop/src/renderer/stores/app-state.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/*/src/components/**/*.{ts,tsx} : Co-locate component dependencies: utils, hooks, constants, config, tests, stories in the same folder as the component using them
Applied to files:
apps/desktop/src/renderer/stores/app-state.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/*/src/components/{ui,ai-elements,react-flow}/*.tsx : Use kebab-case single files for shadcn/ui components (e.g., button.tsx, base-node.tsx) in src/components/ui/, src/components/ai-elements, and src/components/react-flow/
Applied to files:
apps/desktop/src/renderer/stores/app-state.tsapps/desktop/src/renderer/screens/main/components/TasksView/TasksView.tsxapps/desktop/src/renderer/screens/main/components/ComponentsShowcase/index.tsx
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to **/*.{ts,tsx} : Avoid using `any` type - maintain type safety in TypeScript code
Applied to files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsx
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to packages/db/src/**/*.ts : Schema definitions must be in packages/db/src/ using Drizzle ORM
Applied to files:
packages/db/src/schema/relations.ts
🧬 Code graph analysis (10)
apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/PriorityCell.tsx (3)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (1)
TaskWithStatus(33-36)apps/desktop/src/renderer/screens/main/components/TasksView/components/PriorityIcon/PriorityIcon.tsx (1)
PriorityIcon(12-141)apps/desktop/src/renderer/screens/main/components/TasksView/utils/taskSorting.ts (1)
ALL_PRIORITIES(31-37)
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx (2)
packages/db/src/schema/schema.ts (1)
SelectTaskStatus(98-98)apps/desktop/src/renderer/screens/main/components/TasksView/components/StatusIcon/index.ts (1)
StatusIcon(2-2)
apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsx (4)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (1)
TaskWithStatus(33-36)packages/db/src/schema/schema.ts (2)
SelectTask(173-173)SelectTaskStatus(98-98)apps/desktop/src/renderer/screens/main/components/TasksView/utils/taskSorting.ts (1)
compareStatusesForDropdown(95-108)apps/desktop/src/renderer/screens/main/components/TasksView/components/StatusIcon/StatusIcon.tsx (1)
StatusIcon(18-201)
apps/desktop/src/renderer/screens/main/components/TasksView/components/icons/BacklogIcon/BacklogIcon.tsx (1)
apps/desktop/src/renderer/screens/main/components/TasksView/components/StatusIcon/index.ts (1)
StatusIcon(2-2)
apps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.ts (1)
packages/trpc/src/router/integration/linear/utils.ts (1)
mapPriorityFromLinear(23-36)
apps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsx (1)
packages/db/src/schema/auth.ts (1)
members(105-122)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (6)
packages/db/src/schema/schema.ts (3)
SelectTask(173-173)SelectTaskStatus(98-98)tasks(100-170)apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTopBar/TasksTopBar.tsx (1)
TabValue(8-8)apps/desktop/src/renderer/screens/main/components/TasksView/utils/taskSorting.ts (1)
compareTasks(69-89)apps/desktop/src/renderer/hooks/useHybridSearch.ts (1)
useHybridSearch(18-83)apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/PriorityCell.tsx (1)
PriorityCell(27-80)apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsx (1)
StatusCell(23-92)
apps/api/src/app/api/integrations/linear/jobs/sync-task/route.ts (1)
packages/db/src/schema/schema.ts (2)
taskStatus(22-22)taskStatuses(58-95)
apps/api/src/app/api/integrations/linear/jobs/initial-sync/route.ts (3)
apps/api/src/app/api/integrations/linear/jobs/initial-sync/syncWorkflowStates.ts (1)
syncWorkflowStates(17-89)packages/db/src/schema/schema.ts (1)
taskStatuses(58-95)apps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.ts (1)
mapIssueToTask(147-184)
apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx (1)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (1)
TaskWithStatus(33-36)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: Deploy Web
- GitHub Check: Deploy API
- GitHub Check: Deploy Marketing
- GitHub Check: Deploy Electric (Fly.io)
- GitHub Check: Deploy Admin
- GitHub Check: Deploy Docs
- GitHub Check: Build
🔇 Additional comments (28)
apps/desktop/src/renderer/screens/main/components/TasksView/components/icons/BacklogIcon/BacklogIcon.tsx (1)
1-13: LGTM!Clean and focused wrapper component. The pattern of creating specialized icon components from a generic
StatusIconis appropriate for consistency across the Tasks view. Props are properly typed with sensible defaults.apps/desktop/src/renderer/stores/app-state.ts (1)
4-9: LGTM! Clean state extension following established patterns.The new "components" view state follows the same pattern as existing views (tasks, settings, workspaces-list). The zustand store usage with devtools middleware aligns with the project's state management approach. Based on learnings, zustand is the preferred state management solution for this codebase.
Also applies to: 26-26, 37-38, 48-48, 91-97, 119-122
apps/desktop/src/renderer/screens/main/components/ComponentsShowcase/index.tsx (3)
7-20: LGTM! Clear variant definitions for showcase purposes.The explicit color values in STATUS_VARIANTS are appropriate here since this is a showcase component demonstrating different visual configurations. The duplicate types with different colors (e.g., two "unstarted" variants) correctly illustrate that the same status type can have different color treatments.
50-65: Good use of responsive grid layout.The responsive column classes (
grid-cols-2 md:grid-cols-3 lg:grid-cols-4) provide appropriate layout adaptation. Usingindexas key is acceptable here sinceSTATUS_VARIANTSis a static array that won't be reordered or filtered.
22-229: Well-structured showcase component.The component follows the one-component-per-file guideline and provides clear visual documentation of StatusIcon variants. The section-based organization (All Variants, Type Breakdown, Hover States, Progress Variants) makes it easy to understand the different icon configurations.
apps/api/src/app/api/integrations/linear/callback/route.ts (3)
1-8: LGTM!Imports and client initialization follow the guidelines - importing from concrete files and using environment configuration.
10-31: LGTM!The Zod schema properly validates boundary inputs, and error handling redirects users with meaningful error codes.
78-102: LGTM!The database upsert correctly uses Drizzle ORM with proper conflict handling and updates the
updatedAttimestamp.apps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsx (2)
46-46: LGTM!Safe default for the members array using nullish coalescing.
24-44: The members query is safe as written.Collectionsare already scoped to the current organization at theCollectionsProviderlevel—getCollections()is called withactiveOrganizationId, and each organization has its own cached set of collections. Thecollections.memberscollection only contains members for the current organization, so no additionalorganizationIdfilter is needed.Likely an incorrect or invalid review comment.
apps/api/src/app/api/integrations/linear/jobs/sync-task/route.ts (1)
76-85: Status lookup logic is correct.The change from direct status usage to a database lookup via
task.statusIdproperly integrates with the new normalizedtask_statusestable. The early return on missing status prevents downstream failures.packages/db/src/schema/relations.ts (2)
109-118: Relations are well-structured.The
taskStatusesRelationscorrectly establishes bidirectional relationships:oneto organization andmanyto tasks. This aligns with the schema definition wheretaskStatuseshas anorganizationIdforeign key.
93-96: Status relation on tasks is correctly defined.The
onerelation totaskStatusesviastatusIdproperly mirrors the foreign key constraint in the schema.apps/api/src/app/api/integrations/linear/jobs/initial-sync/syncWorkflowStates.ts (2)
17-23: Good use of object parameters pattern.The function signature follows the coding guideline requiring object parameters for functions with 2+ parameters, improving readability and extensibility.
62-82: Upsert logic is correctly implemented.The conflict target uses the unique constraint columns (
organizationId,externalProvider,externalId), and the update set properly usesbuildConflictUpdateColumnsfor consistent conflict resolution.apps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.ts (1)
47-78: Progress calculation logic is well-documented and correct.The JSDoc clearly explains the Linear rendering formula. The implementation correctly handles edge cases (0, 1, 2, 3+ states) and rounds results appropriately.
apps/api/src/app/api/integrations/linear/jobs/initial-sync/route.ts (2)
78-95: Well-structured sync flow avoids N+1 queries.The three-step approach (sync states → build lookup map → sync issues) is efficient. Building the
statusByExternalIdmap upfront prevents individual lookups per issue during mapping.
121-127: Correctly passes status lookup to mapIssueToTask.The
statusByExternalIdparameter enables the mapper to resolvestatusIdwithout additional database calls.apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx (1)
58-114: LGTM on the dropdown rendering logic.The dropdown structure is clean with proper avatar fallbacks, accessible trigger button, and clear visual indication of the selected assignee.
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx (1)
18-45: LGTM on the component structure and flexibility.The design allowing
MenuItemto be passed as a prop enables reuse across dropdown and context menus. Clean implementation with proper key handling and selection state.apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/PriorityCell.tsx (1)
27-79: LGTM!Good error handling with contextual logging, module-level constants, and clean dropdown implementation. The try/catch pattern here is what should be adopted in
AssigneeCell.apps/desktop/src/renderer/screens/main/components/TasksView/TasksView.tsx (1)
8-44: LGTM!Clean refactor with proper separation of concerns. The loading/empty/table conditional flow is clear, and state management is appropriately lifted to enable coordination between the top bar and table view.
apps/desktop/src/renderer/screens/main/components/TasksView/components/TaskContextMenu/TaskContextMenu.tsx (2)
52-55: Good: Correctly avoids array mutation.Using
[...allStatuses].sort()prevents mutating the source array, unlike the issue inStatusCell.tsx.
97-182: LGTM on the context menu structure.Clean composition with shared menu item components, proper submenu organization, and consistent styling. The delete action correctly uses the destructive styling pattern.
apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/TasksTableView.tsx (1)
10-72: Component structure looks good.The separation of group header vs. leaf row rendering is clear, and the grid column alignment correctly accounts for skipping the status column. The use of
TaskContextMenuwrapper for leaf rows aligns well with the context menu surface introduced in this PR.apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (3)
261-273: Date handling is defensive but consider simplification.By default, Drizzle timestamp columns withmode: "date"(the default) return JS Date objects. Thenew Date(date)wrapper is safe but potentially redundant if the schema is using default date mode. This is acceptable defensive coding.
122-276: Column definitions follow react-table best practices.The static column definitions with an empty dependency array is correct per TanStack Table patterns. Cell components properly encapsulate their own state and access row data through the provided context.
45-52: Well-structured hook with clear return type.The hook properly composes data fetching, filtering, sorting, and table configuration. The explicit return type annotation improves maintainability.
apps/api/src/app/api/integrations/linear/jobs/initial-sync/route.ts
Outdated
Show resolved
Hide resolved
apps/api/src/app/api/integrations/linear/jobs/initial-sync/syncWorkflowStates.ts
Outdated
Show resolved
Hide resolved
apps/api/src/app/api/integrations/linear/jobs/sync-task/route.ts
Outdated
Show resolved
Hide resolved
| const getInitials = (name: string) => { | ||
| return name | ||
| .split(" ") | ||
| .map((n) => n[0]) | ||
| .join("") | ||
| .toUpperCase() | ||
| .slice(0, 2); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard against empty or malformed names in getInitials.
If name is an empty string or contains consecutive spaces, n[0] will be undefined, causing a runtime error on toUpperCase(). Consider extracting this to module level and adding a guard.
Proposed fix
+const getInitials = (name: string) => {
+ return name
+ .split(" ")
+ .filter((n) => n.length > 0)
+ .map((n) => n[0])
+ .join("")
+ .toUpperCase()
+ .slice(0, 2);
+};
+
export function AssigneeCell({ info }: AssigneeCellProps) {
const collections = useCollections();
const [open, setOpen] = useState(false);
// ... rest of component
-
- const getInitials = (name: string) => {
- return name
- .split(" ")
- .map((n) => n[0])
- .join("")
- .toUpperCase()
- .slice(0, 2);
- };🤖 Prompt for AI Agents
In
@apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx
around lines 49 - 56, The getInitials function can throw when name is empty or
has consecutive spaces; move getInitials to module scope and update it to
defensively handle falsy or malformed input by trimming the string, splitting on
whitespace, filtering out empty segments, taking the first character of each
valid segment, defaulting to an empty string when no valid characters exist,
joining, uppercasing, and slicing to two chars; ensure callers of getInitials in
AssigneeCell use this new module-level helper.
...desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx
Show resolved
Hide resolved
| // Leaf row - render all cells horizontally with grid for consistent column widths | ||
| // Layout: [Checkbox] [Priority] [ID] [Title] ... [Labels] [Assignee] [Due] [gap] | ||
| // Note: Status column (index 0) returns null for leaf rows, so we skip it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inaccurate comment: column is "Created", not "Due".
The comment mentions "Due" but the actual column defined in useTasksTable is createdAt (Created). Update for clarity:
- // Layout: [Checkbox] [Priority] [ID] [Title] ... [Labels] [Assignee] [Due] [gap]
+ // Layout: [Checkbox] [Priority] [ID] [Title] ... [Labels] [Assignee] [Created]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Leaf row - render all cells horizontally with grid for consistent column widths | |
| // Layout: [Checkbox] [Priority] [ID] [Title] ... [Labels] [Assignee] [Due] [gap] | |
| // Note: Status column (index 0) returns null for leaf rows, so we skip it | |
| // Leaf row - render all cells horizontally with grid for consistent column widths | |
| // Layout: [Checkbox] [Priority] [ID] [Title] ... [Labels] [Assignee] [Created] | |
| // Note: Status column (index 0) returns null for leaf rows, so we skip it |
🤖 Prompt for AI Agents
In
@apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/TasksTableView.tsx
around lines 39 - 41, The inline comment in TasksTableView.tsx is misleading: it
lists the column as "Due" but the table (from useTasksTable) actually uses the
createdAt/Created column; update the comment text in TasksTableView (near the
leaf row/grid layout comment) to replace "Due" with "Created" (or "Created At")
so it accurately reflects the createdAt column used by useTasksTable and avoids
confusion.
...desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In @apps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.ts:
- Around line 158-161: The current mapIssueToTask throws an Error when
statusByExternalId.get(issue.state.id) is missing; change mapIssueToTask to
return null instead of throwing, and log a warning including identifying info
(e.g., issue.id or issue.key and issue.state.id) so the missing workflow state
is visible; update callers that map issues (the code that calls mapIssueToTask
in fetchAllIssues/initial sync) to filter out nulls (e.g., .map(...).filter(task
=> task !== null)) so missing-state issues are skipped and the batch continues.
In
@apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsx:
- Around line 36-39: sortedStatuses is mutating the original statuses array
because Array.prototype.sort() is in-place; inside the useMemo that computes
sortedStatuses (which uses statuses, potentially referencing allStatuses), copy
the array before sorting (e.g., return
[...statuses].sort(compareStatusesForDropdown) or statuses.slice().sort(...)) so
you don't mutate the source cache — update the useMemo to return a sorted copy
and keep the dependency on statuses as-is.
- Around line 69-88: The StatusCell component duplicates the status-list
rendering that was extracted into the StatusMenuItems component; replace the
manual mapping over sortedStatuses inside StatusCell's DropdownMenuContent with
the shared StatusMenuItems component, passing the necessary props
(sortedStatuses, currentStatus, and the selection handler handleSelectStatus)
and ensure StatusMenuItems receives the same props/types (e.g., sortedStatuses,
currentStatus, onSelectStatus) so behavior and selection marker (✓) remain
identical; remove the duplicated JSX block and import StatusMenuItems into
StatusCell.
In @packages/db/src/schema/schema.ts:
- Around line 109-111: The foreign key for statusId currently references
taskStatuses.id without an explicit onDelete policy; update the statusId
references call (the statusId column definition referencing taskStatuses.id) to
include an explicit onDelete behavior (e.g., { onDelete: "restrict" } to prevent
deletion while tasks exist, or another policy like "set null"/"cascade" if you
want automatic handling) so the FK behavior matches the other FKs and is clear
and consistent.
🧹 Nitpick comments (11)
apps/desktop/src/renderer/screens/main/components/TasksView/components/PriorityIcon/PriorityIcon.tsx (3)
20-22: Minor:classNamereplaces rather than augments the default size.When
classNameis provided, it completely replaces"h-4 w-4"rather than being combined. If this is intentional for full override control, consider documenting it. Otherwise, you may want to combine them:-const sizeClass = className || "h-4 w-4"; +const sizeClass = `h-4 w-4 ${className}`;
25-144: Consider using a lookup object instead of repeated if-statements.Per coding guidelines, prefer lookup objects/maps over repeated
if (type === ...)conditionals. This would consolidate the priority rendering logic and make it easier to maintain.Additionally, extract the magic color value and status type strings to named constants.
♻️ Suggested refactor approach
// Extract constants at module top const URGENT_ACTIVE_COLOR = "#F97316"; const ACTIVE_STATUS_TYPES = ["started", "unstarted", "backlog"] as const; // Then use a lookup pattern for SVG content or a switch with exhaustive check const PRIORITY_ICONS: Record<TaskPriority, (props: IconRenderProps) => JSX.Element> = { none: ({ sizeClass, hoverClass, defaultColor }) => (/* SVG */), urgent: ({ sizeClass, hoverClass, fillColor }) => (/* SVG */), high: ({ sizeClass, hoverClass, defaultColor }) => (/* SVG */), medium: ({ sizeClass, hoverClass, defaultColor }) => (/* SVG */), low: ({ sizeClass, hoverClass, defaultColor }) => (/* SVG */), }; // In component: const IconRenderer = PRIORITY_ICONS[priority]; return IconRenderer ? IconRenderer({ sizeClass, hoverClass, defaultColor, fillColor }) : null;
146-147: Add exhaustive type checking for compile-time safety.If
TaskPriorityenum gains new values in the future, this will silently returnnull. Consider adding an exhaustive check to catch missing cases at compile time:♻️ Suggested exhaustive check
+ // Exhaustive check - TypeScript will error if a priority case is missed + const _exhaustiveCheck: never = priority; return null;apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx (2)
49-56: Handle edge case with empty string segments in name parsing.If a name contains consecutive spaces (e.g.,
"John Doe"),split(" ")produces empty strings, andn[0]returnsundefined. While this won't crash, it may produce unexpected initials.Consider filtering out empty segments:
Suggested improvement
const getInitials = (name: string) => { return name .split(" ") + .filter(Boolean) .map((n) => n[0]) .join("") .toUpperCase() .slice(0, 2); };Optionally, extract this as a utility function outside the component to avoid recreating it on each render.
61-74: Add accessible label to the trigger button.The button only displays a visual avatar or icon without any text. Screen reader users won't know what this control does.
Suggested improvement
- <button type="button" className="cursor-pointer"> + <button + type="button" + className="cursor-pointer" + aria-label={currentAssignee ? `Assignee: ${currentAssignee.name}` : "Select assignee"} + >apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx (1)
34-38: Type assertion could mask type mismatches.The cast
status.type as StatusTypebypasses type checking. IfSelectTaskStatus.typeever contains a value not inStatusType, it will silently pass and could cause unexpected rendering inStatusIcon.Consider either:
- Adding a runtime guard/validation
- Ensuring the database schema type aligns with
StatusTypeat the type levelExample with type narrowing
import { StatusIcon, type StatusType, isStatusType } from "../StatusIcon"; // Then in render: <StatusIcon type={isStatusType(status.type) ? status.type : "backlog"} color={status.color} progress={status.progressPercent ?? undefined} />Or export a helper from StatusIcon that validates/coerces the type.
apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsx (1)
47-54: Error handling could leave UI in inconsistent state.The
try/catchlogs the error but the dropdown remains open on failure (line 51 only runs on success path). Consider showing user feedback on failure or ensuring the dropdown closes regardless.Option: close dropdown in finally block
try { collections.tasks.update(taskWithStatus.id, (draft) => { draft.statusId = newStatus.id; }); - setOpen(false); } catch (error) { console.error("[StatusCell] Failed to update status:", error); + // Optionally: show toast/notification to user +} finally { + setOpen(false); }apps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.ts (2)
35-38: Consider exporting the interface for reuse.
WorkflowStateWithPositionis used only internally bycalculateProgressForStates, but since that function is exported and typed with this interface, consumers may need access to the type for building the input array.Export the interface
-interface WorkflowStateWithPosition { +export interface WorkflowStateWithPosition { name: string; position: number; }
126-136: Extract magic numbers to named constants.The values
3(months) and100(page size) should be extracted to named constants at module top for clarity and maintainability.Proposed refactor
+const SYNC_LOOKBACK_MONTHS = 3; +const ISSUES_PAGE_SIZE = 100; + export async function fetchAllIssues( client: LinearClient, ): Promise<LinearIssue[]> { const allIssues: LinearIssue[] = []; let cursor: string | undefined; - const threeMonthsAgo = subMonths(new Date(), 3); + const cutoffDate = subMonths(new Date(), SYNC_LOOKBACK_MONTHS); do { const response = await client.client.request< IssuesQueryResponse, { first: number; after?: string; filter: object } >(ISSUES_QUERY, { - first: 100, + first: ISSUES_PAGE_SIZE, after: cursor, - filter: { updatedAt: { gte: threeMonthsAgo.toISOString() } }, + filter: { updatedAt: { gte: cutoffDate.toISOString() } }, });As per coding guidelines, magic numbers and hardcoded values should be extracted to named constants at module top.
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (1)
103-118: Consider extracting magic numbers to named constants.The font sizing values (
0.5,0.75) and padding (0.5) could be extracted for clarity.Proposed refactor
+const MONOSPACE_CHAR_WIDTH_EM = 0.5; +const TEXT_XS_REM = 0.75; +const SLUG_COLUMN_PADDING_REM = 0.5; +const SLUG_COLUMN_DEFAULT_WIDTH = "5rem"; + const slugColumnWidth = useMemo(() => { - if (!data || data.length === 0) return "5rem"; + if (!data || data.length === 0) return SLUG_COLUMN_DEFAULT_WIDTH; const longestSlug = data.reduce((longest, task) => { return task.slug.length > longest.length ? task.slug : longest; }, ""); - const remPerChar = 0.5 * 0.75; - const padding = 0.5; + const remPerChar = MONOSPACE_CHAR_WIDTH_EM * TEXT_XS_REM; + const padding = SLUG_COLUMN_PADDING_REM; const width = longestSlug.length * remPerChar + padding; return `${Math.ceil(width * 10) / 10}rem`; }, [data]);As per coding guidelines, magic numbers should be extracted to named constants at module top.
packages/db/src/schema/schema.ts (1)
66-69: Consider using an enum for thetypefield.The comment documents specific allowed values (
"backlog" | "unstarted" | "started" | "completed" | "canceled"), but the field is plaintext(). The existingtaskStatusenum (line 22) or a new enum could enforce these values at the database level, preventing invalid data.If you intentionally want flexibility for external providers with different status types, the current approach is fine—just wanted to flag the inconsistency between the comment and implementation.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
apps/api/src/app/api/electric/[...path]/route.tsapps/api/src/app/api/integrations/linear/callback/route.tsapps/api/src/app/api/integrations/linear/jobs/initial-sync/route.tsapps/api/src/app/api/integrations/linear/jobs/initial-sync/syncWorkflowStates.tsapps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.tsapps/api/src/app/api/integrations/linear/jobs/sync-task/route.tsapps/api/src/app/api/integrations/linear/webhook/route.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/PriorityIcon/PriorityIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/StatusIcon/StatusIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/utils/taskSorting.tspackages/db/drizzle/0009_add_organization_id_to_tasks_external_unique.sqlpackages/db/drizzle/meta/0009_snapshot.jsonpackages/db/drizzle/meta/_journal.jsonpackages/db/src/schema/schema.ts
🚧 Files skipped from review as they are similar to previous changes (7)
- apps/desktop/src/renderer/screens/main/components/TasksView/utils/taskSorting.ts
- apps/api/src/app/api/integrations/linear/callback/route.ts
- apps/desktop/src/renderer/screens/main/components/TasksView/components/StatusIcon/StatusIcon.tsx
- apps/api/src/app/api/integrations/linear/jobs/initial-sync/route.ts
- apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/PriorityCell.tsx
- packages/db/drizzle/meta/_journal.json
- apps/api/src/app/api/integrations/linear/jobs/initial-sync/syncWorkflowStates.ts
🧰 Additional context used
📓 Path-based instructions (8)
apps/desktop/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)
apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined insrc/lib/trpc
Use alias as defined intsconfig.jsonwhen possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from@trpc/server/observableinstead of async generators, as the library explicitly checksisObservable(result)and throws an error otherwise
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/PriorityIcon/PriorityIcon.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use object parameters for functions with 2+ parameters instead of positional arguments
Functions with 2+ parameters should accept a single params object with named properties for self-documentation and extensibility
Use prefixed console logging with context pattern: [domain/operation] message
Extract magic numbers and hardcoded values to named constants at module top
Use lookup objects/maps instead of repeated if (type === ...) conditionals
Avoid usinganytype - maintain type safety in TypeScript code
Never swallow errors silently - at minimum log them with context
Import from concrete files directly when possible - avoid barrel file abuse that creates circular dependencies
Avoid deep nesting (4+ levels) - use early returns, extract functions, and invert conditions
Use named properties in options objects instead of boolean parameters to avoid boolean blindness
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/api/src/app/api/electric/[...path]/route.tsapps/api/src/app/api/integrations/linear/jobs/sync-task/route.tsapps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/api/src/app/api/integrations/linear/webhook/route.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/PriorityIcon/PriorityIcon.tsxpackages/db/src/schema/schema.ts
apps/desktop/src/renderer/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/PriorityIcon/PriorityIcon.tsx
**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
One component per file - do not create multi-component files
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/PriorityIcon/PriorityIcon.tsx
apps/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Drizzle ORM for all database operations - never use raw SQL
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/api/src/app/api/electric/[...path]/route.tsapps/api/src/app/api/integrations/linear/jobs/sync-task/route.tsapps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/api/src/app/api/integrations/linear/webhook/route.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/PriorityIcon/PriorityIcon.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Biome for formatting and linting - run at root level with
bun run lint:fixorbiome check --write
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/api/src/app/api/electric/[...path]/route.tsapps/api/src/app/api/integrations/linear/jobs/sync-task/route.tsapps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/api/src/app/api/integrations/linear/webhook/route.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/PriorityIcon/PriorityIcon.tsxpackages/db/src/schema/schema.ts
apps/api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/api/**/*.{ts,tsx}: Use TRPCError with appropriate error codes: NOT_FOUND, UNAUTHORIZED, FORBIDDEN, BAD_REQUEST, INTERNAL_SERVER_ERROR, NOT_IMPLEMENTED
tRPC procedures and API route handlers should validate and delegate; keep orchestrators thin
Extract business logic from tRPC procedures into utility functions when logic exceeds ~50 lines, is used by multiple procedures, or needs independent testing
Validate at boundaries using Zod schemas for tRPC inputs and API route bodies
Files:
apps/api/src/app/api/electric/[...path]/route.tsapps/api/src/app/api/integrations/linear/jobs/sync-task/route.tsapps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.tsapps/api/src/app/api/integrations/linear/webhook/route.ts
packages/db/src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Schema definitions must be in packages/db/src/ using Drizzle ORM
Files:
packages/db/src/schema/schema.ts
🧠 Learnings (2)
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to **/*.{ts,tsx} : Avoid using `any` type - maintain type safety in TypeScript code
Applied to files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to packages/db/src/**/*.ts : Schema definitions must be in packages/db/src/ using Drizzle ORM
Applied to files:
packages/db/drizzle/meta/0009_snapshot.jsonpackages/db/src/schema/schema.ts
🧬 Code graph analysis (4)
apps/api/src/app/api/integrations/linear/jobs/sync-task/route.ts (2)
packages/db/src/schema/schema.ts (2)
taskStatus(22-22)taskStatuses(58-95)apps/api/src/env.ts (1)
env(4-48)
apps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.ts (1)
packages/trpc/src/router/integration/linear/utils.ts (1)
mapPriorityFromLinear(23-36)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (13)
packages/db/src/schema/schema.ts (3)
SelectTask(174-174)SelectTaskStatus(98-98)tasks(100-171)apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTopBar/index.ts (1)
TabValue(1-1)apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTopBar/TasksTopBar.tsx (1)
TabValue(8-8)apps/desktop/src/renderer/screens/main/components/TasksView/utils/taskSorting.ts (1)
compareTasks(69-89)apps/desktop/src/renderer/hooks/useHybridSearch.ts (1)
useHybridSearch(18-83)apps/desktop/src/renderer/screens/main/components/TasksView/components/StatusIcon/index.ts (1)
StatusIcon(2-2)apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/PriorityCell.tsx (1)
PriorityCell(27-81)apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/PriorityCell/index.ts (1)
PriorityCell(1-1)apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsx (1)
StatusCell(23-92)apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/index.ts (1)
StatusCell(1-1)packages/ui/src/components/ui/badge.tsx (1)
Badge(46-46)apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx (1)
AssigneeCell(19-115)apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/index.ts (1)
AssigneeCell(1-1)
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx (2)
packages/db/src/schema/schema.ts (1)
SelectTaskStatus(98-98)apps/desktop/src/renderer/screens/main/components/TasksView/components/StatusIcon/StatusIcon.tsx (1)
StatusIcon(19-190)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Deploy Marketing
- GitHub Check: Deploy Web
- GitHub Check: Deploy Docs
- GitHub Check: Deploy API
- GitHub Check: Deploy Admin
- GitHub Check: Build
🔇 Additional comments (25)
apps/desktop/src/renderer/screens/main/components/TasksView/components/PriorityIcon/PriorityIcon.tsx (1)
1-3: Good accessibility handling.The
aria-hidden="true"attribute on SVGs with the biome-ignore comment explaining the rationale is appropriate for decorative icons within labeled interactive elements.apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/AssigneeCell/AssigneeCell.tsx (1)
19-114: Component implementation looks solid overall.The component correctly uses controlled state for the dropdown, leverages the pre-loaded assignee from joined query to avoid delays, and follows the established collections pattern for updates. The early return optimization in
handleSelectUserwhen the same user is selected is a nice touch.apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx (1)
18-45: Clean, reusable component design.Good abstraction allowing consumers to inject their own
MenuItemcomponent while handling the status rendering logic consistently. The interface is well-typed withSelectTaskStatus.apps/desktop/src/renderer/screens/main/components/TasksView/components/cells/StatusCell/StatusCell.tsx (1)
23-55: Good patterns: lazy loading, prefixed logging, controlled dropdown.
- Lazy loading statuses only when dropdown opens (lines 28-31) is efficient
- Console logging follows
[domain/operation]pattern per guidelines (line 53)- Controlled dropdown state management is clean
packages/db/drizzle/0009_add_organization_id_to_tasks_external_unique.sql (1)
1-2: LGTM! Organization-scoped uniqueness is correct for multi-tenancy.The migration correctly updates the unique constraint to be organization-scoped, allowing the same external task (provider + id) to exist across different organizations. Since this makes the constraint more permissive, existing data will not violate the new constraint.
packages/db/drizzle/meta/0009_snapshot.json (3)
1598-1606: Constraint matches migration - LGTM.The
tasks_external_uniqueconstraint correctly reflects the organization-scoped composite key(organization_id, external_provider, external_id)from migration 0009.
1519-1532: FK behavior is appropriate for data protection.Using
onDelete: "no action"for thestatus_idforeign key prevents accidental deletion of task statuses that are still in use, which is the correct approach for maintaining data integrity.
1632-1645: Thetask_statusenum is unused and should be removed.This enum is defined in schema.ts but no column actually uses it. The
tasks.statusIdfield is a UUID foreign key to thetask_statusestable, and the only enum used in the tasks table istaskPriority. This is a legacy artifact from before status normalization. Remove it in a follow-up migration to clean up the schema.apps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.ts (1)
47-78: LGTM!The progress calculation correctly implements Linear's documented formula with proper handling for 0, 1, 2, and 3+ state scenarios. The defensive
continueon line 62 handles potential sparse array issues gracefully.apps/api/src/app/api/electric/[...path]/route.ts (1)
45-58: LGTM!This correctly handles the content-encoding proxy issue. When the upstream server sends a compressed response, the fetch API automatically decompresses it, but the original
content-encodingandcontent-lengthheaders would be incorrect for the decompressed body. Stripping these headers prevents the client from attempting to decompress already-decompressed content.apps/api/src/app/api/integrations/linear/jobs/sync-task/route.ts (3)
4-8: LGTM!Import updated correctly to include
taskStatusesfrom the schema for status resolution.
76-84: LGTM!The status lookup correctly queries by the task's
statusIdand handles the missing status case with an appropriate error response before proceeding to resolve the Linear state.
179-183: LGTM!Simplified URL construction by using
env.NEXT_PUBLIC_API_URLdirectly.apps/api/src/app/api/integrations/linear/webhook/route.ts (5)
10-14: LGTM!Import updated correctly to include
taskStatusesfor status resolution in webhook processing.
62-77: LGTM!Clean implementation of webhook status tracking. The status is correctly updated based on the processing result, and
processedAtis appropriately set only in the success path.
101-117: Good handling of missing status with proper TODO tracking.The warning log follows the prefixed pattern
[webhook]as per guidelines, and the TODO references ticket SUPER-237 for future resolution. This is a pragmatic approach to handle race conditions between status syncs and webhooks.
152-152: LGTM!Adding
createdAtfromissue.createdAtensures the task's creation timestamp reflects its origin in Linear rather than when it was synced.
154-161: No action needed – the unique constraint exists.The
taskstable has a unique constrainttasks_external_uniquedefined on(organizationId, externalProvider, externalId), which matches the conflict target exactly. The upsert will function correctly.Likely an incorrect or invalid review comment.
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (5)
32-36: LGTM!Clean type extension that combines the base
SelectTaskwith joinedstatusandassigneedata. This provides proper typing for the table rows.
58-75: LGTM!Well-structured live query with appropriate join types: inner join for
status(required) and left join forassignee(optional). TheisNull(tasks.deletedAt)filter correctly excludes soft-deleted tasks.
122-277: LGTM!Well-structured column definitions following TanStack React Table patterns. Cell components are properly extracted to separate files, and the status column correctly handles both grouped and leaf row rendering. The labels display with a sensible limit of 2 visible items and an overflow indicator.
279-298: LGTM!Table instance is properly configured with all necessary row models. Setting
autoResetExpanded: falseis the right choice to preserve group expansion state when data updates.
92-101: LGTM!Clean effect that syncs the
filterTabprop to the table'scolumnFiltersstate. The conditional logic correctly omits the filter for the "all" tab.packages/db/src/schema/schema.ts (2)
97-98: LGTM!Type exports follow the established pattern in this file.
162-169: LGTM!The index on
statusIdwill support efficient joins withtaskStatuses. AddingorganizationIdto the unique constraint correctly scopes external task uniqueness per organization, allowing multiple organizations to sync the same external provider independently.
| const statusId = statusByExternalId.get(issue.state.id); | ||
| if (!statusId) { | ||
| throw new Error(`Status not found for state ${issue.state.id}`); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how mapIssueToTask is called and error handling context
cd apps/api
rg -n -B5 -A10 'mapIssueToTask' --type tsRepository: superset-sh/superset
Length of output: 4174
🏁 Script executed:
#!/bin/bash
# Read the full route.ts file to understand the complete sync flow
cd apps/api
cat -n src/app/api/integrations/linear/jobs/initial-sync/route.tsRepository: superset-sh/superset
Length of output: 4848
🏁 Script executed:
#!/bin/bash
# Read syncWorkflowStates implementation
cd apps/api
cat -n src/app/api/integrations/linear/jobs/initial-sync/syncWorkflowStates.tsRepository: superset-sh/superset
Length of output: 2251
Replace Error with null-return pattern for graceful handling of missing workflow states.
When an issue references a workflow state that wasn't synced (due to deletion, archival, or race conditions), throwing an Error aborts the entire sync batch. syncWorkflowStates is already called before fetchAllIssues, but this doesn't guarantee all states referenced by existing issues will be present in the database.
Return null from mapIssueToTask and filter nulls at the call site to allow partial sync success, or log the missing state and skip that issue:
const taskValues = issues
.map((issue) =>
mapIssueToTask(
issue,
organizationId,
creatorUserId,
userByEmail,
statusByExternalId,
),
)
.filter((task) => task !== null);
🤖 Prompt for AI Agents
In @apps/api/src/app/api/integrations/linear/jobs/initial-sync/utils.ts around
lines 158 - 161, The current mapIssueToTask throws an Error when
statusByExternalId.get(issue.state.id) is missing; change mapIssueToTask to
return null instead of throwing, and log a warning including identifying info
(e.g., issue.id or issue.key and issue.state.id) so the missing workflow state
is visible; update callers that map issues (the code that calls mapIssueToTask
in fetchAllIssues/initial sync) to filter out nulls (e.g., .map(...).filter(task
=> task !== null)) so missing-state issues are skipped and the batch continues.
...r/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In
@apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/constants.ts:
- Around line 1-10: Delete the unused exported constant STATUS_COLORS from the
file (remove the entire constant declaration and its export) and remove any
imports or references to STATUS_COLORS across the codebase; verify the
StatusIcon component uses the current StatusType values ("unstarted", "started")
and not the removed keys, and adjust any remaining mapping logic to rely on the
component's existing status-to-class mapping instead of the removed constant.
In
@apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsx:
- Around line 162-168: The ContextMenuItem components in TaskContextMenu are
using the wrong event prop — replace onClick with onSelect for every
ContextMenuItem (e.g., the ones wired to handleCopyId, handleCopyTitle, and the
delete action) so the Radix UI Item selection handler is invoked; update the JSX
in TaskContextMenu to pass the existing handler functions via onSelect instead
of onClick to restore copy/delete functionality.
In
@apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsx:
- Around line 39-42: The current useMemo for sortedStatuses mutates the original
statuses array by calling statuses.sort(...); change it to produce a new sorted
array (e.g., using [...statuses].sort(compareStatusesForDropdown) or
statuses.toSorted(compareStatusesForDropdown)) so you do not mutate props/state;
update the useMemo block that defines sortedStatuses to sort a copy of statuses
using compareStatusesForDropdown.
In @packages/db/src/schema/schema.ts:
- Around line 105-107: The foreign key definition for statusId (statusId:
uuid("status_id").notNull().references(() => taskStatuses.id)) lacks an onDelete
behavior; update the foreign key chain on the statusId column to include an
explicit .onDelete(...) call on the reference (choose .onDelete('RESTRICT') or
.onDelete('NO ACTION') if you want to prevent deleting a taskStatuses row while
tasks reference it, or .onDelete('CASCADE') if deleting a status should delete
associated tasks), so adjust the statusId column definition to append the
appropriate .onDelete(...) to the .references(() => taskStatuses.id) call.
🧹 Nitpick comments (13)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/LabelsCell/LabelsCell.tsx (1)
19-27: Extract magic number to a named constant.The value
2is repeated across lines 19, 24, and 26 to control maximum visible labels. As per coding guidelines, extract magic numbers to named constants at module top.♻️ Suggested refactor
import type { SelectTask } from "@superset/db/schema"; import { Badge } from "@superset/ui/badge"; import type { CellContext } from "@tanstack/react-table"; +const MAX_VISIBLE_LABELS = 2; + interface LabelsCellProps { info: CellContext<SelectTask, string[] | null>; } export function LabelsCell({ info }: LabelsCellProps) { const currentLabels = info.getValue() || []; // Don't render anything if there are no labels if (currentLabels.length === 0) { return null; } return ( <div className="flex gap-1 flex-shrink-0"> - {currentLabels.slice(0, 2).map((label) => ( + {currentLabels.slice(0, MAX_VISIBLE_LABELS).map((label) => ( <Badge key={label} variant="outline" className="text-xs"> {label} </Badge> ))} - {currentLabels.length > 2 && ( + {currentLabels.length > MAX_VISIBLE_LABELS && ( <Badge variant="outline" className="text-xs"> - +{currentLabels.length - 2} + +{currentLabels.length - MAX_VISIBLE_LABELS} </Badge> )} </div> ); }apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/ActiveIcon.tsx (1)
1-20: Extract magic number to named constant.The component imports
StatusIconcorrectly. The only refinement needed is extracting the hardcodedprogress={50}value to a named constant for clarity and self-documentation, per coding guidelines:♻️ Extract magic number to constant
import { StatusIcon } from "../../StatusIcon"; +const ACTIVE_PROGRESS = 50; + interface ActiveIconProps { color?: string; className?: string; } export function ActiveIcon({ color = "currentColor", className, }: ActiveIconProps) { return ( <StatusIcon type="started" color={color} - progress={50} + progress={ACTIVE_PROGRESS} className={className} /> ); }apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsx (1)
49-61: Consider adding an accessible label to the trigger button.The button currently has no accessible label, which makes it difficult for screen reader users to understand its purpose.
♿ Suggested accessibility improvement
<DropdownMenuTrigger asChild> - <button type="button" className="cursor-pointer"> + <button + type="button" + className="cursor-pointer" + aria-label={task.assignee ? `Assigned to ${task.assignee.name}` : "Unassigned, click to assign"} + > {task.assignee ? (apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/PriorityIcon.tsx (1)
69-69: Extract hardcoded color to a named constant.The hex color
"#F97316"is a magic value. Consider usingcolors.orange[500]from tailwindcss/colors for consistency with the rest of the component, or extract it as a named constant.♻️ Suggested fix
+const URGENT_ACTIVE_COLOR = colors.orange[500]; + export function PriorityIcon({Then update line 69:
- const fillColor = color || (isActive ? "#F97316" : colors.neutral[500]); + const fillColor = color || (isActive ? URGENT_ACTIVE_COLOR : colors.neutral[500]);packages/db/src/schema/schema.ts (1)
68-68: Consider using an enum for thetypefield.The
typefield is defined astextwith a comment indicating specific allowed values ("backlog" | "unstarted" | "started" | "completed" | "canceled"). Using apgEnumwould provide database-level validation and consistency with the existingtaskStatusandtaskPriorityenums in this file.This is a trade-off between flexibility and type safety. If status types may evolve frequently, text is acceptable. Otherwise, consider defining an enum similar to
taskStatus.apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/PriorityCell.tsx (1)
46-48: User receives no feedback on update failure.The error is logged but the user isn't informed that the priority change failed. Consider displaying a toast notification or keeping the dropdown open with an error state.
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsx (1)
17-20: Consider importingTaskWithStatusfrom the shared location.This type is duplicated here and in
useTasksTable.tsx(line 35-38). Import from the hook's index to maintain a single source of truth.-// Task with joined status data -type TaskWithStatus = SelectTask & { - status: SelectTaskStatus; -}; +import type { TaskWithStatus } from "../../useTasksTable";apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/StatusIcon.tsx (1)
176-188: Consider adding exhaustiveness check for type safety.The fallback silently renders the "unstarted" icon for unknown types. A
switchwithneverassertion would catch missing cases at compile time.// Alternative pattern for exhaustiveness: const _exhaustiveCheck: never = type; console.warn(`[StatusIcon] Unknown status type: ${type}`);This is optional since
StatusTypeis a closed union and TypeScript will catch invalid literals at call sites.apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (1)
106-120: Consider extracting magic numbers to named constants.The calculation is well-documented, but extracting the magic numbers would improve maintainability. Based on coding guidelines.
const CHAR_WIDTH_RATIO = 0.5; const FONT_SIZE_REM = 0.75; // text-xs const HORIZONTAL_PADDING_REM = 0.5; const DEFAULT_SLUG_WIDTH = "5rem";apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsx (3)
59-67: Type parameter may beundefinedwhenallStatusesis nullish.The type
(typeof allStatuses)[0]resolves toundefinedwhenallStatusesisundefined. Consider using the concrete type or adding a guard.♻️ Suggested fix
- const handleStatusChange = (status: (typeof allStatuses)[0]) => { + const handleStatusChange = (status: NonNullable<typeof allStatuses>[number]) => {Or import and use
SelectTaskStatusdirectly from the schema.
89-95: Clipboard operations lack error handling.
navigator.clipboard.writeTextreturns a Promise that can reject (e.g., permissions denied). Consider adding error handling for consistency with other handlers.♻️ Suggested fix
const handleCopyId = () => { - navigator.clipboard.writeText(task.slug); + navigator.clipboard.writeText(task.slug).catch((error) => { + console.error("[TaskContextMenu] Failed to copy ID:", error); + }); }; const handleCopyTitle = () => { - navigator.clipboard.writeText(task.title); + navigator.clipboard.writeText(task.title).catch((error) => { + console.error("[TaskContextMenu] Failed to copy title:", error); + }); };
173-179: Consider adding delete confirmation.The delete action directly invokes
onDeletewithout confirmation. For destructive actions, consider adding a confirmation dialog or ensuring the parent handles undo/confirmation.apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx (1)
34-38: Type assertion onstatus.typecould be improved for type safety.The cast
status.type as StatusTypesilently masks type mismatches by falling through to a generic circle icon fallback inStatusIcon. While the database schema comment andStatusTypeunion currently align (both define the same five values: "backlog", "unstarted", "started", "completed", "canceled"), the Drizzletext()column doesn't enforce these constraints at runtime. Consider using a type guard or validation function to make invalid status types explicit rather than relying on the silent fallback.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (35)
apps/desktop/src/renderer/contexts/CollectionsProvider/collections.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/TasksTableView.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTopBar/TasksTopBar.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/AssigneeMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/PriorityIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/StatusIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/constants.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/ActiveIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AllIssuesIcon/AllIssuesIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AllIssuesIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/AssigneeMenuIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/BacklogIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/PriorityMenuIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/LabelsCell/LabelsCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/LabelsCell/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/utils/sorting/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/utils/sorting/sorting.tspackages/db/src/schema/schema.ts
✅ Files skipped from review due to trivial changes (5)
- apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/LabelsCell/index.ts
- apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AllIssuesIcon/index.ts
- apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/index.ts
- apps/desktop/src/renderer/screens/main/components/TasksView/utils/sorting/index.ts
- apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/index.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- apps/desktop/src/renderer/contexts/CollectionsProvider/collections.ts
- apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityMenuItems.tsx
- apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/TasksTableView.tsx
- apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/AssigneeMenuItems.tsx
- apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTopBar/TasksTopBar.tsx
🧰 Additional context used
📓 Path-based instructions (7)
apps/desktop/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)
apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined insrc/lib/trpc
Use alias as defined intsconfig.jsonwhen possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from@trpc/server/observableinstead of async generators, as the library explicitly checksisObservable(result)and throws an error otherwise
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/constants.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AllIssuesIcon/AllIssuesIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/PriorityMenuIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/StatusIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/ActiveIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/BacklogIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/LabelsCell/LabelsCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/PriorityIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/utils/sorting/sorting.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/AssigneeMenuIcon.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use object parameters for functions with 2+ parameters instead of positional arguments
Functions with 2+ parameters should accept a single params object with named properties for self-documentation and extensibility
Use prefixed console logging with context pattern: [domain/operation] message
Extract magic numbers and hardcoded values to named constants at module top
Use lookup objects/maps instead of repeated if (type === ...) conditionals
Avoid usinganytype - maintain type safety in TypeScript code
Never swallow errors silently - at minimum log them with context
Import from concrete files directly when possible - avoid barrel file abuse that creates circular dependencies
Avoid deep nesting (4+ levels) - use early returns, extract functions, and invert conditions
Use named properties in options objects instead of boolean parameters to avoid boolean blindness
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/constants.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AllIssuesIcon/AllIssuesIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/PriorityMenuIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/StatusIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/ActiveIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxpackages/db/src/schema/schema.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/BacklogIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/LabelsCell/LabelsCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/PriorityIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/utils/sorting/sorting.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/AssigneeMenuIcon.tsx
apps/desktop/src/renderer/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/constants.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AllIssuesIcon/AllIssuesIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/PriorityMenuIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/StatusIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/ActiveIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/BacklogIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/LabelsCell/LabelsCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/PriorityIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/utils/sorting/sorting.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/AssigneeMenuIcon.tsx
apps/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Drizzle ORM for all database operations - never use raw SQL
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/constants.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AllIssuesIcon/AllIssuesIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/PriorityMenuIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/StatusIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/ActiveIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/BacklogIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/LabelsCell/LabelsCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/PriorityIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/utils/sorting/sorting.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/AssigneeMenuIcon.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Biome for formatting and linting - run at root level with
bun run lint:fixorbiome check --write
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/constants.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AllIssuesIcon/AllIssuesIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/PriorityMenuIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/StatusIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/ActiveIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxpackages/db/src/schema/schema.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/BacklogIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/LabelsCell/LabelsCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/PriorityIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/utils/sorting/sorting.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/AssigneeMenuIcon.tsx
**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
One component per file - do not create multi-component files
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/PriorityCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AllIssuesIcon/AllIssuesIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/PriorityMenuIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/StatusIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/ActiveIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/BacklogIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/LabelsCell/LabelsCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/PriorityIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/AssigneeMenuIcon.tsx
packages/db/src/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Schema definitions must be in packages/db/src/ using Drizzle ORM
Files:
packages/db/src/schema/schema.ts
🧠 Learnings (6)
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/renderer/**/*.{ts,tsx} : Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility
Applied to files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/index.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/*/src/components/**/[A-Z]*.tsx : Create component folder structure with one folder per component: ComponentName/ComponentName.tsx with barrel export index.ts
Applied to files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/index.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to **/*.{ts,tsx} : Extract magic numbers and hardcoded values to named constants at module top
Applied to files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/constants.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/*/src/components/{ui,ai-elements,react-flow}/*.tsx : Use kebab-case single files for shadcn/ui components (e.g., button.tsx, base-node.tsx) in src/components/ui/, src/components/ai-elements, and src/components/react-flow/
Applied to files:
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AllIssuesIcon/AllIssuesIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/PriorityMenuIcon.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/PriorityIcon.tsx
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to **/*.{ts,tsx} : Avoid using `any` type - maintain type safety in TypeScript code
Applied to files:
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to packages/db/src/**/*.ts : Schema definitions must be in packages/db/src/ using Drizzle ORM
Applied to files:
packages/db/src/schema/schema.ts
🧬 Code graph analysis (11)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsx (1)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (1)
TaskWithStatus(35-38)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/PriorityCell.tsx (3)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (1)
TaskWithStatus(35-38)apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/PriorityIcon.tsx (1)
PriorityIcon(12-141)apps/desktop/src/renderer/screens/main/components/TasksView/utils/sorting/sorting.ts (1)
ALL_PRIORITIES(31-37)
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/constants.ts (1)
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/index.ts (1)
STATUS_COLORS(1-1)
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/PriorityMenuIcon.tsx (1)
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/PriorityIcon.tsx (1)
PriorityIcon(12-141)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (12)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/index.ts (2)
TaskWithStatus(1-1)useTasksTable(1-1)packages/db/src/schema/schema.ts (3)
SelectTask(170-170)SelectTaskStatus(94-94)tasks(96-167)apps/desktop/src/renderer/screens/main/components/TasksView/utils/sorting/sorting.ts (1)
compareTasks(69-89)apps/desktop/src/renderer/hooks/useHybridSearch.ts (1)
useHybridSearch(18-83)apps/desktop/create-release.sh (1)
info(44-46)apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/StatusIcon.tsx (2)
StatusIcon(18-189)StatusType(3-8)apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/PriorityCell.tsx (1)
PriorityCell(27-81)apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/index.ts (1)
PriorityCell(1-1)apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsx (1)
StatusCell(26-95)apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/index.ts (1)
StatusCell(1-1)apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsx (1)
AssigneeCell(20-102)apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/index.ts (1)
AssigneeCell(1-1)
apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsx (8)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (1)
TaskWithStatus(35-38)apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/index.ts (1)
TaskContextMenu(1-1)packages/ui/src/components/ui/context-menu.tsx (8)
ContextMenu(237-237)ContextMenuTrigger(238-238)ContextMenuContent(239-239)ContextMenuSub(248-248)ContextMenuSubTrigger(250-250)ContextMenuSubContent(249-249)ContextMenuItem(240-240)ContextMenuSeparator(244-244)apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/ActiveIcon.tsx (1)
ActiveIcon(8-20)apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx (1)
StatusMenuItems(18-46)apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/AssigneeMenuItems.tsx (1)
AssigneeMenuItems(20-67)apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/PriorityMenuIcon.tsx (1)
PriorityMenuIcon(8-13)apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityMenuItems.tsx (1)
PriorityMenuItems(27-51)
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx (3)
packages/db/src/schema/schema.ts (1)
SelectTaskStatus(94-94)apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/StatusIcon.tsx (1)
StatusIcon(18-189)apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/index.ts (1)
StatusIcon(2-2)
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/BacklogIcon.tsx (1)
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/StatusIcon.tsx (1)
StatusIcon(18-189)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/LabelsCell/LabelsCell.tsx (3)
packages/db/src/schema/schema.ts (1)
SelectTask(170-170)apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/LabelsCell/index.ts (1)
LabelsCell(1-1)packages/ui/src/components/ui/badge.tsx (1)
Badge(46-46)
apps/desktop/src/renderer/screens/main/components/TasksView/utils/sorting/sorting.ts (1)
packages/db/src/schema/schema.ts (2)
SelectTask(170-170)SelectTaskStatus(94-94)
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/AssigneeMenuIcon.tsx (1)
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/index.ts (1)
AssigneeMenuIcon(1-1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Deploy Admin
- GitHub Check: Deploy Marketing
- GitHub Check: Deploy API
- GitHub Check: Deploy Web
- GitHub Check: Deploy Docs
- GitHub Check: Build
🔇 Additional comments (31)
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/ActiveIcon/index.ts (1)
1-1: LGTM!Clean public API entry point for the
ActiveIconcomponent. This single re-export pattern is appropriate and doesn't introduce barrel file complexity or circular dependency risks.apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/AssigneeMenuIcon.tsx (1)
1-13: LGTM!Clean icon wrapper component that follows established patterns. Props are properly typed, the default
currentColoris appropriate for SVG inheritance, and the implementation is consistent with other icon components in the codebase.apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AssigneeMenuIcon/index.ts (1)
1-1: LGTM!Standard barrel file for a leaf-level component module. No circular dependency concerns.
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/AllIssuesIcon/AllIssuesIcon.tsx (1)
1-13: LGTM!Clean and minimal icon wrapper component. Good use of
"currentColor"as the default, which allows the icon to inherit text color from its parent context. The component follows the one-component-per-file guideline and properly avoids Node.js imports in the renderer process.apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/index.ts (1)
1-1: LGTM!Clean barrel export following the component folder structure pattern with
ComponentName/ComponentName.tsxandindex.tsbarrel export. Based on learnings.apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/BacklogIcon/BacklogIcon.tsx (1)
1-13: LGTM!Clean wrapper component that provides a simplified API over
StatusIconfor the backlog state. The implementation correctly:
- Follows one-component-per-file guideline
- Uses typed props interface
- Provides sensible default (
"currentColor") for the color prop- Appropriately omits
showHoverandprogressprops since the backlog icon variant inStatusIcondoesn't utilize progress, and this serves as a static icon wrapperapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/LabelsCell/LabelsCell.tsx (2)
1-31: Overall implementation looks good with clean structure and proper null handling.The component follows react-table conventions and handles edge cases appropriately with the early return for empty labels.
19-23: Use index-based keys or implement label deduplication to avoid React key collision warnings.Labels can contain duplicate values since the schema validation and handlers don't enforce uniqueness. Using
key={label}will cause React warnings and potential rendering issues if the same label appears multiple times. Either deduplicate labels at the data source or use the array index as the key.apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/index.ts (1)
1-1: LGTM!Standard barrel export following the component folder structure pattern with
ComponentName/ComponentName.tsxandindex.ts.apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsx (1)
20-45: LGTM!The component logic is well-structured with appropriate use of
useLiveQueryfor reactive data fetching anduseMemofor derived state. The early return when selecting the same assignee is a good optimization.apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/index.ts (1)
1-1: LGTM!Standard barrel export consistent with the component folder structure pattern.
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityIcon/PriorityIcon.tsx (1)
12-21: LGTM!Good use of default parameters and the component handles all priority cases appropriately with a safe
nullfallback for unmatched values.packages/db/src/schema/schema.ts (1)
58-91: LGTM on the newtaskStatusestable structure.The table design is well-structured with appropriate indexes for common query patterns (
organizationId,type) and a sensible unique constraint for external sync deduplication.apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/index.ts (1)
1-1: LGTM!Standard barrel export pattern for component encapsulation.
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/index.ts (1)
1-1: LGTM!Standard barrel export for the icon component.
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/PriorityCell.tsx (1)
27-80: Well-structured dropdown component.Good use of named constants for labels, proper context-prefixed error logging, and clean separation of concerns. The early return on same-priority selection is a nice optimization.
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/icons/PriorityMenuIcon/PriorityMenuIcon.tsx (1)
8-13: LGTM!Clean wrapper component. Omitting
statusTypeis reasonable for a generic menu icon where the active/inactive styling logic isn't applicable—the icon will use the providedcolordirectly.apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/index.ts (1)
1-2: LGTM!Clean barrel file re-exporting the relevant constants and component.
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsx (1)
44-58: LGTM!Good error handling with prefixed logging pattern and early return for same-status selection.
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/StatusIcon.tsx (2)
18-32: LGTM!Well-structured component with proper SVG accessibility (
aria-hidden), consistent sizing, and conditional hover effects.
74-114: Progress ring implementation looks correct.The clockwise fill from 12 o'clock using
rotate(-90 7 7)and dash-array calculation is well-implemented.apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (4)
35-38: LGTM!Clean type extension combining task, status, and assignee data.
60-77: LGTM!Well-structured live query with proper join semantics and soft-delete filtering.
214-245: Title column embeds StatusCell — verify this is intentional.The status icon appears twice: once for grouping (line 163) and once inline with title (line 222). If this is the intended design (status icon in each row), it looks correct. Otherwise, consider if duplication is needed.
272-288: LGTM!Proper table configuration with
autoResetExpanded: falseto preserve user's expanded state across data updates.apps/desktop/src/renderer/screens/main/components/TasksView/utils/sorting/sorting.ts (3)
4-28: LGTM!Clean lookup object pattern for ordering, following coding guidelines. Unknown types handled with sentinel value for stable sorting.
30-37: Clarify intent:ALL_PRIORITIESorder differs fromcomparePrioritiesForDropdownsort.
ALL_PRIORITIESstarts with "none" (line 32), butcomparePrioritiesForDropdownsorts "urgent" first (lines 115-119). If dropdowns iterateALL_PRIORITIESand then sort with the comparator, the final order will be urgent→high→medium→low→none.If
ALL_PRIORITIESis meant for direct iteration without sorting, update the comment to clarify. If it should match the sort order, reorder the array.Also applies to: 110-119
69-89: LGTM!Multi-criteria sort with clear precedence (status type → position → priority) and proper typing.
apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx (1)
1-16: LGTM! Clean interface definitions.The component interfaces are well-typed with proper use of generics for the
MenuItemcomponent type. The separation ofMenuItemPropsandStatusMenuItemsPropsprovides good flexibility for different menu implementations.apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsx (2)
1-25: LGTM! Imports are well-organized.Imports follow project conventions with proper use of aliases and relative paths. No prohibited Node.js modules in renderer code.
27-57: LGTM! Data loading and memoization are well-structured.The component properly handles potentially undefined data from
useLiveQuerywith defensive null checks before sorting and rendering.
| export const STATUS_COLORS: Record<string, string> = { | ||
| backlog: "text-gray-400", | ||
| todo: "text-blue-400", | ||
| planning: "text-purple-400", | ||
| working: "text-yellow-400", | ||
| "needs-feedback": "text-orange-400", | ||
| "ready-to-merge": "text-green-400", | ||
| completed: "text-green-600", | ||
| canceled: "text-red-400", | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find status-related type definitions
echo "=== Looking for StatusType or status enum definitions ==="
rg -n --type=ts "type\s+Status(Type)?\s*=" -A 5
echo ""
echo "=== Looking for status-related schema/types ==="
rg -n "task_statuses|StatusType|status.*enum" --type=ts -A 3
echo ""
echo "=== Looking for StatusIcon component that uses STATUS_COLORS ==="
fd -t f "StatusIcon\\.tsx?" | head -5 | xargs -I {} cat -n {}
echo ""
echo "=== Looking for how STATUS_COLORS is being used ==="
rg -n "STATUS_COLORS" --type=ts -B 2 -A 2Repository: superset-sh/superset
Length of output: 17825
🏁 Script executed:
#!/bin/bash
# Find all usages of STATUS_COLORS
echo "=== Searching for STATUS_COLORS usages ==="
rg "STATUS_COLORS" -B 2 -A 2
echo ""
echo "=== Searching for color assignments or mappings ==="
rg "color|Color" apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/ -A 1
echo ""
echo "=== Check the complete StatusIcon component ==="
cat -n apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/StatusIcon.tsx | head -30
echo ""
echo "=== Look for where StatusIcon is used with color prop ==="
rg "StatusIcon" apps/desktop/src/renderer/screens/main/components/TasksView/ -A 3 | grep -E "(StatusIcon|color)" | head -20Repository: superset-sh/superset
Length of output: 6958
🏁 Script executed:
#!/bin/bash
# Search for any actual usage of STATUS_COLORS beyond the definition and export
echo "=== All references to STATUS_COLORS in codebase ==="
rg "STATUS_COLORS" --type=ts --type=tsx
echo ""
echo "=== Check if it's imported anywhere ==="
rg "import.*STATUS_COLORS|from.*StatusIcon.*STATUS_COLORS" --type=ts --type=tsxRepository: superset-sh/superset
Length of output: 229
🏁 Script executed:
#!/bin/bash
# Search for STATUS_COLORS more broadly
echo "=== All references to STATUS_COLORS ==="
rg "STATUS_COLORS" -t ts
echo ""
echo "=== Check if imported anywhere ==="
rg "import.*STATUS_COLORS" -t tsRepository: superset-sh/superset
Length of output: 439
Remove unused STATUS_COLORS constant.
STATUS_COLORS is exported but never imported or used anywhere in the codebase. The constant can be safely deleted. Additionally, its keys ("todo", "planning", "working", "needs-feedback", "ready-to-merge") don't match the actual StatusType values ("unstarted", "started") used by the StatusIcon component, suggesting this was leftover code from a previous implementation.
🤖 Prompt for AI Agents
In
@apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/constants.ts
around lines 1 - 10, Delete the unused exported constant STATUS_COLORS from the
file (remove the entire constant declaration and its export) and remove any
imports or references to STATUS_COLORS across the codebase; verify the
StatusIcon component uses the current StatusType values ("unstarted", "started")
and not the removed keys, and adjust any remaining mapping logic to rely on the
component's existing status-to-class mapping instead of the removed constant.
| <ContextMenuItem onClick={handleCopyId}> | ||
| <span>Copy ID</span> | ||
| </ContextMenuItem> | ||
| <ContextMenuItem onClick={handleCopyTitle}> | ||
| <span>Copy Title</span> | ||
| </ContextMenuItem> | ||
| </ContextMenuSubContent> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Locate and examine ContextMenuItem definition
echo "=== Finding ContextMenuItem definition ==="
find . -type f -name "*.tsx" -o -name "*.ts" | xargs grep -l "export.*ContextMenuItem" | head -5
echo ""
echo "=== Checking UI package context-menu component ==="
find . -type f \( -name "*context-menu*" \) | head -10
echo ""
echo "=== Search for ContextMenuItem in the codebase ==="
rg "export.*ContextMenuItem" -A 5 --type tsx --type ts | head -50Repository: superset-sh/superset
Length of output: 304
🏁 Script executed:
#!/bin/bash
# Check the actual file and its imports
echo "=== TaskContextMenu file content (around lines 162-168) ==="
cat -n apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsx | sed -n '155,175p'
echo ""
echo "=== Check imports in TaskContextMenu ==="
head -30 apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsxRepository: superset-sh/superset
Length of output: 2014
🏁 Script executed:
#!/bin/bash
# Find the shadcn/ui context menu definition
echo "=== ContextMenuItem from shadcn/ui ==="
find . -type f -name "*.tsx" | xargs grep -l "ContextMenuItem" | grep -E "(ui|components)" | head -10
echo ""
echo "=== Get the actual ContextMenuItem type definition ==="
rg "interface.*ContextMenuItem|type.*ContextMenuItem|export.*const ContextMenuItem" -A 10 --type tsx --type tsRepository: superset-sh/superset
Length of output: 1075
🏁 Script executed:
#!/bin/bash
# Read the context-menu component definition
echo "=== ContextMenuItem definition ==="
cat -n packages/ui/src/components/ui/context-menu.tsx | head -100Repository: superset-sh/superset
Length of output: 3883
🏁 Script executed:
#!/bin/bash
# Find StatusMenuItems, AssigneeMenuItems, PriorityMenuItems definitions
echo "=== StatusMenuItems definition ==="
find . -name "StatusMenuItems*" -o -name "*StatusMenuItems*" | grep -v node_modules | head -5
echo ""
echo "=== AssigneeMenuItems definition ==="
find . -name "AssigneeMenuItems*" -o -name "*AssigneeMenuItems*" | grep -v node_modules | head -5
echo ""
echo "=== PriorityMenuItems definition ==="
find . -name "PriorityMenuItems*" -o -name "*PriorityMenuItems*" | grep -v node_modules | head -5Repository: superset-sh/superset
Length of output: 478
🏁 Script executed:
#!/bin/bash
# Read the StatusMenuItems file
echo "=== StatusMenuItems file ==="
cat apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/shared/StatusMenuItems.tsx 2>/dev/null || \
find . -name "StatusMenuItems.tsx" | head -1 | xargs catRepository: superset-sh/superset
Length of output: 1206
🏁 Script executed:
#!/bin/bash
# Read more of the context-menu component to find ContextMenuItem definition
echo "=== ContextMenuItem definition (lines 100-150) ==="
cat -n packages/ui/src/components/ui/context-menu.tsx | sed -n '100,150p'Repository: superset-sh/superset
Length of output: 2877
🏁 Script executed:
#!/bin/bash
# Check the full ContextMenuItem definition
echo "=== Complete ContextMenuItem function ==="
sed -n '/^function ContextMenuItem/,/^}/p' packages/ui/src/components/ui/context-menu.tsx | head -40Repository: superset-sh/superset
Length of output: 246
🏁 Script executed:
#!/bin/bash
# Check AssigneeMenuItems and PriorityMenuItems to see how they use MenuItem
echo "=== AssigneeMenuItems file ==="
cat apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/AssigneeMenuItems.tsx
echo ""
echo "=== PriorityMenuItems file ==="
cat apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/PriorityMenuItems.tsxRepository: superset-sh/superset
Length of output: 3156
🏁 Script executed:
#!/bin/bash
# Check how TaskContextMenu uses the shared menu components
echo "=== TaskContextMenu usage of shared components ==="
grep -A 5 -B 2 "StatusMenuItems\|AssigneeMenuItems\|PriorityMenuItems" apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsxRepository: superset-sh/superset
Length of output: 1523
🏁 Script executed:
#!/bin/bash
# Check radix-ui documentation or patterns - look for onSelect vs onClick in context menu usage
echo "=== Search for MenuItem usage patterns in codebase ==="
rg "MenuItem.*onClick" apps/desktop/src/renderer/ | head -20
echo ""
echo "=== Search for MenuItem with onSelect ==="
rg "MenuItem.*onSelect" apps/desktop/src/renderer/ | head -20Repository: superset-sh/superset
Length of output: 5861
🏁 Script executed:
#!/bin/bash
# Verify radix-ui context menu API - check if Item component uses onSelect
echo "=== Check what props radix-ui context menu Item actually supports ==="
rg "@radix-ui/react-context-menu" -A 2 packages/ui/src/components/ui/context-menu.tsx | head -10
# Look for documentation or comments about the prop
echo ""
echo "=== Check for any type definitions or comments about event handlers ==="
rg -B 5 -A 5 "ContextMenuPrimitive.Item" packages/ui/src/components/ui/context-menu.tsx | head -30Repository: superset-sh/superset
Length of output: 1691
🏁 Script executed:
#!/bin/bash
# Let's verify what the actual Radix UI Item component expects
echo "=== Radix UI context menu Item documentation check ==="
# Check package.json to see which version is used
cat packages/ui/package.json | grep "@radix-ui/react-context-menu"
echo ""
echo "=== Final check: Look at actual usage to understand the issue ==="
cat -n apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsx | sed -n '162,175p'Repository: superset-sh/superset
Length of output: 719
Use onSelect instead of onClick for ContextMenuItem event handlers.
Lines 162, 165, and 173 use onClick, but ContextMenuItem (from Radix UI's context menu Item) expects onSelect. This is inconsistent with other usages in the codebase (TabContentContextMenu, WorkspaceListItem, EditorContextMenu) and with StatusMenuItems, AssigneeMenuItems, and PriorityMenuItems which expect MenuItem components to have the onSelect callback signature. The onClick prop will be ignored, causing the copy ID, copy title, and delete actions to be non-functional.
🤖 Prompt for AI Agents
In
@apps/desktop/src/renderer/screens/main/components/TasksView/components/TasksTableView/components/TaskContextMenu/TaskContextMenu.tsx
around lines 162 - 168, The ContextMenuItem components in TaskContextMenu are
using the wrong event prop — replace onClick with onSelect for every
ContextMenuItem (e.g., the ones wired to handleCopyId, handleCopyTitle, and the
delete action) so the Radix UI Item selection handler is invoked; update the JSX
in TaskContextMenu to pass the existing handler functions via onSelect instead
of onClick to restore copy/delete functionality.
| // Sort statuses by workflow order (backlog → unstarted → started → completed → canceled) | ||
| const sortedStatuses = useMemo(() => { | ||
| return statuses.sort(compareStatusesForDropdown); | ||
| }, [statuses]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid mutating array with .sort() — use spread or .toSorted().
sort() mutates in place, which can cause issues with React's rendering assumptions. Create a new sorted array instead.
const sortedStatuses = useMemo(() => {
- return statuses.sort(compareStatusesForDropdown);
+ return [...statuses].sort(compareStatusesForDropdown);
}, [statuses]);🤖 Prompt for AI Agents
In
@apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsx
around lines 39 - 42, The current useMemo for sortedStatuses mutates the
original statuses array by calling statuses.sort(...); change it to produce a
new sorted array (e.g., using [...statuses].sort(compareStatusesForDropdown) or
statuses.toSorted(compareStatusesForDropdown)) so you do not mutate props/state;
update the useMemo block that defines sortedStatuses to sort a copy of statuses
using compareStatusesForDropdown.
| statusId: uuid("status_id") | ||
| .notNull() | ||
| .references(() => taskStatuses.id), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing onDelete behavior on statusId foreign key.
The statusId foreign key doesn't specify an onDelete behavior. If a taskStatuses row is deleted, this will cause a foreign key constraint violation. Consider specifying the intended behavior.
🔧 Suggested fix
If task statuses should not be deletable while tasks reference them (recommended):
statusId: uuid("status_id")
.notNull()
- .references(() => taskStatuses.id),
+ .references(() => taskStatuses.id, { onDelete: "restrict" }),Or if tasks should be cascade-deleted when their status is removed:
statusId: uuid("status_id")
.notNull()
- .references(() => taskStatuses.id),
+ .references(() => taskStatuses.id, { onDelete: "cascade" }),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| statusId: uuid("status_id") | |
| .notNull() | |
| .references(() => taskStatuses.id), | |
| statusId: uuid("status_id") | |
| .notNull() | |
| .references(() => taskStatuses.id, { onDelete: "restrict" }), |
🤖 Prompt for AI Agents
In @packages/db/src/schema/schema.ts around lines 105 - 107, The foreign key
definition for statusId (statusId: uuid("status_id").notNull().references(() =>
taskStatuses.id)) lacks an onDelete behavior; update the foreign key chain on
the statusId column to include an explicit .onDelete(...) call on the reference
(choose .onDelete('RESTRICT') or .onDelete('NO ACTION') if you want to prevent
deleting a taskStatuses row while tasks reference it, or .onDelete('CASCADE') if
deleting a status should delete associated tasks), so adjust the statusId column
definition to append the appropriate .onDelete(...) to the .references(() =>
taskStatuses.id) call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In
@apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsx:
- Around line 48-60: The trigger button inside AssigneeCell (wrapped by
DropdownMenuTrigger) lacks an accessible aria-label; update the button element
in AssigneeCell to include a descriptive aria-label (e.g., "Change assignee" or
"Change assignee for {task.assignee.name}" when task.assignee exists) so screen
readers convey the button's purpose; ensure the label is dynamic and falls back
to a generic phrase when task.assignee is null, keeping the existing Avatar and
HiOutlineUserCircle rendering unchanged.
- Around line 33-44: The handler handleSelectUser currently calls
collections.tasks.update(task.id, ...) with no error handling and closes the
dropdown immediately; wrap the update in a try/catch (await the update if it
returns a Promise), and in the catch log the error with context (e.g., task.id
and userId) and surface user feedback (reopen the dropdown or show an error
toast) instead of silently swallowing the failure so users know the assignment
failed; keep setOpen(false) only after a successful update or revert UI state on
error.
In
@apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsx:
- Around line 35-37: The useMemo block that defines sortedStatuses calls
statuses.sort(...), which mutates the original array; change it to sort a
shallow copy instead (e.g., spread or slice) so the original statuses from the
query isn’t mutated — update the useMemo that returns sortedStatuses
(referencing sortedStatuses, statuses, and compareStatusesForDropdown) to create
a copy of statuses before calling sort and return that sorted copy.
🧹 Nitpick comments (2)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (1)
105-120: Consider extracting magic numbers to named constants.The calculation is well-documented with comments, but extracting constants would improve readability and make adjustments easier. This is a minor refinement.
♻️ Optional refactor
+const SLUG_FONT_SIZE_REM = 0.75; // text-xs = 12px +const MONOSPACE_CHAR_WIDTH_RATIO = 0.5; // ~0.5em per char in monospace +const SLUG_COLUMN_PADDING_REM = 0.5; +const DEFAULT_SLUG_COLUMN_WIDTH = "5rem"; + // Calculate optimal slug column width based on longest slug const slugColumnWidth = useMemo(() => { - if (!data || data.length === 0) return "5rem"; + if (!data || data.length === 0) return DEFAULT_SLUG_COLUMN_WIDTH; const longestSlug = data.reduce((longest, task) => { return task.slug.length > longest.length ? task.slug : longest; }, ""); - // Monospace font-mono at text-xs (0.75rem = 12px) - // Each character is ~0.5em of the font size = 0.5 * 0.75rem = 0.375rem per char - const remPerChar = 0.5 * 0.75; // 0.375rem per character - const padding = 0.5; // rem for horizontal padding + const remPerChar = MONOSPACE_CHAR_WIDTH_RATIO * SLUG_FONT_SIZE_REM; - const width = longestSlug.length * remPerChar + padding; + const width = longestSlug.length * remPerChar + SLUG_COLUMN_PADDING_REM; return `${Math.ceil(width * 10) / 10}rem`; // Round to 1 decimal }, [data]);apps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsx (1)
51-57: Consider extractingformatDateto a shared utility.The function is defined inline and recreated on each render. While the performance impact is minimal, extracting it to a shared utility (e.g.,
renderer/utils/formatDate.ts) would improve reusability across components.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (13)
apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/components/MemberRow/MemberRow.tsxapps/desktop/src/renderer/screens/main/components/TasksView/components/shared/AssigneeMenuItems.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/useHybridSearch.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/OrganizationDropdown.tsxpackages/ui/package.jsonpackages/ui/src/atoms/Avatar/Avatar.tsxpackages/ui/src/atoms/Avatar/index.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/ui/src/atoms/Avatar/Avatar.tsx
- apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/AssigneeMenuItems.tsx
- packages/ui/src/atoms/Avatar/index.ts
🧰 Additional context used
📓 Path-based instructions (6)
apps/desktop/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)
apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined insrc/lib/trpc
Use alias as defined intsconfig.jsonwhen possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from@trpc/server/observableinstead of async generators, as the library explicitly checksisObservable(result)and throws an error otherwise
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/useHybridSearch.tsapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/components/MemberRow/MemberRow.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/OrganizationDropdown.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use object parameters for functions with 2+ parameters instead of positional arguments
Functions with 2+ parameters should accept a single params object with named properties for self-documentation and extensibility
Use prefixed console logging with context pattern: [domain/operation] message
Extract magic numbers and hardcoded values to named constants at module top
Use lookup objects/maps instead of repeated if (type === ...) conditionals
Avoid usinganytype - maintain type safety in TypeScript code
Never swallow errors silently - at minimum log them with context
Import from concrete files directly when possible - avoid barrel file abuse that creates circular dependencies
Avoid deep nesting (4+ levels) - use early returns, extract functions, and invert conditions
Use named properties in options objects instead of boolean parameters to avoid boolean blindness
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/useHybridSearch.tsapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/components/MemberRow/MemberRow.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/OrganizationDropdown.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsx
apps/desktop/src/renderer/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/useHybridSearch.tsapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/components/MemberRow/MemberRow.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/OrganizationDropdown.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsx
apps/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Drizzle ORM for all database operations - never use raw SQL
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/useHybridSearch.tsapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/components/MemberRow/MemberRow.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/OrganizationDropdown.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Biome for formatting and linting - run at root level with
bun run lint:fixorbiome check --write
Files:
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/index.tsapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/useHybridSearch.tsapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/components/MemberRow/MemberRow.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/OrganizationDropdown.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsx
**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
One component per file - do not create multi-component files
Files:
apps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/components/MemberRow/MemberRow.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/OrganizationDropdown.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsxapps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsxapps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsx
🧠 Learnings (11)
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to **/*.{ts,tsx} : Import from concrete files directly when possible - avoid barrel file abuse that creates circular dependencies
Applied to files:
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/index.tspackages/ui/package.json
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/*/src/components/**/*.{ts,tsx} : Co-locate component dependencies: utils, hooks, constants, config, tests, stories in the same folder as the component using them
Applied to files:
packages/ui/package.json
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/*/src/components/**/[A-Z]*.tsx : Create component folder structure with one folder per component: ComponentName/ComponentName.tsx with barrel export index.ts
Applied to files:
packages/ui/package.json
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use Biome for formatting and linting - run at root level with `bun run lint:fix` or `biome check --write`
Applied to files:
packages/ui/package.json
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/*/src/components/{ui,ai-elements,react-flow}/*.tsx : Use kebab-case single files for shadcn/ui components (e.g., button.tsx, base-node.tsx) in src/components/ui/, src/components/ai-elements, and src/components/react-flow/
Applied to files:
packages/ui/package.jsonapps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/OrganizationDropdown.tsx
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to **/*.tsx : One component per file - do not create multi-component files
Applied to files:
packages/ui/package.json
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/renderer/**/*.{ts,tsx} : Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility
Applied to files:
packages/ui/package.json
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Use alias as defined in `tsconfig.json` when possible
Applied to files:
packages/ui/package.json
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to **/*.test.{ts,tsx} : Co-locate tests with implementation files using .test.ts or .test.tsx suffix
Applied to files:
packages/ui/package.json
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to **/*.{ts,tsx} : Extract magic numbers and hardcoded values to named constants at module top
Applied to files:
packages/ui/package.json
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to **/*.{ts,tsx} : Avoid using `any` type - maintain type safety in TypeScript code
Applied to files:
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx
🧬 Code graph analysis (5)
apps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/components/MemberRow/MemberRow.tsx (2)
packages/ui/src/atoms/Avatar/Avatar.tsx (1)
Avatar(59-59)packages/ui/src/atoms/Avatar/index.ts (1)
Avatar(2-2)
apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx (2)
packages/ui/src/atoms/Avatar/Avatar.tsx (1)
Avatar(59-59)packages/ui/src/atoms/Avatar/index.ts (1)
Avatar(2-2)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (6)
packages/db/src/schema/schema.ts (3)
SelectTask(170-170)SelectTaskStatus(94-94)tasks(96-167)apps/desktop/src/renderer/screens/main/components/TasksView/utils/sorting/sorting.ts (1)
compareTasks(69-89)apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/useHybridSearch.ts (1)
useHybridSearch(18-83)apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/PriorityCell/PriorityCell.tsx (1)
PriorityCell(27-81)apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsx (1)
StatusCell(23-79)apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsx (1)
AssigneeCell(19-95)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsx (5)
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (1)
TaskWithStatus(35-38)apps/desktop/src/renderer/screens/main/components/TasksView/utils/sorting/sorting.ts (1)
compareStatusesForDropdown(95-108)packages/db/src/schema/schema.ts (1)
SelectTaskStatus(94-94)apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusIcon/index.ts (2)
StatusIcon(2-2)StatusType(2-2)apps/desktop/src/renderer/screens/main/components/TasksView/components/shared/StatusMenuItems.tsx (1)
StatusMenuItems(18-46)
apps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsx (3)
packages/db/src/schema/auth.ts (1)
members(105-122)packages/ui/src/atoms/Avatar/Avatar.tsx (1)
Avatar(59-59)packages/ui/src/atoms/Avatar/index.ts (1)
Avatar(2-2)
🔇 Additional comments (21)
packages/ui/package.json (2)
15-15: LGTM!The wildcard export pattern correctly exposes atom components via their barrel exports, aligning with the project's component folder structure convention (
ComponentName/index.ts).
52-52: LGTM! Adding the workspace dependency is appropriate for sharing types and utilities across the monorepo. Circular dependency verified—@superset/sharedhas no dependency on@superset/ui.apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/useHybridSearch.ts (2)
49-80: Search logic is correct and well-organized.The deduplication approach (exact matches first, then fuzzy matches excluding already-found IDs) ensures clean results without duplicates. Score inversion from Fuse's 0-best to 1-best is handled correctly.
Minor observation: When query is empty, returning all items with
matchType: "exact"is slightly misleading semantically—consider"all"or"unfiltered"if the match type is consumed downstream for display logic. Not blocking if this is intentional.
1-47: Well-structured hybrid search setup.The Fuse.js configuration is solid:
- Exact search with
threshold: 0for slug/labels ensures precise identifier matching- Fuzzy search with
threshold: 0.3for title/description provides reasonable toleranceignoreLocation: trueis appropriate since task data doesn't have positional relevance- Proper use of
useMemoto avoid recreating Fuse indexes on every renderfuse.jsis properly declared inapps/desktop/package.jsonas^7.1.0apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useHybridSearch/index.ts (1)
1-1: Acceptable barrel export pattern.This single re-export provides a clean public API surface for the hook without creating circular dependency risks. Based on learnings, the guideline is to avoid barrel file abuse—this minimal re-export is a legitimate organizational pattern.
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsx (2)
1-13: LGTM!Imports are well-organized, use alias paths as per coding guidelines, and correctly avoid Node.js modules in the renderer process.
15-17: LGTM!Clean and properly typed interface definition.
apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsx (3)
1-17: LGTM!Imports are well-organized and use aliases as defined in tsconfig.json. The relevant types and utilities are imported from appropriate locations.
39-53: LGTM!The handler correctly short-circuits when the same status is selected, uses try/catch with prefixed logging (
[StatusCell]) per coding guidelines, and properly closes the dropdown after updates.
55-78: LGTM!The dropdown implementation correctly uses shared
StatusMenuItemsfor consistency and delegates rendering to the appropriate child components. The scrollable container withmax-h-64handles long status lists gracefully.apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/useTasksTable.tsx (6)
1-33: LGTM!Imports are well-organized with clear groupings: external packages, UI components, hooks, and local components. Aliases are used appropriately per tsconfig.json.
35-45: LGTM!The
TaskWithStatustype correctly extendsSelectTaskwith the joined relations. TheUseTasksTableParamsinterface follows the coding guideline to use object parameters for functions with 2+ parameters.
60-77: LGTM!The query correctly uses Drizzle-style operations via
@tanstack/dbwith proper inner/left joins and filters out soft-deleted tasks. Theassignee ?? nullcoercion ensures type safety for the nullable relation.
79-92: LGTM!Correctly spreads
allDatabefore sorting to avoid mutation. The hybrid search integration properly falls back to the full sorted list when the query is empty.
125-269: LGTM!The column definitions are static and don't close over external mutable state, so the empty dependency array is correct. The
filterFnfor status correctly implements the tab-based filtering logic. The JSX cell renderers appropriately handle grouped/placeholder states.
272-290: LGTM!The table configuration properly wires up all state handlers and row models. The
autoResetExpanded: falseis a good choice to preserve user's expansion state across data changes.apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx (1)
1-1: LGTM! Clean migration to unified Avatar component.The Avatar import and usage are consistent with the new component API. The
fullNameandimageprops correctly map to user data.Also applies to: 39-39
apps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/components/MemberRow/MemberRow.tsx (1)
2-2: LGTM! Consistent Avatar migration.The Avatar usage aligns with the unified component API and the pattern used in other settings components.
Also applies to: 55-55
apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceSidebarHeader/OrganizationDropdown.tsx (1)
67-72: LGTM! Consistent Avatar usage across trigger and menu items.All three Avatar instances correctly use the unified component API with appropriate sizes (
smfor triggers,xsfor submenu items) androunded-mdstyling for organization avatars.Also applies to: 82-87, 136-141
apps/desktop/src/renderer/screens/main/components/SettingsView/TeamSettings/TeamSettings.tsx (2)
115-119: LGTM! Avatar usage consistent with the unified component API.The
size="md"is appropriate for table row avatars, and the props align with the pattern used in other settings components.
23-43: Query refactoring consolidates member and user data correctly.The leftJoin between members and users is well-structured, with optional chaining on user fields properly handling the join semantics where user data may be absent. Multiple orderBy calls chain correctly—first by role, then by createdAt—providing the expected sort order for team members.
| const handleSelectUser = (userId: string | null) => { | ||
| if (userId === assigneeId) { | ||
| setOpen(false); | ||
| return; | ||
| } | ||
|
|
||
| setOpen(false); | ||
|
|
||
| collections.tasks.update(task.id, (draft) => { | ||
| draft.assigneeId = userId; | ||
| }); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for the update operation.
The collections.tasks.update() call has no error handling. If the update fails, the dropdown closes but the user receives no feedback about the failure. As per coding guidelines: "Never swallow errors silently - at minimum log them with context."
🔧 Suggested fix with error handling
const handleSelectUser = (userId: string | null) => {
if (userId === assigneeId) {
setOpen(false);
return;
}
setOpen(false);
- collections.tasks.update(task.id, (draft) => {
- draft.assigneeId = userId;
- });
+ try {
+ collections.tasks.update(task.id, (draft) => {
+ draft.assigneeId = userId;
+ });
+ } catch (error) {
+ console.error("[AssigneeCell/handleSelectUser] Failed to update task assignee:", error);
+ }
};If the update is async, consider:
- collections.tasks.update(task.id, (draft) => {
- draft.assigneeId = userId;
- });
+ collections.tasks.update(task.id, (draft) => {
+ draft.assigneeId = userId;
+ }).catch((error) => {
+ console.error("[AssigneeCell/handleSelectUser] Failed to update task assignee:", error);
+ });🤖 Prompt for AI Agents
In
@apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsx
around lines 33 - 44, The handler handleSelectUser currently calls
collections.tasks.update(task.id, ...) with no error handling and closes the
dropdown immediately; wrap the update in a try/catch (await the update if it
returns a Promise), and in the catch log the error with context (e.g., task.id
and userId) and surface user feedback (reopen the dropdown or show an error
toast) instead of silently swallowing the failure so users know the assignment
failed; keep setOpen(false) only after a successful update or revert UI state on
error.
| <DropdownMenuTrigger asChild> | ||
| <button type="button" className="cursor-pointer"> | ||
| {task.assignee ? ( | ||
| <Avatar | ||
| size="xs" | ||
| fullName={task.assignee.name} | ||
| image={task.assignee.image} | ||
| /> | ||
| ) : ( | ||
| <HiOutlineUserCircle className="size-5 text-muted-foreground" /> | ||
| )} | ||
| </button> | ||
| </DropdownMenuTrigger> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add accessible label to the trigger button.
The button lacks an aria-label, which makes it unclear to screen reader users what this control does.
♿ Suggested fix
<DropdownMenuTrigger asChild>
- <button type="button" className="cursor-pointer">
+ <button
+ type="button"
+ className="cursor-pointer"
+ aria-label={task.assignee ? `Assigned to ${task.assignee.name}` : "Assign task"}
+ >
{task.assignee ? (📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <DropdownMenuTrigger asChild> | |
| <button type="button" className="cursor-pointer"> | |
| {task.assignee ? ( | |
| <Avatar | |
| size="xs" | |
| fullName={task.assignee.name} | |
| image={task.assignee.image} | |
| /> | |
| ) : ( | |
| <HiOutlineUserCircle className="size-5 text-muted-foreground" /> | |
| )} | |
| </button> | |
| </DropdownMenuTrigger> | |
| <DropdownMenuTrigger asChild> | |
| <button | |
| type="button" | |
| className="cursor-pointer" | |
| aria-label={task.assignee ? `Assigned to ${task.assignee.name}` : "Assign task"} | |
| > | |
| {task.assignee ? ( | |
| <Avatar | |
| size="xs" | |
| fullName={task.assignee.name} | |
| image={task.assignee.image} | |
| /> | |
| ) : ( | |
| <HiOutlineUserCircle className="size-5 text-muted-foreground" /> | |
| )} | |
| </button> | |
| </DropdownMenuTrigger> |
🤖 Prompt for AI Agents
In
@apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/AssigneeCell/AssigneeCell.tsx
around lines 48 - 60, The trigger button inside AssigneeCell (wrapped by
DropdownMenuTrigger) lacks an accessible aria-label; update the button element
in AssigneeCell to include a descriptive aria-label (e.g., "Change assignee" or
"Change assignee for {task.assignee.name}" when task.assignee exists) so screen
readers convey the button's purpose; ensure the label is dynamic and falls back
to a generic phrase when task.assignee is null, keeping the existing Avatar and
HiOutlineUserCircle rendering unchanged.
| const sortedStatuses = useMemo(() => { | ||
| return statuses.sort(compareStatusesForDropdown); | ||
| }, [statuses]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid mutating arrays in useMemo – use a spread copy before sorting.
Array.sort() mutates in place. Since statuses is derived from the query result, this could cause unexpected behavior or React reconciliation issues.
🔧 Proposed fix
const sortedStatuses = useMemo(() => {
- return statuses.sort(compareStatusesForDropdown);
+ return [...statuses].sort(compareStatusesForDropdown);
}, [statuses]);🤖 Prompt for AI Agents
In
@apps/desktop/src/renderer/screens/main/components/TasksView/hooks/useTasksTable/components/StatusCell/StatusCell.tsx
around lines 35 - 37, The useMemo block that defines sortedStatuses calls
statuses.sort(...), which mutates the original array; change it to sort a
shallow copy instead (e.g., spread or slice) so the original statuses from the
query isn’t mutated — update the useMemo that returns sortedStatuses
(referencing sortedStatuses, statuses, and compareStatusesForDropdown) to create
a copy of statuses before calling sort and return that sorted copy.
Description
Related Issues
Type of Change
Testing
Screenshots (if applicable)
Additional Notes
Summary by CodeRabbit
Release Notes
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.