diff --git a/src/app/db/db.ts b/src/app/db/db.ts index d743f7e..76c65da 100644 --- a/src/app/db/db.ts +++ b/src/app/db/db.ts @@ -5,6 +5,7 @@ import * as portfolioSchema from "@/app/db/schema/portfolio"; import * as propertySchema from "@/app/db/schema/property"; import * as recommendationSchema from "@/app/db/schema/recommendations"; import * as materialSchema from "@/app/db/schema/materials"; +import * as Relations from "@/app/db/schema/relations"; export const pool = new Pool({ host: process.env.DB_HOST, @@ -21,6 +22,7 @@ const schema = { ...propertySchema, ...recommendationSchema, ...materialSchema, + ...Relations, }; export const db = drizzle(pool, { diff --git a/src/app/db/schema/materials.ts b/src/app/db/schema/materials.ts index 30ff88c..2aff6c1 100644 --- a/src/app/db/schema/materials.ts +++ b/src/app/db/schema/materials.ts @@ -1,3 +1,4 @@ +import { InferModel } from "drizzle-orm"; import { bigserial, text, @@ -86,3 +87,5 @@ export const material = pgTable("material", { cost: json("cost").$type(), notes: text("notes"), }); + +export type Material = InferModel; diff --git a/src/app/db/schema/portfolio.ts b/src/app/db/schema/portfolio.ts index 16d1865..16de559 100644 --- a/src/app/db/schema/portfolio.ts +++ b/src/app/db/schema/portfolio.ts @@ -9,7 +9,7 @@ import { bigint, } from "drizzle-orm/pg-core"; import { user } from "./users"; -import { InferModel, relations } from "drizzle-orm"; +import { InferModel } from "drizzle-orm"; export const PortfolioStatus: [string, ...string[]] = [ "scoping", @@ -70,15 +70,6 @@ export const portfolio = pgTable("portfolio", { .notNull(), }); -// We have a many to many relationship between users and portfolios -// One user can have many portfolios, and one portfolio can have many users -// We use the Dizzle relational queries pattern to facilitate this - -// Define relation from users to portfolios -export const usersToPortfolioRelations = relations(user, ({ many }) => ({ - portfolios: many(portfolio), -})); - export const portfolioUsers = pgTable("portfolioUsers", { id: bigserial("id", { mode: "bigint" }).primaryKey(), // Define the foreign key constraints using references from Drizzle, from user_id to the users table @@ -104,19 +95,6 @@ export const portfolioUsers = pgTable("portfolioUsers", { .notNull(), }); -// Define relation from portfolios to users -export const portfolioToUsersRelations = relations(portfolio, ({ many }) => ({ - users: many(user), -})); - -// Define relation from portfolioUsers to portfolios (we can have many users to a portfolio) -export const portfolioUsersToPortfolioRelations = relations( - portfolioUsers, - ({ many }) => ({ - portfolio: many(portfolio), - }) -); - export type Portfolio = InferModel; export type NewPortfolio = InferModel; export type PortfolioUsers = InferModel; diff --git a/src/app/db/schema/property.ts b/src/app/db/schema/property.ts index 7e05c08..b4ad717 100644 --- a/src/app/db/schema/property.ts +++ b/src/app/db/schema/property.ts @@ -11,8 +11,7 @@ import { bigint, } from "drizzle-orm/pg-core"; import { portfolio, PortfolioStatus } from "./portfolio"; -import { recommendation } from "./recommendations"; -import { InferModel, relations } from "drizzle-orm"; +import { InferModel } from "drizzle-orm"; // This is a placeholder for the property schema export interface PropertyMeta { @@ -184,26 +183,6 @@ export const propertyTargets = pgTable("property_targets", { heatDemand: text("heat_demand"), }); -// one to one relationship between property and propertyTargets -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; diff --git a/src/app/db/schema/recommendations.ts b/src/app/db/schema/recommendations.ts index bbf0ed7..d9fd0bd 100644 --- a/src/app/db/schema/recommendations.ts +++ b/src/app/db/schema/recommendations.ts @@ -10,8 +10,8 @@ import { bigint, pgEnum, } from "drizzle-orm/pg-core"; -import { material } from "./materials"; -import { InferModel, relations } from "drizzle-orm"; +import { Material, material } from "./materials"; +import { InferModel } from "drizzle-orm"; export const recommendation = pgTable("recommendation", { id: bigserial("id", { mode: "bigint" }).primaryKey(), @@ -81,66 +81,16 @@ export const planRecommendations = pgTable("plan_recommendations", { .references(() => recommendation.id), }); -// create a one to many relation to map a plan to the details in the underlying recommendation -// create a many to many map from a plan to a recommendation -// A recommendation can be in multiple plans and therefore we have a many to many relationship between -// plan and recommendations. This relationship is facilitated by the planRecommdnations table - -export const planRelations = relations(plan, ({ many }) => ({ - planRecommendations: many(planRecommendations), -})); - -export const planRecommendationsRelations = relations( - planRecommendations, - ({ one }) => ({ - plan: one(plan, { - fields: [planRecommendations.planId], - references: [plan.id], - }), - recommendation: one(recommendation, { - fields: [planRecommendations.recommendationId], - references: [recommendation.id], - }), - }) -); - -// We construct a relationship between a recommendation and recommendationMaterials -// On recommendationMaterial will map to a single recommendation - -// Define the relationships for the recommendation table -export const recommendationRelations = relations( - recommendation, - ({ many }) => ({ - recommendationMaterials: many(recommendationMaterials), - }) -); - -// Define the relationships for the material table -export const materialRelations = relations(material, ({ many }) => ({ - recommendationMaterials: many(recommendationMaterials), -})); - -// Define the relationships for the recommendationMaterials table -export const recommendationMaterialsRelations = relations( - recommendationMaterials, - ({ one }) => ({ - recommendation: one(recommendation, { - fields: [recommendationMaterials.recommendationId], - references: [recommendation.id], - }), - material: one(material, { - fields: [recommendationMaterials.materialId], - references: [material.id], - }), - }) -); - export type Plan = InferModel; export type Recommendation = InferModel; export type PlanRecommendations = InferModel< typeof planRecommendations, "select" >; +export type RecommendationMaterial = InferModel< + typeof recommendationMaterials, + "select" +>; // We allow recommendation types to be a string however we'll set up a typing for it here to control // the types we expect @@ -166,3 +116,25 @@ export interface PortfolioPlanRecommendation { materialType: string; numberOfProperties: number; } + +export interface RecommendationMaterialToMaterial extends Material { + material: Material; +} + +export interface RecommendationWithMaterials { + id: string; + type: RecommendationType; + description: string; + estimatedCost: number; + startingUValue: number; + newUValue: number; + sapPoints: number; + heatDemand: number; + co2EquivalentSavings: number; + energySavings: number; + energyCostSavings: number; + propertyValuationIncrease: number; + rentalYieldIncrease: number; + totalWorkHours: number; + recommendationMaterials: RecommendationMaterialToMaterial[]; +} diff --git a/src/app/portfolio/[slug]/components/propertyTable.tsx b/src/app/portfolio/[slug]/components/propertyTable.tsx index 5c009b8..bc13b0d 100644 --- a/src/app/portfolio/[slug]/components/propertyTable.tsx +++ b/src/app/portfolio/[slug]/components/propertyTable.tsx @@ -24,16 +24,16 @@ import { useState } from "react"; import { DataTablePagination } from "./propertyTablePagination"; import React from "react"; import { Input } from "@/app/shadcn_components/ui/input"; -import { PropertyWithTarget } from "@/app/db/schema/property"; +import { PropertyWithRelations } from "@/app/db/schema/property"; interface DataTableProps { - columns: ColumnDef[]; - data: PropertyWithTarget[]; + columns: ColumnDef[]; + data: PropertyWithRelations[]; } function fetchData(offset: number) { // Because this is a client component, this will be handled with react query - let properties: PropertyWithTarget[] = []; + let properties: PropertyWithRelations[] = []; // TODO: implement this return properties; } @@ -43,7 +43,7 @@ export default function DataTable({ columns, }: DataTableProps) { const [sorting, setSorting] = useState([]); - const [tableData, setTableData] = useState(data); + const [tableData, setTableData] = useState(data); const [offset, setOffset] = useState(0); const [currentPageIndex, setCurrentPageIndex] = useState(0); const [columnFilters, setColumnFilters] = React.useState( diff --git a/src/app/portfolio/[slug]/utils.ts b/src/app/portfolio/[slug]/utils.ts index 1d40325..417fb49 100644 --- a/src/app/portfolio/[slug]/utils.ts +++ b/src/app/portfolio/[slug]/utils.ts @@ -2,6 +2,7 @@ import { recommendation, UnnestedRecommendation, PortfolioPlanRecommendation, + RecommendationWithMaterials, } from "./../../db/schema/recommendations"; import { and, eq, inArray } from "drizzle-orm"; import { db } from "@/app/db/db";