Added basic structure for plan page

This commit is contained in:
Khalim Conn-Kowlessar 2023-05-30 18:40:18 +01:00
parent 170d00791e
commit 4d6a38d163
8 changed files with 201 additions and 55 deletions

View file

@ -0,0 +1,17 @@
import { ArrowLeftIcon } from "@heroicons/react/24/outline";
import Link from "next/link";
export default function BackToPortfolio({
portfolioId,
}: {
portfolioId: string;
}) {
return (
<Link href={`/portfolio/${portfolioId}`}>
<div className="ml-2 px-4 py-1 inline-flex items-center bg-brandtan hover:bg-hovertan text-white rounded-md transition-colors duration-200 cursor-pointer">
<ArrowLeftIcon className="w-5 h-5 mr-1" />
Back to portfolio
</div>
</Link>
);
}

View file

@ -1,5 +1,5 @@
import { Dialog, Transition } from "@headlessui/react";
import { Dispatch, Fragment, SetStateAction, useState } from "react";
import { Dispatch, Fragment, SetStateAction } from "react";
import { TanButton } from "../Button";
import { EpcRating } from "@/types/epc";
@ -7,45 +7,19 @@ export default function PartModal({
isOpen,
setIsOpen,
title,
targetEpcRating,
currentEpcRating,
}: {
isOpen: boolean;
setIsOpen: Dispatch<SetStateAction<boolean>>;
title: string;
targetEpcRating: EpcRating | "";
currentEpcRating: EpcRating;
}) {
function handleEditModalClose() {
setIsOpen(false);
}
const [modalEpcTarget, setModalEpcTarget] = useState<EpcRating | "">(
targetEpcRating
);
function handleModalSubmit() {
setIsOpen(false);
}
function getEpcOptions(
epc: EpcRating
): { label: EpcRating; value: EpcRating }[] {
const alphabet = "ABCDEFG";
const index = alphabet.indexOf(epc.toUpperCase());
if (index === -1) {
throw new Error("Invalid letter input");
}
const epcOptions = alphabet.slice(0, index + 1).split("");
return epcOptions.map((opt) => ({
label: opt as EpcRating,
value: opt as EpcRating,
}));
}
return (
<>
<Transition appear show={isOpen} as={Fragment}>

View file

@ -30,8 +30,6 @@ export default function PartCard({
isOpen={isDetailModalOpen}
setIsOpen={setIsDetailModalOpen}
title={title}
targetEpcRating="C"
currentEpcRating="D"
/>
</section>
);

View file

@ -0,0 +1,21 @@
import BackToPortfolio from "@/app/components/portfolio/BackToPortfolio";
export default function Layout({
children,
params,
}: {
children: React.ReactNode;
params: { slug: string; lmkKey: string };
}) {
const portfolioId = params.slug;
return (
<section>
<div className="mt-2">
<BackToPortfolio portfolioId={portfolioId} />
</div>
{children}
</section>
);
}

View file

@ -2,13 +2,15 @@
import { useState } from "react";
import Link from "next/link";
import { ArrowLeftIcon, PencilSquareIcon } from "@heroicons/react/24/outline";
import { PencilSquareIcon } from "@heroicons/react/24/outline";
import { SearchData, EpcRating, EpcKey } from "@/types/epc";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import EditEpcTargetModal from "../../../../components/property/EditEpcTargetModal";
import PartCard from "@/app/components/property/PartCard";
import { TanButton } from "@/app/components/Button";
import { fetchData } from "./utils";
import BackToPortfolio from "@/app/components/portfolio/BackToPortfolio";
const EpcDefaults: { [key in EpcRating]: EpcRating } = {
G: "C",
@ -73,19 +75,6 @@ const partConfig: PartConfig = [
},
];
async function fetchData(postcode: string): Promise<SearchData> {
// TODO - add strict typing to the api response
const response = await fetch(`/api/search?postcode=${postcode}`);
if (!response.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error("Failed to fetch data");
}
return response.json();
}
export default function PropertyPage({
params,
searchParams,
@ -137,13 +126,6 @@ export default function PropertyPage({
return (
<section>
<div className="h-2"></div>
<Link href={`/portfolio/${portfolioId}`}>
<div className="ml-2 px-4 py-1 inline-flex items-center bg-brandtan hover:bg-hovertan text-white rounded-md transition-colors duration-200 cursor-pointer">
<ArrowLeftIcon className="w-5 h-5 mr-1" />
Back to portfolio
</div>
</Link>
<div className="max-w-3xl mx-auto p-6">
<h1 className="text-2xl text-center font-bold mb-4">Your Property</h1>
@ -173,9 +155,11 @@ export default function PropertyPage({
</div>
<div className="flex justify-center">
<TanButton
label="Build My Plan"
label="Build My Retrofit Plan"
onClick={() => {
router.push(`/portfolio/${portfolioId}/property/${lmkKey}/plan`);
router.push(
`/portfolio/${portfolioId}/property/${lmkKey}/plan?postcode=${postcode}`
);
}}
/>
</div>

View file

@ -1,7 +1,125 @@
export default function Plan() {
"use client";
import { SearchData } from "@/types/epc";
import { useQuery } from "@tanstack/react-query";
import { useRouter } from "next/navigation";
import { fetchData } from "../utils";
import { useState } from "react";
import React from "react";
import { PencilSquareIcon } from "@heroicons/react/24/outline";
type PlanPartProps = {
title: string;
cost: number;
co2Emissions: number;
workHours: number;
};
const PlanPart: React.FC<PlanPartProps> = ({
title,
cost,
co2Emissions,
workHours,
}) => {
return (
<div>
<h1>Plan</h1>
<div className="flex bg-white p-4 rounded-lg shadow mb-4 items-center border-l-4 border-l-brandmidblue hover:bg-brandmidblue hover:text-white hover:border-l-brandtan cursor-pointer">
<div className="flex-1">
<h2 className="text-lg font-bold mb-2">{title}</h2>
</div>
<div className="flex-1 text-center">
<p>Cost: {cost}</p>
</div>
<div className="flex-1 text-center">
<p>CO2 Emissions: {co2Emissions}</p>
</div>
<div className="flex-1 text-center">
<p>Work Hours: {workHours}</p>
</div>
</div>
);
};
export default function Plan({
params,
searchParams,
}: {
params: { slug: string; lmkKey: string };
searchParams: { [key: string]: string | string[] | undefined };
}) {
const router = useRouter();
const portfolioId = params.slug;
const lmkKey = params.lmkKey;
const postcode = searchParams.postcode;
const targetEpcRating = searchParams.targetEpcRating ?? "C";
const [budget, setBudget] = useState("Not set");
const [totalCost, setTotalCost] = useState("Not set");
const [installTime, setInstallTime] = useState("Not set");
if (postcode === undefined) {
router.push(`/portfolio/${portfolioId}/error`);
}
const { data, error, isLoading } = useQuery<SearchData, Error>({
queryKey: ["search", postcode],
queryFn: async () => fetchData(postcode as string),
});
// TODO: Add a loading state and error handling
if (isLoading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error fetching data: {error.message}</div>;
}
const propertyData = data.rows.filter((row) => row["lmk-key"] === lmkKey)[0];
return (
<section>
<div className="max-w-5xl mx-auto p-6 flex flex-col items-center">
<div className="text-center mb-4">
<h1 className="text-2xl font-bold">Your Retrofit Plan</h1>
<p>{propertyData.address}</p>
</div>
<div className="flex w-full">
<div className="w-3/4 pr-4">
{/* Clickable Cards */}
{/* Replace this with your actual clickable cards */}
<PlanPart
title="Plan Part 1"
cost={1000}
co2Emissions={50}
workHours={20}
/>
<div className="bg-white p-4 rounded-lg shadow mb-4">
Clickable Card 2
</div>
{/* Add more clickable cards as needed */}
</div>
<div className="w-1/4 ">
<div className="bg-brandmidblue p-4 rounded-lg shadow text-white ">
<h2 className="text-lg font-bold mb-4">Summary</h2>
<ul>
<li className="mb-2 mb-2 flex items-center cursor-pointer">
Target EPC: {targetEpcRating}
<PencilSquareIcon className="h-6 w-6 ml-2" />
</li>
<li className="mb-2">Total cost: {totalCost}</li>
<li className="mb-2">Budget: {budget}</li>
<li className="mb-2">Installation Time: {installTime}</li>
{/* flex items-center text-brandmidblue hover:text-brandblue transition-colors duration-200 cursor-pointer */}
<li className="mb-2 flex items-center cursor-pointer">
Edit Property Details
<PencilSquareIcon className="h-6 w-6 ml-2" />
</li>
</ul>
</div>
</div>
</div>
</div>
</section>
);
}

View file

@ -0,0 +1,14 @@
import { SearchData } from "@/types/epc";
export async function fetchData(postcode: string): Promise<SearchData> {
// TODO - add strict typing to the api response
const response = await fetch(`/api/search?postcode=${postcode}`);
if (!response.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error("Failed to fetch data");
}
return response.json();
}

View file

@ -0,0 +1,20 @@
import BackToPortfolio from "@/app/components/portfolio/BackToPortfolio";
export default function Layout({
children,
params,
}: {
children: React.ReactNode;
params: { slug: string; lmkKey: string };
}) {
const portfolioId = params.slug;
return (
<section>
<div className="mt-2">
<BackToPortfolio portfolioId={portfolioId} />
</div>
{children}
</section>
);
}