Skip to content

Commit

Permalink
Merge pull request #152 from edx/dsheraz/PROD-2506
Browse files Browse the repository at this point in the history
feat: add user summary v2
  • Loading branch information
DawoudSheraz authored Sep 30, 2021
2 parents 0570723 + 15acb37 commit aa5998d
Show file tree
Hide file tree
Showing 10 changed files with 399 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('Feature Based Enrollment Index Page', () => {
});

it('default page render with query param course id', async () => {
const apiMock = jest.spyOn(api, 'default').mockImplementationOnce(() => Promise.resolve({}));
location.search = `?course_id=${courseId}`;
wrapper = mount(<FeatureBasedEnrollmentIndexPageWrapper location={location} />);

Expand All @@ -50,6 +51,7 @@ describe('Feature Based Enrollment Index Page', () => {
expect(homePageLink.text()).toEqual('< Back to Tools');
expect(courseIdInput.prop('defaultValue')).toEqual(courseId);
expect(searchButton.text()).toEqual('Search');
apiMock.mockReset();
});

it('valid search value', async () => {
Expand Down
35 changes: 33 additions & 2 deletions src/overrides.scss
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// Reverting the container width to 1440px to keep the support tools styling consistent
// See https://github.com/edx/paragon/pull/533 for paragon breaking change

$darkCyan: #00262b;

.learner-information {
color: black;
.nav-link.active {
border-color: #fff;
color: #00262B;
border-bottom-color: #00262B;
color: $darkCyan;
border-bottom-color: $darkCyan;
border-width: medium;
}
}
Expand Down Expand Up @@ -49,6 +51,35 @@
border-radius: 3px;
}

.account-info {
font-size: large;
width: auto;
border: none;
border-radius: 0;

border-right: 1px solid lightgray;

table th {
margin: 0.5rem;
padding: 0.5rem;
border-bottom: 1px solid lightgray;
width: 30%;
}
table td {
margin: 0.5rem;
padding: 0.5rem;
border-bottom: 1px solid lightgray;
word-break: break-word;
width: 70%;
}
}

.account-actions {
border: 1px solid lightgray;
font-size: medium;
border-radius: 0.375rem;
}

.fbe-table {
@extend .sso-table;
font-size: medium;
Expand Down
45 changes: 45 additions & 0 deletions src/users/account-actions/AccountActions.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import PropTypes from 'prop-types';

import TogglePasswordStatus from './TogglePasswordStatus';
import ResetPassword from './ResetPassword';
import PasswordHistory from './PasswordHistory';

export default function AccountActions({ userData, changeHandler }) {
return (
<>
<h3 className="w-100">Account Actions</h3>
<TogglePasswordStatus
username={userData.username}
passwordStatus={userData.passwordStatus}
changeHandler={changeHandler}
/>
<ResetPassword
email={userData.email}
changeHandler={changeHandler}
/>
<PasswordHistory
passwordStatus={userData.passwordStatus}
/>
</>
);
}

AccountActions.propTypes = {
userData: PropTypes.shape({
username: PropTypes.string,
email: PropTypes.string,
passwordStatus: PropTypes.shape({
status: PropTypes.string,
passwordToggleHistory: PropTypes.arrayOf(PropTypes.shape(
{
created: PropTypes.string.isRequired,
comment: PropTypes.string.isRequired,
disabled: PropTypes.bool.isRequired,
createdBy: PropTypes.string.isRequired,
},
)),
}),
}).isRequired,
changeHandler: PropTypes.func.isRequired,
};
34 changes: 34 additions & 0 deletions src/users/account-actions/AccountActions.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { mount } from 'enzyme';
import React from 'react';

import AccountActions from './AccountActions';
import UserSummaryData from '../data/test/userSummary';

describe('Account Actions Component Tests', () => {
let wrapper;

beforeEach(() => {
wrapper = mount(<AccountActions {...UserSummaryData} />);
});

afterEach(() => {
wrapper.unmount();
});

it('Action Buttons rendered', () => {
const passwordHistoryButton = wrapper.find('button#toggle-password-history');
const toggleUserStatusButton = wrapper.find('button#toggle-password');
const passwordResetEmailButton = wrapper.find('button#reset-password');

expect(passwordHistoryButton.text()).toEqual('Show History');
expect(passwordHistoryButton.disabled).toBeFalsy();

expect(toggleUserStatusButton.text()).toEqual('Disable User');
expect(toggleUserStatusButton.disabled).toBeFalsy();

expect(passwordResetEmailButton.text()).toEqual('Reset Password');
expect(passwordResetEmailButton.disabled).toBeFalsy();

expect(wrapper.find('h3').text()).toEqual('Account Actions');
});
});
11 changes: 10 additions & 1 deletion src/users/account-actions/PasswordHistory.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,14 @@ export default function PasswordHistory({
}

PasswordHistory.propTypes = {
passwordStatus: PropTypes.string.isRequired,
passwordStatus: PropTypes.shape({
passwordToggleHistory: PropTypes.arrayOf(PropTypes.shape(
{
created: PropTypes.string.isRequired,
comment: PropTypes.string.isRequired,
disabled: PropTypes.bool.isRequired,
createdBy: PropTypes.string.isRequired,
},
)),
}).isRequired,
};
4 changes: 3 additions & 1 deletion src/users/account-actions/TogglePasswordStatus.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export default function TogglePasswordStatus({

TogglePasswordStatus.propTypes = {
username: PropTypes.string.isRequired,
passwordStatus: PropTypes.string.isRequired,
passwordStatus: PropTypes.shape({
status: PropTypes.string.isRequired,
}).isRequired,
changeHandler: PropTypes.func.isRequired,
};
2 changes: 1 addition & 1 deletion src/users/v2/LearnerInformation.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Tabs, Tab } from '@edx/paragon';
import UserSummary from '../UserSummary';
import UserSummary from './UserSummary';
import EnrollmentsV2 from '../enrollments/v2/Enrollments';
import SingleSignOnRecords from './SingleSignOnRecords';
import Licenses from '../licenses/v2/Licenses';
Expand Down
2 changes: 1 addition & 1 deletion src/users/v2/LearnerInformation.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe('Learners and Enrollments component', () => {

const accountInfo = wrapper.find('.tab-content div#learner-information-tabpane-account');
expect(accountInfo.html()).toEqual(expect.stringContaining('active'));
expect(accountInfo.find('#account-table h4').text()).toEqual('Account');
expect(accountInfo.find('#account-table h3').text()).toEqual('Account Details');
});

it('Enrollments/Entitlements Tab', () => {
Expand Down
122 changes: 122 additions & 0 deletions src/users/v2/UserSummary.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React from 'react';
import PropTypes from 'prop-types';
import { formatDate } from '../../utils';
import { getAccountActivationUrl } from '../data/urls';
import IdentityVerificationStatus from '../IdentityVerificationStatus';
import OnboardingStatus from '../OnboardingStatus';
import AccountActions from '../account-actions/AccountActions';

export default function UserSummary({
userData,
changeHandler,
}) {
return (
<section className="mb-3">
<div className="d-flex flex-row flex-wrap">
<div className="col-sm-6">

<div id="account-table" className="flex-column pr-4 m-3 card account-info">
<h3>Account Details</h3>
<table>
<tbody>

<tr>
<th>Full Name</th>
<td>{userData.name}</td>
</tr>

<tr>
<th>Username</th>
<td>{userData.username}</td>
</tr>

<tr>
<th>LMS User ID</th>
<td>{userData.id}</td>
</tr>

<tr>
<th>Email</th>
<td>{userData.email}</td>
</tr>

<tr>
<th>{!userData.isActive ? 'Activation Key/Link' : 'Active'}</th>
<td>
{
// eslint-disable-next-line no-nested-ternary
userData.isActive
? 'yes'
: (
userData.activationKey !== null
? <a href={getAccountActivationUrl(userData.activationKey)} rel="noopener noreferrer" target="_blank" className="word_break">{userData.activationKey}</a>
: 'N/A'
)
}
</td>
</tr>

<tr>
<th>Country</th>
<td>{userData.country}</td>
</tr>

<tr>
<th>Join Date/Time</th>
<td>{formatDate(userData.dateJoined)}</td>
</tr>

<tr>
<th>Last Login</th>
<td>{formatDate(userData.lastLogin)}</td>
</tr>

<tr>
<th>Password Status</th>
<td>{userData.passwordStatus.status}</td>
</tr>

</tbody>

</table>
</div>
</div>
<div className="col-sm-6">
<div className="row p-4 m-3 account-actions" id="account-actions">
<AccountActions userData={userData} changeHandler={changeHandler} />
</div>
<div className="flex-column">
<IdentityVerificationStatus username={userData.username} />
<OnboardingStatus username={userData.username} />
</div>
</div>
</div>
</section>
);
}

UserSummary.propTypes = {
userData: PropTypes.shape({
name: PropTypes.string,
username: PropTypes.string,
id: PropTypes.number,
email: PropTypes.string,
activationKey: PropTypes.string,
isActive: PropTypes.bool,
country: PropTypes.string,
dateJoined: PropTypes.string,
lastLogin: PropTypes.string,
passwordStatus: PropTypes.shape({
status: PropTypes.string,
passwordToggleHistory: PropTypes.arrayOf(PropTypes.shape(
{
created: PropTypes.string.isRequired,
comment: PropTypes.string.isRequired,
disabled: PropTypes.bool.isRequired,
createdBy: PropTypes.string.isRequired,
},
)),
}),
}).isRequired,
changeHandler: PropTypes.func.isRequired,
};
Loading

0 comments on commit aa5998d

Please sign in to comment.