mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-08 11:37:25 +00:00
Creating plans UI (WIP)
This commit is contained in:
parent
9fd9d0641e
commit
5e70c072a5
3 changed files with 176 additions and 75 deletions
|
|
@ -10,6 +10,7 @@ import {
|
|||
bigint,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { material } from "./materials";
|
||||
import { InferModel, relations } from "drizzle-orm";
|
||||
|
||||
export interface ComponentRecommendation {
|
||||
id: number;
|
||||
|
|
@ -21,12 +22,6 @@ export interface ComponentRecommendation {
|
|||
sapPoints: number;
|
||||
}
|
||||
|
||||
export interface Recommendation {
|
||||
Walls?: ComponentRecommendation[];
|
||||
Ventilation?: ComponentRecommendation[];
|
||||
Floor?: ComponentRecommendation[];
|
||||
}
|
||||
|
||||
export const recommendation = pgTable("recommendation", {
|
||||
id: bigserial("id", { mode: "bigint" }).primaryKey(),
|
||||
propertyId: bigint("property_id", { mode: "bigint" })
|
||||
|
|
@ -88,3 +83,40 @@ export const planRecommendations = pgTable("plan_recommendations", {
|
|||
.notNull()
|
||||
.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],
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
export const recommendationRelations = relations(
|
||||
planRecommendations,
|
||||
({ many }) => ({
|
||||
planRecommendations: many(planRecommendations),
|
||||
})
|
||||
);
|
||||
|
||||
export type Plan = InferModel<typeof plan, "select">;
|
||||
export type Recommendation = InferModel<typeof recommendation, "select">;
|
||||
export type PlanRecommendations = InferModel<
|
||||
typeof planRecommendations,
|
||||
"select"
|
||||
>;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Recommendation } from "@/app/db/schema/recommendations";
|
||||
import { PropertyMeta } from "@/app/db/schema/property";
|
||||
import RecommendationContainer from "@/app/components/building-passport/RecommendationContainer";
|
||||
import { getPropertyMeta } from "../utils";
|
||||
import { getPlans, getPropertyMeta } from "../utils";
|
||||
|
||||
export default async function Recommendations({
|
||||
params,
|
||||
|
|
@ -9,77 +9,108 @@ export default async function Recommendations({
|
|||
params: { slug: string; propertyId: string };
|
||||
}) {
|
||||
const propertyMeta = await getPropertyMeta(params.propertyId);
|
||||
|
||||
const recommendations: Recommendation = {
|
||||
Walls: [
|
||||
{
|
||||
id: 1,
|
||||
type: "internal_wall_insulation",
|
||||
description: "140mm Mineral Wool internal wall insulation",
|
||||
estimatedCost: 9_450,
|
||||
default: true,
|
||||
newUValue: 0.29,
|
||||
sapPoints: 4,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: "internal_wall_insulation",
|
||||
description: "30mm Vacuum Insulation Panels wall insulation",
|
||||
estimatedCost: 10_135,
|
||||
default: false,
|
||||
newUValue: 0.28,
|
||||
sapPoints: 12,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
type: "internal_external_wall_insulation",
|
||||
description:
|
||||
"80mm Mineral Wool External Wall Insulation and 30mm rigid insulation internal wall insulation",
|
||||
estimatedCost: 13_450,
|
||||
default: false,
|
||||
newUValue: 0.25,
|
||||
sapPoints: 14,
|
||||
},
|
||||
],
|
||||
Ventilation: [
|
||||
{
|
||||
id: 4,
|
||||
type: "mechanical_ventilation",
|
||||
description: "Two decentralised mechanical ventilation units",
|
||||
estimatedCost: 750,
|
||||
default: true,
|
||||
sapPoints: -2,
|
||||
},
|
||||
],
|
||||
Floor: [
|
||||
{
|
||||
id: 5,
|
||||
type: "suspended_floor_insulation",
|
||||
description: "70mm Rigid insulation foam boards with floor screed",
|
||||
estimatedCost: 3_450,
|
||||
default: true,
|
||||
newUValue: 0.24,
|
||||
sapPoints: 7,
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
type: "suspended_floor_insulation",
|
||||
description: "90mm Rigid insulation foam boards with floor screed",
|
||||
estimatedCost: 4_120,
|
||||
default: true,
|
||||
newUValue: 0.24,
|
||||
sapPoints: 7,
|
||||
},
|
||||
],
|
||||
};
|
||||
const plans = await getPlans(params.propertyId);
|
||||
console.log("PLANS", plans);
|
||||
console.log(plans[0].planRecommendations);
|
||||
|
||||
return (
|
||||
<div className="leading-loose tracking-wider">
|
||||
<div className="flex py-8 text-lg">Recommendations</div>
|
||||
<RecommendationContainer
|
||||
recommendations={recommendations}
|
||||
propertyMeta={propertyMeta}
|
||||
/>
|
||||
<div className="flex py-8 text-lg">Retrofit Plans</div>
|
||||
|
||||
<div>
|
||||
{plans.map((plan, index) => {
|
||||
const totalEstimatedCost = plan.planRecommendations.reduce(
|
||||
(acc, rec) => acc + rec.recommendation.estimatedCost,
|
||||
0
|
||||
);
|
||||
const totalSapPoints = plan.planRecommendations.reduce(
|
||||
(acc, rec) => acc + rec.recommendation.sapPoints,
|
||||
0
|
||||
);
|
||||
|
||||
return (
|
||||
<div key={index}>
|
||||
<div>Created At: {plan.createdAt.toISOString()}</div>
|
||||
<div>Is Default: {plan.isDefault ? "Yes" : "No"}</div>
|
||||
<div>Total Estimated Cost: £{totalEstimatedCost}</div>
|
||||
<div>Total SAP Points: {totalSapPoints}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// const recommendations: Recommendation = {
|
||||
// Walls: [
|
||||
// {
|
||||
// id: 1,
|
||||
// type: "internal_wall_insulation",
|
||||
// description: "140mm Mineral Wool internal wall insulation",
|
||||
// estimatedCost: 9_450,
|
||||
// default: true,
|
||||
// newUValue: 0.29,
|
||||
// sapPoints: 4,
|
||||
// },
|
||||
// {
|
||||
// id: 2,
|
||||
// type: "internal_wall_insulation",
|
||||
// description: "30mm Vacuum Insulation Panels wall insulation",
|
||||
// estimatedCost: 10_135,
|
||||
// default: false,
|
||||
// newUValue: 0.28,
|
||||
// sapPoints: 12,
|
||||
// },
|
||||
// {
|
||||
// id: 3,
|
||||
// type: "internal_external_wall_insulation",
|
||||
// description:
|
||||
// "80mm Mineral Wool External Wall Insulation and 30mm rigid insulation internal wall insulation",
|
||||
// estimatedCost: 13_450,
|
||||
// default: false,
|
||||
// newUValue: 0.25,
|
||||
// sapPoints: 14,
|
||||
// },
|
||||
// ],
|
||||
// Ventilation: [
|
||||
// {
|
||||
// id: 4,
|
||||
// type: "mechanical_ventilation",
|
||||
// description: "Two decentralised mechanical ventilation units",
|
||||
// estimatedCost: 750,
|
||||
// default: true,
|
||||
// sapPoints: -2,
|
||||
// },
|
||||
// ],
|
||||
// Floor: [
|
||||
// {
|
||||
// id: 5,
|
||||
// type: "suspended_floor_insulation",
|
||||
// description: "70mm Rigid insulation foam boards with floor screed",
|
||||
// estimatedCost: 3_450,
|
||||
// default: true,
|
||||
// newUValue: 0.24,
|
||||
// sapPoints: 7,
|
||||
// },
|
||||
// {
|
||||
// id: 5,
|
||||
// type: "suspended_floor_insulation",
|
||||
// description: "90mm Rigid insulation foam boards with floor screed",
|
||||
// estimatedCost: 4_120,
|
||||
// default: true,
|
||||
// newUValue: 0.24,
|
||||
// sapPoints: 7,
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
|
||||
// return (
|
||||
// <div className="leading-loose tracking-wider">
|
||||
// <div className="flex py-8 text-lg">Recommendations</div>
|
||||
// <RecommendationContainer
|
||||
// recommendations={recommendations}
|
||||
// propertyMeta={propertyMeta}
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { recommendation } from "./../../../../db/schema/recommendations";
|
||||
import { columns } from "./../../components/propertyTableColumns";
|
||||
import { db } from "@/app/db/db";
|
||||
import {
|
||||
Feature,
|
||||
|
|
@ -6,9 +8,45 @@ import {
|
|||
PropertyMeta,
|
||||
propertyDetailsEpc,
|
||||
} from "@/app/db/schema/property";
|
||||
import { plan, Plan } from "@/app/db/schema/recommendations";
|
||||
import { getRating } from "@/app/utils";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
// type PlanRelation = Plan & {
|
||||
// planRecommendations: PlanRecommendations;
|
||||
// };
|
||||
|
||||
type PlanRelation = Plan & {
|
||||
planRecommendations: {
|
||||
recommendation: { estimatedCost: number; sapPoints: number };
|
||||
}[];
|
||||
};
|
||||
|
||||
export async function getPlans(propertyId: string): Promise<PlanRelation[]> {
|
||||
const data = await db.query.plan.findMany({
|
||||
where: eq(plan.propertyId, BigInt(propertyId)),
|
||||
with: {
|
||||
planRecommendations: {
|
||||
columns: {},
|
||||
with: {
|
||||
recommendation: {
|
||||
columns: {
|
||||
estimatedCost: true,
|
||||
sapPoints: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
|
||||
return data as PlanRelation[];
|
||||
}
|
||||
|
||||
export async function getPropertyMeta(
|
||||
propertyId: string
|
||||
): Promise<PropertyMeta> {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue