Skip to content

Commit

Permalink
Display warning when plan is about to expire
Browse files Browse the repository at this point in the history
  • Loading branch information
zwidekalanga committed Feb 27, 2024
1 parent c0dd344 commit 31c08d6
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 21 deletions.
1 change: 1 addition & 0 deletions src/components/EnterpriseApp/data/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const ROUTE_NAMES = {
export const BUDGET_STATUSES = {
active: 'Active',
expired: 'Expired',
expiring: 'Expiring',
scheduled: 'Scheduled',
retired: 'Retired',
};
Expand Down
40 changes: 19 additions & 21 deletions src/components/learner-credit-management/BudgetDetailPageHeader.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import {
Stack, Card, Badge, Skeleton,
Stack, Badge, Skeleton,
} from '@edx/paragon';

import { connect } from 'react-redux';
Expand All @@ -12,11 +12,12 @@ import {
useEnterpriseOffer,
formatDate,
useSubsidySummaryAnalyticsApi,
isPlanApproachingExpiry,
} from './data';

import BudgetDetailPageBreadcrumbs from './BudgetDetailPageBreadcrumbs';
import BudgetDetailPageOverviewAvailability from './BudgetDetailPageOverviewAvailability';
import BudgetDetailPageOverviewUtilization from './BudgetDetailPageOverviewUtilization';
import BudgetOverview from './BudgetOverview';
import LearnerCreditExpiryCard from './LearnerCreditExpiryCard';

Check failure on line 20 in src/components/learner-credit-management/BudgetDetailPageHeader.jsx

View workflow job for this annotation

GitHub Actions / tests

Unable to resolve path to module './LearnerCreditExpiryCard'

Check failure on line 20 in src/components/learner-credit-management/BudgetDetailPageHeader.jsx

View workflow job for this annotation

GitHub Actions / tests

Missing file extension for "./LearnerCreditExpiryCard"
import { BUDGET_TYPES } from '../EnterpriseApp/data/constants';

const BudgetStatusBadge = ({
Expand Down Expand Up @@ -61,7 +62,9 @@ const BudgetDetailPageHeader = ({ enterpriseUUID, enterpriseFeatures }) => {
enterpriseOfferMetadata,
isTopDownAssignmentEnabled: enterpriseFeatures.topDownAssignmentRealTimeLcm,
});


Check failure on line 65 in src/components/learner-credit-management/BudgetDetailPageHeader.jsx

View workflow job for this annotation

GitHub Actions / tests

Trailing spaces not allowed
const expiry = isPlanApproachingExpiry(date);

Check failure on line 67 in src/components/learner-credit-management/BudgetDetailPageHeader.jsx

View workflow job for this annotation

GitHub Actions / tests

Trailing spaces not allowed
if (!subsidyAccessPolicy && (isLoadingSubsidySummary || isLoadingEnterpriseOffer)) {
return (
<div data-testid="budget-detail-skeleton">
Expand All @@ -74,23 +77,18 @@ const BudgetDetailPageHeader = ({ enterpriseUUID, enterpriseFeatures }) => {
return (
<Stack gap={2}>
<BudgetDetailPageBreadcrumbs budgetDisplayName={budgetDisplayName} />
<Card className="budget-overview-card">
<Card.Section>
<h2>{budgetDisplayName}</h2>
<BudgetStatusBadge badgeVariant={badgeVariant} status={status} term={term} date={date} />
<BudgetDetailPageOverviewAvailability
budgetId={budgetId}
budgetTotalSummary={budgetTotalSummary}
isAssignable={isAssignable}
/>
<BudgetDetailPageOverviewUtilization
budgetId={budgetId}
budgetTotalSummary={budgetTotalSummary}
budgetAggregates={budgetAggregates}
isAssignable={isAssignable}
/>
</Card.Section>
</Card>
{expiry.isExpiring && <LearnerCreditExpiryCard />}
<BudgetOverview
budgetDisplayName={budgetDisplayName}
badgeVariant={badgeVariant}
status={status}
term={term}
date={date}
budgetId={budgetId}
budgetTotalSummary={budgetTotalSummary}
budgetAggregates={budgetAggregates}
isAssignable={isAssignable}
/>
</Stack>
);
};
Expand Down
63 changes: 63 additions & 0 deletions src/components/learner-credit-management/BudgetOverview.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// BudgetOverview.js
import React from 'react';
import PropTypes from 'prop-types';
import { Badge, Card, Stack } from '@edx/paragon';

import BudgetDetailPageOverviewAvailability from './BudgetDetailPageOverviewAvailability';
import BudgetDetailPageOverviewUtilization from './BudgetDetailPageOverviewUtilization';
import { formatDate } from './data';

const BudgetStatusBadge = ({
badgeVariant, status, term, date,

Check failure on line 11 in src/components/learner-credit-management/BudgetOverview.jsx

View workflow job for this annotation

GitHub Actions / tests

'badgeVariant' is missing in props validation

Check failure on line 11 in src/components/learner-credit-management/BudgetOverview.jsx

View workflow job for this annotation

GitHub Actions / tests

'status' is missing in props validation

Check failure on line 11 in src/components/learner-credit-management/BudgetOverview.jsx

View workflow job for this annotation

GitHub Actions / tests

'term' is missing in props validation

Check failure on line 11 in src/components/learner-credit-management/BudgetOverview.jsx

View workflow job for this annotation

GitHub Actions / tests

'date' is missing in props validation
}) => (
<Stack direction="horizontal" gap={2}>
<Badge variant={badgeVariant}>{status}</Badge>
{(term && date) && (
<span className="small">{term} {formatDate(date)}</span>
)}
</Stack>
);

const BudgetOverview = ({
budgetDisplayName,
badgeVariant,
status,
term,
date,
budgetId,
budgetTotalSummary,
budgetAggregates,
isAssignable,
}) => (
<Card className="budget-overview-card">
<Card.Section>
<h2>{budgetDisplayName}</h2>
<BudgetStatusBadge badgeVariant={badgeVariant} status={status} term={term} date={date} />
<BudgetDetailPageOverviewAvailability
budgetId={budgetId}
budgetTotalSummary={budgetTotalSummary}
isAssignable={isAssignable}
/>
<BudgetDetailPageOverviewUtilization
budgetId={budgetId}
budgetTotalSummary={budgetTotalSummary}
budgetAggregates={budgetAggregates}
isAssignable={isAssignable}
/>
</Card.Section>
</Card>
);

BudgetOverview.propTypes = {
budgetDisplayName: PropTypes.string.isRequired,
badgeVariant: PropTypes.string.isRequired,
status: PropTypes.string.isRequired,
term: PropTypes.string.isRequired,
date: PropTypes.string.isRequired,
budgetId: PropTypes.string.isRequired,
budgetTotalSummary: PropTypes.object.isRequired,

Check failure on line 58 in src/components/learner-credit-management/BudgetOverview.jsx

View workflow job for this annotation

GitHub Actions / tests

Prop type "object" is forbidden
budgetAggregates: PropTypes.object.isRequired,

Check failure on line 59 in src/components/learner-credit-management/BudgetOverview.jsx

View workflow job for this annotation

GitHub Actions / tests

Prop type "object" is forbidden
isAssignable: PropTypes.bool.isRequired,
};

export default BudgetOverview;
63 changes: 63 additions & 0 deletions src/components/learner-credit-management/data/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,60 @@ export const getProgressBarVariant = ({ percentUtilized, remainingFunds }) => {
// Utility function to check if the ID is a UUID
export const isUUID = (id) => /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(id);

// Utility function to check if plan is about to expire
export const isPlanApproachingExpiry = (endDateStr, currentDate = new Date()) => {
const endDate = new Date(endDateStr);
const currentDateObj = new Date(currentDate);
const oneDay = 24 * 60 * 60 * 1000;
const oneHour = 60 * 60 * 1000;
const oneMinute = 60 * 1000;

const timeUntilExpiry = endDate - currentDateObj;
const daysUntilExpiry = Math.floor(timeUntilExpiry / oneDay);
const hoursUntilExpiry = Math.floor((timeUntilExpiry % oneDay) / oneHour);
const minutesUntilExpiry = Math.floor((timeUntilExpiry % oneHour) / oneMinute);

if (daysUntilExpiry > 120) {
return {
isExpiring: false,
warning: {},
};
}

let warning = {};

if (daysUntilExpiry <= 120 && daysUntilExpiry > 60) {
warning = {
title: 'Your Learner Credit plan is ending soon',
message: `Your Learner Credit plan expires ${endDate.toLocaleDateString()}. Contact your representative today to renew your plan and keep people learning.`,
dismissible: false,
};
} else if (daysUntilExpiry <= 60 && daysUntilExpiry > 30) {
warning = {
title: `Your Learner Credit plan expires ${endDate.toLocaleDateString()}`,
message: 'When your Learner Credit plan expires, you will no longer have access to administrative functions and the remaining balance of your budget(s) will be unusable. Contact a representative today to renew your plan.',
dismissible: true,
};
} else if (daysUntilExpiry <= 30 && daysUntilExpiry > 10) {
warning = {
title: 'Your Learner Credit plan expires in less than 30 days',
message: 'When your plan expires you will lose access to administrative functions and the remaining balance of your plan’s budget(s) will be unusable. Contact your representative today to renew your plan.',
dismissible: false,
};
} else if (daysUntilExpiry <= 10) {
warning = {
title: `Reminder: Your Learner Credit plan expires ${endDate.toLocaleDateString()}`,
message: `Your Learner Credit plan expires in ${daysUntilExpiry} days, ${hoursUntilExpiry} hours, and ${minutesUntilExpiry} minutes. Contact your representative today to renew your plan.`,
dismissible: false,
};
}

return {
isExpiring: true,
warning,
};
};

// Utility function to check the budget status
export const getBudgetStatus = ({
startDateStr,
Expand Down Expand Up @@ -167,6 +221,15 @@ export const getBudgetStatus = ({
};
}

if (isPlanApproachingExpiry(endDateStr).isExpiring) {
return {
status: BUDGET_STATUSES.expiring,
badgeVariant: 'warning',
term: 'Expiring',
date: endDateStr,
};
}

// Check if budget is current (today's date between start/end dates)
if (currentDate >= startDate && currentDate <= endDate) {
return {
Expand Down

0 comments on commit 31c08d6

Please sign in to comment.