Skip to content
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

feat(budget): list page budget summaries #215

Merged
merged 15 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<div class="summary-container">
<section matTooltip="Total Family Labour Hours">
<mat-icon svgIcon="picsa_budget_family-labour" class="summary-icon"></mat-icon>
<span class="summary-value">{{ totalFamilyLabourHours }} {{ 'hours' | translate }}</span>
</section>
<!-- TODO - confirm with UoR if inputs/outputs preferred alongside net cash balance -->
<!-- <p matTooltip="Total Inputs Value">
<img [src]="getImagePath('inputs', 'svg')" alt="Receipt Icon" class="icon" /> : <b>{{ totalInputsValue }}</b>
</p>
<p matTooltip="Total Outputs Value">
<img [src]="getImagePath('outputs', 'svg')" alt="Monetization Icon" class="icon" />:
<b>{{ totalOutputsValue }}</b>
</p> -->
@for (produce of totalProduceSummary; track produce.id) {
<section [matTooltip]="'Total ' + produce.label + ' consumed'">
<img [src]="produce.imgPath" [alt]="produce.label + ' Icon'" class="summary-icon" />
<span class="summary-value">{{ produce.total }} {{ 'consumed' | translate }}</span>
</section>
}

<section matTooltip="Final Cash Balance">
<mat-icon svgIcon="picsa_budget_cash_balance" alt="Wallet Icon" class="summary-icon" />
<span class="summary-value">{{ finalCashBalance }} {{ 'balance' | translate }}</span>
</section>
</div>
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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<BudgetSummaryComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [BudgetSummaryComponent],
}).compileComponents();

fixture = TestBed.createComponent(BudgetSummaryComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -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() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(+) Nice concise methods to calculate the totals

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}`;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -51,6 +54,7 @@ const components = [
BudgetPeriodSummaryComponent,
BudgetShareDialogComponent,
BudgetTableComponent,
BudgetSummaryComponent,
];

@NgModule({
Expand All @@ -65,6 +69,8 @@ const components = [
MobxAngularModule,
PicsaDbModule,
RouterModule,
MatTooltipModule,
MatIconModule,
],
exports: components,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<mat-card-content>{{ budget.meta.description }}</mat-card-content>
<mat-card-subtitle>{{ budget._created | date }}</mat-card-subtitle>
</div>
<budget-summary [budgetData]="budget.data" style="margin-bottom: 1.2rem; width: 150px"></budget-summary>
<button
mat-icon-button
[matMenuTriggerFor]="budgetMenu"
Expand All @@ -16,12 +17,10 @@
</button>

<mat-menu #budgetMenu="matMenu">
<button mat-menu-item aria-label="Copy" (click)="showCopyDialog()">
<mat-icon svgIcon="picsa_copy"></mat-icon>Copy
</button>
<button mat-menu-item aria-label="Copy" (click)="showCopyDialog()"><mat-icon>content_copy</mat-icon>Copy</button>
<button mat-menu-item aria-label="Edit" (click)="showRenameDialog()"><mat-icon>edit</mat-icon>Rename</button>
<button mat-menu-item aria-label="Delete" color="light" (click)="handleDeleteClicked($event)">
<mat-icon svgIcon="picsa_delete">Delete</mat-icon>
<mat-icon>delete</mat-icon>
Delete
</button>
</mat-menu>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ mat-card-content {
padding: 0;
margin-bottom: 0.5rem;
}

.date {
display: flex;
justify-content: flex-end;
}
30 changes: 16 additions & 14 deletions apps/picsa-tools/budget-tool/src/app/material.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down Expand Up @@ -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`)
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions apps/picsa-tools/budget-tool/src/app/store/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const NEW_BUDGET_TEMPLATE: IBudget = {
label: '',
type: null as any,
groupings: [],
imgType: 'svg',
},
lengthScale: 'months',
lengthTotal: 5,
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions apps/picsa-tools/budget-tool/src/assets/svgs/cash_balance.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/picsa-tools/budget-tool/src/assets/svgs/family.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions apps/picsa-tools/budget-tool/src/assets/svgs/inputs.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading