import { apiClient } from './api-client'; 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 = | 'PLANNING' | 'UNDER_CONSTRUCTION' | 'HANDOVER' | 'COMPLETED'; export type ProjectPropertyType = | 'APARTMENT' | 'VILLA' | 'TOWNHOUSE' | 'SHOPHOUSE' | 'LAND' | 'MIXED'; // ─── Interfaces ────────────────────────────────────────── export interface ProjectMedia { id: string; url: string; type: 'image' | 'video' | 'master_plan' | 'document'; order: number; caption: string | null; } export interface ProjectBlock { id: string; name: string; totalUnits: number; availableUnits: number; floors: number; } export interface ProjectAmenity { id: string; name: string; icon: string; category: string; } export interface ProjectPriceRange { propertyType: ProjectPropertyType; minPrice: string; maxPrice: string; pricePerM2Min: number | null; pricePerM2Max: number | null; } export interface ProjectPriceHistory { period: string; avgPricePerM2: number; transactionCount: number; } export interface NeighborhoodScore { category: string; score: number; label: string; } export interface ProjectPOI { id: string; name: string; type: string; distance: number; latitude: number; longitude: number; } export interface ProjectDeveloper { id: string; name: string; logoUrl: string | null; totalProjects: number; } export interface ProjectDocument { id: string; name: string; url: string; type: string; sizeBytes: number; } export interface ProjectSummary { id: string; slug: string; name: string; status: ProjectStatus; developer: ProjectDeveloper; city: string; district: string; address: string; latitude: number | null; longitude: number | null; thumbnailUrl: string | null; totalArea: number; totalUnits: number; propertyTypes: ProjectPropertyType[]; minPrice: string | null; maxPrice: string | null; completionDate: string | null; createdAt: string; } export interface ProjectDetail extends ProjectSummary { description: string; media: ProjectMedia[]; blocks: ProjectBlock[]; amenities: ProjectAmenity[]; priceRanges: ProjectPriceRange[]; priceHistory: ProjectPriceHistory[]; neighborhoodScores: NeighborhoodScore[]; pois: ProjectPOI[]; documents: ProjectDocument[]; linkedListingCount: number; suitableFor?: string[]; whyThisLocation?: string | null; } export interface PaginatedResult { data: T[]; total: number; page: number; limit: number; totalPages: number; } export interface CreateProjectPayload { name: string; slug: string; developer: string; developerLogo?: string; totalUnits: number; status: string; latitude: number; longitude: number; address: string; ward: string; district: string; city: string; description?: string; amenities?: Record; masterPlanUrl?: string; minPrice?: string; maxPrice?: string; pricePerM2Range?: Record; totalArea?: number; buildingCount?: number; floorCount?: number; unitTypes?: Record; tags?: string[]; suitableFor?: string[]; whyThisLocation?: string; startDate?: string; completionDate?: string; } export interface UpdateProjectPayload { name?: string; developer?: string; developerLogo?: string | null; totalUnits?: number; completedUnits?: number; status?: string; description?: string | null; amenities?: Record | null; masterPlanUrl?: string | null; minPrice?: string | null; maxPrice?: string | null; pricePerM2Range?: Record | null; totalArea?: number | null; buildingCount?: number | null; floorCount?: number | null; unitTypes?: Record | null; media?: Record[] | null; documents?: Record[] | null; tags?: string[]; suitableFor?: string[]; whyThisLocation?: string | null; isVerified?: boolean; startDate?: string | null; completionDate?: string | null; } export interface SearchProjectsParams { city?: string; district?: string; developerId?: string; status?: ProjectStatus; propertyType?: ProjectPropertyType; minPrice?: string; maxPrice?: string; sort?: string; page?: number; limit?: number; q?: string; } // ─── Status Labels ─────────────────────────────────────── export const PROJECT_STATUS_LABELS: Record = { 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 = { 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', }; export const PROJECT_PROPERTY_TYPE_LABELS: Record = { APARTMENT: 'Căn hộ', VILLA: 'Biệt thự', TOWNHOUSE: 'Nhà phố', SHOPHOUSE: 'Shophouse', LAND: 'Đất nền', MIXED: 'Tổng hợp', }; // ─── API Functions ─────────────────────────────────────── export const duAnApi = { search: (params: SearchProjectsParams = {}) => { const query = new URLSearchParams(); Object.entries(params).forEach(([key, value]) => { if (value !== undefined && value !== '') query.append(key, String(value)); }); const qs = query.toString(); return apiClient.get>( `/projects${qs ? `?${qs}` : ''}`, ); }, /** DEVELOPER / ADMIN only — returns projects owned by the current user. */ searchMine: (params: SearchProjectsParams = {}) => { const query = new URLSearchParams(); Object.entries(params).forEach(([key, value]) => { if (value !== undefined && value !== '') query.append(key, String(value)); }); const qs = query.toString(); return apiClient.get>( `/projects/mine/list${qs ? `?${qs}` : ''}`, ); }, /** Stats for the "Dự án của tôi" dashboard card — admin + owner only. */ getStats: (projectId: string) => apiClient.get<{ projectId: string; linkedListingCount: number; activeListingCount: number; totalInquiries: number; unreadInquiries: number; savedByUsers: number; }>(`/projects/${projectId}/stats`), getBySlug: (slug: string) => apiClient.get(`/projects/${slug}`), getLinkedListings: (projectId: string, params: { page?: number; limit?: number } = {}) => { const query = new URLSearchParams(); if (params.page) query.append('page', String(params.page)); if (params.limit) query.append('limit', String(params.limit)); const qs = query.toString(); return apiClient.get>( `/projects/${projectId}/listings${qs ? `?${qs}` : ''}`, ); }, submitInquiry: (projectId: string, data: { name: string; phone: string; message: string }) => apiClient.post<{ inquiryId: string }>(`/projects/${projectId}/inquiries`, data), create: (payload: CreateProjectPayload) => apiClient.post<{ id: string; slug: string }>('/projects', payload), update: (id: string, payload: UpdateProjectPayload) => apiClient.patch(`/projects/${id}`, payload), delete: (id: string) => apiClient.delete<{ success: boolean }>(`/projects/${id}`), };