fix(portal): mobile responsive — card view for RGI, visible map controls

Mobile (< 640px):
- RGI: card-based layout instead of table (shows nr cerere, status,
  solicitant, termen, rezolutie, UAT in compact card)
- Header: compact "Portal" title, smaller tab buttons
- Map: selection toolbar centered at bottom (always visible)
- UAT info card: smaller text, truncated, doesn't overlap basemap switcher
- Feature panel: narrower (w-56 vs w-64)
- Filter buttons: smaller text

Desktop (>= 640px):
- Same table view as before (hidden on mobile)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
AI Assistant
2026-03-25 06:25:12 +02:00
parent 7a36f0b613
commit 2cd35c790d
+78 -21
View File
@@ -767,11 +767,11 @@ function RgiContent() {
};
return (
<div className="space-y-4 max-w-[1400px] mx-auto p-4">
<div className="space-y-3 sm:space-y-4 max-w-[1400px] mx-auto p-3 sm:p-4">
<div>
<h2 className="text-lg font-bold">Documente Eliberate eTerra</h2>
<p className="text-sm text-muted-foreground">
Lucrari depuse cu documente eliberate descarca direct din eTerra RGI
<h2 className="text-base sm:text-lg font-bold">Documente Eliberate eTerra</h2>
<p className="text-xs sm:text-sm text-muted-foreground">
Lucrari depuse cu documente eliberate
</p>
</div>
@@ -855,7 +855,7 @@ function RgiContent() {
key={opt.id}
onClick={() => setFilterMode(opt.id)}
className={cn(
"px-3 py-1 text-xs rounded font-medium transition-colors",
"px-2 sm:px-3 py-1 text-[11px] sm:text-xs rounded font-medium transition-colors",
filterMode === opt.id
? "bg-background shadow text-foreground"
: "text-muted-foreground hover:text-foreground",
@@ -906,8 +906,65 @@ function RgiContent() {
</Card>
)}
{/* Mobile card view */}
{!loading && processed.length > 0 && (
<Card>
<div className="space-y-2 sm:hidden">
{processed.map((app) => {
const pk = app.applicationPk;
const isExpanded = expandedPk === pk;
const solved = app.hasSolution === 1;
return (
<Card key={pk} className={cn(isExpanded && "ring-1 ring-foreground/10")}>
<CardContent className="p-3">
<div className="flex items-start gap-2" onClick={() => setExpandedPk(isExpanded ? null : pk)}>
<button
type="button"
className="mt-0.5 shrink-0"
onClick={(e) => { e.stopPropagation(); void downloadAllForApp(app); }}
disabled={downloadingAppPk === pk}
>
{downloadingAppPk === pk ? (
<Loader2 className="h-4 w-4 animate-spin text-emerald-500" />
) : solved ? (
<Download className="h-4 w-4 text-emerald-500" />
) : (
<Clock className="h-4 w-4 text-muted-foreground" />
)}
</button>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 justify-between">
<span className="font-mono font-bold text-sm">{app.appNo}</span>
<Badge
variant={solved ? "default" : "secondary"}
className={cn("text-[10px]", solved && "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-400")}
>
{app.statusName || app.stateCode || "-"}
</Badge>
</div>
<p className="text-xs text-muted-foreground truncate">{app.requester || app.deponent || "-"}</p>
<div className="flex items-center gap-3 text-[11px] text-muted-foreground mt-1">
<span>Termen: {fmtTs(app.dueDate)}</span>
<Badge variant="outline" className="text-[9px]">{app.resolutionName || "-"}</Badge>
{app.uat && <span>{app.uat}</span>}
</div>
</div>
{isExpanded ? <ChevronUp className="h-4 w-4 text-muted-foreground shrink-0" /> : <ChevronDown className="h-4 w-4 text-muted-foreground shrink-0" />}
</div>
{isExpanded && (
<div className="mt-2 pt-2 border-t">
<IssuedDocsPanel applicationPk={pk} workspaceId={app.workspaceId} appNo={app.appNo} />
</div>
)}
</CardContent>
</Card>
);
})}
</div>
)}
{/* Desktop table view */}
{!loading && processed.length > 0 && (
<Card className="hidden sm:block">
<CardContent className="p-0">
<div className="overflow-x-auto">
<table className="w-full text-sm">
@@ -1450,15 +1507,15 @@ function HartaContent() {
layerVisibility={layerVisibility}
/>
{/* Top-left: UAT info + change button */}
<div className="absolute top-3 left-3 z-10 flex flex-col gap-2">
<div className="bg-background/95 backdrop-blur-sm border rounded-lg shadow-lg px-3 py-2 flex items-center gap-2">
{/* Top-left: UAT info + change button (responsive) */}
<div className="absolute top-2 left-2 right-[140px] sm:right-auto z-10 flex flex-col gap-2">
<div className="bg-background/95 backdrop-blur-sm border rounded-lg shadow-lg px-2 sm:px-3 py-1.5 sm:py-2 flex items-center gap-2">
<div className="min-w-0">
<p className="text-sm font-semibold truncate">{selectedUat?.name}</p>
<p className="text-[11px] text-muted-foreground">
<p className="text-xs sm:text-sm font-semibold truncate">{selectedUat?.name}</p>
<p className="text-[10px] sm:text-[11px] text-muted-foreground truncate">
{selectedUat?.county && `${selectedUat.county} - `}SIRUTA: {selectedSiruta}
{selectedUat && selectedUat.localFeatures > 0 && (
<span className="ml-2">{selectedUat.localFeatures.toLocaleString()} parcele</span>
<span className="ml-1 sm:ml-2">{selectedUat.localFeatures.toLocaleString()} parcele</span>
)}
</p>
</div>
@@ -1480,10 +1537,10 @@ function HartaContent() {
</div>
{/* Top-right: basemap switcher + simple feature info */}
<div className="absolute top-3 right-3 z-10 flex flex-col items-end gap-2">
<div className="absolute top-2 right-2 z-10 flex flex-col items-end gap-2">
<PortalBasemapSwitcher value={basemap} onChange={setBasemap} />
{clickedFeature && selectionMode === "off" && (
<div className="bg-background/95 backdrop-blur-sm border rounded-lg shadow-lg w-64 overflow-hidden">
<div className="bg-background/95 backdrop-blur-sm border rounded-lg shadow-lg w-56 sm:w-64 overflow-hidden">
<div className="flex items-center justify-between px-3 py-2 border-b">
<h3 className="text-sm font-semibold truncate">
{String(clickedFeature.properties.cadastral_ref ?? clickedFeature.properties.object_id ?? "Parcela")}
@@ -1526,8 +1583,8 @@ function HartaContent() {
)}
</div>
{/* Bottom-left: selection toolbar */}
<div className="absolute bottom-8 left-3 z-10">
{/* Bottom: selection toolbar — centered on mobile */}
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 sm:left-3 sm:translate-x-0 z-10">
<SelectionToolbar
selectedFeatures={selectedFeatures}
selectionMode={selectionMode}
@@ -1555,13 +1612,13 @@ export default function PortalPage() {
<div className="fixed inset-0 z-[100] bg-background overflow-auto flex flex-col">
{/* Header */}
<Tabs defaultValue="rgi" className="flex-1 flex flex-col gap-0">
<header className="border-b bg-background/95 backdrop-blur-sm px-4 py-2 flex items-center gap-4 shrink-0">
<h1 className="text-lg font-bold whitespace-nowrap">Portal Cadastral</h1>
<header className="border-b bg-background/95 backdrop-blur-sm px-3 sm:px-4 py-2 flex items-center gap-2 sm:gap-4 shrink-0">
<h1 className="text-sm sm:text-lg font-bold whitespace-nowrap">Portal</h1>
<TabsList className="h-8">
<TabsTrigger value="rgi" className="text-xs px-3 h-7">
Documente RGI
<TabsTrigger value="rgi" className="text-xs px-2 sm:px-3 h-7">
RGI
</TabsTrigger>
<TabsTrigger value="harta" className="text-xs px-3 h-7">
<TabsTrigger value="harta" className="text-xs px-2 sm:px-3 h-7">
Harta
</TabsTrigger>
</TabsList>