Skip to content

Commit

Permalink
Add home info conform (codeforboston#150)
Browse files Browse the repository at this point in the history
* Did a first draft of zod+conform v1

Must move action to a route for submit to work.

Co-authored-by: plocket <[email protected]>
Co-authored-by: Clayton Schneider <[email protected]>
Co-authored-by: teykamp <[email protected]>

* Route for input1 and Conform on HomeInformation

Co-authored-by: plocket <[email protected]>
Co-authored-by: Clayton Schneider <[email protected]>
Co-authored-by: teykamp <[email protected]>
Co-authored-by: Jeff Korenstein <[email protected]>
Co-authored-by: Leopardfoot <[email protected]>
Co-authored-by: hectorbenitez19 <[email protected]>

* Client side conform form validation

Co-authored-by: Clayton Schneider <[email protected]>
Co-authored-by: hectorbenitez19 <[email protected]>
Co-authored-by: Michael Hughes <[email protected]>
Co-authored-by: Leopardfoot <[email protected]>

* Server side checking/handling

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

* consolidated form to single page

Co-authored-by: Clayton Schneider <[email protected]>
Co-authored-by: Michael Hughes <[email protected]>
Co-authored-by: Leopardfoot <[email protected]>
Co-authored-by: plocket <[email protected]>

* WIP form #2

* Finished HomeInformation on single, CurrentHeatingSystem WIP

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]>

---------

Co-authored-by: plocket <[email protected]>
Co-authored-by: Clayton Schneider <[email protected]>
Co-authored-by: teykamp <[email protected]>
Co-authored-by: teykamp <[email protected]>
Co-authored-by: Jeff Korenstein <[email protected]>
Co-authored-by: Leopardfoot <[email protected]>
Co-authored-by: hectorbenitez19 <[email protected]>
Co-authored-by: Michael Hughes <[email protected]>
Co-authored-by: Camden Blatchly <[email protected]>
  • Loading branch information
10 people authored Mar 20, 2024
1 parent 674ec63 commit 8791631
Show file tree
Hide file tree
Showing 6 changed files with 285 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { Label } from '#/app/components/ui/label.tsx'

