feat: redesign repo cards -- owner/repo, timestamps, sort tabs (OPE-157)#279
Conversation
…-157) Repo cards now show: - owner/repo slug extracted from git_url (e.g. Effect-TS/effect) - File count + function count side by side - 'Indexed 2h ago' relative timestamp from last_indexed_at - Status dot instead of full badge (cleaner) Sort bar above the grid: - Recent (default): sorted by last_indexed_at descending - Name: alphabetical - Size: largest file count first - Repo count shown on right side Repository type updated with function_count, created_at, last_indexed_at. These fields already exist in the backend (select *), just weren't typed. 230 lines, same glow effect, premium feel.
|
@DevanshuNEU is attempting to deploy a commit to the Dev's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds client-side sorting and UI enhancements to the repo list, introduces a per-repo delete workflow with confirmation, threads delete callbacks DashboardHome → RepoListView → RepoList, and makes SettingsPage read maxRepos from usage. Types updated to include repo metadata. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant RepoListUI as "RepoList / RepoCard"
participant Dashboard as "DashboardHome"
participant API
User->>RepoListUI: open delete menu & request delete
RepoListUI->>User: show DeleteConfirmDialog (typed repo name)
User->>RepoListUI: confirm deletion
RepoListUI->>Dashboard: onDeleteRepo(repoId)
Dashboard->>API: DELETE /repos/{repoId} (Authorization)
API-->>Dashboard: 200 OK / error
Dashboard->>RepoListUI: refetch repos / update state
RepoListUI-->>User: updated list (repo removed or error shown)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/RepoList.tsx`:
- Around line 47-59: StatusDot currently treats any non-'indexed' status as
"Pending", hiding error states; update the component (StatusDot and its status
prop handling) to branch explicitly for 'error' (e.g., set a new isError =
status === 'error') and render a distinct label and CSS classes for error (e.g.,
'text-destructive' and 'bg-destructive' or your design system's error classes),
keep 'Indexed' for isIndexed, and fall back to 'Pending' only for other
statuses; adjust both the outer text color and the inner dot classes and the
displayed text accordingly.
- Around line 125-130: The rendering currently skips showing function counts
when repo.function_count is 0 because of the `> 0` guard; update the conditional
in RepoList.tsx (the block that references repo.function_count and uses the
FunctionSquare icon) to render whenever repo.function_count is present (not
null/undefined) rather than strictly greater than zero so a zero value displays;
i.e., remove the `> 0` check and only guard against null/undefined (e.g., check
repo.function_count != null or !== undefined) so zeroes are shown.
- Around line 19-23: The parseRepoSlug function doesn't handle SSH-style GitHub
URLs (e.g., git@github.com:owner/repo.git); update parseRepoSlug to normalize
the input (strip trailing .git) and match both HTTP(S) and SSH patterns (e.g.,
use a regex that captures either github.com/owner/repo or github.com:owner/repo
or an alternate branch that checks for /^git@github\.com:(.+\/.+)$/), returning
the captured owner/repo or '' if none; reference the parseRepoSlug function and
its cleaned/match variables when making the change.
- Around line 143-159: Replace the custom SortTab component with shadcn/ui Tabs
primitives: remove the SortTab definition and update the sort-mode UI that
currently uses SortTab (the sort mode selection invocation) to use <Tabs
defaultValue={...}> wrapping a <TabsList> with individual <TabsTrigger
value="..."> labels for each sort option; wire each TabsTrigger's onValueChange
or Tabs' onValueChange to the existing sort change handler so current sorting
state and active styling map to Tabs' value, and import Tabs, TabsList,
TabsTrigger from "@/components/ui/tabs". Ensure any props/types tied to SortTab
are migrated to the Tabs value API and that keyboard/accessibility behavior is
provided by the Tabs primitives.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 29d6d626-964f-4ac4-9bed-f804a0dac01a
📒 Files selected for processing (2)
frontend/src/components/RepoList.tsxfrontend/src/types.ts
…ded 'Free' (OPE-158) Bug: Settings page had const MAX_REPOS = 3 hardcoded and 'Free tier limit' text for ALL users. Enterprise users saw '5/3 - 0 slots available'. Fix: Uses useUserUsage hook for actual tier and repo limit. - 'Free tier limit' -> 'Enterprise tier limit' (dynamic, capitalized) - '5/3' -> '5/10' for Enterprise (actual limit from backend) - Removed hardcoded MAX_REPOS constant Closes OPE-158
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/pages/SettingsPage.tsx`:
- Around line 34-36: The UI mixes sources for repo slot counts (using
repos.length for current and usage.repositories.limit for limit) and defaults to
a realistic free/1 state before usage loads; change the component to derive both
current and limit from the usage payload (useUserUsage ->
usage.repositories.current and usage.repositories.limit) and stop falling back
to repos.length or hard defaults — render a loading/unavailable state while the
usage query is unresolved. Update references to maxRepos and any display that
used repos.length (e.g., the slot counter) to use usage.repositories.current and
usage.repositories.limit, and ensure tier comes from usage.tier (not a default
'free') until usage is loaded. Ensure similar fixes are applied to the other
occurrences noted (around lines with repos/slot rendering).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 944cc07d-f021-4622-9c4e-c331723fd45e
📒 Files selected for processing (1)
frontend/src/pages/SettingsPage.tsx
…g (OPE-159)
Each repo card now has a three-dot menu (visible on hover) with 'Delete repository'.
Clicking shows confirmation dialog: 'This will permanently remove [name] and all
its indexed data. This action cannot be undone.'
Flow: hover card -> click ... -> click Delete -> confirm -> DELETE /repos/{id}
- Toast notification on success/failure
- Repo list refreshes automatically
- If deleted repo was selected, clears selection
Props chain: DashboardHome (handler) -> RepoListView -> RepoList -> RepoCard
Closes OPE-159
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/src/components/RepoList.tsx (1)
85-105:⚠️ Potential issue | 🟠 MajorAvoid nesting the overflow trigger inside the card button.
RepoCardrenders the entire card as a<button>and then nests another<button>for the kebab menu. That is invalid interactive nesting and it breaks keyboard/focus behavior.♿ Suggested fix
- const cardRef = useRef<HTMLButtonElement>(null) + const cardRef = useRef<HTMLDivElement>(null) ... - <motion.button + <motion.div ref={cardRef} + role="button" + tabIndex={0} initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: index * 0.04, duration: 0.25 }} whileHover={{ y: -2 }} onClick={onSelect} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault() + onSelect() + } + }} onMouseMove={(e) => { if (!cardRef.current) return const rect = cardRef.current.getBoundingClientRect() setMousePos({ x: e.clientX - rect.left, y: e.clientY - rect.top }) }} ... - </motion.button> + </motion.div>Also applies to: 127-135
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/RepoList.tsx` around lines 85 - 105, The repo card currently nests the kebab/overflow trigger button inside the outer interactive motion.button (RepoCard), which creates invalid nested interactive elements and breaks keyboard/focus; fix by extracting the overflow/kebab trigger out of the motion.button: wrap motion.button and the kebab trigger in a non-interactive container (e.g., a div), keep the motion.button (with ref cardRef, onSelect, onMouseMove, onMouseEnter/onMouseLeave) as the primary click target, and render the kebab/menu trigger as a sibling button absolutely positioned over the card (or as a separate focusable element) so it is not nested; update any handlers/refs that previously relied on nesting (also apply same change to the other occurrence around lines 127-135).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/dashboard/DashboardHome.tsx`:
- Around line 217-227: The delete flow currently throws a generic Error('Failed
to delete') which hides the API's real error; update the code to preserve
backend error details by calling the shared helper extractErrorMessage(...) when
the response is not ok (e.g. const msg = await extractErrorMessage(response);
throw new Error(msg)) or by passing that message directly to toast.error in the
catch. Locate the delete block around the fetch to `${API_URL}/repos/${repoId}`
in DashboardHome.tsx and replace the generic throw with logic that uses
extractErrorMessage(response) (retain subsequent calls to toast.success,
refreshRepos(), selectedRepo and setSelectedRepo as-is).
---
Outside diff comments:
In `@frontend/src/components/RepoList.tsx`:
- Around line 85-105: The repo card currently nests the kebab/overflow trigger
button inside the outer interactive motion.button (RepoCard), which creates
invalid nested interactive elements and breaks keyboard/focus; fix by extracting
the overflow/kebab trigger out of the motion.button: wrap motion.button and the
kebab trigger in a non-interactive container (e.g., a div), keep the
motion.button (with ref cardRef, onSelect, onMouseMove,
onMouseEnter/onMouseLeave) as the primary click target, and render the
kebab/menu trigger as a sibling button absolutely positioned over the card (or
as a separate focusable element) so it is not nested; update any handlers/refs
that previously relied on nesting (also apply same change to the other
occurrence around lines 127-135).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 91a1f49e-da04-41f4-a2dd-933c7f4b5a7e
📒 Files selected for processing (3)
frontend/src/components/RepoList.tsxfrontend/src/components/dashboard/DashboardHome.tsxfrontend/src/components/dashboard/RepoListView.tsx
Three-dot menu in top-right of repo detail header (next to repo name). Click '...' -> 'Delete repository' -> confirmation dialog -> DELETE API. Delete is now available in TWO places: 1. Repo card grid (hover card -> three dots) 2. Inside repo detail view (top-right header) Both use same confirmation dialog and same handleDeleteRepo handler. After delete, navigates back to repo list automatically.
Both delete dialogs (card grid + detail view) now require typing the exact repo name before the Delete button enables. Shared DeleteConfirmDialog component: - Shows repo name in bold - Input field with placeholder matching repo name - Delete button disabled until text matches exactly - Button text: 'Delete opencodeintel' (includes name) - Input clears on cancel or close Reused in RepoList (card grid) and RepoDetailView (detail header).
There was a problem hiding this comment.
🧹 Nitpick comments (1)
frontend/src/components/RepoList.tsx (1)
46-62: Consider handling invalid date strings intimeAgo.If
dateStris malformed,new Date(dateStr).getTime()returnsNaN, which propagates through the comparisons and eventually callstoLocaleDateString()on an invalid date. While backend data should be valid, a guard would make this more robust.Proposed fix
function timeAgo(dateStr?: string): string { if (!dateStr) return '' const now = Date.now() const then = new Date(dateStr).getTime() + if (Number.isNaN(then)) return '' const diff = now - then if (diff < 0) return ''🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/RepoList.tsx` around lines 46 - 62, The timeAgo function should validate the parsed date before using it: replace direct use of new Date(dateStr).getTime() with const parsed = new Date(dateStr); if (isNaN(parsed.getTime())) return '' to guard against malformed dateStr, then use parsed.getTime() for diff and parsed.toLocaleDateString(...) at the end; ensure the negative-diff and other existing branches remain unchanged so invalid dates never reach toLocaleDateString.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@frontend/src/components/RepoList.tsx`:
- Around line 46-62: The timeAgo function should validate the parsed date before
using it: replace direct use of new Date(dateStr).getTime() with const parsed =
new Date(dateStr); if (isNaN(parsed.getTime())) return '' to guard against
malformed dateStr, then use parsed.getTime() for diff and
parsed.toLocaleDateString(...) at the end; ensure the negative-diff and other
existing branches remain unchanged so invalid dates never reach
toLocaleDateString.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: c34619e1-379d-4585-b8d7-ebf579a3567b
📒 Files selected for processing (3)
frontend/src/components/RepoList.tsxfrontend/src/components/dashboard/DashboardHome.tsxfrontend/src/components/dashboard/RepoDetailView.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/components/dashboard/DashboardHome.tsx
… URLs StatusDot: added 'failed' status with text-destructive/bg-destructive. Previously showed failed repos as 'Pending' with pulsing dot. parseRepoSlug: now matches both HTTPS (github.com/owner/repo) and SSH (git@github.com:owner/repo) URL formats. Skipped: function_count > 0 guard (intentional -- 0 before indexing is misleading), SortTab -> shadcn Tabs (custom component is simpler).
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
frontend/src/components/RepoList.tsx (1)
172-177:⚠️ Potential issue | 🟡 MinorRender
function_counteven when it is0.The
> 0check hides valid zero values, so some cards lose the new stat even though the backend returned it.Small fix
- {repo.function_count != null && repo.function_count > 0 && ( + {repo.function_count != null && ( <span className="flex items-center gap-1"> <FunctionSquare className="w-3 h-3" /> {repo.function_count.toLocaleString()} </span> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/RepoList.tsx` around lines 172 - 177, The UI hides valid zero values by checking "repo.function_count > 0"; update the render condition in RepoList.tsx to only test for non-null/undefined so zeros display: replace the combined check with only "repo.function_count != null" around the span that uses FunctionSquare and repo.function_count.toLocaleString(), ensuring 0 is rendered while still skipping absent values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/RepoList.tsx`:
- Around line 99-105: The card currently renders a <motion.button> (ref cardRef)
with an inner <button> for the overflow/menu, causing nested button HTML and
conflicting focus/click behavior; change the outer card from a button to a
non-button container (e.g., div or motion.div) when secondary actions are
present, keep the onSelect handler on that container (or move to an inner
focusable element) and render the overflow trigger as its own button with an
accessible aria-label (the menu/icon trigger). Update the component logic in
RepoList to conditionally choose motion.button vs motion.div (or wrap clickable
area in a single interactive element) so the inner menu button is the only
<button>, and ensure keyboard focus/aria-labels are correct for the overflow
control.
- Around line 321-329: The confirmation input in RepoList.tsx (the Input bound
to confirmText via setConfirmText and showing repoName as placeholder) lacks a
programmatic label; add an accessible label by either adding a visually-hidden
<label> tied to that Input (using htmlFor/id) or by supplying a clear aria-label
or aria-labelledby that includes the repoName and explains the action (e.g.,
"Type repository name to confirm deletion: {repoName}"), so screen readers
announce exactly what must be typed before allowing the destructive action.
---
Duplicate comments:
In `@frontend/src/components/RepoList.tsx`:
- Around line 172-177: The UI hides valid zero values by checking
"repo.function_count > 0"; update the render condition in RepoList.tsx to only
test for non-null/undefined so zeros display: replace the combined check with
only "repo.function_count != null" around the span that uses FunctionSquare and
repo.function_count.toLocaleString(), ensuring 0 is rendered while still
skipping absent values.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 70eca776-8060-4e17-bca9-700d05a3cb5f
📒 Files selected for processing (1)
frontend/src/components/RepoList.tsx
Swapped the hand-rolled SortTab pill buttons for shadcn/ui Tabs (Radix TabsPrimitive). Gets us keyboard navigation, ARIA roles, and consistent styling from the design system for free. Removed 18 lines of custom component, added 6 lines of shadcn usage.
|
@CodeRabbit review |
✅ Actions performedReview triggered.
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
frontend/src/components/RepoList.tsx (3)
291-315:⚠️ Potential issue | 🟠 MajorConfirm deletions against a unique, labeled identifier.
This dialog still keys off
repo.name, so typingeffectdoes not distinguish which repo is being deleted when several cards share that name. The field also still has no programmatic label. Use the parsed slug when available and bind that text to the input witharia-label/aria-describedby.Possible fix
const [confirmText, setConfirmText] = useState('') const repoName = repo?.name || '' - const isMatch = confirmText === repoName + const repoIdentifier = repo ? parseRepoSlug(repo.git_url) || repoName : '' + const isMatch = confirmText === repoIdentifier @@ <DialogHeader> <DialogTitle>Delete repository</DialogTitle> <DialogDescription> - This will permanently remove <strong>{repoName}</strong> and all its + This will permanently remove <strong>{repoIdentifier}</strong> and all its indexed data. This action cannot be undone. </DialogDescription> </DialogHeader> <div className="py-2"> - <p className="text-sm text-muted-foreground mb-2"> - Type <strong className="text-foreground">{repoName}</strong> to confirm + <p id="delete-repo-confirm-help" className="text-sm text-muted-foreground mb-2"> + Type <strong className="text-foreground">{repoIdentifier}</strong> to confirm </p> <Input + aria-label={`Type ${repoIdentifier} to confirm deletion`} + aria-describedby="delete-repo-confirm-help" value={confirmText} onChange={(e) => setConfirmText(e.target.value)} - placeholder={repoName} + placeholder={repoIdentifier} autoFocus /> </div> @@ <Button variant="destructive" disabled={!isMatch} onClick={() => { setConfirmText(''); onConfirm() }} > - Delete {repoName} + Delete {repoIdentifier} </Button>Also applies to: 320-325
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/RepoList.tsx` around lines 291 - 315, The confirmation is currently keyed to repo?.name (repoName) and the Input lacks an accessible label, so change the confirmation to use the repository's unique parsed slug when available (e.g., prefer repo.slug || repo.name) and bind that value to the Input's value/placeholder and to the comparison (isMatch) used to enable deletion; also add an accessible label/description to the Input (aria-label or aria-describedby) so screen readers can identify the required text. Update references to repoName, isMatch, confirmText, setConfirmText, and the Input props so the dialog validates against the unique slug and the input is programmatically labeled; apply the same change to the similar Input usage later in the file.
173-176:⚠️ Potential issue | 🟡 MinorRender zero function counts too.
> 0hides legitimate zero values, so repos with no indexed functions lose one of the new stats.Possible fix
- {repo.function_count != null && repo.function_count > 0 && ( + {repo.function_count != null && ( <span className="flex items-center gap-1"> <FunctionSquare className="w-3 h-3" /> {repo.function_count.toLocaleString()} </span> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/RepoList.tsx` around lines 173 - 176, The current render in RepoList.tsx uses the condition "repo.function_count != null && repo.function_count > 0" which suppresses legitimate zero counts; change the condition to only check for presence (e.g., "repo.function_count != null") so that zero values are rendered as "0" alongside the FunctionSquare and the toLocaleString output, ensuring RepoList displays repos with no indexed functions; update the JSX that references repo.function_count and keep the FunctionSquare and toLocaleString usage intact.
100-107:⚠️ Potential issue | 🟠 MajorSplit repo selection from the overflow trigger.
The card is still a
<motion.button>with an inner icon<button>, so selection and delete compete for focus/click handling. The trigger also has no accessible name.Possible fix
- const cardRef = useRef<HTMLButtonElement>(null) + const cardRef = useRef<HTMLDivElement>(null) - <motion.button + <motion.div ref={cardRef} + role="button" + tabIndex={0} initial={{ opacity: 0, y: 12 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: index * 0.04, duration: 0.25 }} whileHover={{ y: -2 }} onClick={onSelect} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault() + onSelect() + } + }} onMouseMove={(e) => { if (!cardRef.current) return const rect = cardRef.current.getBoundingClientRect() setMousePos({ x: e.clientX - rect.left, y: e.clientY - rect.top }) }} @@ - <button + <button + type="button" + aria-label={`Open actions for ${repo.name}`} onClick={(e) => e.stopPropagation()} - className="w-6 h-6 flex items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors opacity-0 group-hover:opacity-100" + className="w-6 h-6 flex items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors opacity-0 group-hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50" > <MoreVertical className="w-3.5 h-3.5" /> </button> @@ - </motion.button> + </motion.div>Also applies to: 135-143
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/RepoList.tsx` around lines 100 - 107, The repo card currently uses <motion.button> (cardRef) with an inner icon <button>, causing selection and overflow/delete to compete; change the outer <motion.button> to a non-interactive element (e.g., <motion.div> or a div with role="button" and tabIndex=0) and keep the inner icon as a real <button> with a clear accessible name (aria-label="Open overflow" or "Delete repository" as appropriate). Update the outer selection handling (onSelect) to respond to onClick and keyboard via onKeyDown (Enter/Space) on the new interactive container, and ensure the inner overflow button calls e.stopPropagation() to prevent triggering onSelect; apply the same fixes for the duplicate card instance around lines 135-143 (the other motion.button with onSelect).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/RepoList.tsx`:
- Around line 197-202: The comparator in RepoList.tsx currently uses created_at
as a direct fallback which lets never-indexed repos jump ahead; change the
sorted.sort comparator used when sortMode === 'recent' to first compare
last_indexed_at values (treat missing last_indexed_at as less recent), and only
if last_indexed_at are equal (or both missing) use created_at as a tie-breaker;
update the function using sortMode, sorted.sort, last_indexed_at and created_at
so that last_indexed_at always determines order and created_at is applied
strictly as a secondary comparator.
---
Duplicate comments:
In `@frontend/src/components/RepoList.tsx`:
- Around line 291-315: The confirmation is currently keyed to repo?.name
(repoName) and the Input lacks an accessible label, so change the confirmation
to use the repository's unique parsed slug when available (e.g., prefer
repo.slug || repo.name) and bind that value to the Input's value/placeholder and
to the comparison (isMatch) used to enable deletion; also add an accessible
label/description to the Input (aria-label or aria-describedby) so screen
readers can identify the required text. Update references to repoName, isMatch,
confirmText, setConfirmText, and the Input props so the dialog validates against
the unique slug and the input is programmatically labeled; apply the same change
to the similar Input usage later in the file.
- Around line 173-176: The current render in RepoList.tsx uses the condition
"repo.function_count != null && repo.function_count > 0" which suppresses
legitimate zero counts; change the condition to only check for presence (e.g.,
"repo.function_count != null") so that zero values are rendered as "0" alongside
the FunctionSquare and the toLocaleString output, ensuring RepoList displays
repos with no indexed functions; update the JSX that references
repo.function_count and keep the FunctionSquare and toLocaleString usage intact.
- Around line 100-107: The repo card currently uses <motion.button> (cardRef)
with an inner icon <button>, causing selection and overflow/delete to compete;
change the outer <motion.button> to a non-interactive element (e.g.,
<motion.div> or a div with role="button" and tabIndex=0) and keep the inner icon
as a real <button> with a clear accessible name (aria-label="Open overflow" or
"Delete repository" as appropriate). Update the outer selection handling
(onSelect) to respond to onClick and keyboard via onKeyDown (Enter/Space) on the
new interactive container, and ensure the inner overflow button calls
e.stopPropagation() to prevent triggering onSelect; apply the same fixes for the
duplicate card instance around lines 135-143 (the other motion.button with
onSelect).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 59a4f674-c6d9-4781-8a9f-1b9cf79efffd
📒 Files selected for processing (1)
frontend/src/components/RepoList.tsx
…input Sort: never-indexed repos no longer jump ahead of recently indexed ones. last_indexed_at is primary sort key, created_at is tiebreaker only. Accessibility: delete confirmation input now has aria-label for screen readers. Skipped: function_count > 0 (intentional), motion.button nesting (large refactor), slug-based confirm (users think in repo names).
Problem
Dashboard shows 5 repos, 3 named 'effect' with no way to tell them apart. No timestamps, no URLs, no subset info. Looks broken.
What changed
Repo cards now show:
Effect-TS/effect)Sort bar above the grid:
Type updates:
Before
Three identical 'effect' cards. No way to distinguish.
After
Files
Closes OPE-157
Summary by CodeRabbit
New Features
Improvements