/* @component-map * App — Main container, tab navigation [app.jsx] * ApartmentList — List/grid of saved apartments [components/ApartmentList.jsx] * ApartmentForm — Add/edit apartment modal [components/ApartmentForm.jsx] * ApartmentMap — Map view with color-coded markers [components/ApartmentMap.jsx] * ApartmentDetail — Detail view modal [components/ApartmentDetail.jsx] * @end-component-map */ // DEPLOY_CONFIG: {"proxy": [{"name": "neighborhood-data-proxy", "path": "/api/neighborhoods", "method": "GET", "target": "https://external.api/neighborhoods", "forward_query_params": true, "normalize_response": {"type": "json", "mapping": {"items": "data", "total": "meta.total", "success": "true"}}}]} import { useMemo, useState } from 'react'; import { ApartmentList } from './components/ApartmentList.jsx'; import { ApartmentForm } from './components/ApartmentForm.jsx'; import { ApartmentMap } from './components/ApartmentMap.jsx'; import { ApartmentDetail } from './components/ApartmentDetail.jsx'; import { useCollection, useAuth } from '@deplixo/sdk'; const SCORE_WEIGHTS = { price: 0.35, location: 0.3, size: 0.2, condition: 0.15, }; const clamp = (value, min, max) => Math.min(max, Math.max(min, value)); const toNumber = (value) => { if (value === null || value === undefined || value === '') return null; const num = Number(value); return Number.isFinite(num) ? num : null; }; const getRating = (apt) => { const raw = apt?.rating ?? apt?.score ?? apt?.conditionRating; const rating = toNumber(raw); return rating === null ? 0 : clamp(rating, 0, 5); }; const getConditionScore = (apt) => { const conditionValue = toNumber(apt?.conditionScore ?? apt?.condition); if (conditionValue !== null) return clamp(conditionValue, 0, 5); return getRating(apt); }; const getLocationScore = (apt) => { const value = toNumber(apt?.locationScore ?? apt?.walkability ?? apt?.transitScore); if (value !== null) return clamp(value, 0, 5); const lat = toNumber(apt?.lat ?? apt?.latitude); const lng = toNumber(apt?.lng ?? apt?.longitude); if (lat === null || lng === null) return 2.5; const normalized = (Math.abs(lat % 1) + Math.abs(lng % 1)) / 2; return clamp(5 - normalized * 5, 0, 5); }; const getSizeScore = (apt) => { const beds = toNumber(apt?.bedrooms ?? apt?.beds ?? apt?.bedCount); const sqft = toNumber(apt?.sqft ?? apt?.squareFeet ?? apt?.area); if (sqft !== null) { return clamp((sqft / 1200) * 5, 0, 5); } if (beds !== null) { return clamp((beds / 3) * 5, 0, 5); } return 2.5; }; const getPriceScore = (apt) => { const rent = toNumber(apt?.rent ?? apt?.price ?? apt?.monthlyRent); if (rent === null) return 2.5; const minRent = 800; const maxRent = 5000; const normalized = 1 - clamp((rent - minRent) / (maxRent - minRent), 0, 1); return clamp(normalized * 5, 0, 5); }; const calculateApartmentScore = (apt) => { const priceScore = getPriceScore(apt); const locationScore = getLocationScore(apt); const sizeScore = getSizeScore(apt); const conditionScore = getConditionScore(apt); const weightedTotal = priceScore * SCORE_WEIGHTS.price + locationScore * SCORE_WEIGHTS.location + sizeScore * SCORE_WEIGHTS.size + conditionScore * SCORE_WEIGHTS.condition; return { priceScore, locationScore, sizeScore, conditionScore, totalScore: Number(weightedTotal.toFixed(2)), }; }; const getDisplayValue = (value, fallback = '—') => { if (value === null || value === undefined || value === '') return fallback; return value; }; const formatCurrency = (value) => { const num = toNumber(value); if (num === null) return '—'; return `$${num.toLocaleString()}`; }; const formatBeds = (apt) => { const beds = toNumber(apt?.bedrooms ?? apt?.beds ?? apt?.bedCount); return beds === null ? '—' : `${beds} bd`; }; const formatSqft = (apt) => { const sqft = toNumber(apt?.sqft ?? apt?.squareFeet ?? apt?.area); return sqft === null ? '—' : `${sqft.toLocaleString()} sqft`; }; const formatLocation = (apt) => { const address = apt?.address || apt?.location || apt?.name; return getDisplayValue(address); }; const getTopPicks = (apartments, count = 3) => [...apartments].slice(0, count); function App() { const { user, loading: authLoading, logout } = useAuth(); const [activeTab, setActiveTab] = useState('list'); const [showForm, setShowForm] = useState(false); const [editingApt, setEditingApt] = useState(null); const [viewingApt, setViewingApt] = useState(null); const collection = useCollection('apartments', { personal: true }); const apartmentsWithScores = useMemo(() => { return (collection.items || []).map((apt) => { const scoring = calculateApartmentScore(apt); return { ...apt, scoring, totalScore: scoring.totalScore, }; }); }, [collection.items]); const rankedApartments = useMemo(() => { return [...apartmentsWithScores].sort((a, b) => (b.totalScore || 0) - (a.totalScore || 0)); }, [apartmentsWithScores]); const topPick = rankedApartments[0] || null; const topPicks = useMemo(() => getTopPicks(rankedApartments, 3), [rankedApartments]); const handleEdit = (apt) => { setEditingApt(apt); setShowForm(true); }; const handleCloseForm = () => { setShowForm(false); setEditingApt(null); }; const handleView = (apt) => setViewingApt(apt); const handleCloseDetail = () => setViewingApt(null); if (authLoading) return
Signing in...
; if (!user) { return (
🏠

ApartmentTracker

Sign in with Google to access your personal apartment list, map, and notes.

You’ll be redirected to Deplixo login, which supports Google sign-in.

); } return (
🏠

ApartmentTracker

{topPick && ( Top Pick: {topPick.totalScore}/5 )} {user.name}
Weighted Score
Price 35% Location 30% Size 20% Condition 15%
{activeTab === 'list' && ( )} {activeTab === 'map' && ( )} {activeTab === 'compare' && (

Top Picks Comparison

Side-by-side view of your highest ranked apartments based on the weighted scoring system.

{topPicks.length === 0 ? (
⚖️

No apartments to compare yet

Add listings to see your top-ranked apartments side by side.

) : (
{topPicks.map((apt, index) => (
#{index + 1} Pick

{formatLocation(apt)}

{apt.totalScore}/5
Price {apt.scoring.priceScore.toFixed(1)}/5
Location {apt.scoring.locationScore.toFixed(1)}/5
Size {apt.scoring.sizeScore.toFixed(1)}/5
Condition {apt.scoring.conditionScore.toFixed(1)}/5
Rent: {formatCurrency(apt.rent ?? apt.price ?? apt.monthlyRent)}
Beds: {formatBeds(apt)}
Sqft: {formatSqft(apt)}
Rating: {getRating(apt).toFixed(1)}/5
Price {SCORE_WEIGHTS.price * 100}% Location {SCORE_WEIGHTS.location * 100}% Size {SCORE_WEIGHTS.size * 100}% Condition {SCORE_WEIGHTS.condition * 100}%
))}
)}
)}
{showForm && ( )} {viewingApt && ( )}
); } export default App; ReactDOM.createRoot(document.getElementById('root')).render();