Merge pull request #173 from Hestia-Homes/main

Dev depoyment
This commit is contained in:
KhalimCK 2026-01-28 18:55:00 +00:00 committed by GitHub
commit 49804b17ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 6032 additions and 13 deletions

View file

@ -35,7 +35,6 @@ const PresignedUrlBodySchema = z
export async function POST(request: NextRequest) {
// For the moment, this api specifically handles uploads of csvs
console.log("Triggering plan build");
const body = await request.json();
let validatedBody;
@ -78,7 +77,7 @@ export async function POST(request: NextRequest) {
JSON.stringify({ msg: "Error triggering plan" }),
{
status: 500,
}
},
);
}

View file

@ -29,6 +29,7 @@ const TitleMap = {
loft_insulation: "Loft Insulation",
room_roof_insulation: "Room Roof Insulation",
flat_roof_insulation: "Flat Roof Insulation",
sloping_ceiling_insulation: "Sloping Ceiling Insulation",
// Floor
solid_floor_insulation: "Solid Floor Insulation",
suspended_floor_insulation: "Suspended Floor Insulation",
@ -104,12 +105,12 @@ export default function RecommendationCard({
setTotalKwhSavings,
}: RecommendationCardProps) {
const defaultComponent = recommendationData.find(
(rec: Recommendation) => rec.default
(rec: Recommendation) => rec.default,
) as Recommendation;
// A recommendation type could have no default recommendation, so we need to check if it exists
const alreadyInstalled = recommendationData.some(
(rec) => rec.alreadyInstalled
(rec) => rec.alreadyInstalled,
);
const [cardComponent, setCardComponent] =

View file

@ -0,0 +1,30 @@
CREATE TYPE "public"."aspect_type" AS ENUM('material', 'condition', 'type', 'area', 'configuration', 'presence', 'risk', 'severity', 'location', 'finish', 'insulation', 'pointing', 'spalling', 'lintels', 'cladding', 'category', 'quantity', 'adequacy', 'rating', 'strategy', 'extent', 'distribution', 'structure', 'covering', 'fire_rating', 'external_decoration', 'work_required', 'age_band', 'construction_type', 'classification', 'system');--> statement-breakpoint
CREATE TYPE "public"."element_type" AS ENUM('property', 'property_construction_type', 'property_classification', 'property_age_band', 'storey_count', 'floor_level', 'floor_level_front_door', 'accessible_housing_register', 'asbestos', 'quality_standard', 'ccu', 'passenger_lift', 'stairlift', 'disabled_hoist_tracking', 'disabled_facilities', 'steps_to_front_door', 'roof', 'pitched_roof_covering', 'flat_roof_covering', 'rainwater_goods', 'loft_insulation', 'porch_canopy', 'chimney', 'fascia', 'soffit', 'fascia_soffit_bargeboards', 'gutters', 'store_roof', 'garage_roof', 'garage_and_store_roof', 'external_wall', 'external_noise_insulation', 'primary_wall', 'secondary_wall', 'downpipes', 'external_decoration', 'cladding', 'spandrel_panels', 'garage_walls', 'party_wall_fire_break', 'external_brickwork_pointing', 'internal_downpipes_external_area', 'external_windows', 'communal_windows', 'secondary_glazing', 'store_windows', 'garage_windows', 'garage_and_store_windows', 'external_door', 'front_door', 'rear_door', 'store_door', 'garage_door', 'garage_and_store_door', 'communal_entrance_door', 'main_door', 'block_entrance_door', 'lintel', 'patio_french_door', 'door_entry_handset', 'paths_and_hardstandings', 'parking_areas', 'boundary_walls', 'front_fencing', 'rear_fencing', 'side_fencing', 'rear_gate', 'front_gate', 'gates', 'retaining_walls', 'private_balcony', 'balcony_balustrade', 'outbuildings', 'garage_structure', 'paving', 'roads', 'soil_and_vent', 'solar_thermals', 'drop_kerb', 'outbuilding_overhaul', 'external_structural_defects', 'access_ramp', 'kitchen', 'kitchen_space_layout', 'tenant_installed_kitchen', 'kitchen_extractor_fan', 'bathroom', 'secondary_bathroom', 'secondary_toilet', 'bathroom_extractor_fan', 'additional_wc_or_whb', 'bathroom_remaining_life_source', 'kitchen_remaining_life_source', 'central_heating', 'heating_boiler', 'heating_distribution', 'secondary_heating', 'hot_water_system', 'cold_water_storage', 'heating_system', 'boiler_fuel', 'water_heating', 'programmable_heating', 'community_heating', 'gas_available', 'heat_recovery_units', 'heating_improvements', 'electrical_wiring', 'consumer_unit', 'smoke_detection', 'heat_detection', 'carbon_monoxide_detection', 'fire_door_rating', 'fire_risk_assessment', 'internal_wiring', 'electrics', 'communal_heating', 'communal_boiler', 'communal_electrics', 'communal_fire_alarm', 'communal_emergency_lighting', 'communal_door_entry', 'communal_cctv', 'communal_bin_store', 'communal_bin_store_doors', 'communal_bin_store_walls', 'communal_bin_store_roof', 'communal_refuse_chute', 'communal_floor_covering', 'communal_kitchen', 'communal_bathroom', 'communal_toilets', 'communal_gates', 'communal_lift', 'communal_passenger_lift', 'communal_balcony_walkway', 'communal_entrance', 'communal_internal_decorations', 'communal_internal_floor', 'communal_walkways', 'communal_external_doors', 'communal_stairs', 'communal_aerial', 'communal_aov', 'communal_internal_doors', 'communal_lateral_mains', 'communal_lighting', 'communal_lighting_conductor', 'communal_store_roof', 'communal_store_walls', 'communal_store_doors', 'communal_warden_call_system', 'communal_bms', 'communal_booster_pump', 'communal_dry_riser', 'communal_wet_riser', 'communal_cold_water_storage', 'communal_sprinkler', 'communal_plug_sockets', 'communal_circulation_space', 'ffhh_damp', 'ffhh_hold_and_cold_water', 'ffhh_drainage_lavatories', 'ffhh_neglected', 'ffhh_natural_light', 'ffhh_ventilation', 'ffhh_food_prep_and_washup', 'ffhh_unsafe_layout', 'ffhh_unstable_building', 'hhsrs_damp_and_mould', 'hhsrs_excess_cold', 'hhsrs_excess_heat', 'hhsrs_asbestos_and_mmf', 'hhsrs_biocides', 'hhsrs_carbon_monoxide', 'hhsrs_lead', 'hhsrs_radiation', 'hhsrs_uncombusted_fuel_gas', 'hhsrs_volatile_organic_compounds', 'hhsrs_crowding_and_space', 'hhsrs_entry_by_intruders', 'hhsrs_lighting', 'hhsrs_noise', 'hhsrs_domestic_hygiene_pests_refuse', 'hhsrs_food_safety', 'hhsrs_personal_hygiene_sanitation', 'hhsrs_water_supply', 'hhsrs_falls_associated_with_baths', 'hhsrs_falls_on_level_surfaces', 'hhsrs_falls_on_stairs', 'hhsrs_falls_between_levels', 'hhsrs_electrical_hazards', 'hhsrs_fire', 'hhsrs_flames_hot_surfaces', 'hhsrs_collision_and_entrapment', 'hhsrs_collision_hazards_low_headroom', 'hhsrs_explosions', 'hhsrs_ergonomics', 'hhsrs_structural_collapse', 'hhsrs_amenities');--> statement-breakpoint
CREATE TABLE "aspect_condition" (
"id" bigserial PRIMARY KEY NOT NULL,
"element_id" bigint NOT NULL,
"aspect_type" "aspect_type" NOT NULL,
"aspect_instance" integer NOT NULL,
"value" text,
"quantity" integer,
"install_date" date,
"renewal_year" integer,
"comments" text
);
--> statement-breakpoint
CREATE TABLE "element" (
"id" bigserial PRIMARY KEY NOT NULL,
"survey_id" bigint NOT NULL,
"element_type" "element_type" NOT NULL,
"element_instance" integer NOT NULL
);
--> statement-breakpoint
CREATE TABLE "property_condition_survey" (
"id" bigserial PRIMARY KEY NOT NULL,
"uprn" bigint NOT NULL,
"date" date NOT NULL,
"source" text NOT NULL
);
--> statement-breakpoint
ALTER TABLE "aspect_condition" ADD CONSTRAINT "aspect_condition_element_id_element_id_fk" FOREIGN KEY ("element_id") REFERENCES "public"."element"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "element" ADD CONSTRAINT "element_survey_id_property_condition_survey_id_fk" FOREIGN KEY ("survey_id") REFERENCES "public"."property_condition_survey"("id") ON DELETE no action ON UPDATE no action;

File diff suppressed because it is too large Load diff

View file

@ -1044,6 +1044,13 @@
"when": 1767823836420,
"tag": "0148_first_gamma_corps",
"breakpoints": true
},
{
"idx": 149,
"version": "7",
"when": 1769597155526,
"tag": "0149_rich_luminals",
"breakpoints": true
}
]
}

View file

@ -0,0 +1,28 @@
import { bigint, bigserial, date, integer, pgTable, text } from "drizzle-orm/pg-core";
import { element } from "./element";
import { aspectType } from "./aspect_type";
import { InferInsertModel, InferSelectModel } from "drizzle-orm";
export const aspectCondition = pgTable("aspect_condition", {
id: bigserial("id", { mode: "bigint" }).primaryKey(),
elementId: bigint("element_id", { mode: "number" })
.notNull()
.references(() => element.id),
aspectType: aspectType("aspect_type").notNull(),
aspectInstance: integer("aspect_instance").notNull(),
value: text("value"),
quantity: integer("quantity"),
installDate: date("install_date"),
renewalYear: integer("renewal_year"),
comments: text("comments"),
});
export type AspectConditionRow =
InferSelectModel<typeof aspectCondition>;
export type NewAspectConditionRow =
InferInsertModel<typeof aspectCondition>;

View file

@ -0,0 +1,37 @@
import { pgEnum } from "drizzle-orm/pg-core";
export const aspectType = pgEnum("aspect_type", [
"material",
"condition",
"type",
"area",
"configuration",
"presence",
"risk",
"severity",
"location",
"finish",
"insulation",
"pointing",
"spalling",
"lintels",
"cladding",
"category",
"quantity",
"adequacy",
"rating",
"strategy",
"extent",
"distribution",
"structure",
"covering",
"fire_rating",
"external_decoration",
"work_required",
"age_band",
"construction_type",
"classification",
"system",
]);
export type AspectType = typeof aspectType.enumValues[number];

View file

@ -0,0 +1,19 @@
import { bigint, bigserial, integer, pgTable } from "drizzle-orm/pg-core";
import { propertyConditionSurvey } from "./property_condition_survey";
import { elementType } from "./element_type";
import { InferInsertModel, InferSelectModel } from "drizzle-orm";
export const element = pgTable("element", {
id: bigserial("id", { mode: "bigint" }).primaryKey(),
surveyId: bigint("survey_id", { mode: "number" })
.notNull()
.references(() => propertyConditionSurvey.id),
elementType: elementType("element_type").notNull(),
elementInstance: integer("element_instance").notNull(),
});
export type ElementRow = InferSelectModel<typeof element>;
export type NewElementRow = InferInsertModel<typeof element>;

View file

@ -0,0 +1,206 @@
import { pgEnum } from "drizzle-orm/pg-core";
export const elementType = pgEnum("element_type", [
"property",
"property_construction_type",
"property_classification",
"property_age_band",
"storey_count",
"floor_level",
"floor_level_front_door",
"accessible_housing_register",
"asbestos",
"quality_standard",
"ccu",
"passenger_lift",
"stairlift",
"disabled_hoist_tracking",
"disabled_facilities",
"steps_to_front_door",
"roof",
"pitched_roof_covering",
"flat_roof_covering",
"rainwater_goods",
"loft_insulation",
"porch_canopy",
"chimney",
"fascia",
"soffit",
"fascia_soffit_bargeboards",
"gutters",
"store_roof",
"garage_roof",
"garage_and_store_roof",
"external_wall",
"external_noise_insulation",
"primary_wall",
"secondary_wall",
"downpipes",
"external_decoration",
"cladding",
"spandrel_panels",
"garage_walls",
"party_wall_fire_break",
"external_brickwork_pointing",
"internal_downpipes_external_area",
"external_windows",
"communal_windows",
"secondary_glazing",
"store_windows",
"garage_windows",
"garage_and_store_windows",
"external_door",
"front_door",
"rear_door",
"store_door",
"garage_door",
"garage_and_store_door",
"communal_entrance_door",
"main_door",
"block_entrance_door",
"lintel",
"patio_french_door",
"door_entry_handset",
"paths_and_hardstandings",
"parking_areas",
"boundary_walls",
"front_fencing",
"rear_fencing",
"side_fencing",
"rear_gate",
"front_gate",
"gates",
"retaining_walls",
"private_balcony",
"balcony_balustrade",
"outbuildings",
"garage_structure",
"paving",
"roads",
"soil_and_vent",
"solar_thermals",
"drop_kerb",
"outbuilding_overhaul",
"external_structural_defects",
"access_ramp",
"kitchen",
"kitchen_space_layout",
"tenant_installed_kitchen",
"kitchen_extractor_fan",
"bathroom",
"secondary_bathroom",
"secondary_toilet",
"bathroom_extractor_fan",
"additional_wc_or_whb",
"bathroom_remaining_life_source",
"kitchen_remaining_life_source",
"central_heating",
"heating_boiler",
"heating_distribution",
"secondary_heating",
"hot_water_system",
"cold_water_storage",
"heating_system",
"boiler_fuel",
"water_heating",
"programmable_heating",
"community_heating",
"gas_available",
"heat_recovery_units",
"heating_improvements",
"electrical_wiring",
"consumer_unit",
"smoke_detection",
"heat_detection",
"carbon_monoxide_detection",
"fire_door_rating",
"fire_risk_assessment",
"internal_wiring",
"electrics",
"communal_heating",
"communal_boiler",
"communal_electrics",
"communal_fire_alarm",
"communal_emergency_lighting",
"communal_door_entry",
"communal_cctv",
"communal_bin_store",
"communal_bin_store_doors",
"communal_bin_store_walls",
"communal_bin_store_roof",
"communal_refuse_chute",
"communal_floor_covering",
"communal_kitchen",
"communal_bathroom",
"communal_toilets",
"communal_gates",
"communal_lift",
"communal_passenger_lift",
"communal_balcony_walkway",
"communal_entrance",
"communal_internal_decorations",
"communal_internal_floor",
"communal_walkways",
"communal_external_doors",
"communal_stairs",
"communal_aerial",
"communal_aov",
"communal_internal_doors",
"communal_lateral_mains",
"communal_lighting",
"communal_lighting_conductor",
"communal_store_roof",
"communal_store_walls",
"communal_store_doors",
"communal_warden_call_system",
"communal_bms",
"communal_booster_pump",
"communal_dry_riser",
"communal_wet_riser",
"communal_cold_water_storage",
"communal_sprinkler",
"communal_plug_sockets",
"communal_circulation_space",
"ffhh_damp",
"ffhh_hold_and_cold_water",
"ffhh_drainage_lavatories",
"ffhh_neglected",
"ffhh_natural_light",
"ffhh_ventilation",
"ffhh_food_prep_and_washup",
"ffhh_unsafe_layout",
"ffhh_unstable_building",
"hhsrs_damp_and_mould",
"hhsrs_excess_cold",
"hhsrs_excess_heat",
"hhsrs_asbestos_and_mmf",
"hhsrs_biocides",
"hhsrs_carbon_monoxide",
"hhsrs_lead",
"hhsrs_radiation",
"hhsrs_uncombusted_fuel_gas",
"hhsrs_volatile_organic_compounds",
"hhsrs_crowding_and_space",
"hhsrs_entry_by_intruders",
"hhsrs_lighting",
"hhsrs_noise",
"hhsrs_domestic_hygiene_pests_refuse",
"hhsrs_food_safety",
"hhsrs_personal_hygiene_sanitation",
"hhsrs_water_supply",
"hhsrs_falls_associated_with_baths",
"hhsrs_falls_on_level_surfaces",
"hhsrs_falls_on_stairs",
"hhsrs_falls_between_levels",
"hhsrs_electrical_hazards",
"hhsrs_fire",
"hhsrs_flames_hot_surfaces",
"hhsrs_collision_and_entrapment",
"hhsrs_collision_hazards_low_headroom",
"hhsrs_explosions",
"hhsrs_ergonomics",
"hhsrs_structural_collapse",
"hhsrs_amenities",
]);
export type ElementType = typeof elementType.enumValues[number];

View file

@ -0,0 +1,23 @@
import { InferInsertModel, InferSelectModel } from "drizzle-orm";
import {
pgTable,
bigserial,
bigint,
date,
text,
} from "drizzle-orm/pg-core";
export const propertyConditionSurvey = pgTable("property_condition_survey", {
id: bigserial("id", { mode: "bigint" }).primaryKey(),
uprn: bigint("uprn", { mode: "number" }).notNull(),
date: date("date").notNull(),
source: text("source").notNull(),
});
export type PropertyConditionSurveyRow =
InferSelectModel<typeof propertyConditionSurvey>;
export type NewPropertyConditionSurveyRow =
InferInsertModel<typeof propertyConditionSurvey>;

View file

@ -106,7 +106,7 @@ const updateSettings = async ({
userId: userId.toString(),
action: "update",
}),
}
},
);
const permissionsData = await permissionsReponse.json();
@ -181,7 +181,7 @@ async function deletePortfolio({
userId: userId.toString(),
action: "delete",
}),
}
},
);
const permissionsData = await permissionsReponse.json();
@ -201,7 +201,7 @@ async function deletePortfolio({
if (!response.ok) {
throw new Error(
"deletePortfolio has been called into action but utterly failed to do the API handoff"
"deletePortfolio has been called into action but utterly failed to do the API handoff",
);
}
@ -241,13 +241,13 @@ export default function PortfolioSettings({
onError: (error) => {
console.error(
"Because the API hand off failed, we're right back here at the mutation station",
error
error,
);
},
});
const [portfolioName, setPortfolioName] = useState(
portfolioSettingsData.name
portfolioSettingsData.name,
);
const [portfolioBudget, setPortfolioBudget] = useState<
@ -255,11 +255,11 @@ export default function PortfolioSettings({
>(portfolioSettingsData.budget);
const [portfolioGoal, setPortfolioGoal] = useState(
portfolioSettingsData.goal
portfolioSettingsData.goal,
);
const [portfolioStatus, setPortfolioStatus] = useState(
portfolioSettingsData.status
portfolioSettingsData.status,
);
// Set up state for deleteModal and deleteConfirmation
@ -268,9 +268,13 @@ export default function PortfolioSettings({
const [deleteConfirmationByName, setDeleteConfirmationByName] = useState("");
if (session.status === "loading") {
// You can return a loading spinner or placeholder here
return <div>Loading...</div>;
}
if (!session.data) {
// The user is not logged in, redirect them to sign in
router.push("/");
return null;
}
@ -473,7 +477,14 @@ export default function PortfolioSettings({
<UsersPermissionsCard portfolioId={portfolioId} />
<div className="rounded-md border border-red-500 mt-2">
<Table>
<TableHead className="text-lg text-brandblue">Danger Zone:</TableHead>
<TableHeader>
<TableRow>
<TableHead colSpan={2} className="text-lg text-brandblue">
Danger Zone:
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableHead className="text-brandblue">
@ -483,6 +494,7 @@ export default function PortfolioSettings({
assigned to this portfolio
</p>
</TableHead>
<TableCell className="flex justify-end">
<Button
className="bg-red-700 w-42"