Commit Graph

24 Commits

Author SHA1 Message Date
Ho Ngoc Hai
88429a1e51 feat(listings): phase B — rich property fields + admin-authored personas
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 6s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 1m8s
Deploy / Build API Image (push) Failing after 29s
E2E Tests / Playwright E2E (push) Failing after 13s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 2s
Security Scanning / Trivy Scan — API Image (push) Failing after 1m9s
Security Scanning / Trivy Scan — Web Image (push) Failing after 37s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 1m2s
Security Scanning / Trivy Filesystem Scan (push) Failing after 51s
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Security Scanning / Security Gate (push) Failing after 1s
Deploy / Rollback Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
Deploy / Build Web Image (push) Failing after 14s
Deploy / Build AI Services Image (push) Failing after 12s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Schema (prisma/migrations/20260419000000_property_rich_fields)
--------------------------------------------------------------
New Prisma enums:
- Furnishing: FULLY_FURNISHED / BASIC_FURNISHED / UNFURNISHED
- PropertyCondition: NEW / LIKE_NEW / RENOVATED / USED

New Property columns (all optional / default empty, no data loss):
- furnishing, propertyCondition — enums above
- balconyDirection — reuses existing Direction enum
- maintenanceFeeVND BigInt (phí quản lý/tháng)
- parkingSlots Int
- viewType String[] (e.g. ["Sông","Thành phố"])
- petFriendly Boolean (null = unknown)
- suitableFor String[] — admin-chosen persona labels
- whyThisLocation Text — admin narrative

Backend wiring end-to-end
-------------------------
- Create/Update DTOs: @IsEnum/@IsString/@IsNumber/@IsBoolean/@IsArray
  validators; maintenanceFeeVND accepted as a numeric string, cast to
  BigInt on the way to Prisma. whyThisLocation capped at 2000 chars.
- Introduced a small `PropertyExtras` interface on the create/update
  commands so the constructor signature stays readable instead of
  ballooning to 30+ positional args. Handlers forward it to the repo.
- Prisma property repository writes all new columns via raw SQL
  INSERT/UPDATE and reads them on findById.
- ListingDetailData + findByIdWithProperty expose the 9 new fields
  (maintenanceFeeVND serialised as decimal string to avoid BigInt JSON).

Frontend
--------
- listings-api.ts: ListingDetail.property + CreateListingPayload carry
  the 9 new fields; Furnishing + PropertyCondition exported as string
  unions.
- validations/listings.ts: zod schema extended; FURNISHING_OPTIONS,
  PROPERTY_CONDITION_OPTIONS, VIEW_TYPE_OPTIONS label arrays added in
  the existing DIRECTIONS style (Vietnamese labels).
- listing-form-steps.tsx StepDetails: new "Nội thất & điều kiện"
  fieldset with selects/inputs for each field. viewType + suitableFor
  are comma-separated text (same convention as amenities).
  petFriendly is a 3-way select (không chọn / Có / Không).
- new/page.tsx + [id]/edit/page.tsx: submit handlers split CSV inputs
  into arrays, coerce petFriendly, prune empty selects.
- listing-detail-client.tsx Details card: new rows for furnishing,
  propertyCondition, balconyDirection, maintenanceFeeVND (VND
  formatted), parkingSlots, viewType (joined · ), petFriendly
  (Cho phép / Không cho phép / hide when null).
- PersonaFitCard now takes the listing directly and MERGES admin
  suitableFor (rendered first with a "Người đăng chọn" badge in primary
  accent) with the derived personas (deduped by label). When
  whyThisLocation is non-empty it overrides the derived narrative.

Tests
-----
- listing-detail-client.spec.tsx fixture gains all 9 nullable/empty
  defaults.
- listing-form-steps.spec.tsx direction-options duplication fixed.
- pnpm --filter @goodgo/api test --run: 1975/1975 pass.
- pnpm --filter @goodgo/web test --run: 624/624 pass.

