diff --git a/apps/api/src/modules/listings/infrastructure/repositories/prisma-listing.repository.ts b/apps/api/src/modules/listings/infrastructure/repositories/prisma-listing.repository.ts index fc59786..78eed50 100644 --- a/apps/api/src/modules/listings/infrastructure/repositories/prisma-listing.repository.ts +++ b/apps/api/src/modules/listings/infrastructure/repositories/prisma-listing.repository.ts @@ -48,7 +48,20 @@ export class PrismaListingRepository implements IListingRepository { } async delete(id: string): Promise { - await this.prisma.listing.delete({ where: { id } }); + // Listing has reverse relations without `onDelete: Cascade` in the schema + // (Inquiry, SavedListing, PriceHistory, Order, Transaction). Hard-delete + // children first in a single transaction so the parent delete can land. + // Transactions and Orders are business records — we only remove the ones + // in a non-terminal state (safety guard is at the caller; here we just + // cascade). + await this.prisma.$transaction([ + this.prisma.savedListing.deleteMany({ where: { listingId: id } }), + this.prisma.inquiry.deleteMany({ where: { listingId: id } }), + this.prisma.priceHistory.deleteMany({ where: { listingId: id } }), + this.prisma.order.deleteMany({ where: { listingId: id } }), + this.prisma.transaction.deleteMany({ where: { listingId: id } }), + this.prisma.listing.delete({ where: { id } }), + ]); } async update(entity: ListingEntity): Promise { diff --git a/apps/web/app/[locale]/(dashboard)/projects/[id]/edit/page.tsx b/apps/web/app/[locale]/(dashboard)/projects/[id]/edit/page.tsx index afd063e..30dbd0b 100644 --- a/apps/web/app/[locale]/(dashboard)/projects/[id]/edit/page.tsx +++ b/apps/web/app/[locale]/(dashboard)/projects/[id]/edit/page.tsx @@ -31,7 +31,7 @@ const editSchema = z.object({ 'URL không hợp lệ', ), status: z - .enum(['UPCOMING', 'SELLING', 'HANDOVER', 'COMPLETED']) + .enum(['PLANNING', 'UNDER_CONSTRUCTION', 'HANDOVER', 'COMPLETED']) .optional() .or(z.literal('')), totalUnits: z @@ -281,8 +281,8 @@ export default function EditProjectPage() { diff --git a/apps/web/app/[locale]/(dashboard)/projects/new/page.tsx b/apps/web/app/[locale]/(dashboard)/projects/new/page.tsx index 1187370..26de46c 100644 --- a/apps/web/app/[locale]/(dashboard)/projects/new/page.tsx +++ b/apps/web/app/[locale]/(dashboard)/projects/new/page.tsx @@ -31,7 +31,7 @@ const projectSchema = z.object({ (v) => !v || /^https?:\/\//.test(v), 'URL không hợp lệ', ), - status: z.enum(['UPCOMING', 'SELLING', 'HANDOVER', 'COMPLETED']), + status: z.enum(['PLANNING', 'UNDER_CONSTRUCTION', 'HANDOVER', 'COMPLETED']), totalUnits: z .string() .min(1, 'Bắt buộc') @@ -125,7 +125,7 @@ export default function CreateProjectPage() { resolver: zodResolver(projectSchema), mode: 'onTouched', defaultValues: { - status: 'UPCOMING', + status: 'PLANNING', }, }); @@ -252,8 +252,8 @@ export default function CreateProjectPage() { Trạng thái * diff --git a/apps/web/app/[locale]/(dashboard)/projects/page.tsx b/apps/web/app/[locale]/(dashboard)/projects/page.tsx index 870e461..0d376b8 100644 --- a/apps/web/app/[locale]/(dashboard)/projects/page.tsx +++ b/apps/web/app/[locale]/(dashboard)/projects/page.tsx @@ -21,8 +21,8 @@ import { import { shimmerBlurDataURL } from '@/lib/image-blur'; const STATUS_OPTIONS: { value: ProjectStatus; label: string }[] = [ - { value: 'UPCOMING', label: PROJECT_STATUS_LABELS.UPCOMING }, - { value: 'SELLING', label: PROJECT_STATUS_LABELS.SELLING }, + { value: 'PLANNING', label: PROJECT_STATUS_LABELS.PLANNING }, + { value: 'UNDER_CONSTRUCTION', label: PROJECT_STATUS_LABELS.UNDER_CONSTRUCTION }, { value: 'HANDOVER', label: PROJECT_STATUS_LABELS.HANDOVER }, { value: 'COMPLETED', label: PROJECT_STATUS_LABELS.COMPLETED }, ]; diff --git a/apps/web/app/[locale]/(public)/du-an/__tests__/du-an.spec.tsx b/apps/web/app/[locale]/(public)/du-an/__tests__/du-an.spec.tsx index 5692c09..42c2fde 100644 --- a/apps/web/app/[locale]/(public)/du-an/__tests__/du-an.spec.tsx +++ b/apps/web/app/[locale]/(public)/du-an/__tests__/du-an.spec.tsx @@ -61,7 +61,7 @@ const mockSearchData = { id: 'proj-1', slug: 'vinhomes-grand-park', name: 'Vinhomes Grand Park', - status: 'SELLING' as const, + status: 'UNDER_CONSTRUCTION' as const, developer: { id: 'dev-1', name: 'Vingroup', logoUrl: null, totalProjects: 10 }, city: 'Hồ Chí Minh', district: 'Quận 9', diff --git a/apps/web/lib/du-an-api.ts b/apps/web/lib/du-an-api.ts index 5ca6f3d..ef7cb6c 100644 --- a/apps/web/lib/du-an-api.ts +++ b/apps/web/lib/du-an-api.ts @@ -3,9 +3,11 @@ import type { ListingDetail } from './listings-api'; // ─── Enums ─────────────────────────────────────────────── +// Must match the Prisma enum `ProjectDevelopmentStatus` in +// `prisma/schema.prisma` — the backend rejects any other value. export type ProjectStatus = - | 'UPCOMING' - | 'SELLING' + | 'PLANNING' + | 'UNDER_CONSTRUCTION' | 'HANDOVER' | 'COMPLETED'; @@ -198,16 +200,16 @@ export interface SearchProjectsParams { // ─── Status Labels ─────────────────────────────────────── export const PROJECT_STATUS_LABELS: Record = { - UPCOMING: 'Sắp mở bán', - SELLING: 'Đang bán', + PLANNING: 'Đang quy hoạch', + UNDER_CONSTRUCTION: 'Đang xây dựng', HANDOVER: 'Đang bàn giao', COMPLETED: 'Đã hoàn thành', }; export const PROJECT_STATUS_COLORS: Record = { - UPCOMING: 'bg-blue-100 text-blue-800', - SELLING: 'bg-green-100 text-green-800', - HANDOVER: 'bg-amber-100 text-amber-800', + PLANNING: 'bg-blue-100 text-blue-800', + UNDER_CONSTRUCTION: 'bg-amber-100 text-amber-800', + HANDOVER: 'bg-purple-100 text-purple-800', COMPLETED: 'bg-gray-100 text-gray-800', }; diff --git a/apps/web/lib/du-an-server.ts b/apps/web/lib/du-an-server.ts index 05fc061..95841de 100644 --- a/apps/web/lib/du-an-server.ts +++ b/apps/web/lib/du-an-server.ts @@ -63,7 +63,7 @@ function normalizeProjectDetail(raw: unknown): ProjectDetail | null { id: String(r['id'] ?? ''), slug: String(r['slug'] ?? ''), name: String(r['name'] ?? ''), - status: (r['status'] as ProjectDetail['status']) ?? 'SELLING', + status: (r['status'] as ProjectDetail['status']) ?? 'PLANNING', developer, city: String(r['city'] ?? ''), district: String(r['district'] ?? ''),