A modern, scalable Project Management Dashboard built with React 19, React Router v7 (App Router), Redux Toolkit, and TailwindCSS v4.
A modern, scalable Project Management Dashboard built with React 19, React Router v7 (App Router), TailwindCSS v4, and Clerk Authentication.
The project follows real-world production architecture, focusing on:
- UI-first development
- Feature-based folder organization
- Skeleton loaders for async UX
- Clean separation of concerns
- Accessibility and performance best practices
This project is built in React Router v7 framework mode (file-based routing). Routes must be defined inside app/routes.
- Fully configured React Router v7 App Directory (file-based routing)
- Modular & scalable component architecture
- Global Layout system (Navbar + Sidebar)
- Intelligent layout hiding for routes like
/loginand404 - Organized folder structure following industry-level best practices
- Integrated ClerkProvider at the app root
- Authentication handled fully by Clerk
- Supports:
- OAuth providers (Google-ready)
- Session-based auth
- Secure route protection
The dashboard is composed of multiple independent widgets:
- Stats Grid (animated with Framer Motion)
- Project overview
- Recent activities
- Task summary
Each widget: - Is built as an isolated component
- Uses real backend data via Redux Toolkit and API integration
- Is ready for backend integration
- Avoids premature global state coupling
Added highly polished skeleton loaders for:
StatsGridSkeletonProjectOverviewSkeletonRecentActivitySkeletonTaskSummarySkeleton
- Smooth entrance animations for dashboard widgets using Framer Motion
- Staggered card animations in the StatsGrid
- Skeleton → content transitions for improved perceived performance
- Subtle hover micro-interactions for stat cards
Skeletons:
- Match final layout structure
- Use TailwindCSS animate-pulse
- Prevent layout shift
- Are easily replaceable with real loading states later
Performance Optimizations
- Dashboard sections are:
- Lazy-loaded with React.lazy
- Wrapped with Suspense
- Memoized using memo()
- Optimized re-renders and layout stability
- Search bar
- Light/Dark theme toggle
- User avatar
- Fully keyboard accessible
- Desktop fixed version
- Mobile slide-in version
- Touch-friendly overlay
- Click outside to close
- Escape key support
- ARIA role-based navigation
Standardized form behavior across the entire application by customizing base shadcn/ui components.
Improvements include:
- Consistent labeled form layout for all dialogs
- Structured 2-column grid alignment for related fields
- Full-width selects for primary inputs (Lead, Members)
- Centralized Select dropdown styling with:
- Blue hover state
- Blue keyboard focus state
- Accessible interactions
- Customized Input & Textarea focus styles:
- Thin black border on focus
- Removed default ring glow
- Cleaner modern SaaS look
- Updated styles directly inside
/components/ui/*to avoid repetition
Implemented a reusable CreateProjectDialog component for creating new projects.
Features:
- Lazy-loaded using React.lazy
- Conditionally mounted (renders only when opened)
- Memoized with React.memo to prevent unnecessary re-renders
- Structured labeled form layout
- 2-column responsive grid alignment
- Status & Priority selectors
- Start/End date inputs
- Project Lead and Team Members dropdowns
- Global Select hover/focus styling
- Consistent Input & Textarea focus behavior
- Fully accessible (focus trap, keyboard support, ARIA compliant)
Performance:
- Dialog mounts only when opened
- Reduces initial bundle size
- Improves first paint
- Ensures smooth open/close animations
- Integrated with Redux Toolkit (
createProjectthunk) - Uses React Hook Form for form handling
- Transforms frontend payload to backend schema
- Displays loading and success states using toast notifications
- Fetches workspace members dynamically from backend
The form transforms data before sending:
{ name, description, status, priority, start_date, end_date, team_lead, team_members }
Ensures compatibility with backend API expectations.
Implemented a fully responsive Projects module following feature-based architecture.
- Added /projects route using React Router v7 file-based routing
- Integrated with global Layout (Navbar + Sidebar)
- Protected under Clerk authentication
- Responsive layout
- Header with contextual page description
- Gradient “New Project” action button
- Search input field (UI-ready)
- Status filter dropdown (shadcn Select)
- Priority filter dropdown (shadcn Select)
- Grid-based project layou
- Accessible semantic structure (, ,
) - Reusable and memoized
- Fully typed with strict TypeScript interfaces
- Status badge with dynamic color mapping
- Priority display
- Progress bar with ARIA attributes
- Accessible navigation via Link
- Responsive design and hover interactions
- Projects are fetched from backend using Redux thunk
- Data is stored in
projectSlice - UI updates automatically when new project is created
- Eliminates need for manual refresh
Component → Dispatch Thunk → API → Redux Store → UI
Ensures:
- Single source of truth
- Predictable state updates
- Scalable architecture
- Added NotFound (404) Page
- Automatically served for unknown routes
- Sidebar + Navbar are hidden on 404 page
Implemented a scalable nested routing structure for project-level navigation.
The application now supports dynamic routes using: /projects/:projectId
Each project contains nested sections:
/projects/:projectId/tasks /projects/:projectId/analytics /projects/:projectId/calendar /projects/:projectId/settings
- Uses useParams() to extract projectId
- Renders shared project header
- Provides tab-based navigation
- Uses for nested rendering
- Avoids re-rendering full layout on tab switch
- Nested Routing Configuration
- Routes are structured using React Router v7 framework mode:
- Index route defaults to tasks
- Child routes render inside ProjectLayout
- Clean separation between layout and content
- Built a dynamic ProjectSidebar component:
- Displays workspace projects
- Uses shadcn/ui Collapsible for expandable navigation
- Links to dynamic project routes
- Auto-expands active project based on URL
- Fully accessible and keyboard-friendly
Implemented a scalable and extensible task management system inside each project.
Tasks are rendered via nested routing under:
- /projects/:projectId The default index route renders the Tasks module.
- Built using @tanstack/react-table v8
- Integrated with shadcn/ui Table components
- Fully typed using strict TypeScript generics
- Column sorting
- Custom cell rendering
- Status badges
- Priority formatting
- Empty state handling
- Responsive container with horizontal scroll
- Accessible semantic table structure
Implemented a responsive and accessible Project Settings module under:
- Replaced tab-based layout with responsive 2-column grid
- Left: General project configuration
- Right: Project members management
- Consistent bordered sections aligned with Tasks module UI
- Mobile-first responsive design
- Fully typed form using React Hook Form + Zod
- Editable fields:
- Project Name
- Description
- Start & End Dates
- Status
- Priority
- Accessible labels and ARIA attributes
- Clean focus states aligned with global UI system
- Responsive member list
- Role display (Team Lead / Member)
- Change Role action
- Remove member action
- Improved mobile layout:
- Stacked action buttons
- Equal button width
- Proper alignment
- Semantic structure using
, - , and
UI Improvements:
- Refined button alignment for better UX
- Improved spacing consistency
- Reduced visual imbalance in action buttons
- Fully responsive and accessible design
Implemented a fully interactive and responsive Calendar module under:
/projects/:projectId/calendar
The Calendar module provides a visual task scheduling interface within each project.
It is built using FullCalendar and follows performance, scalability, and accessibility best practices.- Monthly grid view (DayGridMonth)
- Custom day cell rendering
- Full-cell highlighting for task dates
- Dynamic task badge injection
- Date click → opens Task Modal
- Drag-and-drop support (UI-ready for backend integration)
- Responsive layout with sidebar integration
- Derived Upcoming & Overdue task panels
- Optimized event lookup using Map for constant-time access (O(1))
The Calendar page uses a responsive grid system:
- Desktop → 2/3 Calendar | 1/3 Sidebar (Upcoming & Overdue)
- Tablet → Stacked layout
- Mobile → Fully stacked vertical layout
Sidebar includes:
- Upcoming Tasks
- Overdue Tasks
Both sections:
- Use semantic
<section>and<ul>structures - Implement accessible
<time>elements - Follow consistent UI system styling
- Support keyboard navigation and focus states
- Events transformed using a custom
useCalendarEventshook - Precomputed
Mapfor constant-time date lookups - Memoized derived state (upcoming & overdue tasks)
- Avoided repeated
.find()operations inside calendar cells - Local modal state (no unnecessary global state usage)
- ARIA-labelled sections
- Semantic HTML structure
- Focus-visible styling
- Screen-reader friendly time elements
- Keyboard-accessible interactions
Implemented a fully responsive and data-driven Analytics module under:
/projects/:projectId/analytics
The Analytics module provides real-time project insights using visual KPIs and interactive charts.
It transforms raw task data into meaningful performance metrics using memoized calculations and responsive visualizations.Built with:
- Recharts (Bar & Pie charts)
- Lucide React (icons)
- TailwindCSS design system
- Semantic & accessible chart structures
- React.memo for render optimization
Displayed at the top of the page:
- Completion Rate (%)
- Active Tasks (IN_PROGRESS)
- Overdue Tasks
- Team Size
Each KPI:
- Uses a reusable
MetricCardcomponent - Is memoized with React.memo
- Supports semantic structure using
<article> - Uses accessible ARIA labels
- Includes smooth hover micro-interactions
- Displays TODO, IN_PROGRESS, DONE counts
- Dynamic semantic color mapping
- No deprecated
<Cell>usage - Uses data-driven
fillproperty - Custom tooltip component
- Responsive via
ResponsiveContainer - Accessible via
<section>+<figure>+<figcaption>
- Displays distribution of task types (Feature, Bug, Improvement, etc.)
- Custom tooltip styling
- Animated transitions
- Legend support
- Fully responsive container
- Screen-reader friendly structure
- Calculates LOW / MEDIUM / HIGH distribution
- Displays percentage relative to total tasks
- Derived from memoized analytics logic
- Fully typed using strict TypeScript interfaces
All analytics calculations are wrapped inside
useMemo()to prevent unnecessary recalculations.Derived values include:
- Total tasks
- Completed tasks
- In-progress tasks
- Overdue tasks
- Status distribution
- Type distribution
- Priority percentage breakdown
Benefits:
- Prevents redundant loops
- Ensures stable chart data
- Optimizes re-renders
- Keeps analytics computation isolated from UI
- Semantic
<main>,<section>,<header>,<article>structure - ARIA-labelled chart sections
- Screen-reader descriptions using
<figcaption class="sr-only"> - Accessible tooltip contrast
- Keyboard-friendly UI structure
- Clear color contrast in charts
- Responsive KPI grid (1 → 2 → 4 columns)
- Two-column chart layout on large screens
- Stacked layout on mobile
- Consistent bordered container system (aligned with Tasks & Settings modules)
- Dark mode fully supported
- Reusable
MetricCard,StatusBarChart, andTypePieChartcomponents - Strict TypeScript typing for analytics data models
- Clean separation between data processing and visualization
- Future-ready for backend integration
Implemented a fully scalable and production-ready Team Management module.
Route:
/team
The Team module allows workspace-level team management with a consistent UI system aligned with the Projects module.
Built with:
- TanStack React Table v8
- shadcn/ui components
- Controlled dialog pattern
- Strict TypeScript typing
- Reusable StatsGrid integration
The Team page includes:
- Header with primary CTA (Invite Member)
- Stats overview grid
- Search input for member filtering
- Sortable team data table
All sections follow semantic HTML structure using:
<main><header><section>
Reused the existing
StatsGridcomponent from Dashboard.Displays:
- Total Members
- Active Projects
- Total Tasks
Ensures:
- Component reuse
- Design consistency
- Avoided duplicate UI logic
Implemented using:
@tanstack/react-table v8createColumnHelpergetCoreRowModelgetSortedRowModel
Features:
- Sortable Name column
- Custom cell rendering
- Role-based badge color mapping
- Dynamic avatar color generation
- Empty state handling
- Fully typed column definitions
- Semantic and accessible table structure
- Deterministic avatar background colors
- Generated using character-based hashing
- Improves visual differentiation between users
- Role-based color mapping using a scalable object pattern
- Avoided nested ternary conditions
- Easily extensible for additional roles
Refactored to follow the same architecture pattern as CreateProjectDialog.
Improvements:
- Controlled dialog pattern (
open+onOpenChange) - Parent-managed state
- Memoized component using
React.memo - useCallback for submit handler
- Accessible form structure
- Role selector using shadcn Select
- Loading state handling
- DialogFooter consistency
Performance:
- Prevents unnecessary re-renders
- Keeps modal logic isolated
- Enables permission-based rendering in future
Standardized CTA button styling across:
- New Project
- Invite Member
Ensures:
- Consistent primary button design
- Shared visual identity
- Reduced styling duplication
- Scalable design-system alignment
Implemented client-side filtering:
- Filters members by name
- Case-insensitive matching
- Ready for debounce integration
- Feature-based modular design
- Clean separation of UI and logic
- Reusable data table pattern
- Type-safe role management
- Future-ready for backend integration
The application integrates with a backend service using a structured API layer and centralized state management.
A reusable Axios client is configured to handle all API requests.
- Centralized base URL configuration
- Automatic JSON headers
- Request interceptor for authentication token injection
- Scalable structure for all API modules
apiClient.interceptors.request.use(async (config) => { const token = await getToken();
if (token) { config.headers.Authorization =
Bearer ${token}; }return config; });
Authentication is managed using Clerk.
- Clerk provides
getToken()viauseAuth() - Token is stored globally using a setter function
- Axios interceptor attaches token to every request
AuthProviderauthToken.ts
This ensures all API requests are automatically authenticated.
A dedicated API module handles all workspace-related backend communication.
export const workspaceApi = { getAll: async () => { const res = await apiClient.get("/api/workspace"); return res.data.data; } };
Redux Toolkit is used for global state management.
- Stores all user workspaces
- Maintains current workspace selection
- Persists workspace ID in localStorage
- Handles API loading and error states
fetchWorkspaces→ fetches workspace data from backendsetWorkspaceWithPersistence→ syncs Redux + localStoragedeleteWorkspaceWithCleanup→ removes workspace safely
The selected workspace is persisted using localStorage:
- Automatically restored on app reload
- Falls back to first available workspace if invalid
- Ensures consistent user experience across sessions
A dedicated task slice is implemented for managing task data.
- Add, update, delete tasks
- Store project-specific tasks
- Designed for future backend integration
All API responses and state are strictly typed using TypeScript.
export interface Workspace { id: string; name: string; slug: string; image_url?: string; }
This ensures:
- Safer API integration
- Better developer experience
- Fewer runtime errors
The application is fully integrated with the backend APIs.
- Fetch workspaces using
fetchWorkspacesthunk - Persist current workspace in Redux + localStorage
- Automatically restore workspace on reload
- Fetch workspace members using
fetchWorkspaceMembers - Used in:
- Project Lead selection
- Team Members selection
- Fetch projects using
fetchProjects - Create project using
createProjectthunk - Optimistically updates Redux store after creation
Frontend sends:
- team_lead (email)
- team_members (emails)
Backend:
- Converts emails → user IDs
- Validates workspace membership
- Stores relational data using Prisma
This ensures:
- Clean frontend logic
- Strong backend validation
- Secure multi-tenant architecture
Clerk Auth → Token → Axios Interceptor ↓ API Layer (workspaceApi) ↓ Redux Thunks ↓ Redux Store (workspaceSlice) ↓ UI Components
This layered architecture ensures:
- Clean separation of concerns
- Scalability
- Maintainability
Implemented a multi-tenant workspace system using Clerk Organizations integrated with Redux state.
- Displays all available workspaces (organizations)
- Highlights the currently active workspace
- Allows seamless switching between workspaces
- Updates:
- Clerk active organization (auth context)
- Redux workspace state (UI context)
- Application route (
/workspace/:workspaceId)
Workspace selection follows a synchronized flow:
- Clerk
setActive()updates the organization context - Redux updates the current workspace state
- Application navigates to workspace-specific route
This ensures:
- Consistent auth context
- Correct API scoping
- UI state synchronization
- Uses Clerk's
openCreateOrganization()API - Opens a fully managed modal (centered, responsive)
- Handles:
- Organization creation
- Validation
- Closing interactions (ESC, outside click)
No custom modal implementation is required.
- Redux stores
currentWorkspaceId useEffectensures Clerk stays in sync with Redux- Prevents mismatch between UI and auth context
- Workspace ID is persisted in localStorage
- Automatically restored on reload
- Falls back to a valid workspace if needed
Clerk handles:
- Authentication
- Organization context
Redux handles:
- UI state
- Workspace selection
This separation ensures scalability and maintainability in a multi-tenant SaaS architecture.
The application uses Clerk's organization invitation system combined with webhooks and background processing to ensure database consistency.
- Admin invites user via Clerk Organization
- User accepts invitation
- Clerk triggers
organizationMembership.createdwebhook - Backend receives webhook and forwards event to Inngest
- Inngest processes the event and:
- Creates or updates
WorkspaceMemberin database
- Creates or updates
- User logs in and gets immediate access to workspace and projects
- Avoids frontend-based sync issues
- Ensures reliable and retry-safe processing
- Works even if user logs in later
- Prevents race conditions between auth and DB
Clerk (Auth + Org) → Webhook (Event trigger) → Inngest (Background processing) → Database (WorkspaceMember sync)
The application relies on the
organizationMembership.createdwebhook event.Ensure this event is enabled in the Clerk dashboard under Webhooks.
Without this, invited users will not be synced into the database, resulting in:
- Empty workspace state
- 403 errors on protected APIs
- Projects update instantly after creation (no reload)
- Workspace switching updates entire app state
- Skeleton loaders improve perceived performance
- Optimistic UI updates for better UX
This mimics real production SaaS behavior.
Area Technology Language TypeScript Framework React 19 Router React Router v7 (App Router) State Management Redux Toolkit Styles TailwindCSS v4 UI Components shadcn/ui Icons Lucide React Bundler Vite Auth Clerk Database (Upcoming) Neon PostgreSQL