diff --git a/heat-stack/app/components/ui/heat/CaseSummaryComponents/CurrentHeatingSystem.tsx b/heat-stack/app/components/ui/heat/CaseSummaryComponents/CurrentHeatingSystem.tsx index 5162cae3..cc3427d1 100644 --- a/heat-stack/app/components/ui/heat/CaseSummaryComponents/CurrentHeatingSystem.tsx +++ b/heat-stack/app/components/ui/heat/CaseSummaryComponents/CurrentHeatingSystem.tsx @@ -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 (
@@ -110,10 +110,7 @@ export function CurrentHeatingSystem() {
-
-
Heating Fuel Usage
- -
+ {/* removed temporarily for single page app format */} diff --git a/heat-stack/app/components/ui/heat/CaseSummaryComponents/EnergyUseHistory.tsx b/heat-stack/app/components/ui/heat/CaseSummaryComponents/EnergyUseHistory.tsx index 6afd08d6..45d8096a 100644 --- a/heat-stack/app/components/ui/heat/CaseSummaryComponents/EnergyUseHistory.tsx +++ b/heat-stack/app/components/ui/heat/CaseSummaryComponents/EnergyUseHistory.tsx @@ -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 (

Energy Use History

+
+ +
diff --git a/heat-stack/app/components/ui/heat/CaseSummaryComponents/ErrorList.tsx b/heat-stack/app/components/ui/heat/CaseSummaryComponents/ErrorList.tsx new file mode 100644 index 00000000..4789c7d6 --- /dev/null +++ b/heat-stack/app/components/ui/heat/CaseSummaryComponents/ErrorList.tsx @@ -0,0 +1,17 @@ +export function ErrorList({ + id, + errors, +}: { + id?: string + errors?: Array | null +}) { + return errors?.length ? ( + + ) : null +} \ No newline at end of file diff --git a/heat-stack/app/components/ui/heat/CaseSummaryComponents/HomeInformation.tsx b/heat-stack/app/components/ui/heat/CaseSummaryComponents/HomeInformation.tsx index 8d8ce730..82c9f9d9 100644 --- a/heat-stack/app/components/ui/heat/CaseSummaryComponents/HomeInformation.tsx +++ b/heat-stack/app/components/ui/heat/CaseSummaryComponents/HomeInformation.tsx @@ -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; + address: FieldMetadata; + livingSpace: FieldMetadata; +}}; + +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' @@ -12,18 +66,20 @@ export function HomeInformation() {

Home Information

-
+ {/* */}
Resident/Client
- - -
-
- - + + +
+ +
@@ -33,23 +89,13 @@ export function HomeInformation() {
- - - - -
-
- - -
-
- - -
-
- - -
+ + +
+
@@ -64,7 +110,13 @@ export function HomeInformation() {
- + +
+ +

The home's above-grade, conditioned space

@@ -74,9 +126,10 @@ export function HomeInformation() { {/* removed temporarily for single page app format */} {/*
- -
*/} - + +
*/} + + {/* */}
) } diff --git a/heat-stack/app/routes/_heat+/Inputs1.tsx b/heat-stack/app/routes/_heat+/Inputs1.tsx index 76bdb05f..ab449dd8 100644 --- a/heat-stack/app/routes/_heat+/Inputs1.tsx +++ b/heat-stack/app/routes/_heat+/Inputs1.tsx @@ -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() + const [form, fields] = useForm({ + lastResult, + onValidate({ formData }) { + return parseWithZod(formData, { schema: HomeInformationSchema }) + }, + defaultValue: { + + }, + shouldValidate: 'onBlur', + }) + return ( -
- -
+ +
+ + + ) } diff --git a/heat-stack/app/routes/_heat+/single.tsx b/heat-stack/app/routes/_heat+/single.tsx index 95617be5..4d6e82dc 100644 --- a/heat-stack/app/routes/_heat+/single.tsx +++ b/heat-stack/app/routes/_heat+/single.tsx @@ -1,15 +1,101 @@ +/** THE BELOW PROBABLY NEEDS TO MOVE TO A ROUTE RATHER THAN A COMPONENT, including action function, */ +// import { redirect } from '@remix-run/react' +import { useForm } from '@conform-to/react' +import { parseWithZod } from '@conform-to/zod' +import { invariantResponse } from '@epic-web/invariant' +import { json, ActionFunctionArgs } from '@remix-run/node' +import { Form, redirect, useActionData } from '@remix-run/react' +import { z } from 'zod' + +// Ours +import { ErrorList } from '#app/components/ui/heat/CaseSummaryComponents/ErrorList.tsx' 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' import HeatLoadAnalysis from './heatloadanalysis.tsx' +import { Button } from '#/app/components/ui/button.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), +}) + +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), +}) + +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 + // - [x] ~Save to cookie and redirect to next form~ Put everything on the same page + // - [ ] (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 + + // await updateNote({ id: params.noteId, title, content }) + + return redirect(`/inputs1`) +} export default function Inputs() { + const lastResult = useActionData() + const [form, fields] = useForm({ + lastResult, + onValidate({ formData }) { + return parseWithZod(formData, { schema: HomeInformationSchema }) + }, + defaultValue: { + + }, + shouldValidate: 'onBlur', + }) + return ( -
- - - + <> +
+ + + + + + -
+ ) }