export function CurrentHeatingSystem() {
const titleClass = 'text-5xl font-extrabold tracking-wide'
const subtitleClass = 'text-2xl font-semibold text-zinc-950 mt-9'
const descriptiveClass = 'mt-2 text-sm text-slate-500'
const componentMargin = 'mt-10'
const subtitleClass = 'text-2xl font-semibold text-zinc-950 mt-9'

return (
<div>
Expand Down Expand Up @@ -110,10 +110,7 @@ export function CurrentHeatingSystem() {
</div>
</div>

<div>
<h6 className={`${subtitleClass}`}>Heating Fuel Usage</h6>
<Button type="submit">Upload</Button>
</div>

</Form>

{/* removed temporarily for single page app format */}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
import { AnalysisHeader } from './AnalysisHeader.tsx'
import { EnergyUseHistoryChart } from './EnergyUseHistoryChart.tsx'
import { Button } from '#/app/components/ui/button.tsx'

import { Form } from '@remix-run/react'
import { ErrorList } from "./ErrorList.tsx"
import { Input } from '#/app/components/ui/input.tsx'
import { Label } from '#/app/components/ui/label.tsx'
import { FieldMetadata, useForm } from '@conform-to/react'


export function EnergyUseHistory() {
const titleClass = 'text-5xl font-extrabold tracking-wide mt-10'
const subtitleClass = 'text-2xl font-semibold text-zinc-950 mt-9'

return (
<div>
<h2 className={`${titleClass}`}>Energy Use History</h2>
<div>
<Button type="submit">Upload</Button>
</div>
<AnalysisHeader />
<EnergyUseHistoryChart />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export function ErrorList({
id,
errors,
}: {
id?: string
errors?: Array<string> | null
}) {
return errors?.length ? (
<ul id={id} className="flex flex-col gap-1">
{errors.map((error, i) => (
<li key={i} className="text-[10px] text-foreground-destructive">
{error}
</li>
))}
</ul>
) : null
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,63 @@
import { Form } from '@remix-run/react'
// import { Button } from '#/app/components/ui/button.tsx'
import { ErrorList } from "./ErrorList.tsx"
import { Button } from '#/app/components/ui/button.tsx'
import { Input } from '#/app/components/ui/input.tsx'
import { Label } from '#/app/components/ui/label.tsx'
import { FieldMetadata, useForm } from '@conform-to/react'

// /** THE BELOW PROBABLY NEED TO MOVE TO A ROUTE RATHER THAN A COMPONENT, including action function, */
// // import { redirect } from '@remix-run/react'
// import { json, ActionFunctionArgs } from '@remix-run/node'
// import { invariantResponse } from '@epic-web/invariant'
// import { parseWithZod } from '@conform-to/zod'
// import { z } from 'zod'

// const nameMaxLength = 1
// const addressMaxLength = 1

// /** 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().max(nameMaxLength),
// address: z.string().max(addressMaxLength),
// livingSpace: z.number().min(1),
// })

// export async function action({ request, params }: ActionFunctionArgs) {
// invariantResponse(params.homeId, 'homeId param is required')

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

// if (!submission.value) {
// return json({ status: 'error', submission } as const, {
// status: 400,
// })
// }
// const { name, address, livingSpace } = submission.value

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

// // 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>;
}};

export function HomeInformation(props: HomeInformationProps) {

export function HomeInformation() {
const titleClass = 'text-5xl font-extrabold tracking-wide'
const subtitleClass = 'text-2xl font-semibold text-zinc-950 mt-9'
const descriptiveClass = 'mt-2 text-sm text-slate-500'
Expand All @@ -12,18 +66,20 @@ export function HomeInformation() {
<div>
<h2 className={`${titleClass}`}>Home Information</h2>

<Form method="post" action="/homes">
{/* <Form method="post" action="/inputs1"> */}
<div className={`${componentMargin}`}>
<h6 className={`${subtitleClass}`}>Resident/Client</h6>

<div className="mt-4 flex space-x-4">
<div>
<Label htmlFor="firstName">First Name</Label>
<Input name="firstName" id="firstName" type="text" />
</div>
<div>
<Label htmlFor="lastName">Last Name</Label>
<Input name="lastName" id="lastName" type="text" />
<Label htmlFor="name">Name</Label>
<Input name={props.fields.name.name} id="name" type="text" />
<div className="min-h-[32px] px-4 pb-3 pt-1">
<ErrorList
id={props.fields.name.errorId}
errors={props.fields.name.errors}
/>
</div>
</div>
</div>
</div>
Expand All @@ -33,23 +89,13 @@ export function HomeInformation() {

<div className="mt-4 flex space-x-4">
<div>
<Label htmlFor="address">Street address</Label>
<Input name="address" id="address" type="text" />
<Input name="addressTwo" id="adressTwo" type="text" />

<div className="mt-4 flex space-x-4">
<div>
<Label htmlFor="city">City/Town</Label>
<Input name="city" id="city" type="text" />
</div>
<div>
<Label htmlFor="state">State</Label>
<Input name="state" id="state" type="text" />
</div>
<div>
<Label htmlFor="zipcode">Zipcode</Label>
<Input name="zipcode" id="zipcode" type="text" />
</div>
<Label htmlFor="address">Address</Label>
<Input name={props.fields.address.name} id="address" type="text" />
<div className="min-h-[32px] px-4 pb-3 pt-1">
<ErrorList
id={props.fields.address.errorId}
errors={props.fields.address.errors}
/>
</div>
</div>
</div>
Expand All @@ -64,7 +110,13 @@ export function HomeInformation() {

<div className="mt-4 flex space-x-2">
<div>
<Input name="livingArea" id="livingArea" type="number" />
<Input name={props.fields.livingSpace.name} id="livingArea" type="number" />
<div className="min-h-[32px] px-4 pb-3 pt-1">
<ErrorList
id={props.fields.livingSpace.errorId}
errors={props.fields.livingSpace.errors}
/>
</div>
<p className={`${descriptiveClass}`}>
The home's above-grade, conditioned space
</p>
Expand All @@ -74,9 +126,10 @@ export function HomeInformation() {

{/* removed temporarily for single page app format */}
{/* <div>
<Button type="submit">Next ={'>'}</Button>
</div> */}
</Form>
<Button type="submit">Next ={'>'}</Button>
</div> */}

{/* </Form> */}
</div>
)
}
83 changes: 80 additions & 3 deletions heat-stack/app/routes/_heat+/Inputs1.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,86 @@


// Archived 03/19/2024


import { useForm } from '@conform-to/react'
import { HomeInformation } from '../../components/ui/heat/CaseSummaryComponents/HomeInformation.tsx'

/** THE BELOW PROBABLY NEED TO MOVE TO A ROUTE RATHER THAN A COMPONENT, including action function, */
// import { redirect } from '@remix-run/react'
import { json, ActionFunctionArgs } from '@remix-run/node'
import { parseWithZod } from '@conform-to/zod'
import { invariantResponse } from '@epic-web/invariant'
import { Form, redirect, useActionData } from '@remix-run/react'
import { z } from 'zod'
import { ErrorList } from '#app/components/ui/heat/CaseSummaryComponents/ErrorList.tsx'

const nameMaxLength = 50
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),
})

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,
})

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}
}

// TODO NEXT WEEK
// - [x] Server side error checking/handling
// - [ ] Save to cookie and redirect to next form
// - [ ] Build form #2 and #3
// - [ ] Form errors (if we think of a use case - 2 fields conflicting...)

const { name, address, livingSpace } = submission.value

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

return redirect(`/inputs1`)
}

export default function Inputs1() {
const lastResult = useActionData<typeof action>()
const [form, fields] = useForm({
lastResult,
onValidate({ formData }) {
return parseWithZod(formData, { schema: HomeInformationSchema })
},
defaultValue: {

},
shouldValidate: 'onBlur',
})

return (
<div>
<HomeInformation />
</div>

<Form id={form.id} method="post" onSubmit={form.onSubmit} action="/inputs1">
<HomeInformation fields={fields} />
<ErrorList id={form.errorId} errors={form.errors} />
</Form>
)
}
Loading

0 comments on commit 8791631

Please sign in to comment.