260324:2133 Refactor correspondence & rfa
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
'use client';
|
||||
|
||||
import { CorrespondenceForm } from '@/components/correspondences/form';
|
||||
import { useCorrespondence } from '@/hooks/use-correspondence';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { useParams } from 'next/navigation';
|
||||
|
||||
export default function EditCorrespondencePage() {
|
||||
const params = useParams();
|
||||
const uuid = (params?.uuid as string) ?? '';
|
||||
|
||||
const { data: correspondence, isLoading, isError } = useCorrespondence(uuid);
|
||||
|
||||
if (!uuid) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-screen">
|
||||
<h1 className="text-xl font-bold text-red-500">Invalid Correspondence UUID</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex bg-muted/20 min-h-screen justify-center items-center">
|
||||
<Loader2 className="h-8 w-8 animate-spin" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError || !correspondence) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-screen">
|
||||
<h1 className="text-xl font-bold text-red-500">Failed to load correspondence</h1>
|
||||
<p>Please try again later or verify the UUID.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto py-6">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold">Edit Correspondence</h1>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
Editing: <span className="font-mono font-semibold">{correspondence.correspondenceNumber}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-card border rounded-lg p-6 shadow-sm">
|
||||
<CorrespondenceForm initialData={correspondence} uuid={uuid} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -7,34 +7,40 @@ import { SearchResults } from '@/components/search/results';
|
||||
import { SearchFilters as FilterType } from '@/types/search';
|
||||
import { useSearch } from '@/hooks/use-search';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
|
||||
const PAGE_SIZE = 20;
|
||||
|
||||
function SearchContent() {
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
// URL Params state
|
||||
const query = searchParams.get('q') || '';
|
||||
const typeParam = searchParams.get('type');
|
||||
const statusParam = searchParams.get('status');
|
||||
|
||||
// Local Filter State (synced with URL initially, but can be independent before apply)
|
||||
// For simplicity, we'll keep filters in sync with valid search params or local state that pushes to URL
|
||||
const [filters, setFilters] = useState<FilterType>({
|
||||
types: typeParam ? [typeParam] : [],
|
||||
statuses: statusParam ? [statusParam] : [],
|
||||
});
|
||||
const [page, setPage] = useState(1);
|
||||
|
||||
// Construct search DTO — only send fields recognized by backend SearchQueryDto
|
||||
// Backend uses forbidNonWhitelisted: true, so unknown fields (types[], statuses[]) cause 400
|
||||
const searchDto = {
|
||||
q: query,
|
||||
type: filters.types?.length ? filters.types[0] : undefined,
|
||||
status: filters.statuses?.length ? filters.statuses[0] : undefined,
|
||||
page,
|
||||
limit: PAGE_SIZE,
|
||||
};
|
||||
|
||||
const { data: results, isLoading, isError } = useSearch(searchDto);
|
||||
|
||||
const total = results?.meta?.total ?? 0;
|
||||
const totalPages = Math.ceil(total / PAGE_SIZE);
|
||||
|
||||
const handleFilterChange = (newFilters: FilterType) => {
|
||||
setFilters(newFilters);
|
||||
// Optional: Update URL to reflect filters?
|
||||
setPage(1);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -44,20 +50,50 @@ function SearchContent() {
|
||||
<p className="text-muted-foreground mt-1">
|
||||
{isLoading
|
||||
? 'Searching...'
|
||||
: `Found ${results?.meta?.total ?? results?.data?.length ?? 0} results for "${query}"`}
|
||||
: `Found ${total} results${query ? ` for "${query}"` : ''}`}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
||||
<div className="lg:col-span-1">
|
||||
<SearchFilters onFilterChange={handleFilterChange} />
|
||||
<SearchFilters filters={filters} onFilterChange={handleFilterChange} />
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-3">
|
||||
<div className="lg:col-span-3 space-y-4">
|
||||
{isError ? (
|
||||
<div className="text-red-500 py-8 text-center">Failed to load search results.</div>
|
||||
) : (
|
||||
<SearchResults results={results?.data || []} query={query} loading={isLoading} />
|
||||
<>
|
||||
<SearchResults results={results?.data || []} query={query} loading={isLoading} />
|
||||
|
||||
{totalPages > 1 && (
|
||||
<div className="flex items-center justify-between pt-2">
|
||||
<span className="text-sm text-muted-foreground">
|
||||
Page {page} of {totalPages}
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={page === 1}
|
||||
onClick={() => setPage((p) => p - 1)}
|
||||
>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
Prev
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={page >= totalPages}
|
||||
onClick={() => setPage((p) => p + 1)}
|
||||
>
|
||||
Next
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user