Skip to content

Commit 6fc4da4

Browse files
committed
Removed the need for an accordion and display the latest checkin for the quarter (no grace period) and, for members without activity during the quarter, a link to the last known checkin.
1 parent bf023eb commit 6fc4da4

File tree

5 files changed

+59
-143
lines changed

5 files changed

+59
-143
lines changed

web-ui/src/components/reports-section/checkin-report/LinkSection.css

Lines changed: 0 additions & 6 deletions
This file was deleted.

web-ui/src/components/reports-section/checkin-report/LinkSection.jsx

Lines changed: 0 additions & 35 deletions
This file was deleted.

web-ui/src/components/reports-section/checkin-report/TeamMemberMap.css

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
.team-member-map-accordion {
2-
.team-member-map-accordion-summary {
1+
.team-member-map-row {
2+
.team-member-map-row-summary {
33
display: flex;
44
flex-direction: row;
55
justify-content: space-between;
66
align-items: center;
77
padding: 0.5rem 1.5rem;
88
margin-top: 0.5rem;
9-
cursor: pointer;
109

1110
.team-member-map-summmary-content {
1211
display: grid;

web-ui/src/components/reports-section/checkin-report/TeamMemberMap.jsx

Lines changed: 47 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@ import {
66
Chip,
77
Typography,
88
AccordionDetails,
9-
Box
9+
Box,
10+
Card,
11+
CardContent,
1012
} from '@mui/material';
13+
import { Link } from 'react-router-dom';
1114
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
1215
import { getAvatarURL } from '../../../api/api.js';
1316
import { AppContext } from '../../../context/AppContext.jsx';
1417
import { selectCheckinsForMember } from '../../../context/selectors.js';
1518
import {
1619
isPastCheckin,
1720
getCheckinDate,
18-
getQuarterBeginEndWithGrace,
19-
getCheckinDateForPeriod,
20-
getLastCheckinDate,
2121
statusForPeriodByMemberScheduling
2222
} from './checkin-utils.js';
23-
import LinkSection from './LinkSection.jsx';
23+
import { getQuarterBeginEnd } from '../../../helpers';
2424
import './TeamMemberMap.css';
2525

2626
const SortOption = {
@@ -33,13 +33,14 @@ const TeamMemberMap = ({ members, closed, planned, reportDate }) => {
3333
const [sortBy, setSortBy] = useState(SortOption.BY_MEMBER);
3434

3535
const epoch = new Date(0);
36-
const pdls = members.reduce((map, member) => {
37-
if (member.pdlId && !map[member.pdlId]) {
38-
map[member.pdlId] = members.find((m) => m.id === member.pdlId);
39-
}
36+
const memberMap = members.reduce((map, member) => {
37+
map[member.id] = member;
4038
return map;
4139
}, {});
4240

41+
const linkStyle={ textDecoration: 'none' };
42+
const checkinPath = (member, checkin) => `/checkins/${member.id}/${checkin.id}`;
43+
4344
const sortByName = (left, right) => {
4445
if (left && right) {
4546
return `${left.lastName} ${left.firstName}`.localeCompare(
@@ -50,34 +51,32 @@ const TeamMemberMap = ({ members, closed, planned, reportDate }) => {
5051
};
5152

5253
const sortByPDLName = (a, b) =>
53-
sortByName(pdls[a.reportDatePDLId], pdls[b.reportDatePDLId]);
54+
sortByName(memberMap[a.reportDatePDLId], memberMap[b.reportDatePDLId]);
5455

5556
// We're going to cache the checkins into the member data structures so that
5657
// we can properly sort by PDL when the PDL, in the past, is different than
5758
// the current PDL.
58-
const { startOfQuarter, endOfQuarter } = getQuarterBeginEndWithGrace(reportDate);
59+
const { startOfQuarter, endOfQuarter } = getQuarterBeginEnd(reportDate);
5960
members.map(member => {
60-
const checkins = selectCheckinsForMember(
61+
member.checkins = selectCheckinsForMember(
6162
state,
6263
member.id,
6364
).filter(checkin => closed || !checkin.completed)
64-
.filter(checkin => planned || isPastCheckin(checkin));
65-
member.checkins = checkins;
65+
.filter(checkin => planned || isPastCheckin(checkin))
66+
.filter(checkin => (getCheckinDate(checkin) <= endOfQuarter));
6667

6768
// If there are checkins, we're going to sort them with the latest
6869
// first. Since the member's PDL could have changed since the last
6970
// checkin, we are going to use the PDL id of the checkin instead
7071
// of the current PDL. They may be the same, but again they may not.
72+
member.checkin = null;
7173
member.reportDatePDLId = member.pdlId;
72-
if (checkins.length > 0) {
73-
checkins.sort((a, b) => getCheckinDate(b) - getCheckinDate(a));
74-
75-
for(let checkin of checkins) {
76-
const checkinDate = getCheckinDate(checkin);
77-
if (checkinDate >= startOfQuarter && checkinDate <= endOfQuarter) {
78-
member.reportDatePDLId = checkin.pdlId;
79-
break;
80-
}
74+
if (member.checkins.length > 0) {
75+
member.checkins.sort((a, b) => getCheckinDate(b) - getCheckinDate(a));
76+
const checkin = member.checkins[0];
77+
if (getCheckinDate(checkin) >= startOfQuarter) {
78+
member.checkin = checkin;
79+
member.reportDatePDLId = member.checkin.pdlId;
8180
}
8281
}
8382
});
@@ -105,18 +104,10 @@ const TeamMemberMap = ({ members, closed, planned, reportDate }) => {
105104
</Box>
106105
{
107106
members.map(member => {
108-
const pdl = pdls[member.reportDatePDLId];
109-
const checkins = member.checkins;
110-
107+
const pdl = memberMap[member.reportDatePDLId];
111108
return (
112-
<Accordion
113-
key={member.id}
114-
className="team-member-map-accordion"
115-
>
116-
<AccordionSummary
117-
expandIcon={<ExpandMoreIcon />}
118-
className="team-member-map-accordion-summary"
119-
>
109+
<Card key={member.id} className="team-member-map-row">
110+
<CardContent className="team-member-map-row-summary">
120111
<Avatar
121112
sx={{ width: 54, height: 54 }}
122113
src={getAvatarURL(member.workEmail)}
@@ -148,36 +139,30 @@ const TeamMemberMap = ({ members, closed, planned, reportDate }) => {
148139
<Typography
149140
variant="caption"
150141
component={'time'}
151-
dateTime={getLastCheckinDate(checkins).toISOString()}
142+
dateTime={getCheckinDate(member.checkin).toISOString()}
152143
sx={{ display: { xs: 'none', sm: 'none', md: 'grid' } }}
153144
className="team-member-map-summmary-latest-activity"
154145
>
155146
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
156-
{getCheckinDateForPeriod(
157-
checkins,
158-
reportDate
159-
).getFullYear() === epoch.getFullYear() ? (
147+
{member.checkin == null ? (
160148
<Typography component="nobr" variant="h6">
161149
No activity yet{' '}
162150
<span role="img" aria-label="unscheduled">
163151
🚫
164152
</span>
165153
</Typography>
166154
) : (
167-
<>
155+
<Link style={linkStyle}
156+
to={checkinPath(member, member.checkin)}>
168157
<Typography component="nobr" variant="h6">
169-
{getCheckinDateForPeriod(
170-
checkins,
171-
reportDate
172-
).toLocaleDateString(navigator.language, {
158+
{getCheckinDate(member.checkin)
159+
.toLocaleDateString(navigator.language, {
173160
year: 'numeric',
174161
month: '2-digit',
175162
day: 'numeric'
176163
})}{' '}
177-
{getCheckinDateForPeriod(
178-
checkins,
179-
reportDate
180-
).getTime() > new Date().getTime() ? (
164+
{getCheckinDate(member.checkin)
165+
.getTime() > new Date().getTime() ? (
181166
<span role="img" aria-label="scheduled">
182167
📆
183168
</span>
@@ -187,38 +172,36 @@ const TeamMemberMap = ({ members, closed, planned, reportDate }) => {
187172
</span>
188173
)}
189174
</Typography>
190-
</>
175+
</Link>
191176
)}
177+
{member.checkin == null && member.checkins.length > 0 &&
178+
<span>Last Activity: {
179+
<Link style={linkStyle}
180+
to={checkinPath(member, member.checkins[0])}>
181+
{getCheckinDate(member.checkins[0]).toString()
182+
.split(' ').slice(0, 5).join(' ')}
183+
</Link>}
184+
</span>
185+
}
192186
</Box>
193187
</Typography>
194188
<Chip
195189
label={statusForPeriodByMemberScheduling(
196-
checkins,
190+
member.checkin,
197191
reportDate
198192
)}
199193
color={
200194
statusForPeriodByMemberScheduling(
201-
checkins,
195+
member.checkin,
202196
reportDate
203197
) === 'Done'
204198
? 'secondary'
205199
: 'primary'
206200
}
207201
/>
208202
</div>
209-
</AccordionSummary>
210-
<AccordionDetails id="accordion-checkin-date">
211-
{checkins.length === 0
212-
? 'No check-in activity found for this member and PDL.'
213-
: checkins.map(checkin => (
214-
<LinkSection
215-
key={checkin.id}
216-
checkin={checkin}
217-
member={member}
218-
/>
219-
))}
220-
</AccordionDetails>
221-
</Accordion>
203+
</CardContent>
204+
</Card>
222205
);
223206
})
224207
}

web-ui/src/components/reports-section/checkin-report/checkin-utils.js

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { getQuarterBeginEnd } from '../../../helpers';
1010
* @returns {Date} The date of the check-in.
1111
*/
1212
export const getCheckinDate = checkin => {
13-
if (!checkin || !checkin.checkInDate) return;
13+
if (!checkin || !checkin.checkInDate) return new Date(0);
1414
const [year, month, day, hour, minute] = checkin.checkInDate;
1515
return new Date(year, month - 1, day, hour, minute, 0);
1616
};
@@ -28,63 +28,38 @@ export const getLastCheckinDate = checkins => {
2828
}, new Date(0));
2929
};
3030

31-
/**
32-
* Get the dates the reporting period including a grace period.
33-
* @param {Date} reportDate - The date of the report.
34-
* @returns {{ startOfQuarter: Date, endOfQuarter: Date }} The start and end dates of the quarter pluas a grace period.
35-
*/
36-
export const getQuarterBeginEndWithGrace = (reportDate) => {
37-
const { startOfQuarter, endOfQuarter } = getQuarterBeginEnd(reportDate);
38-
const endOfQuarterWithGrace = new Date(endOfQuarter);
39-
endOfQuarterWithGrace.setDate(endOfQuarter.getDate() + 30);
40-
return {
41-
startOfQuarter: startOfQuarter,
42-
endOfQuarter: endOfQuarterWithGrace
43-
};
44-
};
45-
4631
/**
4732
* Get the date of the last scheduled check-in for the reporting period.
48-
* Include the grace period for the end of the quarter.
4933
* @param {Checkin[]} checkins - Check-ins for a member.
5034
* @param {Date} reportDate - The date of the report.
5135
* @returns {Date} The date of the last scheduled check-in.
5236
*/
5337
export const getCheckinDateForPeriod = (checkins, reportDate) => {
54-
const { startOfQuarter, endOfQuarter } = getQuarterBeginEndWithGrace(reportDate);
38+
const { startOfQuarter, endOfQuarter } = getQuarterBeginEnd(reportDate);
5539
const scheduled = checkins.filter(checkin => {
5640
const checkinDate = getCheckinDate(checkin);
57-
return (
58-
checkinDate >= startOfQuarter && checkinDate <= endOfQuarter // Include grace period
59-
);
41+
return (checkinDate >= startOfQuarter && checkinDate <= endOfQuarter);
6042
});
6143
return getLastCheckinDate(scheduled);
6244
};
6345

6446
/**
6547
* Determine check-in status for a member during the reporting period.
6648
* Include the grace period for the end of the quarter.
67-
* @param {Checkin[]} checkins - Check-ins for a member.
49+
* @param {Checkin} checkin - Latest Check-in for a member.
6850
* @param {Date} reportDate - The date of the report.
6951
* @returns {SchedulingStatus} The status of the check-ins.
7052
*/
7153
export const statusForPeriodByMemberScheduling = (
72-
checkins = [],
54+
checkin,
7355
reportDate
7456
) => {
75-
if (checkins.length === 0) return 'Not Scheduled';
57+
if (!checkin) return 'Not Scheduled';
7658
const { startOfQuarter, endOfQuarter } = getQuarterBeginEnd(reportDate);
77-
const endOfQuarterWithGrace = new Date(endOfQuarter);
78-
endOfQuarterWithGrace.setMonth(endOfQuarter.getMonth() + 1);
79-
const scheduled = checkins.filter(checkin => {
80-
const checkinDate = getCheckinDate(checkin);
81-
return (
82-
checkinDate >= startOfQuarter && checkinDate <= endOfQuarterWithGrace // Include grace period
83-
);
84-
});
85-
if (scheduled.length === 0) return 'Not Scheduled';
86-
const completed = scheduled.filter(checkin => checkin.completed);
87-
if (completed.length === scheduled.length) return 'Completed';
59+
const checkinDate = getCheckinDate(checkin);
60+
const scheduled = checkinDate >= startOfQuarter && checkinDate <= endOfQuarter;
61+
if (!scheduled) return 'Not Scheduled';
62+
if (checkin.completed) return 'Completed';
8863
return 'Scheduled';
8964
};
9065

0 commit comments

Comments
 (0)