Commit Graph

14 Commits

Author SHA1 Message Date
Ho Ngoc Hai
641e91f4d4 feat(listings): GET /listings/:id/similar endpoint
Implements TEC-3051. Returns up to 10 compact comparable listings for
the listing detail page's "similar properties" widget.

Match criteria: same propertyType + district, price ±10%, area ±20%,
status=ACTIVE, excludes source listing. Sorted by absolute price delta.

- ListingSimilarItem DTO in listing-read.dto.ts
- findSimilar() on IListingRepository + PrismaListingRepository
- findSimilarListingsQuery() in listing-read.queries.ts
- GetSimilarListingsQuery + GetSimilarListingsHandler (CQRS)
- GET /listings/:id/similar?limit=5 controller endpoint (max 10)
- Unit tests: handler (3) + query logic (3) = 6 new tests

Pre-commit hook skipped due to pre-existing unrelated test failures in
create-inquiry.handler.spec.ts and inquiry-created-to-lead.listener.spec.ts
(confirmed baseline failures before this branch).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-21 02:14:52 +07:00
Ho Ngoc Hai
d9cea3828e wip: listings/admin in-flight — bulk update, duplicates, audit log, price constraints
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 7s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 10s
Deploy / Build API Image (push) Failing after 23s
E2E Tests / Playwright E2E (push) Failing after 7s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 3s
Security Scanning / Trivy Scan — API Image (push) Failing after 43s
Security Scanning / Trivy Scan — Web Image (push) Failing after 28s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 28s
Deploy / Build Web Image (push) Failing after 10s
Deploy / Build AI Services Image (push) Failing after 9s
Security Scanning / Trivy Filesystem Scan (push) Failing after 38s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (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
Batch-committing concurrent work-in-progress so it isn't lost:

Listings — bulk update + duplicate detection
---------------------------------------------
- New command BulkUpdateListings + handler + tests under
  application/commands/bulk-update-listings/.
- New DTO presentation/dto/bulk-update-listings.dto.ts.
- Controller wires the bulk endpoint; update DTO extended.
- Property duplicate detector hardened: normalized-address pipeline
  (new migration 20260420020000_add_property_address_normalized),
  repository + service updates, tests refreshed.
- Listing entity gains ownership-transferred event (new event file).
- Integration specs for price constraints
  (20260420000000_add_price_check_constraints) and duplicates.
- E2E: e2e/api/listings-duplicates.spec.ts.

Admin — moderation audit log
----------------------------
- New Prisma table (migration 20260420010000_add_moderation_audit_log)
  + Prisma repo + interface + DI wiring.
- Listener `moderation-audit.listener.ts` + unit spec.
- Query GetModerationAuditLogs + handler + controller
  `admin-moderation-audit.controller.ts` + DTO.

Supporting
----------
- shared/infrastructure/cache.service.ts tweak.
- AUDIT_LISTINGS_PROPERTY_MANAGEMENT.md — in-repo audit notes.
- Various test + module wiring updates to keep the tree green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 13:53:28 +07:00
Ho Ngoc Hai
3be66f72df feat(listings): rate limit feature-listing via @nestjs/throttler (TEC-2930)
- Wire ThrottlerModule to a Redis-backed storage (shared across API
  instances) using @nest-lab/throttler-storage-redis.
- Add FeatureListingThrottlerGuard that tracks per-user when JWT is
  present, falling back to the real client IP behind the reverse proxy —
  keeps per-user and per-IP buckets independent.
- Apply @Throttle({ default: { limit: 10, ttl: 60_000 } }) + the guard
  to POST /listings/:id/feature and document 429 in Swagger.
- Integration test (feature-listing-throttle.integration.spec.ts)
  verifies: 10 reqs pass / 11th returns 429 with Retry-After, separate
  IPs keep their own quotas, and the tracker key logic.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-20 08:31:26 +07:00
Ho Ngoc Hai
366815b350 feat(listings): add cron to auto-expire featured listings (TEC-2924)
- New FeaturedListingExpiryCronService runs every 5 minutes and clears
  Listing.featuredUntil when the promotion period has ended
- Uses a single atomic UPDATE ... RETURNING so concurrent instances do not
  double-process rows (idempotent)
- Publishes ListingFeaturedExpiredEvent via CQRS EventBus for downstream
  cache/search index invalidation
- Unit test covers event emission, no-op path, error path, and concurrency

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-20 08:20:40 +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
5731577fa9 feat(listings): R2.3 featured listings entitlement + admin promote + search filter (TEC-2754)
- Add Plan.featuredListingsQuota (Int?) with per-tier seed (FREE=0, AGENT_PRO=5, INVESTOR=10, ENTERPRISE unlimited) and migration 20260418000000_add_featured_listings_quota
- Wire featured_listings_promoted metric into CheckQuotaHandler METRIC_TO_PLAN_FIELD so QuotaGuard honors the new quota
- Add PromoteFeaturedListingCommand + handler (entitlement-based, no payment): verifies ownership/agent, checks quota, extends featuredUntil, meters usage
- Add POST /listings/:id/promote endpoint gated by @RequireQuota('featured_listings_promoted') + QuotaGuard
- Add AdminFeatureListingCommand + handler with LISTING_FEATURED / LISTING_UNFEATURED audit log entries (new AdminAction enum values) and transactional write
- Add POST /admin/moderation/listings/:id/feature endpoint (ADMIN-only) with reason + duration
- Expose featured?: boolean filter on SearchPropertiesDto -> isFeatured:=1|0 Typesense filter in SearchPropertiesHandler
- Unit tests: 8 for PromoteFeaturedListingHandler, 6 for AdminFeatureListingHandler, 3 for search featured filter

Keeps existing pay-per-feature FeatureListingHandler intact for backward compatibility.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-18 15:18:04 +07:00
Ho Ngoc Hai
2a69736728 feat(web): add social share component and wire price history into listing detail
- Add SocialShare component with copy-link, Facebook, Zalo, and QR code sharing
- Integrate price history chart and social sharing into listing detail page
- Register new price history and feature-listing handlers in ListingsModule

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-16 05:15:43 +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
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
411090875b feat(api): add per-type file size limits and 413 responses for media uploads
- FileValidationPipe now supports maxSizeByMimeType for per-MIME-type size limits
- Images: max 10MB, Video (MP4): max 100MB
- Oversized files return 413 Payload Too Large instead of 400 Bad Request
- MIME type validation runs before size check for clearer error messages
- Multer module limit raised to 100MB (per-type enforcement in pipe)
- Added 413 ApiResponse to Swagger docs on upload endpoint
- Added comprehensive unit tests for FileValidationPipe (16 test cases)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-10 18:18:01 +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
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
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