Skip to content

Commit 7250135

Browse files
Add solution date selector
1 parent b442faf commit 7250135

File tree

9 files changed

+97
-16
lines changed

9 files changed

+97
-16
lines changed

application/frontend/src/app/core/components/time-navigation/time-navigation.component.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,21 @@
1414
<mat-icon svgIcon="navigate_before" [attr.title]="'Previous ' + range?.label"></mat-icon>
1515
</button>
1616
<label>{{ getFormattedRangeOffset() }}</label>
17+
<mat-form-field class="offset-picker" appearance="standard">
18+
<input
19+
matInput
20+
[value]="currentOffsetDate"
21+
[min]="globalStart"
22+
[max]="globalEnd"
23+
[matDatepicker]="offsetPicker"
24+
(dateChange)="onDateChange($event)" />
25+
<mat-datepicker #offsetPicker></mat-datepicker>
26+
</mat-form-field>
27+
<button mat-icon-button type="button" title="Open calendar" (click)="offsetPicker.open()">
28+
<mat-icon svgIcon="calendar"></mat-icon>
29+
</button>
1730
<button
31+
class="next-button"
1832
type="button"
1933
mat-stroked-button
2034
(click)="onNextClick()"

application/frontend/src/app/core/components/time-navigation/time-navigation.component.scss

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,14 @@ app-time-navigation {
1414
margin-left: 8px;
1515
}
1616
}
17+
18+
.offset-picker {
19+
visibility: hidden;
20+
overflow: hidden;
21+
width: 0;
22+
height: 2rem;
23+
}
24+
25+
.next-button {
26+
margin-left: 0 !important;
27+
}

application/frontend/src/app/core/components/time-navigation/time-navigation.component.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@ import { MatIconRegistry } from '@angular/material/icon';
1212
import { MaterialModule } from 'src/app/material';
1313
import { FakeMatIconRegistry } from 'src/test/material-fakes';
1414
import { TimeNavigationComponent } from './time-navigation.component';
15+
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
16+
import { MatDatepickerModule } from '@angular/material/datepicker';
1517

