import React, { useState, useMemo, useRef, useEffect } from ‘react’;
import { MOCK_PROPERTIES } from ‘./data’;
import { Property, AIReport, VisualOptimization, PropertyUnit } from ‘./types’;
import { PropertyCard } from ‘./components/PropertyCard’;
import { ReportView } from ‘./components/ReportView’;
import { AnalyticsOverview } from ‘./components/AnalyticsOverview’;
import { generatePropertyReport } from ‘./services/geminiService’;
import { ImageEditModal } from ‘./components/ImageEditModal’;
import { AddPropertyModal } from ‘./components/AddPropertyModal’;
import { EditPropertyModal } from ‘./components/EditPropertyModal’;
import { AddUnitModal } from ‘./components/AddUnitModal’;
import { EditUnitModal } from ‘./components/EditUnitModal’;
import { TourManagementModal } from ‘./components/TourManagementModal’;

const App: React.FC = () => {
const [properties, setProperties] = useState(MOCK_PROPERTIES);
const [selectedProperty, setSelectedProperty] = useState(null);
const [report, setReport] = useState(null);
const [activeOptimization, setActiveOptimization] = useState(null);
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
const [isEditPropertyModalOpen, setIsEditPropertyModalOpen] = useState(false);
const [propertyToEdit, setPropertyToEdit] = useState(null);
const [isAddUnitModalOpen, setIsAddUnitModalOpen] = useState(false);
const [unitToEdit, setUnitToEdit] = useState(null);
const [unitForTour, setUnitForTour] = useState(null);
const [loading, setLoading] = useState(false);

// User Menu State
const [isUserMenuOpen, setIsUserMenuOpen] = useState(false);
const userMenuRef = useRef(null);

// Global UI Scaling State
const [uiScale, setUiScale] = useState(1); // 1 = 100% / 16px base

// Filter and Sort states for units
const [unitSortFilter, setUnitSortFilter] = useState(‘default’);

// Apply UI scaling globally by modifying root font size
useEffect(() => {
document.documentElement.style.fontSize = `${uiScale * 100}%`;
}, [uiScale]);

// Handle clicking outside the user menu to close it
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (userMenuRef.current && !userMenuRef.current.contains(event.target as Node)) {
setIsUserMenuOpen(false);
}
};
document.addEventListener(‘mousedown’, handleClickOutside);
return () => document.removeEventListener(‘mousedown’, handleClickOutside);
}, []);

const handleGenerateReport = async () => {
if (!selectedProperty) return;
setLoading(true);
try {
const result = await generatePropertyReport(selectedProperty);
setReport(result);
} catch (error) {
console.error(”Failed to generate report:”, error);
alert(”Failed to connect to AI Strategic Engine. Please try again.”);
} finally {
setLoading(false);
}
};

const handleBack = () => {
setSelectedProperty(null);
setReport(null);
setUnitSortFilter(‘default’);
};

const handleOpenEditModal = (p: Property) => {
setPropertyToEdit(p);
setIsEditPropertyModalOpen(true);
};

const handleOptimizationTrigger = (optimization: VisualOptimization) => {
setActiveOptimization(optimization);
};

const handleAddProperty = (newProperty: Property) => {
setProperties([…properties, newProperty]);
setIsAddModalOpen(false);
};

const handleUpdateProperty = (updatedProperty: Property) => {
setProperties(prev => prev.map(p => p.id === updatedProperty.id ? updatedProperty : p));
if (selectedProperty?.id === updatedProperty.id) {
setSelectedProperty(updatedProperty);
}
setIsEditPropertyModalOpen(false);
setPropertyToEdit(null);
};

const handleDeleteProperty = (propertyId: string) => {
setProperties(prev => prev.filter(p => p.id !== propertyId));
if (selectedProperty?.id === propertyId) {
setSelectedProperty(null);
}
setIsEditPropertyModalOpen(false);
setPropertyToEdit(null);
};

const handleAddUnit = (newUnit: PropertyUnit) => {
if (!selectedProperty) return;

const updatedProperty = {
…selectedProperty,
units: […selectedProperty.units, newUnit]
};

setProperties(prev => prev.map(p =>
p.id === selectedProperty.id ? updatedProperty : p
));
setSelectedProperty(updatedProperty);
setIsAddUnitModalOpen(false);
};

const handleUpdateUnit = (updatedUnit: PropertyUnit) => {
if (!selectedProperty) return;

const updatedProperty = {
…selectedProperty,
units: selectedProperty.units.map(u => u.id === updatedUnit.id ? updatedUnit : u)
};

setProperties(prev => prev.map(p =>
p.id === selectedProperty.id ? updatedProperty : p
));
setSelectedProperty(updatedProperty);
setUnitToEdit(null);
};

const handleUpdateUnitTour = (url: string) => {
if (!selectedProperty || !unitForTour) return;

const updatedUnit = { …unitForTour, tourUrl: url };
const updatedProperty = {
…selectedProperty,
units: selectedProperty.units.map(u => u.id === unitForTour.id ? updatedUnit : u)
};

setProperties(prev => prev.map(p =>
p.id === selectedProperty.id ? updatedProperty : p
));
setSelectedProperty(updatedProperty);
setUnitForTour(null);
};

// Compute the processed units list based on Sort & Filter selection
const processedUnits = useMemo(() => {
if (!selectedProperty) return [];

let units = […selectedProperty.units];

// Filter Logic
if (unitSortFilter === ‘available’) units = units.filter(u => u.availability === ‘Available’);
if (unitSortFilter === ‘offer’) units = units.filter(u => u.availability === ‘Under Offer’);
if (unitSortFilter === ‘leased’) units = units.filter(u => u.availability === ‘Leased’);

// Sort Logic
units.sort((a, b) => {
switch (unitSortFilter) {
case ‘floor’:
return a.floor – b.floor;
case ‘size’:
return b.sizeSqM – a.sizeSqM;
case ‘price’:
return b.pricePerMonth – a.pricePerMonth;
case ‘alphabet’:
return a.name.localeCompare(b.name);
default:
return 0;
}
});

return units;
}, [selectedProperty, unitSortFilter]);

return (

{activeOptimization && selectedProperty && (
setActiveOptimization(null)}
onUpdate={(newImageUrl) => {
setProperties(prev => prev.map(p =>
p.id === selectedProperty.id ? { …p, image: newImageUrl } : p
));
setSelectedProperty({ …selectedProperty, image: newImageUrl });
setActiveOptimization(null);
}}
/>
)}

{isAddModalOpen && (
setIsAddModalOpen(false)}
onAdd={handleAddProperty}
/>
)}

{isEditPropertyModalOpen && propertyToEdit && (
setIsEditPropertyModalOpen(false)}
onUpdate={handleUpdateProperty}
onDelete={handleDeleteProperty}
/>
)}

{isAddUnitModalOpen && (
setIsAddUnitModalOpen(false)}
onAdd={handleAddUnit}
/>
)}

{unitToEdit && (
setUnitToEdit(null)}
onUpdate={handleUpdateUnit}
/>
)}

{unitForTour && (
setUnitForTour(null)}
onUpdate={handleUpdateUnitTour}
/>
)}

PropAI
Strategic Advisor

setIsUserMenuOpen(!isUserMenuOpen)}
>

Commercial Portfolio

Avatar

{isUserMenuOpen && (

Enterprise Account

Johnathan Pierce

Chief Asset Manager

{[
{ label: ‘Profile Settings’, icon: },
{ label: ‘Subscription Plan’, icon: },
{ label: ‘Team Management’, icon: },
{ label: ‘API & Integrations’, icon: },
].map((item, idx) => (

))}
Interface Scale

{Math.round(uiScale * 100)}%
{[
{ label: ‘S’, val: 0.85 },
{ label: ‘M’, val: 1.0 },
{ label: ‘L’, val: 1.15 },
{ label: ‘XL’, val: 1.3 }
].map((s) => (

))}

)}


{!selectedProperty ? (

My Portfolio

Analyze high-value assets, upload professional visuals, and accelerate leasing speed with strategic advisor insights.

{properties.map(p => (

))}

) : (

{selectedProperty.name}

Active Insight

{selectedProperty.address}

{report ? (
setReport(null)}
onOptimizeVisual={handleOptimizationTrigger}
/>
) : (


Asset Inventory Management

setUnitSortFilter(e.target.value)}
>
Sort & Filter: All

By Floor (Low-High)
By Size (Large-Small)
By Rent (High-Low)
By Alphabetical (A-Z)

Status: Available
Status: Under Offer
Status: Leased

{processedUnits.length === 0 ? (

{selectedProperty.units.length === 0 ? ”No units added to this asset yet.” : ”No units match your current filter.”}

{selectedProperty.units.length > 0 && (

)}

) : (

{processedUnits.map(unit => (

{unit.name}


{unit.availability}

Elevation
Floor {unit.floor}
Rentable Area
{unit.sizeSqM.toLocaleString()} m²

{unit.amenities.map(a => (
{a}
))}
Monthly Capital
${unit.pricePerMonth.toLocaleString()}


))}

)}

)}

)}

);
};

export default App;