Skip to content

Commit

Permalink
feat: produce consumed editor
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismclarke committed Apr 16, 2023
1 parent 4e58be8 commit eb8771e
Show file tree
Hide file tree
Showing 15 changed files with 172 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,13 @@
<!-- Card summary and delete button -->
<div style="position: relative; display: flex">
<budget-card [card]="card"></budget-card>
<button
mat-stroked-button
color="primary"
class="delete-button"
(click)="deleteClicked.emit(card)"
>
<button mat-stroked-button color="primary" class="delete-button" (click)="deleteClicked.emit(card)">
<mat-icon class="delete-button-icon">close</mat-icon>
</button>
</div>

<!-- Inputs/Outputs Editor -->
<div
class="input-values"
*ngIf="card.type === 'inputs' || card.type === 'outputs'"
>
<div class="input-values" *ngIf="card.type === 'inputs' || card.type === 'outputs'">
<mat-form-field class="card-input-field quantity" style="margin-right: 8px">
<mat-label>{{ 'Quantity' | translate }}</mat-label>
<input
Expand Down Expand Up @@ -63,6 +55,20 @@
/>
</mat-form-field>
</div>

<!-- Produce Consumed Editor -->
<div class="input-values" *ngIf="card.type === 'produceConsumed'">
<mat-form-field class="card-input-field quantity" style="margin-right: 8px">
<mat-label>{{ 'Quantity' | translate }}</mat-label>
<input
matInput
class="input-editable"
[value]="card.values ? card.values.quantity : null"
type="number"
(change)="setProduceConsumed($event)"
/>
</mat-form-field>
</div>
</div>

<!-- TODO - add custom card delete bindings -->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { PicsaDialogService } from '@picsa/shared/features';

import {
IBudgetCardDB,
IBudgetCardWithValues,
} from '../../../models/budget-tool.models';
import { IBudgetCardDB, IBudgetCardWithValues } from '../../../models/budget-tool.models';
import { BudgetStore } from '../../../store/budget.store';

@Component({
Expand All @@ -27,12 +24,18 @@ export class BudgetCardEditorComponent {
const target = e.target as HTMLInputElement;
this.card.values[key] = Number(target.value);
if (this.card.values.quantity && this.card.values.cost) {
this.card.values.total =
this.card.values.quantity * this.card.values.cost;
this.card.values.total = this.card.values.quantity * this.card.values.cost;
}
this.valueChanged.emit(this.card);
}

// HACK - produceConsumed only populates quanityt, not values or cost
setProduceConsumed(e: Event) {
const target = e.target as HTMLInputElement;
this.card.values = { quantity: Number(target.value), cost: 0, total: 0 };
this.valueChanged.emit(this.card);
}

async promptCustomDelete(e: Event) {
e.stopPropagation();
const dialogRef = await this.dialog.open('delete');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<h3>Outputs</h3>

<mat-card
appearance="outlined"
*ngFor="let card of cards; let i = index"
Expand All @@ -6,17 +8,26 @@
<budget-card [card]="card"></budget-card>
<div class="input-values-container">
<mat-form-field class="card-input-field quantity">
<mat-label>{{'Quantity' | translate}}</mat-label>
<mat-label>{{'Consumed' | translate}}</mat-label>
<input
matInput
class="input-editable"
[value]="card.quantity"
[value]="card.values.quantity"
type="number"
(change)="setValue($event, 'quantity', i)"
(change)="setValue($event, i)"
/>
</mat-form-field>
<div style="display: flex; margin-left: 10px; font-size: 0.75em; color: rgba(0, 0, 0, 0.38)">
<div style="flex: 1">
<div>{{ 'Total Produced' | translate }}</div>
<div>{{ totalOutputs[card.id] | number: '.0' }}</div>
</div>
<div style="flex: 1">
<div>{{ 'Total Consumed' | translate }}</div>
<div>{{ totalConsumed[card.id] | number: '.0' }}</div>
</div>
</div>
</div>
</mat-card>
<div *ngIf="cards.length === 0" style="margin: 1em 0 2em 0">
You have not selected any cards
</div>

<div *ngIf="cards.length === 0" style="margin: 1em 0 2em 0">{{'No outputs produce for consumption' | translate}}</div>
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';

import { IBudgetCardWithValues } from '../../../../models/budget-tool.models';
import { BudgetStore } from '../../../../store/budget.store';

@Component({
selector: 'budget-cell-editor-produce-consumed',
Expand All @@ -9,22 +11,99 @@ import { IBudgetCardWithValues } from '../../../../models/budget-tool.models';

/* The budget cell editor sits on top of the budget table, so that when opened covers the table
*/
export class BudgetCellEditorProduceConsumedComponent {
export class BudgetCellEditorProduceConsumedComponent implements OnInit {
// Legacy version
@Input() cards: IBudgetCardWithValues[] = [];

@Output() valueChanged = new EventEmitter<IBudgetCardWithValues[]>();

public totalOutputs: Record<string, number>;
public totalConsumed: Record<string, number>;

constructor(private store: BudgetStore) {}

ngOnInit(): void {
//Called after the constructor, initializing input properties, and the first call to ngOnChanges.
//Add 'implements OnInit' to the class.
console.log('cards', this.cards);
const { cards } = this.generateProduceConsumedCards();
this.cards = cards;
}

/**
* Generate a list of cards to be used for produce consumed selection
* The list restricts to only cards that have had outputs produced within
* the active period, and tracks total quanitities available/consumed
*/
private generateProduceConsumedCards() {
const allBudgetPeriods = this.store.activeBudget.data || [];

// Extract values for any existing produce consumed values
const periodConsumed: Record<string, number> = {};
const currentPeriodData = allBudgetPeriods[this.store.activePeriod];
for (const card of currentPeriodData.produceConsumed) {
// HACK - Legacy formatting stored consumed quantity at top-level and not within values
const legacyQuantity = card['quantity' as string];
if (legacyQuantity) card.values = { quantity: legacyQuantity, cost: 0, total: 0 };
periodConsumed[card.id] = card.values.quantity || 0;
}

this.totalOutputs = this.calcTotalOutputs();
this.totalConsumed = this.calcTotalConsumed();

// Create a list of produceConsumed cards from list of output card, including only those
// that have had outputs produced and assigned with existing period values
const cards = this.store.budgetCardsByType.outputs
.filter(({ id }) => id in this.totalOutputs && id !== 'money')
.map((c) => {
const card = c as IBudgetCardWithValues;
card.type = 'produceConsumed';
card.values = { quantity: periodConsumed[c.id] || 0, cost: 0, total: 0 };
return card;
});

return { cards };
}

private calcTotalOutputs() {
const allBudgetPeriods = this.store.activeBudget.data || [];
// sum all quantities of all outputs up to current period and store in a list
// TODO - could also track totalConsumed and use for data validation
const totalOutputs: Record<string, number> = {};
const consumablePeriods = allBudgetPeriods.filter((_, i) => i <= this.store.activePeriod);
for (const { outputs } of consumablePeriods) {
for (const card of outputs) {
totalOutputs[card.id] ??= 0;
totalOutputs[card.id] += card.values.quantity;
}
}
return totalOutputs;
}

private calcTotalConsumed() {
const allBudgetPeriods = this.store.activeBudget.data || [];
// sum all quantities of all outputs up to current period and store in a list
// TODO - could also track totalConsumed and use for data validation
const totalOutputs: Record<string, number> = {};
const consumablePeriods = allBudgetPeriods.filter((_, i) => i <= this.store.activePeriod);
for (const { produceConsumed } of consumablePeriods) {
for (const card of produceConsumed) {
// HACK - Legacy formatting stored consumed quantity at top-level and not within values
const legacyQuantity = card['quantity' as string];
if (legacyQuantity) card.values = { quantity: legacyQuantity, cost: 0, total: 0 };
totalOutputs[card.id] ??= 0;
totalOutputs[card.id] += card.values.quantity;
}
}
return totalOutputs;
}

// using manual bindings instead of ngmodel as nested ngfor-ngmodel with matInput tricky
setValue(e: Event, key: 'quantity' | 'cost', cardIndex: number) {
setValue(e: Event, cardIndex: number) {
const card = this.cards[cardIndex];
const target = e.target as HTMLInputElement;
card.values[key] = Number(target.value);
card.values.quantity = Number(target.value);
this.cards[cardIndex] = card;
this.valueChanged.emit(this.cards);
// only emit cards with quantities specified
const consumedCards = this.cards.filter((c) => c.values.quantity);
this.valueChanged.emit(consumedCards);
this.generateProduceConsumedCards();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@
<div id="editorContainer">
<div *ngFor="let editorStep of editorSteps" class="type-row" [id]="'edit-' + editorStep.type">
<div class="type-label">{{ editorStep.label | translate }}</div>
<div class="selected-summary" *ngIf="data[editorStep.type] as selected">
<div class="selected-summary" *ngIf="data[editorStep.type] as selectedCards">
<div class="selected-cards-container" [attr.data-type]="editorStep.type">
<budget-card-editor
*ngFor="let card of selected; index as i; trackBy: trackByFn"
*ngFor="let card of selectedCards; index as i; trackBy: trackByFn"
[card]="card"
(deleteClicked)="removeSelectedCard(editorStep.type, i)"
(valueChanged)="updateCardValue(editorStep.type, i, $event)"
@fadeInOut
>
</budget-card-editor>

<budget-card-placeholder (click)="showCardsList(editorStep.type)"></budget-card-placeholder>
</div>
</div>
Expand All @@ -38,7 +37,6 @@
<budget-cell-editor-produce-consumed
*ngSwitchCase="'produceConsumed'"
@fadeInOut
[cards]="data.produceConsumed"
(valueChanged)="onEditorChange($event, 'produceConsumed')"
></budget-cell-editor-produce-consumed>
<!-- Default Editor -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,8 @@
grid-template-columns: repeat(auto-fit, minmax(275px, 1fr));
gap: 16px;
}
&[data-type='produceConsumed'] {
grid-template-columns: repeat(auto-fit, minmax(275px, 1fr));
gap: 16px;
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { Component, ElementRef, EventEmitter, Input, Output, TemplateRef, ViewChild } from '@angular/core';
import { FadeInOut, ANIMATION_DELAYED } from '@picsa/shared/animations';
import { MatDialog } from '@angular/material/dialog';
import { ANIMATION_DELAYED, FadeInOut } from '@picsa/shared/animations';
import { _wait } from '@picsa/utils';

import {
IBudgetCard,
IBudgetCardWithValues,
IBudgetPeriodData,
IBudgetPeriodType,
} from '../../models/budget-tool.models';
import { BudgetStore } from '../../store/budget.store';
import { MatDialog } from '@angular/material/dialog';
import { _wait } from '@picsa/utils';

const EDITOR_STEPS: { type: IBudgetPeriodType; label: string }[] = [
{ type: 'activities', label: 'Activities' },
Expand Down Expand Up @@ -110,9 +111,12 @@ export class BudgetEditorComponent {
this.loadEditorData();
}

// the store already knows what period and type it is, so just pass the updated values
// back up to save
// the store already knows what period and type it is, so just pass the updated values to save
onEditorChange(values: IBudgetCardWithValues[], type: IBudgetPeriodType) {
this.store.saveEditor(values, type);
// HACK - fix change detection issue for produceConsumed cards
if (type === 'produceConsumed') {
this.data.produceConsumed = values;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ export type IBudgetCardType = IBudgetPeriodType | 'enterprise' | 'other';

export interface IBudgetCardWithValues extends IBudgetCard {
values: IBudgetCardValues;
quantity?: number; // possible legacy use? (e.g. produce-consumed)
}

interface IBudgetCardCustomMeta {
Expand Down
1 change: 0 additions & 1 deletion apps/picsa-tools/budget-tool/src/app/store/budget.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ export class BudgetStore implements OnDestroy {
@observable balance: IBudgetBalance = [];
// get unique list of types in enterprise cards
@computed get enterpriseTypeCards(): IBudgetCardDB[] {
console.log('get enterprisetype cards');
const enterpriseCards = this.budgetCards.filter((c) => c.type === 'enterprise');
return this._createCardGroupCards(enterpriseCards);
}
Expand Down
3 changes: 3 additions & 0 deletions libs/i18n/assets/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
"Length": "Length",
"Male Member": "Male Member",
"Next": "Next",
"No outputs produce for consumption": "No outputs produce for consumption",
"Outputs": "Outputs",
"Produce Consumed": "Produce Consumed",
"Quantity": "Quantity",
Expand All @@ -112,6 +113,8 @@
"Summary": "Summary",
"Title": "Title",
"Total": "Total",
"Total Consumed": "Total Consumed",
"Total Produced": "Total Produced",
"Values": "Values",
"What is your type of enterprise?": "What is your type of enterprise?",
"description": "description",
Expand Down
3 changes: 3 additions & 0 deletions libs/i18n/assets/ny.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
"Family Labour": "",
"Female Member": "",
"Male Member": "",
"No outputs produce for consumption": "",
"Produce Consumed": "",
"Select Country": "",
"Total Consumed": "",
"Total Produced": "",
"Activities": "zochitika",
"add custom": "onjezani mwambo",
"afforestation": "kugogoda",
Expand Down
3 changes: 3 additions & 0 deletions libs/i18n/assets/sw.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"Line": "",
"Male Member": "",
"Monitoring": "",
"No outputs produce for consumption": "",
"On average": "",
"onions": "",
"out of": "",
Expand All @@ -43,6 +44,8 @@
"Terciles": "",
"title": "",
"Total": "",
"Total Consumed": "",
"Total Produced": "",
"Values": "",
"Year": "",
"years out of every 10": "",
Expand Down
3 changes: 3 additions & 0 deletions libs/i18n/assets/tg.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"Line": "",
"Male Member": "",
"Monitoring": "",
"No outputs produce for consumption": "",
"On average": "",
"onions": "",
"out of": "",
Expand All @@ -43,6 +44,8 @@
"Terciles": "",
"title": "",
"Total": "",
"Total Consumed": "",
"Total Produced": "",
"Values": "",
"Year": "",
"years out of every 10": "",
Expand Down
Loading

0 comments on commit eb8771e

Please sign in to comment.