Skip to content

Commit

Permalink
Removed the need for an accordion and display the latest checkin for …
Browse files Browse the repository at this point in the history
…the quarter (no grace period) and, for members without activity during the quarter, a link to the last known checkin.
  • Loading branch information
ocielliottc committed Jan 15, 2025
1 parent bf023eb commit 6fc4da4
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 143 deletions.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
.team-member-map-accordion {
.team-member-map-accordion-summary {
.team-member-map-row {
.team-member-map-row-summary {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 0.5rem 1.5rem;
margin-top: 0.5rem;
cursor: pointer;

.team-member-map-summmary-content {
display: grid;
Expand Down
111 changes: 47 additions & 64 deletions web-ui/src/components/reports-section/checkin-report/TeamMemberMap.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ import {
Chip,
Typography,
AccordionDetails,
Box
Box,
Card,
CardContent,
} from '@mui/material';
import { Link } from 'react-router-dom';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { getAvatarURL } from '../../../api/api.js';
import { AppContext } from '../../../context/AppContext.jsx';
import { selectCheckinsForMember } from '../../../context/selectors.js';
import {
isPastCheckin,
getCheckinDate,
getQuarterBeginEndWithGrace,
getCheckinDateForPeriod,
getLastCheckinDate,
statusForPeriodByMemberScheduling
} from './checkin-utils.js';
import LinkSection from './LinkSection.jsx';
import { getQuarterBeginEnd } from '../../../helpers';
import './TeamMemberMap.css';

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

const epoch = new Date(0);
const pdls = members.reduce((map, member) => {
if (member.pdlId && !map[member.pdlId]) {
map[member.pdlId] = members.find((m) => m.id === member.pdlId);
}
const memberMap = members.reduce((map, member) => {
map[member.id] = member;
return map;
}, {});

const linkStyle={ textDecoration: 'none' };
const checkinPath = (member, checkin) => `/checkins/${member.id}/${checkin.id}`;

const sortByName = (left, right) => {
if (left && right) {
return `${left.lastName} ${left.firstName}`.localeCompare(
Expand All @@ -50,34 +51,32 @@ const TeamMemberMap = ({ members, closed, planned, reportDate }) => {
};

const sortByPDLName = (a, b) =>
sortByName(pdls[a.reportDatePDLId], pdls[b.reportDatePDLId]);
sortByName(memberMap[a.reportDatePDLId], memberMap[b.reportDatePDLId]);

// We're going to cache the checkins into the member data structures so that
// we can properly sort by PDL when the PDL, in the past, is different than
// the current PDL.
const { startOfQuarter, endOfQuarter } = getQuarterBeginEndWithGrace(reportDate);
const { startOfQuarter, endOfQuarter } = getQuarterBeginEnd(reportDate);
members.map(member => {
const checkins = selectCheckinsForMember(
member.checkins = selectCheckinsForMember(
state,
member.id,
).filter(checkin => closed || !checkin.completed)
.filter(checkin => planned || isPastCheckin(checkin));
member.checkins = checkins;
.filter(checkin => planned || isPastCheckin(checkin))
.filter(checkin => (getCheckinDate(checkin) <= endOfQuarter));

// If there are checkins, we're going to sort them with the latest
// first. Since the member's PDL could have changed since the last
// checkin, we are going to use the PDL id of the checkin instead
// of the current PDL. They may be the same, but again they may not.
member.checkin = null;
member.reportDatePDLId = member.pdlId;
if (checkins.length > 0) {
checkins.sort((a, b) => getCheckinDate(b) - getCheckinDate(a));

for(let checkin of checkins) {
const checkinDate = getCheckinDate(checkin);
if (checkinDate >= startOfQuarter && checkinDate <= endOfQuarter) {
member.reportDatePDLId = checkin.pdlId;
break;
}
if (member.checkins.length > 0) {
member.checkins.sort((a, b) => getCheckinDate(b) - getCheckinDate(a));
const checkin = member.checkins[0];
if (getCheckinDate(checkin) >= startOfQuarter) {
member.checkin = checkin;
member.reportDatePDLId = member.checkin.pdlId;
}
}
});
Expand Down Expand Up @@ -105,18 +104,10 @@ const TeamMemberMap = ({ members, closed, planned, reportDate }) => {
</Box>
{
members.map(member => {
const pdl = pdls[member.reportDatePDLId];
const checkins = member.checkins;

const pdl = memberMap[member.reportDatePDLId];
return (
<Accordion
key={member.id}
className="team-member-map-accordion"
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
className="team-member-map-accordion-summary"
>
<Card key={member.id} className="team-member-map-row">
<CardContent className="team-member-map-row-summary">
<Avatar
sx={{ width: 54, height: 54 }}
src={getAvatarURL(member.workEmail)}
Expand Down Expand Up @@ -148,36 +139,30 @@ const TeamMemberMap = ({ members, closed, planned, reportDate }) => {
<Typography
variant="caption"
component={'time'}
dateTime={getLastCheckinDate(checkins).toISOString()}
dateTime={getCheckinDate(member.checkin).toISOString()}
sx={{ display: { xs: 'none', sm: 'none', md: 'grid' } }}
className="team-member-map-summmary-latest-activity"
>
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
{getCheckinDateForPeriod(
checkins,
reportDate
).getFullYear() === epoch.getFullYear() ? (
{member.checkin == null ? (
<Typography component="nobr" variant="h6">
No activity yet{' '}
<span role="img" aria-label="unscheduled">
🚫
</span>
</Typography>
) : (
<>
<Link style={linkStyle}
to={checkinPath(member, member.checkin)}>
<Typography component="nobr" variant="h6">
{getCheckinDateForPeriod(
checkins,
reportDate
).toLocaleDateString(navigator.language, {
{getCheckinDate(member.checkin)
.toLocaleDateString(navigator.language, {
year: 'numeric',
month: '2-digit',
day: 'numeric'
})}{' '}
{getCheckinDateForPeriod(
checkins,
reportDate
).getTime() > new Date().getTime() ? (
{getCheckinDate(member.checkin)
.getTime() > new Date().getTime() ? (
<span role="img" aria-label="scheduled">
📆
</span>
Expand All @@ -187,38 +172,36 @@ const TeamMemberMap = ({ members, closed, planned, reportDate }) => {
</span>
)}
</Typography>
</>
</Link>
)}
{member.checkin == null && member.checkins.length > 0 &&
<span>Last Activity: {
<Link style={linkStyle}
to={checkinPath(member, member.checkins[0])}>
{getCheckinDate(member.checkins[0]).toString()
.split(' ').slice(0, 5).join(' ')}
</Link>}
</span>
}
</Box>
</Typography>
<Chip
label={statusForPeriodByMemberScheduling(
checkins,
member.checkin,
reportDate
)}
color={
statusForPeriodByMemberScheduling(
checkins,
member.checkin,
reportDate
) === 'Done'
? 'secondary'
: 'primary'
}
/>
</div>
</AccordionSummary>
<AccordionDetails id="accordion-checkin-date">
{checkins.length === 0
? 'No check-in activity found for this member and PDL.'
: checkins.map(checkin => (
<LinkSection
key={checkin.id}
checkin={checkin}
member={member}
/>
))}
</AccordionDetails>
</Accordion>
</CardContent>
</Card>
);
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { getQuarterBeginEnd } from '../../../helpers';
* @returns {Date} The date of the check-in.
*/
export const getCheckinDate = checkin => {
if (!checkin || !checkin.checkInDate) return;
if (!checkin || !checkin.checkInDate) return new Date(0);
const [year, month, day, hour, minute] = checkin.checkInDate;
return new Date(year, month - 1, day, hour, minute, 0);
};
Expand All @@ -28,63 +28,38 @@ export const getLastCheckinDate = checkins => {
}, new Date(0));
};

/**
* Get the dates the reporting period including a grace period.
* @param {Date} reportDate - The date of the report.
* @returns {{ startOfQuarter: Date, endOfQuarter: Date }} The start and end dates of the quarter pluas a grace period.
*/
export const getQuarterBeginEndWithGrace = (reportDate) => {
const { startOfQuarter, endOfQuarter } = getQuarterBeginEnd(reportDate);
const endOfQuarterWithGrace = new Date(endOfQuarter);
endOfQuarterWithGrace.setDate(endOfQuarter.getDate() + 30);
return {
startOfQuarter: startOfQuarter,
endOfQuarter: endOfQuarterWithGrace
};
};

/**
* Get the date of the last scheduled check-in for the reporting period.
* Include the grace period for the end of the quarter.
* @param {Checkin[]} checkins - Check-ins for a member.
* @param {Date} reportDate - The date of the report.
* @returns {Date} The date of the last scheduled check-in.
*/
export const getCheckinDateForPeriod = (checkins, reportDate) => {
const { startOfQuarter, endOfQuarter } = getQuarterBeginEndWithGrace(reportDate);
const { startOfQuarter, endOfQuarter } = getQuarterBeginEnd(reportDate);
const scheduled = checkins.filter(checkin => {
const checkinDate = getCheckinDate(checkin);
return (
checkinDate >= startOfQuarter && checkinDate <= endOfQuarter // Include grace period
);
return (checkinDate >= startOfQuarter && checkinDate <= endOfQuarter);
});
return getLastCheckinDate(scheduled);
};

/**
* Determine check-in status for a member during the reporting period.
* Include the grace period for the end of the quarter.
* @param {Checkin[]} checkins - Check-ins for a member.
* @param {Checkin} checkin - Latest Check-in for a member.
* @param {Date} reportDate - The date of the report.
* @returns {SchedulingStatus} The status of the check-ins.
*/
export const statusForPeriodByMemberScheduling = (
checkins = [],
checkin,
reportDate
) => {
if (checkins.length === 0) return 'Not Scheduled';
if (!checkin) return 'Not Scheduled';
const { startOfQuarter, endOfQuarter } = getQuarterBeginEnd(reportDate);
const endOfQuarterWithGrace = new Date(endOfQuarter);
endOfQuarterWithGrace.setMonth(endOfQuarter.getMonth() + 1);
const scheduled = checkins.filter(checkin => {
const checkinDate = getCheckinDate(checkin);
return (
checkinDate >= startOfQuarter && checkinDate <= endOfQuarterWithGrace // Include grace period
);
});
if (scheduled.length === 0) return 'Not Scheduled';
const completed = scheduled.filter(checkin => checkin.completed);
if (completed.length === scheduled.length) return 'Completed';
const checkinDate = getCheckinDate(checkin);
const scheduled = checkinDate >= startOfQuarter && checkinDate <= endOfQuarter;
if (!scheduled) return 'Not Scheduled';
if (checkin.completed) return 'Completed';
return 'Scheduled';
};

Expand Down

0 comments on commit 6fc4da4

Please sign in to comment.