Commit Graph

5 Commits

Author SHA1 Message Date
Ho Ngoc Hai
33a5ff407b feat(auth): add DEVELOPER + PARK_OPERATOR roles with owner scoping (B2B accounts)
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 16s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 50s
Deploy / Build API Image (push) Failing after 25s
Deploy / Build Web Image (push) Failing after 11s
Deploy / Build AI Services Image (push) Failing after 10s
E2E Tests / Playwright E2E (push) Failing after 12s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 4s
Security Scanning / Trivy Scan — API Image (push) Failing after 1m16s
Security Scanning / Trivy Scan — Web Image (push) Failing after 1m2s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 50s
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 0s
Deploy / Rollback Production (push) Has been skipped
Deploy / Rollback Staging (push) Failing after 10m50s
Two new B2B roles for CĐT (project developers) and KCN operators, provisioned by
admin. Each account owns a subset of ProjectDevelopment / IndustrialPark records
and can CRUD them from the dashboard; admin retains full access.

Phase 1 — Schema
- Extend UserRole enum with DEVELOPER + PARK_OPERATOR (before ADMIN)
- ProjectDevelopment.ownerId FK (User, ON DELETE SET NULL) + index
- IndustrialPark.ownerId FK + index
- Migration 20260420030000

Phase 2a — Backend authorization
- CreateProjectCommand + CreateIndustrialParkCommand accept ownerId; controllers
  auto-set it to the caller's user id when role=DEVELOPER / PARK_OPERATOR
- Update + Delete commands gain (requesterUserId, requesterRole) and enforce
  ADMIN-or-owner via ForbiddenException; reassigning ownerId is admin-only
- Search params gain optional ownerId filter wired through Prisma repos
- New endpoints: GET /projects/mine/list, GET /industrial/parks/mine/list
- user-rate-limit guard: add DEVELOPER + PARK_OPERATOR entries (300/window)

Phase 2b — Admin provision
- ProvisionDeveloperCommand/Handler: create user (role=DEVELOPER), pre-validate
  target projects have no existing owner, batch-assign ownerId
- ProvisionParkOperatorCommand/Handler: same for PARK_OPERATOR + IndustrialPark
- POST /admin/accounts/developers, POST /admin/accounts/park-operators (admin-only)
- DTOs with phone/password/fullName/email + optional {project,park}Ids[]

Phase 2c — Project stats for developer dashboard
- GetProjectStatsQuery + handler: aggregates linkedListingCount, activeListingCount,
  totalInquiries, unreadInquiries, savedByUsers via Property → Listing → Inquiry chain
- GET /projects/:id/stats — admin sees all, DEVELOPER only their own (403 otherwise)

Phase 3 — Frontend
- Dashboard layout role-aware: DEVELOPER sees "Dự án của tôi" + CRM + Profile (hides
  listings/analytics/subscription); PARK_OPERATOR sees "KCN của tôi" equivalent
- /projects dashboard page switches to duAnApi.searchMine() when role=DEVELOPER
- /industrial-parks page switches to industrialApi.searchMine() when role=PARK_OPERATOR
- Admin nav gains "Tài khoản CĐT" + "Tài khoản KCN" entries
- New pages /admin/accounts/developers + /admin/accounts/park-operators with
  checkbox-based multi-select for linking entities
- adminApi.provisionDeveloper + provisionParkOperator + types
- duAnApi.searchMine + getStats; industrialApi.searchMine
- Login demo accounts list includes CĐT Vingroup + KCN VSIP

Phase 4 — Seed (prisma/seed-b2b-accounts.ts)
- DEVELOPER "CĐT Vingroup" (+84912000001) owns 4 projects
- DEVELOPER "CĐT Masterise Homes" (+84912000003) owns 2 projects
- PARK_OPERATOR "Vận hành KCN VSIP" (+84912000002) owns 2 seeded KCN
- Password Velik@2026 for all

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 22:12:16 +07:00
Ho Ngoc Hai
6b783c357d feat(listings+projects): wire listing PATCH + project rich content parity
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 10s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 2s
Security Scanning / Trivy Scan — API Image (push) Failing after 28s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 37s
Deploy / Build API Image (push) Failing after 12s
Deploy / Build Web Image (push) Failing after 10s
Deploy / Build AI Services Image (push) Failing after 9s
E2E Tests / Playwright E2E (push) Failing after 9s
Security Scanning / Trivy Scan — Web Image (push) Failing after 38s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 38s
Security Scanning / Trivy Filesystem Scan (push) Failing after 28s
Deploy / Deploy to Production (push) Has been skipped
Security Scanning / Security Gate (push) Failing after 1s
Two CRUD/parity gaps closed:

