Skip to content

Commit

Permalink
Merge pull request #66 from ducktordanny/feat/second-phase-results-ui
Browse files Browse the repository at this point in the history
Feat: Second phase of transportation problem - UI side
  • Loading branch information
ducktordanny authored Oct 30, 2022
2 parents 7f4f142 + b31ea0c commit c727dc6
Show file tree
Hide file tree
Showing 49 changed files with 1,305 additions and 558 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ Thumbs.db
/temp
/resources
.vercel

.angular
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

> All notable changes to this project will be documented in this file.
## [0.7.0] - 2022-10-30

- Add second phase support for transportation problem on API and UI side
- UI style fixes
- Fix layout jumps caused by clearing the results when calculating a new case.
- Fix not supporting safe area view

## [0.6.1] - 2022-09-01

- Fix showing progress bar when switching between pages
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,8 @@
import {TransportTable} from '@opres/shared/types';
import {resultTableMock} from '@opres/shared/data/mocks';

import {transport} from '../transport.util';

describe('TransportUtil', () => {
const resultTableMock: TransportTable = [
{
'0': {cost: 8},
'1': {cost: 7},
'2': {cost: 3},
'3': {cost: 2, transported: 15},
},
{
'0': {cost: 1, transported: 18},
'1': {cost: 4},
'2': {cost: 2, transported: 25},
'3': {cost: 5},
},
{
'0': {cost: 2},
'1': {cost: 3, transported: 13},
'2': {cost: 4, transported: 10},
'3': {cost: 7, transported: 5},
},
{
'0': {cost: 1},
'1': {cost: 1, transported: 19},
'2': {cost: 4},
'3': {cost: 4},
},
];
const demandsMock = [0, 0, 10, 5];
const stocksMock = [0, 0, 15, 0];
const indexMocks = 2;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import 'apps/frontend/src/app/styles/variables/colors';
@import 'libs/shared/styles/colors';

mat-card {
display: flex;
Expand Down
10 changes: 9 additions & 1 deletion apps/frontend/src/app/components/layout/layout.style.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import '../../styles/variables/colors';
@import 'libs/shared/styles/colors';

.layout-wrapper {
display: flex;
Expand Down Expand Up @@ -42,6 +42,14 @@
box-sizing: border-box;
}
}

mat-progress-bar {
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 10;
}
}

.selected {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import '../../styles/variables/colors';
@import 'libs/shared/styles/colors';

form {
padding: 16px env(safe-area-inset-right) 16px 16px;
Expand Down
2 changes: 1 addition & 1 deletion apps/frontend/src/app/pages/home/home.style.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import '../../styles/variables/colors';
@import 'libs/shared/styles/colors';

:host {
overflow-x: hidden;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {HttpClientModule} from '@angular/common/http';
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {MatStepperModule} from '@angular/material/stepper';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';

import {
InputTableModule,
InputTableService,
TableModule,
} from '@opres/ui/tables';
import {TranslateModule} from '@ngx-translate/core';

import {AllTabModule} from '../+tabs/all-tab/all.tab.module';
import {TabsModule} from '../+tabs/tabs.module';
import {TransportProblemService} from '../transport-problem.service';

import {FirstPhaseResultInputComponent} from './first-phase-result-input.component';

describe('FirstPhaseResultInputComponent', () => {
let fixture: ComponentFixture<FirstPhaseResultInputComponent>;
let component: FirstPhaseResultInputComponent;
let inputTableService: InputTableService;
const firstStepFormGroupMock = {
shops: 4,
storages: 4,
};
const costsMock = [
{'0': 8, '1': 7, '2': 3, '3': 2},
{'0': 1, '1': 4, '2': 2, '3': 5},
{'0': 2, '1': 3, '2': 4, '3': 7},
{'0': 1, '1': 1, '2': 4, '3': 4},
];
const transportationsMock = [
{'0': null, '1': null, '2': null, '3': 15},
{'0': 8, '1': null, '2': 35, '3': null},
{'0': 10, '1': 13, '2': null, '3': 5},
{'0': null, '1': 19, '2': null, '3': null},
];

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [FirstPhaseResultInputComponent],
imports: [
AllTabModule,
HttpClientModule,
InputTableModule,
MatStepperModule,
NoopAnimationsModule,
TableModule,
TabsModule,
TranslateModule.forRoot(),
],
providers: [TransportProblemService],
});

fixture = TestBed.createComponent(FirstPhaseResultInputComponent);
component = fixture.componentInstance;
inputTableService = TestBed.inject(InputTableService);
});

it('should be created', () =>
expect(component).toBeInstanceOf(FirstPhaseResultInputComponent));

it('should check default values', (done) => {
expect(component.firstStepFormGroup.getRawValue()).toEqual(
firstStepFormGroupMock,
);
expect(component.secondStepFormGroup.getRawValue()).toEqual({});
expect(component.costs$.getValue()).toEqual(costsMock);
expect(component.transportations$.getValue()).toEqual(transportationsMock);
component.isLoading$.subscribe((loading: boolean) =>
expect(loading).toEqual(false),
);
done();
});

it('should change values of tables', () => {
component.onTransportationsChange([]);
expect(component.transportations$.getValue()).toEqual([]);
component.onCostsChange([]);
expect(component.costs$.getValue()).toEqual([]);
});

it('should clear tables', () => {
const tableClearSpy = jest.spyOn(inputTableService, 'clear');
component.onTransportationsClear();
expect(tableClearSpy).toHaveBeenCalledWith('transportations');
component.onCostsClear();
expect(tableClearSpy).toHaveBeenCalledWith('costs');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Output,
} from '@angular/core';
import {FormControl, FormGroup} from '@angular/forms';

import {Table, TransportTable} from '@opres/shared/types';
import {InputTableService} from '@opres/ui/tables';
import {LanguageSwitcherService} from '@frontend/components/layout/language-switcher/language-switcher.service';
import {LoadingHandlerService} from '@frontend/services/loading-handler.service';
import {forEach, mapValues} from 'lodash';
import {BehaviorSubject} from 'rxjs';

import {
transportProblemCacheBuster$,
TransportProblemService,
} from '../transport-problem.service';

@Component({
selector: 'first-phase-result-input',
templateUrl: './first-phase-result-input.template.html',
styleUrls: [
'../+tabs/tabs.style.scss',
'./first-phase-result-input.style.scss',
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FirstPhaseResultInputComponent {
@Output() public calculate = new EventEmitter<TransportTable>();
public firstStepFormGroup: FormGroup;
public secondStepFormGroup: FormGroup;
public costs$ = new BehaviorSubject<Table>([
{'0': 8, '1': 7, '2': 3, '3': 2},
{'0': 1, '1': 4, '2': 2, '3': 5},
{'0': 2, '1': 3, '2': 4, '3': 7},
{'0': 1, '1': 1, '2': 4, '3': 4},
]);
public transportations$ = new BehaviorSubject<Table>([
{'0': null, '1': null, '2': null, '3': 15},
{'0': 8, '1': null, '2': 35, '3': null},
{'0': 10, '1': 13, '2': null, '3': 5},
{'0': null, '1': 19, '2': null, '3': null},
]);
public isLoading$ = this.loadingHandler.isLoading;

constructor(
private transportProblemService: TransportProblemService,
private languageSwitcherService: LanguageSwitcherService,
private inputTableService: InputTableService,
private loadingHandler: LoadingHandlerService,
) {
this.firstStepFormGroup = new FormGroup({
shops: new FormControl(4, transportProblemService.tableSizeValidators),
storages: new FormControl(4, transportProblemService.tableSizeValidators),
});
this.secondStepFormGroup = new FormGroup({});
}

public onCostsChange(table: Table): void {
this.costs$.next(table);
transportProblemCacheBuster$.next();
this.validateCostsTable();
}

public onTransportationsChange(table: Table): void {
this.transportations$.next(table);
transportProblemCacheBuster$.next();
this.validateTransportationsTable();
}

public onCostsClear(): void {
this.inputTableService.clear('costs');
}

public onTransportationsClear(): void {
this.inputTableService.clear('transportations');
}

public onCalculate(): void {
this.calculate.emit(this.getTransportTableFromCurrentInput());
}

private getTransportTableFromCurrentInput(): TransportTable {
const transportations = this.transportations$.getValue();
return this.costs$.getValue().map((row, index) => {
return mapValues(row, (cost, key) => ({
cost: cost as number,
transported: transportations[index][key] || undefined,
}));
});
}

private validateCostsTable(): void {
const costs = this.costs$.getValue();
let hasNullCost = false;

for (const row of costs)
forEach(row, (cost) => {
if (cost === null || cost === undefined) hasNullCost = true;
});

if (hasNullCost) this.firstStepFormGroup.setErrors({nullCost: true});
else this.firstStepFormGroup.setErrors(null);
}

private validateTransportationsTable(): void {
const transportations = this.transportations$.getValue();
let hasTransported = false;

for (const row of transportations)
forEach(row, (transport) => {
if (transport) hasTransported = true;
});

if (!hasTransported)
this.secondStepFormGroup.setErrors({noTransport: true});
else this.secondStepFormGroup.setErrors(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {NgModule} from '@angular/core';
import {MatStepperModule} from '@angular/material/stepper';

import {InputTableModule, TableModule} from '@opres/ui/tables';

import {TabsModule} from '../+tabs/tabs.module';

import {FirstPhaseResultInputComponent} from './first-phase-result-input.component';

@NgModule({
declarations: [FirstPhaseResultInputComponent],
imports: [InputTableModule, MatStepperModule, TableModule, TabsModule],
exports: [FirstPhaseResultInputComponent],
})
export class FirstPhaseResultInputModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
:host {
::ng-deep.mat-horizontal-content-container {
padding: 0;
}

.table-wrapper {
overflow-x: auto;
padding: 8px;
}
}
Loading

0 comments on commit c727dc6

Please sign in to comment.