Skip to content
This repository was archived by the owner on Feb 28, 2021. It is now read-only.

Commit 9062abb

Browse files
committed
List active shifts on the volunteer page
1 parent 2b77f5f commit 9062abb

File tree

2 files changed

+79
-9
lines changed

2 files changed

+79
-9
lines changed

src/components/VolunteerListItem.tsx

+57-6
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44

55
import React from 'react';
66
import bind from 'bind-decorator';
7+
import clsx from 'clsx';
78

89
import AccessCodeDialogButton from './AccessCodeDialogButton';
910
import AvatarDialogButton from './AvatarDialogButton';
1011
import ConditionalLink from './ConditionalLink';
1112
import ConditionalListItem from './ConditionalListItem';
1213
import { Volunteer } from '../app/Volunteer';
14+
import { VolunteerActivityInfo } from '../app/Event';
1315
import slug from '../app/util/Slug';
1416

1517
import IconButton from '@material-ui/core/IconButton';
@@ -24,18 +26,38 @@ const styles = (theme: Theme) =>
2426
link: {
2527
textDecoration: 'none',
2628
color: 'inherit',
27-
}
29+
},
30+
31+
active: {
32+
...theme.activeSessionStyle,
33+
borderBottomWidth: 0,
34+
},
35+
unavailable: {
36+
...theme.pastSessionStyle,
37+
borderBottomWidth: 0,
38+
filter: 'grayscale(80%)',
39+
},
2840
});
2941

3042
/**
3143
* Properties accepted by the <Volunteer> element.
3244
*/
3345
interface Properties {
46+
/**
47+
* Indicates that the volunteer is active.
48+
*/
49+
active?: boolean;
50+
3451
/**
3552
* The volunteer for whom this element is being rendered.
3653
*/
3754
volunteer: Volunteer;
3855

56+
/**
57+
* Optional activity information that will be displayed for status-based list items.
58+
*/
59+
volunteerActivityInfo?: VolunteerActivityInfo;
60+
3961
/**
4062
* Type of list item that should be rendered. The contents of the list item will automatically
4163
* be compiled based on the |volunteer| property.
@@ -46,6 +68,11 @@ interface Properties {
4668
*/
4769
type: "header" | "status";
4870

71+
/**
72+
* Indicates that the volunteer is unavailable.
73+
*/
74+
unavailable?: boolean;
75+
4976
/**
5077
* An event that is to be invoked if the photo of the |volunteer| has been updated. The ability
5178
* to change the photo will be enabled based on whether this property has been set.
@@ -58,6 +85,29 @@ interface Properties {
5885
* property can be used to influence which subset of information should be presented.
5986
*/
6087
class VolunteerListItem extends React.PureComponent<Properties & WithStyles<typeof styles>> {
88+
/**
89+
* Compiles the status line for the volunteer based on the available information.
90+
*/
91+
compileVolunteerStatusLine(): string {
92+
const { volunteer, volunteerActivityInfo } = this.props;
93+
94+
let status = volunteer.title;
95+
96+
if (volunteerActivityInfo && volunteerActivityInfo.currentShift) {
97+
const { currentShift } = volunteerActivityInfo;
98+
99+
const until = ' until ' + currentShift.endTime.format('HH:mm');
100+
101+
if (currentShift.isUnavailable()) {
102+
status += ' • unavailable' + until;
103+
} else if (currentShift.isEvent()) {
104+
status += ' • ' + currentShift.event.sessions[0].name + until;
105+
}
106+
}
107+
108+
return status;
109+
}
110+
61111
/**
62112
* Opens the dialer on the device (if any) to make a phone call to this volunteer. Should only
63113
* be called when the volunteer has a known telephone number.
@@ -75,7 +125,7 @@ class VolunteerListItem extends React.PureComponent<Properties & WithStyles<type
75125
}
76126

77127
render() {
78-
const { classes, onPictureUpdated, type, volunteer } = this.props;
128+
const { active, classes, onPictureUpdated, type, unavailable, volunteer } = this.props;
79129

80130
let location: string | undefined = '/volunteers/' + slug(volunteer.name);
81131

@@ -97,20 +147,21 @@ class VolunteerListItem extends React.PureComponent<Properties & WithStyles<type
97147
);
98148
break;
99149
case 'status':
100-
// TODO: Include the volunteer's current shift.
101-
secondary = volunteer.title;
150+
secondary = this.compileVolunteerStatusLine();
102151
break;
103152
default:
104153
throw new Error('Invalid type value: ' + type);
105154
}
106155

107156
return (
108157
<ConditionalLink className={classes.link} to={location}>
109-
<ConditionalListItem button={!!location}>
158+
<ConditionalListItem button={!!location} className={clsx(active && classes.active, unavailable && classes.unavailable)}>
110159
<AvatarDialogButton volunteer={volunteer} onPictureUpdated={onPictureUpdated} />
111160
<ListItemText
112161
primary={primary}
113-
secondary={secondary} />
162+
primaryTypographyProps={{ noWrap: true }}
163+
secondary={secondary}
164+
secondaryTypographyProps={{ noWrap: true }} />
114165
{actions}
115166
</ConditionalListItem>
116167
</ConditionalLink>

src/views/VolunteerListPage.tsx

+22-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ import { VolunteerGroupTabs, VolunteerGroupTabInfo } from '../components/Volunte
1313
import VolunteerListItem from '../components/VolunteerListItem';
1414

1515
import List from '@material-ui/core/List';
16+
import { Theme } from '@material-ui/core/styles/createMuiTheme';
17+
import createStyles from '@material-ui/core/styles/createStyles';
18+
import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles';
19+
20+
const styles = (theme: Theme) =>
21+
createStyles({
22+
fullWidth: {
23+
...theme.fullWidthPaperMixin,
24+
}
25+
});
1626

1727
/**
1828
* Properties available to the controller through the router.
@@ -51,7 +61,7 @@ interface State {
5161
* name and an avatar, as well as their title and, if any, their current activity. When there are
5262
* multiple groups of volunteers, a tab switcher will be shown as well.
5363
*/
54-
export class VolunteerListPage extends React.Component<Properties, State> {
64+
class VolunteerListPage extends React.Component<Properties & WithStyles<typeof styles>, State> {
5565
state: State = {
5666
activeTabIndex: 0,
5767
tabs: [],
@@ -110,20 +120,29 @@ export class VolunteerListPage extends React.Component<Properties, State> {
110120

111121
render() {
112122
const { activeTabIndex, tabs, volunteers } = this.state;
123+
const { classes } = this.props;
113124

114125
return (
115126
<>
116127
<VolunteerGroupTabs activeTabChange={this.onVolunteerGroupChange}
117128
activeTabIndex={activeTabIndex}
118129
tabs={tabs} />
119130

120-
<List>
131+
<List className={classes.fullWidth}>
121132
{volunteers.map(volunteer =>
122133
<VolunteerListItem key={volunteer.volunteer.userToken}
123134
type="status"
124-
volunteer={volunteer.volunteer} /> )}
135+
active={volunteer.currentShift &&
136+
volunteer.currentShift.isEvent()}
137+
unavailable={volunteer.currentShift &&
138+
volunteer.currentShift.isUnavailable()}
139+
volunteer={volunteer.volunteer}
140+
volunteerActivityInfo={volunteer} /> )}
125141
</List>
126142
</>
127143
);
128144
}
129145
}
146+
147+
const StyledVolunteerListPage = withStyles(styles)(VolunteerListPage);
148+
export { StyledVolunteerListPage as VolunteerListPage };

0 commit comments

Comments
 (0)