Skip to content
This repository was archived by the owner on May 31, 2023. It is now read-only.

Commit cb72b1b

Browse files
committed
GUI support for hiding plates
1 parent 5bdad2e commit cb72b1b

File tree

13 files changed

+260
-13
lines changed

13 files changed

+260
-13
lines changed

src/sample-db-app/src/app/actions/plate.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export const UPLOAD_FAILURE = '[Plate] Upload Failure';
1313
export const UPDATE = '[Plate] Update';
1414
export const UPDATE_FAILURE = '[Plate] Update Failure';
1515
export const SELECT = '[Plate] Select';
16+
export const HIDE = '[Plate] Hide';
17+
export const HIDE_FAILURE = '[Plate] Hide Failure';
18+
export const UNHIDE = '[Plate] Unhide';
19+
export const UNHIDE_FAILURE = '[Plate] Unhide Failure';
20+
export const TOGGLE_HIDDEN = '[Plate] Toggle Hidden';
1621

1722
export class GetAllAction implements Action {
1823
readonly type = GET_ALL;
@@ -84,6 +89,35 @@ export class SelectAction implements Action {
8489
constructor(public payload: string) { }
8590
}
8691

92+
export class HideAction implements Action {
93+
readonly type = HIDE;
94+
95+
constructor(public payload: number[]) { }
96+
}
97+
98+
export class HideFailureAction implements Action {
99+
readonly type = HIDE_FAILURE;
100+
101+
constructor(public payload: string) { }
102+
}
103+
104+
export class UnhideAction implements Action {
105+
readonly type = UNHIDE;
106+
107+
constructor(public payload: number[]) { }
108+
}
109+
110+
export class UnhideFailureAction implements Action{
111+
readonly type = UNHIDE_FAILURE;
112+
113+
constructor(public payload: string) { }
114+
}
115+
116+
export class ToggleHiddenAction implements Action {
117+
readonly type = TOGGLE_HIDDEN;
118+
constructor() { }
119+
}
120+
87121
export type Actions
88122
= GetAllAction
89123
| LoadOneAction
@@ -95,4 +129,9 @@ export type Actions
95129
| UploadFailureAction
96130
| UpdateAction
97131
| UpdateFailureAction
98-
| SelectAction;
132+
| SelectAction
133+
| HideAction
134+
| HideFailureAction
135+
| UnhideAction
136+
| UnhideFailureAction
137+
| ToggleHiddenAction;

src/sample-db-app/src/app/components/plate/plate-detail.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ import { SpecimenType } from '../../models/specimen-type';
1818
</md-card-subtitle>
1919
</md-card-title>
2020
<div class="control-buttons">
21-
<button md-mini-fab class="delete-button" mdTooltip="Delete Plate" (click)="deleteButton.emit()">
21+
<button md-mini-fab class="control-button" mdTooltip="Toggle Hidden" (click)="toggleHidden.emit()">
22+
<md-icon>{{visibility}}</md-icon>
23+
</button>
24+
<button md-mini-fab class="control-button" mdTooltip="Delete Plate" (click)="deleteButton.emit()">
2225
<md-icon>close</md-icon>
2326
</button>
2427
</div>
@@ -80,7 +83,7 @@ import { SpecimenType } from '../../models/specimen-type';
8083
md-card-title {
8184
width: 100%
8285
}
83-
.edit-button {
86+
.control-button {
8487
margin-bottom: 5px;
8588
}
8689
.control-buttons {
@@ -97,6 +100,7 @@ import { SpecimenType } from '../../models/specimen-type';
97100
})
98101
export class MatrixPlateDetailComponent {
99102
@Output() deleteButton = new EventEmitter();
103+
@Output() toggleHidden = new EventEmitter();
100104
@Input() plate: MatrixPlate;
101105
@Input() location: Location;
102106
@Input() matrixTubes: {[id: string]: MatrixTube};
@@ -115,6 +119,10 @@ export class MatrixPlateDetailComponent {
115119
return this.location ? this.location.description : undefined;
116120
}
117121

122+
get visibility() {
123+
return this.plate.hidden ? 'visibility' : 'visibility_off';
124+
}
125+
118126
wellPosition(t: MatrixTube) {
119127
return t.well_position;
120128
}

src/sample-db-app/src/app/components/plate/plate-preview.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,19 @@ import { Location } from '../../models/location';
99
<a [routerLink]="['/plate', id]">
1010
<md-card>
1111
<md-card-title-group>
12-
<md-card-title>{{ uid }}</md-card-title>
12+
<md-card-title style="display: flex">
13+
<div style="width: 190px">
14+
<span>{{ uid }}</span>
15+
</div>
16+
<div style="flex-grow: 1">
17+
<span class="hidden-toggle"><md-icon>{{ visibile }}</md-icon></span>
18+
</div>
19+
</md-card-title>
1320
<md-card-subtitle>{{ plateLocation }}</md-card-subtitle>
1421
</md-card-title-group>
1522
<md-card-content>
16-
<p><span><small>Created: {{ created | date:'mediumDate' }}</small></span></p>
17-
<p><span><small>Last Updated: {{ last_updated | date:'mediumDate' }}</small></span></p>
23+
<p><span class="content"><small>Created: {{ created | date:'mediumDate' }}</small></span></p>
24+
<p><span class="content"><small>Last Updated: {{ last_updated | date:'mediumDate' }}</small></span></p>
1825
<p *ngIf="numTubes !== undefined"><span><small># Tubes: {{ numTubes }}</small></span></p>
1926
</md-card-content>
2027
</md-card>
@@ -36,6 +43,7 @@ import { Location } from '../../models/location';
3643
}
3744
md-card-title {
3845
margin-right: 10px;
46+
width: 100%;
3947
}
4048
md-card-title-group {
4149
margin: 0;
@@ -53,19 +61,28 @@ import { Location } from '../../models/location';
5361
margin-top: 10px;
5462
margin: 1px 0 0;
5563
}
56-
span {
64+
.content {
5765
display: inline-block;
5866
font-size: 13px;
5967
}
6068
md-card-footer {
6169
padding: 0 25px 25px;
6270
}
71+
.hidden-toggle {
72+
color: grey;
73+
}
6374
`]
6475
})
6576
export class MatrixPlatePreviewComponent {
6677
@Input() plate: MatrixPlate;
6778
@Input() location: Location;
6879

80+
private _toggleClicked = false;
81+
82+
get visibile() {
83+
return this.plate.hidden ? 'visibility_off' : 'visibility';
84+
}
85+
6986
get id() {
7087
return this.plate.id;
7188
}
@@ -89,4 +106,5 @@ export class MatrixPlatePreviewComponent {
89106
get numTubes() {
90107
return this.plate ? this.plate.tubes.length : undefined;
91108
}
109+
92110
}

src/sample-db-app/src/app/components/sidenav.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
44
selector: 'sdb-sidenav',
55
changeDetection: ChangeDetectionStrategy.OnPush,
66
template: `
7-
<md-sidenav [opened]="open" [disableClose]=true >
7+
<md-sidenav [opened]="open" [disableClose]="disableClose" >
88
<md-nav-list>
99
<ng-content></ng-content>
1010
</md-nav-list>
@@ -18,4 +18,5 @@ import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
1818
})
1919
export class SidenavComponent {
2020
@Input() open = false;
21+
@Input() disableClose = true;
2122
}

src/sample-db-app/src/app/containers/plate/plate-page.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,14 @@ export class MatrixPlateListPageComponent {
3838
action: go(['/plate/create']),
3939
color: 'primary',
4040
tooltip: 'Add New Plate'
41+
},
42+
{
43+
icon: 'visibility',
44+
action: new matrixPlate.ToggleHiddenAction(),
45+
color: 'primary',
46+
tooltip: 'Toggle Hidden Plates'
4147
}
48+
4249
];
4350
toolbarColor = 'accent';
4451
toolbarTitle = 'Plates';
@@ -47,7 +54,7 @@ export class MatrixPlateListPageComponent {
4754
this.store = store;
4855
const sortProperty$ = of('location');
4956
this.locations$ = store.select(fromRoot.getLocationEntities);
50-
this.plates$ = store.select(fromRoot.getAllMatrixPlates);
57+
this.plates$ = store.select(fromRoot.getActiveMatrixPlates);
5158
this.plates$ = combineLatest(sortProperty$, this.plates$, (prop, items) => {
5259
return _.sortBy(items, prop);
5360
});
@@ -58,4 +65,6 @@ export class MatrixPlateListPageComponent {
5865
dispatchAction(action) {
5966
this.store.dispatch(action);
6067
}
68+
69+
6170
}

src/sample-db-app/src/app/containers/plate/selected-plate-page.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { SpecimenType } from '../../models/specimen-type';
2020
template: `
2121
<sdb-plate-detail
2222
(deleteButton)="deletePlate()"
23+
(toggleHidden)="toggleHidden()"
2324
[plate]="plate$ | async"
2425
[location]="location$ | async"
2526
[matrixTubes]="matrixTubes$ | async"
@@ -67,4 +68,14 @@ export class SelectedMatrixPlatePageComponent {
6768
}
6869
});
6970
}
71+
72+
toggleHidden() {
73+
this.plate$.take(1).subscribe(selectedPlate => {
74+
if (selectedPlate.hidden) {
75+
this.store.dispatch(new matrixPlate.UnhideAction([+selectedPlate.id]));
76+
} else {
77+
this.store.dispatch(new matrixPlate.HideAction([+selectedPlate.id]));
78+
}
79+
});
80+
}
7081
}

src/sample-db-app/src/app/containers/study/create-study-page.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ export class CreateStudyPageComponent {
3636
short_code: null,
3737
is_longitudinal: false,
3838
lead_person: null,
39-
subjects: []
39+
subjects: [],
40+
hidden: false
4041
};
4142
}
4243

src/sample-db-app/src/app/effects/plate.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,46 @@ export class MatrixPlateEffects {
116116
});
117117
});
118118

119+
@Effect()
120+
hide$: Observable<Action> = this.actions$
121+
.ofType(matrixPlate.HIDE)
122+
.map(toPayload)
123+
.switchMap(payload => {
124+
return this.matrixPlateService.hidePlates(payload)
125+
.concatMap((matrixPlates: MatrixPlate[]) => {
126+
return [ new matrixPlate.LoadSuccessAction(matrixPlates), go('plate') ];
127+
})
128+
.catch(err => {
129+
if (err.status < 500 && err.status > 400) {
130+
if (err.message) {
131+
return of(new matrixPlate.HideFailureAction(err.message));
132+
} else {
133+
return of(new matrixPlate.HideFailureAction(JSON.parse(err._body).message));
134+
}
135+
}
136+
});
137+
});
138+
139+
@Effect()
140+
unhide$: Observable<Action> = this.actions$
141+
.ofType(matrixPlate.UNHIDE)
142+
.map(toPayload)
143+
.switchMap(payload => {
144+
return this.matrixPlateService.unhidePlates(payload)
145+
.concatMap((matrixPlates: MatrixPlate[]) => {
146+
return [ new matrixPlate.LoadSuccessAction(matrixPlates), go('plate') ];
147+
})
148+
.catch(err => {
149+
if (err.status < 500 && err.status > 400) {
150+
if (err.message) {
151+
return of(new matrixPlate.UnhideFailureAction(err.message));
152+
} else {
153+
return of(new matrixPlate.UnhideFailureAction(JSON.parse(err._body).message));
154+
}
155+
}
156+
});
157+
});
158+
119159

120160
constructor(private actions$: Actions, private matrixPlateService: MatrixPlateService) { }
121161
}

src/sample-db-app/src/app/reducers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ export const getSelectedMatrixPlateLocation = createSelector(getSelectedMatrixPl
181181
return plate ? locations[plate.location] : undefined;
182182
});
183183
export const getAllMatrixPlates = createSelector(getMatrixPlateState, fromMatrixPlate.getAll);
184+
export const getActiveMatrixPlates = createSelector(getMatrixPlateState, fromMatrixPlate.getActive);
184185
export const getUpdateMatrixPlateError = createSelector(getMatrixPlateState, fromMatrixPlate.getUpdateError);
185186
export const getUploadMatrixPlateError = createSelector(getMatrixPlateState, fromMatrixPlate.getUploadError);
186187
export const getDeleteMatrixPlateError = createSelector(getMatrixPlateState, fromMatrixPlate.getDeleteError);

src/sample-db-app/src/app/reducers/plate.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,24 @@ export interface State {
88
deleteError: string | null;
99
uploadError: string | null;
1010
updateError: string | null;
11+
hideError: string | null;
12+
unhideError: string | null;
1113
ids: string[];
1214
entities: { [id: string]: MatrixPlate };
1315
selectedPlateId: string | null;
16+
showHidden: boolean;
1417
}
1518

1619
export const initialState: State = {
1720
deleteError: null,
1821
uploadError: null,
1922
updateError: null,
23+
hideError: null,
24+
unhideError: null,
2025
ids: [],
2126
entities: {},
22-
selectedPlateId: null
27+
selectedPlateId: null,
28+
showHidden: false
2329
};
2430

2531
export function reducer(state = initialState, action: matrixPlate.Actions | bulk.Actions): State {
@@ -29,7 +35,7 @@ export function reducer(state = initialState, action: matrixPlate.Actions | bulk
2935
const newPlates = plates.filter(plate => !state.entities[plate.id]);
3036

3137
const newPlateIds = newPlates.map(plate => plate.id);
32-
const newPlateEntities = newPlates.reduce((entities: { [id: string]: MatrixPlate }, plate: MatrixPlate) => {
38+
const plateEntities = plates.reduce((entities: { [id: string]: MatrixPlate }, plate: MatrixPlate) => {
3339
return Object.assign(entities, {
3440
[plate.id]: plate
3541
});
@@ -39,8 +45,10 @@ export function reducer(state = initialState, action: matrixPlate.Actions | bulk
3945
deleteError: null,
4046
uploadError: null,
4147
updateError: null,
48+
hideError: null,
49+
unhideError: null,
4250
ids: [ ...state.ids, ...newPlateIds ],
43-
entities: Object.assign({}, state.entities, newPlateEntities),
51+
entities: Object.assign({}, state.entities, plateEntities),
4452
});
4553

4654
case matrixPlate.LOAD_ONE:
@@ -105,6 +113,17 @@ export function reducer(state = initialState, action: matrixPlate.Actions | bulk
105113

106114
return Object.assign({}, state, {selectedPlateId: selectedId});
107115

116+
case matrixPlate.HIDE_FAILURE:
117+
const hideError = action.payload;
118+
return Object.assign({}, state, {hideError: hideError});
119+
120+
case matrixPlate.UNHIDE_FAILURE:
121+
const unhideError = action.payload;
122+
return Object.assign({}, state, {unhideError: unhideError});
123+
124+
case matrixPlate.TOGGLE_HIDDEN:
125+
return Object.assign({}, state, {showHidden: !state.showHidden});
126+
108127
case bulk.DELETE_BARCODES_SUCCESS:
109128
case bulk.DELETE_SPECIMENS_SUCCESS:
110129
const deletedTubeIds = action.payload.matrixTubeIds;
@@ -144,6 +163,8 @@ export const getUpdateError = (state: State) => state.updateError;
144163

145164
export const getUploadError = (state: State) => state.uploadError;
146165

166+
export const shouldShowHidden = (state: State) => state.showHidden;
167+
147168
export const getSelected = createSelector(getEntities, getSelectedId, (entities, selectedId) => {
148169
return entities[selectedId];
149170
});
@@ -152,4 +173,19 @@ export const getAll = createSelector(getEntities, getIds, (entities, ids) => {
152173
return ids.map(id => entities[id]);
153174
});
154175

176+
export const getUnhidden = createSelector(getAll, (allPlates) => {
177+
return allPlates.filter(plate => !plate.hidden);
178+
});
179+
180+
export const getHidden = createSelector(getAll, (allPlates) => {
181+
return allPlates.filter(plate => plate.hidden);
182+
});
183+
184+
export const getActive = createSelector(shouldShowHidden, getAll, getUnhidden, (shouldShowHidden, allPlates, unhiddenPlates) => {
185+
if (shouldShowHidden) {
186+
return allPlates;
187+
} else {
188+
return unhiddenPlates;
189+
}
190+
});
155191

0 commit comments

Comments
 (0)