Setting up plans page

This commit is contained in:
Khalim Conn-Kowlessar 2023-08-16 17:23:52 +01:00
parent c64969bd07
commit 3b9602b293
8 changed files with 30 additions and 233 deletions

View file

@ -25,7 +25,7 @@ export default function EpcCard({
return (
<div
className={
"flex flex-col items-center p-8 shadow rounded-md max-w-xl justify-start text-gray-100 " +
"flex flex-col items-center p-4 shadow rounded-md max-w-xl justify-start text-gray-100 " +
bgStyling
}
>

View file

@ -10,7 +10,7 @@ import { Separator } from "@/app/shadcn_components/ui/separator";
import { PropertyMeta } from "@/app/db/schema/property";
import { sapToEpc } from "@/app/utils";
import { useState } from "react";
import { sumRecommendationMetricMap } from "@/app/portfolio/[slug]/building-passport/[propertyId]/recommendations/utils";
import { sumRecommendationMetricMap } from "@/app/portfolio/[slug]/building-passport/[propertyId]/plans/utils";
import { RecommendationMetricMap } from "@/types/recommendations";
import RecommendationEpcSummaryCard from "./RecommendationEpcSummaryCard";

View file

@ -4,7 +4,7 @@ import { Dispatch, Fragment, SetStateAction, useState } from "react";
import { Dialog, Transition } from "@headlessui/react";
import RecommendationTable from "@/app/components/building-passport/RecommendationTable";
import { RecommendationMetricMap } from "@/types/recommendations";
import { sumRecommendationMetricMap } from "@/app/portfolio/[slug]/building-passport/[propertyId]/recommendations/utils";
import { sumRecommendationMetricMap } from "@/app/portfolio/[slug]/building-passport/[propertyId]/plans/utils";
import uvalueColumns from "./RecommendationTableColumns";
import { sapToEpc } from "@/app/utils";

View file

@ -46,10 +46,10 @@ export function Toolbar({ propertyId, portfolioId }: ToolbarProps) {
const recommendationsButton = (
<NavigationMenuLink
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
href={`/portfolio/${portfolioId}/building-passport/${propertyId}/recommendations`}
href={`/portfolio/${portfolioId}/building-passport/${propertyId}/plans`}
>
<WrenchScrewdriverIcon className="h-4 w-4 mr-2" />
Retrofit Recommendations
Retrofit Plans
</NavigationMenuLink>
);
@ -66,13 +66,13 @@ export function Toolbar({ propertyId, portfolioId }: ToolbarProps) {
<NavigationMenuList>
{preAssessmentReportButton}
{recommendationsButton}
<NavigationMenuLink
{/* <NavigationMenuLink
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
href={`/portfolio/${portfolioId}/building-passport/${propertyId}/plan-optimiser`}
>
<LightBulbIcon className="h-4 w-4 mr-2" />
Plan optimiser
</NavigationMenuLink>
</NavigationMenuLink> */}
<NavigationMenuItem
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
onClick={handleClickSettings}

View file

@ -21,7 +21,7 @@ import {
function AddressCard({ address }: { address: string | null }) {
// In the future, we might want to use react-wrap-balancer for some of this text
return (
<div className="flex flex-col items-center p-8 shadow rounded-md max-w-xl mx-auto justify-start text-gray-100 bg-brandblue">
<div className="flex flex-col items-center p-4 shadow rounded-md max-w-xl mx-auto justify-start text-gray-100 bg-brandblue">
<div className="text-2xl font-bold max-w-l">{address}</div>
</div>
);
@ -35,6 +35,9 @@ interface PropertyDetailsCardProps {
};
}
const rowTitleStyle = "text-gray-100 align-top pb-3";
const rowValueStyle = "text-gray-100 text-end pr-8 pt-1 align-top pb-3";
function PropertyDetailsCard({
conditionReportData,
propertyMeta,
@ -45,33 +48,29 @@ function PropertyDetailsCard({
.join(" ");
return (
<div className="w-full flex flex-col items-center p-5 shadow rounded-md justify-start text-gray-100 bg-brandblue">
<div className="grid grid-cols-2 gap-8 text-m w-full h-full">
<div className="w-full flex flex-col items-center p-4 shadow rounded-md justify-start text-gray-100 bg-brandblue">
<div className="grid grid-cols-2 gap-8 text-m w-full h-full text-sm">
<div className="border-r">
<table className="w-full">
<table className="w-full ">
<tbody>
<tr>
<td className="text-gray-100 ">Year built:</td>
<td className="text-gray-100 text-end pr-8 py-1">
{propertyMeta.yearBuilt}
</td>
<td className={rowTitleStyle}>Year built:</td>
<td className={rowValueStyle}>{propertyMeta.yearBuilt}</td>
</tr>
<tr>
<td className="text-gray-100 ">Property Type:</td>
<td className="text-gray-100 text-end pr-8 py-1">
{propertyText}
</td>
<td className={rowTitleStyle}>Property Type:</td>
<td className={rowValueStyle}>{propertyText}</td>
</tr>
<tr>
<td className="text-gray-100 ">Total floor area:</td>
<td className="text-gray-100 text-end pr-8 py-1">
<td className={rowTitleStyle}>Total floor area:</td>
<td className={rowValueStyle}>
{`${conditionReportData.totalFloorArea} m`}
<sup>2</sup>
</td>
</tr>
<tr>
<td className="text-gray-100 ">In conservation area:</td>
<td className="text-gray-100 text-end pr-8 py-1">
<td className={rowTitleStyle}>In conservation area:</td>
<td className={rowValueStyle}>
{propertyDetailsSpatial.inConservationArea}
</td>
</tr>
@ -81,26 +80,20 @@ function PropertyDetailsCard({
<table className="w-full">
<tbody>
<tr>
<td className="text-gray-100 ">Local Authority:</td>
<td className="text-gray-100 text-end pr-8 py-1">
{propertyMeta.localAuthority}
</td>
<td className={rowTitleStyle}>Local Authority:</td>
<td className={rowValueStyle}>{propertyMeta.localAuthority}</td>
</tr>
<tr>
<td className="text-gray-100 ">Constituency:</td>
<td className="text-gray-100 text-end pr-8 py-1">
{propertyMeta.constituency}
</td>
<td className={rowTitleStyle}>Constituency:</td>
<td className={rowValueStyle}>{propertyMeta.constituency}</td>
</tr>
<tr>
<td className="text-gray-100 ">Tenure</td>
<td className="text-gray-100 text-end pr-8 py-1">
{propertyMeta.tenure}
</td>
<td className={rowTitleStyle}>Tenure</td>
<td className={rowValueStyle}>{propertyMeta.tenure}</td>
</tr>
<tr>
<td className="text-gray-100 ">Number of rooms:</td>
<td className="text-gray-100 text-end pr-8 py-1">
<td className={rowTitleStyle}>Number of rooms:</td>
<td className={rowValueStyle}>
{propertyMeta.numberOfRooms || "unkown"}
</td>
</tr>

View file

@ -1,182 +0,0 @@
import { Recommendation } from "@/app/db/schema/recommendations";
import { PropertyMeta } from "@/app/db/schema/property";
import RecommendationContainer from "@/app/components/building-passport/RecommendationContainer";
import { getPlans, getPropertyMeta } from "../utils";
import { formatDateTime, formatNumber, sapToEpc } from "@/app/utils";
import EpcCard from "@/app/components/building-passport/EpcCard";
import { ChevronRight } from "lucide-react";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/app/shadcn_components/ui/card";
import { Button } from "@/app/shadcn_components/ui/button";
function PlanCard({
expectedEpcRating,
createdAt,
totalEstimatedCost,
totalSapPoints,
}: {
expectedEpcRating: string;
createdAt: Date;
totalEstimatedCost: number;
totalSapPoints: number;
}) {
return (
<Card className="flex items-start">
<div className="flex-none w-1/5">
<EpcCard
epcRating={expectedEpcRating}
fullMargin={true}
expected={true}
/>
</div>
<div className="flex-grow pl-4 flex flex-col justify-between">
<CardHeader className="flex justify-end items-center">
<CardTitle>
<Button className="bg-brandblue hover:bg-hoverblue">
Go to plan
<ChevronRight className="ml-2 h-4 w-4" />
</Button>
</CardTitle>
</CardHeader>
<CardDescription>Created: {formatDateTime(createdAt)}</CardDescription>
<CardContent>
<div className="flex justify-between mb-2">
<span>Total cost:</span>
<span>£{formatNumber(totalEstimatedCost)}</span>
</div>
<div className="flex justify-between">
<span>Total SAP points:</span>
<span>{totalSapPoints}</span>
</div>
</CardContent>
</div>
</Card>
);
}
export default async function Recommendations({
params,
}: {
params: { slug: string; propertyId: string };
}) {
const propertyMeta = await getPropertyMeta(params.propertyId);
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">Retrofit Plans</div>
<div className="max-w-3xl">
{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
);
// Placeholder while we return 999 for all sap points
const expectedSapPoints = Math.min(
propertyMeta.currentSapPoints + totalSapPoints,
100
);
const expectedEpcRating = sapToEpc(expectedSapPoints);
return (
<PlanCard
key={index}
expectedEpcRating={expectedEpcRating}
createdAt={plan.createdAt}
totalEstimatedCost={totalEstimatedCost}
totalSapPoints={totalSapPoints}
/>
);
})}
</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>
// );
}

View file

@ -1,10 +0,0 @@
import { RecommendationMetricMap } from "@/types/recommendations";
export function sumRecommendationMetricMap(
obj: RecommendationMetricMap
): number {
// In the recommendations section of the building passport we have the cost map which
// contains the costs of the recommendations. We need to sum these costs to display
// the total cost of the recommendations
return Object.values(obj).reduce((sum, current) => sum + current, 0);
}

View file

@ -12,10 +12,6 @@ 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 };