Skip to content

Commit

Permalink
feat: Retired workflow on the list policy screens
Browse files Browse the repository at this point in the history
  • Loading branch information
zwidekalanga committed May 8, 2024
1 parent 220f4fa commit b615886
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 40 deletions.
3 changes: 3 additions & 0 deletions src/components/learner-credit-management/BudgetCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const BudgetCard = ({ original }) => {
id,
isAssignable,
isRetired,
retiredAt,
name,
source,
start,
Expand All @@ -51,6 +52,7 @@ const BudgetCard = ({ original }) => {
enterpriseSlug={enterpriseSlug}
isAssignable={isAssignable}
isRetired={isRetired}
retiredAt={retiredAt}
/>
);
}
Expand Down Expand Up @@ -107,6 +109,7 @@ BudgetCard.propTypes = {
}),
isAssignable: PropTypes.bool,
isRetired: PropTypes.bool,
retiredAt: PropTypes.string,
enterpriseUUID: PropTypes.string.isRequired,
enterpriseSlug: PropTypes.string.isRequired,
status: PropTypes.string,
Expand Down
87 changes: 51 additions & 36 deletions src/components/learner-credit-management/SubBudgetCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const BaseSubBudgetCard = ({
isLoading,
isAssignable,
isRetired,
retiredAt,
}) => {
const { isFetching: isFetchingBudgets } = useEnterpriseBudgets({
enablePortalLearnerCreditManagementScreen,
Expand All @@ -75,15 +76,26 @@ const BaseSubBudgetCard = ({
startDateStr: start,
endDateStr: end,
isBudgetRetired: isRetired,
retiredDateStr: retiredAt,
});
const formattedDate = budgetLabel?.date ? dayjs(budgetLabel?.date).format('MMMM D, YYYY') : undefined;

const isBudgetActiveOrRetired = () => {
const { status } = budgetLabel;

return (
status === BUDGET_STATUSES.active
|| status === BUDGET_STATUSES.expiring
|| status === BUDGET_STATUSES.retired
);
};

const renderActions = (budgetId) => (
<Button
data-testid="view-budget"
as={Link}
to={`/${enterpriseSlug}/admin/${ROUTE_NAMES.learnerCredit}/${budgetId}`}
variant={[BUDGET_STATUSES.expired, BUDGET_STATUSES.retired].includes(budgetLabel.status) ? 'outline-primary' : 'primary'}
variant={[BUDGET_STATUSES.expired].includes(budgetLabel.status) ? 'outline-primary' : 'primary'}
>
View budget
</Button>
Expand All @@ -93,59 +105,62 @@ const BaseSubBudgetCard = ({
const subtitle = (
<Stack direction="horizontal" gap={2.5}>
<Badge variant={budgetLabel.badgeVariant}>{budgetLabel.status}</Badge>
{(budgetLabel.term && formattedDate) && (
{budgetLabel.term && formattedDate && (
<span data-testid="budget-date">
{budgetLabel.term} {formattedDate}
</span>
)}
</Stack>
);

const showActions = budgetLabel.status !== BUDGET_STATUSES.scheduled;
const showBottomMargin = !isBudgetActiveOrRetired;

return (
<Card.Header
title={<BackgroundFetchingWrapper>{budgetType}</BackgroundFetchingWrapper>}
subtitle={<BackgroundFetchingWrapper>{subtitle}</BackgroundFetchingWrapper>}
actions={
budgetLabel.status !== BUDGET_STATUSES.scheduled
? renderActions(budgetId)
: undefined
}
className={classNames('align-items-center', {
'mb-4.5': budgetLabel.status !== BUDGET_STATUSES.active && budgetLabel.status !== BUDGET_STATUSES.expiring,
})}
actions={showActions ? renderActions(budgetId) : undefined}
className={classNames('align-items-center', { 'mb-4.5': showBottomMargin })}
/>
);
};

const renderCardSection = () => (
<Card.Section
title={<h4>Balance</h4>}
muted
>
<Col className="d-flex justify-content-start w-md-75">
<Col xs="6" md="auto" className="mb-3 mb-md-0 ml-n4.5">
<div className="small font-weight-bold">Available</div>
<span className="small">
{isFetchingBudgets ? <Skeleton /> : formatPrice(available)}
</span>
</Col>
{isAssignable && (
const renderCardSection = () => {
if (!isBudgetActiveOrRetired) {
return null;
}

return (
<Card.Section
title={<h4>Balance</h4>}
muted
>
<Col className="d-flex justify-content-start w-md-75">
<Col xs="6" md="auto" className="mb-3 mb-md-0 ml-n4.5">
<div className="small font-weight-bold">Available</div>
<span className="small">
{isFetchingBudgets ? <Skeleton /> : formatPrice(available)}
</span>
</Col>
{isAssignable && (
<Col xs="6" md="auto" className="mb-3 mb-md-0">
<div className="small font-weight-bold">Assigned</div>
<span className="small">
{isFetchingBudgets ? <Skeleton /> : formatPrice(pending)}
</span>
</Col>
)}
<Col xs="6" md="auto" className="mb-3 mb-md-0">
<div className="small font-weight-bold">Assigned</div>
<div className="small font-weight-bold">Spent</div>
<span className="small">
{isFetchingBudgets ? <Skeleton /> : formatPrice(pending)}
{isFetchingBudgets ? <Skeleton /> : formatPrice(spent)}
</span>
</Col>
)}
<Col xs="6" md="auto" className="mb-3 mb-md-0">
<div className="small font-weight-bold">Spent</div>
<span className="small">
{isFetchingBudgets ? <Skeleton /> : formatPrice(spent)}
</span>
</Col>
</Col>
</Card.Section>
);
</Card.Section>
);
};

return (
<Card
Expand All @@ -155,8 +170,7 @@ const BaseSubBudgetCard = ({
<Card.Body>
<Stack gap={4.5}>
{renderCardHeader(displayName || 'Overview', id)}
{(budgetLabel.status === BUDGET_STATUSES.active || budgetLabel.status === BUDGET_STATUSES.expiring)
&& renderCardSection()}
{renderCardSection()}
</Stack>
</Card.Body>
</Card>
Expand All @@ -178,6 +192,7 @@ BaseSubBudgetCard.propTypes = {
displayName: PropTypes.string,
isAssignable: PropTypes.bool,
isRetired: PropTypes.bool,
retiredAt: PropTypes.string,
};

BaseSubBudgetCard.defaultProps = {
Expand Down
8 changes: 4 additions & 4 deletions src/components/learner-credit-management/data/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ export const getBudgetStatus = ({
endDateStr,
isBudgetRetired,
currentDate = new Date(),
retiredDateStr = null,
}) => {
const startDate = new Date(startDateStr);
const endDate = new Date(endDateStr);
Expand All @@ -163,10 +164,9 @@ export const getBudgetStatus = ({
if (isBudgetRetired) {
return {
status: BUDGET_STATUSES.retired,
badgeVariant: 'info',
// no term or date for retired budgets
term: null,
date: null,
badgeVariant: 'light',
term: 'Retired',
date: retiredDateStr,
};
}

Expand Down
68 changes: 68 additions & 0 deletions src/components/learner-credit-management/tests/BudgetCard.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -547,4 +547,72 @@ describe('<BudgetCard />', () => {
expect(screen.getByText('Spent')).toBeInTheDocument();
expect(screen.getByText(formatPrice(mockBudgetAggregates.spent))).toBeInTheDocument();
});

it.each([
{ isAssignableBudget: false },
{ isAssignableBudget: true },
])('displays correctly for a retired Policy (enterprise-access) (%s)', ({ isAssignableBudget }) => {
const mockBudgetAggregates = {
total: 5000,
spent: 200,
pending: isAssignableBudget ? 100 : undefined,
available: isAssignableBudget ? 4700 : 4800,
};

// Mock budget data
const mockBudget = {
id: mockBudgetUuid,
name: mockBudgetDisplayName,
start: '2022-01-01',
end: '3023-01-01',
source: BUDGET_TYPES.policy,
aggregates: {
available: mockBudgetAggregates.available,
pending: mockBudgetAggregates.pending,
spent: mockBudgetAggregates.spent,
},
isAssignable: isAssignableBudget,
enterpriseSlug,
enterpriseUUID,
isRetired: true,
retiredAt: '2022-01-01',
};

useSubsidySummaryAnalyticsApi.mockReturnValue({
isLoading: false,
subsidySummary: undefined,
});

render(<BudgetCardWrapper original={mockBudget} />);

// Assertions for budget card display
expect(screen.getByText(mockBudgetDisplayName)).toBeInTheDocument();
expect(screen.queryByText('Executive Education')).not.toBeInTheDocument();

const formattedString = `Retired ${dayjs(mockBudget.retiredAt).format('MMMM D, YYYY')}`;
const elementsWithTestId = screen.getAllByTestId('budget-date');
const firstElementWithTestId = elementsWithTestId[0];
expect(firstElementWithTestId).toHaveTextContent(formattedString);

// Verify 'View budget' CTA
const viewBudgetCTA = screen.getByText('View budget', { selector: 'a' });
expect(viewBudgetCTA).toBeInTheDocument(); // Ensure 'View budget' CTA is present
expect(viewBudgetCTA).toHaveAttribute(
'href',
`/${enterpriseSlug}/admin/learner-credit/${mockBudgetUuid}`,
);

// Assertions for aggregates display
expect(screen.getByText('Balance')).toBeInTheDocument();
expect(screen.getByText('Available')).toBeInTheDocument();
expect(screen.getByText(formatPrice(mockBudgetAggregates.available))).toBeInTheDocument();
if (isAssignableBudget) {
expect(screen.getByText('Assigned')).toBeInTheDocument();
expect(screen.getByText(formatPrice(mockBudgetAggregates.pending))).toBeInTheDocument();
} else {
expect(screen.queryByText('Assigned')).not.toBeInTheDocument();
}
expect(screen.getByText('Spent')).toBeInTheDocument();
expect(screen.getByText(formatPrice(mockBudgetAggregates.spent))).toBeInTheDocument();
});
});

0 comments on commit b615886

Please sign in to comment.