diff --git a/apps/picsa-tools/budget-tool/src/app/components/budget-summary/budget-summary.component.html b/apps/picsa-tools/budget-tool/src/app/components/budget-summary/budget-summary.component.html new file mode 100644 index 000000000..dde8d9168 --- /dev/null +++ b/apps/picsa-tools/budget-tool/src/app/components/budget-summary/budget-summary.component.html @@ -0,0 +1,25 @@ +
+
+ + {{ totalFamilyLabourHours }} {{ 'hours' | translate }} +
+ + + @for (produce of totalProduceSummary; track produce.id) { +
+ + {{ produce.total }} {{ 'consumed' | translate }} +
+ } + +
+ + {{ finalCashBalance }} {{ 'balance' | translate }} +
+
diff --git a/apps/picsa-tools/budget-tool/src/app/components/budget-summary/budget-summary.component.scss b/apps/picsa-tools/budget-tool/src/app/components/budget-summary/budget-summary.component.scss new file mode 100644 index 000000000..f664f0c08 --- /dev/null +++ b/apps/picsa-tools/budget-tool/src/app/components/budget-summary/budget-summary.component.scss @@ -0,0 +1,20 @@ +:host { + display: block; +} +.summary-container { + display: flex; + flex-direction: column; + gap: 8px; +} +section { + display: flex; + align-items: center; +} +.summary-icon { + width: 24px; + height: 24px; + font-size: 24px; +} +.summary-value { + margin-left: 8px; +} diff --git a/apps/picsa-tools/budget-tool/src/app/components/budget-summary/budget-summary.component.spec.ts b/apps/picsa-tools/budget-tool/src/app/components/budget-summary/budget-summary.component.spec.ts new file mode 100644 index 000000000..3ac1ac8e0 --- /dev/null +++ b/apps/picsa-tools/budget-tool/src/app/components/budget-summary/budget-summary.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BudgetSummaryComponent } from './budget-summary.component'; + +describe('BudgetSummaryComponent', () => { + let component: BudgetSummaryComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BudgetSummaryComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(BudgetSummaryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/picsa-tools/budget-tool/src/app/components/budget-summary/budget-summary.component.ts b/apps/picsa-tools/budget-tool/src/app/components/budget-summary/budget-summary.component.ts new file mode 100644 index 000000000..fa92df272 --- /dev/null +++ b/apps/picsa-tools/budget-tool/src/app/components/budget-summary/budget-summary.component.ts @@ -0,0 +1,71 @@ +import { Component, Input, OnInit } from '@angular/core'; + +import { IBudget } from '../../models/budget-tool.models'; +import { IBudgetCardWithValues } from '../../schema'; + +interface ISummaryEntry { + label: string; + total: number; + id: string; + imgPath: string; +} + +@Component({ + selector: 'budget-summary', + templateUrl: './budget-summary.component.html', + styleUrls: ['./budget-summary.component.scss'], +}) +export class BudgetSummaryComponent implements OnInit { + @Input() budgetData: IBudget['data']; + + totalFamilyLabourHours: number = 0; + totalInputsValue: number = 0; + totalOutputsValue: number = 0; + totalProduceSummary: ISummaryEntry[] = []; + finalCashBalance: number = 0; + + ngOnInit(): void { + this.calculateSummary(); + } + + calculateSummary() { + this.budgetData.forEach((item) => { + item.familyLabour.forEach((member) => { + this.totalFamilyLabourHours += member.values.quantity; + }); + + item.inputs.forEach((input) => { + this.totalInputsValue += input.values.total; + }); + + item.outputs.forEach((output) => { + this.totalOutputsValue += output.values.total; + }); + + item.produceConsumed.forEach((consumed) => { + this.updateProduceSummary(consumed); + }); + }); + + this.finalCashBalance = this.totalOutputsValue - this.totalInputsValue; + } + + updateProduceSummary(consumed: IBudgetCardWithValues) { + const existingProduce = this.totalProduceSummary.find((p) => p.label === consumed.label); + + if (existingProduce) { + existingProduce.total += consumed.values.quantity; + } else { + this.totalProduceSummary.push({ + label: consumed.label, + total: consumed.values.quantity, + id: consumed.id, + imgPath: this.getImagePath(consumed.id, consumed.imgType), + }); + } + } + + getImagePath(imageId: string, extension: 'png' | 'svg'): string { + return `assets/budget-cards/${imageId}.${extension}`; + } +} diff --git a/apps/picsa-tools/budget-tool/src/app/components/budget-tool.components.ts b/apps/picsa-tools/budget-tool/src/app/components/budget-tool.components.ts index 313905518..97df18330 100644 --- a/apps/picsa-tools/budget-tool/src/app/components/budget-tool.components.ts +++ b/apps/picsa-tools/budget-tool/src/app/components/budget-tool.components.ts @@ -7,6 +7,7 @@ import { PicsaCommonComponentsModule } from '@picsa/components'; import { PicsaDialogsModule } from '@picsa/shared/features'; import { PicsaDbModule } from '@picsa/shared/modules'; import { MobxAngularModule } from 'mobx-angular'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { BudgetMaterialModule } from '../material.module'; // Components @@ -29,6 +30,8 @@ import { BudgetListItemComponent, BudgetListItemRenameDialog } from './list-item import { BudgetShareDialogComponent } from './share-dialog/share-dialog.component'; import { BudgetPeriodSummaryComponent } from './summary/period-summary'; import { BudgetTableComponent } from './table/budget-table'; +import { BudgetSummaryComponent } from './budget-summary/budget-summary.component'; +import { MatIconModule } from '@angular/material/icon'; const components = [ BudgetBalanceLegendComponent, @@ -51,6 +54,7 @@ const components = [ BudgetPeriodSummaryComponent, BudgetShareDialogComponent, BudgetTableComponent, + BudgetSummaryComponent, ]; @NgModule({ @@ -65,6 +69,8 @@ const components = [ MobxAngularModule, PicsaDbModule, RouterModule, + MatTooltipModule, + MatIconModule, ], exports: components, }) diff --git a/apps/picsa-tools/budget-tool/src/app/components/card/card-new/card-new.ts b/apps/picsa-tools/budget-tool/src/app/components/card/card-new/card-new.ts index a86ee5379..b53bc8521 100644 --- a/apps/picsa-tools/budget-tool/src/app/components/card/card-new/card-new.ts +++ b/apps/picsa-tools/budget-tool/src/app/components/card/card-new/card-new.ts @@ -30,6 +30,7 @@ export class BudgetCardNew { label: '', type: this.type, groupings: groupings as IBudgetCardGrouping[], + imgType: 'svg', }; const dialogRef = this.dialog.open(BudgetCardNewDialog, { width: '250px', diff --git a/apps/picsa-tools/budget-tool/src/app/components/list-item/budget-list-item.html b/apps/picsa-tools/budget-tool/src/app/components/list-item/budget-list-item.html index 5411078c2..f8d0cdcea 100644 --- a/apps/picsa-tools/budget-tool/src/app/components/list-item/budget-list-item.html +++ b/apps/picsa-tools/budget-tool/src/app/components/list-item/budget-list-item.html @@ -6,6 +6,7 @@ {{ budget.meta.description }} {{ budget._created | date }} + + diff --git a/apps/picsa-tools/budget-tool/src/app/components/list-item/budget-list-item.scss b/apps/picsa-tools/budget-tool/src/app/components/list-item/budget-list-item.scss index 413685641..40325cd55 100644 --- a/apps/picsa-tools/budget-tool/src/app/components/list-item/budget-list-item.scss +++ b/apps/picsa-tools/budget-tool/src/app/components/list-item/budget-list-item.scss @@ -2,3 +2,8 @@ mat-card-content { padding: 0; margin-bottom: 0.5rem; } + +.date { + display: flex; + justify-content: flex-end; +} diff --git a/apps/picsa-tools/budget-tool/src/app/material.module.ts b/apps/picsa-tools/budget-tool/src/app/material.module.ts index 04db47ddb..31dbff569 100644 --- a/apps/picsa-tools/budget-tool/src/app/material.module.ts +++ b/apps/picsa-tools/budget-tool/src/app/material.module.ts @@ -16,6 +16,8 @@ import { MatStepperModule } from '@angular/material/stepper'; import { MatToolbarModule } from '@angular/material/toolbar'; import { DomSanitizer } from '@angular/platform-browser'; +import { BUDGET_CARDS } from './data'; + // use custom module to make it easier to control what is available through app @NgModule({ imports: [ @@ -56,22 +58,22 @@ export class BudgetMaterialModule { this.registerIcons(); } registerIcons() { - const BUDGET_ICONS = { - download: 'download', - delete: 'delete', - settings: 'settings', - controls: 'controls', - copy: 'copy', - }; - if (this.matIconRegistry && this.domSanitizer) { - for (const [key, value] of Object.entries(BUDGET_ICONS)) { + // create mat-icon entries for all budget cards with svg icons in assets/budget-cards folder + const budgetCardSVGs = BUDGET_CARDS.filter((b) => b.imgType === 'svg'); + for (const { id } of budgetCardSVGs) { + this.matIconRegistry.addSvgIcon( + `picsa_budget_${id}`, + this.domSanitizer.bypassSecurityTrustResourceUrl(`assets/budget-cards/${id}.svg`) + ); + } + + // create additional entries for custom svgs in assets/svgs folder + const svgs = ['cash_balance', 'family', 'inputs', 'outputs']; + for (const id of svgs) { this.matIconRegistry.addSvgIcon( - `picsa_${key}`, - this.domSanitizer.bypassSecurityTrustResourceUrl( - // NOTE - svgs are imported from shared lib (see angular.json for config) - `assets/images/${value}.svg` - ) + `picsa_budget_${id}`, + this.domSanitizer.bypassSecurityTrustResourceUrl(`assets/svgs/${id}.svg`) ); } } diff --git a/apps/picsa-tools/budget-tool/src/app/schema/cards/schema_v1.ts b/apps/picsa-tools/budget-tool/src/app/schema/cards/schema_v1.ts index 95a47fec2..48f07bd8e 100644 --- a/apps/picsa-tools/budget-tool/src/app/schema/cards/schema_v1.ts +++ b/apps/picsa-tools/budget-tool/src/app/schema/cards/schema_v1.ts @@ -15,7 +15,7 @@ export type IBudgetCard_v1 = { groupings?: IBudgetCardGrouping[]; customMeta?: IBudgetCardCustomMeta; values?: IBudgetCardValues; - imgType?: 'svg' | 'png'; + imgType: 'svg' | 'png'; /** Optional image overide (default used card id) */ imgId?: string; _deleted?: boolean; @@ -62,6 +62,7 @@ export const ENTRY_TEMPLATE_V1 = (options: { return { id: generateID(), customMeta: { imgData, createdBy: '', dateCreated: new Date().toISOString() }, + imgType: 'svg', groupings, label, type, diff --git a/apps/picsa-tools/budget-tool/src/app/store/templates.ts b/apps/picsa-tools/budget-tool/src/app/store/templates.ts index 90521ad8a..aeb7ffc4d 100644 --- a/apps/picsa-tools/budget-tool/src/app/store/templates.ts +++ b/apps/picsa-tools/budget-tool/src/app/store/templates.ts @@ -31,6 +31,7 @@ export const NEW_BUDGET_TEMPLATE: IBudget = { label: '', type: null as any, groupings: [], + imgType: 'svg', }, lengthScale: 'months', lengthTotal: 5, diff --git a/apps/picsa-tools/budget-tool/src/assets/budget-cards/cash-balance.svg b/apps/picsa-tools/budget-tool/src/assets/budget-cards/cash-balance.svg new file mode 100644 index 000000000..2dc9b61bf --- /dev/null +++ b/apps/picsa-tools/budget-tool/src/assets/budget-cards/cash-balance.svg @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/apps/picsa-tools/budget-tool/src/assets/svgs/cash_balance.svg b/apps/picsa-tools/budget-tool/src/assets/svgs/cash_balance.svg new file mode 100644 index 000000000..2dc9b61bf --- /dev/null +++ b/apps/picsa-tools/budget-tool/src/assets/svgs/cash_balance.svg @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/apps/picsa-tools/budget-tool/src/assets/svgs/family.svg b/apps/picsa-tools/budget-tool/src/assets/svgs/family.svg new file mode 100644 index 000000000..902f04e99 --- /dev/null +++ b/apps/picsa-tools/budget-tool/src/assets/svgs/family.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/apps/picsa-tools/budget-tool/src/assets/svgs/inputs.svg b/apps/picsa-tools/budget-tool/src/assets/svgs/inputs.svg new file mode 100644 index 000000000..8b8da8bd0 --- /dev/null +++ b/apps/picsa-tools/budget-tool/src/assets/svgs/inputs.svg @@ -0,0 +1,34 @@ + + + + + + \ No newline at end of file diff --git a/apps/picsa-tools/budget-tool/src/assets/svgs/outputs.svg b/apps/picsa-tools/budget-tool/src/assets/svgs/outputs.svg new file mode 100644 index 000000000..d20694a03 --- /dev/null +++ b/apps/picsa-tools/budget-tool/src/assets/svgs/outputs.svg @@ -0,0 +1,109 @@ + + + + + + \ No newline at end of file diff --git a/package.json b/package.json index 8cf0cc56a..879c9f50d 100644 --- a/package.json +++ b/package.json @@ -161,7 +161,7 @@ "eslint-plugin-simple-import-sort": "^10.0.0", "execa": "^5.1.1", "fs-extra": "^10.1.0", - "husky": "^8.0.0", + "husky": "^8.0.3", "jest": "29.4.3", "jest-circus": "^29.5.0", "jest-environment-jsdom": "29.4.3", diff --git a/yarn.lock b/yarn.lock index 5cfed63ae..773ea3235 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14847,7 +14847,7 @@ __metadata: languageName: node linkType: hard -"husky@npm:^8.0.0": +"husky@npm:^8.0.3": version: 8.0.3 resolution: "husky@npm:8.0.3" bin: @@ -19047,7 +19047,7 @@ __metadata: glob: ^8.1.0 hls.js: ^1.4.12 html2canvas: ^1.4.1 - husky: ^8.0.0 + husky: ^8.0.3 intro.js: ^7.0.1 jest: 29.4.3 jest-circus: ^29.5.0