Skip to content

Commit

Permalink
247 interactive chart (#255)
Browse files Browse the repository at this point in the history
* Start implementing interactive checkboxes

Co-authored-by: TBardini <[email protected]>
Co-authored-by: Devin Altobello <[email protected]>

* Add checkbox interactivity to EnergyUseHistoryChart

* Refactor single.tsx to transform usage_data from Map to object for better 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.

* fix: updated Zod types for usage_data and refactored type imports

* Refactor EnergyUseHistoryChart to include variant prop in TableRow

* Refactor replacedMapToObject in single.tsx and replace spread operator with structuredClone() in EnergyUseHistoryChart.tsx

* Refactor zod parsing and types for typecheck

Co-authored-by: Charlie Kelly <[email protected]>
Co-authored-by: plocket <[email protected]>
Co-authored-by: thomas-davis <[email protected]>
Co-authored-by: TBardini <[email protected]>

* reroll package-lock.json

---------

Co-authored-by: plocket <[email protected]>
Co-authored-by: TBardini <[email protected]>
Co-authored-by: Devin Altobello <[email protected]>
Co-authored-by: TBardini <[email protected]>
Co-authored-by: Thad Kerosky <[email protected]>
Co-authored-by: Charlie Kelly <[email protected]>
Co-authored-by: plocket <[email protected]>
Co-authored-by: thomas-davis <[email protected]>
  • Loading branch information
9 people authored Oct 23, 2024
1 parent 739b1c3 commit e0f8e32
Show file tree
Hide file tree
Showing 7 changed files with 9,282 additions and 14,640 deletions.
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",
Expand Down Expand Up @@ -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,
// 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">
Expand All @@ -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>
<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 +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>
</div>
</div>
<div className="basis-1/3">
Expand All @@ -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)}{' '}
%
</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 { 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 {
Expand Down Expand Up @@ -53,23 +54,35 @@ import { tr } from '@faker-js/faker'
// 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

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

return newRecords
})
}

return (
<Table id='EnergyUseHistoryChart'>
<Table id="EnergyUseHistoryChart">
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">#</TableHead>
Expand All @@ -85,23 +98,21 @@ export function EnergyUseHistoryChart(props: { usage_data: any }) {
</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
Expand All @@ -113,35 +124,35 @@ export function EnergyUseHistoryChart(props: { usage_data: any }) {
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>
Expand Down
Loading

0 comments on commit e0f8e32

Please sign in to comment.