-
-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
247 interactive chart #255
Changes from all commits
fc4022f
5b167ac
394f05c
e87d2a5
f806c88
3983fab
6a00207
6f23911
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -1,9 +1,7 @@ | ||||||||
import { type z } from 'zod' | ||||||||
import { type HeatLoadAnalysisZod } from '#types/index' | ||||||||
|
||||||||
type HeatLoadAnalysisZod = z.infer<typeof HeatLoadAnalysisZod> | ||||||||
export function AnalysisHeader(props: { usage_data: any }) { | ||||||||
import { type z } from 'zod'; | ||||||||
import { type UsageDataSchema } from '#/types/types.ts'; | ||||||||
|
||||||||
export function AnalysisHeader({ usage_data }: { usage_data: UsageDataSchema}) { | ||||||||
// Example usage_data | ||||||||
// new Map([[ | ||||||||
// "estimated_balance_point", | ||||||||
|
@@ -34,27 +32,30 @@ 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 = 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) => billingRecord.analysis_type === 1, | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Zod solution: #255 (comment) |
||||||||
// Do wee need this code instead? (billingRecord) => billingRecord.analysis_type !== "NOT_ALLOWED_IN_CALCULATIONS", | ||||||||
); | ||||||||
|
||||||||
const recordsIncludedByDefault = heatingAnalysisTypeRecords?.filter( | ||||||||
(billingRecord: any) => | ||||||||
billingRecord.get('default_inclusion_by_calculation') == true && | ||||||||
billingRecord.get('inclusion_override') == false, | ||||||||
).length | ||||||||
(billingRecord) => | ||||||||
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) => | ||||||||
billingRecord.default_inclusion_by_calculation === false && | ||||||||
billingRecord.inclusion_override === true, | ||||||||
).length; | ||||||||
|
||||||||
const numRecordsForHeatingCalculations = | ||||||||
recordsIncludedByDefault + recordsIncludedByOverride | ||||||||
(recordsIncludedByDefault || 0) + (recordsIncludedByOverride || 0); | ||||||||
|
||||||||
|
||||||||
return ( | ||||||||
<div className="section-title"> | ||||||||
|
@@ -64,14 +65,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> | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle the case where summaryOutputs is undefined
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Zod solution: #255 (comment) |
||||||||
<br /> | ||||||||
Balance Point Temperature | ||||||||
<br /> | ||||||||
<div className="item"> | ||||||||
{summaryOutputs?.get('estimated_balance_point')} °F | ||||||||
</div>{' '} | ||||||||
{summaryOutputs?.estimated_balance_point} °F | ||||||||
</div> | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Zod solution: #255 (comment) |
||||||||
<br /> | ||||||||
</div> | ||||||||
</div> | ||||||||
|
@@ -83,8 +84,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> | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Zod solution: #255 (comment) |
||||||||
</div> | ||||||||
</div> | ||||||||
<div className="basis-1/3"> | ||||||||
|
@@ -93,19 +94,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)}{' '} | ||||||||
% | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Zod solution: #255 (comment) |
||||||||
</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> | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Zod solution: #255 (comment) |
||||||||
<br /> | ||||||||
</div> | ||||||||
</div> | ||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import { useState, useEffect } from 'react' | ||
import { type z } from 'zod' | ||
import { NaturalGasUsageData, type NaturalGasBillRecord as NaturalGasBillRecordZod } from '#types/index' | ||
import { type UsageDataSchema, type BillingRecordsSchema } from '#/types/types.ts' | ||
import { Checkbox } from '../../../../components/ui/checkbox.tsx' | ||
|
||
import { | ||
|
@@ -15,7 +16,7 @@ | |
import NonHeatingUsage from './assets/NonHeatingUsage.png' | ||
import NotAllowedInCalculations from './assets/NotAllowedInCalculations.png' | ||
|
||
import { tr } from '@faker-js/faker' | ||
|
||
// type NaturalGasBillRecord = z.infer<typeof NaturalGasBillRecordZod> | ||
// const naturalGasBillRecord01: NaturalGasBillRecord = { | ||
|
@@ -53,23 +54,35 @@ | |
// naturalGasBillRecord04, | ||
// ] | ||
|
||
|
||
|
||
|
||
|
||
// export function EnergyUseHistoryChart(props: z.infer<typeof NaturalGasUsageData>) { | ||
export function EnergyUseHistoryChart(props: { usage_data: any }) { | ||
console.log("EnergyUseHistoryChart Component:", props.usage_data?.get('billing_records')) | ||
|
||
const billingRecords = props.usage_data?.get('billing_records') | ||
|
||
const handleOverrideCheckboxChange = () => { | ||
console.log("handleOverrideCheckboxChange") | ||
|
||
export function EnergyUseHistoryChart({ usage_data }: { usage_data: UsageDataSchema }) { | ||
const [billingRecords, setBillingRecords] = useState<BillingRecordsSchema>([]) | ||
|
||
useEffect(() => { | ||
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 = structuredClone(prevRecords) | ||
const period = newRecords[index] | ||
|
||
if (period) { | ||
const currentOverride = period.inclusion_override | ||
// Toggle 'inclusion_override' | ||
period.inclusion_override = !currentOverride | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are the expected effects from this mutation? |
||
|
||
newRecords[index] = { ...period } | ||
} | ||
|
||
return newRecords | ||
}) | ||
} | ||
|
||
return ( | ||
<Table id='EnergyUseHistoryChart'> | ||
<Table id="EnergyUseHistoryChart"> | ||
<TableHeader> | ||
<TableRow> | ||
<TableHead className="w-[100px]">#</TableHead> | ||
|
@@ -85,23 +98,21 @@ | |
</TableRow> | ||
</TableHeader> | ||
<TableBody> | ||
{/* {naturalGasBill.map((period, index) => { */} | ||
{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 | ||
|
||
/* switch case for 1, -1, 0 */ | ||
switch (analysisType){ | ||
switch (analysisType) { | ||
case 1: | ||
analysisType_Image = HeatingUsage | ||
break | ||
|
@@ -113,35 +124,35 @@ | |
overrideCheckboxDisabled = true | ||
break | ||
} | ||
|
||
// 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} variant={variant}> | ||
<TableCell className="font-medium">{index + 1}</TableCell> | ||
<TableCell><img src={analysisType_Image} alt='Analysis Type'></img></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.usage}</TableCell> | ||
<TableCell> | ||
{period.get('whole_home_heat_loss_rate')? | ||
period.get('whole_home_heat_loss_rate').toFixed(0) | ||
: | ||
"-" | ||
} | ||
</TableCell> | ||
{period.whole_home_heat_loss_rate | ||
? period.whole_home_heat_loss_rate.toFixed(0) | ||
: '-'} | ||
</TableCell> | ||
<TableCell> | ||
<Checkbox | ||
checked={period.get('inclusion_override')} | ||
<Checkbox | ||
checked={period.inclusion_override} | ||
disabled={overrideCheckboxDisabled} | ||
onClick={handleOverrideCheckboxChange} | ||
onClick={() => handleOverrideCheckboxChange(index)} | ||
/> | ||
</TableCell> | ||
</TableRow> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Future issues:
?.
optional chaining operator fromuser_data
?.
optional chaining operators in general? They indicate that something is not as expected, which will have consequences somewhere in the code. Those cases should be handled.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll handle this using Zod validation, so we can safely remove the
?.
operator. The schema ensures default values. No need for optional chaining since Zod validates the structure and provides defaults where necessary. The validation is in types/index.ts, ensuring consistency.This a example:
Since we have validation with Zod, we no longer need to manually use ?. or write something like this:
{summaryOutputs?.average_indoor_temperature ?? 'N/A'} °F
, because Zod will handle it for us.Let me know what you think!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Briefly talked about tradeoffs of using optional in meeting, esp mentioning required form fields being driven by Zod. To me that means if we take the .optional() approach, we may need multiple zod objects for the same piece.
Let's cover this in #258