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: adding customer detail modal #403

Merged
merged 2 commits into from
Aug 21, 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
16 changes: 9 additions & 7 deletions src/Configuration/Customers/CustomerDetailView/CustomerCard.jsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import PropTypes from 'prop-types';
import {
ActionRow,
Button,
Card,
Icon,
Hyperlink,
Toast,
ActionRow, Button, Card, Icon, Hyperlink, Toast, useToggle,
} from '@openedx/paragon';
import { Launch, ContentCopy } from '@openedx/paragon/icons';
import { getConfig } from '@edx/frontend-platform';
import { formatDate, useCopyToClipboard } from '../data/utils';
import DJANGO_ADMIN_BASE_URL from '../data/constants';
import CustomerDetailModal from './CustomerDetailModal';

const CustomerCard = ({ enterpriseCustomer }) => {
const { ADMIN_PORTAL_BASE_URL } = getConfig();
const { showToast, copyToClipboard, setShowToast } = useCopyToClipboard();
const [isDetailsOpen, openDetails, closeDetails] = useToggle(false);

return (
<div>
<CustomerDetailModal
customer={enterpriseCustomer}
isOpen={isDetailsOpen}
close={closeDetails}
/>
<Card variant="dark" className="mb-0">
<Card.Section
actions={(
<ActionRow>
<Button>View Details</Button>
<Button onClick={openDetails}>View Details</Button>
<Button
className="text-dark-500"
as="a"
Expand Down
203 changes: 203 additions & 0 deletions src/Configuration/Customers/CustomerDetailView/CustomerDetailModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import PropTypes from 'prop-types';
import {
ActionRow, Badge, Button, Icon, ModalDialog,
} from '@openedx/paragon';
import { Check, Launch } from '@openedx/paragon/icons';
import { getConfig } from '@edx/frontend-platform';
import classNames from 'classnames';

const CustomerDetailModal = ({ customer, isOpen, close }) => {
const { DJANGO_ADMIN_LMS_BASE_URL } = getConfig();
const DATA_SHARING_CONSENT = {
at_enrollment: 'At enrollment',
externally_managed: 'Externally managed',
};
return (
<ModalDialog
title="Enterprise customer details"
isOpen={isOpen}
onClose={close}
size="lg"
hasCloseButton
isFullscreenOnMobile
>
<ModalDialog.Header>
<h1>
{customer.name}
</h1>
</ModalDialog.Header>

<ModalDialog.Body>
<Badge variant="light" style={{ width: 'fit-content' }}>View only</Badge>
<h3 className="mb-3">Enterprise info</h3>
<h4 className="mb-0">Name</h4>
<p>{customer.name || '--'}</p>
<p className="d-flex">
<Icon
className={classNames(
'mr-3',
{ 'text-white': !customer.active },
)}
src={Check}
/>
Active Admin Portal
</p>
<h4 className="mb-0">Slug</h4>
<p>/{customer.slug || '--'}/</p>
<h4 className="mb-0">Auth org id</h4>
<p>{customer.authOrgId || '--'}</p>
<h4 className="mb-0">Country</h4>
<p>{customer.country || '--'}</p>
<h3 className="mt-4 mb-3">Data sharing consent</h3>
<p className="d-flex">
<Icon
className={classNames(
'mr-3',
{ 'text-white': !customer.enableDataSharingConsent },
)}
src={Check}
/>
Activate data sharing consent prompt
</p>
<h4 className="mb-0">Data sharing consent enforcement</h4>
<p>{DATA_SHARING_CONSENT[customer.enforceDataSharingConsent]}</p>
<h3 className="mt-4 mb-3">Email and language</h3>
<h4 className="mb-0">Customer admin contact email</h4>
<p>{customer.contactEmail || '--'}</p>
<h4 className="mb-0">Customer reply-to email</h4>
<p>{customer.replyTo || '--'}</p>
<h4 className="mb-0">Automated email sender alias</h4>
<p>{customer.senderAlias || '--'}</p>
<h4 className="mb-0">Learner default language</h4>
<p>{customer.defaultLanguage || '--'}</p>
<p className="d-flex">
<Icon
className={classNames(
'mr-3',
{ 'text-white': !customer.hideLaborMarketData },
)}
src={Check}
/>
Hide labor market data
</p>
<h3 className="mt-4 mb-3">Integration and learner platform settings</h3>
<p className="d-flex">
<Icon
className={classNames(
'mr-3',
{ 'text-white': !customer.enablePortalReportingConfigScreen },
)}
src={Check}
/>
Display learning platform configuration screen
</p>
<p className="d-flex">
<Icon
className={classNames(
'mr-3',
{ 'text-white': !customer.enablePortalSamlConfigurationScreen },
)}
src={Check}
/>
Display SSO configuration screen
</p>
<p className="d-flex">
<Icon
className={classNames(
'mr-3',
{ 'text-white': !customer.enableSlugLogin },
)}
src={Check}
/>
Allow slug login for SSO
</p>
<p className="d-flex">
<Icon
className={classNames(
'mr-3',
{ 'text-white': !customer.replaceSensitiveSsoUsername },
)}
src={Check}
/>
Replace sensitive SSO username
</p>
<p className="d-flex">
<Icon
className={classNames(
'mr-3',
{ 'text-white': !customer.hideCourseOriginalPrice },
)}
src={Check}
/>
Hide course price on learning platform
</p>
<p className="d-flex">
<Icon
className={classNames(
'mr-3',
{ 'text-white': !customer.hideCourseOriginalPrice },
)}
src={Check}
/>
Hide course price on learning platform
</p>
<p className="d-flex">
<Icon
className={classNames(
'mr-3',
{ 'text-white': !customer.enableGenerationOfApiCredentials },
)}
src={Check}
/>
Allow generation of API credentials
</p>
</ModalDialog.Body>

<ModalDialog.Footer>
<ActionRow>
<ModalDialog.CloseButton variant="tertiary" onClick={close}>
Close
</ModalDialog.CloseButton>
<Button
as="a"
href={`${DJANGO_ADMIN_LMS_BASE_URL}/admin/enterprise/enterprisecustomer/${customer.uuid}/change`}
target="_blank"
rel="noopener noreferrer"
iconAfter={Launch}
>
Open in Django
</Button>
</ActionRow>
</ModalDialog.Footer>
</ModalDialog>
);
};

CustomerDetailModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
close: PropTypes.func.isRequired,
customer: PropTypes.shape({
active: PropTypes.bool,
authOrgId: PropTypes.string,
contactEmail: PropTypes.string,
country: PropTypes.string,
defaultLanguage: PropTypes.string,
enableDataSharingConsent: PropTypes.bool,
enableGenerationOfApiCredentials: PropTypes.bool,
enablePortalReportingConfigScreen: PropTypes.bool,
enablePortalSamlConfigurationScreen: PropTypes.bool,
enableSlugLogin: PropTypes.bool,
enforceDataSharingConsent: PropTypes.string,
hideCourseOriginalPrice: PropTypes.bool,
hideLaborMarketData: PropTypes.bool,
modified: PropTypes.string,
name: PropTypes.string,
replaceSensitiveSsoUsername: PropTypes.bool,
replyTo: PropTypes.string,
senderAlias: PropTypes.string,
slug: PropTypes.string,
uuid: PropTypes.string,
}).isRequired,
};

export default CustomerDetailModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* eslint-disable react/prop-types */
import { screen, render } from '@testing-library/react';
import '@testing-library/jest-dom';

import { IntlProvider } from '@edx/frontend-platform/i18n';
import CustomerDetailModal from '../CustomerDetailModal';

jest.mock('../../data/utils', () => ({
getEnterpriseCustomer: jest.fn(),
}));

const mockData = {
active: true,
authOrgId: null,
country: 'US',
defaultLanguage: 'English',
enableDataSharingConsent: true,
enableGenerationOfApiCredentials: true,
enableSlugLogin: true,
enforceDataSharingConsent: 'at_enrollment',
hideCourseOriginalPrice: true,
name: 'Test Customer Name',
replaceSensitiveSsoUsername: true,
replyTo: null,
senderAlias: null,
slug: 'customer-6',
uuid: 'test-id',
};

describe('CustomerDetailModal', () => {
it('renders customer detail modal', () => {
const props = {
customer: mockData,
isOpen: true,
close: jest.fn(() => {}),
};
render(
<IntlProvider locale="en">
<CustomerDetailModal {...props} />
</IntlProvider>,
);

expect(screen.getAllByText('Test Customer Name')).toHaveLength(2);
expect(screen.getByText('View only')).toBeInTheDocument();
// null values will show as dashes
expect(screen.getAllByText('--')).toHaveLength(4);
expect(screen.getByText('/customer-6/')).toBeInTheDocument();
expect(screen.getByText('At enrollment')).toBeInTheDocument();
expect(screen.getByText('English')).toBeInTheDocument();
});
});