mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-08 11:37:25 +00:00
Added basic structure for building passport, optimised layout
This commit is contained in:
parent
6f8e7b256e
commit
97158d3dde
15 changed files with 239 additions and 26 deletions
|
|
@ -1,5 +1,14 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {};
|
||||
const nextConfig = {
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: "https",
|
||||
hostname: "lh3.googleusercontent.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
// use next-axiom for full stack monitoring
|
||||
const { withAxiom } = require("next-axiom");
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
"use client";
|
||||
|
||||
import React, { use, useState } from "react";
|
||||
import React, { useState } from "react";
|
||||
import { Transition } from "@headlessui/react";
|
||||
import ProfileDropDown from "./ProfileDropDown";
|
||||
import { signOut } from "next-auth/react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import Image from "next/image";
|
||||
|
||||
function makeLink(href: string, label: string) {
|
||||
return (
|
||||
|
|
@ -17,7 +18,7 @@ function makeLink(href: string, label: string) {
|
|||
);
|
||||
}
|
||||
|
||||
function Nav() {
|
||||
function Nav({ userImage }: { userImage: string }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const pathname = usePathname();
|
||||
|
||||
|
|
@ -46,7 +47,7 @@ function Nav() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="flex-grow"></div>
|
||||
<ProfileDropDown />
|
||||
<ProfileDropDown userImage={userImage} />
|
||||
<div className="-mr-2 flex md:hidden">
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,16 @@
|
|||
"use client";
|
||||
|
||||
import { Menu } from "@headlessui/react";
|
||||
import { signOut, useSession } from "next-auth/react";
|
||||
import { signOut } from "next-auth/react";
|
||||
import Link from "next/link";
|
||||
|
||||
function ProfileDropDown() {
|
||||
const { data: session, status } = useSession();
|
||||
|
||||
if (status !== "authenticated" || !session || !session.user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function ProfileDropDown({ userImage }: { userImage: string }) {
|
||||
return (
|
||||
<Menu as="div" className="relative">
|
||||
<Menu.Button className="rounded-full">
|
||||
{session.user.image ? (
|
||||
{userImage ? (
|
||||
<img
|
||||
src={session.user.image}
|
||||
src={userImage}
|
||||
alt={"Profile"}
|
||||
className="inline h-12 w-12 rounded-full text-white"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@ export default function StatusBadge({
|
|||
}) {
|
||||
const statusConfig = statusColor[status];
|
||||
|
||||
console.log("status");
|
||||
console.log(status);
|
||||
|
||||
return (
|
||||
<HoverCard>
|
||||
<HoverCardTrigger>
|
||||
|
|
|
|||
88
src/app/components/building-passport/Toolbar.tsx
Normal file
88
src/app/components/building-passport/Toolbar.tsx
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
"use client";
|
||||
|
||||
import {
|
||||
Cog6ToothIcon,
|
||||
NewspaperIcon,
|
||||
HomeModernIcon,
|
||||
LightBulbIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import {
|
||||
NavigationMenu,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuList,
|
||||
NavigationMenuLink,
|
||||
} from "@/app/shadcn_components/ui/navigation-menu";
|
||||
import { cva } from "class-variance-authority";
|
||||
import type { PropertyMeta } from "@/app/db/schema/property";
|
||||
|
||||
interface ToolbarProps {
|
||||
propertyMeta: PropertyMeta;
|
||||
portfolioId: number;
|
||||
}
|
||||
|
||||
const navigationMenuTriggerStyle = cva(
|
||||
"bg-gray-50 cursor-pointer group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-gray-200 hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-gray-200"
|
||||
);
|
||||
|
||||
export function Toolbar({ propertyMeta, portfolioId }: ToolbarProps) {
|
||||
function handleClickSettings() {
|
||||
console.log("Settings were clicked, implement me");
|
||||
}
|
||||
|
||||
function handleClickPortfolioPlan() {
|
||||
console.log("Opt Plan was clicked, implement me");
|
||||
}
|
||||
|
||||
const propertyId = propertyMeta.id;
|
||||
|
||||
const preAssessmentReportButton = propertyMeta.hasPreConditionReport && (
|
||||
<NavigationMenuLink
|
||||
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
|
||||
href={`/portfolio/${portfolioId}/building-passport/${propertyId}/pre-assessment-report`}
|
||||
>
|
||||
<NewspaperIcon className="h-4 w-4 mr-2" />
|
||||
Pre-assessment Condition Report
|
||||
</NavigationMenuLink>
|
||||
);
|
||||
|
||||
const recommendationsButton = propertyMeta.hasPreConditionReport && (
|
||||
<NavigationMenuLink
|
||||
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
|
||||
href={`/portfolio/${portfolioId}/building-passport/${propertyId}/recommendations`}
|
||||
>
|
||||
<HomeModernIcon className="h-4 w-4 mr-2" />
|
||||
Retrofit Recommendations
|
||||
</NavigationMenuLink>
|
||||
);
|
||||
|
||||
return (
|
||||
<NavigationMenu>
|
||||
<NavigationMenuLink
|
||||
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
|
||||
href={`/portfolio/${portfolioId}/building-passport/${propertyId}`}
|
||||
>
|
||||
<HomeModernIcon className="h-4 w-4 mr-2" />
|
||||
Property Information
|
||||
</NavigationMenuLink>
|
||||
|
||||
<NavigationMenuList>
|
||||
{preAssessmentReportButton}
|
||||
{recommendationsButton}
|
||||
<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>
|
||||
<NavigationMenuItem
|
||||
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
|
||||
onClick={handleClickSettings}
|
||||
>
|
||||
<Cog6ToothIcon className="h-4 w-4 mr-2" />
|
||||
Settings
|
||||
</NavigationMenuItem>
|
||||
</NavigationMenuList>
|
||||
</NavigationMenu>
|
||||
);
|
||||
}
|
||||
8
src/app/db/schema/property.ts
Normal file
8
src/app/db/schema/property.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// This is a placeholder for the property schema
|
||||
export interface PropertyMeta {
|
||||
id: number;
|
||||
address: string;
|
||||
postcode: string;
|
||||
hasPreConditionReport: boolean;
|
||||
hasRecommendations: boolean;
|
||||
}
|
||||
|
|
@ -2,6 +2,9 @@ import "./globals.css";
|
|||
import Provider from "./components/Provider";
|
||||
import Nav from "./components/Navbar";
|
||||
import { ReactQueryProvider } from "./ReactQueryProvider";
|
||||
import { AuthOptions } from "@/app/api/auth/[...nextauth]/route";
|
||||
import { getServerSession } from "next-auth/next";
|
||||
import { cache } from "react";
|
||||
|
||||
import { Inter } from "next/font/google";
|
||||
|
||||
|
|
@ -16,7 +19,12 @@ export const metadata = {
|
|||
description: "Start your retrofit journey today",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
const getSession = cache(async () => {
|
||||
const session = await getServerSession(AuthOptions);
|
||||
return session;
|
||||
});
|
||||
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
|
|
@ -25,12 +33,15 @@ export default function RootLayout({
|
|||
// possibly don't want, or at least don't want it to look exactly like this, with the same links and ability to sign out
|
||||
// on small screens.
|
||||
|
||||
const session = await getSession();
|
||||
const userImage = session?.user?.image;
|
||||
|
||||
return (
|
||||
<html lang="en" className={inter.className}>
|
||||
<body>
|
||||
<Provider>
|
||||
<ReactQueryProvider>
|
||||
<Nav />
|
||||
<Nav userImage={userImage} />
|
||||
{children}
|
||||
</ReactQueryProvider>
|
||||
</Provider>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
import { Toolbar } from "@/app/components/building-passport/Toolbar";
|
||||
|
||||
export default function DashboardLayout({
|
||||
children, // will be a page or nested layout
|
||||
params,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
params: { slug: string; propertyId: string };
|
||||
}) {
|
||||
const propertyId = Number(params.propertyId) ?? null;
|
||||
const portfolioId = Number(params.slug) ?? null;
|
||||
|
||||
// The layout is a server component by default so we can fetch meta data here
|
||||
// TODO: Implement get api
|
||||
const propertyMeta = {
|
||||
id: 1,
|
||||
address: "123 Fake Street",
|
||||
postcode: "AB1 2CD",
|
||||
hasPreConditionReport: true,
|
||||
hasRecommendations: true,
|
||||
};
|
||||
|
||||
if (!propertyId && propertyId !== 0) {
|
||||
throw Error("Invalid propertyId");
|
||||
}
|
||||
|
||||
if (!portfolioId && portfolioId !== 0) {
|
||||
throw Error("Invalid portfolioId");
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className="mx-auto w-full max-w-screen-2xl">
|
||||
<div className="flex justify-start items-end p-8">
|
||||
<h1 className="text-3xl font-bold mr-3 text-gray-900">
|
||||
{propertyMeta.address}
|
||||
</h1>
|
||||
<p className="text-xl text-gray-700">{propertyMeta.postcode}</p>
|
||||
</div>
|
||||
<div className="col-span-12 justify-center bg-gray-50 py-2 rounded-md">
|
||||
<Toolbar propertyMeta={propertyMeta} portfolioId={portfolioId} />
|
||||
</div>
|
||||
|
||||
{children}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { Button } from "@/app/shadcn_components/ui/button";
|
||||
import { Toolbar } from "@/app/components/building-passport/Toolbar";
|
||||
|
||||
export default function BuildingPassportHome() {
|
||||
return (
|
||||
<div>
|
||||
<div className="flex p-8">Basic information</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
export default function PlanOptimiser() {
|
||||
return (
|
||||
<div>
|
||||
<div className="flex p-8">Plan optimiser</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
export default function PreAssessmentReport() {
|
||||
return (
|
||||
<div>
|
||||
<div className="flex p-8">Pre Assessment Report</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
export default function Recommendations() {
|
||||
return (
|
||||
<div>
|
||||
<div className="flex p-8">Recommendations</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
25
src/app/portfolio/[slug]/building-passport/error.tsx
Normal file
25
src/app/portfolio/[slug]/building-passport/error.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
"use client";
|
||||
|
||||
export default function Error() {
|
||||
return (
|
||||
<div className="grid h-screen px-4 bg-white pt-32">
|
||||
<div className="text-center">
|
||||
<h1 className="font-black text-gray-200 text-9xl">404</h1>
|
||||
|
||||
<p className="text-2xl font-bold tracking-tight text-gray-900 sm:text-4xl">
|
||||
Something went wrong!
|
||||
</p>
|
||||
|
||||
<p className="mt-4 text-gray-500">Please try again or return to home</p>
|
||||
|
||||
<a
|
||||
type="button"
|
||||
className="inline-block px-5 py-3 mt-6 text-sm font-medium text-white bg-brandblue rounded hover:bg-hoverblue focus:outline-none focus:ring"
|
||||
href="/home"
|
||||
>
|
||||
Home
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -99,7 +99,7 @@ export const columns: ColumnDef<Property>[] = [
|
|||
<HomeIcon className="h-8 w-8 text-gray-200" />
|
||||
<div className="flex flex-col">
|
||||
<a
|
||||
href={`portfolio/${portfolioId}/building-passport/${propertyId}`}
|
||||
href={`${portfolioId}/building-passport/${propertyId}`}
|
||||
className="font-medium underline text-gray-800 cursor-pointer"
|
||||
>
|
||||
{address}
|
||||
|
|
@ -166,7 +166,7 @@ export const columns: ColumnDef<Property>[] = [
|
|||
cell: ({ row }) => {
|
||||
const property = row.original;
|
||||
const propertyId = property.id;
|
||||
const porfolioId = property.portfolioId;
|
||||
const portfolioId = property.portfolioId;
|
||||
|
||||
const loadingStatus = property.loadingStatus;
|
||||
if (loadingStatus === "loading") {
|
||||
|
|
@ -192,9 +192,7 @@ export const columns: ColumnDef<Property>[] = [
|
|||
// onClick={() => navigator.clipboard.writeText(payment.id)}
|
||||
className="text-gray-700 cursor-pointer"
|
||||
>
|
||||
<a
|
||||
href={`porfolio/${porfolioId}/building-passport/${propertyId}`}
|
||||
>
|
||||
<a href={`${portfolioId}/building-passport/${propertyId}`}>
|
||||
Building Passport
|
||||
</a>
|
||||
</DropdownMenuItem>
|
||||
|
|
|
|||
|
|
@ -45,8 +45,11 @@ export function DataTablePagination<TData>({
|
|||
<div className="flex items-center justify-end px-2">
|
||||
<div className="flex items-center space-x-6 lg:space-x-8">
|
||||
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
|
||||
Page {table.getState().pagination.pageIndex + 1} of{" "}
|
||||
{table.getPageCount()}
|
||||
Page{" "}
|
||||
{table.getPageCount() === 0
|
||||
? 0
|
||||
: table.getState().pagination.pageIndex + 1}{" "}
|
||||
of {table.getPageCount()}
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue