Skip to content

Conversation

@pranay911
Copy link

@pranay911 pranay911 commented Dec 9, 2025

This PR introduces complete column sorting support along with sorting icons for enhanced usability and accessibility. Previously, the project table supported partial sorting without any visual indication, making it unclear to users which column was currently sorted.

This update adds:

  • Sorting icons (▲ ▼ ↕) for all sortable table headers
  • Full sorting logic across all column types (string, numeric, and mixed fields)
  • Keyboard accessibility (Enter / Space to trigger sorting)
  • aria-sort for screen readers
  • Highlighting of the active sorted column
  • Improved hover and focus states

Related Issues
Closes #245

Screenshots
523495440-c71b615b-bbd0-4d08-8641-8337cae9c024

Summary by CodeRabbit

  • New Features
    • Added interactive, sortable project columns with visual sort indicators
    • Introduced comprehensive filtering: project name search, language, stage, competition, activity, issue count, and popularity
    • Added Reset button to quickly clear all filters
    • Improved keyboard accessibility for table interactions
    • Enhanced UI with sticky table header and improved empty state messaging

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Dec 9, 2025

@pranay911 is attempting to deploy a commit to the AJEET PRATAP SINGH's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 9, 2025

Walkthrough

Enhanced ProjectsContainer with client-side sorting and filtering capabilities. Added sortable column headers with keyboard accessibility, visual sorting indicators, and multi-criteria filters (name search, language, stage, competition, activity, issue range, popularity). Implemented a two-stage data pipeline: filter then sort with deterministic tiebreakers. Added Reset button and improved empty state messaging.

Changes

Cohort / File(s) Summary
Dashboard sorting and filtering enhancement
apps/web/src/components/dashboard/ProjectsContainer.tsx
Adds client-side sorting with column state, keyboard accessibility (Enter/Space), and visual indicators (sort icons). Introduces multi-criteria filtering UI (name, language, stage, competition, activity, issues range, popularity) with filter derivation via useMemo. Implements two-stage data pipeline (filter → sort) with per-column comparators. Replaces static columns with dynamic structure including sortable flags. Adds Reset button and improves empty state rendering.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Sort comparator logic: Verify correctness and consistency across all columns (Project, Issues, Language, Popularity, Stage, Competition, Activity); check tiebreaker behavior
  • Filter derivation and application: Ensure filter options derive correctly from projects; validate edge cases for range filters (min/max issues) and multi-select filters
  • Keyboard accessibility: Confirm handleHeaderKey implementation covers Enter/Space; validate aria-sort attribute updates reflect current state
  • Sort icon rendering: Verify correct icon selection (ArrowsUpDownIcon, ChevronUpIcon, ChevronDownIcon) based on column state
  • Performance: Check useMemo dependencies for filter options and sorted array to avoid unnecessary recalculations

Poem

📊 A rabbit sorts with eager paws,
Filters dance by many laws,
Arrow icons up and down,
Keyboard darts without a crown,
Dashboard sparkles, clean and bright,
Data flows just right! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and accurately describes the main change: adding sorting icons and complete column sorting support to the table component.
Linked Issues check ✅ Passed The PR fully addresses all coding requirements from issue #245: adds sorting icons, implements complete sorting logic for all columns, provides keyboard accessibility and aria attributes, and highlights active sorted columns.
Out of Scope Changes check ✅ Passed All changes are within scope of issue #245. The filtering UI enhancements, reset button, and dynamic filter options are supporting features directly related to and necessary for the sorting functionality.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cla-assistant
Copy link

cla-assistant bot commented Dec 9, 2025

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

1 similar comment
@cla-assistant
Copy link

cla-assistant bot commented Dec 9, 2025

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/web/src/components/dashboard/ProjectsContainer.tsx (2)

1-21: Import organization doesn't follow guidelines.

Per coding guidelines, imports should be organized: React → third-party → local components → utils → types. Consider reordering:

 "use client";

-import React, { useMemo, useState } from "react";
+import React, { useMemo, useState } from "react";
+
+import Image from "next/image";
+import { usePathname } from "next/navigation";
+import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
+import { ChevronUpIcon, ChevronDownIcon, ArrowsUpDownIcon } from "@heroicons/react/24/solid";
+
 import { Badge } from "@/components/ui/badge";
 import { Button } from "@/components/ui/button";
 import {
   Table,
   TableBody,
   TableCell,
   TableHead,
   TableHeader,
   TableRow,
 } from "@/components/ui/table";
+
 import { useProjectTitleStore } from "@/store/useProjectTitleStore";
-import { DashboardProjectsProps } from "@/types";
-import Image from "next/image";
 import { useFilterStore } from "@/store/useFilterStore";
-import { usePathname } from "next/navigation";
-import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
-import { ChevronUpIcon, ChevronDownIcon, ArrowsUpDownIcon } from "@heroicons/react/24/solid";
+
+import { DashboardProjectsProps } from "@/types";

168-231: Avoid any type — use proper TypeScript types.

