Files
pragma-stack/frontend/src/app/[locale]/(authenticated)/projects/page.tsx
Felipe Cardoso 924fbbda5d fix(frontend): remove locale-dependent routing and migrate to centralized locale-aware router
- Replaced `next/navigation` with `@/lib/i18n/routing` across components, pages, and tests.
- Removed redundant `locale` props from `ProjectWizard` and related pages.
- Updated navigation to exclude explicit `locale` in paths.
- Refactored tests to use mocks from `next-intl/navigation`.
2026-01-03 01:34:53 +01:00

146 lines
4.5 KiB
TypeScript

/**
* Projects List Page
*
* Displays all projects with filtering, sorting, and search.
* Supports grid and list view modes.
*
* @see Issue #54
*/
'use client';
import { useState, useCallback, useMemo } from 'react';
import { useRouter } from '@/lib/i18n/routing';
import { toast } from 'sonner';
import { Plus } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Link } from '@/lib/i18n/routing';
import { ProjectFilters, ProjectsGrid } from '@/components/projects';
import type { ViewMode, SortBy, SortOrder, Complexity } from '@/components/projects';
import type { ProjectStatus } from '@/components/projects/types';
import { useProjects, type ProjectListItem } from '@/lib/api/hooks/useProjects';
import { useDebounce } from '@/lib/hooks/useDebounce';
export default function ProjectsPage() {
const router = useRouter();
// Filter state
const [searchQuery, setSearchQuery] = useState('');
const [statusFilter, setStatusFilter] = useState<ProjectStatus | 'all'>('all');
const [complexityFilter, setComplexityFilter] = useState<Complexity>('all');
const [sortBy, setSortBy] = useState<SortBy>('recent');
const [sortOrder, setSortOrder] = useState<SortOrder>('desc');
const [viewMode, setViewMode] = useState<ViewMode>('grid');
// Debounce search for API calls
const debouncedSearch = useDebounce(searchQuery, 300);
// Fetch projects
const { data, isLoading, error } = useProjects({
search: debouncedSearch || undefined,
status: statusFilter,
complexity: complexityFilter !== 'all' ? complexityFilter : undefined,
sortBy,
sortOrder,
page: 1,
limit: 50,
});
// Check if any filters are active (for empty state message)
const hasFilters = useMemo(() => {
return searchQuery !== '' || statusFilter !== 'all' || complexityFilter !== 'all';
}, [searchQuery, statusFilter, complexityFilter]);
// Handle project card click
const handleProjectClick = useCallback(
(project: ProjectListItem) => {
router.push(`/projects/${project.id}`);
},
[router]
);
// Handle project action
const handleProjectAction = useCallback(
(project: ProjectListItem, action: 'archive' | 'pause' | 'resume' | 'delete') => {
// TODO: Implement actual API calls
switch (action) {
case 'archive':
toast.success(`Archived: ${project.name}`);
break;
case 'pause':
toast.info(`Paused: ${project.name}`);
break;
case 'resume':
toast.success(`Resumed: ${project.name}`);
break;
case 'delete':
toast.error(`Deleted: ${project.name}`);
break;
}
},
[]
);
// Show error toast if fetch fails
if (error) {
toast.error('Failed to load projects', {
description: 'Please try again later',
});
}
return (
<div className="container mx-auto px-4 py-6">
{/* Header */}
<div className="mb-6 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div>
<h1 className="text-3xl font-bold">Projects</h1>
<p className="text-muted-foreground">Manage and monitor your projects</p>
</div>
<Button asChild>
<Link href="/projects/new">
<Plus className="mr-2 h-4 w-4" />
Create Project
</Link>
</Button>
</div>
{/* Filters */}
<ProjectFilters
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
statusFilter={statusFilter}
onStatusFilterChange={setStatusFilter}
complexityFilter={complexityFilter}
onComplexityFilterChange={setComplexityFilter}
sortBy={sortBy}
onSortByChange={setSortBy}
sortOrder={sortOrder}
onSortOrderChange={setSortOrder}
viewMode={viewMode}
onViewModeChange={setViewMode}
className="mb-6"
/>
{/* Projects Grid */}
<ProjectsGrid
projects={data?.data ?? []}
isLoading={isLoading}
viewMode={viewMode}
onProjectClick={handleProjectClick}
onProjectAction={handleProjectAction}
hasFilters={hasFilters}
/>
{/* Pagination - TODO: Add when more than 50 projects */}
{data && data.pagination.totalPages > 1 && (
<div className="mt-6 flex items-center justify-between text-sm text-muted-foreground">
<span>
Showing {data.data.length} of {data.pagination.total} projects
</span>
{/* TODO: Add pagination controls */}
</div>
)}
</div>
);
}