Skip to content

Commit

Permalink
Merge pull request #403 from openedx/kiram15/ENT-9168
Browse files Browse the repository at this point in the history
feat: adding customer detail modal
  • Loading branch information
kiram15 authored Aug 21, 2024
2 parents c2e53a1 + 83f2c7a commit 6b3bb62
Show file tree
Hide file tree
Showing 3 changed files with 263 additions and 7 deletions.
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();
});
});

0 comments on commit 6b3bb62

Please sign in to comment.