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: add zero state for customer view headers #415

Merged
merged 2 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ const CustomerIntegrations = ({
integrationCount++;
}

if (!integrationCount) {
return null;
}

return (
<div>
{(integrationCount > 0) && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import { useState } from 'react';
import { useParams } from 'react-router-dom';
import { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { Form, Skeleton } from '@openedx/paragon';
import useAllAssociatedPlans from '../data/hooks/useAllAssociatedPlans';
import LearnerCreditPlanCard from './LearnerCreditPlanCard';
import SubscriptionPlanCard from './SubscriptionPlanCard';

const CustomerPlanContainer = ({ slug }) => {
const { id } = useParams();
const {
activePolicies,
activeSubscriptions,
countOfActivePlans,
countOfAllPlans,
inactivePolicies,
inactiveSubscriptions,
isLoading,
} = useAllAssociatedPlans(id);
const CustomerPlanContainer = ({
slug,
activePolicies,
activeSubscriptions,
countOfActivePlans,
countOfAllPlans,
inactivePolicies,
inactiveSubscriptions,
isLoading,
}) => {
const [showInactive, setShowInactive] = useState(false);
useEffect(() => {
if (!countOfActivePlans && countOfAllPlans) {
setShowInactive(true);

Check warning on line 20 in src/Configuration/Customers/CustomerDetailView/CustomerPlanContainer.jsx

View check run for this annotation

Codecov / codecov/patch

src/Configuration/Customers/CustomerDetailView/CustomerPlanContainer.jsx#L20

Added line #L20 was not covered by tests
}
}, []);
const renderActivePoliciesCard = activePolicies.map(policy => (
<LearnerCreditPlanCard key={policy.uuid} isActive slug={slug} policy={policy} />
));
Expand All @@ -30,23 +32,25 @@
const renderInActiveSubscriptions = inactiveSubscriptions.map(subscription => (
<SubscriptionPlanCard key={subscription.uuid} isActive={false} slug={slug} subscription={subscription} />
));

return (
<div>
{!isLoading ? (
<div>
<div className="d-flex justify-content-between">
<h2>Associated subsidy plans ({showInactive ? countOfAllPlans : countOfActivePlans})</h2>
<Form.Switch
className="ml-2.5 mt-2.5"
checked={showInactive}
disabled={countOfAllPlans === countOfActivePlans}
onChange={() => {
setShowInactive(prevState => !prevState);
}}
data-testid="show-removed-toggle"
>
Show inactive
</Form.Switch>
{(countOfAllPlans > countOfActivePlans && countOfActivePlans) ? (
<Form.Switch
className="ml-2.5 mt-2.5"
checked={showInactive}
onChange={() => {
setShowInactive(prevState => !prevState);
}}
data-testid="show-removed-toggle"
>
Show inactive
</Form.Switch>
) : null}

Check warning on line 53 in src/Configuration/Customers/CustomerDetailView/CustomerPlanContainer.jsx

View check run for this annotation

Codecov / codecov/patch

src/Configuration/Customers/CustomerDetailView/CustomerPlanContainer.jsx#L53

Added line #L53 was not covered by tests
</div>
<hr />
{renderActivePoliciesCard}
Expand All @@ -65,6 +69,35 @@

CustomerPlanContainer.propTypes = {
slug: PropTypes.string.isRequired,
activePolicies: PropTypes.arrayOf(PropTypes.shape({
uuid: PropTypes.string.isRequired,
subsidyActiveDatetime: PropTypes.string.isRequired,
subsidyExpirationDatetime: PropTypes.string.isRequired,
policyType: PropTypes.string.isRequired,
created: PropTypes.string.isRequired,
})).isRequired,
activeSubscriptions: PropTypes.arrayOf(PropTypes.shape({
uuid: PropTypes.string.isRequired,
startDate: PropTypes.string.isRequired,
expirationDate: PropTypes.string.isRequired,
created: PropTypes.string.isRequired,
})).isRequired,
countOfActivePlans: PropTypes.number.isRequired,
countOfAllPlans: PropTypes.number.isRequired,
inactivePolicies: PropTypes.arrayOf(PropTypes.shape({
uuid: PropTypes.string.isRequired,
subsidyActiveDatetime: PropTypes.string.isRequired,
subsidyExpirationDatetime: PropTypes.string.isRequired,
policyType: PropTypes.string.isRequired,
created: PropTypes.string.isRequired,
})).isRequired,
inactiveSubscriptions: PropTypes.arrayOf(PropTypes.shape({
uuid: PropTypes.string.isRequired,
startDate: PropTypes.string.isRequired,
expirationDate: PropTypes.string.isRequired,
created: PropTypes.string.isRequired,
})).isRequired,
isLoading: PropTypes.bool.isRequired,
};

export default CustomerPlanContainer;
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
import CustomerIntegrations from './CustomerIntegrations';
import EnterpriseCustomerUsersTable from './EnterpriseCustomerUsersTable';
import CustomerPlanContainer from './CustomerPlanContainer';
import useAllAssociatedPlans from '../data/hooks/useAllAssociatedPlans';

const CustomerViewContainer = () => {
const { id } = useParams();
const [enterpriseCustomer, setEnterpriseCustomer] = useState({});
const [isLoading, setIsLoading] = useState(true);
const intl = useIntl();
const associatedPlans = useAllAssociatedPlans(id);

const fetchData = useCallback(
async () => {
Expand All @@ -38,6 +40,23 @@
fetchData();
}, []);

const renderPlanContainer = () => {
if (!isLoading && !associatedPlans.isLoading && associatedPlans.countOfAllPlans) {
return (

Check warning on line 45 in src/Configuration/Customers/CustomerDetailView/CustomerViewContainer.jsx

View check run for this annotation

Codecov / codecov/patch

src/Configuration/Customers/CustomerDetailView/CustomerViewContainer.jsx#L45

Added line #L45 was not covered by tests
<Stack gap={2}>
<CustomerPlanContainer slug={enterpriseCustomer.slug} {...associatedPlans} />
</Stack>
);
}
if (!associatedPlans.isLoading && !associatedPlans.countOfAllPlans) {
return false;
}
if (associatedPlans.isLoading) {
return <Skeleton height={230} />;
}
return null;

Check warning on line 57 in src/Configuration/Customers/CustomerDetailView/CustomerViewContainer.jsx

View check run for this annotation

Codecov / codecov/patch

src/Configuration/Customers/CustomerDetailView/CustomerViewContainer.jsx#L57

Added line #L57 was not covered by tests
};

return (
<div>
{!isLoading ? (
Expand All @@ -64,11 +83,9 @@
</Stack>
</Container>
<Container className="mt-4">
<Stack gap={2}>
{!isLoading ? <CustomerPlanContainer slug={enterpriseCustomer.slug} /> : <Skeleton height={230} />}
</Stack>
{renderPlanContainer()}
</Container>
<Container className="mt-4">
<Container className="mt-4 mb-4">
<Stack gap={2}>
<CustomerIntegrations
slug={enterpriseCustomer.slug}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,52 +9,60 @@ const EnterpriseCustomerUsersTable = () => {
isLoading,
enterpriseUsersTableData,
fetchEnterpriseUsersData,
showTable,
} = useCustomerUsersTableData(id);

return (
<div>
<h2>Associated users ({enterpriseUsersTableData.itemCount})</h2>
<hr />
<DataTable
isLoading={isLoading}
isExpandable
isPaginated
manualPagination
isFilterable
manualFilters
initialState={{
pageSize: 8,
pageIndex: 0,
sortBy: [],
filters: [],
}}
defaultColumnValues={{ Filter: TextFilter }}
fetchData={fetchEnterpriseUsersData}
data={enterpriseUsersTableData.results}
itemCount={enterpriseUsersTableData.itemCount}
pageCount={enterpriseUsersTableData.pageCount}
columns={[
{
id: 'details',
Header: 'User details',
accessor: 'details',
Cell: EnterpriseCustomerUserDetail,
},
{
id: 'administrator',
Header: 'Administrator',
accessor: 'administrator',
disableFilters: true,
Cell: AdministratorCell,
},
{
id: 'learner',
Header: 'Learner',
accessor: 'learner',
disableFilters: true,
Cell: LearnerCell,
},
]}
/>
{showTable ? (
<div>
<h2>Associated users {enterpriseUsersTableData.itemCount > 0
&& <span>({enterpriseUsersTableData.itemCount})</span>}
</h2>
<hr />
<DataTable
isLoading={isLoading}
isExpandable
isPaginated
manualPagination
isFilterable
manualFilters
initialState={{
pageSize: 8,
pageIndex: 0,
sortBy: [],
filters: [],
}}
defaultColumnValues={{ Filter: TextFilter }}
fetchData={fetchEnterpriseUsersData}
data={enterpriseUsersTableData.results}
itemCount={enterpriseUsersTableData.itemCount}
pageCount={enterpriseUsersTableData.pageCount}
columns={[
{
id: 'details',
Header: 'User details',
accessor: 'details',
Cell: EnterpriseCustomerUserDetail,
},
{
id: 'administrator',
Header: 'Administrator',
accessor: 'administrator',
disableFilters: true,
Cell: AdministratorCell,
},
{
id: 'learner',
Header: 'Learner',
accessor: 'learner',
disableFilters: true,
Cell: LearnerCell,
},
]}
/>
</div>
) : null}
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import userEvent from '@testing-library/user-event';
import { getConfig } from '@edx/frontend-platform';
import { IntlProvider } from '@edx/frontend-platform/i18n';

import useAllAssociatedPlans from '../../data/hooks/useAllAssociatedPlans';
import { formatDate } from '../../data/utils';
import CustomerPlanContainer from '../CustomerPlanContainer';

Expand All @@ -19,11 +18,6 @@ jest.mock('@edx/frontend-platform', () => ({
getConfig: jest.fn(),
}));

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useParams: () => ({ id: 'test-uuid' }),
}));

const CUSTOMER_SLUG = 'test-slug';

describe('CustomerPlanContainer', () => {
Expand All @@ -44,7 +38,7 @@ describe('CustomerPlanContainer', () => {
ENTERPRISE_ACCESS_BASE_URL: 'http:www.enterprise-access.com',
LICENSE_MANAGER_URL: 'http:www.license-manager.com',
}));
useAllAssociatedPlans.mockReturnValue({
const mockProps = {
isLoading: false,
activePolicies: [{
subsidyActiveDatetime: '2024-08-23T20:02:57.651943Z',
Expand All @@ -71,10 +65,10 @@ describe('CustomerPlanContainer', () => {
policyType: 'learnerCredit',
isSubsidyActive: false,
}],
});
};
render(
<IntlProvider locale="en">
<CustomerPlanContainer slug={CUSTOMER_SLUG} />
<CustomerPlanContainer slug={CUSTOMER_SLUG} {...mockProps} />
</IntlProvider>,
);
const djangoLinks = screen.getAllByRole('link', { name: 'Open in Django' });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,20 @@ describe('CustomerViewIntegrations', () => {
expect(screen.getByText('API')).toBeInTheDocument();
});
});
it('does not render cards', async () => {
formatDate.mockReturnValue('September 15, 2024');
render(
<IntlProvider locale="en">
<CustomerIntegrations
slug="marcel-the-shell"
activeIntegrations={mockIntegratedChannelData}
activeSSO={[]}
apiCredentialsEnabled={false}
/>
</IntlProvider>,
);
await waitFor(() => {
expect(screen.queryByText('Associated integrations (0)')).not.toBeInTheDocument();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const mockData = {
],
},
fetchEnterpriseUsersData: mockFetchEnterpriseUsersData,
showTable: true,
};

jest.mock('../../data/hooks/useCustomerUsersTableData');
Expand Down Expand Up @@ -80,4 +81,24 @@ describe('EnterpriseCustomerUsersTable', () => {
});
});
});

it('does not render user table section', () => {
const emptyResults = {
isLoading: false,
enterpriseUsersTableData: {
itemCount: 0,
pageCount: 1,
results: [],
},
fetchEnterpriseUsersData: mockFetchEnterpriseUsersData,
};
useCustomerUsersTableData.mockReturnValue(emptyResults);
render(
<IntlProvider locale="en">
<EnterpriseCustomerUsersTable />
</IntlProvider>,
);
expect(screen.queryByText('Search user details')).not.toBeInTheDocument();
expect(screen.queryByText('Associated users (0)')).not.toBeInTheDocument();
});
});
Loading