mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-08 11:37:25 +00:00
Fixing typescript errors
This commit is contained in:
parent
661cb80414
commit
4346660a35
4 changed files with 325 additions and 0 deletions
41
src/app/components/portfolio/summary/EpcBarChart.tsx
Normal file
41
src/app/components/portfolio/summary/EpcBarChart.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { BarChart } from "@tremor/react";
|
||||
|
||||
const dataFormatter = (number: number) =>
|
||||
Intl.NumberFormat("us").format(number).toString();
|
||||
|
||||
const EpcBarChart = ({
|
||||
chartdata,
|
||||
}: {
|
||||
chartdata: {
|
||||
name: string;
|
||||
A?: number;
|
||||
B?: number;
|
||||
C?: number;
|
||||
D?: number;
|
||||
E?: number;
|
||||
F?: number;
|
||||
G?: number;
|
||||
}[];
|
||||
}) => (
|
||||
<BarChart
|
||||
className="w-64 h-40"
|
||||
data={chartdata}
|
||||
index="name"
|
||||
categories={["G", "F", "E", "D", "C", "B", "A"]} // Each treated as a separate series
|
||||
colors={[
|
||||
"#e41e3b", // Color for 'G'
|
||||
"#ef8026", // Color for 'F'
|
||||
"#f3a96a", // Color for 'E'
|
||||
"#f7cd14", // Color for 'D'
|
||||
"#8dbd40", // Color for 'C'
|
||||
"#2da55c", // Color for 'B'
|
||||
"#117d58", // Color for 'A'
|
||||
]}
|
||||
valueFormatter={dataFormatter}
|
||||
yAxisWidth={48}
|
||||
stack={true}
|
||||
showLegend={false}
|
||||
/>
|
||||
);
|
||||
|
||||
export default EpcBarChart;
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { Fragment, useState } from "react";
|
||||
|
||||
const SelectComparisonModal = ({
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
userPortfolios,
|
||||
onAddColumn,
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
userPortfolios: { name: string; id: bigint }[];
|
||||
onAddColumn: (columnName: string) => void;
|
||||
}) => {
|
||||
const [selectedPortfolio, setSelectedPortfolio] = useState("");
|
||||
|
||||
const addColumn = () => {
|
||||
onAddColumn(selectedPortfolio);
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Transition show={isOpen} as={Fragment}>
|
||||
<Dialog
|
||||
as="div"
|
||||
className="fixed inset-0 z-10 overflow-y-auto"
|
||||
onClose={setIsOpen}
|
||||
>
|
||||
<div className="min-h-screen px-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Dialog.Overlay className="fixed inset-0 bg-black opacity-30" />
|
||||
</Transition.Child>
|
||||
|
||||
{/* This element is to trick the browser into centering the modal contents. */}
|
||||
<span
|
||||
className="inline-block h-screen align-middle"
|
||||
aria-hidden="true"
|
||||
>
|
||||
​
|
||||
</span>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<div className="inline-block w-full max-w-md p-6 my-8 overflow-hidden text-left align-middle transition-all transform bg-white shadow-xl rounded-2xl">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium leading-6 text-gray-900"
|
||||
>
|
||||
Add Comparison
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<select
|
||||
className="w-full p-2 border border-gray-300 rounded-md"
|
||||
value={selectedPortfolio}
|
||||
onChange={(e) => setSelectedPortfolio(e.target.value)}
|
||||
>
|
||||
{userPortfolios.map((portfolio) => (
|
||||
<option
|
||||
key={portfolio.id.toString()}
|
||||
value={portfolio.name}
|
||||
>
|
||||
{portfolio.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex justify-center px-4 py-2 text-sm font-medium text-blue-900 bg-blue-100 border border-transparent rounded-md hover:bg-blue-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-blue-500"
|
||||
onClick={addColumn}
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectComparisonModal;
|
||||
146
src/app/components/portfolio/summary/SummaryTable.tsx
Normal file
146
src/app/components/portfolio/summary/SummaryTable.tsx
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
useReactTable,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
ColumnDef,
|
||||
CellContext,
|
||||
} from "@tanstack/react-table";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/app/shadcn_components/ui/table";
|
||||
import SelectComparisonModal from "./SelectComparisonModal";
|
||||
import EpcBarChart from "./EpcBarChart";
|
||||
|
||||
interface DataItem {
|
||||
title: string;
|
||||
today: string;
|
||||
summary: string;
|
||||
}
|
||||
|
||||
const SummaryTable = ({
|
||||
portfolioName,
|
||||
data,
|
||||
userPortfolios,
|
||||
}: {
|
||||
portfolioName: string;
|
||||
data: DataItem[];
|
||||
userPortfolios: { name: string; id: bigint }[];
|
||||
}) => {
|
||||
const initialchartdata = [
|
||||
{ name: "G", G: 2488 },
|
||||
{ name: "F", F: 1445 },
|
||||
{ name: "E", E: 743 },
|
||||
{ name: "D", D: 281 },
|
||||
{ name: "C", C: 251 },
|
||||
{ name: "B", B: 232 },
|
||||
{ name: "A", A: 98 },
|
||||
];
|
||||
|
||||
// Initial columns
|
||||
// Initial columns
|
||||
const [columns, setColumns] = useState<ColumnDef<DataItem>[]>([
|
||||
{
|
||||
accessorKey: "title",
|
||||
header: () => null,
|
||||
cell: (info) => <b>{info.getValue() as string}</b>,
|
||||
},
|
||||
{
|
||||
accessorKey: "today",
|
||||
header: () => <span>Today</span>,
|
||||
cell: (info) => {
|
||||
// Check if the title is "EPCs" and render the bar chart
|
||||
if (info.row.original.title === "EPCs") {
|
||||
return <EpcBarChart chartdata={initialchartdata} />;
|
||||
}
|
||||
// Otherwise, just return the text
|
||||
return <span>{info.getValue() as string}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "summary",
|
||||
header: () => <span>{portfolioName}</span>,
|
||||
cell: (info) => {
|
||||
return <span>{info.getValue() as string}</span>;
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
});
|
||||
|
||||
const addColumn = (columnName: string) => {
|
||||
const newColumn: ColumnDef<DataItem> = {
|
||||
accessorKey: columnName.toLowerCase().replace(/\s+/g, "_"),
|
||||
header: () => <span>{columnName}</span>,
|
||||
cell: ({ getValue }: CellContext<DataItem, unknown>) => {
|
||||
const value = getValue();
|
||||
return (
|
||||
<span>{typeof value === "string" ? value : "Invalid data"}</span>
|
||||
);
|
||||
},
|
||||
};
|
||||
setColumns((oldColumns) => [...oldColumns, newColumn]);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="my-8">
|
||||
<button
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
className="mb-4 p-2 bg-brandgold text-white rounded"
|
||||
>
|
||||
Add Comparison
|
||||
</button>
|
||||
<SelectComparisonModal
|
||||
isOpen={isModalOpen}
|
||||
setIsOpen={setIsModalOpen}
|
||||
userPortfolios={userPortfolios}
|
||||
onAddColumn={addColumn}
|
||||
/>
|
||||
|
||||
<div className="overflow-x-auto shadow-md sm:rounded-lg">
|
||||
<Table className="min-w-full">
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead key={header.id} className="px-6 py-3">
|
||||
{flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id} className="px-6 py-4">
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SummaryTable;
|
||||
39
src/app/portfolio/[slug]/(portfolio)/summary/page.tsx
Normal file
39
src/app/portfolio/[slug]/(portfolio)/summary/page.tsx
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import SummaryTable from "@/app/components/portfolio/summary/SummaryTable";
|
||||
import { getPortfolio, getUserPortfolios } from "../../utils";
|
||||
import { getSession } from "next-auth/react";
|
||||
|
||||
export default async function PortfolioSummary({
|
||||
params,
|
||||
}: {
|
||||
params: { slug: string };
|
||||
}) {
|
||||
const portfolioId = params.slug;
|
||||
const { name: portfolioName } = await getPortfolio(portfolioId);
|
||||
// Retrieve all of the names and ids of the portfolios, attributed to this user
|
||||
// Get user id from the session
|
||||
const userPortfolios = await getUserPortfolios(portfolioId);
|
||||
|
||||
// Dummy data for rows
|
||||
const data = [
|
||||
{
|
||||
title: "EPCs",
|
||||
today: "Initial Bar Chart Here",
|
||||
summary: "Comparison Bar Chart",
|
||||
},
|
||||
{ title: "Total Co2", today: "100t", summary: "50t" },
|
||||
{ title: "Energy bills", today: "£20,000", summary: "£10,000" },
|
||||
{ title: "Cost", today: "", summary: "£32,412" },
|
||||
{ title: "Cost per CO2 reduction", today: "", summary: "£50" },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="container mx-auto px-4">
|
||||
<h1 className="text-3xl text-gray-700 font-bold my-4">Overview</h1>
|
||||
<SummaryTable
|
||||
portfolioName={portfolioName}
|
||||
data={data}
|
||||
userPortfolios={userPortfolios}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue