feat: ilan listesine harita görünümü eklendi (split layout)
- Liste / Harita toggle butonu header'da
- Harita modunda sol panel: kart listesi (fotoğraf, fiyat, oda/m²)
+ sağ panel: MapLibre harita tüm koordinatlı ilanlar
- İlan renkleri duruma göre: aktif=mavi, pasif=gri, satıldı=turuncu, kiralandı=mor
- Pini tıkla → kart listesinde o ilanın kartına scroll
- Kartı tıkla → haritada o ilanın pinine flyTo + popup açılır
- Popup içinde başlık, fiyat, özellikler, 'Detay →' linki
- Koordinatsız ilanlar listede görünür ama haritada pin yok (📍 Konum yok)
- PropertiesMapView: dynamic next/dynamic wrapper (ssr: false)
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef } from "react";
|
||||
import maplibregl from "maplibre-gl";
|
||||
import "maplibre-gl/dist/maplibre-gl.css";
|
||||
import type { Property } from "@/lib/appwrite/schema";
|
||||
import {
|
||||
PROPERTY_TYPE_LABELS,
|
||||
LISTING_TYPE_LABELS,
|
||||
} from "@/lib/appwrite/schema";
|
||||
|
||||
const STYLE_URL = "https://tiles.openfreemap.org/styles/bright";
|
||||
const TURKEY_CENTER: [number, number] = [35.0, 39.0];
|
||||
|
||||
const STATUS_COLORS: Record<string, string> = {
|
||||
aktif: "#2563eb",
|
||||
pasif: "#9ca3af",
|
||||
satildi: "#f97316",
|
||||
kiralandit: "#8b5cf6",
|
||||
};
|
||||
|
||||
interface Props {
|
||||
properties: Property[];
|
||||
selectedId?: string | null;
|
||||
onSelect: (id: string) => void;
|
||||
}
|
||||
|
||||
export function PropertiesMapViewInner({ properties, selectedId, onSelect }: Props) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const mapRef = useRef<maplibregl.Map | null>(null);
|
||||
const markersRef = useRef<Map<string, maplibregl.Marker>>(new Map());
|
||||
const popupsRef = useRef<Map<string, maplibregl.Popup>>(new Map());
|
||||
const onSelectRef = useRef(onSelect);
|
||||
onSelectRef.current = onSelect;
|
||||
|
||||
const mapped = properties.filter((p) => p.mapLat != null && p.mapLng != null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
let center: [number, number] = TURKEY_CENTER;
|
||||
let zoom = 5.5;
|
||||
if (mapped.length === 1) {
|
||||
center = [mapped[0].mapLng!, mapped[0].mapLat!];
|
||||
zoom = 14;
|
||||
} else if (mapped.length > 1) {
|
||||
const avgLng = mapped.reduce((s, p) => s + p.mapLng!, 0) / mapped.length;
|
||||
const avgLat = mapped.reduce((s, p) => s + p.mapLat!, 0) / mapped.length;
|
||||
center = [avgLng, avgLat];
|
||||
zoom = 9;
|
||||
}
|
||||
|
||||
const map = new maplibregl.Map({
|
||||
container: containerRef.current,
|
||||
style: STYLE_URL,
|
||||
center,
|
||||
zoom,
|
||||
});
|
||||
|
||||
map.addControl(new maplibregl.NavigationControl(), "top-right");
|
||||
mapRef.current = map;
|
||||
|
||||
map.on("load", () => {
|
||||
for (const p of mapped) {
|
||||
const color = STATUS_COLORS[p.status] ?? "#2563eb";
|
||||
|
||||
const popup = new maplibregl.Popup({
|
||||
offset: 28,
|
||||
closeButton: true,
|
||||
maxWidth: "260px",
|
||||
className: "property-popup",
|
||||
}).setHTML(`
|
||||
<div style="font-family:system-ui,sans-serif;padding:2px 0">
|
||||
<p style="font-weight:600;font-size:13px;margin:0 0 3px;line-height:1.3">${p.title}</p>
|
||||
<p style="font-size:11px;color:#6b7280;margin:0 0 6px">${[p.neighborhood, p.district, p.city].filter(Boolean).join(", ")}</p>
|
||||
<p style="font-size:18px;font-weight:700;color:#111;margin:0 0 6px">${p.price.toLocaleString("tr-TR")} <span style="font-size:12px;font-weight:400;color:#6b7280">${p.currency ?? "TRY"}</span></p>
|
||||
<div style="display:flex;gap:5px;flex-wrap:wrap;margin-bottom:10px">
|
||||
<span style="background:#f3f4f6;border-radius:999px;padding:2px 8px;font-size:11px;color:#374151">${PROPERTY_TYPE_LABELS[p.propertyType] ?? p.propertyType}</span>
|
||||
<span style="background:#f3f4f6;border-radius:999px;padding:2px 8px;font-size:11px;color:#374151">${LISTING_TYPE_LABELS[p.listingType] ?? p.listingType}</span>
|
||||
${p.roomCount ? `<span style="background:#f3f4f6;border-radius:999px;padding:2px 8px;font-size:11px;color:#374151">${p.roomCount}</span>` : ""}
|
||||
${p.netM2 ? `<span style="background:#f3f4f6;border-radius:999px;padding:2px 8px;font-size:11px;color:#374151">${p.netM2} m²</span>` : ""}
|
||||
</div>
|
||||
<a href="/properties/${p.$id}" style="display:inline-flex;align-items:center;background:#2563eb;color:white;border-radius:6px;padding:5px 14px;font-size:12px;text-decoration:none;font-weight:500">Detay →</a>
|
||||
</div>
|
||||
`);
|
||||
|
||||
const marker = new maplibregl.Marker({ color })
|
||||
.setLngLat([p.mapLng!, p.mapLat!])
|
||||
.setPopup(popup)
|
||||
.addTo(map);
|
||||
|
||||
marker.getElement().addEventListener("click", () => {
|
||||
onSelectRef.current(p.$id);
|
||||
});
|
||||
|
||||
markersRef.current.set(p.$id, marker);
|
||||
popupsRef.current.set(p.$id, popup);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
map.remove();
|
||||
mapRef.current = null;
|
||||
markersRef.current.clear();
|
||||
popupsRef.current.clear();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// Fly to + open popup when selection changes
|
||||
useEffect(() => {
|
||||
if (!selectedId || !mapRef.current) return;
|
||||
const prop = mapped.find((p) => p.$id === selectedId);
|
||||
if (!prop) return;
|
||||
|
||||
mapRef.current.flyTo({
|
||||
center: [prop.mapLng!, prop.mapLat!],
|
||||
zoom: Math.max(mapRef.current.getZoom(), 14),
|
||||
duration: 700,
|
||||
});
|
||||
|
||||
const marker = markersRef.current.get(selectedId);
|
||||
if (marker && !marker.getPopup().isOpen()) {
|
||||
marker.togglePopup();
|
||||
}
|
||||
}, [selectedId]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
if (mapped.length === 0) {
|
||||
return (
|
||||
<div className="flex h-full items-center justify-center bg-muted/30 rounded-lg border text-sm text-muted-foreground">
|
||||
Koordinatlı ilan yok — ilan formundan konum ekleyin.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <div ref={containerRef} className="h-full w-full rounded-lg overflow-hidden border" />;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
"use client";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import { MapPin } from "lucide-react";
|
||||
import type { Property } from "@/lib/appwrite/schema";
|
||||
|
||||
const Inner = dynamic(
|
||||
() =>
|
||||
import("./properties-map-view-inner").then((m) => m.PropertiesMapViewInner),
|
||||
{
|
||||
ssr: false,
|
||||
loading: () => (
|
||||
<div className="flex h-full items-center justify-center rounded-lg border bg-muted/30">
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<MapPin className="size-4 animate-pulse" />
|
||||
Harita yükleniyor...
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
interface Props {
|
||||
properties: Property[];
|
||||
selectedId?: string | null;
|
||||
onSelect: (id: string) => void;
|
||||
}
|
||||
|
||||
export function PropertiesMapView(props: Props) {
|
||||
return <Inner {...props} />;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useState, useRef } from "react";
|
||||
import Link from "next/link";
|
||||
import { MoreHorizontal, Plus, Pencil, Trash2, ExternalLink } from "lucide-react";
|
||||
import { MoreHorizontal, Plus, Pencil, Trash2, ExternalLink, List, Map } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { deletePropertyAction } from "@/lib/appwrite/property-actions";
|
||||
import { PropertyFormSheet } from "./property-form-sheet";
|
||||
import { PropertiesMapView } from "@/components/map/properties-map-view";
|
||||
import { getPropertyImagePreviewUrl, parseImageIds } from "@/lib/appwrite/storage-utils";
|
||||
import type { Property } from "@/lib/appwrite/schema";
|
||||
import {
|
||||
PROPERTY_STATUS_LABELS,
|
||||
@@ -26,20 +28,19 @@ interface PropertiesClientProps {
|
||||
initialProperties: Property[];
|
||||
}
|
||||
|
||||
type ViewMode = "list" | "map";
|
||||
|
||||
export function PropertiesClient({ initialProperties }: PropertiesClientProps) {
|
||||
const [properties, setProperties] = useState(initialProperties);
|
||||
const [sheetOpen, setSheetOpen] = useState(false);
|
||||
const [editing, setEditing] = useState<Property | null>(null);
|
||||
const [viewMode, setViewMode] = useState<ViewMode>("list");
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||
const rowRefs = useRef<Record<string, HTMLTableRowElement>>({});
|
||||
const cardRefs = useRef<Record<string, HTMLDivElement>>({});
|
||||
|
||||
function openCreate() {
|
||||
setEditing(null);
|
||||
setSheetOpen(true);
|
||||
}
|
||||
|
||||
function openEdit(p: Property) {
|
||||
setEditing(p);
|
||||
setSheetOpen(true);
|
||||
}
|
||||
function openCreate() { setEditing(null); setSheetOpen(true); }
|
||||
function openEdit(p: Property) { setEditing(p); setSheetOpen(true); }
|
||||
|
||||
async function handleDelete(p: Property) {
|
||||
if (!confirm(`"${p.title}" silinsin mi?`)) return;
|
||||
@@ -52,94 +53,231 @@ export function PropertiesClient({ initialProperties }: PropertiesClientProps) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleSuccess() {
|
||||
// Revalidation handles the data refresh; re-fetching in client is not
|
||||
// necessary since the page will reload on next navigation.
|
||||
// For immediate local update we just remove the item or close the sheet.
|
||||
function handleMapSelect(id: string) {
|
||||
setSelectedId(id);
|
||||
// Scroll the card into view in the list panel
|
||||
const card = cardRefs.current[id];
|
||||
card?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
||||
}
|
||||
|
||||
function handleCardClick(id: string) {
|
||||
setSelectedId(id === selectedId ? null : id);
|
||||
}
|
||||
|
||||
const mappedCount = properties.filter((p) => p.mapLat != null && p.mapLng != null).length;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">İlanlar</h1>
|
||||
<Button onClick={openCreate} size="sm">
|
||||
<Plus className="mr-1.5 size-4" />
|
||||
Yeni İlan
|
||||
</Button>
|
||||
<div className="flex flex-col gap-4 flex-1">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between gap-3 flex-wrap">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">İlanlar</h1>
|
||||
{viewMode === "map" && mappedCount < properties.length && (
|
||||
<p className="text-xs text-muted-foreground mt-0.5">
|
||||
{mappedCount} / {properties.length} ilanın koordinatı var
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* View toggle */}
|
||||
<div className="flex rounded-md border overflow-hidden">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setViewMode("list")}
|
||||
className={`flex items-center gap-1.5 px-3 py-1.5 text-sm transition-colors ${
|
||||
viewMode === "list"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "bg-background text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
<List className="size-3.5" />
|
||||
Liste
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setViewMode("map")}
|
||||
className={`flex items-center gap-1.5 px-3 py-1.5 text-sm transition-colors border-l ${
|
||||
viewMode === "map"
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "bg-background text-muted-foreground hover:text-foreground"
|
||||
}`}
|
||||
>
|
||||
<Map className="size-3.5" />
|
||||
Harita
|
||||
</button>
|
||||
</div>
|
||||
<Button onClick={openCreate} size="sm">
|
||||
<Plus className="mr-1.5 size-4" />
|
||||
Yeni İlan
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Başlık</TableHead>
|
||||
<TableHead>Tip</TableHead>
|
||||
<TableHead>Tür</TableHead>
|
||||
<TableHead>Şehir</TableHead>
|
||||
<TableHead className="text-right">Fiyat</TableHead>
|
||||
<TableHead>Durum</TableHead>
|
||||
<TableHead />
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{properties.length === 0 && (
|
||||
{/* List view */}
|
||||
{viewMode === "list" && (
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableCell colSpan={7} className="text-muted-foreground text-center py-10">
|
||||
Henüz ilan yok.
|
||||
</TableCell>
|
||||
<TableHead>Başlık</TableHead>
|
||||
<TableHead>Tip</TableHead>
|
||||
<TableHead>Tür</TableHead>
|
||||
<TableHead>Şehir</TableHead>
|
||||
<TableHead className="text-right">Fiyat</TableHead>
|
||||
<TableHead>Durum</TableHead>
|
||||
<TableHead />
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{properties.length === 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={7} className="text-muted-foreground text-center py-10">
|
||||
Henüz ilan yok.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{properties.map((p) => (
|
||||
<TableRow
|
||||
key={p.$id}
|
||||
ref={(el) => { if (el) rowRefs.current[p.$id] = el; }}
|
||||
>
|
||||
<TableCell className="font-medium max-w-[200px] truncate">{p.title}</TableCell>
|
||||
<TableCell>{PROPERTY_TYPE_LABELS[p.propertyType] ?? p.propertyType}</TableCell>
|
||||
<TableCell>{LISTING_TYPE_LABELS[p.listingType] ?? p.listingType}</TableCell>
|
||||
<TableCell>{[p.city, p.district].filter(Boolean).join(", ")}</TableCell>
|
||||
<TableCell className="text-right tabular-nums">
|
||||
{p.price.toLocaleString("tr-TR")} {p.currency ?? "TRY"}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<StatusBadge status={p.status} />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="size-8">
|
||||
<MoreHorizontal className="size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href={`/properties/${p.$id}`}>
|
||||
<ExternalLink className="mr-2 size-4" />
|
||||
Detay
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => openEdit(p)}>
|
||||
<Pencil className="mr-2 size-4" />
|
||||
Düzenle
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleDelete(p)}
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<Trash2 className="mr-2 size-4" />
|
||||
Sil
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Map view — split layout */}
|
||||
{viewMode === "map" && (
|
||||
<div className="flex gap-4 flex-1 min-h-0" style={{ height: "calc(100vh - 14rem)" }}>
|
||||
{/* Left: scrollable property cards */}
|
||||
<div className="w-80 shrink-0 overflow-y-auto space-y-2 pr-1">
|
||||
{properties.length === 0 && (
|
||||
<p className="text-muted-foreground text-sm text-center py-10">Henüz ilan yok.</p>
|
||||
)}
|
||||
{properties.map((p) => (
|
||||
<TableRow key={p.$id}>
|
||||
<TableCell className="font-medium max-w-[200px] truncate">{p.title}</TableCell>
|
||||
<TableCell>{PROPERTY_TYPE_LABELS[p.propertyType] ?? p.propertyType}</TableCell>
|
||||
<TableCell>{LISTING_TYPE_LABELS[p.listingType] ?? p.listingType}</TableCell>
|
||||
<TableCell>{[p.city, p.district].filter(Boolean).join(", ")}</TableCell>
|
||||
<TableCell className="text-right tabular-nums">
|
||||
{p.price.toLocaleString("tr-TR")} {p.currency ?? "TRY"}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<StatusBadge status={p.status} />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="size-8">
|
||||
<MoreHorizontal className="size-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href={`/properties/${p.$id}`}>
|
||||
<ExternalLink className="mr-2 size-4" />
|
||||
Detay
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => openEdit(p)}>
|
||||
<Pencil className="mr-2 size-4" />
|
||||
Düzenle
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => handleDelete(p)}
|
||||
className="text-destructive focus:text-destructive"
|
||||
{properties.map((p) => {
|
||||
const coverImageId = parseImageIds(p.imageIds)[0];
|
||||
const hasCoords = p.mapLat != null && p.mapLng != null;
|
||||
const isSelected = selectedId === p.$id;
|
||||
return (
|
||||
<div
|
||||
key={p.$id}
|
||||
ref={(el) => { if (el) cardRefs.current[p.$id] = el; }}
|
||||
onClick={() => hasCoords && handleCardClick(p.$id)}
|
||||
className={`rounded-lg border bg-card overflow-hidden transition-all ${
|
||||
hasCoords ? "cursor-pointer hover:shadow-md" : "opacity-60"
|
||||
} ${isSelected ? "ring-2 ring-primary shadow-md" : ""}`}
|
||||
>
|
||||
{/* Cover image */}
|
||||
{coverImageId && (
|
||||
<div className="h-28 overflow-hidden bg-muted">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={getPropertyImagePreviewUrl(coverImageId, 320, 180)}
|
||||
alt={p.title}
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="p-3 space-y-1">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<p className="text-sm font-semibold leading-snug line-clamp-2">{p.title}</p>
|
||||
<StatusBadge status={p.status} />
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground line-clamp-1">
|
||||
{[p.neighborhood, p.district, p.city].filter(Boolean).join(", ")}
|
||||
</p>
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-sm font-bold">
|
||||
{p.price.toLocaleString("tr-TR")}
|
||||
<span className="text-xs font-normal text-muted-foreground ml-1">{p.currency ?? "TRY"}</span>
|
||||
</p>
|
||||
<div className="flex gap-1">
|
||||
{p.roomCount && (
|
||||
<span className="text-xs bg-muted text-muted-foreground px-1.5 py-0.5 rounded">
|
||||
{p.roomCount}
|
||||
</span>
|
||||
)}
|
||||
{p.netM2 && (
|
||||
<span className="text-xs bg-muted text-muted-foreground px-1.5 py-0.5 rounded">
|
||||
{p.netM2}m²
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between pt-0.5">
|
||||
{!hasCoords && (
|
||||
<span className="text-xs text-muted-foreground">📍 Konum yok</span>
|
||||
)}
|
||||
<Link
|
||||
href={`/properties/${p.$id}`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="ml-auto text-xs text-primary hover:underline"
|
||||
>
|
||||
<Trash2 className="mr-2 size-4" />
|
||||
Sil
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
Detay →
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Right: map */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<PropertiesMapView
|
||||
properties={properties}
|
||||
selectedId={selectedId}
|
||||
onSelect={handleMapSelect}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<PropertyFormSheet
|
||||
open={sheetOpen}
|
||||
onOpenChange={setSheetOpen}
|
||||
property={editing}
|
||||
onSuccess={handleSuccess}
|
||||
onSuccess={() => {}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -153,7 +291,7 @@ function StatusBadge({ status }: { status: string }) {
|
||||
kiralandit: "outline",
|
||||
};
|
||||
return (
|
||||
<Badge variant={map[status] ?? "secondary"}>
|
||||
<Badge variant={map[status] ?? "secondary"} className="shrink-0 text-xs">
|
||||
{PROPERTY_STATUS_LABELS[status as keyof typeof PROPERTY_STATUS_LABELS] ?? status}
|
||||
</Badge>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user