Skip to content

Commit

Permalink
Refactor single.tsx to transform usage_data from Map to object for be…
Browse files Browse the repository at this point in the history
…tter readability and validation. Updated components (AnalysisHeader, EnergyUseHistory, EnergyUseHistoryChart) to handle usage_data as an object instead of Map. Introduced mapToObject function in single.tsx to initiate this change and propagate it to other components. Enhanced Zod type definitions across the components for stronger type validation and clarity. These changes improve the organization and readability of the code related to types in the heat-stack/types directory.
  • Loading branch information
TBardini committed Sep 30, 2024
1 parent c494646 commit 1ab7eda
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 113 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { type z } from 'zod'
import { type HeatLoadAnalysisZod } from '#types/index'
import { type z } from 'zod';
import { usageDataSchema, BillingRecordsSchema, SummaryOutputSchema } from '#types/index'

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

View workflow job for this annotation

GitHub Actions / ⬣ Heat-Stack - ESLint

All imports in the declaration are only used as types. Use `import type`

type HeatLoadAnalysisZod = z.infer<typeof HeatLoadAnalysisZod>
export function AnalysisHeader(props: { usage_data: any }) {

// type HeatLoadAnalysisZod = z.infer<typeof HeatLoadAnalysisZod>
type usageDataZod = z.infer<typeof usageDataSchema>
type BillingRecordsZod = z.infer<typeof BillingRecordsSchema>
type SummaryOutputZod = z.infer<typeof SummaryOutputSchema>

export function AnalysisHeader({ usage_data }: { usage_data: usageDataZod}) {
// Example usage_data
// new Map([[
// "estimated_balance_point",
Expand Down Expand Up @@ -34,27 +38,29 @@ export function AnalysisHeader(props: { usage_data: any }) {
// 3312125.0171753373
// ]])

const summaryOutputs = props.usage_data?.get('summary_output')
// Extract the summary_output from usage_data
const summaryOutputs: SummaryOutputZod | undefined = usage_data?.summary_output;

// Calculate the number of billing periods included in Heating calculations
const heatingAnalysisTypeRecords = props.usage_data
?.get('billing_records')
?.filter((billingRecord: any) => billingRecord.get('analysis_type') == 1)
const heatingAnalysisTypeRecords = usage_data?.billing_records?.filter(
(billingRecord: BillingRecordsZod) => billingRecord.analysis_type === 1,
);

const recordsIncludedByDefault = heatingAnalysisTypeRecords?.filter(
(billingRecord: any) =>
billingRecord.get('default_inclusion_by_calculation') == true &&
billingRecord.get('inclusion_override') == false,
).length
(billingRecord:BillingRecordsZod) =>
billingRecord.default_inclusion_by_calculation === true &&
billingRecord.inclusion_override === false,
).length;

const recordsIncludedByOverride = heatingAnalysisTypeRecords?.filter(
(billingRecord: any) =>
billingRecord.get('default_inclusion_by_calculation') == false &&
billingRecord.get('inclusion_override') == true,
).length
(billingRecord: BillingRecordsZod) =>
billingRecord.default_inclusion_by_calculation === false &&
billingRecord.inclusion_override === true,
).length;

const numRecordsForHeatingCalculations =
recordsIncludedByDefault + recordsIncludedByOverride
(recordsIncludedByDefault || 0) + (recordsIncludedByOverride || 0);


return (
<div className="section-title">
Expand All @@ -64,14 +70,14 @@ export function AnalysisHeader(props: { usage_data: any }) {
<div className="item-title-small">
Average Indoor Temperature <br />
<div className="item">
{summaryOutputs?.get('average_indoor_temperature')} °F
</div>{' '}
{summaryOutputs?.average_indoor_temperature} °F
</div>
<br />
Balance Point Temperature
<br />
<div className="item">
{summaryOutputs?.get('estimated_balance_point')} °F
</div>{' '}
{summaryOutputs?.estimated_balance_point} °F
</div>
<br />
</div>
</div>
Expand All @@ -83,8 +89,8 @@ export function AnalysisHeader(props: { usage_data: any }) {
Daily non-heating Usage <br />
<div className="item">
{/* Rounding to two decimal places */}
{summaryOutputs?.get('other_fuel_usage').toFixed(2)} therms
</div>{' '}
{summaryOutputs?.other_fuel_usage?.toFixed(2)} therms
</div>
</div>
</div>
<div className="basis-1/3">
Expand All @@ -93,19 +99,17 @@ export function AnalysisHeader(props: { usage_data: any }) {
<div className="item">
{/* Rounding to two decimal places */}
{(
summaryOutputs?.get('standard_deviation_of_heat_loss_rate') *
100
).toFixed(2)}{' '}
summaryOutputs?.standard_deviation_of_heat_loss_rate * 100
)?.toFixed(2)}{' '}
%
</div>{' '}
</div>
<br />
Whole-home UA
<br />
<div className="item">
{/* Rounding to zero decimal places */}
{summaryOutputs?.get('whole_home_heat_loss_rate').toFixed(0)}{' '}
BTU/h-°F
</div>{' '}
{summaryOutputs?.whole_home_heat_loss_rate?.toFixed(0)} BTU/h-°F
</div>
<br />
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Upload } from 'lucide-react'
import { Suspense, /* lazy */ } from 'react'

import { Button } from '#/app/components/ui/button.tsx'
import { type usageDataSchema } from '#/types/types.ts'
import { AnalysisHeader } from './AnalysisHeader.tsx'
import { EnergyUseHistoryChart } from './EnergyUseHistoryChart.tsx'

Expand All @@ -11,40 +11,46 @@ import { EnergyUseHistoryChart } from './EnergyUseHistoryChart.tsx'
// import { Input } from '#/app/components/ui/input.tsx'
// import { Label } from '#/app/components/ui/label.tsx'


// export function EnergyUseHistory(props: EnergyUseProps) {
export function EnergyUseHistory(props: { usage_data: any }) {
// console.log(`EnergyUseHistory:`, props.usage_data);
export function EnergyUseHistory({
usage_data,
}: {
usage_data: usageDataSchema
}) {
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} pb-6`}>Energy Use History</h2>
<div>
<Suspense fallback={'<div>Blah</div>'}>
<input
id="energy_use_upload"
aria-label="Upload your energy billing company's bill."
//onChange
accept=".xml,.csv,application/xml,text/xml,text/csv,application/csv,application/x-csv,text/comma-separated-values,text/x-comma-separated-values"
name="energy_use_upload"
type="file"
/>
<Button type="submit"> <Upload className="h-4 w-4 mr-2" /> Upload</Button>
</Suspense>
(<a className="inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80 gap-1" href="https://github.com/codeforboston/home-energy-analysis-tool/issues/162#issuecomment-2246594484">Get example file here</a>)

</div>
{props.usage_data && <AnalysisHeader usage_data={ props.usage_data } /> }
{props.usage_data && <EnergyUseHistoryChart usage_data={props.usage_data} />}

<input
id="energy_use_upload"
aria-label="Upload your energy billing company's bill."
accept=".xml,.csv,application/xml,text/xml,text/csv,application/csv,application/x-csv,text/comma-separated-values,text/x-comma-separated-values"
name="energy_use_upload"
type="file"
/>
<Button type="submit">
<Upload className="mr-2 h-4 w-4" /> Upload
</Button>

<a
className="inline-flex items-center gap-1 rounded-md border border-transparent bg-secondary px-2.5 py-0.5 text-xs font-semibold text-secondary-foreground transition-colors hover:bg-secondary/80 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
href="https://github.com/codeforboston/home-energy-analysis-tool/issues/162#issuecomment-2246594484"
>
Get example file here
</a>

{usage_data && (
<>
<AnalysisHeader usage_data={usage_data} />
<EnergyUseHistoryChart usage_data={usage_data} />
</>
)}
</div>
)
}



// const file = event.target.files?.[0]

// if (file) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useState, useEffect } from "react"
import { useState, useEffect } from 'react'
import { type z } from 'zod'
import { type usageDataSchema, BillingRecordsSchema } from '#/types/types.ts'

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

View workflow job for this annotation

GitHub Actions / ⬣ Heat-Stack - ESLint

All imports in the declaration are only used as types. Use `import type`
import {
NaturalGasUsageData,
type NaturalGasBillRecord as NaturalGasBillRecordZod,
Expand Down Expand Up @@ -57,31 +58,35 @@ import { tr } from '@faker-js/faker'
// naturalGasBillRecord04,
// ]

export function EnergyUseHistoryChart(props: { usage_data: Map<string, any> }) {
const [billingRecords, setBillingRecords] = useState<Map<string, any>[]>([])
export function EnergyUseHistoryChart({ usage_data }: { usage_data: usageDataSchema }) {
const [billingRecords, setBillingRecords] = useState<BillingRecordsSchema>([])

useEffect(() => {
const records = props.usage_data?.get('billing_records') || []
setBillingRecords(records as Map<string, any>[])
}, [props.usage_data])

const handleOverrideCheckboxChange = (index: number) => {
setBillingRecords((prevRecords) => {
const newRecords = [...prevRecords]
const periodMap = newRecords[index]

if (periodMap instanceof Map) {
const currentOverride = periodMap.get('inclusion_override')
periodMap.set('inclusion_override', !currentOverride)
newRecords[index] = periodMap
}

return newRecords
})
}
if (usage_data?.billing_records) {
// Process the billing records directly without converting from Map
setBillingRecords(usage_data.billing_records)
}
}, [usage_data])

const handleOverrideCheckboxChange = (index: number) => {
setBillingRecords((prevRecords) => {
const newRecords = [...prevRecords]
const period = newRecords[index]

if (period) {
const currentOverride = period.inclusion_override
// Toggle 'inclusion_override'
period.inclusion_override = !currentOverride

newRecords[index] = { ...period }
}

return newRecords
})
}

return (
<Table id='EnergyUseHistoryChart'>
<Table id="EnergyUseHistoryChart">
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">#</TableHead>
Expand All @@ -97,16 +102,16 @@ export function EnergyUseHistoryChart(props: { usage_data: Map<string, any> }) {
</TableRow>
</TableHeader>
<TableBody>
{billingRecords.map((period: Map<string, any>, index: number) => {
const startDate = new Date(period.get('period_start_date'))
const endDate = new Date(period.get('period_end_date'))
{billingRecords.map((period, index) => {
const startDate = new Date(period.period_start_date)
const endDate = new Date(period.period_end_date)

// Calculate days in period
const timeInPeriod = endDate.getTime() - startDate.getTime()
const daysInPeriod = Math.round(timeInPeriod / (1000 * 3600 * 24))

// Set Analysis Type image and checkbox setting
const analysisType = period.get('analysis_type')
const analysisType = period.analysis_type
let analysisType_Image = undefined
let overrideCheckboxDisabled = false

Expand All @@ -125,39 +130,39 @@ export function EnergyUseHistoryChart(props: { usage_data: Map<string, any> }) {
}

// Adjust inclusion for user input
let calculatedInclusion = period.get('default_inclusion_by_calculation')
if (period.get('inclusion_override')) {
let calculatedInclusion = period.default_inclusion_by_calculation
if (period.inclusion_override) {
calculatedInclusion = !calculatedInclusion
}

const variant = calculatedInclusion ? 'included' : 'excluded'

return (
<TableRow key={index}>
<TableCell className="font-medium">{index + 1}</TableCell>
<TableCell>
<img src={analysisType_Image} alt="Analysis Type" />
</TableCell>
<TableCell>{startDate.toLocaleDateString()}</TableCell>
<TableCell>{endDate.toLocaleDateString()}</TableCell>
<TableCell>{daysInPeriod}</TableCell>
<TableCell>{period.get('usage')}</TableCell>
<TableCell>
{period.get('whole_home_heat_loss_rate')
? period.get('whole_home_heat_loss_rate').toFixed(0)
: '-'}
</TableCell>
<TableCell>
<Checkbox
checked={period.get('inclusion_override')}
disabled={overrideCheckboxDisabled}
onClick={() => handleOverrideCheckboxChange(index)}
/>
</TableCell>
</TableRow>
)
})}
</TableBody>
</Table>
)
}
<TableRow key={index}>
<TableCell className="font-medium">{index + 1}</TableCell>
<TableCell>
<img src={analysisType_Image} alt="Analysis Type" />
</TableCell>
<TableCell>{startDate.toLocaleDateString()}</TableCell>
<TableCell>{endDate.toLocaleDateString()}</TableCell>
<TableCell>{daysInPeriod}</TableCell>
<TableCell>{period.usage}</TableCell>
<TableCell>
{period.whole_home_heat_loss_rate
? period.whole_home_heat_loss_rate.toFixed(0)
: '-'}
</TableCell>
<TableCell>
<Checkbox
checked={period.inclusion_override}
disabled={overrideCheckboxDisabled}
onClick={() => handleOverrideCheckboxChange(index)}
/>
</TableCell>
</TableRow>
)
})}
</TableBody>
</Table>
)
}
Loading

0 comments on commit 1ab7eda

Please sign in to comment.