feat(ancpi): selectable extracts with numbered ZIP download
- Checkbox on each row (ordered selection → numbered files in ZIP) - "Descarcă selecție (N)" button appears when items selected - Tooltip shows position in ZIP: "#1 in ZIP", "#2 in ZIP" - Select-all checkbox in header - Tooltips on Descarcă tot + Descarcă selecție buttons Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -179,6 +179,30 @@ export function EpayTab() {
|
||||
/* -- Filter ------------------------------------------------------ */
|
||||
const [filterTab, setFilterTab] = useState<FilterValue>("all");
|
||||
|
||||
/* -- Selection (ordered — index = numbering in ZIP) -------------- */
|
||||
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
||||
const [downloadingSelection, setDownloadingSelection] = useState(false);
|
||||
|
||||
const toggleSelect = (id: string) => {
|
||||
setSelectedIds((prev) =>
|
||||
prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id],
|
||||
);
|
||||
};
|
||||
|
||||
const handleDownloadSelection = async () => {
|
||||
if (selectedIds.length === 0) return;
|
||||
setDownloadingSelection(true);
|
||||
try {
|
||||
const ids = selectedIds.join(",");
|
||||
const a = document.createElement("a");
|
||||
a.href = `/api/ancpi/download-zip?ids=${encodeURIComponent(ids)}`;
|
||||
a.download = `Extrase_CF_selectie_${selectedIds.length}.zip`;
|
||||
a.click();
|
||||
} finally {
|
||||
setTimeout(() => setDownloadingSelection(false), 2000);
|
||||
}
|
||||
};
|
||||
|
||||
/* -- Search ------------------------------------------------------ */
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
@@ -398,8 +422,37 @@ export function EpayTab() {
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Download selection */}
|
||||
{selectedIds.length > 0 && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
className="h-7 text-xs"
|
||||
disabled={downloadingSelection}
|
||||
onClick={() => void handleDownloadSelection()}
|
||||
>
|
||||
{downloadingSelection ? (
|
||||
<Loader2 className="h-3 w-3 mr-1 animate-spin" />
|
||||
) : (
|
||||
<Download className="h-3 w-3 mr-1" />
|
||||
)}
|
||||
Descarca selectie ({selectedIds.length})
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
ZIP cu {selectedIds.length} extrase numerotate in ordinea selectarii
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
{/* Download all valid */}
|
||||
{validCount > 0 && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@@ -414,6 +467,12 @@ export function EpayTab() {
|
||||
)}
|
||||
Descarca tot ({validCount})
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
ZIP cu toate extrasele valabile
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -496,6 +555,38 @@ export function EpayTab() {
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b bg-muted/40">
|
||||
<th className="px-3 py-2 text-center font-medium w-8">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="rounded cursor-pointer"
|
||||
checked={
|
||||
filteredOrders.length > 0 &&
|
||||
filteredOrders
|
||||
.filter((o) => o.status === "completed" && o.minioPath)
|
||||
.every((o) => selectedIds.includes(o.id))
|
||||
}
|
||||
onChange={() => {
|
||||
const downloadable = filteredOrders.filter(
|
||||
(o) => o.status === "completed" && o.minioPath,
|
||||
);
|
||||
const allSelected = downloadable.every((o) =>
|
||||
selectedIds.includes(o.id),
|
||||
);
|
||||
if (allSelected) {
|
||||
setSelectedIds([]);
|
||||
} else {
|
||||
setSelectedIds(downloadable.map((o) => o.id));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Selecteaza/deselecteaza tot</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</th>
|
||||
<th className="px-3 py-2 text-left font-medium w-8">#</th>
|
||||
<th className="px-3 py-2 text-left font-medium">
|
||||
Nr. Cadastral
|
||||
@@ -523,6 +614,30 @@ export function EpayTab() {
|
||||
expired && "opacity-70",
|
||||
)}
|
||||
>
|
||||
{/* Checkbox */}
|
||||
<td className="px-3 py-2 text-center">
|
||||
{order.status === "completed" && order.minioPath ? (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<input
|
||||
type="checkbox"
|
||||
className="rounded cursor-pointer"
|
||||
checked={selectedIds.includes(order.id)}
|
||||
onChange={() => toggleSelect(order.id)}
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{selectedIds.includes(order.id)
|
||||
? `#${selectedIds.indexOf(order.id) + 1} in ZIP`
|
||||
: "Adauga in selectie"}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
) : (
|
||||
<span className="text-muted-foreground/30">—</span>
|
||||
)}
|
||||
</td>
|
||||
{/* # */}
|
||||
<td className="px-3 py-2 text-xs text-muted-foreground tabular-nums">
|
||||
{idx + 1}
|
||||
|
||||
Reference in New Issue
Block a user