diff --git a/src/app/components/portfolio/BackToPortfolio.tsx b/src/app/components/portfolio/BackToPortfolio.tsx new file mode 100644 index 00000000..8ebbe125 --- /dev/null +++ b/src/app/components/portfolio/BackToPortfolio.tsx @@ -0,0 +1,17 @@ +import { ArrowLeftIcon } from "@heroicons/react/24/outline"; +import Link from "next/link"; + +export default function BackToPortfolio({ + portfolioId, +}: { + portfolioId: string; +}) { + return ( + +
+ + Back to portfolio +
+ + ); +} diff --git a/src/app/components/property/PartModal.tsx b/src/app/components/property/PartModal.tsx index af3ae857..aa1fd3b4 100644 --- a/src/app/components/property/PartModal.tsx +++ b/src/app/components/property/PartModal.tsx @@ -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>; title: string; - targetEpcRating: EpcRating | ""; - currentEpcRating: EpcRating; }) { function handleEditModalClose() { setIsOpen(false); } - const [modalEpcTarget, setModalEpcTarget] = useState( - 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 ( <> diff --git a/src/app/components/property/partCard.tsx b/src/app/components/property/partCard.tsx index 2e56974f..92691e6e 100644 --- a/src/app/components/property/partCard.tsx +++ b/src/app/components/property/partCard.tsx @@ -30,8 +30,6 @@ export default function PartCard({ isOpen={isDetailModalOpen} setIsOpen={setIsDetailModalOpen} title={title} - targetEpcRating="C" - currentEpcRating="D" /> ); diff --git a/src/app/portfolio/[slug]/property/[lmkKey]/layout.tsx b/src/app/portfolio/[slug]/property/[lmkKey]/layout.tsx new file mode 100644 index 00000000..48b33995 --- /dev/null +++ b/src/app/portfolio/[slug]/property/[lmkKey]/layout.tsx @@ -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 ( +
+
+ +
+ + {children} +
+ ); +} diff --git a/src/app/portfolio/[slug]/property/[lmkKey]/page.tsx b/src/app/portfolio/[slug]/property/[lmkKey]/page.tsx index 4d4ebd44..22a446c8 100644 --- a/src/app/portfolio/[slug]/property/[lmkKey]/page.tsx +++ b/src/app/portfolio/[slug]/property/[lmkKey]/page.tsx @@ -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 { - // 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 (
-
- -
- - Back to portfolio -
-

Your Property

@@ -173,9 +155,11 @@ export default function PropertyPage({
{ - router.push(`/portfolio/${portfolioId}/property/${lmkKey}/plan`); + router.push( + `/portfolio/${portfolioId}/property/${lmkKey}/plan?postcode=${postcode}` + ); }} />
diff --git a/src/app/portfolio/[slug]/property/[lmkKey]/plan/page.tsx b/src/app/portfolio/[slug]/property/[lmkKey]/plan/page.tsx index ebf363e9..1eedb9fb 100644 --- a/src/app/portfolio/[slug]/property/[lmkKey]/plan/page.tsx +++ b/src/app/portfolio/[slug]/property/[lmkKey]/plan/page.tsx @@ -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 = ({ + title, + cost, + co2Emissions, + workHours, +}) => { return ( -
-

Plan

+
+
+

{title}

+
+
+

Cost: {cost}

+
+
+

CO2 Emissions: {co2Emissions}

+
+
+

Work Hours: {workHours}

+
); +}; + +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({ + queryKey: ["search", postcode], + queryFn: async () => fetchData(postcode as string), + }); + + // TODO: Add a loading state and error handling + if (isLoading) { + return
Loading...
; + } + + if (error) { + return
Error fetching data: {error.message}
; + } + + const propertyData = data.rows.filter((row) => row["lmk-key"] === lmkKey)[0]; + return ( +
+
+
+

Your Retrofit Plan

+

{propertyData.address}

+
+
+
+ {/* Clickable Cards */} + {/* Replace this with your actual clickable cards */} + +
+ Clickable Card 2 +
+ {/* Add more clickable cards as needed */} +
+
+
+

Summary

+
    +
  • + Target EPC: {targetEpcRating} + +
  • +
  • Total cost: {totalCost}
  • +
  • Budget: {budget}
  • +
  • Installation Time: {installTime}
  • + {/* flex items-center text-brandmidblue hover:text-brandblue transition-colors duration-200 cursor-pointer */} +
  • + Edit Property Details + +
  • +
+
+
+
+
+
+ ); } diff --git a/src/app/portfolio/[slug]/property/[lmkKey]/utils.tsx b/src/app/portfolio/[slug]/property/[lmkKey]/utils.tsx new file mode 100644 index 00000000..14c826d5 --- /dev/null +++ b/src/app/portfolio/[slug]/property/[lmkKey]/utils.tsx @@ -0,0 +1,14 @@ +import { SearchData } from "@/types/epc"; + +export async function fetchData(postcode: string): Promise { + // 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(); +} diff --git a/src/app/portfolio/[slug]/search/layout.tsx b/src/app/portfolio/[slug]/search/layout.tsx new file mode 100644 index 00000000..091be683 --- /dev/null +++ b/src/app/portfolio/[slug]/search/layout.tsx @@ -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 ( +
+
+ +
+ {children} +
+ ); +}