1618
describe('TimeNavigationComponent', () => {
1719
let component: TimeNavigationComponent;
1820
let fixture: ComponentFixture<TimeNavigationComponent>;
1921

2022
beforeEach(async () => {
2123
await TestBed.configureTestingModule({
22-
imports: [MaterialModule],
24+
imports: [MaterialModule, NoopAnimationsModule, MatDatepickerModule],
2325
declarations: [TimeNavigationComponent],
2426
})
2527
.overrideProvider(MatIconRegistry, { useFactory: () => new FakeMatIconRegistry() })

application/frontend/src/app/core/components/time-navigation/time-navigation.component.ts

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ import {
1515
Inject,
1616
Input,
1717
LOCALE_ID,
18+
OnChanges,
1819
Output,
20+
SimpleChanges,
1921
ViewEncapsulation,
2022
} from '@angular/core';
23+
import { MatDatepickerInputEvent } from '@angular/material/datepicker';
2124
import { Range } from 'src/app/shared/models';
2225

2326
@Component({
@@ -27,17 +30,52 @@ import { Range } from 'src/app/shared/models';
2730
changeDetection: ChangeDetectionStrategy.OnPush,
2831
encapsulation: ViewEncapsulation.None,
2932
})
30-
export class TimeNavigationComponent {
33+
export class TimeNavigationComponent implements OnChanges {
3134
@Input() range: Range;
3235
@Input() rangeOffset: number;
3336
@Input() nowRangeOffset: number;
34-
@Input() previousRangeOffset: number;
35-
@Input() nextRangeOffset: number;
3637
@Input() timezoneOffset = 0;
38+
@Input() globalDuration: [Long, Long];
3739
@Output() rangeOffsetChange = new EventEmitter<number>();
3840

41+
currentOffsetDate: Date;
42+
globalStart: Date;
43+
globalEnd: Date;
44+
previousRangeOffset: number;
45+
nextRangeOffset: number;
46+
3947
constructor(@Inject(LOCALE_ID) private locale: string) {}
4048

49+
ngOnChanges(changes: SimpleChanges): void {
50+
if (changes.globalDuration) {
51+
this.globalStart = this.getGlobalStart();
52+
this.globalEnd = this.getGlobalEnd();
53+
}
54+
55+
if (changes.rangeOffset || changes.timezoneOffset) {
56+
this.currentOffsetDate = new Date((this.rangeOffset + this.timezoneOffset) * 1000);
57+
this.currentOffsetDate.setTime(
58+
this.currentOffsetDate.getTime() + this.currentOffsetDate.getTimezoneOffset() * 60 * 1000
59+
);
60+
61+
const previousDate = new Date(this.currentOffsetDate);
62+
previousDate.setDate(previousDate.getDate() - 1);
63+
this.previousRangeOffset =
64+
this.globalStart.getTime() <= previousDate.getTime() ? previousDate.getTime() / 1000 : null;
65+
66+
const nextDate = new Date(this.currentOffsetDate);
67+
nextDate.setDate(nextDate.getDate() + 1);
68+
this.nextRangeOffset = nextDate.getTime() / 1000;
69+
this.nextRangeOffset =
70+
this.globalEnd.getTime() >= nextDate.getTime() ? nextDate.getTime() / 1000 : null;
71+
}
72+
}
73+
74+
onDateChange(event: MatDatepickerInputEvent<Date>): void {
75+
const utcSeconds = (event.value.getTime() + event.value.getTimezoneOffset() * 60 * 1000) / 1000;
76+
this.rangeOffsetChange.emit(utcSeconds + this.timezoneOffset);
77+
}
78+
4179
onNextClick(): void {
4280
this.rangeOffsetChange.emit(this.nextRangeOffset);
4381
}
@@ -50,6 +88,18 @@ export class TimeNavigationComponent {
5088
this.rangeOffsetChange.emit(this.nowRangeOffset);
5189
}
5290

91+
getGlobalStart(): Date {
92+
const date = new Date((this.globalDuration[0].toNumber() + this.timezoneOffset) * 1000);
93+
date.setTime(date.getTime() + date.getTimezoneOffset() * 60 * 1000);
94+
return date;
95+
}
96+
97+
getGlobalEnd(): Date {
98+
const date = new Date((this.globalDuration[1].toNumber() + this.timezoneOffset) * 1000);
99+
date.setTime(date.getTime() + date.getTimezoneOffset() * 60 * 1000);
100+
return date;
101+
}
102+
53103
getFormattedRangeOffset(): string {
54104
return Number.isFinite(this.rangeOffset)
55105
? formatDate((this.rangeOffset + this.timezoneOffset) * 1000, 'longDate', this.locale, 'UTC')

application/frontend/src/app/core/containers/app/app.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export class AppComponent {
4545
'arrow_downward',
4646
'arrow_drop_down',
4747
'arrow_upward',
48+
'calendar',
4849
'clear',
4950
'clock',
5051
'cloud_download',

application/frontend/src/app/core/containers/post-solve-control-bar/post-solve-control-bar.component.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@
3535
[range]="range$ | async"
3636
[rangeOffset]="rangeOffset$ | async"
3737
[nowRangeOffset]="nowRangeOffset$ | async"
38-
[previousRangeOffset]="previousRangeOffset$ | async"
39-
[nextRangeOffset]="nextRangeOffset$ | async"
4038
[timezoneOffset]="timezoneOffset$ | async"
39+
[globalDuration]="globalDuration$ | async"
4140
(rangeOffsetChange)="onRangeOffsetChange($event)">
4241
</app-time-navigation>

application/frontend/src/app/core/containers/post-solve-control-bar/post-solve-control-bar.component.spec.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import * as fromConfig from '../../selectors/config.selectors';
2121
import * as fromScenario from '../../selectors/scenario.selectors';
2222
import * as fromUndoRedo from '../../selectors/undo-redo.selectors';
2323
import { PostSolveControlBarComponent } from './post-solve-control-bar.component';
24+
import ShipmentModelSelectors from '../../selectors/shipment-model.selectors';
25+
import Long from 'long';
2426

2527
@Component({
2628
selector: 'app-generate-button',
@@ -44,11 +46,10 @@ class MockMapToggleButtonComponent {
4446
})
4547
class MockTimeNavigationComponent {
4648
@Input() range: Range;
47-
@Input() rangeOffset: number;
48-
@Input() nowRangeOffset: number;
4949
@Input() previousRangeOffset: number;
5050
@Input() nextRangeOffset: number;
5151
@Input() timezoneOffset: number;
52+
@Input() globalDuration: [Long, Long];
5253
@Output() rangeOffsetChange = new EventEmitter<number>();
5354
}
5455

@@ -73,8 +74,10 @@ describe('PostSolveControlBarComponent', () => {
7374
{ selector: RoutesChartSelectors.selectSelectedRange, value: [] },
7475
{ selector: RoutesChartSelectors.selectRangeOffset, value: 0 },
7576
{ selector: RoutesChartSelectors.selectNowRangeOffset, value: 0 },
76-
{ selector: RoutesChartSelectors.selectPreviousRangeOffset, value: 0 },
77-
{ selector: RoutesChartSelectors.selectNextRangeOffset, value: 0 },
77+
{
78+
selector: ShipmentModelSelectors.selectGlobalDuration,
79+
value: [Long.ZERO, Long.ZERO],
80+
},
7881
{ selector: fromConfig.selectTimezoneOffset, value: 0 },
7982
{ selector: fromUndoRedo.selectCanUndo, value: false },
8083
{ selector: fromUndoRedo.selectCanRedo, value: false },

application/frontend/src/app/core/containers/post-solve-control-bar/post-solve-control-bar.component.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { Page } from '../../models';
2222
import * as fromConfig from '../../selectors/config.selectors';
2323
import * as fromScenario from '../../selectors/scenario.selectors';
2424
import * as fromUndoRedo from '../../selectors/undo-redo.selectors';
25+
import ShipmentModelSelectors from '../../selectors/shipment-model.selectors';
2526

2627
@Component({
2728
selector: 'app-post-solve-control-bar',
@@ -35,11 +36,10 @@ export class PostSolveControlBarComponent implements OnInit {
3536
range$: Observable<Range>;
3637
rangeOffset$: Observable<number>;
3738
nowRangeOffset$: Observable<number>;
38-
previousRangeOffset$: Observable<number>;
39-
nextRangeOffset$: Observable<number>;
4039
timezoneOffset$: Observable<number>;
4140
canUndo$: Observable<boolean>;
4241
canRedo$: Observable<boolean>;
42+
globalDuration$: Observable<[Long, Long]>;
4343

4444
get Page(): typeof Page {
4545
return Page;
@@ -56,13 +56,10 @@ export class PostSolveControlBarComponent implements OnInit {
5656
this.range$ = this.store.pipe(select(RoutesChartSelectors.selectSelectedRange));
5757
this.rangeOffset$ = this.store.pipe(select(RoutesChartSelectors.selectRangeOffset));
5858
this.nowRangeOffset$ = this.store.pipe(select(RoutesChartSelectors.selectNowRangeOffset));
59-
this.previousRangeOffset$ = this.store.pipe(
60-
select(RoutesChartSelectors.selectPreviousRangeOffset)
61-
);
62-
this.nextRangeOffset$ = this.store.pipe(select(RoutesChartSelectors.selectNextRangeOffset));
6359
this.timezoneOffset$ = this.store.pipe(select(fromConfig.selectTimezoneOffset));
6460
this.canUndo$ = this.store.pipe(select(fromUndoRedo.selectCanUndo));
6561
this.canRedo$ = this.store.pipe(select(fromUndoRedo.selectCanRedo));
62+
this.globalDuration$ = this.store.pipe(select(ShipmentModelSelectors.selectGlobalDuration));
6663
}
6764

6865
onRangeOffsetChange(rangeOffset: number): void {
Lines changed: 4 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)