Listings edit — PATCH was dead-ended at the frontend
----------------------------------------------------
Backend PATCH /listings/:id existed and accepted Phase B fields but
the dashboard edit page was read-only with a disclaimer stub. Now:
- listings-api.ts exports UpdateListingPayload (Partial<CreatePayload>)
  and listingsApi.update(id, data).
- /listings/[id]/edit/page.tsx wires handleSubmit → maps the form to
  UpdateListingPayload (coerces numerics, splits CSV amenities/view/
  suitableFor, normalises petFriendly 3-way select), calls update,
  shows green success banner or red error banner. Removed the
  disclaimer text.
- Form footer now has Huỷ + Lưu thay đổi buttons.

Projects rich content — parity with Phase B listings
---------------------------------------------------
Same "Phù hợp với ai / Vì sao nên chọn dự án này" pattern now on
project detail.

Schema
- ProjectDevelopment: suitableFor String[] @default([]) +
  whyThisLocation String? @db.Text. Migration 20260419100000 applied
  via db:push.

Backend
- CreateProjectDto / UpdateProjectDto pick up optional suitableFor +
  whyThisLocation (MaxLength 2000).
- CreateProjectCommand / UpdateProjectCommand append the two trailing
  args; handlers forward them.
- ProjectDevelopment entity carries the props + updateDetails
  branches.
- ProjectListItem (inherited by ProjectDetailData) exposes both.
- Prisma repo writes them on raw INSERT/UPDATE and reads them in
  toDomain + toListItem. Controller passes dto → commands.

Frontend
- du-an-api.ts: ProjectDetail / CreateProjectPayload /
  UpdateProjectPayload gain suitableFor + whyThisLocation. duAnApi
  exports create / update / delete (already landed earlier, now in
  sync with the new fields).
- du-an-server.ts normalizer pulls the two fields safely (filter
  strings, default empty array / null).
- Dashboard /projects/new + /projects/[id]/edit: new "Phù hợp & lý
  do khu vực" form section (CSV split + 2000-char textarea). Submit
  handlers forward to create/update payloads.
- Public /du-an/[slug] detail (du-an-detail-client.tsx): two new
  cards just below the quick-stats grid —
  * ProjectPersonaFitCard: chips for each suitableFor label with a
    "Chủ đầu tư chọn" badge (bg-primary/10), plus a disabled
    <Button><Sparkles /> AI nhận định dự án (sắp ra mắt)</Button>
    teaser with a TODO pointing to a future project-AI advisor
    endpoint.
  * ProjectWhyLocationCard: renders whyThisLocation in
    whitespace-pre-wrap; skipped when the field is empty.

Verification
- API typecheck clean; 1975/1975 tests pass.
- Web typecheck clean in touched files; 624/624 tests pass.
- Lucide-only icons; Vietnamese labels; no new npm packages;
  runtime imports preserved for NestJS-DI classes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:33:54 +07:00
Ho Ngoc Hai
dfc01c3bee fix: align Project status enum to Prisma + cascade child records on listing delete
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 10s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 31s
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 34s
Security Scanning / Trivy Scan — Web Image (push) Failing after 23s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 25s
Security Scanning / Trivy Filesystem Scan (push) Failing after 26s
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Build API Image (push) Failing after 16s
Deploy / Build Web Image (push) Failing after 9s
Deploy / Build AI Services Image (push) Failing after 8s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Security Scanning / Security Gate (push) Failing after 0s
Deploy / Rollback Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
Project status was declared on the frontend as
UPCOMING/SELLING/HANDOVER/COMPLETED but the Prisma enum
ProjectDevelopmentStatus is PLANNING/UNDER_CONSTRUCTION/HANDOVER/
COMPLETED — CREATE failed with "status must be one of …". Aligned the
TypeScript union + PROJECT_STATUS_LABELS/COLORS, filter options on
/projects list, and both new + edit forms. Updated the
normalizeProjectDetail fallback and the du-an test spec to match.

Listings DELETE was blocked by FK references (Inquiry, SavedListing,
PriceHistory, Order, Transaction have no onDelete: Cascade in schema).
Wrapped the Prisma listing delete in a $transaction that removes the
child rows first, then the listing itself, so CRUD from the dashboard
actually lands instead of returning "Referenced record does not exist".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 13:36:46 +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
e21e096e54 feat(web): complete du-an project pages, neighborhood components, and public notification bell
- Add grid/map view toggle on /du-an listing page with Mapbox project markers
- Enhance du-an detail with master plan viewer, neighborhood radar chart, POI map, and price history chart
- Create neighborhood component suite: radar chart (Recharts), POI map (Mapbox), score badges
- Add du-an API client, server-side fetching, and React Query hooks
- Wire NotificationBell into public layout header for authenticated users
- Fix missing PROJECT_STATUS_COLORS import in du-an detail client

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-16 05:11:21 +07:00