docs: consolidate exploration & audit reports under docs/ (TEC-3094)

- Move 8 stray .md (+5 .txt) from ~/Desktop into docs/explorations/from-desktop/
- Reorganize 27 .md/.txt at workspace root:
  - audit reports -> docs/audits/
  - exploration reports -> docs/explorations/
  - design system -> docs/design-system/
- Keep only README/CHANGELOG/CONTRIBUTING/CLAUDE at repo root
- Refresh docs/README.md as canonical index with links to all groups
- Note: pre-existing docs/audits/AUDIT_INDEX.md and AUDIT_SUMMARY.md were
  overwritten by the newer root-level versions during the move

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-21 16:29:24 +07:00
parent 912121cf09
commit 08b96f9c2d
39 changed files with 15129 additions and 562 deletions

View File

@@ -0,0 +1,536 @@
# UI Mapping Quick Guide — GoodGo API Fields
A fast reference for wiring UI mockups to real API data. All fields with their actual types and endpoints.
---
## 🏠 Property/Listing Display
### Card View (Search Results)
```
┌─────────────────────────────────┐
│ [Featured Badge] 2 hrs ago │ ← listing.featuredUntil | createdAt
├─────────────────────────────────┤
│ $2.5B [SALE] [ACTIVE] │ ← priceVND | transactionType | status
│ 75 m² • 2BR • 1BA • Q1, HCMC │ ← areaM2 | bedrooms | bathrooms | district
├─────────────────────────────────┤
│ 👁️ 342 ❤️ 28 💬 12 │ ← viewCount | saveCount | inquiryCount
├─────────────────────────────────┤
│ Agent: John | ⭐ 4.8 │ ← agent.user.fullName | agent.qualityScore
└─────────────────────────────────┘
```
**API Fields**:
- Price: `listing.priceVND` → format as VND
- Type/Status: `listing.transactionType`, `listing.status`
- Area: `property.areaM2`
- Bedrooms: `property.bedrooms` (null = studio)
- Bathrooms: `property.bathrooms`
- District: `property.district`
- Metrics: `listing.viewCount`, `listing.saveCount`, `listing.inquiryCount`
- Agent: `listing.agent.user.fullName`, `listing.agent.qualityScore`
**Endpoint**: `GET /search` or search index
---
### Detail Page (Hero Section)
```
Main Image Carousel
┌────────────────────────────────────────────────┐
│ │
│ Apartment in District 1 for $2.5B │
│ 75 m² | 2BR 1BA | ACTIVE │
│ │
│ ⭐ 4.8 (234 views) | 28 saves | 12 inquiries │
│ │
│ [Agent: John] [Verified] [License: xxx] │
└────────────────────────────────────────────────┘
```
**API Fields** (from Listing + Property):
- Title: `property.title`
- Price: `listing.priceVND`
- Area: `property.areaM2`
- Beds/Baths: `property.bedrooms`, `property.bathrooms`
- Status: `listing.status`
- Views: `listing.viewCount`
- Saves: `listing.saveCount`
- Inquiries: `listing.inquiryCount`
- Agent Name: `listing.agent.user.fullName`
- Agent Verified: `listing.agent.isVerified`
- License: `listing.agent.licenseNumber`
**Endpoint**: Individual listing API or detail endpoint
---
### Property Details Panel
```
LOCATION
├─ Address: 123 Nguyen Hue, District 1, HCMC
├─ Lat/Lng: 10.7769, 106.7009
├─ Metro Distance: 450m
└─ Project: [Project Name if applicable]
PHYSICAL
├─ Type: APARTMENT
├─ Area: 75 m²
├─ Usable Area: 70 m²
├─ Bedrooms: 2 (or Studio if -1)
├─ Bathrooms: 1
├─ Floors: 25 total, Unit on Floor 12
├─ Direction: NORTHEAST
├─ Year Built: 2020
└─ Legal: Sở hữu lâu dài
AMENITIES
├─ Furnishing: FULLY_FURNISHED
├─ Condition: LIKE_NEW
├─ Parking: 1 slot
├─ Maintenance Fee: 2.5M/month
├─ Pet Friendly: Yes
├─ Views: Street, Park
└─ Suitable For: Young couples, Families
```
**API Fields** (all from `property`):
- Address: `address`, `ward`, `district`, `city`
- Coordinates: `location.lat`, `location.lng`
- Metro: `metroDistanceM`
- Project: `projectName`, `projectDevelopmentId`
- Type: `propertyType`
- Sizes: `areaM2`, `usableAreaM2`
- Rooms: `bedrooms`, `bathrooms`
- Levels: `floor`, `totalFloors`
- Direction: `direction`
- Year: `yearBuilt`
- Legal: `legalStatus`
- Furnishing: `furnishing`
- Condition: `propertyCondition`
- Parking: `parkingSlots`
- Fee: `maintenanceFeeVND`
- Pet: `petFriendly`
- Views: `viewType[]`
- Suitable: `suitableFor[]`
---
## 📊 Analytics & Market Data
### Market Snapshot Widget
```
HCMC Market Overview (Last Updated: 2 hours ago)
┌─────────────────────────────┐
│ Active Listings 2,345 │ ← activeCount
│ Avg Price $2.8B │ ← avgPrice (format)
│ Median Price $2.5B │ ← medianPrice (format)
│ Price/m² $35M │ ← avgPricePerM2
│ Avg Days on Mkt 45d │ ← daysOnMarket
│ New (24h) 12 │ ← newListings24h
│ │
│ Price Change (24h) +2% │ ← priceChangePct.d1
│ Price Change (7d) +5% │ ← priceChangePct.d7
│ Price Change (30d) +12% │ ← priceChangePct.d30
└─────────────────────────────┘
Cache: Next update in 25 min ← nextRefreshAt
```
**Endpoint**: `GET /analytics/market-snapshot?city=HCMC`
**Response Fields** (`MarketSnapshotDto`):
- `activeCount`
- `avgPrice` → convert string to number for display
- `medianPrice`
- `avgPricePerM2`
- `daysOnMarket`
- `newListings24h`
- `priceChangePct.d1/d7/d30` → percentage values
- `nextRefreshAt` → show "Updates in X mins"
---
### Price Trend Chart (Line)
```
Price Over Time (Q1 2026 - Q2 2026)
$4B ┌─────╮
│ │
$3B │ ╰──────
$2B └──────────
Q1 Q2 Q3
```
**Endpoint**: `GET /analytics/price-trend?district=Quận%201&city=HCMC&propertyType=APARTMENT`
**Response Fields** (`PriceTrendDto`):
- `trend[]`:
- `period` → x-axis label (e.g., "Q1 2026")
- `medianPrice` → y-axis value (parse from string)
- `avgPriceM2` → optional secondary axis
- `totalListings` → tooltip data
---
### Heatmap (District Color Intensity)
```
[District] [Price/m²] [Listings] [Median Price]
├─ D1 $45M 234 $2.8B
├─ D2 $38M 412 $2.2B
└─ D3 $28M 567 $1.8B
→ Use avgPriceM2 for color intensity
```
**Endpoint**: `GET /analytics/heatmap?city=HCMC&period=2026-04`
**Response Fields** (`HeatmapDto`):
- `dataPoints[]`:
- `district` → overlay label
- `avgPriceM2` → color intensity (higher = redder)
- `totalListings` → hover tooltip
- `medianPrice` → detail view
---
### District Stats Table
```
District Median Price Price/m² Listings Days Market YoY Change
├─ D1 $2.8B $45M 234 42d +12%
├─ D2 $2.2B $38M 412 48d +8%
└─ D3 $1.8B $28M 567 52d +5%
```
**Endpoint**: `GET /analytics/district-stats?city=HCMC&period=2026-04`
**Response Fields** (`DistrictStatsDto`):
- `districts[]`:
- `district` → row label
- `medianPrice` → format as VND
- `avgPriceM2`
- `totalListings`
- `daysOnMarket`
- `yoyChange` → percentage
---
### Trending Areas Widget
```
🔥 Trending (Last 30 Days)
┌────────────────────────────────────┐
│ 1. District 1 │ ← scoreRank
│ 📊 12 new | 45 inquiries │ ← listings | inquiries
│ 👁️ 234 views | ↑5% price │ ← views | priceChangePct
│ │
│ 2. District 5 │
│ 📊 8 new | 32 inquiries │
│ 👁️ 156 views | ↑2% price │
│ │
│ 3. District 9 │
│ 📊 15 new | 28 inquiries │
│ 👁️ 189 views | ↓1% price │
└────────────────────────────────────┘
Score = inquiries×0.6 + views×0.3 + listings×0.1
```
**Endpoint**: `GET /analytics/trending-areas?period=30&limit=10&level=district`
**Response Fields** (`TrendingAreasDto`):
- `areas[]`:
- `scoreRank` → ranking number
- `name` → district name (e.g., "Quận 1")
- `listings` → new listings count
- `inquiries`
- `views`
- `priceChangePct` → YoY change or null
---
## 💰 Valuations (AVM)
### Quick Estimate Card
```
PROPERTY VALUATION (by Coordinates)
Estimated Value: $2.5B
Confidence: 82% (Good)
Price/m²: $33.3M
Model: AVM v2 Ensemble
Comparable Sales (5 nearby):
├─ 234B (apt, 75m²) - 450m away
├─ 231B (apt, 78m²) - 520m away
└─ 238B (apt, 72m²) - 380m away
```
**Endpoint**: `GET /analytics/valuation?propertyId=prop-123` OR `POST /analytics/valuation`
**GET Response** (`ValuationDto`):
- `estimatedPrice` → format as VND
- `confidence` → 0.0-1.0, convert to percentage
- `pricePerM2`
- `modelVersion`
- `comparables[]`:
- `priceVND`
- `areaM2`
- `distanceMeters` → convert to distance label
**POST Request**:
```json
{
"propertyType": "APARTMENT",
"area": 75,
"district": "Quận 1",
"city": "Hồ Chí Minh",
"bedrooms": 2,
"bathrooms": 1,
"yearBuilt": 2020,
"useV2": true,
"distanceToHospitalKm": 0.5,
"distanceToParkKm": 1.2,
"hasElevator": true,
"hasParking": true
}
```
---
### Valuation History Chart
```
Estimated Value Over 12 Months
$2.8B ┌─────╮
│ │
$2.5B │ ╰──────
$2.2B └──────────
Apr Aug Dec
```
**Endpoint**: `GET /analytics/valuation/history/prop-123?limit=50`
**Response Fields**:
- `history[]`:
- `valuedAt` → x-axis timestamp
- `estimatedPrice` → y-axis value (parse)
- `confidence` → tooltip
- `pricePerM2` → tooltip
- `modelVersion` → metadata
---
### Property Comparison Table
```
Property A Property B Property C
Value $2.5B $2.2B $2.8B
Conf 85% 78% 92%
/m² $33.3M $31.4M $35M
Model v2 Ensemble v2 Ensemble v1 Standard
[Comparables shown for each]
```
**Endpoint**: `POST /analytics/valuation/compare`
**Request**:
```json
{
"propertyIds": ["prop-a", "prop-b", "prop-c"]
}
```
**Response Fields**:
- `comparisons[]`:
- `propertyId`
- `address`, `district`, `areaM2`
- `valuation` (same as ValuationDto above)
---
## 🌟 Neighborhood & POIs
### Neighborhood Score Card
```
NEIGHBORHOOD SCORE
Overall: 78/100 ⭐⭐⭐⭐
├─ Education 85/100
├─ Healthcare 92/100
├─ Transport 78/100
├─ Shopping 85/100
├─ Greenery 65/100
└─ Safety 72/100
POI Summary:
├─ 3 Schools
├─ 5 Hospitals
├─ 2 Transit Stations
├─ 8 Shopping Centers
├─ 2 Parks
└─ 12 Restaurants
```
**Endpoint**: `GET /analytics/neighborhoods/Quận%201/score?city=HCMC` (PUBLIC)
**Response Fields** (`NeighborhoodScoreResult`):
- `totalScore` → 0-100
- `educationScore`, `healthcareScore`, `transportScore`, etc. → each 0-100
- `poiCounts` → map of category→count
- `calculatedAt` → timestamp
---
### Nearby POIs Map
```
Center: (10.7769, 106.7009)
POI Markers (within 2km):
🏫 School (450m)
Name: Saigon Star International School
Address: 123 Nguyen Hue, D1
🏥 Hospital (890m)
Name: Vinmec Hospital
Address: 458 Ly Thuong Kiet, D1
🛍️ Mall (1.2km)
Name: The Landmark 81
🚇 Metro (680m)
Name: Ben Thanh Station
```
**Endpoint**: `GET /analytics/pois/nearby?lat=10.7769&lng=106.7009&radius=2000&limit=30` (PUBLIC)
**Response Fields** (`NearbyPOIsResultDto`):
- `center`: `{lat, lng}`
- `pois[]`:
- `name`
- `type` → SCHOOL, HOSPITAL, METRO_STATION, MALL, PARK, RESTAURANT, etc.
- `category` → school | hospital | transit | shopping | restaurant | park
- `lat`, `lng` → for map markers
- `distance` → meters from center
- `address`
---
## 👨‍💼 Agent Profile
### Agent Card
```
┌─────────────────────────────┐
│ [Avatar] John Doe │
│ ⭐ 4.8/5 │ ← qualityScore
│ [Verified] [Licensed] │ ← isVerified | licenseNumber exists
│ │
│ License: RE-12345 │ ← licenseNumber
│ Agency: Saigon Realty │ ← agency
│ Response Time: 2.5 hours │ ← responseTimeAvg (seconds→hours)
│ Deals: 42 completed │ ← totalDeals
│ │
│ Service Areas: │
│ District 1, 3, 7, Binh Thanh│ ← serviceAreas[]
│ │
│ Bio: "10+ years experience" │ ← bio
│ │
│ [Message] [View Listings] │
└─────────────────────────────┘
```
**API Fields** (Agent + User):
- Avatar: `user.avatarUrl`
- Name: `user.fullName`
- Rating: `qualityScore` → convert 0-100 to 0-5 stars
- Verified Badge: `isVerified`
- License: `licenseNumber` (display if exists)
- Agency: `agency`
- Response Time: `responseTimeAvg` (seconds) → format as "X.X hours"
- Deals: `totalDeals`
- Service Areas: `serviceAreas[]` → join with comma
---
## 📋 Listing Management
### Listing Status Flow
```
DRAFT
PENDING_REVIEW ← re-moderation if edited while ACTIVE
ACTIVE ← publishedAt timestamp
RESERVED ← buyer offer
SOLD/RENTED ← transaction complete
Alternative: REJECTED → DRAFT (can re-submit)
Alternative: EXPIRED → DRAFT (can re-list)
```
**Status Colors**:
- DRAFT: Gray
- PENDING_REVIEW: Yellow
- ACTIVE: Green
- RESERVED: Blue
- SOLD: Dark Gray
- RENTED: Dark Gray
- EXPIRED: Orange
- REJECTED: Red
---
### Listing Engagement Metrics
```
VIEWS 342 ← listing.viewCount
SAVES 28 ← listing.saveCount
INQUIRIES 12 ← listing.inquiryCount
```
**Engagement Targets**:
- Views → increased each time listing is viewed
- Saves → increased when user bookmarks
- Inquiries → increased when buyer sends inquiry
---
## 💡 Common Conversions
### Price Formatting
```typescript
// Input: priceVND = "2500000000" (string)
const formatted = new Intl.NumberFormat('vi-VN', {
style: 'currency',
currency: 'VND'
}).format(Number(priceVND));
// Output: "2.500.000.000 ₫" or "$2.5B"
```
### Confidence to Stars
```typescript
// Input: confidence = 0.82 (0.0-1.0)
const stars = Math.round(confidence * 5); // 0-5 stars
const label = confidence > 0.8 ? "High" : confidence > 0.6 ? "Good" : "Low";
```
### Response Time
```typescript
// Input: responseTimeAvg = 9000 (seconds)
const hours = (responseTimeAvg / 3600).toFixed(1);
// Output: "2.5 hours"
```
### Distance Display
```typescript
// Input: distanceMeters = 890
const label = distanceMeters < 1000
? `${distanceMeters}m`
: `${(distanceMeters/1000).toFixed(1)}km`;
```
---
**Last Updated**: April 2026