c15bdcc6bff194ceb56ba996285a4465c2f60098
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c15bdcc6bf |
fix(industrial): improve OSM review UX + public map visibility
Some checks failed
CI / E2E Tests (push) Has been skipped
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 9s
CI / AI Services (Python) — Smoke (push) Failing after 7s
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 1m7s
Deploy / Build API Image (push) Failing after 16s
Deploy / Build Web Image (push) Failing after 6s
Deploy / Build AI Services Image (push) Failing after 7s
E2E Tests / Playwright E2E (push) Failing after 15s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 5s
Security Scanning / Trivy Scan — API Image (push) Failing after 1m13s
Security Scanning / Trivy Scan — Web Image (push) Failing after 49s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 40s
Security Scanning / Trivy Filesystem Scan (push) Failing after 40s
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
Four UX issues surfaced when reviewing the new OSM-sync pipeline against the actual 2,193 imports — fixed in this commit: 1. Admin queue surfaced noise first. `ListOsmPendingHandler` now sorts by `totalAreaHa DESC` (real KCN first, single-factory `landuse=industrial` polygons last) and accepts `minAreaHa` (default 50 ha) plus a `region` filter. The admin page exposes both as dropdowns — "Tất cả / ≥ 5 / ≥ 50 / ≥ 200 / ≥ 500 ha". Top-of-queue is now Bàu Bàng (2,597 ha) and Nhơn Trạch (2,535 ha). 2. Promote dialog said "KCN KCN Đại An" — duplicate prefix. Reworded to "Sắp promote: <name>" so the row name stands on its own. 3. Province was "Chưa xác định" on 2,107 of 2,193 OSM rows. The OSM tags lacked any addr:* hint, so the importer never had anything to write. Added `scripts/data/vn-province-centroids.ts` (63 provinces with capital-city coords) and a `nearestProvince(lat, lng)` fallback in `parseFeature()`. Shipped a one-shot backfill script `scripts/backfill-osm-provinces.ts` and ran it — every existing OSM row now has a province (Hồ Chí Minh: 408, Lạng Sơn: 232, Quảng Ninh: 220, Hà Nội: 172, Hải Phòng: 105, …). Admin can correct on promote if the nearest-centroid heuristic picked the wrong neighbour for a long-thin province. 4. Public map looked empty — only 20 curated parks visible. Added an opt-in toggle "Hiển thị KCN OSM" with a small legend above the map. When on, the bbox endpoint returns OSM raw rows too; markers render in amber (vs. green for curated) at slightly smaller radius and lower opacity, so the visual hierarchy stays clear. Refetch is wired through a ref so the toggle takes effect without remounting the map. Verified in browser preview: zoom-out shows clusters of 320 / 71 / etc. across the country with the toggle on, and just three small clusters (20 curated parks) when off. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
b3143991ce |
feat(industrial): OSM bulk import + bbox map + admin review (PR 2-4/4)
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 10s
CI / E2E Tests (push) Has been skipped
CI / AI Services (Python) — Smoke (push) Failing after 4s
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 49s
Deploy / Build API Image (push) Failing after 9s
Deploy / Build Web Image (push) Failing after 4s
Deploy / Build AI Services Image (push) Failing after 6s
E2E Tests / Playwright E2E (push) Failing after 8s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 3s
Security Scanning / Trivy Scan — API Image (push) Failing after 51s
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 / Trivy Scan — Web Image (push) Failing after 44s
Security Scanning / Trivy Filesystem Scan (push) Has been cancelled
Security Scanning / Security Gate (push) Has been cancelled
Security Scanning / Trivy Scan — AI Services Image (push) Has started running
Pulls every `landuse=industrial` feature from OpenStreetMap into the
IndustrialPark catalog and surfaces it on the public KCN map. Admins can
promote raw OSM rows into the public catalog or lock individual fields
to protect them from the monthly reconciliation sync.
PR 2 — Bulk import script (scripts/sync-osm-industrial-parks.ts):
• Splits Vietnam into 4 chunks (north / northCentral / southCentral /
south) to stay under Overpass 504 timeouts.
• Posts to overpass-api.de with form-encoded body, converts via
osmtogeojson, derives centroid + area via @turf/centroid + @turf/area.
• Upsert keyed on osmId. Honours `osmLocked` (skip row entirely) and
`lockedFields[]` (skip individual columns) so admin edits survive.
• Inserts use $executeRawUnsafe with ST_SetSRID(ST_MakePoint, 4326)
because Prisma can't manage the Unsupported geometry NOT NULL column.
• CLI flags: --dry-run, --chunk=NAME.
PR 3 — Bbox spatial API + Mapbox layer:
• GET /industrial/parks/by-bbox returns a GeoJSON FeatureCollection
filtered by ST_MakeEnvelope. Sends Point-only at zoom < 12,
MultiPolygon outline at zoom >= 12 to keep payloads light.
• Public consumers see MANUAL + OSM_PROMOTED only; admins can pass
includeOsmRaw=true to also see raw OSM imports.
• OsmParkBboxMap component drives Mapbox from viewport moveend with
AbortController-debounced fetches, clusters at zoom < 12, expands
via getClusterExpansionZoom (callback-style API).
• /khu-cong-nghiep page now uses the bbox map in map + split views.
PR 4 — Admin review queue + monthly cron:
• Commands: PromoteOsmPark (OSM → OSM_PROMOTED + isPublic=true,
optional lockFields), LockOsmPark (toggle row-level skip flag).
• Query: ListOsmPending lists rows with dataSource='OSM' for review.
• OsmSyncCronService runs `0 2 1 * *` Asia/Ho_Chi_Minh and spawns
sync-osm-industrial-parks.ts per chunk. Skipped unless
OSM_SYNC_ENABLED=true so dev never accidentally hits Overpass.
• New admin page /admin/industrial/osm-review: searchable table,
promote dialog with quick-pick lock fields (name, developer,
description, etc.) plus a free-text fallback, lock/unlock toggle,
deep-link to openstreetmap.org for verification.
Repository changes:
• PrismaIndustrialParkRepository now filters public queries to
`isPublic = true AND dataSource IN (MANUAL, OSM_PROMOTED)` so raw
OSM rows stay hidden from end users.
• Added *.rdb to .gitignore (Redis dump local artefact).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
36a9b00cf1 |
feat(industrial): update TypeScript types for Float→Decimal USD field migration (GOO-27)
Migration SQL (20260422120000_industrial_usd_to_decimal) and Prisma schema already reflected Decimal(18,4). This commit completes the TypeScript / frontend layer. API changes: - Domain repo interfaces (IndustrialListingListItem, IndustrialListingDetailData, IndustrialParkListItem, IndustrialParkDetailData, IndustrialMarketData): USD money fields changed from number|null → string|null (PostgreSQL numeric serialises as string in raw query results) - Raw DB interface types in Prisma repositories updated to string|null for Decimal columns - toDomain() mappers: parseFloat() added where entity props require number|null for business-logic arithmetic - estimate-industrial-rent handler: Number() cast on Prisma ORM Decimal objects before arithmetic and comparisons Web changes: - khu-cong-nghiep-api.ts: IndustrialParkListItem, IndustrialParkDetail, IndustrialListingItem, IndustrialMarketData USD fields → string|null with JSDoc - listing-card.tsx: parseFloat() wrapping for priceUsdM2/totalLeasePrice display - park-compare-client.tsx: parseFloat() for landRentUsdM2Year in radar score Note: pre-existing test failures in filter-bar/login/search specs are unrelated to this migration (confirmed present on branch before this change). Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
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>
|
||
|
|
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>
|
||
|
|
5810f0be56 |
feat(web): add industrial compare page, listing search, and Mapbox park map
- Add interactive Mapbox map to /khu-cong-nghiep landing page with park markers and popups - Build compare page at /khu-cong-nghiep/so-sanh with recharts RadarChart and detailed comparison table - Build listing search page at /khu-cong-nghiep/cho-thue with filters for property type, lease type, area, and price - Add IndustrialListing types, API client functions, and React Query hooks Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
7ce651fce5 |
feat(web): add khu-cong-nghiep, chuyen-nhuong, and reports pages
Add three new frontend page sections: - Industrial parks (khu-cong-nghiep): listing, detail, filter bar - Transfer listings (chuyen-nhuong): search, category tabs, detail - AI reports dashboard: list, create, viewer with TOC Includes components, API clients, hooks, server helpers, i18n keys, navigation links in public and dashboard layouts, and lint fixes. Co-Authored-By: Paperclip <noreply@paperclip.ing> |