added decent homes reporting

This commit is contained in:
Khalim Conn-Kowlessar 2025-08-24 18:04:25 +00:00
parent 0a90eecc91
commit ce1a78005a
7 changed files with 436 additions and 420 deletions

98
package-lock.json generated
View file

@ -12,6 +12,7 @@
"@headlessui/react": "^2.2.7",
"@heroicons/react": "^2.0.18",
"@hookform/resolvers": "^3.9.1",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.5",
@ -2734,6 +2735,43 @@
"integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
"license": "MIT"
},
"node_modules/@radix-ui/react-accordion": {
"version": "1.2.12",
"resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.12.tgz",
"integrity": "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-collapsible": "1.1.12",
"@radix-ui/react-collection": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-controllable-state": "1.2.2"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/primitive": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
"license": "MIT"
},
"node_modules/@radix-ui/react-arrow": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
@ -2787,6 +2825,66 @@
}
}
},
"node_modules/@radix-ui/react-collapsible": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz",
"integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/primitive": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
"license": "MIT"
},
"node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-presence": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collection": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",

View file

@ -18,6 +18,7 @@
"@headlessui/react": "^2.2.7",
"@heroicons/react": "^2.0.18",
"@hookform/resolvers": "^3.9.1",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.5",

View file

@ -63,7 +63,7 @@ export function Toolbar({ propertyId, portfolioId, conditionReport }: ToolbarPro
const preAssessmentReportButton = (
<NavigationMenuLink
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
href={`/portfolio/${portfolioId}/building-passport/${propertyId}/pre-assessment-report`}
href={`/portfolio/${portfolioId}/building-passport/${propertyId}/assessment`}
>
<NewspaperIcon className="h-4 w-4 mr-2" />
Data
@ -110,18 +110,6 @@ export function Toolbar({ propertyId, portfolioId, conditionReport }: ToolbarPro
</NavigationMenuLink>
);
const conditionButton = (
<NavigationMenuLink
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
href={`/portfolio/${portfolioId}/building-passport/${propertyId}/condition`}
>
<WrenchScrewdriverIcon className="h-4 w-4 mr-2" />
Condition Report
</NavigationMenuLink>
);
console.log("conditionReport", conditionReport)
return (
<NavigationMenu>
<NavigationMenuLink
@ -137,9 +125,7 @@ export function Toolbar({ propertyId, portfolioId, conditionReport }: ToolbarPro
{solarAnalysisButton}
{recommendationsButton}
{documentsButton}
{/*We only show the conditionButton if the condition report is not empty*/}
{Object.keys(conditionReport).length > 0 && conditionButton}
{energyAssessmentsReportButton}
{/* {energyAssessmentsReportButton} */}
<NavigationMenuItem
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
onClick={handleClickSettings}

View file

@ -36,8 +36,6 @@ export default async function DashboardLayout(
{ uprn: String(propertyMeta.uprn), documentType: "ECO_CONDITION_REPORT" }
);
console.log("conditionReport", conditionReport)
if (!propertyId && propertyId !== "0") {
throw Error("Invalid propertyId");
}

View file

@ -1,193 +0,0 @@
import EpcCard from "@/app/components/building-passport/EpcCard";
import FeatureTable from "@/app/components/building-passport/FeatureTable";
import {
ConditionReportData,
PropertyDetailsEpc,
PropertyDetailsSpatial,
PropertyMeta,
} from "@/app/db/schema/property";
import { formatDateTime } from "@/app/utils";
import {
generalColumns,
nonInstrusiveColumns,
retrofitColumns,
} from "@/app/components/building-passport/FeatureTableColumns";
import {
formatGeneralFeatures,
formatHeatDemandFeatures,
formatRetrofitFeatures,
getConditionReport,
getPropertyMeta,
getSpatialData,
getNonIntrusiveSurvey,
} from "../utils";
function AddressCard({ address }: { address: string | null }) {
// In the future, we might want to use react-wrap-balancer for some of this text
return (
<div className="flex flex-col items-center p-4 shadow rounded-md max-w-xl mx-auto justify-start text-gray-100 bg-brandblue">
<div className="text-2xl font-bold max-w-l">{address}</div>
</div>
);
}
interface PropertyDetailsCardProps {
conditionReportData: PropertyDetailsEpc;
propertyMeta: PropertyMeta;
propertyDetailsSpatial: PropertyDetailsSpatial;
}
const rowTitleStyle = "text-brandblue align-top pb-3";
const rowValueStyle = "text-brandblue text-end pr-8 pt-1 align-top pb-3";
function PropertyDetailsCard({
conditionReportData,
propertyMeta,
propertyDetailsSpatial,
}: PropertyDetailsCardProps) {
const propertyText = [propertyMeta.builtForm, propertyMeta.propertyType]
.filter(Boolean)
.join(" ");
return (
<div className="w-full flex flex-col items-center p-4 shadow rounded-md justify-start bg-gray-100">
<div className="grid grid-cols-2 gap-8 text-m w-full h-full text-sm">
<div className="border-r">
<table className="w-full ">
<tbody>
<tr>
<td className={rowTitleStyle}>Year built:</td>
<td className={rowValueStyle}>{propertyMeta.yearBuilt}</td>
</tr>
<tr>
<td className={rowTitleStyle}>Property Type:</td>
<td className={rowValueStyle}>{propertyText}</td>
</tr>
<tr>
<td className={rowTitleStyle}>Total floor area:</td>
<td className={rowValueStyle}>
{`${conditionReportData.totalFloorArea} m`}
<sup>2</sup>
</td>
</tr>
<tr>
<td className={rowTitleStyle}>In conservation area:</td>
<td className={rowValueStyle}>
{propertyDetailsSpatial.conservationStatus ? "Yes" : "No"}
</td>
</tr>
<tr>
<td className={rowTitleStyle}>Is listed:</td>
<td className={rowValueStyle}>
{propertyDetailsSpatial.isListedBuilding ? "Yes" : "No"}
</td>
</tr>
<tr>
<td className={rowTitleStyle}>Is heritage:</td>
<td className={rowValueStyle}>
{propertyDetailsSpatial.isHeritageBuilding ? "Yes" : "No"}
</td>
</tr>
</tbody>
</table>
</div>
<table className="w-full">
<tbody>
<tr>
<td className={rowTitleStyle}>Local Authority:</td>
<td className={rowValueStyle}>{propertyMeta.localAuthority}</td>
</tr>
<tr>
<td className={rowTitleStyle}>Constituency:</td>
<td className={rowValueStyle}>{propertyMeta.constituency}</td>
</tr>
<tr>
<td className={rowTitleStyle}>Tenure</td>
<td className={rowValueStyle}>{propertyMeta.tenure}</td>
</tr>
<tr>
<td className={rowTitleStyle}>Number of Habitable Rooms:</td>
<td className={rowValueStyle}>
{propertyMeta.numberOfRooms || "unkown"}
</td>
</tr>
</tbody>
</table>
</div>
</div>
);
}
const formatDate = (dateString: Date) => {
const date = new Date(dateString);
return date.toLocaleDateString("en-GB", {
weekday: "long", // "Monday" through "Sunday"
year: "numeric", // "2024"
month: "long", // "January" through "December"
day: "numeric", // "1", "2", ..., "31"
});
};
export default async function PreAssessmentReport(
props: {
params: Promise<{ slug: string; propertyId: string }>;
}
) {
const params = await props.params;
const propertyMeta = await getPropertyMeta(params.propertyId);
const conditionReportData = await getConditionReport(params.propertyId);
const propertyDetailsSpatial = await getSpatialData(propertyMeta.uprn);
const generalFeatures = formatGeneralFeatures(
conditionReportData,
propertyMeta.propertyType
);
const nonIntrusiveSurvey = await getNonIntrusiveSurvey(propertyMeta.uprn);
const retrofitFeatures = formatRetrofitFeatures(conditionReportData);
const heatingDemand = formatHeatDemandFeatures(conditionReportData);
return (
<div className="leading-loose tracking-wider">
<div className="flex py-8 text-lg">Pre Assessment Report</div>
<div className="text-gray-700 text-sm">
Last updated: {formatDateTime(propertyMeta.updatedAt)}
</div>
<div className="flex flex-col items-stretch mb-4">
<div className="flex flex-row justify-start mt-4 space-x-4">
<EpcCard
epcRating={propertyMeta.currentEpcRating}
fullMargin={false}
/>
<PropertyDetailsCard
conditionReportData={conditionReportData}
propertyMeta={propertyMeta}
propertyDetailsSpatial={propertyDetailsSpatial}
/>
</div>
</div>
{nonIntrusiveSurvey && (
<div>
<div className="flex py-8 text-lg">Non-Intrusive Survey</div>
<div className="flex mb-2 text-sm text-gray-500">
Conducted by: {nonIntrusiveSurvey.surveyor} on{" "}
{formatDate(nonIntrusiveSurvey.surveyDate)}
</div>
<FeatureTable
data={nonIntrusiveSurvey.notes}
columns={nonInstrusiveColumns}
/>
</div>
)}
<div className="flex py-8 text-lg">General Features</div>
<FeatureTable data={generalFeatures} columns={generalColumns} />
<div className="flex py-8 text-lg">Existing Property Features</div>
<FeatureTable data={retrofitFeatures} columns={retrofitColumns} />
<div className="flex py-8 text-lg">Heating Demand</div>
<FeatureTable data={heatingDemand} columns={generalColumns} />
</div>
);
}

View file

@ -1,3 +1,4 @@
import S3 from "aws-sdk/clients/s3";
import {
Recommendation,
planRecommendations,
@ -31,6 +32,35 @@ import {
} from "@/app/db/schema/funding";
import { getUploadedFile, uploadedFiles, DB_REPORT_TYPES } from "@/app/db/surveyDB/schema/surveyDB";
export async function getEnergyAssessmentFromS3(s3Uri: string): Promise<any> {
const url = new URL(s3Uri);
const bucketMatch = url.hostname.match(/^(.+)\.s3/);
const bucket = bucketMatch?.[1];
const key = url.pathname.startsWith("/") ? url.pathname.slice(1) : url.pathname;
if (!bucket || !key) {
throw new Error("Could not extract bucket or key from URI");
}
const s3 = new S3({
region: "eu-west-2",
accessKeyId: process.env.RETROFIT_ENERGY_ASSESSMENTS_AWS_ACCESS_KEY,
secretAccessKey: process.env.ENERGY_ASSESSMENTS_AWS_SECRET,
});
const result = await s3
.getObject({
Bucket: bucket,
Key: key,
})
.promise();
const body = result.Body?.toString("utf-8");
return body ? JSON.parse(body) : null;
}
type RecommendationList = {
recommendation: Recommendation;
}[];

View file

@ -12,193 +12,227 @@ module.exports = {
"./node_modules/@tremor/**/*.{js,ts,jsx,tsx}",
],
theme: {
transparent: "transparent",
current: "currentColor",
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
colors: {
// Tremor light mode
tremor: {
brand: {
faint: colors.blue[50],
muted: colors.blue[200],
subtle: colors.blue[400],
DEFAULT: colors.blue[500],
emphasis: colors.blue[700],
inverted: colors.white,
},
background: {
muted: colors.gray[50],
subtle: colors.gray[100],
DEFAULT: colors.white,
emphasis: colors.gray[700],
},
border: {
DEFAULT: colors.gray[200],
},
ring: {
DEFAULT: colors.gray[200],
},
content: {
subtle: colors.gray[400],
DEFAULT: colors.gray[500],
emphasis: colors.gray[700],
strong: colors.gray[900],
inverted: colors.white,
},
},
// Tremor dark mode
// dark mode
"dark-tremor": {
brand: {
faint: "#0B1229",
muted: colors.blue[950],
subtle: colors.blue[800],
DEFAULT: colors.blue[500],
emphasis: colors.blue[400],
inverted: colors.blue[950],
},
background: {
muted: "#131A2B",
subtle: colors.gray[800],
DEFAULT: colors.gray[900],
emphasis: colors.gray[300],
},
border: {
DEFAULT: colors.gray[800],
},
ring: {
DEFAULT: colors.gray[800],
},
content: {
subtle: colors.gray[600],
DEFAULT: colors.gray[500],
emphasis: colors.gray[200],
strong: colors.gray[50],
inverted: colors.gray[950],
},
},
epc_a: "#117d58",
epc_b: "#2da55c",
epc_c: "#8dbd40",
epc_d: "#f7cd14",
epc_e: "#f3a96a",
epc_f: "#ef8026",
epc_g: "#e41e3b",
brandblue: "#14163d",
hoverblue: "#3e4073",
brandtan: "#d3b488",
hovertan: "#947750",
brandgold: "#f1bb06",
hovergold: "#c79d12",
brandbrown: "#c4a47c",
brandmidblue: "#3943b7",
brandlightblue: "#00a9f4",
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
textColor: {
brandblue: "#14163d",
hoverblue: "#3e4073",
brandtan: "#d3b488",
hovertan: "#947750",
brandbrown: "#c4a47c",
brandmidblue: "#3943b7",
brandlightblue: "#00a9f4",
},
borderRadius: {
lg: `var(--radius)`,
md: `calc(var(--radius) - 2px)`,
sm: "calc(var(--radius) - 4px)",
},
fontFamily: {
sans: ["var(--font-sans)", ...fontFamily.sans],
},
keyframes: {
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
maxWidth: {
"8xl": "90rem",
},
boxShadow: {
// light
"tremor-input": "0 1px 2px 0 rgb(0 0 0 / 0.05)",
"tremor-card":
"0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
"tremor-dropdown":
"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
// dark
"dark-tremor-input": "0 1px 2px 0 rgb(0 0 0 / 0.05)",
"dark-tremor-card":
"0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
"dark-tremor-dropdown":
"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
},
borderRadius: {
"tremor-small": "0.375rem",
"tremor-default": "0.5rem",
"tremor-full": "9999px",
},
fontSize: {
"tremor-label": ["0.75rem", { lineHeight: "1rem" }],
"tremor-default": ["0.875rem", { lineHeight: "1.25rem" }],
"tremor-title": ["1.125rem", { lineHeight: "1.75rem" }],
"tremor-metric": ["1.875rem", { lineHeight: "2.25rem" }],
},
},
transparent: 'transparent',
current: 'currentColor',
container: {
center: true,
padding: '2rem',
screens: {
'2xl': '1400px'
}
},
extend: {
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))'
},
colors: {
tremor: {
brand: {
faint: 'colors.blue[50]',
muted: 'colors.blue[200]',
subtle: 'colors.blue[400]',
DEFAULT: 'colors.blue[500]',
emphasis: 'colors.blue[700]',
inverted: 'colors.white'
},
background: {
muted: 'colors.gray[50]',
subtle: 'colors.gray[100]',
DEFAULT: 'colors.white',
emphasis: 'colors.gray[700]'
},
border: {
DEFAULT: 'colors.gray[200]'
},
ring: {
DEFAULT: 'colors.gray[200]'
},
content: {
subtle: 'colors.gray[400]',
DEFAULT: 'colors.gray[500]',
emphasis: 'colors.gray[700]',
strong: 'colors.gray[900]',
inverted: 'colors.white'
}
},
'dark-tremor': {
brand: {
faint: '#0B1229',
muted: 'colors.blue[950]',
subtle: 'colors.blue[800]',
DEFAULT: 'colors.blue[500]',
emphasis: 'colors.blue[400]',
inverted: 'colors.blue[950]'
},
background: {
muted: '#131A2B',
subtle: 'colors.gray[800]',
DEFAULT: 'colors.gray[900]',
emphasis: 'colors.gray[300]'
},
border: {
DEFAULT: 'colors.gray[800]'
},
ring: {
DEFAULT: 'colors.gray[800]'
},
content: {
subtle: 'colors.gray[600]',
DEFAULT: 'colors.gray[500]',
emphasis: 'colors.gray[200]',
strong: 'colors.gray[50]',
inverted: 'colors.gray[950]'
}
},
epc_a: '#117d58',
epc_b: '#2da55c',
epc_c: '#8dbd40',
epc_d: '#f7cd14',
epc_e: '#f3a96a',
epc_f: '#ef8026',
epc_g: '#e41e3b',
brandblue: '#14163d',
hoverblue: '#3e4073',
brandtan: '#d3b488',
hovertan: '#947750',
brandgold: '#f1bb06',
hovergold: '#c79d12',
brandbrown: '#c4a47c',
brandmidblue: '#3943b7',
brandlightblue: '#00a9f4',
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))'
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))'
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))'
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))'
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))'
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
}
},
textColor: {
brandblue: '#14163d',
hoverblue: '#3e4073',
brandtan: '#d3b488',
hovertan: '#947750',
brandbrown: '#c4a47c',
brandmidblue: '#3943b7',
brandlightblue: '#00a9f4'
},
borderRadius: {
'tremor-small': '0.375rem',
'tremor-default': '0.5rem',
'tremor-full': '9999px'
},
fontFamily: {
sans: [
'var(--font-sans)',
...fontFamily.sans
]
},
keyframes: {
'accordion-down': {
from: {
height: 0
},
to: {
height: 'var(--radix-accordion-content-height)'
}
},
'accordion-up': {
from: {
height: 'var(--radix-accordion-content-height)'
},
to: {
height: 0
}
},
'accordion-down': {
from: {
height: '0'
},
to: {
height: 'var(--radix-accordion-content-height)'
}
},
'accordion-up': {
from: {
height: 'var(--radix-accordion-content-height)'
},
to: {
height: '0'
}
}
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out'
},
maxWidth: {
'8xl': '90rem'
},
boxShadow: {
'tremor-input': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
'tremor-card': '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
'tremor-dropdown': '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
'dark-tremor-input': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
'dark-tremor-card': '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
'dark-tremor-dropdown': '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)'
},
fontSize: {
'tremor-label': [
'0.75rem',
{
lineHeight: '1rem'
}
],
'tremor-default': [
'0.875rem',
{
lineHeight: '1.25rem'
}
],
'tremor-title': [
'1.125rem',
{
lineHeight: '1.75rem'
}
],
'tremor-metric': [
'1.875rem',
{
lineHeight: '2.25rem'
}
]
}
}
},
variants: {
extend: {
@ -234,28 +268,90 @@ module.exports = {
/^(fill-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
},
// This enables the EPC colours for tremor. They're listed from EPC G -> A
...[
"[#e41e3b]",
"[#ef8026]",
"[#f3a96a]",
"[#f7cd14]",
"[#8dbd40]",
"[#2da55c]",
"[#117d58]",
].flatMap((customColor) => [
`bg-${customColor}`,
`border-${customColor}`,
`hover:bg-${customColor}`,
`hover:border-${customColor}`,
`hover:text-${customColor}`,
`fill-${customColor}`,
`ring-${customColor}`,
`stroke-${customColor}`,
`text-${customColor}`,
`ui-selected:bg-${customColor}`,
`ui-selected:border-${customColor}`,
`ui-selected:text-${customColor}`,
]),
"bg-[#e41e3b]",
"border-[#e41e3b]",
"hover:bg-[#e41e3b]",
"hover:border-[#e41e3b]",
"hover:text-[#e41e3b]",
"fill-[#e41e3b]",
"ring-[#e41e3b]",
"stroke-[#e41e3b]",
"text-[#e41e3b]",
"ui-selected:bg-[#e41e3b]",
"ui-selected:border-[#e41e3b]",
"ui-selected:text-[#e41e3b]",
"bg-[#ef8026]",
"border-[#ef8026]",
"hover:bg-[#ef8026]",
"hover:border-[#ef8026]",
"hover:text-[#ef8026]",
"fill-[#ef8026]",
"ring-[#ef8026]",
"stroke-[#ef8026]",
"text-[#ef8026]",
"ui-selected:bg-[#ef8026]",
"ui-selected:border-[#ef8026]",
"ui-selected:text-[#ef8026]",
"bg-[#f3a96a]",
"border-[#f3a96a]",
"hover:bg-[#f3a96a]",
"hover:border-[#f3a96a]",
"hover:text-[#f3a96a]",
"fill-[#f3a96a]",
"ring-[#f3a96a]",
"stroke-[#f3a96a]",
"text-[#f3a96a]",
"ui-selected:bg-[#f3a96a]",
"ui-selected:border-[#f3a96a]",
"ui-selected:text-[#f3a96a]",
"bg-[#f7cd14]",
"border-[#f7cd14]",
"hover:bg-[#f7cd14]",
"hover:border-[#f7cd14]",
"hover:text-[#f7cd14]",
"fill-[#f7cd14]",
"ring-[#f7cd14]",
"stroke-[#f7cd14]",
"text-[#f7cd14]",
"ui-selected:bg-[#f7cd14]",
"ui-selected:border-[#f7cd14]",
"ui-selected:text-[#f7cd14]",
"bg-[#8dbd40]",
"border-[#8dbd40]",
"hover:bg-[#8dbd40]",
"hover:border-[#8dbd40]",
"hover:text-[#8dbd40]",
"fill-[#8dbd40]",
"ring-[#8dbd40]",
"stroke-[#8dbd40]",
"text-[#8dbd40]",
"ui-selected:bg-[#8dbd40]",
"ui-selected:border-[#8dbd40]",
"ui-selected:text-[#8dbd40]",
"bg-[#2da55c]",
"border-[#2da55c]",
"hover:bg-[#2da55c]",
"hover:border-[#2da55c]",
"hover:text-[#2da55c]",
"fill-[#2da55c]",
"ring-[#2da55c]",
"stroke-[#2da55c]",
"text-[#2da55c]",
"ui-selected:bg-[#2da55c]",
"ui-selected:border-[#2da55c]",
"ui-selected:text-[#2da55c]",
"bg-[#117d58]",
"border-[#117d58]",
"hover:bg-[#117d58]",
"hover:border-[#117d58]",
"hover:text-[#117d58]",
"fill-[#117d58]",
"ring-[#117d58]",
"stroke-[#117d58]",
"text-[#117d58]",
"ui-selected:bg-[#117d58]",
"ui-selected:border-[#117d58]",
"ui-selected:text-[#117d58]",
],
plugins: [
function ({ addVariant }) {