Skip to content

Commit

Permalink
CurrentHeatingSystem form checking (#158)
Browse files Browse the repository at this point in the history
* WIP: explore abstract in zod types

Co-authored-by: plocket <[email protected]>
Co-authored-by: Clayton Schneider <[email protected]>
Co-authored-by: Leopardfoot <[email protected]>
Co-authored-by: Camden Blatchly <[email protected]>

* Added zod types for CurrentHeatingSystem and change types/index.ts to zod.

Co-authored-by: Camden Blatchly <[email protected]>
Co-authored-by: plocket <[email protected]>

* fix display analysis+charts to use zod

---------

Co-authored-by: plocket <[email protected]>
Co-authored-by: Clayton Schneider <[email protected]>
Co-authored-by: Leopardfoot <[email protected]>
Co-authored-by: Camden Blatchly <[email protected]>
  • Loading branch information
5 people authored Mar 27, 2024
1 parent 627ec5e commit 5787b7d
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 115 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { type HeatLoadAnalysis } from '#types/index.js'
import { type z } from 'zod'
import { type HeatLoadAnalysis as HeatLoadAnalysisZod} from '#types/index'

type HeatLoadAnalysis = z.infer<typeof HeatLoadAnalysisZod>
export function AnalysisHeader() {
const heatLoadAnalysis: HeatLoadAnalysis = {
rulesEngineVersion: 'Beta 1',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { Button } from '#/app/components/ui/button.tsx'
import { Input } from '#/app/components/ui/input.tsx'
import { Label } from '#/app/components/ui/label.tsx'

export function CurrentHeatingSystem() {
type CurrentHeatingSystemProps = {fields: any};

export function CurrentHeatingSystem(props: CurrentHeatingSystemProps) {
const titleClass = 'text-5xl font-extrabold tracking-wide'
const descriptiveClass = 'mt-2 text-sm text-slate-500'
const componentMargin = 'mt-10'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { AnalysisHeader } from './AnalysisHeader.tsx'
import { EnergyUseHistoryChart } from './EnergyUseHistoryChart.tsx'
import { Button } from '#/app/components/ui/button.tsx'

import { FieldMetadata, useForm } from '@conform-to/react'

Check warning on line 1 in heat-stack/app/components/ui/heat/CaseSummaryComponents/EnergyUseHistory.tsx

View workflow job for this annotation

GitHub Actions / ⬣ ESLint

'FieldMetadata' is defined but never used

Check warning on line 1 in heat-stack/app/components/ui/heat/CaseSummaryComponents/EnergyUseHistory.tsx

View workflow job for this annotation

GitHub Actions / ⬣ ESLint

'useForm' is defined but never used
import { Form } from '@remix-run/react'

Check warning on line 2 in heat-stack/app/components/ui/heat/CaseSummaryComponents/EnergyUseHistory.tsx

View workflow job for this annotation

GitHub Actions / ⬣ ESLint

'Form' is defined but never used
import { ErrorList } from "./ErrorList.tsx"

Check warning on line 3 in heat-stack/app/components/ui/heat/CaseSummaryComponents/EnergyUseHistory.tsx

View workflow job for this annotation

GitHub Actions / ⬣ ESLint

`./ErrorList.tsx` import should occur after import of `./EnergyUseHistoryChart.tsx`
import { Input } from '#/app/components/ui/input.tsx'
import { Label } from '#/app/components/ui/label.tsx'
import { FieldMetadata, useForm } from '@conform-to/react'
import { Button } from '#/app/components/ui/button.tsx'

import { AnalysisHeader } from './AnalysisHeader.tsx'
import { EnergyUseHistoryChart } from './EnergyUseHistoryChart.tsx'


// type EnergyUseProps = {fields: any};

// export function EnergyUseHistory(props: EnergyUseProps) {
export function EnergyUseHistory() {
const titleClass = 'text-5xl font-extrabold tracking-wide mt-10'
const subtitleClass = 'text-2xl font-semibold text-zinc-950 mt-9'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type NaturalGasBillRecord } from '#types/index.js'
import { type z } from 'zod'
import { type NaturalGasBillRecord as NaturalGasBillRecordZod } from '#types/index'
import { Checkbox } from '../../../../components/ui/checkbox.tsx'

import {
Expand All @@ -10,6 +11,7 @@ import {
TableRow,
} from '../../../../components/ui/table.tsx'

type NaturalGasBillRecord = z.infer<typeof NaturalGasBillRecordZod>
const naturalGasBillRecord01: NaturalGasBillRecord = {
periodStartDate: new Date('12/08/2017'),
periodEndDate: new Date('01/07/2018'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,7 @@ import { FieldMetadata, useForm } from '@conform-to/react'

// // return redirect(`/inputs1`)
// }
type HomeInformationProps = {fields: {
name: FieldMetadata<string, {
name: string;
address: string;
livingSpace: number;
}, string[]>;
address: FieldMetadata<string, {
name: string;
address: string;
livingSpace: number;
}, string[]>;
livingSpace: FieldMetadata<any>;
}};
type HomeInformationProps = {fields: any};

export function HomeInformation(props: HomeInformationProps) {

Expand Down
2 changes: 1 addition & 1 deletion heat-stack/app/routes/_heat+/inputs2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { CurrentHeatingSystem } from '../../components/ui/heat/CaseSummaryCompon
export default function Inputs2() {
return (
<div>
<CurrentHeatingSystem />
<CurrentHeatingSystem fields={{}}/>
</div>
)
}
84 changes: 53 additions & 31 deletions heat-stack/app/routes/_heat+/single.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { z } from 'zod'

// Ours
import { ErrorList } from '#app/components/ui/heat/CaseSummaryComponents/ErrorList.tsx'
import { Home, Location, Case } from '../../../types/index.ts'
import { CurrentHeatingSystem } from '../../components/ui/heat/CaseSummaryComponents/CurrentHeatingSystem.tsx'
import { EnergyUseHistory } from '../../components/ui/heat/CaseSummaryComponents/EnergyUseHistory.tsx'
import { HomeInformation } from '../../components/ui/heat/CaseSummaryComponents/HomeInformation.tsx'
Expand All @@ -20,53 +21,71 @@ const addressMaxLength = 100

/** Modeled off the conform example at
* https://github.com/epicweb-dev/web-forms/blob/b69e441f5577b91e7df116eba415d4714daacb9d/exercises/03.schema-validation/03.solution.conform-form/app/routes/users%2B/%24username_%2B/notes.%24noteId_.edit.tsx#L48 */
const HomeInformationSchema = z.object({
name: z.string().min(1).max(nameMaxLength),
address: z.string().min(1).max(addressMaxLength),
livingSpace: z.number().min(1),
})

const EnergyUseSchema = z.object({
fuelType: z.string().min(1).max(nameMaxLength),
efficiency: z.string().min(1).max(addressMaxLength),
override: z.number().min(1),
setpoint: z.string().min(1).max(nameMaxLength),
setbackTemp: z.string().min(1).max(addressMaxLength),
setbackHours: z.number().min(1),
// const HomeInformationSchema = {
// name: z.string().min(1).max(nameMaxLength),
// address: z.string().min(1).max(addressMaxLength),
// livingSpace: z.number().min(1),
// }
// // type Home = z.infer<typeof HomeSchema>

// // TODO Next: Ask an LLM how we get fuelType out of HomeSchema from zod

const HomeFormSchema = Home.pick({ livingArea: true })
.and(Location.pick({ address: true }))
.and(Case.pick({ name: true }))

const CurrentHeatingSystemSchema = Home.pick({
fuelType: true,
heatingSystemEfficiency: true,
thermostatSetPoint: true,
setbackTemperature: true,
setbackHoursPerDay: true,
designTemperatureOverride: true,
})

const Schema = HomeFormSchema.and(CurrentHeatingSystemSchema)

// const EnergyUseSchema = '';

export async function action({ request, params }: ActionFunctionArgs) {
// Checks if url has a homeId parameter, throws 400 if not there
// invariantResponse(params.homeId, 'homeId param is required')

const formData = await request.formData()
const submission = parseWithZod(formData, {
schema: HomeInformationSchema,
schema: Schema,
})

if(submission.status !== "success") {
if (submission.status !== 'success') {
return submission.reply()
// submission.reply({
// // You can also pass additional error to the `reply` method
// formErrors: ['Submission failed'],
// fieldErrors: {
// address: ['Address is invalid'],
// },
// // or avoid sending the the field value back to client by specifying the field names
// hideFields: ['password'],
// }),
// {status: submission.status === "error" ? 400 : 200}
// submission.reply({
// // You can also pass additional error to the `reply` method
// formErrors: ['Submission failed'],
// fieldErrors: {
// address: ['Address is invalid'],
// },

// // or avoid sending the the field value back to client by specifying the field names
// hideFields: ['password'],
// }),
// {status: submission.status === "error" ? 400 : 200}
}

// TODO NEXT WEEK
// - [x] Server side error checking/handling
// - [x] ~Save to cookie and redirect to next form~ Put everything on the same page
// - [ ] - Get zod and Typescript to play nice
// - [ ] (We're here) Build form #2
// - [ ] Build form #3
// - [ ] Form errors (if we think of a use case - 2 fields conflicting...)

const { name, address, livingSpace } = submission.value
const { name, address, livingArea, fuelType,
heatingSystemEfficiency,
thermostatSetPoint,
setbackTemperature,
setbackHoursPerDay,
designTemperatureOverride } = submission.value

// await updateNote({ id: params.noteId, title, content })

Expand All @@ -78,17 +97,20 @@ export default function Inputs() {
const [form, fields] = useForm({
lastResult,
onValidate({ formData }) {
return parseWithZod(formData, { schema: HomeInformationSchema })
},
defaultValue: {

return parseWithZod(formData, { schema: Schema })
},
defaultValue: {},
shouldValidate: 'onBlur',
})

return (
<>
<Form id={form.id} method="post" onSubmit={form.onSubmit} action="/single">
<Form
id={form.id}
method="post"
onSubmit={form.onSubmit}
action="/single"
>
<HomeInformation fields={fields} />
<CurrentHeatingSystem fields={fields} />
<EnergyUseHistory />
Expand Down
62 changes: 0 additions & 62 deletions heat-stack/types/index.d.ts

This file was deleted.

68 changes: 68 additions & 0 deletions heat-stack/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { z } from 'zod';

// JS team wants to discuss this name
export const Case = z.object({
name: z.string()
})

export const HeatLoadAnalysis = z.object({
rulesEngineVersion: z.string(),
estimatedBalancePoint: z.number(),
otherFuelUsage: z.number(),
averageIndoorTemperature: z.number(),
differenceBetweenTiAndTbp: z.number(),
/**
* designTemperature in Fahrenheit
*/
designTemperature: z.number().max(-10).min(50),
wholeHomeHeatLossRate: z.number(),
standardDeviationHeatLossRate: z.number(),
averageHeatLoad: z.number(),
maximumHeatLoad: z.number(),
});

export const Home = z.object({
/**
* unit: square feet
*/
livingArea: z.number().min(500).max(10000),
fuelType: z.enum(['Natural Gas','Oil','Propane']),
designTemperatureOverride: z.number(),
/**
* unit: percentage in decimal numbers, but not 0 to 1
*/
heatingSystemEfficiency: z.number().min(60).max(100),
thermostatSetPoint: z.number(),
setbackTemperature: z.number(),
setbackHoursPerDay: z.number(),
numberOfOccupants: z.number(),
estimatedWaterHeatingEfficiency: z.number(),
standByLosses: z.number(),
});

export const Location = z.object({
address: z.string(),
});

export const NaturalGasBill = z.object({
provider: z.string(),
});

export const NaturalGasBillRecord = z.object({
periodStartDate: z.date(),
periodEndDate: z.date(),
usageTherms: z.number(),
inclusionOverride: z.enum(['Include', 'Do not include', 'Include in other analysis']),
});

export const OilPropaneBill = z.object({
provider: z.string(),
precedingDeliveryDate: z.date(),
});

export const OilPropaneBillRecord = z.object({
periodStartDate: z.date(),
periodEndDate: z.date(),
gallons: z.number(),
inclusionOverride: z.enum(['Include', 'Do not include', 'Include in other analysis']),
});

0 comments on commit 5787b7d

Please sign in to comment.