mirror of
https://github.com/Hestia-Homes/assessment-model.git
synced 2026-06-08 11:37:25 +00:00
added fundamental layout for decent homes moniroting
This commit is contained in:
parent
c265826a7c
commit
45f5634f03
7 changed files with 218 additions and 226 deletions
|
|
@ -13,9 +13,13 @@
|
|||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"files.defaultWorkspace": "/workspaces/assessment-model"
|
||||
"files.defaultWorkspace": "/workspaces/assessment-model",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.tabSize": 2,
|
||||
"editor.insertSpaces": true
|
||||
},
|
||||
"extensions": [
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
168
package-lock.json
generated
168
package-lock.json
generated
|
|
@ -14,7 +14,7 @@
|
|||
"@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-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.5",
|
||||
"@radix-ui/react-hover-card": "^1.0.6",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
|
|
@ -39,7 +39,6 @@
|
|||
"drizzle-kit": "^0.31.4",
|
||||
"drizzle-orm": "^0.44.3",
|
||||
"esbuild": "^0.25.8",
|
||||
"eslint": "8.41.0",
|
||||
"eslint-config-next": "13.4.3",
|
||||
"lucide-react": "^0.233.0",
|
||||
"next": "^15.4.2",
|
||||
|
|
@ -56,6 +55,7 @@
|
|||
"tailwindcss-animate": "^1.0.6",
|
||||
"tsx": "^4.20.3",
|
||||
"typescript": "5.0.4",
|
||||
"vaul": "^1.1.2",
|
||||
"xlsx": "^0.18.5",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
|
|
@ -66,6 +66,8 @@
|
|||
"cypress": "^14.5.3",
|
||||
"cypress-social-logins": "^1.14.1",
|
||||
"dotenv": "^16.3.1",
|
||||
"eslint": "^8.57.1",
|
||||
"prettier": "^3.6.2",
|
||||
"start-server-and-test": "^2.0.0"
|
||||
}
|
||||
},
|
||||
|
|
@ -1733,9 +1735,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "8.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz",
|
||||
"integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==",
|
||||
"version": "8.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
|
||||
"integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
|
|
@ -1850,13 +1852,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.14",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
||||
"integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||
"integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
|
||||
"deprecated": "Use @eslint/config-array instead",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@humanwhocodes/object-schema": "^2.0.2",
|
||||
"@humanwhocodes/object-schema": "^2.0.3",
|
||||
"debug": "^4.3.1",
|
||||
"minimatch": "^3.0.5"
|
||||
},
|
||||
|
|
@ -2942,20 +2944,20 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dialog": {
|
||||
"version": "1.1.14",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz",
|
||||
"integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==",
|
||||
"version": "1.1.15",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz",
|
||||
"integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.2",
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-dismissable-layer": "1.1.10",
|
||||
"@radix-ui/react-focus-guards": "1.1.2",
|
||||
"@radix-ui/react-dismissable-layer": "1.1.11",
|
||||
"@radix-ui/react-focus-guards": "1.1.3",
|
||||
"@radix-ui/react-focus-scope": "1.1.7",
|
||||
"@radix-ui/react-id": "1.1.1",
|
||||
"@radix-ui/react-portal": "1.1.9",
|
||||
"@radix-ui/react-presence": "1.1.4",
|
||||
"@radix-ui/react-presence": "1.1.5",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-slot": "1.2.3",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||
|
|
@ -2977,6 +2979,78 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dialog/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-dialog/node_modules/@radix-ui/react-dismissable-layer": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
|
||||
"integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||
"@radix-ui/react-use-escape-keydown": "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-dialog/node_modules/@radix-ui/react-focus-guards": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
|
||||
"integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dialog/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-direction": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
|
||||
|
|
@ -5480,6 +5554,12 @@
|
|||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@ungap/structured-clone": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
|
||||
"integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-android-arm-eabi": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz",
|
||||
|
|
@ -8145,28 +8225,29 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "8.41.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz",
|
||||
"integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==",
|
||||
"version": "8.57.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
|
||||
"integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
|
||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.4.0",
|
||||
"@eslint/eslintrc": "^2.0.3",
|
||||
"@eslint/js": "8.41.0",
|
||||
"@humanwhocodes/config-array": "^0.11.8",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
"@eslint/eslintrc": "^2.1.4",
|
||||
"@eslint/js": "8.57.1",
|
||||
"@humanwhocodes/config-array": "^0.13.0",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@nodelib/fs.walk": "^1.2.8",
|
||||
"ajv": "^6.10.0",
|
||||
"@ungap/structured-clone": "^1.2.0",
|
||||
"ajv": "^6.12.4",
|
||||
"chalk": "^4.0.0",
|
||||
"cross-spawn": "^7.0.2",
|
||||
"debug": "^4.3.2",
|
||||
"doctrine": "^3.0.0",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"eslint-scope": "^7.2.0",
|
||||
"eslint-visitor-keys": "^3.4.1",
|
||||
"espree": "^9.5.2",
|
||||
"eslint-scope": "^7.2.2",
|
||||
"eslint-visitor-keys": "^3.4.3",
|
||||
"espree": "^9.6.1",
|
||||
"esquery": "^1.4.2",
|
||||
"esutils": "^2.0.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
|
|
@ -8176,7 +8257,6 @@
|
|||
"globals": "^13.19.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.2.0",
|
||||
"import-fresh": "^3.0.0",
|
||||
"imurmurhash": "^0.1.4",
|
||||
"is-glob": "^4.0.0",
|
||||
"is-path-inside": "^3.0.3",
|
||||
|
|
@ -8186,9 +8266,8 @@
|
|||
"lodash.merge": "^4.6.2",
|
||||
"minimatch": "^3.1.2",
|
||||
"natural-compare": "^1.4.0",
|
||||
"optionator": "^0.9.1",
|
||||
"optionator": "^0.9.3",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"strip-json-comments": "^3.1.0",
|
||||
"text-table": "^0.2.0"
|
||||
},
|
||||
"bin": {
|
||||
|
|
@ -11735,6 +11814,22 @@
|
|||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
|
||||
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/pretty-bytes": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
|
||||
|
|
@ -14075,6 +14170,19 @@
|
|||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vaul": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz",
|
||||
"integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dialog": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/verror": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
"@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-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.5",
|
||||
"@radix-ui/react-hover-card": "^1.0.6",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
|
|
@ -45,7 +45,6 @@
|
|||
"drizzle-kit": "^0.31.4",
|
||||
"drizzle-orm": "^0.44.3",
|
||||
"esbuild": "^0.25.8",
|
||||
"eslint": "8.41.0",
|
||||
"eslint-config-next": "13.4.3",
|
||||
"lucide-react": "^0.233.0",
|
||||
"next": "^15.4.2",
|
||||
|
|
@ -62,6 +61,7 @@
|
|||
"tailwindcss-animate": "^1.0.6",
|
||||
"tsx": "^4.20.3",
|
||||
"typescript": "5.0.4",
|
||||
"vaul": "^1.1.2",
|
||||
"xlsx": "^0.18.5",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
|
|
@ -72,6 +72,8 @@
|
|||
"cypress": "^14.5.3",
|
||||
"cypress-social-logins": "^1.14.1",
|
||||
"dotenv": "^16.3.1",
|
||||
"eslint": "^8.57.1",
|
||||
"prettier": "^3.6.2",
|
||||
"start-server-and-test": "^2.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,15 +70,15 @@ export function Toolbar({ propertyId, portfolioId, conditionReport }: ToolbarPro
|
|||
</NavigationMenuLink>
|
||||
);
|
||||
|
||||
const energyAssessmentsReportButton = (
|
||||
<NavigationMenuLink
|
||||
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
|
||||
href={`/portfolio/${portfolioId}/building-passport/${propertyId}/energy-assessment`}
|
||||
>
|
||||
<BoltIcon className="h-4 w-4 mr-2" />
|
||||
Energy Assessment
|
||||
</NavigationMenuLink>
|
||||
);
|
||||
// const energyAssessmentsReportButton = (
|
||||
// <NavigationMenuLink
|
||||
// className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
|
||||
// href={`/portfolio/${portfolioId}/building-passport/${propertyId}/energy-assessment`}
|
||||
// >
|
||||
// <BoltIcon className="h-4 w-4 mr-2" />
|
||||
// Energy Assessment
|
||||
// </NavigationMenuLink>
|
||||
// );
|
||||
|
||||
const documentsButton = (
|
||||
<NavigationMenuLink
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
Cog6ToothIcon,
|
||||
BuildingOfficeIcon,
|
||||
ChartBarIcon,
|
||||
WrenchScrewdriverIcon,
|
||||
HomeModernIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import {
|
||||
NavigationMenu,
|
||||
|
|
@ -43,8 +43,11 @@ export function Toolbar({ portfolioId, scenarios }: ToolbarProps) {
|
|||
router.push(`/portfolio/${portfolioId}/summary`);
|
||||
}
|
||||
|
||||
function handleClickMeasures() {
|
||||
router.push(`/portfolio/${portfolioId}/measures`);
|
||||
// function handleClickMeasures() {
|
||||
// router.push(`/portfolio/${portfolioId}/measures`);
|
||||
// }
|
||||
function handleClickDecentHomes() {
|
||||
router.push(`/portfolio/${portfolioId}/decent-homes`);
|
||||
}
|
||||
|
||||
const [modalIsOpen, setModalIsOpen] = useState(false);
|
||||
|
|
@ -66,7 +69,15 @@ export function Toolbar({ portfolioId, scenarios }: ToolbarProps) {
|
|||
onClick={handleClickSummary}
|
||||
>
|
||||
<ChartBarIcon className="h-4 w-4 mr-2" />
|
||||
Summary
|
||||
Retrofit Summary
|
||||
</NavigationMenuItem>
|
||||
|
||||
<NavigationMenuItem
|
||||
className={navigationMenuTriggerStyle() + " ml-3 mr-2"}
|
||||
onClick={handleClickDecentHomes}
|
||||
>
|
||||
<HomeModernIcon className="h-4 w-4 mr-2" />
|
||||
Decent Homes
|
||||
</NavigationMenuItem>
|
||||
|
||||
{/* <NavigationMenuItem
|
||||
|
|
|
|||
|
|
@ -19,7 +19,18 @@ import {
|
|||
import clsx from "clsx";
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import { PropertyDetailsEpc } from "@/app/db/schema/property";
|
||||
|
||||
import {
|
||||
getAllRoomData,
|
||||
getRoomsWithDamp,
|
||||
getRoomsWithDefects,
|
||||
getRoomsWithBadWindows,
|
||||
areAllWindowsOk,
|
||||
getElevationsWithIssues,
|
||||
hasSufficientSpace,
|
||||
hasEfficientHeatingSystem,
|
||||
isInsulationAdequate,
|
||||
meetsSapThreshold,
|
||||
} from "./decent_homes_utils";
|
||||
|
||||
function ChecklistItem({
|
||||
label,
|
||||
|
|
@ -120,14 +131,6 @@ function formatRoomName(name: string) {
|
|||
.replace("Room Info", "Room");
|
||||
}
|
||||
|
||||
function getRecommendedOccupants(bedrooms: number): number {
|
||||
if (bedrooms <= 0) return 0;
|
||||
if (bedrooms === 1) return 2;
|
||||
if (bedrooms === 2) return 4;
|
||||
if (bedrooms === 3) return 6;
|
||||
return 7; // 4 or more
|
||||
}
|
||||
|
||||
export default function ConditionReport({
|
||||
conditionReport,
|
||||
totalFloorArea,
|
||||
|
|
@ -140,152 +143,35 @@ export default function ConditionReport({
|
|||
},
|
||||
totalFloorArea: number;
|
||||
currentSapPoints: number;
|
||||
conditionData: PropertyDetailsEpc
|
||||
conditionData: PropertyDetailsEpc;
|
||||
}) {
|
||||
const allRoomData = getAllRoomData(conditionReport.rooms);
|
||||
const roomsWithDamp = getRoomsWithDamp(allRoomData);
|
||||
const roomsWithDefects = getRoomsWithDefects(allRoomData);
|
||||
const roomsWithBadWindows = getRoomsWithBadWindows(allRoomData);
|
||||
const windowsOk = areAllWindowsOk(allRoomData);
|
||||
|
||||
// Documentation on decent home standards can be found here:
|
||||
// https://assets.publishing.service.gov.uk/media/5a7968b740f0b63d72fc5926/138355.pdf
|
||||
const elevationsWithIssues = getElevationsWithIssues(conditionReport.access_and_elevations);
|
||||
|
||||
const rooms = conditionReport.rooms;
|
||||
const {
|
||||
isSufficient: enoughSpace,
|
||||
occupantsToUse,
|
||||
numberOfBedrooms,
|
||||
areaPerPerson,
|
||||
} = hasSufficientSpace(conditionReport.occupant_info, totalFloorArea, allRoomData);
|
||||
|
||||
const allRoomData = [
|
||||
...Object.entries(rooms).filter(([k, v]) => v?.room_info),
|
||||
...(rooms.bedrooms || []).map((b: any, i: number) => ["Bedroom " + (i + 1), b]),
|
||||
...(rooms.bathrooms || []).map((b: any, i: number) => ["Bathroom " + (i + 1), b]),
|
||||
];
|
||||
const sapOk = meetsSapThreshold(currentSapPoints);
|
||||
const efficientHeating = hasEfficientHeatingSystem(conditionData.heating || "");
|
||||
const insulationOk = isInsulationAdequate(conditionData.heating || "", conditionData.roofRating, conditionData.wallsRating);
|
||||
const thermalComfortOk = efficientHeating && insulationOk;
|
||||
|
||||
const hasDampIssues = allRoomData.some(
|
||||
([, room]: any) =>
|
||||
room.room_info?.ventilation_info
|
||||
?.are_there_any_visible_or_reported_signs_of_damp_mould_or_excessive_condensation_within_the_room
|
||||
);
|
||||
|
||||
const hasDefects = allRoomData.some(
|
||||
([, room]: any) => room.room_info?.does_the_room_have_any_defects === "Yes"
|
||||
);
|
||||
|
||||
const windowsOk = allRoomData.every(([, room]: any) => {
|
||||
const wi = room.room_info?.windows_info;
|
||||
return wi?.does_the_room_have_any_windows
|
||||
? wi?.condition_of_the_windows === "Good condition"
|
||||
: true;
|
||||
});
|
||||
|
||||
const heatingWorking =
|
||||
conditionReport.heating_system?.general_condition
|
||||
?.is_the_heating_system_in_working_order === true;
|
||||
|
||||
const kitchenOk = rooms.kitchen?.room_info?.overall_condition_of_the_room === "Good";
|
||||
|
||||
const bathroomsOk = Array.isArray(rooms.bathrooms) &&
|
||||
rooms.bathrooms.length > 0 &&
|
||||
rooms.bathrooms.every(
|
||||
(b: any) =>
|
||||
b?.room_info?.overall_condition_of_the_room === "Good"
|
||||
)
|
||||
|
||||
const roomsWithDefects = allRoomData.filter(
|
||||
([, room]: any) => room.room_info?.does_the_room_have_any_defects === "Yes"
|
||||
);
|
||||
|
||||
const roomsWithDamp = allRoomData.filter(
|
||||
([, room]: any) =>
|
||||
room.room_info?.ventilation_info
|
||||
?.are_there_any_visible_or_reported_signs_of_damp_mould_or_excessive_condensation_within_the_room
|
||||
);
|
||||
|
||||
const roomsWithBadWindows = allRoomData.filter(
|
||||
([, room]: any) => {
|
||||
const wi = room.room_info?.windows_info;
|
||||
return wi?.does_the_room_have_any_windows && wi.condition_of_the_windows !== "Good condition";
|
||||
}
|
||||
);
|
||||
|
||||
// Check if the property has adequate space
|
||||
// We've seen a case where the number of adult occupants + child occupants is greater than the total_number_of_occupants so we
|
||||
// take the biggest of the two
|
||||
const totalOccupants = conditionReport.occupant_info?.total_number_of_occupants ?? 0;
|
||||
const totalAdults = conditionReport.occupant_info?.no_of_adult_occupants ?? 0;
|
||||
const totalChildren = conditionReport.occupant_info?.no_of_child_occupants ?? 0;
|
||||
const numberOfBedrooms = Array.isArray(rooms.bedrooms)
|
||||
? rooms.bedrooms.length
|
||||
: 0;
|
||||
|
||||
const occupantsToUse = Math.max(totalAdults + totalChildren, totalOccupants);
|
||||
|
||||
const maxOccupants = getRecommendedOccupants(numberOfBedrooms);
|
||||
const areaPerPerson = totalFloorArea / occupantsToUse;
|
||||
const hasSufficientSpace = (occupantsToUse <= maxOccupants) && (areaPerPerson >= 20);
|
||||
|
||||
// Category 1 Hazard structural
|
||||
const frontElevation = conditionReport.access_and_elevations?.external_elevation_front?.external_elevation;
|
||||
|
||||
const elevationEntries: [string, any][] = [
|
||||
["Front Elevation", frontElevation],
|
||||
["Back Elevation", conditionReport.access_and_elevations?.external_elevation_back?.external_elevation],
|
||||
["Gable One", conditionReport.access_and_elevations?.external_elevation_gable_one?.external_elevation],
|
||||
["Gable Two", conditionReport.access_and_elevations?.external_elevation_gable_two?.external_elevation],
|
||||
];
|
||||
|
||||
// If a wall inherits front elevation, use the front's data
|
||||
const applyFrontDefaults = (label: string, elevationKey: string): [string, any] => {
|
||||
const section = conditionReport.access_and_elevations?.[elevationKey];
|
||||
if (section?.do_all_answers_for_the_front_elevation_apply_to_this_wall) {
|
||||
return [label, frontElevation];
|
||||
}
|
||||
return [label, section?.external_elevation ?? null];
|
||||
};
|
||||
|
||||
const allElevations: [string, any][] = [
|
||||
["Front Elevation", frontElevation],
|
||||
applyFrontDefaults("Back Elevation", "external_elevation_back"),
|
||||
applyFrontDefaults("Gable One", "external_elevation_gable_one"),
|
||||
applyFrontDefaults("Gable Two", "external_elevation_gable_two"),
|
||||
];
|
||||
|
||||
const elevationsWithIssues = allElevations.filter(([_, elevation]) =>
|
||||
elevation &&
|
||||
(
|
||||
elevation.does_any_structural_defect_need_resolving_before_retrofit === true ||
|
||||
elevation.are_there_any_signs_of_water_penetration_caused_by_failed_rainwater_goods_or_pipework === true ||
|
||||
elevation.are_there_any_visible_signs_of_movement === true ||
|
||||
elevation.are_there_any_visible_signs_of_cracking_to_the_existing_external_finish === true
|
||||
)
|
||||
);
|
||||
|
||||
// Thermal comfort
|
||||
const meetsSapThreshold = currentSapPoints >= 35;
|
||||
|
||||
const heatingType = conditionData.heating?.toLowerCase();
|
||||
const hasEfficientHeating =
|
||||
heatingType?.includes("gas") ||
|
||||
heatingType?.includes("oil") ||
|
||||
heatingType?.includes("storage") ||
|
||||
heatingType?.includes("warm air") ||
|
||||
heatingType?.includes("underfloor") ||
|
||||
heatingType?.includes("lpg") ||
|
||||
heatingType?.includes("solid fuel");
|
||||
|
||||
const loftInsulationRating = conditionData.roofRating; // assume rating 3+ is ≥50mm
|
||||
const wallInsulationRating = conditionData.wallsRating; // assume rating 3+ is insulated
|
||||
|
||||
let insulationIsAdequate = false;
|
||||
|
||||
if (heatingType?.includes("gas") || heatingType?.includes("oil")) {
|
||||
insulationIsAdequate =
|
||||
(loftInsulationRating != null && loftInsulationRating >= 3) ||
|
||||
(wallInsulationRating != null && wallInsulationRating >= 3);
|
||||
} else if (
|
||||
heatingType?.includes("storage") ||
|
||||
heatingType?.includes("lpg") ||
|
||||
heatingType?.includes("solid fuel")
|
||||
) {
|
||||
insulationIsAdequate =
|
||||
(loftInsulationRating != null && loftInsulationRating >= 4) &&
|
||||
(wallInsulationRating != null && wallInsulationRating >= 3);
|
||||
}
|
||||
|
||||
const thermalComfortOk = hasEfficientHeating && insulationIsAdequate;
|
||||
const kitchenOk = conditionReport.rooms.kitchen?.room_info?.overall_condition_of_the_room === "Good";
|
||||
const bathroomsOk = Array.isArray(conditionReport.rooms.bathrooms) &&
|
||||
conditionReport.rooms.bathrooms.length > 0 &&
|
||||
conditionReport.rooms.bathrooms.every(
|
||||
(b: any) => b?.room_info?.overall_condition_of_the_room === "Good"
|
||||
);
|
||||
const hasDefects = roomsWithDefects.length > 0;
|
||||
|
||||
return (
|
||||
<div className="space-y-6 mt-8">
|
||||
|
|
@ -295,7 +181,6 @@ export default function ConditionReport({
|
|||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{/* Category 1 Hazards */}
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-sm font-semibold text-muted-foreground mb-1 uppercase tracking-wide">
|
||||
Category 1 Hazards
|
||||
|
|
@ -325,18 +210,18 @@ export default function ConditionReport({
|
|||
|
||||
<ChecklistItem
|
||||
label={
|
||||
heatingWorking
|
||||
conditionReport.heating_system?.general_condition?.is_the_heating_system_in_working_order
|
||||
? "Heating system operational"
|
||||
: "Heating system not operational"
|
||||
}
|
||||
passed={heatingWorking}
|
||||
alert={!heatingWorking}
|
||||
passed={conditionReport.heating_system?.general_condition?.is_the_heating_system_in_working_order}
|
||||
alert={!conditionReport.heating_system?.general_condition?.is_the_heating_system_in_working_order}
|
||||
/>
|
||||
|
||||
<ChecklistItem
|
||||
label={meetsSapThreshold ? "SAP rating meets minimum threshold (≥ 35)" : "SAP rating below recommended minimum"}
|
||||
passed={meetsSapThreshold}
|
||||
alert={!meetsSapThreshold}
|
||||
label={sapOk ? "SAP rating meets minimum threshold (≥ 35)" : "SAP rating below recommended minimum"}
|
||||
passed={sapOk}
|
||||
alert={!sapOk}
|
||||
note={`SAP Points: ${currentSapPoints}`}
|
||||
/>
|
||||
|
||||
|
|
@ -344,16 +229,10 @@ export default function ConditionReport({
|
|||
label={thermalComfortOk ? "Thermal comfort conditions met" : "Thermal comfort conditions not met"}
|
||||
passed={thermalComfortOk}
|
||||
alert={!thermalComfortOk}
|
||||
note={
|
||||
`Heating: ${conditionData.heating}; ` +
|
||||
`Roof Rating: ${conditionData.roofRating ?? "N/A"}; ` +
|
||||
`Wall Rating: ${conditionData.wallsRating ?? "N/A"}`
|
||||
}
|
||||
note={`Heating: ${conditionData.heating}; Roof Rating: ${conditionData.roofRating ?? "N/A"}; Wall Rating: ${conditionData.wallsRating ?? "N/A"}`}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Category 2 Hazards */}
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-sm font-semibold text-muted-foreground mb-1 uppercase tracking-wide">
|
||||
Category 2 Hazards
|
||||
|
|
@ -378,36 +257,27 @@ export default function ConditionReport({
|
|||
/>
|
||||
|
||||
<ChecklistItem
|
||||
label={
|
||||
kitchenOk
|
||||
? "Kitchen in good condition"
|
||||
: "Kitchen not in good condition"
|
||||
}
|
||||
label={kitchenOk ? "Kitchen in good condition" : "Kitchen not in good condition"}
|
||||
passed={kitchenOk}
|
||||
alert={!kitchenOk}
|
||||
/>
|
||||
|
||||
<ChecklistItem
|
||||
label={
|
||||
bathroomsOk
|
||||
? "Bathrooms in good condition"
|
||||
: "Bathrooms not in good condition"
|
||||
}
|
||||
label={bathroomsOk ? "Bathrooms in good condition" : "Bathrooms not in good condition"}
|
||||
passed={bathroomsOk}
|
||||
alert={!bathroomsOk}
|
||||
/>
|
||||
|
||||
<ChecklistItem
|
||||
label="Sufficient space for number of occupants"
|
||||
passed={hasSufficientSpace}
|
||||
alert={!hasSufficientSpace}
|
||||
passed={enoughSpace}
|
||||
alert={!enoughSpace}
|
||||
note={`${occupantsToUse} occupants, ${numberOfBedrooms} bedrooms. ${areaPerPerson}m² per person`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,9 +143,6 @@ export default async function PreAssessmentReport(
|
|||
conditionReport = await getEnergyAssessmentFromS3(conditionReportMeta.s3JsonUri);
|
||||
}
|
||||
|
||||
// console.log("conditionReport", conditionReport.rooms.utility)
|
||||
console.log("Condition data", propertyMeta)
|
||||
|
||||
const nonIntrusiveSurvey = await getNonIntrusiveSurvey(propertyMeta.uprn);
|
||||
|
||||
const retrofitFeatures = formatRetrofitFeatures(conditionReportData);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue