|
|
@@ -1,18 +1,18 @@
|
|
|
-import { Component, Inject, Input, OnInit } from '@angular/core';
|
|
|
+import { Component, Inject, OnInit } from '@angular/core';
|
|
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|
|
import { Activity } from '../../activity/activity.interface';
|
|
|
import { MatDialogModule } from '@angular/material/dialog';
|
|
|
import { MatButtonModule } from '@angular/material/button';
|
|
|
import { MatExpansionModule } from '@angular/material/expansion';
|
|
|
import { CommonModule } from '@angular/common';
|
|
|
-interface WorkerActivity {
|
|
|
- activityName: string;
|
|
|
- outputs: { name: string; value: number; uom: string }[];
|
|
|
-}
|
|
|
|
|
|
-interface WorkerSummary {
|
|
|
- name: string;
|
|
|
- activities: WorkerActivity[];
|
|
|
+interface ResourceSummary {
|
|
|
+ type: string; // worker, tractor, etc.
|
|
|
+ resources: {
|
|
|
+ name: string;
|
|
|
+ totalQuantity: number;
|
|
|
+ uom: string;
|
|
|
+ }[];
|
|
|
}
|
|
|
|
|
|
@Component({
|
|
|
@@ -30,7 +30,7 @@ interface WorkerSummary {
|
|
|
export class CalculateDialogComponent implements OnInit {
|
|
|
filteredActivities: Activity[] = [];
|
|
|
|
|
|
- workerSummary: WorkerSummary[] = [];
|
|
|
+ resourceSummary: ResourceSummary[] = [];
|
|
|
totalOutputs: Record<string, number> = {};
|
|
|
totalOutputUoms: Record<string, string> = {};
|
|
|
totalTargets: Record<string, number> = {};
|
|
|
@@ -38,11 +38,17 @@ export class CalculateDialogComponent implements OnInit {
|
|
|
totalDuration: number = 0;
|
|
|
durationUom: string = '';
|
|
|
|
|
|
+ averageOutputsPerMonth: Record<string, number> = {};
|
|
|
+ averageTargetsPerMonth: Record<string, number> = {};
|
|
|
+ averageDurationPerMonth: number = 0;
|
|
|
+
|
|
|
+ overallStart: Date | null = null;
|
|
|
+ overallEnd: Date | null = null;
|
|
|
+
|
|
|
constructor(
|
|
|
private dialogRef: MatDialogRef<CalculateDialogComponent>,
|
|
|
@Inject(MAT_DIALOG_DATA) public data: { activities: Activity[] }
|
|
|
) {
|
|
|
- // assign injected data to local variable
|
|
|
this.filteredActivities = data.activities;
|
|
|
}
|
|
|
|
|
|
@@ -51,71 +57,97 @@ export class CalculateDialogComponent implements OnInit {
|
|
|
}
|
|
|
|
|
|
calculateTotals(): void {
|
|
|
- const workerMap: Record<string, { activityName: string; outputs: { name: string; value: number; uom: string }[] }[]> = {};
|
|
|
+ const resourceTypeMap: Record<string, Record<string, { totalQuantity: number; uom: string }>> = {};
|
|
|
const outputTotals: Record<string, number> = {};
|
|
|
const outputUoms: Record<string, string> = {};
|
|
|
const targetTotals: Record<string, number> = {};
|
|
|
const targetUoms: Record<string, string> = {};
|
|
|
- let totalDuration = 0;
|
|
|
+ const sumOutputs: Record<string, number> = {};
|
|
|
+ const sumTargets: Record<string, number> = {};
|
|
|
+
|
|
|
+ const allStartDates: Date[] = [];
|
|
|
+ const allEndDates: Date[] = [];
|
|
|
+ let totalDurationQuantity = 0;
|
|
|
let durationUom = '';
|
|
|
|
|
|
for (const activity of this.filteredActivities) {
|
|
|
- // Workers
|
|
|
- for (const resource of activity.resources) {
|
|
|
- if (resource.type.toLowerCase() === 'worker') {
|
|
|
- if (!workerMap[resource.name]) {
|
|
|
- workerMap[resource.name] = [];
|
|
|
- }
|
|
|
-
|
|
|
- const activityOutputs = activity.outputs.map(o => ({
|
|
|
- name: o.name,
|
|
|
- value: o.value.quantity,
|
|
|
- uom: o.value.uom,
|
|
|
- }));
|
|
|
-
|
|
|
- workerMap[resource.name].push({
|
|
|
- activityName: activity.name,
|
|
|
- outputs: activityOutputs,
|
|
|
- });
|
|
|
+ const start = new Date(activity.dateStart);
|
|
|
+ const end = new Date(activity.dateEnd);
|
|
|
+ allStartDates.push(start);
|
|
|
+ allEndDates.push(end);
|
|
|
+
|
|
|
+ // Group resources by type and sum quantity
|
|
|
+ for (const res of activity.resources) {
|
|
|
+ if (!resourceTypeMap[res.type]) resourceTypeMap[res.type] = {};
|
|
|
+ if (!resourceTypeMap[res.type][res.name]) {
|
|
|
+ resourceTypeMap[res.type][res.name] = { totalQuantity: 0, uom: res.value.uom };
|
|
|
}
|
|
|
+
|
|
|
+ resourceTypeMap[res.type][res.name].totalQuantity += res.value.quantity;
|
|
|
}
|
|
|
|
|
|
- // Outputs (totals)
|
|
|
+ // Outputs totals & sum for averages
|
|
|
for (const output of activity.outputs) {
|
|
|
const { uom, quantity } = output.value;
|
|
|
outputTotals[uom] = (outputTotals[uom] || 0) + quantity;
|
|
|
outputUoms[uom] = uom;
|
|
|
+ sumOutputs[uom] = (sumOutputs[uom] || 0) + quantity;
|
|
|
}
|
|
|
|
|
|
- // Targets (totals)
|
|
|
+ // Targets totals & sum for averages
|
|
|
for (const target of activity.targets) {
|
|
|
const { uom, quantity } = target.value;
|
|
|
targetTotals[uom] = (targetTotals[uom] || 0) + quantity;
|
|
|
targetUoms[uom] = uom;
|
|
|
+ sumTargets[uom] = (sumTargets[uom] || 0) + quantity;
|
|
|
}
|
|
|
|
|
|
- // Duration (sum only if hours)
|
|
|
- const dur = activity.duration.value;
|
|
|
- if (dur.uom.toLowerCase() === 'hour' || dur.uom.toLowerCase() === 'hours') {
|
|
|
- totalDuration += dur.quantity;
|
|
|
- }
|
|
|
- durationUom = dur.uom;
|
|
|
+ // Duration sum
|
|
|
+ totalDurationQuantity += activity.duration.value.quantity;
|
|
|
+ durationUom = activity.duration.value.uom;
|
|
|
}
|
|
|
|
|
|
- // Map worker data into summary
|
|
|
- this.workerSummary = Object.keys(workerMap).map(name => ({
|
|
|
- name,
|
|
|
- activities: workerMap[name],
|
|
|
+ // Overall start/end
|
|
|
+ const overallStart = new Date(Math.min(...allStartDates.map(d => d.getTime())));
|
|
|
+ const overallEnd = new Date(Math.max(...allEndDates.map(d => d.getTime())));
|
|
|
+ const totalDays = (overallEnd.getTime() - overallStart.getTime()) / (1000 * 60 * 60 * 24);
|
|
|
+ const totalMonths = totalDays / 30;
|
|
|
+
|
|
|
+ // Averages per month
|
|
|
+ const averageOutputs: Record<string, number> = {};
|
|
|
+ const averageTargets: Record<string, number> = {};
|
|
|
+ let averageDuration = 0;
|
|
|
+
|
|
|
+ if (totalMonths >= 1) {
|
|
|
+ for (const key in sumOutputs) averageOutputs[key] = sumOutputs[key] / totalMonths;
|
|
|
+ for (const key in sumTargets) averageTargets[key] = sumTargets[key] / totalMonths;
|
|
|
+ averageDuration = totalDurationQuantity / totalMonths;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Transform map into ResourceSummary array
|
|
|
+ this.resourceSummary = Object.keys(resourceTypeMap).map(type => ({
|
|
|
+ type,
|
|
|
+ resources: Object.keys(resourceTypeMap[type]).map(name => ({
|
|
|
+ name,
|
|
|
+ totalQuantity: resourceTypeMap[type][name].totalQuantity,
|
|
|
+ uom: resourceTypeMap[type][name].uom
|
|
|
+ }))
|
|
|
}));
|
|
|
|
|
|
this.totalOutputs = outputTotals;
|
|
|
this.totalOutputUoms = outputUoms;
|
|
|
this.totalTargets = targetTotals;
|
|
|
this.totalTargetUoms = targetUoms;
|
|
|
- this.totalDuration = totalDuration;
|
|
|
+ this.totalDuration = totalDurationQuantity;
|
|
|
this.durationUom = durationUom;
|
|
|
- }
|
|
|
|
|
|
+ this.averageOutputsPerMonth = averageOutputs;
|
|
|
+ this.averageTargetsPerMonth = averageTargets;
|
|
|
+ this.averageDurationPerMonth = averageDuration;
|
|
|
+
|
|
|
+ this.overallStart = overallStart;
|
|
|
+ this.overallEnd = overallEnd;
|
|
|
+ }
|
|
|
|
|
|
closeDialog(): void {
|
|
|
this.dialogRef.close();
|