Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: default stale start dates to today #1180

Merged
merged 3 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/components/app/data/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,6 @@ export const ASSIGNMENT_TYPES = {
ERRORED: 'errored',
EXPIRING: 'expiring',
};

// Start date threshold to default to today days, sets start date to today if course start date is beyond this value
brobro10000 marked this conversation as resolved.
Show resolved Hide resolved
export const START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS = 14;
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export default function useEnterpriseCourseEnrollments(queryOptions = {}) {
},
enabled: isEnabled,
});

// TODO: Talk about how we don't have access to weeksToComplete on the dashboard page.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[curious] Should we backlog a ticket as a follow-up story to make the Dashboard start dates have parity to other start dates with regard to weeksToComplete? Maybe include the ticket number in the TODO comment?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can tackle this in the follow-up PR for coverage tomorrow morning.

const allEnrollmentsByStatus = useMemo(() => transformAllEnrollmentsByStatus({
enterpriseCourseEnrollments,
requests,
Expand Down
4 changes: 1 addition & 3 deletions src/components/app/data/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,6 @@ export const canUnenrollCourseEnrollment = (courseEnrollment) => {
*/
export const transformCourseEnrollment = (rawCourseEnrollment) => {
const courseEnrollment = { ...rawCourseEnrollment };

// Return the fields expected by the component(s)
courseEnrollment.title = courseEnrollment.displayName;
courseEnrollment.microMastersTitle = courseEnrollment.micromastersTitle;
Expand Down Expand Up @@ -409,7 +408,6 @@ export const transformLearnerContentAssignment = (learnerContentAssignment, ente
courseKey = parentContentKey;
courseRunId = contentKey;
}

return {
linkToCourse: `/${enterpriseSlug}/course/${courseKey}`,
courseRunId,
Expand Down Expand Up @@ -879,7 +877,7 @@ export function transformCourseMetadataByAllocatedCourseRunAssignments({
courseRuns: courseMetadata.courseRuns.filter(
courseRun => allocatedCourseRunAssignmentKeys.includes(courseRun.key),
),
availableCourseRuns: courseMetadata.courseRuns.filter(
availableCourseRuns: courseMetadata.availableCourseRuns.filter(
courseRun => allocatedCourseRunAssignmentKeys.includes(courseRun.key),
),
};
Expand Down
7 changes: 4 additions & 3 deletions src/components/course/course-header/CourseImportantDates.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import PropTypes from 'prop-types';
import {
DATE_FORMAT,
DATETIME_FORMAT,
getNormalizedStartDate,
getSoonestEarliestPossibleExpirationData,
hasCourseStarted,
useIsCourseAssigned,
Expand Down Expand Up @@ -88,11 +89,11 @@ const CourseImportantDates = () => {
// Match soonest expiring assignment to the corresponding course start date from course metadata
let soonestExpiringAllocatedAssignmentCourseStartDate = null;
if (soonestExpiringAssignment) {
soonestExpiringAllocatedAssignmentCourseStartDate = courseMetadata.availableCourseRuns.find(
const soonestExpiringAllocatedAssignment = courseMetadata.availableCourseRuns.find(
(courseRun) => courseRun.key === soonestExpiringAssignment?.contentKey,
)?.start;
);
soonestExpiringAllocatedAssignmentCourseStartDate = getNormalizedStartDate(soonestExpiringAllocatedAssignment);
}

// Parse logic of date existence and labels
const enrollByDate = soonestExpirationDate ?? null;
const courseStartDate = soonestExpiringAllocatedAssignmentCourseStartDate
Expand Down
4 changes: 3 additions & 1 deletion src/components/course/data/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import GetSmarterLogo from '../../../assets/icons/getsmarter-header-icon.svg';
export const COURSE_PACING_MAP = {
SELF_PACED: 'self_paced',
INSTRUCTOR_PACED: 'instructor_paced',
INSTRUCTOR: 'instructor',
SELF: 'self',
brobro10000 marked this conversation as resolved.
Show resolved Hide resolved
brobro10000 marked this conversation as resolved.
Show resolved Hide resolved
};

export const SUBSIDY_DISCOUNT_TYPE_MAP = {
Expand Down Expand Up @@ -122,5 +124,5 @@ export const DISABLED_ENROLL_USER_MESSAGES = {
/* eslint-enable max-len */

export const DATE_FORMAT = 'MMM D, YYYY';
export const DATETIME_FORMAT = 'MMM D, YYYY h:mm, a';
export const DATETIME_FORMAT = 'MMM D, YYYY h:mma';
export const ZERO_PRICE = 0.00;
70 changes: 64 additions & 6 deletions src/components/course/data/utils.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,20 @@
findHighestLevelEntitlementSku,
findHighestLevelSkuByEntityModeType,
isEnrollmentUpgradeable,
START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS,
} from '../../app/data';

export function hasCourseStarted(start) {
return dayjs(start).isBefore(dayjs());
}
/**
* Determines if the course has already started. Mostly used around text formatting for tense
* Introduces 'jitter' in the form of a 30 second offset to take into account any additional
* formatting that takes place down stream related to setting values to today's date through dayjs()
brobro10000 marked this conversation as resolved.
Show resolved Hide resolved
*
* This should also help with reducing 'flaky' tests.
*
* @param start
* @returns {boolean}
*/
export const hasCourseStarted = (start) => dayjs(start).isBefore(dayjs().subtract(30, 'seconds'));
brobro10000 marked this conversation as resolved.
Show resolved Hide resolved

export function findUserEnrollmentForCourseRun({ userEnrollments, key }) {
return userEnrollments.find(
Expand Down Expand Up @@ -59,13 +68,43 @@
}

export function isCourseSelfPaced(pacingType) {
return pacingType === COURSE_PACING_MAP.SELF_PACED;
return pacingType === COURSE_PACING_MAP.SELF_PACED || pacingType === COURSE_PACING_MAP.SELF;
brobro10000 marked this conversation as resolved.
Show resolved Hide resolved
}

export function isCourseInstructorPaced(pacingType) {
return pacingType === COURSE_PACING_MAP.INSTRUCTOR_PACED;
return pacingType === COURSE_PACING_MAP.INSTRUCTOR_PACED || pacingType === COURSE_PACING_MAP.INSTRUCTOR;
brobro10000 marked this conversation as resolved.
Show resolved Hide resolved
}

const isWithinMinimumStartDateThreshold = ({ start }) => dayjs(start).isBefore(dayjs().subtract(START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS, 'days'));

/**
* If the start date of the course is before today offset by the START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS
* then return today's formatted date. Otherwise, pass-through the start date in ISO format.
*
* @param start
* @param pacingType
* @param end
* @param weeksToComplete
brobro10000 marked this conversation as resolved.
Show resolved Hide resolved
* @returns {string}
*/
export const getNormalizedStartDate = ({
start, pacingType, end, weeksToComplete,
}) => {
const todayToIso = dayjs().toISOString();
if (!start) {
return todayToIso;
}
const startDateIso = dayjs(start).toISOString();
if (isCourseSelfPaced({ pacingType })) {
if (hasTimeToComplete({ end, weeksToComplete }) || isWithinMinimumStartDateThreshold({ start })) {
// always today's date (incentives enrollment)
return todayToIso;

Check warning on line 101 in src/components/course/data/utils.jsx

View check run for this annotation

Codecov / codecov/patch

src/components/course/data/utils.jsx#L101

Added line #L101 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want test coverage on this 2 code paths?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, as discussed synchronously, the coverage can be a followup post testing on stage. Since the pipeline will be paused for both admin and learner portal, the coverage can be brought up to date with subsequent PRs tomorrow to get started on testing.

That being said there is a component that uses identical logic useCourseRunCardHeading which reuses the same logic which I will piggyback off of for testing 👍🏽

}
return startDateIso;

Check warning on line 103 in src/components/course/data/utils.jsx

View check run for this annotation

Codecov / codecov/patch

src/components/course/data/utils.jsx#L103

Added line #L103 was not covered by tests
}
return startDateIso;
};

export function getDefaultProgram(programs = []) {
if (programs.length === 0) {
return undefined;
Expand Down Expand Up @@ -932,11 +971,30 @@
if (dateFormat) {
soonestExpirationDate = dayjs(soonestExpirationDate).format(dateFormat);
}

return {
soonestExpirationDate,
soonestExpirationReason: sortedByExpirationDate[0].earliestPossibleExpiration.reason,
soonestExpiringAssignment: sortedByExpirationDate[0],
sortedExpirationAssignments: sortedByExpirationDate,
};
}

/**
* If the start date of the course is before today offset by the START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS
* then return today's formatted date. Otherwise, pass-through the start date in ISO format.
*
* For cases where a start date does not exist, just return today's date.
*
* @param start
* @param format
brobro10000 marked this conversation as resolved.
Show resolved Hide resolved
* @returns {string}
*/
export const setStaleCourseStartDates = ({ start }) => {
brobro10000 marked this conversation as resolved.
Show resolved Hide resolved
if (!start) {
return dayjs().toISOString();

Check warning on line 994 in src/components/course/data/utils.jsx

View check run for this annotation

Codecov / codecov/patch

src/components/course/data/utils.jsx#L994

Added line #L994 was not covered by tests
}
if (dayjs(start).isBefore(dayjs().subtract(START_DATE_DEFAULT_TO_TODAY_THRESHOLD_DAYS, 'days'))) {
return dayjs().toISOString();

Check warning on line 997 in src/components/course/data/utils.jsx

View check run for this annotation

Codecov / codecov/patch

src/components/course/data/utils.jsx#L997

Added line #L997 was not covered by tests
}
return dayjs(start).toISOString();

Check warning on line 999 in src/components/course/data/utils.jsx

View check run for this annotation

Codecov / codecov/patch

src/components/course/data/utils.jsx#L999

Added line #L999 was not covered by tests
};
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
useEnterpriseCustomer,
} from '../../../../app/data';
import { isCourseEnded, isDefinedAndNotNull, isTodayWithinDateThreshold } from '../../../../../utils/common';
import { getNormalizedStartDate } from '../../../../course/data';

const messages = defineMessages({
statusBadgeLabelInProgress: {
Expand Down Expand Up @@ -401,8 +402,15 @@ const BaseCourseCard = ({
};

const renderStartDate = () => {
const formattedStartDate = startDate ? dayjs(startDate).format('MMMM Do, YYYY') : null;
const isCourseStarted = dayjs(startDate) <= dayjs();
// TODO: Determine if its worth exposing weeks_to_complete in assignments to utilize this function effectively
brobro10000 marked this conversation as resolved.
Show resolved Hide resolved
const courseStartDate = getNormalizedStartDate({
start: startDate,
pacingType: pacing,
end: endDate,
weeksToComplete: null,
});
const formattedStartDate = dayjs(courseStartDate).format('MMMM Do, YYYY');
const isCourseStarted = dayjs(courseStartDate).isBefore(dayjs());
if (formattedStartDate && !isCourseStarted) {
return <span className="font-weight-light">Starts {formattedStartDate}</span>;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,6 @@ export function useCourseEnrollmentsBySection(courseEnrollmentsByStatus) {
]),
[courseEnrollmentsByStatus],
);

const completedCourseEnrollments = useMemo(
() => sortedEnrollmentsByEnrollmentDate(courseEnrollmentsByStatus.completed),
[courseEnrollmentsByStatus.completed],
Expand Down
Loading