Phase B of 4. Next: Phase E AI advisor via Anthropic Opus (URL+key to
be provided by the user).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 15:08:04 +07:00
Ho Ngoc Hai
6067adc095 feat(listings): phase A — surface usableAreaM2, floor/totalFloors, metroDistanceM
Some checks failed
E2E Tests / Playwright E2E (push) Failing after 9s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 3s
Security Scanning / Trivy Scan — API Image (push) Failing after 46s
Security Scanning / Trivy Filesystem Scan (push) Has been cancelled
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 9s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 1m18s
Deploy / Build API Image (push) Failing after 28s
Deploy / Build Web Image (push) Failing after 12s
Deploy / Build AI Services Image (push) Failing after 10s
Security Scanning / Trivy Scan — Web Image (push) Failing after 31s
Deploy / Deploy to Staging (push) Has been cancelled
Deploy / Smoke Test Staging (push) Has been cancelled
Deploy / Rollback Staging (push) Has been cancelled
Deploy / Smoke Test Production (push) Has been cancelled
Deploy / Rollback Production (push) Has been cancelled
Deploy / Deploy to Production (push) Has been cancelled
Security Scanning / Security Gate (push) Has been cancelled
Security Scanning / Trivy Scan — AI Services Image (push) Has started running
The Property table already stores usableAreaM2, floor, totalFloors,
metroDistanceM and nearbyPOIs but the listing detail endpoint was
dropping them. Add them to ListingDetailData + the Prisma read query,
mirror the additions on the frontend ListingDetail type, and render
them on the detail page:

- Quick-specs bar now shows "Tầng X / Y" (floor/totalFloors) with a
  sensible fallback to `floors`, plus "Cách metro" when populated.
- Details card adds rows: "Diện tích sử dụng", "Tầng / Tổng tầng"
  (merges floor + totalFloors), "Cách metro gần nhất" (formatted m/km).
- New "transit" icon for the metro stat.

Purely additive surfacing — no schema change, no migration. Listings
missing these fields still render as before.

Test fixture in listing-detail-client.spec.tsx extended with the new
nullable fields so the type stays compatible.

Phase A of 4 (Listings detail enhancement plan).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 14:41:17 +07:00
Ho Ngoc Hai
ba0bf97426 feat: dashboard CRUD for Projects + Industrial Parks, listings delete, BĐS homepage card
Some checks failed
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 1m15s
Deploy / Build API Image (push) Failing after 20s
Deploy / Build AI Services Image (push) Failing after 12s
E2E Tests / Playwright E2E (push) Failing after 16s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 35s
Security Scanning / Trivy Filesystem Scan (push) Failing after 30s
Backup Verification / Backup Restore Verification (push) Failing after 14m37s
Security Scanning / Trivy Scan — API Image (push) Failing after 1m4s
Security Scanning / Trivy Scan — Web Image (push) Failing after 36s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 11m6s
Deploy / Build Web Image (push) Failing after 12s
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 8s
CI / E2E Tests (push) Has been skipped
Security Scanning / Security Gate (push) Has been cancelled
Backend — DELETE endpoints (hard delete, ADMIN or owner):
- DELETE /projects/:id (Admin) — new DeleteProjectCommand/Handler,
  repository.delete() adapter, module wiring.
- DELETE /industrial/parks/:id (Admin) — same pattern.
- DELETE /listings/:id (JWT + owner-or-Admin check in handler).

Frontend — API clients:
- lib/du-an-api.ts: add create/update/delete + CreateProjectPayload,
  UpdateProjectPayload types.
- lib/khu-cong-nghiep-api.ts: add createPark/updatePark/deletePark +
  Create/Update payload types.
- lib/listings-api.ts: add delete().

Dashboard pages — new:
- /projects (Quản lý dự án): list with filters + edit/delete actions,
  /projects/new form (sectioned Cards, zod-validated), /projects/[id]/edit
  with danger-zone delete.
- /industrial-parks (Quản lý KCN): same triad. Fix occupancy-rate display
  (percentage already 0-100, no need to *100).

Dashboard listings page:
- Add Edit/Delete row actions with confirm + useMutation; error banner
  on mutation failure. Table view gains a "Thao tác" column; list view
  gains a footer action bar below each card.