The helper functions and dynamic property access use any which violates coding guidelines. Consider typing more strictly:

-    const getStr = (v: any) => (v == null ? "" : String(v).toLowerCase());
-    const getNum = (v: any) => {
+    const getStr = (v: unknown): string => (v == null ? "" : String(v).toLowerCase());
+    const getNum = (v: unknown): number => {
       const n = Number(v);
       return Number.isFinite(n) ? n : 0;
     };

For the dynamic property access on lines 218-219, consider using a type-safe approach:

       case "stage":
       case "competition":
       case "activity": {
-          const A = getStr((a as any)[sortColumn]);
-          const B = getStr((b as any)[sortColumn]);
+          const key = sortColumn as keyof Pick<DashboardProjectsProps, "stage" | "competition" | "activity">;
+          const A = getStr(a[key]);
+          const B = getStr(b[key]);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 95cd4cd and a44a2db.

📒 Files selected for processing (1)
  • apps/web/src/components/dashboard/ProjectsContainer.tsx (6 hunks)
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx,js,jsx}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables

Files:

  • apps/web/src/components/dashboard/ProjectsContainer.tsx
apps/web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

apps/web/src/**/*.{ts,tsx}: Always follow the design system defined in apps/web/src/lib/design-tokens.ts and apps/web/tailwind.config.ts
NEVER use hardcoded hex values (e.g., #5519f7) directly in components; ALWAYS reference colors from the design token system using Tailwind classes
Use semantic color names that describe purpose, not appearance
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale (0.25rem increments); for section padding use mobile p-4 (1rem) and desktop p-[60px]
Use appropriate border radius: small elements rounded-lg, medium rounded-xl, large rounded-2xl, buttons rounded-[16px]
Use animation durations: fast duration-100 (0.1s), normal duration-300 (0.3s), slow duration-600 (0.6s)

Files:

  • apps/web/src/components/dashboard/ProjectsContainer.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{tsx,ts}: Prefer functional components with TypeScript and use proper TypeScript types, avoid any
Extract reusable logic into custom hooks
Use descriptive prop names and define prop types using TypeScript interfaces or types
Prefer controlled components over uncontrolled
Use zustand for global state (located in src/store/)
Use absolute imports from @/ prefix when available
Include proper aria labels for accessibility
Ensure keyboard navigation works
Maintain proper heading hierarchy
Provide alt text for images
Avoid unnecessary re-renders

Files:

  • apps/web/src/components/dashboard/ProjectsContainer.tsx
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (.cursorrules)

Organize imports: react → third-party → local components → utils → types

Files:

  • apps/web/src/components/dashboard/ProjectsContainer.tsx
**/*[A-Z]*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Use PascalCase for component file names (e.g., UserProfile.tsx)

Files:

  • apps/web/src/components/dashboard/ProjectsContainer.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{ts,tsx}: Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Memoize expensive computations

Files:

  • apps/web/src/components/dashboard/ProjectsContainer.tsx
apps/web/src/**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursorrules)

Optimize images using next/image

apps/web/src/**/*.{tsx,ts}: Use Zustand for global state, located in src/store/
Use PascalCase for types and interfaces with descriptive names
Use dynamic imports for code splitting when appropriate
Optimize images using next/image
Memoize expensive computations
Define a type when defining const functions

Files:

  • apps/web/src/components/dashboard/ProjectsContainer.tsx
**/*.{js,jsx,ts,tsx,py,java,go,rb,php}

📄 CodeRabbit inference engine (.cursor/rules/general_rules.mdc)

**/*.{js,jsx,ts,tsx,py,java,go,rb,php}: Always use lowercase when writing comments
Avoid unnecessary comments; code should be self-documenting when possible
Use comments to explain 'why', not 'what'

Files:

  • apps/web/src/components/dashboard/ProjectsContainer.tsx
apps/web/src/components/**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/components/**/*.{tsx,ts,jsx,js}: Never use hardcoded hex values directly in components; always reference colors from the design token system using Tailwind classes
Use semantic color names from the design token system that describe purpose, not appearance (e.g., bg-brand-purple, bg-surface-primary, text-text-primary)
Use font-sans for standard UI text (Geist Sans) and font-mono for code, technical content, or monospace needs (DM Mono)
Follow Tailwind's spacing scale for section padding: p-4 (1rem) on mobile, p-[60px] on desktop
Use rounded-lg (0.5rem) for small elements, rounded-xl (1rem) for medium elements, rounded-2xl (1.5rem) for large elements, and rounded-[16px] for buttons
Use duration-100 (0.1s) for fast transitions, duration-300 (0.3s) for normal transitions, and duration-600 (0.6s) for slow transitions
Use available custom animations: animate-accordion-down, animate-accordion-up, animate-scrollRight, animate-scrollLeft, animate-customspin, animate-spin-slow, animate-spin-slow-reverse, animate-marquee, animate-marquee-vertical, animate-shine
Prefer functional components with TypeScript
Extract reusable logic into custom hooks
Prefer controlled components over uncontrolled
Include proper aria labels for accessibility
Ensure keyboard navigation works in interactive components
Maintain proper heading hierarchy in page components
Provide alt text for images
Use 'class:' instead of the ternary operator in class tags whenever possible
Implement accessibility features on interactive elements (e.g., tabindex='0', aria-label, onClick, onKeyDown)
Always follow the design system defined in apps/web/src/lib/design-tokens.ts and apps/web/tailwind.config.ts

Files:

  • apps/web/src/components/dashboard/ProjectsContainer.tsx
apps/web/src/components/**/*.{tsx,ts}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/components/**/*.{tsx,ts}: Use proper TypeScript types and avoid using any type
Use descriptive prop names and define prop types using TypeScript interfaces or types
Name components using PascalCase (e.g., UserProfile.tsx)

Files:

  • apps/web/src/components/dashboard/ProjectsContainer.tsx
apps/web/src/**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (apps/web/.cursor/rules/frontend_rules.mdc)

apps/web/src/**/*.{tsx,ts,jsx,js}: Organize imports in order: React → third-party → local components → utils → types
Use absolute imports from @/ prefix when available
Remove unused imports
Use UPPER_SNAKE_CASE for constants
Use camelCase for functions and variables
Always use Tailwind classes for styling HTML elements; avoid using CSS or style tags
Use descriptive variable and function names; name event functions with a 'handle' prefix (e.g., handleClick, handleKeyDown)
Use const with arrow functions instead of function declarations (e.g., 'const toggle = () =>')

Files:

  • apps/web/src/components/dashboard/ProjectsContainer.tsx
🔇 Additional comments (7)
apps/web/src/components/dashboard/ProjectsContainer.tsx (7)

46-54: LGTM!

Clean column definition structure with sortable metadata. This supports the sorting feature requirements well.


62-94: State initialization looks good.

Sensible defaults with issues sorted descending. Filter state is well-organized for the multiple filter criteria.


67-83: Keyboard accessibility properly implemented.

The handleHeaderKey correctly handles both Enter and Space keys with preventDefault() to avoid scroll behavior. Sort toggle logic is sound.


396-433: Excellent accessibility implementation for sortable headers.

Good use of role="button", tabIndex, aria-sort, and keyboard event handling. The implementation correctly:

  • Sets aria-sort to "ascending", "descending", or "none"
  • Makes only sortable columns focusable with tabIndex={0}
  • Handles both click and keyboard interactions

Minor: The type cast on line 420 (e as React.KeyboardEvent, col.key) is unnecessary since onKeyDown already provides the correct event type.


245-254: LGTM!

Clear visual feedback with distinct icons for unsorted (↕), ascending (▲), and descending (▼) states. The reduced opacity on unsorted columns appropriately indicates interactivity without drawing attention.


436-475: Table body correctly uses sorted data.

The table rows iterate over the sorted array which applies filters first, then sorting — maintaining the correct data pipeline as described in the objectives.


374-376: Remove the as any cast on Button variant.

The variant={"ghost" as any} violates TypeScript best practices and should not use type assertions. Verify that "ghost" is a valid variant in the Button component's type definition. If valid, remove the cast; if invalid, use a variant that exists in the component's types.

Comment on lines +140 to +149
// Issues range
const issues = Number(p.totalIssueCount ?? 0);
if (minIssues !== "") {
const mi = Number(minIssues);
if (!Number.isFinite(mi) || issues < mi) return false;
}
if (maxIssues !== "") {
const ma = Number(maxIssues);
if (!Number.isFinite(ma) || issues > ma) return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Invalid numeric input silently filters out all results.

If a user enters non-numeric text (e.g., "abc") in min/max issues fields, Number("abc") returns NaN, causing !Number.isFinite(mi) to be true, which filters out all projects. Consider ignoring invalid values instead:

       if (minIssues !== "") {
         const mi = Number(minIssues);
-        if (!Number.isFinite(mi) || issues < mi) return false;
+        if (Number.isFinite(mi) && issues < mi) return false;
       }
       if (maxIssues !== "") {
         const ma = Number(maxIssues);
-        if (!Number.isFinite(ma) || issues > ma) return false;
+        if (Number.isFinite(ma) && issues > ma) return false;
       }

This way invalid input is effectively ignored rather than hiding all results.

🤖 Prompt for AI Agents
In apps/web/src/components/dashboard/ProjectsContainer.tsx around lines 140-149,
the current logic treats non-numeric min/max inputs as invalid and returns false
(filtering out all projects). Change the checks so that if Number(minIssues) or
Number(maxIssues) produces NaN (i.e., not finite) you simply skip that
particular min/max check instead of returning false; specifically, parse the
value to a number, only apply the comparison when Number.isFinite(parsed) is
true, and otherwise ignore the filter (no early return) so invalid text input
does not hide all results.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] : Add Sorting Icons & Complete Column Sorting Support

1 participant