@burdenoff/microfe-housingvista 2026.703.6 → 2026.703.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/housingvista/pages/properties/PropertyDetailPage.js +1 -1
- package/dist/housingvista/pages/properties/PropertyDetailPage.js.map +1 -1
- package/dist/housingvista/pages/transactions/TransactionsPage.js +1 -1
- package/dist/housingvista/pages/transactions/TransactionsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PropertyDetailPage.js","names":[],"sources":["../../../../src/housingvista/pages/properties/PropertyDetailPage.tsx"],"sourcesContent":["/**\n * HousingVista Property Detail Page\n * Full property detail view backed by the single-item `housingvistaProperty(id)`\n * query, with the core buyer journey wired to real mutations: shortlist, book a\n * viewing, submit an offer, plus owner-side edit and delete.\n * @module housingvista\n */\n\nimport { useState, useMemo, type FormEvent } from 'react';\nimport { useParams, useNavigate, useLocation } from 'react-router-dom';\nimport {\n ArrowLeft,\n MapPin,\n Bed,\n Bath,\n Square,\n Car,\n Heart,\n Share2,\n CheckCircle,\n Calendar,\n DollarSign,\n ChevronLeft,\n ChevronRight,\n Building,\n Pencil,\n Trash2,\n Calculator,\n TrendingUp,\n MessageSquare,\n KeyRound,\n} from 'lucide-react';\nimport {\n EmphasisPanel,\n PagePurpose,\n NextSteps,\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Breadcrumbs,\n StatusBadge,\n EmptyState,\n LoadingSkeleton,\n ContactOwnerDialog,\n ReviewsSection,\n} from '../../components';\nimport type { BreadcrumbItem } from '../../components';\nimport {\n useProperty,\n useProperties,\n useShortlists,\n useSaveShortlist,\n useRemoveShortlist,\n useBookViewing,\n useSubmitOffer,\n useUpdateProperty,\n useDeleteProperty,\n useRentalUnits,\n useSubmitRentalApplication,\n useCreateInvestment,\n} from '../../hooks';\nimport { notify } from '../../lib/toast';\nimport { cn, formatCurrency, formatNumber, formatDate } from '../../utils/helpers';\nimport type { Property, PropertyStatus } from '../../types';\n\nconst statusVariantMap: Record<string, 'success' | 'warning' | 'error' | 'info' | 'default' | 'muted'> = {\n for_sale: 'success',\n for_rent: 'info',\n sold: 'muted',\n rented: 'muted',\n under_contract: 'warning',\n off_market: 'error',\n};\n\nconst statusOptions: { value: PropertyStatus; label: string }[] = [\n { value: 'for_sale', label: 'For Sale' },\n { value: 'for_rent', label: 'For Rent' },\n { value: 'under_contract', label: 'Under Contract' },\n { value: 'sold', label: 'Sold' },\n { value: 'rented', label: 'Rented' },\n { value: 'off_market', label: 'Off Market' },\n];\n\ntype TabId = 'details' | 'amenities' | 'location';\nconst tabs: { id: TabId; label: string }[] = [\n { id: 'details', label: 'Details' },\n { id: 'amenities', label: 'Amenities' },\n { id: 'location', label: 'Location' },\n];\n\nconst inputClass =\n 'h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\nexport const PropertyDetailPage = () => {\n const { id } = useParams<{ id: string }>();\n const navigate = useNavigate();\n const location = useLocation();\n\n const { data: property, loading } = useProperty(id);\n const { data: allProperties } = useProperties({ pagination: { limit: 8, offset: 0 } });\n const { data: shortlists } = useShortlists();\n const { data: rentalUnits } = useRentalUnits({\n filter: id ? { propertyId: id } : undefined,\n pagination: { limit: 1, offset: 0 },\n });\n const { saveShortlist, loading: savingShortlist } = useSaveShortlist();\n const { removeShortlist, loading: removingShortlist } = useRemoveShortlist();\n const { bookViewing, loading: bookingViewing } = useBookViewing();\n const { submitOffer, loading: submittingOffer } = useSubmitOffer();\n const { updateProperty, loading: updatingProperty } = useUpdateProperty();\n const { deleteProperty, loading: deletingProperty } = useDeleteProperty();\n const { submitRentalApplication, loading: submittingRental } = useSubmitRentalApplication();\n const { createInvestment, loading: creatingInvestment } = useCreateInvestment();\n\n const similarProperties = useMemo(() => allProperties.filter((p) => p.id !== id).slice(0, 3), [allProperties, id]);\n const isShortlisted = useMemo(() => shortlists.some((s) => s.propertyId === id), [shortlists, id]);\n\n const [currentImageIndex, setCurrentImageIndex] = useState(0);\n const [activeTab, setActiveTab] = useState<TabId>('details');\n const [viewingOpen, setViewingOpen] = useState(false);\n const [offerOpen, setOfferOpen] = useState(false);\n const [editOpen, setEditOpen] = useState(false);\n const [deleteOpen, setDeleteOpen] = useState(false);\n const [rentalOpen, setRentalOpen] = useState(false);\n\n // Rental application form state\n const [applicantName, setApplicantName] = useState('');\n const [applicantEmail, setApplicantEmail] = useState('');\n const [applicantPhone, setApplicantPhone] = useState('');\n const [monthlyIncome, setMonthlyIncome] = useState('');\n const [moveInDate, setMoveInDate] = useState('');\n\n // Investment-analysis calculator inputs (blank = use a price-derived default)\n const [rentInput, setRentInput] = useState('');\n const [costInput, setCostInput] = useState('');\n\n // Viewing form state\n const [viewingDate, setViewingDate] = useState('');\n const [viewingType, setViewingType] = useState<'in_person' | 'virtual'>('in_person');\n const [attendeeName, setAttendeeName] = useState('');\n const [attendeeEmail, setAttendeeEmail] = useState('');\n const [viewingNotes, setViewingNotes] = useState('');\n\n // Offer form state\n const [offerAmount, setOfferAmount] = useState('');\n const [offerNotes, setOfferNotes] = useState('');\n\n // Edit form state (seeded on open)\n const [editTitle, setEditTitle] = useState('');\n const [editDescription, setEditDescription] = useState('');\n const [editStatus, setEditStatus] = useState<PropertyStatus>('for_sale');\n const [editPrice, setEditPrice] = useState('');\n\n if (loading) {\n return (\n <div className=\"space-y-6 p-6\">\n <LoadingSkeleton className=\"h-80\" />\n <LoadingSkeleton variant=\"detail\" />\n </div>\n );\n }\n\n if (!property) {\n return (\n <div className=\"p-6\">\n <EmptyState\n illustration=\"empty-search\"\n icon={<Building className=\"h-12 w-12\" />}\n title=\"Property Not Found\"\n description=\"The property you're looking for doesn't exist or has been removed.\"\n action={\n <button\n onClick={() => navigate('../properties')}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Properties\n </button>\n }\n />\n </div>\n );\n }\n\n const breadcrumbs: BreadcrumbItem[] = [{ label: 'Properties', href: '../' }, { label: property.title }];\n\n const formatPrice = (price: Property['price']) => {\n const amount = formatCurrency(price.amount, price.currency);\n if (price.type === 'rent' && price.period) return `${amount}/${price.period.replace('ly', '')}`;\n return amount;\n };\n\n const nextImage = () => setCurrentImageIndex((prev) => (prev === property.images.length - 1 ? 0 : prev + 1));\n const prevImage = () => setCurrentImageIndex((prev) => (prev === 0 ? property.images.length - 1 : prev - 1));\n\n const handleShare = () => {\n if (navigator.share) {\n navigator.share({ title: property.title, text: property.description, url: window.location.href });\n } else {\n navigator.clipboard.writeText(window.location.href);\n notify.info('Link copied', 'The property link is on your clipboard.');\n }\n };\n\n const toggleShortlist = async () => {\n try {\n if (isShortlisted) {\n await removeShortlist(property.id);\n notify.success('Removed from shortlist');\n } else {\n await saveShortlist(property.id);\n notify.success('Saved to shortlist');\n }\n } catch (err) {\n notify.error('Could not update shortlist', (err as Error).message);\n }\n };\n\n const handleBookViewing = async (e: FormEvent) => {\n e.preventDefault();\n if (!viewingDate) return;\n try {\n await bookViewing({\n propertyId: property.id,\n scheduledAt: new Date(viewingDate).toISOString(),\n type: viewingType,\n attendeeName: attendeeName.trim() || undefined,\n attendeeEmail: attendeeEmail.trim() || undefined,\n notes: viewingNotes.trim() || undefined,\n });\n notify.success('Viewing requested', 'The owner will confirm your slot shortly.');\n setViewingOpen(false);\n setViewingDate('');\n setAttendeeName('');\n setAttendeeEmail('');\n setViewingNotes('');\n } catch (err) {\n notify.error('Could not book viewing', (err as Error).message);\n }\n };\n\n const handleSubmitOffer = async (e: FormEvent) => {\n e.preventDefault();\n const amount = Number(offerAmount);\n if (!amount || amount <= 0) {\n notify.warning('Enter a valid offer amount');\n return;\n }\n try {\n await submitOffer({\n propertyId: property.id,\n amount,\n currency: property.price.currency,\n notes: offerNotes.trim() || undefined,\n });\n notify.success('Offer submitted', `Your offer of ${formatCurrency(amount, property.price.currency)} was sent.`);\n setOfferOpen(false);\n setOfferAmount('');\n setOfferNotes('');\n } catch (err) {\n notify.error('Could not submit offer', (err as Error).message);\n }\n };\n\n const openEdit = () => {\n setEditTitle(property.title);\n setEditDescription(property.description);\n setEditStatus(property.status);\n setEditPrice(String(property.price.amount));\n setEditOpen(true);\n };\n\n const handleEdit = async (e: FormEvent) => {\n e.preventDefault();\n try {\n await updateProperty(property.id, {\n title: editTitle.trim(),\n description: editDescription.trim(),\n status: editStatus,\n price: Number(editPrice) || property.price.amount,\n });\n notify.success('Property updated');\n setEditOpen(false);\n } catch (err) {\n notify.error('Could not update property', (err as Error).message);\n }\n };\n\n const handleDelete = async () => {\n try {\n await deleteProperty(property.id);\n notify.success('Property deleted');\n navigate('../properties');\n } catch (err) {\n notify.error('Could not delete property', (err as Error).message);\n }\n };\n\n // First available rental unit for this property (needed to submit an application).\n const rentalUnitId = rentalUnits[0]?.id;\n\n const handleSubmitRental = async (e: FormEvent) => {\n e.preventDefault();\n if (!rentalUnitId) {\n notify.warning('No rental unit is available for this property yet');\n return;\n }\n if (!applicantName.trim() || !applicantEmail.trim()) {\n notify.warning('Enter your name and email to apply');\n return;\n }\n try {\n await submitRentalApplication({\n rentalUnitId,\n applicantName: applicantName.trim(),\n applicantEmail: applicantEmail.trim(),\n applicantPhone: applicantPhone.trim() || undefined,\n monthlyIncome: monthlyIncome ? Number(monthlyIncome) : undefined,\n moveInDate: moveInDate ? new Date(moveInDate).toISOString() : undefined,\n });\n notify.success('Application submitted', 'The owner will review your rental application shortly.');\n setRentalOpen(false);\n setApplicantName('');\n setApplicantEmail('');\n setApplicantPhone('');\n setMonthlyIncome('');\n setMoveInDate('');\n } catch (err) {\n notify.error('Could not submit application', (err as Error).message);\n }\n };\n\n const handleTrackInvestment = async () => {\n try {\n await createInvestment({\n propertyId: property.id,\n title: `${property.title} — investment`,\n status: 'active',\n investmentType: 'full_ownership',\n riskLevel: 'medium',\n totalValue: property.price.amount,\n startDate: new Date().toISOString(),\n });\n notify.success('Tracking investment', `${property.title} was added to your portfolio.`);\n } catch (err) {\n notify.error('Could not track investment', (err as Error).message);\n }\n };\n\n // Investment analysis — modelled client-side from the listing price + user inputs.\n const price = property.price.amount;\n const monthlyRent = Number(rentInput) || Math.round(price * 0.005); // ~0.5%/mo default\n const monthlyHoldingCosts = Number(costInput) || Math.round(price * 0.0015); // ~0.15%/mo default\n const grossAnnualRent = monthlyRent * 12;\n const grossYield = price > 0 ? (grossAnnualRent / price) * 100 : 0;\n const annualHoldingCosts = monthlyHoldingCosts * 12;\n const netAnnualIncome = grossAnnualRent - annualHoldingCosts;\n const projectedRoi = price > 0 ? (netAnnualIncome / price) * 100 : 0;\n\n return (\n <div className=\"space-y-6 p-6\">\n <Breadcrumbs items={breadcrumbs} />\n\n {/* Header */}\n <div className=\"flex flex-wrap items-center gap-3\">\n <button\n onClick={() => navigate('../properties')}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-3 py-1.5 text-sm text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back\n </button>\n <StatusBadge\n label={property.status.replace('_', ' ')}\n variant={statusVariantMap[property.status] ?? 'default'}\n dot\n />\n {property.verificationStatus === 'verified' && <StatusBadge label=\"Verified\" variant=\"success\" dot />}\n {property.price.negotiable && <StatusBadge label=\"Negotiable\" variant=\"info\" />}\n <div className=\"ml-auto flex items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={openEdit}>\n <Pencil className=\"mr-1.5 h-4 w-4\" />\n Edit\n </Button>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => setDeleteOpen(true)}>\n <Trash2 className=\"mr-1.5 h-4 w-4\" />\n Delete\n </Button>\n </div>\n </div>\n\n <PagePurpose className=\"max-w-3xl\">\n Everything you need to evaluate this property in one place — photos, specifications, amenities, and location.\n Use it to shortlist the property, schedule a viewing, or make an offer.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"housingvista-property-detail\"\n steps={[\n {\n id: 'schedule',\n label: 'Schedule a viewing',\n description: 'Book a time to see the property in person or virtually.',\n onClick: () => setViewingOpen(true),\n },\n {\n id: 'offer',\n label: 'Make an offer',\n description: 'Submit a price and let the owner respond.',\n onClick: () => setOfferOpen(true),\n },\n {\n id: 'location',\n label: 'Explore the location',\n description: 'See the neighborhood, nearby landmarks, and map.',\n onClick: () => setActiveTab('location'),\n },\n ]}\n />\n\n {/* Image Gallery */}\n {property.images.length > 0 && (\n <div className=\"relative overflow-hidden rounded-xl border border-border-subtle\">\n <img\n src={property.images[currentImageIndex]?.url}\n alt={property.images[currentImageIndex]?.title ?? property.title}\n className=\"h-80 w-full object-cover sm:h-96\"\n />\n {property.images.length > 1 && (\n <>\n <button\n onClick={prevImage}\n className=\"absolute left-4 top-1/2 -translate-y-1/2 rounded-full bg-bg-overlay/70 p-2 text-text-inverse transition-colors hover:bg-bg-overlay\"\n >\n <ChevronLeft className=\"h-5 w-5\" />\n </button>\n <button\n onClick={nextImage}\n className=\"absolute right-4 top-1/2 -translate-y-1/2 rounded-full bg-bg-overlay/70 p-2 text-text-inverse transition-colors hover:bg-bg-overlay\"\n >\n <ChevronRight className=\"h-5 w-5\" />\n </button>\n <div className=\"absolute bottom-4 left-1/2 flex -translate-x-1/2 gap-2\">\n {property.images.map((img, index) => (\n <button\n key={img.id}\n className={cn(\n 'h-2 w-2 rounded-full',\n index === currentImageIndex ? 'bg-text-inverse' : 'bg-text-inverse/50'\n )}\n onClick={() => setCurrentImageIndex(index)}\n />\n ))}\n </div>\n </>\n )}\n <div className=\"absolute right-4 top-4 flex gap-2\">\n <button\n onClick={toggleShortlist}\n disabled={savingShortlist || removingShortlist}\n aria-label={isShortlisted ? 'Remove from shortlist' : 'Save to shortlist'}\n className=\"rounded-full bg-bg-surface/80 p-2 backdrop-blur-sm transition-colors hover:bg-bg-surface disabled:opacity-60\"\n >\n <Heart\n className={cn(\n 'h-5 w-5',\n isShortlisted ? 'fill-status-error-text text-status-error-text' : 'text-text-secondary'\n )}\n />\n </button>\n <button\n onClick={handleShare}\n className=\"rounded-full bg-bg-surface/80 p-2 backdrop-blur-sm transition-colors hover:bg-bg-surface\"\n >\n <Share2 className=\"h-5 w-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"absolute bottom-4 right-4\">\n <span className=\"rounded-md bg-bg-overlay/70 px-2 py-1 text-xs font-medium text-text-inverse\">\n {currentImageIndex + 1} / {property.images.length}\n </span>\n </div>\n </div>\n )}\n\n {/* Main Content Grid */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Left: Main content */}\n <div className=\"space-y-6 lg:col-span-2\">\n <EmphasisPanel>\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">{property.title}</h1>\n <div className=\"mt-2 flex items-center text-text-secondary\">\n <MapPin className=\"mr-1.5 h-4 w-4 shrink-0\" />\n <span className=\"text-sm\">\n {[property.location.address, property.location.neighborhood, property.location.city]\n .filter(Boolean)\n .join(', ')}\n </span>\n </div>\n </div>\n <div className=\"text-right\">\n <div className=\"text-2xl font-bold text-text-primary\">{formatPrice(property.price)}</div>\n {property.price.pricePerSqft && (\n <div className=\"text-sm text-text-secondary\">{formatCurrency(property.price.pricePerSqft)}/sqft</div>\n )}\n </div>\n </div>\n\n {/* Key Specs */}\n <div className=\"mt-6 grid grid-cols-2 gap-4 border-t border-border-subtle pt-6 sm:grid-cols-4\">\n {property.specifications.bedrooms != null && (\n <div className=\"flex items-center gap-2 text-sm text-text-primary\">\n <Bed className=\"h-5 w-5 text-text-secondary\" />\n <span>\n {property.specifications.bedrooms} Bed{property.specifications.bedrooms > 1 ? 's' : ''}\n </span>\n </div>\n )}\n {property.specifications.bathrooms != null && (\n <div className=\"flex items-center gap-2 text-sm text-text-primary\">\n <Bath className=\"h-5 w-5 text-text-secondary\" />\n <span>\n {property.specifications.bathrooms} Bath{property.specifications.bathrooms > 1 ? 's' : ''}\n </span>\n </div>\n )}\n <div className=\"flex items-center gap-2 text-sm text-text-primary\">\n <Square className=\"h-5 w-5 text-text-secondary\" />\n <span>{formatNumber(property.specifications.totalArea)} sqft</span>\n </div>\n {property.specifications.parking != null && property.specifications.parking > 0 && (\n <div className=\"flex items-center gap-2 text-sm text-text-primary\">\n <Car className=\"h-5 w-5 text-text-secondary\" />\n <span>{property.specifications.parking} Parking</span>\n </div>\n )}\n </div>\n\n {property.description && (\n <div className=\"mt-6 border-t border-border-subtle pt-6\">\n <h3 className=\"mb-3 text-base font-semibold text-text-primary\">Description</h3>\n <p className=\"text-sm leading-relaxed text-text-secondary\">{property.description}</p>\n </div>\n )}\n </EmphasisPanel>\n\n {/* Tabbed Content */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface\">\n <div className=\"border-b border-border-subtle\">\n <nav className=\"flex space-x-6 overflow-x-auto px-6\">\n {tabs.map((tab) => (\n <button\n key={tab.id}\n onClick={() => setActiveTab(tab.id)}\n className={cn(\n 'whitespace-nowrap border-b-2 py-3 text-sm font-medium transition-colors',\n activeTab === tab.id\n ? 'border-action-primary-border text-text-primary'\n : 'border-transparent text-text-secondary hover:text-text-primary'\n )}\n >\n {tab.label}\n </button>\n ))}\n </nav>\n </div>\n\n <div className=\"p-6\">\n {activeTab === 'details' && (\n <div className=\"grid grid-cols-1 gap-6 sm:grid-cols-2\">\n <div>\n <h4 className=\"mb-3 text-sm font-semibold text-text-primary\">Property Information</h4>\n <dl className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Property Type</dt>\n <dd className=\"font-medium capitalize text-text-primary\">{property.type}</dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Category</dt>\n <dd className=\"font-medium capitalize text-text-primary\">{property.category}</dd>\n </div>\n {property.specifications.ageOfProperty != null && (\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Age of Property</dt>\n <dd className=\"font-medium text-text-primary\">\n {property.specifications.ageOfProperty} years\n </dd>\n </div>\n )}\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Furnishing</dt>\n <dd className=\"font-medium capitalize text-text-primary\">\n {property.specifications.furnishingStatus?.replace('_', ' ')}\n </dd>\n </div>\n {property.specifications.facing && (\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Facing</dt>\n <dd className=\"font-medium text-text-primary\">{property.specifications.facing}</dd>\n </div>\n )}\n {property.specifications.floors != null && (\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Floor</dt>\n <dd className=\"font-medium text-text-primary\">\n {property.specifications.floors} of {property.specifications.totalFloors}\n </dd>\n </div>\n )}\n </dl>\n </div>\n <div>\n <h4 className=\"mb-3 text-sm font-semibold text-text-primary\">Area Details</h4>\n <dl className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Total Area</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatNumber(property.specifications.totalArea)} sqft\n </dd>\n </div>\n {property.specifications.carpetArea && (\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Carpet Area</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatNumber(property.specifications.carpetArea)} sqft\n </dd>\n </div>\n )}\n {property.specifications.builtUpArea && (\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Built-up Area</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatNumber(property.specifications.builtUpArea)} sqft\n </dd>\n </div>\n )}\n {property.price.pricePerSqft && (\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Price per sqft</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatCurrency(property.price.pricePerSqft)}\n </dd>\n </div>\n )}\n </dl>\n </div>\n </div>\n )}\n\n {activeTab === 'amenities' && (\n <div>\n {property.amenities.length > 0 ? (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n {property.amenities.map((amenity) => (\n <div key={amenity.id} className=\"flex items-center gap-2 rounded-lg bg-bg-sunken/50 p-3\">\n <CheckCircle className=\"h-4 w-4 text-status-success-text\" />\n <span className=\"text-sm text-text-primary\">{amenity.name}</span>\n </div>\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No amenities listed\"\n description=\"This property has no amenities recorded yet.\"\n className=\"border-0 bg-transparent p-6\"\n />\n )}\n </div>\n )}\n\n {activeTab === 'location' && (\n <div className=\"space-y-4\">\n {property.location.address && (\n <div>\n <div className=\"mb-1 text-xs text-text-secondary\">Full Address</div>\n <div className=\"text-sm font-medium text-text-primary\">{property.location.address}</div>\n </div>\n )}\n <div className=\"grid grid-cols-1 gap-4 text-sm sm:grid-cols-3\">\n <div>\n <div className=\"mb-1 text-xs text-text-secondary\">Neighborhood</div>\n <div className=\"font-medium text-text-primary\">{property.location.neighborhood || '—'}</div>\n </div>\n <div>\n <div className=\"mb-1 text-xs text-text-secondary\">City, State</div>\n <div className=\"font-medium text-text-primary\">\n {property.location.city}, {property.location.state}\n </div>\n </div>\n <div>\n <div className=\"mb-1 text-xs text-text-secondary\">PIN / ZIP</div>\n <div className=\"font-medium text-text-primary\">{property.location.pincode || '—'}</div>\n </div>\n </div>\n\n {property.location.landmarks.length > 0 && (\n <div className=\"border-t border-border-subtle pt-4\">\n <h4 className=\"mb-2 text-xs font-medium text-text-secondary\">Nearby Landmarks</h4>\n <div className=\"flex flex-wrap gap-2\">\n {property.location.landmarks.map((landmark) => (\n <span\n key={landmark}\n className=\"rounded-full bg-bg-sunken px-3 py-1 text-xs text-text-primary\"\n >\n {landmark}\n </span>\n ))}\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n\n {/* Right Sidebar */}\n <div className=\"space-y-6\">\n {/* Quick Actions */}\n <div id=\"property-actions\" className=\"scroll-mt-6 rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <h3 className=\"mb-4 text-base font-semibold text-text-primary\">Take Action</h3>\n <div className=\"space-y-2\">\n <Button type=\"button\" className=\"w-full justify-center\" onClick={() => setViewingOpen(true)}>\n <Calendar className=\"mr-2 h-4 w-4\" />\n Schedule Viewing\n </Button>\n {property.status === 'for_rent' && (\n <>\n <Button\n type=\"button\"\n className=\"w-full justify-center\"\n onClick={() => setRentalOpen(true)}\n disabled={!rentalUnitId}\n >\n <KeyRound className=\"mr-2 h-4 w-4\" />\n Apply to Rent\n </Button>\n {!rentalUnitId && (\n <p className=\"px-1 text-xs text-text-secondary\">\n No rental unit is listed for this property yet — applications open once a unit is added.\n </p>\n )}\n </>\n )}\n <Button\n type=\"button\"\n variant=\"outline\"\n className=\"w-full justify-center\"\n onClick={() => setOfferOpen(true)}\n >\n <DollarSign className=\"mr-2 h-4 w-4\" />\n Make Offer\n </Button>\n <ContactOwnerDialog\n entityId={property.id}\n entityType=\"housingvista-property\"\n title={property.title}\n pagePath={location.pathname}\n heading=\"Contact owner\"\n description={`Ask a question or start a conversation about ${property.title}.`}\n trigger={\n <Button type=\"button\" variant=\"outline\" className=\"w-full justify-center\">\n <MessageSquare className=\"mr-2 h-4 w-4\" />\n Contact Owner\n </Button>\n }\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n className=\"w-full justify-center\"\n onClick={toggleShortlist}\n disabled={savingShortlist || removingShortlist}\n >\n <Heart\n className={cn('mr-2 h-4 w-4', isShortlisted && 'fill-status-error-text text-status-error-text')}\n />\n {isShortlisted ? 'Saved to Shortlist' : 'Save to Shortlist'}\n </Button>\n </div>\n </div>\n\n {/* Investment analysis — modelled client-side from the listing price */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <div className=\"mb-4 flex items-center gap-2\">\n <Calculator className=\"h-5 w-5 text-text-secondary\" />\n <h3 className=\"text-base font-semibold text-text-primary\">Investment Analysis</h3>\n </div>\n <div className=\"space-y-3\">\n <div>\n <label className=\"mb-1.5 block text-xs font-medium text-text-secondary\">\n Expected monthly rent ({property.price.currency})\n </label>\n <input\n type=\"number\"\n min={0}\n value={rentInput}\n onChange={(e) => setRentInput(e.target.value)}\n placeholder={String(monthlyRent)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-xs font-medium text-text-secondary\">\n Monthly holding costs ({property.price.currency})\n </label>\n <input\n type=\"number\"\n min={0}\n value={costInput}\n onChange={(e) => setCostInput(e.target.value)}\n placeholder={String(monthlyHoldingCosts)}\n className={inputClass}\n />\n <p className=\"mt-1 text-[11px] text-text-secondary\">Maintenance, tax, insurance, and management.</p>\n </div>\n </div>\n <div className=\"mt-4 space-y-3 border-t border-border-subtle pt-4 text-sm\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">Gross yield</span>\n <span className=\"font-semibold text-text-primary\">{grossYield.toFixed(2)}%</span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">Annual holding costs</span>\n <span className=\"font-medium text-text-primary\">\n {formatCurrency(annualHoldingCosts, property.price.currency)}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">Net annual income</span>\n <span\n className={cn(\n 'font-medium',\n netAnnualIncome >= 0 ? 'text-status-success-text' : 'text-status-error-text'\n )}\n >\n {formatCurrency(netAnnualIncome, property.price.currency)}\n </span>\n </div>\n <div className=\"flex items-center justify-between border-t border-border-subtle pt-3\">\n <span className=\"flex items-center gap-1.5 font-medium text-text-primary\">\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n Projected ROI\n </span>\n <span className=\"text-lg font-bold text-text-primary\">{projectedRoi.toFixed(2)}%</span>\n </div>\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n className=\"mt-4 w-full justify-center\"\n onClick={handleTrackInvestment}\n disabled={creatingInvestment}\n >\n <TrendingUp className=\"mr-2 h-4 w-4\" />\n {creatingInvestment ? 'Adding…' : 'Track as Investment'}\n </Button>\n </div>\n\n {/* Property Stats (real, derived) */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <h3 className=\"mb-4 text-base font-semibold text-text-primary\">At a Glance</h3>\n <div className=\"space-y-3 text-sm\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">Status</span>\n <StatusBadge\n label={property.status.replace('_', ' ')}\n variant={statusVariantMap[property.status] ?? 'default'}\n />\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">Verification</span>\n <span className=\"font-medium capitalize text-text-primary\">{property.verificationStatus}</span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">Listed</span>\n <span className=\"font-medium text-text-primary\">{formatDate(property.createdAt.toISOString())}</span>\n </div>\n </div>\n </div>\n\n {/* Similar Properties */}\n {similarProperties.length > 0 && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <h3 className=\"mb-4 text-base font-semibold text-text-primary\">Similar Properties</h3>\n <div className=\"space-y-3\">\n {similarProperties.map((sp) => (\n <button\n key={sp.id}\n onClick={() => navigate(`../properties/${sp.id}`)}\n className=\"flex w-full items-start gap-3 rounded-lg border border-border-subtle p-3 text-left transition-colors hover:bg-bg-sunken/50\"\n >\n {sp.images[0] && (\n <img src={sp.images[0].url} alt={sp.title} className=\"h-14 w-14 rounded object-cover\" />\n )}\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-sm font-medium text-text-primary\">{sp.title}</div>\n <div className=\"text-sm font-semibold text-text-primary\">{formatCurrency(sp.price.amount)}</div>\n <div className=\"truncate text-xs text-text-secondary\">\n {[sp.location.neighborhood, sp.location.city].filter(Boolean).join(', ')}\n </div>\n </div>\n </button>\n ))}\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Q&A / reviews — real EntityComments-backed thread */}\n <ReviewsSection entityId={property.id} entityType=\"housingvista-property\" />\n\n {/* Apply to Rent dialog */}\n <ResponsiveDialog open={rentalOpen} onOpenChange={setRentalOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleSubmitRental}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Apply to rent</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Submit a rental application for {property.title}. The owner will review and respond.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Full name *</label>\n <input\n type=\"text\"\n required\n value={applicantName}\n onChange={(e) => setApplicantName(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Email *</label>\n <input\n type=\"email\"\n required\n value={applicantEmail}\n onChange={(e) => setApplicantEmail(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Phone</label>\n <input\n type=\"tel\"\n value={applicantPhone}\n onChange={(e) => setApplicantPhone(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Monthly income ({property.price.currency})\n </label>\n <input\n type=\"number\"\n min={0}\n value={monthlyIncome}\n onChange={(e) => setMonthlyIncome(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Preferred move-in date</label>\n <input\n type=\"date\"\n value={moveInDate}\n onChange={(e) => setMoveInDate(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setRentalOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={submittingRental || !rentalUnitId}>\n {submittingRental ? 'Submitting…' : 'Submit application'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Schedule Viewing dialog */}\n <ResponsiveDialog open={viewingOpen} onOpenChange={setViewingOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleBookViewing}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Schedule a viewing</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>Pick a date and time to see {property.title}.</ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Date & time *</label>\n <input\n type=\"datetime-local\"\n required\n value={viewingDate}\n onChange={(e) => setViewingDate(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Type</label>\n <select\n value={viewingType}\n onChange={(e) => setViewingType(e.target.value as 'in_person' | 'virtual')}\n className={inputClass}\n >\n <option value=\"in_person\">In person</option>\n <option value=\"virtual\">Virtual</option>\n </select>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <input\n type=\"text\"\n placeholder=\"Your name\"\n value={attendeeName}\n onChange={(e) => setAttendeeName(e.target.value)}\n className={inputClass}\n />\n <input\n type=\"email\"\n placeholder=\"Your email\"\n value={attendeeEmail}\n onChange={(e) => setAttendeeEmail(e.target.value)}\n className={inputClass}\n />\n </div>\n <textarea\n placeholder=\"Notes (optional)\"\n rows={2}\n value={viewingNotes}\n onChange={(e) => setViewingNotes(e.target.value)}\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setViewingOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={bookingViewing || !viewingDate}>\n {bookingViewing ? 'Booking…' : 'Request viewing'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Make Offer dialog */}\n <ResponsiveDialog open={offerOpen} onOpenChange={setOfferOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleSubmitOffer}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Make an offer</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>Listing price is {formatPrice(property.price)}.</ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Your offer ({property.price.currency}) *\n </label>\n <input\n type=\"number\"\n required\n min={1}\n value={offerAmount}\n onChange={(e) => setOfferAmount(e.target.value)}\n placeholder=\"Enter amount\"\n className={inputClass}\n />\n </div>\n <textarea\n placeholder=\"Message to the owner (optional)\"\n rows={3}\n value={offerNotes}\n onChange={(e) => setOfferNotes(e.target.value)}\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setOfferOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={submittingOffer}>\n {submittingOffer ? 'Submitting…' : 'Submit offer'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Edit dialog */}\n <ResponsiveDialog open={editOpen} onOpenChange={setEditOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleEdit}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit property</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>Update the core listing details.</ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Title *</label>\n <input\n type=\"text\"\n required\n value={editTitle}\n onChange={(e) => setEditTitle(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Description</label>\n <textarea\n rows={3}\n value={editDescription}\n onChange={(e) => setEditDescription(e.target.value)}\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Status</label>\n <select\n value={editStatus}\n onChange={(e) => setEditStatus(e.target.value as PropertyStatus)}\n className={inputClass}\n >\n {statusOptions.map((s) => (\n <option key={s.value} value={s.value}>\n {s.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Price ({property.price.currency})\n </label>\n <input\n type=\"number\"\n min={0}\n value={editPrice}\n onChange={(e) => setEditPrice(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setEditOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={updatingProperty}>\n {updatingProperty ? 'Saving…' : 'Save changes'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Delete confirm dialog */}\n <ResponsiveDialog open={deleteOpen} onOpenChange={setDeleteOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Delete this property?</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {property.title} will be permanently removed. This cannot be undone.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setDeleteOpen(false)}>\n Cancel\n </Button>\n <Button type=\"button\" variant=\"destructive\" onClick={handleDelete} disabled={deletingProperty}>\n {deletingProperty ? 'Deleting…' : 'Delete property'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PropertyDetailPage.js","names":[],"sources":["../../../../src/housingvista/pages/properties/PropertyDetailPage.tsx"],"sourcesContent":["/**\n * HousingVista Property Detail Page\n * Full property detail view backed by the single-item `housingvistaProperty(id)`\n * query, with the core buyer journey wired to real mutations: shortlist, book a\n * viewing, submit an offer, plus owner-side edit and delete.\n * @module housingvista\n */\n\nimport { useState, useMemo, type FormEvent } from 'react';\nimport { useParams, useNavigate, useLocation } from 'react-router-dom';\nimport {\n ArrowLeft,\n MapPin,\n Bed,\n Bath,\n Square,\n Car,\n Heart,\n Share2,\n CheckCircle,\n Calendar,\n DollarSign,\n ChevronLeft,\n ChevronRight,\n Building,\n Pencil,\n Trash2,\n Calculator,\n TrendingUp,\n MessageSquare,\n KeyRound,\n} from 'lucide-react';\nimport {\n EmphasisPanel,\n PagePurpose,\n NextSteps,\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Breadcrumbs,\n StatusBadge,\n EmptyState,\n LoadingSkeleton,\n ContactOwnerDialog,\n ReviewsSection,\n} from '../../components';\nimport type { BreadcrumbItem } from '../../components';\nimport {\n useProperty,\n useProperties,\n useShortlists,\n useSaveShortlist,\n useRemoveShortlist,\n useBookViewing,\n useSubmitOffer,\n useUpdateProperty,\n useDeleteProperty,\n useRentalUnits,\n useSubmitRentalApplication,\n useCreateInvestment,\n} from '../../hooks';\nimport { notify } from '../../lib/toast';\nimport { cn, formatCurrency, formatNumber, formatDate } from '../../utils/helpers';\nimport type { Property, PropertyStatus } from '../../types';\n\nconst statusVariantMap: Record<string, 'success' | 'warning' | 'error' | 'info' | 'default' | 'muted'> = {\n for_sale: 'success',\n for_rent: 'info',\n sold: 'muted',\n rented: 'muted',\n under_contract: 'warning',\n off_market: 'error',\n};\n\nconst statusOptions: { value: PropertyStatus; label: string }[] = [\n { value: 'for_sale', label: 'For Sale' },\n { value: 'for_rent', label: 'For Rent' },\n { value: 'under_contract', label: 'Under Contract' },\n { value: 'sold', label: 'Sold' },\n { value: 'rented', label: 'Rented' },\n { value: 'off_market', label: 'Off Market' },\n];\n\ntype TabId = 'details' | 'amenities' | 'location';\nconst tabs: { id: TabId; label: string }[] = [\n { id: 'details', label: 'Details' },\n { id: 'amenities', label: 'Amenities' },\n { id: 'location', label: 'Location' },\n];\n\nconst inputClass =\n 'h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\nexport const PropertyDetailPage = () => {\n const { id } = useParams<{ id: string }>();\n const navigate = useNavigate();\n const location = useLocation();\n\n const { data: property, loading } = useProperty(id);\n const { data: allProperties } = useProperties({ pagination: { limit: 8, offset: 0 } });\n const { data: shortlists } = useShortlists();\n const { data: rentalUnits } = useRentalUnits({\n filter: id ? { propertyId: id } : undefined,\n pagination: { limit: 1, offset: 0 },\n });\n const { saveShortlist, loading: savingShortlist } = useSaveShortlist();\n const { removeShortlist, loading: removingShortlist } = useRemoveShortlist();\n const { bookViewing, loading: bookingViewing } = useBookViewing();\n const { submitOffer, loading: submittingOffer } = useSubmitOffer();\n const { updateProperty, loading: updatingProperty } = useUpdateProperty();\n const { deleteProperty, loading: deletingProperty } = useDeleteProperty();\n const { submitRentalApplication, loading: submittingRental } = useSubmitRentalApplication();\n const { createInvestment, loading: creatingInvestment } = useCreateInvestment();\n\n const similarProperties = useMemo(() => allProperties.filter((p) => p.id !== id).slice(0, 3), [allProperties, id]);\n const isShortlisted = useMemo(() => shortlists.some((s) => s.propertyId === id), [shortlists, id]);\n\n const [currentImageIndex, setCurrentImageIndex] = useState(0);\n const [activeTab, setActiveTab] = useState<TabId>('details');\n const [viewingOpen, setViewingOpen] = useState(false);\n const [offerOpen, setOfferOpen] = useState(false);\n const [editOpen, setEditOpen] = useState(false);\n const [deleteOpen, setDeleteOpen] = useState(false);\n const [rentalOpen, setRentalOpen] = useState(false);\n\n // Rental application form state\n const [applicantName, setApplicantName] = useState('');\n const [applicantEmail, setApplicantEmail] = useState('');\n const [applicantPhone, setApplicantPhone] = useState('');\n const [monthlyIncome, setMonthlyIncome] = useState('');\n const [moveInDate, setMoveInDate] = useState('');\n\n // Investment-analysis calculator inputs (blank = use a price-derived default)\n const [rentInput, setRentInput] = useState('');\n const [costInput, setCostInput] = useState('');\n\n // Viewing form state\n const [viewingDate, setViewingDate] = useState('');\n const [viewingType, setViewingType] = useState<'in_person' | 'virtual'>('in_person');\n const [attendeeName, setAttendeeName] = useState('');\n const [attendeeEmail, setAttendeeEmail] = useState('');\n const [viewingNotes, setViewingNotes] = useState('');\n\n // Offer form state\n const [offerAmount, setOfferAmount] = useState('');\n const [offerNotes, setOfferNotes] = useState('');\n\n // Edit form state (seeded on open)\n const [editTitle, setEditTitle] = useState('');\n const [editDescription, setEditDescription] = useState('');\n const [editStatus, setEditStatus] = useState<PropertyStatus>('for_sale');\n const [editPrice, setEditPrice] = useState('');\n\n if (loading) {\n return (\n <div className=\"space-y-6 p-6\">\n <LoadingSkeleton className=\"h-80\" />\n <LoadingSkeleton variant=\"detail\" />\n </div>\n );\n }\n\n if (!property) {\n return (\n <div className=\"p-6\">\n <EmptyState\n illustration=\"empty-search\"\n icon={<Building className=\"h-12 w-12\" />}\n title=\"Property Not Found\"\n description=\"The property you're looking for doesn't exist or has been removed.\"\n action={\n <button\n onClick={() => navigate('../properties')}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Properties\n </button>\n }\n />\n </div>\n );\n }\n\n const breadcrumbs: BreadcrumbItem[] = [{ label: 'Properties', href: '../' }, { label: property.title }];\n\n const formatPrice = (price: Property['price']) => {\n const amount = formatCurrency(price.amount, price.currency);\n if (price.type === 'rent' && price.period) return `${amount}/${price.period.replace('ly', '')}`;\n return amount;\n };\n\n const nextImage = () => setCurrentImageIndex((prev) => (prev === property.images.length - 1 ? 0 : prev + 1));\n const prevImage = () => setCurrentImageIndex((prev) => (prev === 0 ? property.images.length - 1 : prev - 1));\n\n const handleShare = () => {\n if (navigator.share) {\n navigator.share({ title: property.title, text: property.description, url: window.location.href });\n } else {\n navigator.clipboard.writeText(window.location.href);\n notify.info('Link copied', 'The property link is on your clipboard.');\n }\n };\n\n const toggleShortlist = async () => {\n try {\n if (isShortlisted) {\n await removeShortlist(property.id);\n notify.success('Removed from shortlist');\n } else {\n await saveShortlist(property.id);\n notify.success('Saved to shortlist');\n }\n } catch (err) {\n notify.error('Could not update shortlist', (err as Error).message);\n }\n };\n\n const handleBookViewing = async (e: FormEvent) => {\n e.preventDefault();\n if (!viewingDate) return;\n try {\n await bookViewing({\n propertyId: property.id,\n scheduledAt: new Date(viewingDate).toISOString(),\n type: viewingType,\n attendeeName: attendeeName.trim() || undefined,\n attendeeEmail: attendeeEmail.trim() || undefined,\n notes: viewingNotes.trim() || undefined,\n });\n notify.success('Viewing requested', 'The owner will confirm your slot shortly.');\n setViewingOpen(false);\n setViewingDate('');\n setAttendeeName('');\n setAttendeeEmail('');\n setViewingNotes('');\n } catch (err) {\n notify.error('Could not book viewing', (err as Error).message);\n }\n };\n\n const handleSubmitOffer = async (e: FormEvent) => {\n e.preventDefault();\n const amount = Number(offerAmount);\n if (!amount || amount <= 0) {\n notify.warning('Enter a valid offer amount');\n return;\n }\n try {\n await submitOffer({\n propertyId: property.id,\n amount,\n currency: property.price.currency,\n notes: offerNotes.trim() || undefined,\n });\n notify.success('Offer submitted', `Your offer of ${formatCurrency(amount, property.price.currency)} was sent.`);\n setOfferOpen(false);\n setOfferAmount('');\n setOfferNotes('');\n } catch (err) {\n notify.error('Could not submit offer', (err as Error).message);\n }\n };\n\n const openEdit = () => {\n setEditTitle(property.title);\n setEditDescription(property.description);\n setEditStatus(property.status);\n setEditPrice(String(property.price.amount));\n setEditOpen(true);\n };\n\n const handleEdit = async (e: FormEvent) => {\n e.preventDefault();\n try {\n await updateProperty(property.id, {\n title: editTitle.trim(),\n description: editDescription.trim(),\n status: editStatus,\n price: Number(editPrice) || property.price.amount,\n });\n notify.success('Property updated');\n setEditOpen(false);\n } catch (err) {\n notify.error('Could not update property', (err as Error).message);\n }\n };\n\n const handleDelete = async () => {\n try {\n await deleteProperty(property.id);\n notify.success('Property deleted');\n navigate('../properties');\n } catch (err) {\n notify.error('Could not delete property', (err as Error).message);\n }\n };\n\n // First available rental unit for this property (needed to submit an application).\n const rentalUnitId = rentalUnits[0]?.id;\n\n const handleSubmitRental = async (e: FormEvent) => {\n e.preventDefault();\n if (!rentalUnitId) {\n notify.warning('No rental unit is available for this property yet');\n return;\n }\n if (!applicantName.trim() || !applicantEmail.trim()) {\n notify.warning('Enter your name and email to apply');\n return;\n }\n try {\n await submitRentalApplication({\n rentalUnitId,\n applicantName: applicantName.trim(),\n applicantEmail: applicantEmail.trim(),\n applicantPhone: applicantPhone.trim() || undefined,\n monthlyIncome: monthlyIncome ? Number(monthlyIncome) : undefined,\n moveInDate: moveInDate ? new Date(moveInDate).toISOString() : undefined,\n });\n notify.success('Application submitted', 'The owner will review your rental application shortly.');\n setRentalOpen(false);\n setApplicantName('');\n setApplicantEmail('');\n setApplicantPhone('');\n setMonthlyIncome('');\n setMoveInDate('');\n } catch (err) {\n notify.error('Could not submit application', (err as Error).message);\n }\n };\n\n const handleTrackInvestment = async () => {\n try {\n await createInvestment({\n propertyId: property.id,\n title: `${property.title} — investment`,\n status: 'active',\n investmentType: 'full_ownership',\n riskLevel: 'medium',\n totalValue: property.price.amount,\n startDate: new Date().toISOString(),\n });\n notify.success('Tracking investment', `${property.title} was added to your portfolio.`);\n } catch (err) {\n notify.error('Could not track investment', (err as Error).message);\n }\n };\n\n // Investment analysis — modelled client-side from the listing price + user inputs.\n const price = property.price.amount;\n const monthlyRent = Number(rentInput) || Math.round(price * 0.005); // ~0.5%/mo default\n const monthlyHoldingCosts = Number(costInput) || Math.round(price * 0.0015); // ~0.15%/mo default\n const grossAnnualRent = monthlyRent * 12;\n const grossYield = price > 0 ? (grossAnnualRent / price) * 100 : 0;\n const annualHoldingCosts = monthlyHoldingCosts * 12;\n const netAnnualIncome = grossAnnualRent - annualHoldingCosts;\n const projectedRoi = price > 0 ? (netAnnualIncome / price) * 100 : 0;\n\n return (\n <div className=\"space-y-6 p-6\">\n <Breadcrumbs items={breadcrumbs} />\n\n {/* Header */}\n <div className=\"flex flex-wrap items-center gap-3\">\n <button\n onClick={() => navigate('../properties')}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-3 py-1.5 text-sm text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back\n </button>\n <StatusBadge\n label={property.status.replace('_', ' ')}\n variant={statusVariantMap[property.status] ?? 'default'}\n dot\n />\n {property.verificationStatus === 'verified' && <StatusBadge label=\"Verified\" variant=\"success\" dot />}\n {property.price.negotiable && <StatusBadge label=\"Negotiable\" variant=\"info\" />}\n <div className=\"ml-auto flex items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={openEdit}>\n <Pencil className=\"mr-1.5 h-4 w-4\" />\n Edit\n </Button>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => setDeleteOpen(true)}>\n <Trash2 className=\"mr-1.5 h-4 w-4\" />\n Delete\n </Button>\n </div>\n </div>\n\n <PagePurpose className=\"max-w-3xl\">\n Everything you need to evaluate this property in one place — photos, specifications, amenities, and location.\n Use it to shortlist the property, schedule a viewing, or make an offer.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"housingvista-property-detail\"\n steps={[\n {\n id: 'schedule',\n label: 'Schedule a viewing',\n description: 'Book a time to see the property in person or virtually.',\n onClick: () => setViewingOpen(true),\n },\n {\n id: 'offer',\n label: 'Make an offer',\n description: 'Submit a price and let the owner respond.',\n onClick: () => setOfferOpen(true),\n },\n {\n id: 'location',\n label: 'Explore the location',\n description: 'See the neighborhood, nearby landmarks, and map.',\n onClick: () => setActiveTab('location'),\n },\n ]}\n />\n\n {/* Image Gallery */}\n {property.images.length > 0 && (\n <div className=\"relative overflow-hidden rounded-xl border border-border-subtle\">\n <img\n src={property.images[currentImageIndex]?.url}\n alt={property.images[currentImageIndex]?.title ?? property.title}\n className=\"h-80 w-full object-cover sm:h-96\"\n />\n {property.images.length > 1 && (\n <>\n <button\n onClick={prevImage}\n className=\"absolute left-4 top-1/2 -translate-y-1/2 rounded-full bg-bg-overlay/70 p-2 text-text-inverse transition-colors hover:bg-bg-overlay\"\n >\n <ChevronLeft className=\"h-5 w-5\" />\n </button>\n <button\n onClick={nextImage}\n className=\"absolute right-4 top-1/2 -translate-y-1/2 rounded-full bg-bg-overlay/70 p-2 text-text-inverse transition-colors hover:bg-bg-overlay\"\n >\n <ChevronRight className=\"h-5 w-5\" />\n </button>\n <div className=\"absolute bottom-4 left-1/2 flex -translate-x-1/2 gap-2\">\n {property.images.map((img, index) => (\n <button\n key={img.id}\n className={cn(\n 'h-2 w-2 rounded-full',\n index === currentImageIndex ? 'bg-text-inverse' : 'bg-text-inverse/50'\n )}\n onClick={() => setCurrentImageIndex(index)}\n />\n ))}\n </div>\n </>\n )}\n <div className=\"absolute right-4 top-4 flex gap-2\">\n <button\n onClick={toggleShortlist}\n disabled={savingShortlist || removingShortlist}\n aria-label={isShortlisted ? 'Remove from shortlist' : 'Save to shortlist'}\n className=\"rounded-full bg-bg-surface/80 p-2 backdrop-blur-sm transition-colors hover:bg-bg-surface disabled:opacity-60\"\n >\n <Heart\n className={cn(\n 'h-5 w-5',\n isShortlisted ? 'fill-status-error-text text-status-error-text' : 'text-text-secondary'\n )}\n />\n </button>\n <button\n onClick={handleShare}\n className=\"rounded-full bg-bg-surface/80 p-2 backdrop-blur-sm transition-colors hover:bg-bg-surface\"\n >\n <Share2 className=\"h-5 w-5 text-text-secondary\" />\n </button>\n </div>\n <div className=\"absolute bottom-4 right-4\">\n <span className=\"rounded-md bg-bg-overlay/70 px-2 py-1 text-xs font-medium text-text-inverse\">\n {currentImageIndex + 1} / {property.images.length}\n </span>\n </div>\n </div>\n )}\n\n {/* Main Content Grid */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Left: Main content */}\n <div className=\"space-y-6 lg:col-span-2\">\n <EmphasisPanel>\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">{property.title}</h1>\n <div className=\"mt-2 flex items-center text-text-secondary\">\n <MapPin className=\"mr-1.5 h-4 w-4 shrink-0\" />\n <span className=\"text-sm\">\n {[property.location.address, property.location.neighborhood, property.location.city]\n .filter(Boolean)\n .join(', ')}\n </span>\n </div>\n </div>\n <div className=\"text-right\">\n <div className=\"text-2xl font-bold text-text-primary\">{formatPrice(property.price)}</div>\n {property.price.pricePerSqft && (\n <div className=\"text-sm text-text-secondary\">{formatCurrency(property.price.pricePerSqft)}/sqft</div>\n )}\n </div>\n </div>\n\n {/* Key Specs */}\n <div className=\"mt-6 grid grid-cols-2 gap-4 border-t border-border-subtle pt-6 sm:grid-cols-4\">\n {property.specifications.bedrooms != null && (\n <div className=\"flex items-center gap-2 text-sm text-text-primary\">\n <Bed className=\"h-5 w-5 text-text-secondary\" />\n <span>\n {property.specifications.bedrooms} Bed{property.specifications.bedrooms > 1 ? 's' : ''}\n </span>\n </div>\n )}\n {property.specifications.bathrooms != null && (\n <div className=\"flex items-center gap-2 text-sm text-text-primary\">\n <Bath className=\"h-5 w-5 text-text-secondary\" />\n <span>\n {property.specifications.bathrooms} Bath{property.specifications.bathrooms > 1 ? 's' : ''}\n </span>\n </div>\n )}\n <div className=\"flex items-center gap-2 text-sm text-text-primary\">\n <Square className=\"h-5 w-5 text-text-secondary\" />\n <span>{formatNumber(property.specifications.totalArea)} sqft</span>\n </div>\n {property.specifications.parking != null && property.specifications.parking > 0 && (\n <div className=\"flex items-center gap-2 text-sm text-text-primary\">\n <Car className=\"h-5 w-5 text-text-secondary\" />\n <span>{property.specifications.parking} Parking</span>\n </div>\n )}\n </div>\n\n {property.description && (\n <div className=\"mt-6 border-t border-border-subtle pt-6\">\n <h3 className=\"mb-3 text-base font-semibold text-text-primary\">Description</h3>\n <p className=\"text-sm leading-relaxed text-text-secondary\">{property.description}</p>\n </div>\n )}\n </EmphasisPanel>\n\n {/* Tabbed Content */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface\">\n <div className=\"border-b border-border-subtle\">\n <nav className=\"flex space-x-6 overflow-x-auto px-6\">\n {tabs.map((tab) => (\n <button\n key={tab.id}\n onClick={() => setActiveTab(tab.id)}\n className={cn(\n 'whitespace-nowrap border-b-2 py-3 text-sm font-medium transition-colors',\n activeTab === tab.id\n ? 'border-action-primary-border text-text-primary'\n : 'border-transparent text-text-secondary hover:text-text-primary'\n )}\n >\n {tab.label}\n </button>\n ))}\n </nav>\n </div>\n\n <div className=\"p-6\">\n {activeTab === 'details' && (\n <div className=\"grid grid-cols-1 gap-6 sm:grid-cols-2\">\n <div>\n <h4 className=\"mb-3 text-sm font-semibold text-text-primary\">Property Information</h4>\n <dl className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Property Type</dt>\n <dd className=\"font-medium capitalize text-text-primary\">{property.type}</dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Category</dt>\n <dd className=\"font-medium capitalize text-text-primary\">{property.category}</dd>\n </div>\n {property.specifications.ageOfProperty != null && (\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Age of Property</dt>\n <dd className=\"font-medium text-text-primary\">\n {property.specifications.ageOfProperty} years\n </dd>\n </div>\n )}\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Furnishing</dt>\n <dd className=\"font-medium capitalize text-text-primary\">\n {property.specifications.furnishingStatus?.replace('_', ' ')}\n </dd>\n </div>\n {property.specifications.facing && (\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Facing</dt>\n <dd className=\"font-medium text-text-primary\">{property.specifications.facing}</dd>\n </div>\n )}\n {property.specifications.floors != null && (\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Floor</dt>\n <dd className=\"font-medium text-text-primary\">\n {property.specifications.floors} of {property.specifications.totalFloors}\n </dd>\n </div>\n )}\n </dl>\n </div>\n <div>\n <h4 className=\"mb-3 text-sm font-semibold text-text-primary\">Area Details</h4>\n <dl className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Total Area</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatNumber(property.specifications.totalArea)} sqft\n </dd>\n </div>\n {property.specifications.carpetArea && (\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Carpet Area</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatNumber(property.specifications.carpetArea)} sqft\n </dd>\n </div>\n )}\n {property.specifications.builtUpArea && (\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Built-up Area</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatNumber(property.specifications.builtUpArea)} sqft\n </dd>\n </div>\n )}\n {property.price.pricePerSqft && (\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Price per sqft</dt>\n <dd className=\"font-medium text-text-primary\">\n {formatCurrency(property.price.pricePerSqft)}\n </dd>\n </div>\n )}\n </dl>\n </div>\n </div>\n )}\n\n {activeTab === 'amenities' && (\n <div>\n {property.amenities.length > 0 ? (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n {property.amenities.map((amenity) => (\n <div key={amenity.id} className=\"flex items-center gap-2 rounded-lg bg-bg-sunken/50 p-3\">\n <CheckCircle className=\"h-4 w-4 text-status-success-text\" />\n <span className=\"text-sm text-text-primary\">{amenity.name}</span>\n </div>\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No amenities listed\"\n description=\"This property has no amenities recorded yet.\"\n className=\"border-0 bg-transparent p-6\"\n />\n )}\n </div>\n )}\n\n {activeTab === 'location' && (\n <div className=\"space-y-4\">\n {property.location.address && (\n <div>\n <div className=\"mb-1 text-xs text-text-secondary\">Full Address</div>\n <div className=\"text-sm font-medium text-text-primary\">{property.location.address}</div>\n </div>\n )}\n <div className=\"grid grid-cols-1 gap-4 text-sm sm:grid-cols-3\">\n <div>\n <div className=\"mb-1 text-xs text-text-secondary\">Neighborhood</div>\n <div className=\"font-medium text-text-primary\">{property.location.neighborhood || '—'}</div>\n </div>\n <div>\n <div className=\"mb-1 text-xs text-text-secondary\">City, State</div>\n <div className=\"font-medium text-text-primary\">\n {property.location.city}, {property.location.state}\n </div>\n </div>\n <div>\n <div className=\"mb-1 text-xs text-text-secondary\">PIN / ZIP</div>\n <div className=\"font-medium text-text-primary\">{property.location.pincode || '—'}</div>\n </div>\n </div>\n\n {property.location.landmarks.length > 0 && (\n <div className=\"border-t border-border-subtle pt-4\">\n <h4 className=\"mb-2 text-xs font-medium text-text-secondary\">Nearby Landmarks</h4>\n <div className=\"flex flex-wrap gap-2\">\n {property.location.landmarks.map((landmark) => (\n <span\n key={landmark}\n className=\"rounded-full bg-bg-sunken px-3 py-1 text-xs text-text-primary\"\n >\n {landmark}\n </span>\n ))}\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n\n {/* Right Sidebar */}\n <div className=\"space-y-6\">\n {/* Quick Actions */}\n <div id=\"property-actions\" className=\"scroll-mt-6 rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <h3 className=\"mb-4 text-base font-semibold text-text-primary\">Take Action</h3>\n <div className=\"space-y-2\">\n <Button type=\"button\" className=\"w-full justify-center\" onClick={() => setViewingOpen(true)}>\n <Calendar className=\"mr-2 h-4 w-4\" />\n Schedule Viewing\n </Button>\n {property.status === 'for_rent' && (\n <>\n <Button\n type=\"button\"\n className=\"w-full justify-center\"\n onClick={() => setRentalOpen(true)}\n disabled={!rentalUnitId}\n >\n <KeyRound className=\"mr-2 h-4 w-4\" />\n Apply to Rent\n </Button>\n {!rentalUnitId && (\n <p className=\"px-1 text-xs text-text-secondary\">\n No rental unit is listed for this property yet — applications open once a unit is added.\n </p>\n )}\n </>\n )}\n <Button\n type=\"button\"\n variant=\"outline\"\n className=\"w-full justify-center\"\n onClick={() => setOfferOpen(true)}\n >\n <DollarSign className=\"mr-2 h-4 w-4\" />\n Make Offer\n </Button>\n <ContactOwnerDialog\n entityId={property.id}\n entityType=\"housingvista-property\"\n title={property.title}\n pagePath={location.pathname}\n heading=\"Contact owner\"\n description={`Ask a question or start a conversation about ${property.title}.`}\n trigger={\n <Button type=\"button\" variant=\"outline\" className=\"w-full justify-center\">\n <MessageSquare className=\"mr-2 h-4 w-4\" />\n Contact Owner\n </Button>\n }\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n className=\"w-full justify-center\"\n onClick={toggleShortlist}\n disabled={savingShortlist || removingShortlist}\n >\n <Heart\n className={cn('mr-2 h-4 w-4', isShortlisted && 'fill-status-error-text text-status-error-text')}\n />\n {isShortlisted ? 'Saved to Shortlist' : 'Save to Shortlist'}\n </Button>\n </div>\n </div>\n\n {/* Investment analysis — modelled client-side from the listing price */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <div className=\"mb-4 flex items-center gap-2\">\n <Calculator className=\"h-5 w-5 text-text-secondary\" />\n <h3 className=\"text-base font-semibold text-text-primary\">Investment Analysis</h3>\n </div>\n <div className=\"space-y-3\">\n <div>\n <label className=\"mb-1.5 block text-xs font-medium text-text-secondary\">\n Expected monthly rent ({property.price.currency})\n </label>\n <input\n type=\"number\"\n min={0}\n value={rentInput}\n onChange={(e) => setRentInput(e.target.value)}\n placeholder={String(monthlyRent)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-xs font-medium text-text-secondary\">\n Monthly holding costs ({property.price.currency})\n </label>\n <input\n type=\"number\"\n min={0}\n value={costInput}\n onChange={(e) => setCostInput(e.target.value)}\n placeholder={String(monthlyHoldingCosts)}\n className={inputClass}\n />\n <p className=\"mt-1 text-[11px] text-text-secondary\">Maintenance, tax, insurance, and management.</p>\n </div>\n </div>\n <div className=\"mt-4 space-y-3 border-t border-border-subtle pt-4 text-sm\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">Gross yield</span>\n <span className=\"font-semibold text-text-primary\">{grossYield.toFixed(2)}%</span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">Annual holding costs</span>\n <span className=\"font-medium text-text-primary\">\n {formatCurrency(annualHoldingCosts, property.price.currency)}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">Net annual income</span>\n <span\n className={cn(\n 'font-medium',\n netAnnualIncome >= 0 ? 'text-status-success-text' : 'text-status-error-text'\n )}\n >\n {formatCurrency(netAnnualIncome, property.price.currency)}\n </span>\n </div>\n <div className=\"flex items-center justify-between border-t border-border-subtle pt-3\">\n <span className=\"flex items-center gap-1.5 font-medium text-text-primary\">\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n Projected ROI\n </span>\n <span className=\"text-lg font-bold text-text-primary\">{projectedRoi.toFixed(2)}%</span>\n </div>\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n className=\"mt-4 w-full justify-center\"\n onClick={handleTrackInvestment}\n disabled={creatingInvestment}\n >\n <TrendingUp className=\"mr-2 h-4 w-4\" />\n {creatingInvestment ? 'Adding…' : 'Track as Investment'}\n </Button>\n </div>\n\n {/* Property Stats (real, derived) */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <h3 className=\"mb-4 text-base font-semibold text-text-primary\">At a Glance</h3>\n <div className=\"space-y-3 text-sm\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">Status</span>\n <StatusBadge\n label={property.status.replace('_', ' ')}\n variant={statusVariantMap[property.status] ?? 'default'}\n />\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">Verification</span>\n <span className=\"font-medium capitalize text-text-primary\">{property.verificationStatus}</span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-text-secondary\">Listed</span>\n <span className=\"font-medium text-text-primary\">{formatDate(String(property.createdAt))}</span>\n </div>\n </div>\n </div>\n\n {/* Similar Properties */}\n {similarProperties.length > 0 && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5\">\n <h3 className=\"mb-4 text-base font-semibold text-text-primary\">Similar Properties</h3>\n <div className=\"space-y-3\">\n {similarProperties.map((sp) => (\n <button\n key={sp.id}\n onClick={() => navigate(`../properties/${sp.id}`)}\n className=\"flex w-full items-start gap-3 rounded-lg border border-border-subtle p-3 text-left transition-colors hover:bg-bg-sunken/50\"\n >\n {sp.images[0] && (\n <img src={sp.images[0].url} alt={sp.title} className=\"h-14 w-14 rounded object-cover\" />\n )}\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-sm font-medium text-text-primary\">{sp.title}</div>\n <div className=\"text-sm font-semibold text-text-primary\">{formatCurrency(sp.price.amount)}</div>\n <div className=\"truncate text-xs text-text-secondary\">\n {[sp.location.neighborhood, sp.location.city].filter(Boolean).join(', ')}\n </div>\n </div>\n </button>\n ))}\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Q&A / reviews — real EntityComments-backed thread */}\n <ReviewsSection entityId={property.id} entityType=\"housingvista-property\" />\n\n {/* Apply to Rent dialog */}\n <ResponsiveDialog open={rentalOpen} onOpenChange={setRentalOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleSubmitRental}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Apply to rent</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Submit a rental application for {property.title}. The owner will review and respond.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Full name *</label>\n <input\n type=\"text\"\n required\n value={applicantName}\n onChange={(e) => setApplicantName(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Email *</label>\n <input\n type=\"email\"\n required\n value={applicantEmail}\n onChange={(e) => setApplicantEmail(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Phone</label>\n <input\n type=\"tel\"\n value={applicantPhone}\n onChange={(e) => setApplicantPhone(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Monthly income ({property.price.currency})\n </label>\n <input\n type=\"number\"\n min={0}\n value={monthlyIncome}\n onChange={(e) => setMonthlyIncome(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Preferred move-in date</label>\n <input\n type=\"date\"\n value={moveInDate}\n onChange={(e) => setMoveInDate(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setRentalOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={submittingRental || !rentalUnitId}>\n {submittingRental ? 'Submitting…' : 'Submit application'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Schedule Viewing dialog */}\n <ResponsiveDialog open={viewingOpen} onOpenChange={setViewingOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleBookViewing}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Schedule a viewing</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>Pick a date and time to see {property.title}.</ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Date & time *</label>\n <input\n type=\"datetime-local\"\n required\n value={viewingDate}\n onChange={(e) => setViewingDate(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Type</label>\n <select\n value={viewingType}\n onChange={(e) => setViewingType(e.target.value as 'in_person' | 'virtual')}\n className={inputClass}\n >\n <option value=\"in_person\">In person</option>\n <option value=\"virtual\">Virtual</option>\n </select>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <input\n type=\"text\"\n placeholder=\"Your name\"\n value={attendeeName}\n onChange={(e) => setAttendeeName(e.target.value)}\n className={inputClass}\n />\n <input\n type=\"email\"\n placeholder=\"Your email\"\n value={attendeeEmail}\n onChange={(e) => setAttendeeEmail(e.target.value)}\n className={inputClass}\n />\n </div>\n <textarea\n placeholder=\"Notes (optional)\"\n rows={2}\n value={viewingNotes}\n onChange={(e) => setViewingNotes(e.target.value)}\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setViewingOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={bookingViewing || !viewingDate}>\n {bookingViewing ? 'Booking…' : 'Request viewing'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Make Offer dialog */}\n <ResponsiveDialog open={offerOpen} onOpenChange={setOfferOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleSubmitOffer}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Make an offer</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>Listing price is {formatPrice(property.price)}.</ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Your offer ({property.price.currency}) *\n </label>\n <input\n type=\"number\"\n required\n min={1}\n value={offerAmount}\n onChange={(e) => setOfferAmount(e.target.value)}\n placeholder=\"Enter amount\"\n className={inputClass}\n />\n </div>\n <textarea\n placeholder=\"Message to the owner (optional)\"\n rows={3}\n value={offerNotes}\n onChange={(e) => setOfferNotes(e.target.value)}\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setOfferOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={submittingOffer}>\n {submittingOffer ? 'Submitting…' : 'Submit offer'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Edit dialog */}\n <ResponsiveDialog open={editOpen} onOpenChange={setEditOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <form onSubmit={handleEdit}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit property</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>Update the core listing details.</ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Title *</label>\n <input\n type=\"text\"\n required\n value={editTitle}\n onChange={(e) => setEditTitle(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Description</label>\n <textarea\n rows={3}\n value={editDescription}\n onChange={(e) => setEditDescription(e.target.value)}\n className=\"w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">Status</label>\n <select\n value={editStatus}\n onChange={(e) => setEditStatus(e.target.value as PropertyStatus)}\n className={inputClass}\n >\n {statusOptions.map((s) => (\n <option key={s.value} value={s.value}>\n {s.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n Price ({property.price.currency})\n </label>\n <input\n type=\"number\"\n min={0}\n value={editPrice}\n onChange={(e) => setEditPrice(e.target.value)}\n className={inputClass}\n />\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setEditOpen(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" disabled={updatingProperty}>\n {updatingProperty ? 'Saving…' : 'Save changes'}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n\n {/* Delete confirm dialog */}\n <ResponsiveDialog open={deleteOpen} onOpenChange={setDeleteOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Delete this property?</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {property.title} will be permanently removed. This cannot be undone.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setDeleteOpen(false)}>\n Cancel\n </Button>\n <Button type=\"button\" variant=\"destructive\" onClick={handleDelete} disabled={deletingProperty}>\n {deletingProperty ? 'Deleting…' : 'Delete property'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -179,7 +179,7 @@ var b = "h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 t
|
|
|
179
179
|
sortable: !0,
|
|
180
180
|
render: (e) => /* @__PURE__ */ _("span", {
|
|
181
181
|
className: "text-sm text-text-secondary",
|
|
182
|
-
children: t(e.createdAt
|
|
182
|
+
children: t(String(e.createdAt))
|
|
183
183
|
})
|
|
184
184
|
},
|
|
185
185
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransactionsPage.js","names":[],"sources":["../../../../src/housingvista/pages/transactions/TransactionsPage.tsx"],"sourcesContent":["/**\n * HousingVista Transactions Listing Page\n * Migrated from legacy housingvista-frontend transactions.tsx\n * @module housingvista\n */\n\nimport { useState, useMemo, useEffect, type FormEvent } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Plus, FileText, DollarSign, TrendingUp, CheckCircle, Eye } from 'lucide-react';\nimport {\n Button,\n EmphasisPanel,\n NextSteps,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout, FilterBar, DataTable, StatusBadge, EmptyState, StatCard } from '../../components';\nimport type { Column } from '../../components';\nimport { useTransactions, useCreateTransaction, useProperties, type TransactionFilterInput } from '../../hooks';\nimport { useServerPagination } from '../../hooks/useServerPagination';\nimport { notify } from '../../lib/toast';\nimport { formatCurrency, formatDate } from '../../utils/helpers';\nimport type { Transaction, TransactionStatus, TransactionType } from '../../types';\n\nconst inputClass =\n 'h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\nconst TRANSACTION_TYPES: TransactionType[] = ['sale', 'purchase', 'rental', 'lease'];\nconst CREATE_STATUSES: TransactionStatus[] = ['draft', 'negotiation', 'under_contract', 'completed'];\n\n// ---- Status badge variant map ----\nconst statusVariantMap: Record<string, 'success' | 'warning' | 'error' | 'info' | 'default' | 'muted'> = {\n draft: 'muted',\n negotiation: 'info',\n under_contract: 'warning',\n pending_approval: 'warning',\n approved: 'info',\n in_progress: 'info',\n completed: 'success',\n cancelled: 'error',\n disputed: 'error',\n};\n\nexport const TransactionsPage = () => {\n const navigate = useNavigate();\n const { t } = useI18n();\n const pagination = useServerPagination(10);\n\n // ---- Search + status filter (server-side) ----\n const [searchQuery, setSearchQuery] = useState('');\n const [debouncedSearch, setDebouncedSearch] = useState('');\n const [selectedStatus, setSelectedStatus] = useState<TransactionStatus | 'all'>('all');\n\n // Debounce the search box into the server filter (300ms).\n useEffect(() => {\n const handle = setTimeout(() => setDebouncedSearch(searchQuery), 300);\n return () => clearTimeout(handle);\n }, [searchQuery]);\n\n const filter: TransactionFilterInput = {\n search: debouncedSearch.trim() || undefined,\n status: selectedStatus === 'all' ? undefined : selectedStatus,\n };\n\n const { data: transactions, loading } = useTransactions({ filter, pagination: pagination.pagination });\n const { createTransaction, loading: creating } = useCreateTransaction();\n const { data: properties } = useProperties({ pagination: { limit: 50, offset: 0 } });\n\n // Reset to the first page whenever the search text or status tab changes.\n const handleSearchChange = (value: string) => {\n setSearchQuery(value);\n pagination.setPage(1);\n };\n const selectStatus = (value: TransactionStatus | 'all') => {\n setSelectedStatus(value);\n pagination.setPage(1);\n };\n\n const tr = (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n\n // ---- Create-transaction dialog state ----\n const [createOpen, setCreateOpen] = useState(false);\n const [propertyId, setPropertyId] = useState('');\n const [newTitle, setNewTitle] = useState('');\n const [newType, setNewType] = useState<TransactionType>('sale');\n const [newStatus, setNewStatus] = useState<TransactionStatus>('draft');\n const [amount, setAmount] = useState('');\n const [currency, setCurrency] = useState('USD');\n\n const openCreate = () => {\n setPropertyId('');\n setNewTitle('');\n setNewType('sale');\n setNewStatus('draft');\n setAmount('');\n setCurrency('USD');\n setCreateOpen(true);\n };\n\n const handlePropertyChange = (id: string) => {\n setPropertyId(id);\n const picked = properties.find((p) => p.id === id);\n if (picked?.price?.currency) setCurrency(picked.price.currency);\n };\n\n const handleCreate = async (e: FormEvent) => {\n e.preventDefault();\n if (!propertyId) {\n notify.warning('Select a property', 'Choose the property this transaction is for.');\n return;\n }\n if (!newTitle.trim()) {\n notify.warning('Add a title', 'Give this transaction a short, recognizable name.');\n return;\n }\n const amountValue = Number(amount);\n if (!Number.isFinite(amountValue) || amountValue <= 0) {\n notify.warning('Enter a valid amount', 'The transaction amount must be greater than zero.');\n return;\n }\n try {\n await createTransaction({\n propertyId,\n title: newTitle.trim(),\n type: newType,\n status: newStatus,\n amount: amountValue,\n currency: currency.trim() || 'USD',\n });\n notify.success('Transaction created', `${newTitle.trim()} was added to your transactions.`);\n setCreateOpen(false);\n } catch (err) {\n notify.error('Could not create transaction', (err as Error).message);\n }\n };\n\n const statusTabs: { value: TransactionStatus | 'all'; label: string }[] = [\n { value: 'all', label: tr('housingvista.transactions.statusTabs.all', 'All') },\n { value: 'in_progress', label: tr('housingvista.transactions.statusTabs.inProgress', 'In Progress') },\n { value: 'completed', label: tr('housingvista.transactions.statusTabs.completed', 'Completed') },\n { value: 'cancelled', label: tr('housingvista.transactions.statusTabs.cancelled', 'Cancelled') },\n { value: 'negotiation', label: tr('housingvista.transactions.statusTabs.negotiation', 'Negotiation') },\n { value: 'pending_approval', label: tr('housingvista.transactions.statusTabs.pending', 'Pending') },\n ];\n\n const stats = useMemo(() => {\n const total = transactions.length;\n const totalValue = transactions.reduce((sum, t) => sum + t.amount, 0);\n const active = transactions.filter(\n (t) => t.status === 'in_progress' || t.status === 'negotiation' || t.status === 'pending_approval'\n ).length;\n const completed = transactions.filter((t) => t.status === 'completed').length;\n return { total, totalValue, active, completed };\n }, [transactions]);\n\n // ---- Table columns ----\n const columns: Column<Transaction>[] = [\n {\n key: 'id',\n header: tr('housingvista.transactions.table.id', 'ID'),\n render: (t) => <span className=\"font-medium text-text-primary\">#{t.id.split('-')[1]}</span>,\n },\n {\n key: 'property',\n header: tr('housingvista.transactions.table.property', 'Property'),\n render: (t) => (\n <div className=\"max-w-[200px]\">\n <div className=\"truncate text-sm font-medium text-text-primary\">{t.property.title}</div>\n <div className=\"truncate text-xs text-text-secondary\">\n {t.property.location.city}, {t.property.location.state}\n </div>\n </div>\n ),\n },\n {\n key: 'type',\n header: tr('housingvista.transactions.table.type', 'Type'),\n render: (t) => <span className=\"capitalize text-sm text-text-primary\">{t.type}</span>,\n },\n {\n key: 'status',\n header: tr('housingvista.transactions.table.status', 'Status'),\n render: (t) => (\n <StatusBadge label={t.status.replace(/_/g, ' ')} variant={statusVariantMap[t.status] ?? 'default'} dot />\n ),\n },\n {\n key: 'amount',\n header: tr('housingvista.transactions.table.amount', 'Amount'),\n sortable: true,\n render: (t) => <span className=\"font-semibold text-text-primary\">{formatCurrency(t.amount, t.currency)}</span>,\n },\n {\n key: 'createdAt',\n header: tr('housingvista.transactions.table.date', 'Date'),\n sortable: true,\n render: (t) => <span className=\"text-sm text-text-secondary\">{formatDate(t.createdAt.toISOString())}</span>,\n },\n {\n key: 'actions',\n header: '',\n sortable: false,\n render: (t) => (\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(t.id);\n }}\n className=\"inline-flex items-center gap-1 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n <Eye className=\"h-3.5 w-3.5\" />\n {tr('housingvista.transactions.table.view', 'View')}\n </button>\n ),\n },\n ];\n\n return (\n <PageLayout\n title={tr('housingvista.transactions.title', 'Transactions')}\n description={tr('housingvista.transactions.description', 'Manage your property transactions, escrow, and deals')}\n purpose={tr(\n 'housingvista.transactions.purpose',\n 'Track every buy, sell, and lease deal end to end — from offer and negotiation through escrow, approvals, and closing. Use this page to see what money is in motion, which deals need action, and the full history of each transaction.'\n )}\n actions={\n <Button type=\"button\" onClick={openCreate} className=\"gap-2\">\n <Plus className=\"h-4 w-4\" />\n {tr('housingvista.transactions.actions.newTransaction', 'New Transaction')}\n </Button>\n }\n >\n {/* Stats — primary insight zone (one EmphasisPanel per page) */}\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <StatCard\n label={tr('housingvista.transactions.stats.totalTransactions', 'Total Transactions')}\n value={stats.total}\n icon={<FileText className=\"h-5 w-5\" />}\n />\n <StatCard\n label={tr('housingvista.transactions.stats.totalValue', 'Total Value')}\n value={stats.totalValue}\n prefix=\"$\"\n icon={<DollarSign className=\"h-5 w-5\" />}\n />\n <StatCard\n label={tr('housingvista.transactions.stats.active', 'Active')}\n value={stats.active}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n />\n <StatCard\n label={tr('housingvista.transactions.stats.completed', 'Completed')}\n value={stats.completed}\n icon={<CheckCircle className=\"h-5 w-5\" />}\n />\n </div>\n </EmphasisPanel>\n\n <NextSteps\n storageKey=\"housingvista-transactions\"\n steps={[\n {\n id: 'focus-active',\n label: tr('housingvista.transactions.nextSteps.active', 'See deals that need attention'),\n description: tr(\n 'housingvista.transactions.nextSteps.activeDesc',\n 'Filter to in-progress deals where money is moving.'\n ),\n onClick: () => selectStatus('in_progress'),\n },\n {\n id: 'open-detail',\n label: tr('housingvista.transactions.nextSteps.open', 'Open a transaction'),\n description: tr(\n 'housingvista.transactions.nextSteps.openDesc',\n 'Drill into escrow, approvals, and the full timeline.'\n ),\n onClick: () => {\n const first = transactions[0];\n if (first) navigate(`../transactions/${first.id}`);\n },\n },\n {\n id: 'browse-properties',\n label: tr('housingvista.transactions.nextSteps.browse', 'Start a new deal from a listing'),\n description: tr('housingvista.transactions.nextSteps.browseDesc', 'Find a property to make an offer on.'),\n onClick: () => navigate('../properties'),\n },\n ]}\n />\n\n {/* Search */}\n <FilterBar\n searchValue={searchQuery}\n onSearchChange={handleSearchChange}\n searchPlaceholder={tr(\n 'housingvista.transactions.search.placeholder',\n 'Search by property or transaction ID...'\n )}\n />\n\n {/* Status Tabs */}\n <div className=\"flex flex-wrap items-center gap-2\">\n {statusTabs.map((tab) => (\n <button\n key={tab.value}\n onClick={() => selectStatus(tab.value)}\n className={`rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${\n selectedStatus === tab.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n }`}\n >\n {tab.label}\n </button>\n ))}\n </div>\n\n {/* Results info */}\n <p className=\"text-sm text-text-secondary\">\n {tr('housingvista.transactions.results.showing', 'Showing')} {transactions.length}{' '}\n {tr('housingvista.transactions.results.transactions', 'transactions')}\n </p>\n\n {/* Table / Empty */}\n {transactions.length > 0 ? (\n <DataTable\n columns={columns}\n data={transactions}\n keyField=\"id\"\n onRowClick={(t) => navigate(t.id)}\n pageSize={pagination.pageSize}\n serverPagination={{\n page: pagination.page,\n pageSize: pagination.pageSize,\n hasNextPage: transactions.length >= pagination.pageSize,\n loading,\n onPageChange: pagination.setPage,\n onPageSizeChange: pagination.setPageSize,\n }}\n />\n ) : (\n <EmptyState\n illustration=\"empty-search\"\n icon={<FileText className=\"h-12 w-12\" />}\n title={tr('housingvista.transactions.empty.title', 'No transactions found')}\n description={tr(\n 'housingvista.transactions.empty.description',\n 'Try adjusting your search criteria or create a new transaction.'\n )}\n action={\n <Button type=\"button\" onClick={openCreate} className=\"gap-2\">\n <Plus className=\"h-4 w-4\" />\n {tr('housingvista.transactions.empty.createNewTransaction', 'Create New Transaction')}\n </Button>\n }\n />\n )}\n\n {/* Create transaction dialog */}\n <ResponsiveDialog open={createOpen} onOpenChange={setCreateOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <form onSubmit={handleCreate}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('housingvista.transactions.create.title', 'New transaction')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'housingvista.transactions.create.description',\n 'Start a deal against a property. You can add documents, milestones, and escrow later.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {tr('housingvista.transactions.create.property', 'Property')} *\n </label>\n <select\n required\n value={propertyId}\n onChange={(e) => handlePropertyChange(e.target.value)}\n className={inputClass}\n >\n <option value=\"\" disabled>\n {properties.length > 0\n ? tr('housingvista.transactions.create.selectProperty', 'Select a property…')\n : tr('housingvista.transactions.create.noProperties', 'No properties available')}\n </option>\n {properties.map((p) => (\n <option key={p.id} value={p.id}>\n {p.title}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {tr('housingvista.transactions.create.titleField', 'Title')} *\n </label>\n <input\n type=\"text\"\n required\n placeholder={tr('housingvista.transactions.create.titlePlaceholder', 'e.g. Maple Court purchase')}\n value={newTitle}\n onChange={(e) => setNewTitle(e.target.value)}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {tr('housingvista.transactions.create.type', 'Type')}\n </label>\n <select\n value={newType}\n onChange={(e) => setNewType(e.target.value as TransactionType)}\n className={inputClass}\n >\n {TRANSACTION_TYPES.map((option) => (\n <option key={option} value={option} className=\"capitalize\">\n {option}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {tr('housingvista.transactions.create.status', 'Status')}\n </label>\n <select\n value={newStatus}\n onChange={(e) => setNewStatus(e.target.value as TransactionStatus)}\n className={inputClass}\n >\n {CREATE_STATUSES.map((option) => (\n <option key={option} value={option}>\n {option.replace(/_/g, ' ')}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {tr('housingvista.transactions.create.amount', 'Amount')} *\n </label>\n <input\n type=\"number\"\n required\n min={0}\n step=\"0.01\"\n placeholder=\"0.00\"\n value={amount}\n onChange={(e) => setAmount(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {tr('housingvista.transactions.create.currency', 'Currency')}\n </label>\n <input\n type=\"text\"\n placeholder=\"USD\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value.toUpperCase())}\n maxLength={3}\n className={inputClass}\n />\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setCreateOpen(false)}>\n {tr('housingvista.transactions.create.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={creating}>\n {creating\n ? tr('housingvista.transactions.create.creating', 'Creating…')\n : tr('housingvista.transactions.create.submit', 'Create transaction')}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TransactionsPage.js","names":[],"sources":["../../../../src/housingvista/pages/transactions/TransactionsPage.tsx"],"sourcesContent":["/**\n * HousingVista Transactions Listing Page\n * Migrated from legacy housingvista-frontend transactions.tsx\n * @module housingvista\n */\n\nimport { useState, useMemo, useEffect, type FormEvent } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Plus, FileText, DollarSign, TrendingUp, CheckCircle, Eye } from 'lucide-react';\nimport {\n Button,\n EmphasisPanel,\n NextSteps,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n} from '@burdenoff/fe-libs/ui';\nimport { PageLayout, FilterBar, DataTable, StatusBadge, EmptyState, StatCard } from '../../components';\nimport type { Column } from '../../components';\nimport { useTransactions, useCreateTransaction, useProperties, type TransactionFilterInput } from '../../hooks';\nimport { useServerPagination } from '../../hooks/useServerPagination';\nimport { notify } from '../../lib/toast';\nimport { formatCurrency, formatDate } from '../../utils/helpers';\nimport type { Transaction, TransactionStatus, TransactionType } from '../../types';\n\nconst inputClass =\n 'h-10 w-full rounded-md border border-border-subtle bg-bg-surface px-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n\nconst TRANSACTION_TYPES: TransactionType[] = ['sale', 'purchase', 'rental', 'lease'];\nconst CREATE_STATUSES: TransactionStatus[] = ['draft', 'negotiation', 'under_contract', 'completed'];\n\n// ---- Status badge variant map ----\nconst statusVariantMap: Record<string, 'success' | 'warning' | 'error' | 'info' | 'default' | 'muted'> = {\n draft: 'muted',\n negotiation: 'info',\n under_contract: 'warning',\n pending_approval: 'warning',\n approved: 'info',\n in_progress: 'info',\n completed: 'success',\n cancelled: 'error',\n disputed: 'error',\n};\n\nexport const TransactionsPage = () => {\n const navigate = useNavigate();\n const { t } = useI18n();\n const pagination = useServerPagination(10);\n\n // ---- Search + status filter (server-side) ----\n const [searchQuery, setSearchQuery] = useState('');\n const [debouncedSearch, setDebouncedSearch] = useState('');\n const [selectedStatus, setSelectedStatus] = useState<TransactionStatus | 'all'>('all');\n\n // Debounce the search box into the server filter (300ms).\n useEffect(() => {\n const handle = setTimeout(() => setDebouncedSearch(searchQuery), 300);\n return () => clearTimeout(handle);\n }, [searchQuery]);\n\n const filter: TransactionFilterInput = {\n search: debouncedSearch.trim() || undefined,\n status: selectedStatus === 'all' ? undefined : selectedStatus,\n };\n\n const { data: transactions, loading } = useTransactions({ filter, pagination: pagination.pagination });\n const { createTransaction, loading: creating } = useCreateTransaction();\n const { data: properties } = useProperties({ pagination: { limit: 50, offset: 0 } });\n\n // Reset to the first page whenever the search text or status tab changes.\n const handleSearchChange = (value: string) => {\n setSearchQuery(value);\n pagination.setPage(1);\n };\n const selectStatus = (value: TransactionStatus | 'all') => {\n setSelectedStatus(value);\n pagination.setPage(1);\n };\n\n const tr = (key: string, fallback: string) => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n\n // ---- Create-transaction dialog state ----\n const [createOpen, setCreateOpen] = useState(false);\n const [propertyId, setPropertyId] = useState('');\n const [newTitle, setNewTitle] = useState('');\n const [newType, setNewType] = useState<TransactionType>('sale');\n const [newStatus, setNewStatus] = useState<TransactionStatus>('draft');\n const [amount, setAmount] = useState('');\n const [currency, setCurrency] = useState('USD');\n\n const openCreate = () => {\n setPropertyId('');\n setNewTitle('');\n setNewType('sale');\n setNewStatus('draft');\n setAmount('');\n setCurrency('USD');\n setCreateOpen(true);\n };\n\n const handlePropertyChange = (id: string) => {\n setPropertyId(id);\n const picked = properties.find((p) => p.id === id);\n if (picked?.price?.currency) setCurrency(picked.price.currency);\n };\n\n const handleCreate = async (e: FormEvent) => {\n e.preventDefault();\n if (!propertyId) {\n notify.warning('Select a property', 'Choose the property this transaction is for.');\n return;\n }\n if (!newTitle.trim()) {\n notify.warning('Add a title', 'Give this transaction a short, recognizable name.');\n return;\n }\n const amountValue = Number(amount);\n if (!Number.isFinite(amountValue) || amountValue <= 0) {\n notify.warning('Enter a valid amount', 'The transaction amount must be greater than zero.');\n return;\n }\n try {\n await createTransaction({\n propertyId,\n title: newTitle.trim(),\n type: newType,\n status: newStatus,\n amount: amountValue,\n currency: currency.trim() || 'USD',\n });\n notify.success('Transaction created', `${newTitle.trim()} was added to your transactions.`);\n setCreateOpen(false);\n } catch (err) {\n notify.error('Could not create transaction', (err as Error).message);\n }\n };\n\n const statusTabs: { value: TransactionStatus | 'all'; label: string }[] = [\n { value: 'all', label: tr('housingvista.transactions.statusTabs.all', 'All') },\n { value: 'in_progress', label: tr('housingvista.transactions.statusTabs.inProgress', 'In Progress') },\n { value: 'completed', label: tr('housingvista.transactions.statusTabs.completed', 'Completed') },\n { value: 'cancelled', label: tr('housingvista.transactions.statusTabs.cancelled', 'Cancelled') },\n { value: 'negotiation', label: tr('housingvista.transactions.statusTabs.negotiation', 'Negotiation') },\n { value: 'pending_approval', label: tr('housingvista.transactions.statusTabs.pending', 'Pending') },\n ];\n\n const stats = useMemo(() => {\n const total = transactions.length;\n const totalValue = transactions.reduce((sum, t) => sum + t.amount, 0);\n const active = transactions.filter(\n (t) => t.status === 'in_progress' || t.status === 'negotiation' || t.status === 'pending_approval'\n ).length;\n const completed = transactions.filter((t) => t.status === 'completed').length;\n return { total, totalValue, active, completed };\n }, [transactions]);\n\n // ---- Table columns ----\n const columns: Column<Transaction>[] = [\n {\n key: 'id',\n header: tr('housingvista.transactions.table.id', 'ID'),\n render: (t) => <span className=\"font-medium text-text-primary\">#{t.id.split('-')[1]}</span>,\n },\n {\n key: 'property',\n header: tr('housingvista.transactions.table.property', 'Property'),\n render: (t) => (\n <div className=\"max-w-[200px]\">\n <div className=\"truncate text-sm font-medium text-text-primary\">{t.property.title}</div>\n <div className=\"truncate text-xs text-text-secondary\">\n {t.property.location.city}, {t.property.location.state}\n </div>\n </div>\n ),\n },\n {\n key: 'type',\n header: tr('housingvista.transactions.table.type', 'Type'),\n render: (t) => <span className=\"capitalize text-sm text-text-primary\">{t.type}</span>,\n },\n {\n key: 'status',\n header: tr('housingvista.transactions.table.status', 'Status'),\n render: (t) => (\n <StatusBadge label={t.status.replace(/_/g, ' ')} variant={statusVariantMap[t.status] ?? 'default'} dot />\n ),\n },\n {\n key: 'amount',\n header: tr('housingvista.transactions.table.amount', 'Amount'),\n sortable: true,\n render: (t) => <span className=\"font-semibold text-text-primary\">{formatCurrency(t.amount, t.currency)}</span>,\n },\n {\n key: 'createdAt',\n header: tr('housingvista.transactions.table.date', 'Date'),\n sortable: true,\n render: (t) => <span className=\"text-sm text-text-secondary\">{formatDate(String(t.createdAt))}</span>,\n },\n {\n key: 'actions',\n header: '',\n sortable: false,\n render: (t) => (\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(t.id);\n }}\n className=\"inline-flex items-center gap-1 text-xs font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n <Eye className=\"h-3.5 w-3.5\" />\n {tr('housingvista.transactions.table.view', 'View')}\n </button>\n ),\n },\n ];\n\n return (\n <PageLayout\n title={tr('housingvista.transactions.title', 'Transactions')}\n description={tr('housingvista.transactions.description', 'Manage your property transactions, escrow, and deals')}\n purpose={tr(\n 'housingvista.transactions.purpose',\n 'Track every buy, sell, and lease deal end to end — from offer and negotiation through escrow, approvals, and closing. Use this page to see what money is in motion, which deals need action, and the full history of each transaction.'\n )}\n actions={\n <Button type=\"button\" onClick={openCreate} className=\"gap-2\">\n <Plus className=\"h-4 w-4\" />\n {tr('housingvista.transactions.actions.newTransaction', 'New Transaction')}\n </Button>\n }\n >\n {/* Stats — primary insight zone (one EmphasisPanel per page) */}\n <EmphasisPanel className=\"p-4\">\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n <StatCard\n label={tr('housingvista.transactions.stats.totalTransactions', 'Total Transactions')}\n value={stats.total}\n icon={<FileText className=\"h-5 w-5\" />}\n />\n <StatCard\n label={tr('housingvista.transactions.stats.totalValue', 'Total Value')}\n value={stats.totalValue}\n prefix=\"$\"\n icon={<DollarSign className=\"h-5 w-5\" />}\n />\n <StatCard\n label={tr('housingvista.transactions.stats.active', 'Active')}\n value={stats.active}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n />\n <StatCard\n label={tr('housingvista.transactions.stats.completed', 'Completed')}\n value={stats.completed}\n icon={<CheckCircle className=\"h-5 w-5\" />}\n />\n </div>\n </EmphasisPanel>\n\n <NextSteps\n storageKey=\"housingvista-transactions\"\n steps={[\n {\n id: 'focus-active',\n label: tr('housingvista.transactions.nextSteps.active', 'See deals that need attention'),\n description: tr(\n 'housingvista.transactions.nextSteps.activeDesc',\n 'Filter to in-progress deals where money is moving.'\n ),\n onClick: () => selectStatus('in_progress'),\n },\n {\n id: 'open-detail',\n label: tr('housingvista.transactions.nextSteps.open', 'Open a transaction'),\n description: tr(\n 'housingvista.transactions.nextSteps.openDesc',\n 'Drill into escrow, approvals, and the full timeline.'\n ),\n onClick: () => {\n const first = transactions[0];\n if (first) navigate(`../transactions/${first.id}`);\n },\n },\n {\n id: 'browse-properties',\n label: tr('housingvista.transactions.nextSteps.browse', 'Start a new deal from a listing'),\n description: tr('housingvista.transactions.nextSteps.browseDesc', 'Find a property to make an offer on.'),\n onClick: () => navigate('../properties'),\n },\n ]}\n />\n\n {/* Search */}\n <FilterBar\n searchValue={searchQuery}\n onSearchChange={handleSearchChange}\n searchPlaceholder={tr(\n 'housingvista.transactions.search.placeholder',\n 'Search by property or transaction ID...'\n )}\n />\n\n {/* Status Tabs */}\n <div className=\"flex flex-wrap items-center gap-2\">\n {statusTabs.map((tab) => (\n <button\n key={tab.value}\n onClick={() => selectStatus(tab.value)}\n className={`rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${\n selectedStatus === tab.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:bg-bg-sunken hover:text-text-primary'\n }`}\n >\n {tab.label}\n </button>\n ))}\n </div>\n\n {/* Results info */}\n <p className=\"text-sm text-text-secondary\">\n {tr('housingvista.transactions.results.showing', 'Showing')} {transactions.length}{' '}\n {tr('housingvista.transactions.results.transactions', 'transactions')}\n </p>\n\n {/* Table / Empty */}\n {transactions.length > 0 ? (\n <DataTable\n columns={columns}\n data={transactions}\n keyField=\"id\"\n onRowClick={(t) => navigate(t.id)}\n pageSize={pagination.pageSize}\n serverPagination={{\n page: pagination.page,\n pageSize: pagination.pageSize,\n hasNextPage: transactions.length >= pagination.pageSize,\n loading,\n onPageChange: pagination.setPage,\n onPageSizeChange: pagination.setPageSize,\n }}\n />\n ) : (\n <EmptyState\n illustration=\"empty-search\"\n icon={<FileText className=\"h-12 w-12\" />}\n title={tr('housingvista.transactions.empty.title', 'No transactions found')}\n description={tr(\n 'housingvista.transactions.empty.description',\n 'Try adjusting your search criteria or create a new transaction.'\n )}\n action={\n <Button type=\"button\" onClick={openCreate} className=\"gap-2\">\n <Plus className=\"h-4 w-4\" />\n {tr('housingvista.transactions.empty.createNewTransaction', 'Create New Transaction')}\n </Button>\n }\n />\n )}\n\n {/* Create transaction dialog */}\n <ResponsiveDialog open={createOpen} onOpenChange={setCreateOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-lg\">\n <form onSubmit={handleCreate}>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tr('housingvista.transactions.create.title', 'New transaction')}\n </ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {tr(\n 'housingvista.transactions.create.description',\n 'Start a deal against a property. You can add documents, milestones, and escrow later.'\n )}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3 py-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {tr('housingvista.transactions.create.property', 'Property')} *\n </label>\n <select\n required\n value={propertyId}\n onChange={(e) => handlePropertyChange(e.target.value)}\n className={inputClass}\n >\n <option value=\"\" disabled>\n {properties.length > 0\n ? tr('housingvista.transactions.create.selectProperty', 'Select a property…')\n : tr('housingvista.transactions.create.noProperties', 'No properties available')}\n </option>\n {properties.map((p) => (\n <option key={p.id} value={p.id}>\n {p.title}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {tr('housingvista.transactions.create.titleField', 'Title')} *\n </label>\n <input\n type=\"text\"\n required\n placeholder={tr('housingvista.transactions.create.titlePlaceholder', 'e.g. Maple Court purchase')}\n value={newTitle}\n onChange={(e) => setNewTitle(e.target.value)}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {tr('housingvista.transactions.create.type', 'Type')}\n </label>\n <select\n value={newType}\n onChange={(e) => setNewType(e.target.value as TransactionType)}\n className={inputClass}\n >\n {TRANSACTION_TYPES.map((option) => (\n <option key={option} value={option} className=\"capitalize\">\n {option}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {tr('housingvista.transactions.create.status', 'Status')}\n </label>\n <select\n value={newStatus}\n onChange={(e) => setNewStatus(e.target.value as TransactionStatus)}\n className={inputClass}\n >\n {CREATE_STATUSES.map((option) => (\n <option key={option} value={option}>\n {option.replace(/_/g, ' ')}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {tr('housingvista.transactions.create.amount', 'Amount')} *\n </label>\n <input\n type=\"number\"\n required\n min={0}\n step=\"0.01\"\n placeholder=\"0.00\"\n value={amount}\n onChange={(e) => setAmount(e.target.value)}\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"mb-1.5 block text-sm font-medium text-text-primary\">\n {tr('housingvista.transactions.create.currency', 'Currency')}\n </label>\n <input\n type=\"text\"\n placeholder=\"USD\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value.toUpperCase())}\n maxLength={3}\n className={inputClass}\n />\n </div>\n </div>\n </div>\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={() => setCreateOpen(false)}>\n {tr('housingvista.transactions.create.cancel', 'Cancel')}\n </Button>\n <Button type=\"submit\" disabled={creating}>\n {creating\n ? tr('housingvista.transactions.create.creating', 'Creating…')\n : tr('housingvista.transactions.create.submit', 'Create transaction')}\n </Button>\n </ResponsiveDialogFooter>\n </form>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-housingvista",
|
|
3
|
-
"version": "2026.703.
|
|
3
|
+
"version": "2026.703.7",
|
|
4
4
|
"description": "Burdenoff HousingVista Microfrontend - Comprehensive real estate platform for property management, investment, and transactions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|