Adding solar analysis page

This commit is contained in:
Khalim Conn-Kowlessar 2024-07-10 12:18:08 +01:00
parent b77c45c810
commit 5927a7d2be
4 changed files with 165 additions and 8 deletions

View file

@ -5,7 +5,7 @@ import {
NewspaperIcon,
HomeModernIcon,
WrenchScrewdriverIcon,
LightBulbIcon,
SunIcon,
} from "@heroicons/react/24/outline";
import {
NavigationMenu,
@ -29,10 +29,6 @@ export function Toolbar({ propertyId, portfolioId }: ToolbarProps) {
console.log("Settings were clicked, implement me");
}
function handleClickPortfolioPlan() {
console.log("Opt Plan was clicked, implement me");
}
const preAssessmentReportButton = (
<NavigationMenuLink
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
@ -43,6 +39,16 @@ export function Toolbar({ propertyId, portfolioId }: ToolbarProps) {
</NavigationMenuLink>
);
const solarAnalysisButton = (
<NavigationMenuLink
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
href={`/portfolio/${portfolioId}/building-passport/${propertyId}/solar-analysis`}
>
<SunIcon className="h-4 w-4 mr-2" />
Solar Analysis
</NavigationMenuLink>
);
const recommendationsButton = (
<NavigationMenuLink
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
@ -65,6 +71,7 @@ export function Toolbar({ propertyId, portfolioId }: ToolbarProps) {
<NavigationMenuList>
{preAssessmentReportButton}
{solarAnalysisButton}
{recommendationsButton}
{/* <NavigationMenuLink
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}

View file

@ -8,7 +8,7 @@ import {
} from "drizzle-orm/pg-core";
import { InferModel } from "drizzle-orm";
export const Solar = pgTable("solar", {
export const solar = pgTable("solar", {
id: bigserial("id", { mode: "bigint" }).primaryKey(),
longitude: real("longitude").notNull(),
latitude: real("latitude").notNull(),
@ -29,5 +29,63 @@ export const Solar = pgTable("solar", {
});
// Define types for selecting and inserting data
export type ApiResponse = InferModel<typeof Solar, "select">;
export type NewApiResponse = InferModel<typeof Solar, "insert">;
export type Solar = InferModel<typeof solar, "select">;
interface Center {
latitude: number;
longitude: number;
}
interface BoundingBox {
ne: Center;
sw: Center;
}
interface ImageryDate {
day: number;
year: number;
month: number;
}
interface SolarPanelConfig {
panelsCount: number;
yearlyEnergyDcKwh: number;
roofSegmentSummaries: any[];
}
interface SolarPotential {
solarPanels: any[];
buildingStats: any;
wholeRoofStats: any;
panelWidthMeters: number;
roofSegmentStats: any[];
panelHeightMeters: number;
solarPanelConfigs: SolarPanelConfig[];
panelCapacityWatts: number;
panelLifetimeYears: number;
maxArrayAreaMeters2: number;
maxArrayPanelsCount: number;
maxSunshineHoursPerYear: number;
carbonOffsetFactorKgPerMwh: number;
}
interface GoogleApiResponse {
name: string;
center: Center;
regionCode: string;
boundingBox: BoundingBox;
imageryDate: ImageryDate;
imageryQuality: string;
solarPotential: SolarPotential;
imageryProcessedDate: ImageryDate;
}
export interface SolarInterface {
id: bigint;
longitude: number;
latitude: number;
uprn: bigint;
createdAt: Date;
updatedAt: Date;
googleApiResponse: GoogleApiResponse;
}

View file

@ -0,0 +1,77 @@
import {
FlagIcon,
LightBulbIcon,
SunIcon,
InformationCircleIcon,
} from "@heroicons/react/24/outline";
import { getPropertyMeta } from "../utils";
import { getSolarData } from "./utils";
export default async function SolarAnalysisPage({
params,
}: {
params: { slug: string; propertyId: string };
}) {
const propertyMeta = await getPropertyMeta(params.propertyId);
const solarData = await getSolarData(Number(propertyMeta.uprn));
if (!solarData) {
return (
<div className="flex items-center justify-center min-h-screen">
<div className="text-center text-gray-500">
<div className="text-2xl font-semibold mb-4">
No Solar Analysis Data Available
</div>
<p className="text-lg">Please check back later for updates.</p>
</div>
</div>
);
}
const {
panelWidthMeters,
panelHeightMeters,
panelCapacityWatts,
panelLifetimeYears,
} = solarData.googleApiResponse.solarPotential;
return (
<div className="leading-loose tracking-wider">
<div className="flex py-8 text-lg">Solar Analysis</div>
<div className="py-8 max-w-7xl mx-auto">
<div className="bg-white shadow-md rounded-lg p-6 mb-8 w-full">
<h2 className="text-l font-semibold text-gray-800 mb-4">
Simulation Panel Configuration
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div className="flex items-center p-4 bg-blue-50 rounded-lg shadow w-full">
<InformationCircleIcon className="w-6 h-6 text-blue-500 mr-2" />
<p className="text-lg text-gray-700">
Dimensions: {panelWidthMeters}m x {panelHeightMeters}m
</p>
</div>
<div className="flex items-center p-4 bg-green-50 rounded-lg shadow w-full">
<LightBulbIcon className="w-6 h-6 text-green-500 mr-2" />
<p className="text-lg text-gray-700">
Wattage: {panelCapacityWatts}W
</p>
</div>
<div className="flex items-center p-4 bg-yellow-50 rounded-lg shadow w-full">
<SunIcon className="w-6 h-6 text-yellow-500 mr-2" />
<p className="text-lg text-gray-700">
Lifetime: {panelLifetimeYears} years
</p>
</div>
<div className="flex items-center p-4 bg-orange-50 rounded-lg shadow w-full">
<FlagIcon className="w-6 h-6 text-orange-500 mr-2" />
<p className="text-lg text-gray-700">
Region: {solarData.googleApiResponse.regionCode}
</p>
</div>
</div>
</div>
{/* Add more sections here as needed */}
</div>
</div>
);
}

View file

@ -0,0 +1,15 @@
import { db } from "@/app/db/db";
import { SolarInterface, solar } from "@/app/db/schema/solar";
import { eq } from "drizzle-orm";
export async function getSolarData(uprn: number): Promise<SolarInterface> {
const data = await db.query.solar.findFirst({
where: eq(solar.uprn, BigInt(uprn)),
});
if (!data) {
throw new Error("Network response was not ok");
}
return data as SolarInterface;
}