diff --git a/src/app/db/schema/property.ts b/src/app/db/schema/property.ts index a99a2e9c..7e05c087 100644 --- a/src/app/db/schema/property.ts +++ b/src/app/db/schema/property.ts @@ -11,6 +11,7 @@ import { bigint, } from "drizzle-orm/pg-core"; import { portfolio, PortfolioStatus } from "./portfolio"; +import { recommendation } from "./recommendations"; import { InferModel, relations } from "drizzle-orm"; // This is a placeholder for the property schema @@ -184,13 +185,25 @@ export const propertyTargets = pgTable("property_targets", { }); // one to one relationship between property and propertyTargets -export const propertyTargetRelations = relations(property, ({ one }) => ({ +export const propertyRelations = relations(property, ({ one, many }) => ({ target: one(propertyTargets, { fields: [property.id], references: [propertyTargets.propertyId], }), + recommendations: many(recommendation), })); +// Define the other side of the one to many relation between a property and its recomendations +export const recommendationsRelations = relations( + recommendation, + ({ one }) => ({ + author: one(property, { + fields: [recommendation.propertyId], + references: [property.id], + }), + }) +); + // TODO: We'll need a property details buildings materials for verisk data? export type Property = InferModel; @@ -199,7 +212,11 @@ export type PropertyDetailsEpc = InferModel< "select" >; // This type is used for the getProperties function in src/app/portfolio/[slug]/utils.ts -export interface PropertyWithTarget { +export interface PropertyToRecommendation { + estimatedCost?: number | null; +} + +export interface PropertyWithRelations { status: string | null; id: bigint; portfolioId: bigint; @@ -207,5 +224,6 @@ export interface PropertyWithTarget { address: string | null; postcode: string | null; target: { epc?: string | null; heatDemand?: number | null }; + recommendations: PropertyToRecommendation[]; cost?: number | null; } diff --git a/src/app/portfolio/[slug]/(portfolio)/page.tsx b/src/app/portfolio/[slug]/(portfolio)/page.tsx index afa258c8..ac73e42a 100644 --- a/src/app/portfolio/[slug]/(portfolio)/page.tsx +++ b/src/app/portfolio/[slug]/(portfolio)/page.tsx @@ -1,9 +1,8 @@ import { HomeIcon } from "@heroicons/react/24/outline"; import { getPortfolio, getProperties } from "../utils"; -import { Toolbar } from "@/app/components/portfolio/Toolbar"; import DataTable from "@/app/portfolio/[slug]/components/propertyTable"; import { columns } from "@/app/portfolio/[slug]/components/propertyTableColumns"; -import { PropertyWithTarget } from "@/app/db/schema/property"; +import { PropertyWithRelations } from "@/app/db/schema/property"; // We enfore caching of data for 60 seconds export const revalidate = 60; @@ -206,7 +205,7 @@ export default async function Page({ } = await getPortfolio(portfolioId); // Default limit to 1000 and offset to 0 for now - will handle pagination later - const properties: PropertyWithTarget[] = await getProperties( + const properties: PropertyWithRelations[] = await getProperties( portfolioId, 1000, 0 diff --git a/src/app/portfolio/[slug]/components/propertyTableColumns.tsx b/src/app/portfolio/[slug]/components/propertyTableColumns.tsx index 89bb2423..36580a0f 100644 --- a/src/app/portfolio/[slug]/components/propertyTableColumns.tsx +++ b/src/app/portfolio/[slug]/components/propertyTableColumns.tsx @@ -17,7 +17,10 @@ import { FunnelIcon } from "@heroicons/react/24/outline"; import { formatNumber } from "@/app/utils"; import { cn } from "@/lib/utils"; import { PortfolioStatus } from "@/app/db/schema/portfolio"; -import { PropertyWithTarget } from "@/app/db/schema/property"; +import { + PropertyToRecommendation, + PropertyWithRelations, +} from "@/app/db/schema/property"; interface DataTableColumnHeaderProps extends React.HTMLAttributes { @@ -64,7 +67,7 @@ export function DataTableFilterHeader({ ); } -export const columns: ColumnDef[] = [ +export const columns: ColumnDef[] = [ { accessorKey: "address", header: ({ column }) => { @@ -124,7 +127,17 @@ export const columns: ColumnDef[] = [ accessorKey: "cost", header: () =>
Cost
, cell: ({ row }) => { - const cost = parseFloat(row.getValue("cost")); + const recommendations = row.original.recommendations; + + const cost = recommendations.reduce( + (acc: number, rec: PropertyToRecommendation) => { + if (rec.estimatedCost === null || rec.estimatedCost === undefined) { + return acc; + } + return acc + rec.estimatedCost; + }, + 0 + ); const creationStatus = row.original.creationStatus; if (creationStatus === "LOADING") { diff --git a/src/app/portfolio/[slug]/utils.ts b/src/app/portfolio/[slug]/utils.ts index ac170be8..1d403257 100644 --- a/src/app/portfolio/[slug]/utils.ts +++ b/src/app/portfolio/[slug]/utils.ts @@ -8,7 +8,7 @@ import { db } from "@/app/db/db"; import { portfolio } from "@/app/db/schema/portfolio"; import { property } from "@/app/db/schema/property"; import type { Portfolio } from "@/app/db/schema/portfolio"; -import type { PropertyWithTarget } from "@/app/db/schema/property"; +import type { PropertyWithRelations } from "@/app/db/schema/property"; import { plan, planRecommendations } from "@/app/db/schema/recommendations"; export async function getPortfolio(portfolioId: string): Promise { @@ -32,8 +32,8 @@ export async function getProperties( portfolioId: string, limit: number = 1000, offset: number = 0 -): Promise { - const data: PropertyWithTarget[] = await db.query.property.findMany({ +): Promise { + const data: PropertyWithRelations[] = await db.query.property.findMany({ limit: limit, offset: offset, columns: { @@ -51,6 +51,12 @@ export async function getProperties( epc: true, }, }, + recommendations: { + columns: { + estimatedCost: true, + }, + where: eq(recommendation.default, true), + }, }, }); diff --git a/src/app/utils.ts b/src/app/utils.ts index 55c10d95..3396730f 100644 --- a/src/app/utils.ts +++ b/src/app/utils.ts @@ -83,7 +83,7 @@ export function formatNumber(number: number): string { const formattedNumber: string = ( roundedNumber / Math.pow(1000, suffixIndex) - ).toString(); + ).toFixed(2); return formattedNumber + suffixes[suffixIndex]; }