Dashboard nav:
- Catalog group: /du-an → /projects (Quản lý dự án), /khu-cong-nghiep
  → /industrial-parks (Quản lý KCN). Desktop primaryNav updated too.

Public homepage:
- Add "Bất động sản" as a 5th feature card/tab → /search, using
  listingsApi for the "Featured listings" section.
- Bump grid to lg:grid-cols-5, update features subtitle copy ("Năm/Five
  core services").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 10:37:33 +07:00
Ho Ngoc Hai
38b9def99a feat: implement project development module, transfer management features, and industrial AVM model integration 2026-04-18 20:34:35 +07:00
Ho Ngoc Hai
6cf2c23170 feat(listings): add source field to PriceHistory + unit tests
- Add `source` column to PriceHistory Prisma model (manual_update, admin_override, market_adjustment)
- Add migration for the new column with default 'manual_update'
- Update ListingPriceChangedEvent domain event with optional source parameter
- Update RecordPriceHistoryHandler to persist source
- Update GetPriceHistoryHandler to return source in query results
- Add unit tests for RecordPriceHistoryHandler (5 cases)
- Add unit tests for GetPriceHistoryHandler (3 cases)
- Add ListingPriceChangedEvent tests to domain events spec (4 cases)
- Add getPriceHistory controller tests (2 cases)

All 1805 tests pass, typecheck clean.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-16 17:43:48 +07:00
Ho Ngoc Hai
430c67f244 feat(listings): add featured boost to search and expose isFeatured in API responses
Featured listings now sort first in search results via featuredUntil desc ordering.
All listing read DTOs (detail, search, seller) include isFeatured boolean and featuredUntil timestamp.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-16 09:16:44 +07:00
Ho Ngoc Hai
62a8842193 feat(listings): complete PATCH /api/v1/listings/:id endpoint
- Add mediaOrder field to UpdateListingDto, Command, and Handler for
  reordering media items
- Add updateMediaOrder method to IPropertyRepository and Prisma impl
- Fix PrismaPropertyRepository.update() to persist amenities, nearbyPOIs,
  floors, floor, totalFloors, and metroDistanceM columns
- Add unit tests for media order updates in handler spec
- Add DTO validation tests for mediaOrder with nested validation
- Add e2e integration tests covering content updates, auth, ownership
  guard, and forbidden field rejection

Existing guards enforced:
- Only seller or assigned agent can update (403 for others)
- ACTIVE listings transition to PENDING_REVIEW on edit
- propertyType, address, location blocked via DTO whitelist

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-16 06:10:27 +07:00
Ho Ngoc Hai
d4e100a00c feat(api): add price history, Stringee SMS, Zalo OA, WebSocket notifications, and feature-listing command
- Add PriceHistory model + migration, price-changed domain event, and event handler
- Add GetPriceHistory query handler and controller endpoint
- Implement StringeeSmsService and ZaloOaService with unit tests
- Add Zalo ZNS templates for Vietnamese notification messages
- Add WebSocket notification gateway for real-time push
- Add FeatureListingCommand for promoted listings
- Apply remaining consistent-type-imports lint fixes across API modules

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-16 05:15:04 +07:00
Ho Ngoc Hai
c920934fb6 fix(lint): enforce consistent-type-imports and fix import ordering across codebase
Auto-fix 862 lint errors: convert value imports used only as types to
`import type`, fix import group ordering in seed.ts and du-an-api.ts,
remove unused imports in auth controller, and clean up stale eslint-disable
comments referencing non-existent rules.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-16 05:13:56 +07:00
Ho Ngoc Hai
ce781df76d fix(listings): extract PostGIS coordinates in read queries instead of returning 0,0
findByIdWithProperty and searchListings used Prisma include which cannot
extract PostGIS geometry(Point,4326) columns. Added raw SQL with ST_Y/ST_X
to return actual lat/lng. Search uses batch extraction via ANY() for efficiency.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-16 02:32:30 +07:00
Ho Ngoc Hai
50a0d739a7 fix: wire Nhắn tin button with InquiryModal on listing detail page
The messaging button on the listing detail page was inert — clicking
it did nothing. This commit completes the inquiry flow:

- Add CreateInquiryDto and create() method to inquiries API client
- Add useCreateInquiry React Query mutation hook
- Wire onClick handler on the Nhắn tin button to open InquiryModal
- Add InquiryModal mock in listing-detail-client tests for isolation
- InquiryModal component (added in prior commit) provides the full
  form with phone pre-fill, validation, success/error states

All 593 web tests pass.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-15 11:25:06 +07:00
Ho Ngoc Hai
20b79acf08 fix(deploy): tag rollback images before pull, prune after smoke test
Previously, `docker image prune` ran immediately after deploying new
containers, potentially deleting the old images needed for rollback
if smoke tests subsequently failed. Now the deploy pipeline:

1. Tags current images as :rollback before pulling new versions
2. Only runs `docker image prune` after smoke tests pass
3. Uses explicit :rollback tags for rollback instead of relying on
   Docker layer cache (which is fragile)

Applied to:
- scripts/deploy-production.sh (manual deploy script)
- .github/workflows/deploy.yml (staging + production CI jobs)
- docs/deployment.md (updated rollback documentation)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-15 11:17:32 +07:00
Ho Ngoc Hai
a9fa214544 feat: comprehensive seed, Lucide icons, grouped dashboard nav, API fixes
- Rewrite prisma/seed.ts to populate all 27 models with realistic
  Vietnamese real estate data (8 users with login, 10 properties,
  10 listings, orders, payments, reviews, notifications, etc.)
- Replace all emoji icons with Lucide React SVG icons across frontend
  for consistent rendering, sizing, and accessibility
- Redesign dashboard nav: grouped sidebar with section headers,
  primary/secondary split on desktop, icon-only secondary items
- Replace language switcher flag emoji with Globe icon
- Replace SVG theme toggle with Lucide Moon/Sun icons
- Fix API startup: graceful fallback for Sentry profiling, Google OAuth,
  and Zalo OAuth when credentials are not configured
- Relax rate limiting in development mode (10k req/min)
- Fix listings API to include media[] array in search response
- Add optional chaining for property.media across frontend components
- Update OAuth strategy tests to match graceful fallback behavior

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
2026-04-13 11:13:04 +07:00
Ho Ngoc Hai
25420720e7 fix(api,ci): remove type-only imports for DI and isolate CI ports from dev
- Remove `type` keyword from NestJS injectable class imports across all
  modules to fix runtime DI resolution (330+ handler/listener files)
- Offset CI docker-compose ports (5433/6380/8109/9002) to avoid
  conflicts with running dev containers
- Update .env.test, playwright.config.ts, and e2e workflow to use
  isolated CI ports with configurable overrides
- Fix prisma/seed.ts to use deterministic IDs for Prisma 7 upsert
  compatibility (phoneHash replaced phone as unique index)
- Add dedicated Docker bridge network for CI service containers

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
2026-04-13 01:40:14 +07:00
Ho Ngoc Hai
1aad9b9f95 test: increase test coverage for listings, auth, and search modules
Add 33 new test files to reach coverage targets:
- Listings: 13 → 28 test files (50%+)
- Auth: 21 → 36 test files (50%+)
- Search: 10 → 13 test files (59%+)

New tests cover domain entities, value objects, services, guards,
decorators, DTOs, repositories, controllers, and event handlers.
Total: 204 test files, 1178 tests passing.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 21:39:20 +07:00
Ho Ngoc Hai
862078df37 feat(web): add auth+search i18n translations and filter-bar accessibility
Add missing auth and search translation namespaces to vi.json and en.json
that are required by login/register pages and search filter-bar component.
Update filter-bar with useTranslations('search'), aria-labels, and
role="search" for WCAG 2.1 AA compliance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 10:22:59 +07:00
Ho Ngoc Hai
8179f1c16e feat(api): complete domain event publishing with aggregate root pattern
- Add getUncommittedEvents() and commit() to AggregateRoot base class
- Create 6 new domain events: SubscriptionExpired, SubscriptionRenewed,
  ListingStatusChanged, UserKycUpdated, UserDeactivated, PaymentRefunded
- Wire events into entity state changes: SubscriptionEntity (markExpired,
  renewPeriod), ListingEntity (all transitions), UserEntity (KYC, deactivate),
  PaymentEntity (markRefunded)
- Add 7 new event listeners across notifications, admin, and search modules
  (25 total @OnEvent handlers)
- Fix ReviewDeletedListener to handle LISTING target type
- Restore watcher notifications in ListingSoldListener
- Update barrel exports and module registrations

Resolves: TEC-1564

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 10:22:20 +07:00
Ho Ngoc Hai
c9fc1f52cb feat(listings): add price validator, moderation service, and improve handlers
Add domain-level price validator and moderation services with Prisma
implementation. Improve listing creation, status management, and media
upload handlers. Add price validator spec.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 09:43:06 +07:00
Ho Ngoc Hai
e0154a0105 fix: resolve lint errors — import deduplication, ordering, and test config
- Enable prefer-inline for import-x/no-duplicates to support barrel
  import patterns (value + type imports from same module)
- Inline duplicate type imports in middleware.ts and listing-form-steps.tsx
- Fix import ordering across API test files and MCP controller
- Add next-intl mock to search spec (FilterBar uses useTranslations)
- Exclude [locale] test duplicates from vitest (need proper i18n test setup)

All 801 tests passing (653 API + 119 web + 29 MCP). Zero lint errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 08:49:29 +07:00
Ho Ngoc Hai
62f4f001b6 test(api): add domain layer unit tests across all modules
Cover admin events, notifications, reviews, search VOs, listings (property,
media, events, price/geo/address VOs), auth events, payment events,
subscription events, and analytics events. Raises domain test coverage
from ~24% to ~75%.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-09 00:36:39 +07:00
Ho Ngoc Hai
6baa4707de feat(listings): implement listing duplicate detection service
Add DuplicateDetector domain service that flags potential duplicate listings
using PostGIS ST_DWithin geo-proximity (100m radius) combined with trigram-based
title similarity (>70% threshold). Detection runs during CreateListing but never
blocks creation — warnings are returned in the response for seller/admin review.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 14:21:49 +07:00
Ho Ngoc Hai
2502aa69b7 fix: production readiness — resolve build, lint, and code quality issues
- Fix Next.js build failure: remove duplicate route at (dashboard)/listings/[id]
  that conflicted with (public)/listings/[id] (same URL path in two route groups)
- Fix 772 ESLint errors: auto-fix import ordering (import-x/order), remove unused
  imports/variables, convert empty interfaces to type aliases, replace require()
  with ESM imports, fix consistent-type-imports violations
- Add CLAUDE.md for developer onboarding documentation
- All checks pass: 0 lint errors, typecheck clean, 230 tests passing, build success

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 07:15:06 +07:00
Ho Ngoc Hai
e9889539ea fix: eliminate untyped repository returns and standardize DomainException usage across all handlers
- Create typed DTOs (ListingDetailData, ListingSearchItem, ListingSellerItem) for repository read methods
- Replace all Promise<any> and PaginatedResult<any> with concrete types in repository interface and implementation
- Remove `as any` casts in search params by using Prisma enum types (TransactionType, PropertyType)
- Migrate all 16 handlers from NestJS built-in exceptions to domain exceptions (NotFoundException, ValidationException, etc.)
- Add CONTRIBUTING.md documenting error handling convention
- All 230 tests pass, typecheck clean

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 06:25:44 +07:00
Ho Ngoc Hai
8a33aae026 feat(listings): implement Listings module with CRUD, media upload, and moderation
Full DDD/CQRS implementation for the Listings module (TEC-1423):
- Domain: Property, Listing, PropertyMedia entities with status machine
- Value Objects: Address, GeoPoint, Price with validation
- Events: ListingCreated, ListingApproved, ListingSold
- Commands: CreateListing, UpdateListingStatus, UploadMedia, ModerateListing
- Queries: GetListing, SearchListings, GetPendingModeration
- Infrastructure: Prisma repositories with PostGIS support, MinIO media storage
- Presentation: REST controller with JWT auth, role-based moderation
- 21 domain unit tests (all passing)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-08 01:47:15 +07:00