Compare commits

...

59 Commits

Author SHA1 Message Date
ayesha waris
6acce16bff Merge branch 'master' into eemaanamir/INF-1062 2024-02-06 19:18:47 +05:00
Ahtisham Shahid
0ee0f41f3e Merge pull request #988 from openedx/ahtisham/INF-1242
feat: added notification title for contentReported
2024-02-06 14:13:06 +05:00
renovate[bot]
35b66ae38b fix(deps): update react-router monorepo to v6.22.0 2024-02-05 13:10:30 +00:00
renovate[bot]
361e14c980 fix(deps): update dependency @edx/frontend-platform to v7.1.0 2024-02-05 07:54:43 +00:00
Omar Al-Ithawi
8e967fa3bf feat: tutor-mfe compatiblilty for atlas pull (#987)
- install atlas
 - remove `--filter` to pull all languages by default
 - use ATLAS_OPTIONS to allow custom `--filter`
 - include frontend-platform in `atlas pull`

Refs: FC-0012 OEP-58
2024-02-02 09:33:00 -05:00
Ahtisham Shahid
5c6ddd2888 feat: added notification title for contentReported 2024-01-31 13:18:10 +05:00
Brian Smith
7ef1b5b92d chore(deps): update paragon and frontend-build to openedx scope (#982) 2024-01-29 10:52:37 -05:00
eemaanamir
09c02cb367 perf: updated onChannelToggle to improve performance 2024-01-24 16:35:19 +05:00
eemaanamir
36f7c939e5 refactor: further simplified onChannelToggle 2024-01-24 15:53:09 +05:00
eemaanamir
aeb8109d94 refactor: onChannelToggle updated 2024-01-24 13:26:30 +05:00
eemaanamir
a6699f94c9 refactor: onChannelToggle updated for readability 2024-01-23 19:47:59 +05:00
edX requirements bot
672d39f99c chore: update browserslist DB (#978)
Co-authored-by: abdullahwaheed <abdullahwaheed@users.noreply.github.com>
2024-01-23 15:20:54 +05:00
eemaanamir
a3497adb6d chore: refactoring the code for readability according to ESLint 2024-01-23 15:07:40 +05:00
edx-transifex-bot
08a0d8e30b chore(i18n): update translations (#977)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2024-01-23 15:00:21 +05:00
eemaanamir
d3e8b36e69 feat: make notification channel headings clickable in notification preferences 2024-01-22 15:01:11 +05:00
renovate[bot]
208c7a1ada fix(deps): update react-router monorepo to v6.21.3 2024-01-22 07:57:21 +00:00
renovate[bot]
afe2a754bd fix(deps): update dependency core-js to v3.35.1 2024-01-22 07:56:21 +00:00
Attiya Ishaque
594ffe4aa9 Merge pull request #971 from openedx/attiya/VAN-1790
feat: add  work experience field
2024-01-19 18:44:25 +05:00
attiyaishaque
8ecdedcdc8 feat: add work experience field 2024-01-19 16:03:17 +05:00
renovate[bot]
11e144dec0 fix(deps): update react-router monorepo to v6.21.2 2024-01-15 07:21:47 +00:00
renovate[bot]
c71e586e64 fix(deps): update dependency redux-saga to v1.3.0 2024-01-15 07:21:36 +00:00
renovate[bot]
d0d2aeed71 fix(deps): update dependency core-js to v3.35.0 2024-01-15 07:20:55 +00:00
renovate[bot]
737833cdeb fix(deps): update dependency classnames to v2.5.1 2024-01-15 05:13:19 +00:00
Bilal Qamar
c00ebc9f64 chore: bumped frontend-platform to v6 (#950)
* chore: bumped frontend-platform to v6

* refactor: updated snapshots

* refactor: updated package-lock

* refactor: bumped frontend-platform version

* refactor: updated package-lock and snapshots

* refactor: updated package-lock
2024-01-11 18:36:13 +05:00
dependabot[bot]
41ee7e9538 chore(deps): bump follow-redirects from 1.15.2 to 1.15.4 (#972)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-11 14:01:51 +05:00
renovate[bot]
b7181d5643 fix(deps): update dependency @edx/frontend-component-footer to v12.7.1 2024-01-08 14:39:38 +00:00
renovate[bot]
50e0235a6a fix(deps): update dependency @edx/frontend-component-header to v4.11.1 2024-01-08 06:21:27 +00:00
Dmytro
5166546048 fix: Fixed the display of the selection of available time zones (#896) 2024-01-01 23:07:56 +05:00
renovate[bot]
49d3acd1a1 fix(deps): update dependency @edx/frontend-component-header to v4.11.0 2024-01-01 09:06:06 +00:00
renovate[bot]
cdf799d515 fix(deps): update dependency @edx/frontend-component-footer to v12.7.0 2024-01-01 07:29:31 +00:00
Syed Ali Abbas Zaidi
ed26467f0d feat: migrate enzyme with RTL (#945) 2023-12-26 13:22:06 +05:00
edx-transifex-bot
fea3991915 chore(i18n): update translations (#962)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2023-12-26 11:37:24 +05:00
renovate[bot]
30ca5f577e fix(deps): update dependency core-js to v3.34.0 2023-12-25 06:51:49 +00:00
renovate[bot]
4047c3c923 fix(deps): update dependency @edx/frontend-component-footer to v12.6.2 2023-12-25 06:51:26 +00:00
renovate[bot]
3d32c8624d fix(deps): update dependency regenerator-runtime to v0.14.1 2023-12-18 07:00:42 +00:00
renovate[bot]
0c70e31655 chore(deps): update dependency @edx/frontend-build to v13.0.14 2023-12-18 07:00:13 +00:00
renovate[bot]
10e1a451f7 fix(deps): update dependency @edx/frontend-component-footer to v12.6.1 2023-12-11 07:34:44 +00:00
renovate[bot]
7f2d3700d7 chore(deps): update dependency @edx/frontend-build to v13.0.12 2023-12-11 07:34:26 +00:00
dependabot[bot]
eb88d9b46d chore(deps-dev): bump @adobe/css-tools from 4.2.0 to 4.3.2 (#951)
Bumps [@adobe/css-tools](https://github.com/adobe/css-tools) from 4.2.0 to 4.3.2.
- [Changelog](https://github.com/adobe/css-tools/blob/main/History.md)
- [Commits](https://github.com/adobe/css-tools/commits)

---
updated-dependencies:
- dependency-name: "@adobe/css-tools"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-08 15:00:28 +05:00
sundasnoreen12
26dbac709f Merge pull request #954 from openedx/sundas/INF-1036
fix: disable app level toggle during api call
2023-12-07 14:18:39 +05:00
sundasnoreen12
d6592a77f9 fix: disable app level toggle during api call 2023-12-06 15:23:06 +05:00
renovate[bot]
91d0feaed9 chore(deps): update actions/checkout action to v4 (#953)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-06 13:15:40 +05:00
renovate[bot]
237599c830 fix(deps): update react-router monorepo to v6.20.1 2023-12-04 07:10:49 +00:00
sundasnoreen12
56d0ad3db9 Merge pull request #947 from openedx/sundas/INF-1164
fix: app level toggle is now disabled during api call
2023-11-30 13:20:52 +05:00
sundasnoreen12
c0b504763e fix: app level toggle is now disabled during api call 2023-11-29 22:57:04 +05:00
renovate[bot]
4aa44b89a3 fix(deps): update react-router monorepo to v6.20.0 2023-11-29 13:36:02 +00:00
renovate[bot]
be661b8a27 fix(deps): update dependency @edx/frontend-component-header to v4.10.1 2023-11-27 10:35:20 +00:00
renovate[bot]
3c75052d8b fix(deps): update dependency @edx/frontend-component-footer to v12.6.0 2023-11-27 07:27:16 +00:00
renovate[bot]
229a674469 chore(deps): update dependency @edx/frontend-build to v13.0.8 2023-11-20 11:05:36 +00:00
edx-transifex-bot
2c75c7f790 chore(i18n): update translations (#938)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2023-11-20 13:08:36 +05:00
renovate[bot]
d595986d0e fix(deps): update dependency core-js to v3.33.3 2023-11-20 08:06:20 +00:00
Mashal Malik
43d8784014 refactor: updated README file to reflect template changes (#893)
* refactor: updated README file to reflect template changes

* refactor: updated README file to reflect template changes

* refactor: updated README file to reflect template changes

* refactor: updated README file to reflect template changes
2023-11-14 12:57:40 +05:00
renovate[bot]
f15b71d20c chore(deps): update dependency @edx/frontend-build to v13.0.5 2023-11-13 07:23:24 +00:00
renovate[bot]
aae79c45bb fix(deps): update dependency core-js to v3.33.2 2023-11-13 07:21:25 +00:00
dependabot[bot]
5233c6aa59 chore(deps): bump @babel/traverse from 7.22.5 to 7.23.2 (#914)
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.5 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-08 17:56:54 +05:00
renovate[bot]
d59c9e38bd chore(deps): update dependency @edx/frontend-build to v13.0.4 2023-11-06 07:42:20 +00:00
renovate[bot]
eb35897bf7 fix(deps): update dependency @edx/frontend-component-footer to v12.5.1 2023-11-06 07:41:03 +00:00
Stanislav
c28b8c6840 fix: Add ID attribute to the main content (#880)
Co-authored-by: Stanislav Lunyachek <lunyachek@MacBook-Pro-M1.local>
2023-11-03 16:17:14 +05:00
Bilal Qamar
1698720aad Revert "refactor: bumped frontend-platform version to v6" (#932)
* Revert "refactor: bumped frontend-platform version to v6 (#910)"

This reverts commit 57a2c7bcb6.

* refactor: updated package-lock
2023-11-01 16:31:22 +05:00
80 changed files with 5551 additions and 8376 deletions

View File

@@ -1,4 +1,4 @@
// eslint-disable-next-line import/no-extraneous-dependencies
const { createConfig } = require('@edx/frontend-build');
const { createConfig } = require('@openedx/frontend-build');
module.exports = createConfig('eslint');

View File

@@ -14,7 +14,7 @@ jobs:
- lint
- test
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup Nodejs Env
run: echo "NODE_VER=`cat .nvmrc`" >> $GITHUB_ENV
- uses: actions/setup-node@v2

View File

@@ -61,13 +61,14 @@ pull_translations:
rm -rf src/i18n/messages
mkdir src/i18n/messages
cd src/i18n/messages \
&& atlas pull --filter=$(transifex_langs) \
&& atlas pull $(ATLAS_OPTIONS) \
translations/frontend-platform/src/i18n/messages:frontend-platform \
translations/paragon/src/i18n/messages:paragon \
translations/frontend-component-footer/src/i18n/messages:frontend-component-footer \
translations/frontend-component-header/src/i18n/messages:frontend-component-header \
translations/frontend-app-account/src/i18n/messages:frontend-app-account
$(intl_imports) paragon frontend-component-header frontend-component-footer frontend-app-account
$(intl_imports) frontend-platform paragon frontend-component-header frontend-component-footer frontend-app-account
endif
# This target is used by Travis.

View File

@@ -1,10 +1,13 @@
####################
frontend-app-account
####################
|ci-badge| |Codecov| |npm_version| |npm_downloads| |license| |semantic-release|
frontend-app-account
====================
Introduction
------------
********
Purpose
********
This is a micro-frontend application responsible for the display and updating of a user's account information.
@@ -18,8 +21,28 @@ In this MFE: Private user settings UIs. Public facing profile is in a `separate
- IDV (Identity Verification)
***************
Getting Started
***************
Prerequisites
=============
The `devstack`_ is currently recommended as a development environment for your
new MFE. If you start it with ``make dev.up.lms`` that should give you
everything you need as a companion to this frontend.
Note that it is also possible to use `Tutor`_ to develop an MFE. You can refer
to the `relevant tutor-mfe documentation`_ to get started using it.
.. _Devstack: https://github.com/openedx/devstack
.. _Tutor: https://github.com/overhangio/tutor
.. _relevant tutor-mfe documentation: https://github.com/overhangio/tutor-mfe#mfe-development
Installation
------------
============
This MFE is bundled with `Devstack <https://github.com/openedx/devstack>`_, see the `Getting Started <https://github.com/openedx/devstack#getting-started>`_ section for setup instructions.
@@ -41,7 +64,7 @@ This MFE is bundled with `Devstack <https://github.com/openedx/devstack>`_, see
.. image:: ./docs/images/localhost_preview.png
Environment Variables/Setup Notes
---------------------------------
=================================
This MFE is configured via environment variables supplied at build time. All micro-frontends have a shared set of required environment variables, as documented in the Open edX Developer Guide under `Required Environment Variables <https://edx.readthedocs.io/projects/edx-developer-docs/en/latest/developers_guide/micro_frontends_in_open_edx.html#required-environment-variables>`__.
@@ -71,7 +94,7 @@ Enable the account deletion option, defaults to true.
To disable account deletion set ``ENABLE_ACCOUNT_DELETION`` to ``'false'`` (string), otherwise it will default to true.
edX-specific Environment Variables
**********************************
==================================
Furthermore, there are several edX-specific environment variables that enable integrations with closed-source services private to the edX organization, and are unsupported in Open edX. Enabling these environment variables will result in undefined behavior in Open edX installations:
@@ -96,18 +119,101 @@ Example build syntax with a single environment variable:
For more information see the document: `Micro-frontend applications in Open
edX <https://edx.readthedocs.io/projects/edx-developer-docs/en/latest/developers_guide/micro_frontends_in_open_edx.html#required-environment-variables>`__.
Cloning and Startup
===================
.. code-block::
1. Clone your new repo:
``git clone https://github.com/openedx/frontend-app-account.git``
2. Use node v18.x.
The current version of the micro-frontend build scripts support node 18.
Using other major versions of node *may* work, but this is unsupported. For
convenience, this repository includes an .nvmrc file to help in setting the
correct node version via `nvm <https://github.com/nvm-sh/nvm>`_.
3. Install npm dependencies:
``cd frontend-app-account && npm ci``
4. Start the dev server:
``npm start``
Known Issues
------------
===========
None
Development Roadmap
-------------------
===================
We don't have anything planned for the core of the MFE (the account settings page) - this MFE is currently in maintenance mode.
There may be a replacement for IDV coming down the pipe, so that may be DEPRed.
In the future, it's possible that demographics could be modeled as a plugin rather than being hard-coded into this MFE.
License
=======
The code in this repository is licensed under the AGPLv3 unless otherwise
noted.
Please see `LICENSE <LICENSE>`_ for details.
Contributing
============
Contributions are very welcome. Please read `How To Contribute`_ for details.
.. _How To Contribute: https://openedx.org/r/how-to-contribute
This project is currently accepting all types of contributions, bug fixes,
security fixes, maintenance work, or new features. However, please make sure
to have a discussion about your new feature idea with the maintainers prior to
beginning development to maximize the chances of your change being accepted.
You can start a conversation by creating a new issue on this repo summarizing
your idea.
Getting Help
===========
If you're having trouble, we have discussion forums at
https://discuss.openedx.org where you can connect with others in the community.
Our real-time conversations are on Slack. You can request a `Slack
invitation`_, then join our `community Slack workspace`_. Because this is a
frontend repository, the best place to discuss it would be in the `#wg-frontend
channel`_.
For anything non-trivial, the best path is to open an issue in this repository
with as many details about the issue you are facing as you can provide.
https://github.com/openedx/frontend-app-account/issues
For more information about these options, see the `Getting Help`_ page.
.. _Slack invitation: https://openedx.org/slack
.. _community Slack workspace: https://openedx.slack.com/
.. _#wg-frontend channel: https://openedx.slack.com/archives/C04BM6YC7A6
.. _Getting Help: https://openedx.org/community/connect
The Open edX Code of Conduct
============================
All community members are expected to follow the `Open edX Code of Conduct`_.
.. _Open edX Code of Conduct: https://openedx.org/code-of-conduct/
Reporting Security Issues
=========================
Please do not report security issues in public. Please email security@openedx.org.
==============================

View File

@@ -1,4 +1,4 @@
const { createConfig } = require('@edx/frontend-build');
const { createConfig } = require('@openedx/frontend-build');
module.exports = createConfig('jest', {
setupFilesAfterEnv: [

11434
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -28,21 +28,22 @@
],
"dependencies": {
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"@edx/frontend-component-footer": "12.5.0",
"@edx/frontend-component-header": "4.8.0",
"@edx/frontend-platform": "6.0.2",
"@edx/paragon": "21.5.3",
"@edx/frontend-component-footer": "13.0.2",
"@edx/frontend-component-header": "5.0.2",
"@edx/frontend-platform": "7.1.0",
"@edx/openedx-atlas": "^0.6.0",
"@fortawesome/fontawesome-svg-core": "1.2.36",
"@fortawesome/free-brands-svg-icons": "5.15.4",
"@fortawesome/free-regular-svg-icons": "5.15.4",
"@fortawesome/free-solid-svg-icons": "5.15.4",
"@fortawesome/react-fontawesome": "0.2.0",
"@openedx/paragon": "22.0.0",
"@tensorflow-models/blazeface": "0.0.7",
"@tensorflow/tfjs-converter": "3.21.0",
"@tensorflow/tfjs-core": "3.21.0",
"bowser": "2.11.0",
"classnames": "2.3.2",
"core-js": "3.33.1",
"classnames": "2.5.1",
"core-js": "3.35.1",
"font-awesome": "4.7.0",
"form-urlencoded": "6.1.4",
"formdata-polyfill": "4.0.10",
@@ -65,28 +66,26 @@
"react-dom": "17.0.2",
"react-helmet": "6.1.0",
"react-redux": "7.2.9",
"react-router": "6.14.2",
"react-router-dom": "6.14.2",
"react-router": "6.22.0",
"react-router-dom": "6.22.0",
"react-router-hash-link": "2.4.3",
"react-scrollspy": "3.4.3",
"react-transition-group": "4.4.5",
"redux": "4.2.1",
"redux-devtools-extension": "2.13.9",
"redux-logger": "3.0.6",
"redux-saga": "1.2.3",
"redux-saga": "1.3.0",
"redux-thunk": "2.4.2",
"regenerator-runtime": "0.14.0",
"regenerator-runtime": "0.14.1",
"reselect": "4.1.8",
"universal-cookie": "4.0.4"
},
"devDependencies": {
"@edx/browserslist-config": "1.2.0",
"@edx/frontend-build": "13.0.3",
"@edx/reactifex": "1.1.0",
"@openedx/frontend-build": "13.0.27",
"@testing-library/jest-dom": "5.17.0",
"@testing-library/react": "12.1.5",
"@wojtekmaj/enzyme-adapter-react-17": "0.8.0",
"enzyme": "3.11.0",
"react-test-renderer": "17.0.2",
"reactifex": "1.1.1",
"redux-mock-store": "1.5.4"

View File

@@ -15,8 +15,8 @@ import {
} from '@edx/frontend-platform/i18n';
import {
Button, Hyperlink, Icon, Alert,
} from '@edx/paragon';
import { CheckCircle, Error, WarningFilled } from '@edx/paragon/icons';
} from '@openedx/paragon';
import { CheckCircle, Error, WarningFilled } from '@openedx/paragon/icons';
import messages from './AccountSettingsPage.messages';
import {
@@ -45,6 +45,7 @@ import {
GENDER_OPTIONS,
COUNTRY_WITH_STATES,
COPPA_COMPLIANCE_YEAR,
WORK_EXPERIENCE_OPTIONS,
getStatesList,
} from './data/constants';
import { fetchSiteLanguages } from './site-language';
@@ -142,6 +143,10 @@ class AccountSettingsPage extends React.Component {
value: key,
label: this.props.intl.formatMessage(messages[`account.settings.field.gender.options.${key || 'empty'}`]),
})),
workExperienceOptions: WORK_EXPERIENCE_OPTIONS.map(key => ({
value: key,
label: key === '' ? this.props.intl.formatMessage(messages['account.settings.field.work.experience.options.empty']) : key,
})),
}));
handleEditableFieldChange = (name, value) => {
@@ -149,7 +154,17 @@ class AccountSettingsPage extends React.Component {
};
handleSubmit = (formId, values) => {
this.props.saveSettings(formId, values);
const { formValues } = this.props;
let extendedProfileObject = {};
if ('extended_profile' in formValues && formValues.extended_profile.some((field) => field.field_name === formId)) {
extendedProfileObject = {
extended_profile: formValues.extended_profile.map(field => (field.field_name === formId
? { ...field, field_value: values }
: field)),
};
}
this.props.saveSettings(formId, values, extendedProfileObject);
};
handleSubmitProfileName = (formId, values) => {
@@ -469,13 +484,15 @@ class AccountSettingsPage extends React.Component {
yearOfBirthOptions,
educationLevelOptions,
genderOptions,
workExperienceOptions,
} = this.getLocalizedOptions(this.context.locale, this.props.formValues.country);
// Show State field only if the country is US (could include Canada later)
const showState = this.props.formValues.country === COUNTRY_WITH_STATES;
const { verifiedName } = this.props;
const hasWorkExperience = !!this.props.formValues?.extended_profile?.find(field => field.field_name === 'work_experience');
const timeZoneOptions = this.getLocalizedTimeZoneOptions(
this.props.timeZoneOptions,
this.props.countryTimeZoneOptions,
@@ -679,6 +696,18 @@ class AccountSettingsPage extends React.Component {
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.gender.empty'])}
{...editableFieldProps}
/>
{hasWorkExperience
&& (
<EditableSelectField
name="work_experience"
type="select"
value={this.props.formValues?.extended_profile?.find(field => field.field_name === 'work_experience')?.field_value}
options={workExperienceOptions}
label={this.props.intl.formatMessage(messages['account.settings.field.work.experience'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.work.experience.empty'])}
{...editableFieldProps}
/>
)}
<EditableSelectField
name="language_proficiencies"
type="select"
@@ -850,6 +879,7 @@ AccountSettingsPage.propTypes = {
country: PropTypes.string,
level_of_education: PropTypes.string,
gender: PropTypes.string,
extended_profile: PropTypes.string,
language_proficiencies: PropTypes.string,
pending_name_change: PropTypes.string,
phone_number: PropTypes.string,

View File

@@ -570,6 +570,21 @@ const messages = defineMessages({
defaultMessage: 'Notifications',
description: 'Label for Notifications',
},
'account.settings.field.work.experience': {
id: 'account.settings.work.experience',
defaultMessage: 'Work Experience',
description: 'Label for account settings Work experience field.',
},
'account.settings.field.work.experience.empty': {
id: 'account.settings.field.work.experience.empty',
defaultMessage: 'Add work experience',
description: 'Placeholder for empty account settings work experience field.',
},
'account.settings.field.work.experience.options.empty': {
id: 'account.settings.field.work.experience.options.empty',
defaultMessage: 'Select work experience',
description: 'Placeholder for the work experience levels dropdown.',
},
});
export default messages;

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { AppContext } from '@edx/frontend-platform/react';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { connect } from 'react-redux';
import { Button, Hyperlink } from '@edx/paragon';
import { Button, Hyperlink } from '@openedx/paragon';
import { betaLanguageBannerSelector } from './data/selectors';
import messages from './AccountSettingsPage.messages';

View File

@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
Form, StatefulButton, ModalDialog, ActionRow, useToggle, Button,
} from '@edx/paragon';
} from '@openedx/paragon';
import React, { useCallback, useEffect, useState } from 'react';
import { connect, useDispatch } from 'react-redux';
import messages from './AccountSettingsPage.messages';

View File

@@ -5,7 +5,7 @@ import classNames from 'classnames';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
Button, Form, StatefulButton,
} from '@edx/paragon';
} from '@openedx/paragon';
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

View File

@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
Button, Form, StatefulButton,
} from '@edx/paragon';
} from '@openedx/paragon';
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -98,9 +98,28 @@ const EditableSelectField = (props) => {
value: confirmationValue,
});
};
const selectOptions = options.map(option => (
<option value={option.value} key={`${option.value}-${option.label}`}>{option.label}</option>
));
const selectOptions = options.map((option) => {
if (option.group) {
// If the option has a 'group' property, it represents an element with sub-options.
return (
<optgroup label={option.label} key={option.label}>
{option.group.map((subOption) => (
<option
value={subOption.value}
key={`${subOption.value}-${subOption.label}`}
>
{subOption.label}
</option>
))}
</optgroup>
);
}
return (
<option value={option.value} key={`${option.value}-${option.label}`}>
{option.label}
</option>
);
});
return (
<SwitchContent

View File

@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
import {
Button, StatefulButton, Form,
} from '@edx/paragon';
} from '@openedx/paragon';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faExclamationTriangle, faPencilAlt } from '@fortawesome/free-solid-svg-icons';

View File

@@ -1,7 +1,7 @@
import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { breakpoints, useWindowSize, Icon } from '@edx/paragon';
import { OpenInNew } from '@edx/paragon/icons';
import { breakpoints, useWindowSize, Icon } from '@openedx/paragon';
import { OpenInNew } from '@openedx/paragon/icons';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';

View File

@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Alert } from '@edx/paragon';
import { Alert } from '@openedx/paragon';
const OneTimeDismissibleAlert = (props) => {
const [dismissed, setDismissed] = useState(localStorage.getItem(props.id) !== 'true');

View File

@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { TransitionReplace } from '@edx/paragon';
import { TransitionReplace } from '@openedx/paragon';
const onChildExit = (htmlNode) => {
// If the leaving child has focus, take control and redirect it

View File

@@ -7,7 +7,7 @@ import {
Form,
ModalDialog,
StatefulButton,
} from '@edx/paragon';
} from '@openedx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {

View File

@@ -151,7 +151,7 @@ describe('NameChange', () => {
const submitButton = screen.getByText('Choose name');
fireEvent.click(submitButton);
expect(mockDispatch).toHaveBeenCalledWith({
payload: { formId, commitValues: false },
payload: { formId, commitValues: false, extendedProfile: {} },
type: 'ACCOUNT_SETTINGS__SAVE_SETTINGS',
});
});

View File

@@ -77,9 +77,9 @@ export const beginNameChange = (formId) => ({
});
// SAVE SETTINGS ACTIONS
export const saveSettings = (formId, commitValues) => ({
export const saveSettings = (formId, commitValues, extendedProfile = {}) => ({
type: SAVE_SETTINGS.BASE,
payload: { formId, commitValues },
payload: { formId, commitValues, extendedProfile },
});
export const saveSettingsBegin = () => ({

View File

@@ -34,6 +34,21 @@ export const GENDER_OPTIONS = [
'm',
'o',
];
export const WORK_EXPERIENCE_OPTIONS = [
'',
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10+',
];
export const COUNTRY_WITH_STATES = 'US';

View File

@@ -83,8 +83,8 @@ export function* handleSaveSettings(action) {
yield put(saveSettingsBegin());
const { username, userId } = getAuthenticatedUser();
const { commitValues, formId } = action.payload;
const commitData = { [formId]: commitValues };
const { commitValues, formId, extendedProfile } = action.payload;
const commitData = Object.keys(extendedProfile).length > 0 ? extendedProfile : { [formId]: commitValues };
let savedValues = null;
if (formId === 'siteLanguage') {
const previousSiteLanguage = getLocale();

View File

@@ -156,7 +156,7 @@ function chooseFormValue(draft, committed) {
return draft !== undefined ? draft : committed;
}
const formValuesSelector = createSelector(
export const formValuesSelector = createSelector(
valuesSelector,
draftsSelector,
(values, drafts) => {
@@ -164,6 +164,20 @@ const formValuesSelector = createSelector(
Object.entries(values).forEach(([name, value]) => {
if (typeof value === 'boolean') {
formValues[name] = chooseFormValue(drafts[name], value);
} else if (typeof value === 'object' && name === 'extended_profile' && value !== null) {
const extendedProfile = value.slice();
const draftsKeys = Object.keys(drafts);
if (draftsKeys.length !== 0) {
const draftFieldName = draftsKeys[0];
const index = extendedProfile.findIndex((profile) => profile.field_name === draftFieldName);
if (index !== -1) {
extendedProfile[index] = { field_name: draftFieldName, field_value: drafts[draftFieldName] };
}
}
formValues.extended_profile = [...extendedProfile];
} else {
formValues[name] = chooseFormValue(drafts[name], value) || '';
}

View File

@@ -0,0 +1,72 @@
import { profileDataManagerSelector, formValuesSelector } from './selectors';
const testValue = 'test VALUE';
describe('profileDataManagerSelector', () => {
it('returns the profileDataManager from the state', () => {
const state = {
accountSettings: {
profileDataManager: { testValue },
},
};
const result = profileDataManagerSelector(state);
expect(result).toEqual(state.accountSettings.profileDataManager);
});
it('should correctly select form values', () => {
const state = {
accountSettings: {
values: {
name: 'John Doe',
age: 25,
},
drafts: {
age: 26,
},
verifiedNameHistory: 'test',
confirmationValues: {},
},
};
const result = formValuesSelector(state);
const expected = {
name: 'John Doe',
age: 26,
verified_name: '',
useVerifiedNameForCerts: false,
};
expect(result).toEqual(expected);
});
it('should correctly select form values with extended_profile', () => {
// Mock data with extended_profile field in both values and drafts
const state = {
accountSettings: {
values: {
extended_profile: [
{ field_name: 'test_field', field_value: '5' },
],
},
drafts: { test_field: '6' },
verifiedNameHistory: 'test',
confirmationValues: {},
},
};
const result = formValuesSelector(state);
const expected = {
verified_name: '',
useVerifiedNameForCerts: false,
extended_profile: [ // Draft value should override the committed value
{ field_name: 'test_field', field_value: '6' }, // Value from the committed values
],
};
expect(result).toEqual(expected);
});
});

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Hyperlink } from '@edx/paragon';
import { Hyperlink } from '@openedx/paragon';
// Messages
import { getConfig } from '@edx/frontend-platform';

View File

@@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import {
AlertModal,
Button, Input, ValidationFormGroup, ActionRow,
} from '@edx/paragon';
} from '@openedx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { faExclamationCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Button, Hyperlink } from '@edx/paragon';
import { Button, Hyperlink } from '@openedx/paragon';
// Actions
import {

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Hyperlink } from '@edx/paragon';
import { Hyperlink } from '@openedx/paragon';
import { getConfig } from '@edx/frontend-platform';
import messages from './messages';

View File

@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { ModalLayer, ModalCloseButton } from '@edx/paragon';
import { ModalLayer, ModalCloseButton } from '@openedx/paragon';
import messages from './messages';

View File

@@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { Form } from '@edx/paragon';
import { Form } from '@openedx/paragon';
import { DECLINED } from '../data/constants';
const Checkboxes = (props) => {

View File

@@ -5,7 +5,7 @@ import {
intlShape,
} from '@edx/frontend-platform/i18n';
import { Hyperlink, Form } from '@edx/paragon';
import { Hyperlink, Form } from '@openedx/paragon';
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { mount } from 'enzyme';
import { fireEvent, render, screen } from '@testing-library/react';
import { withLocation, withNavigate } from './hoc';
const mockedNavigator = jest.fn();
@@ -15,24 +15,24 @@ jest.mock('react-router-dom', () => ({
// eslint-disable-next-line react/prop-types
const MockComponent = ({ navigate, location }) => (
// eslint-disable-next-line react/button-has-type, react/prop-types
<button id="btn" onClick={() => navigate('/some-route')}>{location}</button>
<button data-testid="btn" onClick={() => navigate('/some-route')}>{location}</button>
);
const WrappedComponent = withNavigate(withLocation(MockComponent));
test('Provide Navigation to Component', () => {
const wrapper = mount(
render(
<WrappedComponent />,
);
const btn = wrapper.find('#btn');
btn.simulate('click');
const btn = screen.getByTestId('btn');
fireEvent.click(btn);
expect(mockedNavigator).toHaveBeenCalledWith('/some-route');
});
test('Provide Location Pathname to Component', () => {
const wrapper = mount(
render(
<WrappedComponent />,
);
expect(wrapper.find('#btn').text()).toContain('/current-location');
expect(screen.getByTestId('btn').textContent).toContain('/current-location');
});

View File

@@ -13,7 +13,7 @@ import {
Form,
ModalDialog,
StatefulButton,
} from '@edx/paragon';
} from '@openedx/paragon';
import { closeForm, saveSettingsReset } from '../data/actions';
import { nameChangeSelector } from '../data/selectors';

View File

@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { getConfig } from '@edx/frontend-platform';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { Hyperlink } from '@edx/paragon';
import { Hyperlink } from '@openedx/paragon';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';

View File

@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
import { StatefulButton } from '@edx/paragon';
import { StatefulButton } from '@openedx/paragon';
import { resetPassword } from './data/actions';
import messages from './messages';

View File

@@ -0,0 +1,101 @@
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { AppContext } from '@edx/frontend-platform/react';
import {
render, screen, fireEvent,
} from '@testing-library/react';
import configureStore from 'redux-mock-store';
import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n';
import AccountSettingsPage from '../AccountSettingsPage';
import mockData from './mockData';
const mockDispatch = jest.fn();
jest.mock('@edx/frontend-platform/analytics', () => ({
sendTrackingLogEvent: jest.fn(),
getCountryList: jest.fn(),
}));
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useDispatch: () => mockDispatch,
}));
jest.mock('@edx/frontend-platform/auth');
const IntlAccountSettingsPage = injectIntl(AccountSettingsPage);
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
describe('AccountSettingsPage', () => {
let props = {};
let store = {};
const appContext = { locale: 'en', authenticatedUser: { userId: 3, roles: [] } };
const reduxWrapper = children => (
<AppContext.Provider value={appContext}>
<Router>
<IntlProvider locale="en">
<Provider store={store}>
{children}
</Provider>
</IntlProvider>
</Router>
</AppContext.Provider>
);
beforeEach(() => {
store = mockStore(mockData);
props = {
loaded: true,
siteLanguage: {},
formValues: {
username: 'test_username',
accomplishments_shared: false,
name: 'test_name',
email: 'test_email@test.com',
id: 534,
extended_profile: [
{
field_name: 'work_experience',
field_value: '',
},
],
},
fetchSettings: jest.fn(),
};
});
afterEach(() => jest.clearAllMocks());
it('renders AccountSettingsPage correctly with editing enabled', async () => {
const { getByText, rerender, getByLabelText } = render(reduxWrapper(<IntlAccountSettingsPage {...props} />));
const workExperienceText = getByText('Work Experience');
const workExperienceEditButton = workExperienceText.parentElement.querySelector('button');
expect(workExperienceEditButton).toBeInTheDocument();
store = mockStore({
...mockData,
accountSettings: {
...mockData.accountSettings,
openFormId: 'work_experience',
},
});
rerender(reduxWrapper(<IntlAccountSettingsPage {...props} />));
const submitButton = screen.getByText('Save');
expect(submitButton).toBeInTheDocument();
const workExperienceSelect = getByLabelText('Work Experience');
// Use fireEvent.change to simulate changing the selected value
fireEvent.change(workExperienceSelect, { target: { value: '4' } });
fireEvent.click(submitButton);
});
});

View File

@@ -0,0 +1,169 @@
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { Provider } from 'react-redux';
import renderer from 'react-test-renderer';
import configureStore from 'redux-mock-store';
import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n';
import EditableSelectField from '../EditableSelectField';
const mockDispatch = jest.fn();
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useDispatch: () => mockDispatch,
}));
jest.mock('@edx/frontend-platform/auth');
jest.mock('../data/selectors', () => jest.fn().mockImplementation(() => ({ certPreferenceSelector: () => ({}) })));
const IntlEditableSelectField = injectIntl(EditableSelectField);
const mockStore = configureStore();
describe('EditableSelectField', () => {
let props = {};
let store = {};
const reduxWrapper = children => (
<Router>
<IntlProvider locale="en">
<Provider store={store}>{children}</Provider>
</IntlProvider>
</Router>
);
beforeEach(() => {
store = mockStore();
props = {
name: 'testField',
label: 'Main Label',
emptyLabel: 'Empty Main Label',
type: 'text',
value: 'Test Field',
userSuppliedValue: '',
options: [
{
label: 'Default Option',
value: 'defaultOption',
},
{
label: 'User Options',
group: [
{
label: 'Suboption 1',
value: 'suboption1',
},
],
},
{
label: 'Other Options',
group: [
{
label: 'Suboption 2',
value: 'suboption2',
},
{
label: 'Suboption 3',
value: 'suboption3',
},
],
},
],
saveState: 'default',
error: '',
confirmationMessageDefinition: {
id: 'confirmationMessageId',
defaultMessage: 'Default Confirmation Message',
description: 'Description of the confirmation message',
},
confirmationValue: 'Confirmation Value',
helpText: 'Helpful Text',
isEditing: false,
isEditable: true,
isGrayedOut: false,
};
});
afterEach(() => jest.clearAllMocks());
it('renders EditableSelectField correctly with editing disabled', () => {
const tree = renderer.create(reduxWrapper(<IntlEditableSelectField {...props} />)).toJSON();
expect(tree).toMatchSnapshot();
});
it('renders EditableSelectField correctly with editing enabled', () => {
props = {
...props,
isEditing: true,
};
const tree = renderer.create(reduxWrapper(<IntlEditableSelectField {...props} />)).toJSON();
expect(tree).toMatchSnapshot();
});
it('renders EditableSelectField with an error', () => {
const errorProps = {
...props,
error: 'This is an error message',
};
const tree = renderer.create(reduxWrapper(<IntlEditableSelectField {...errorProps} />)).toJSON();
expect(tree).toMatchSnapshot();
});
it('renders selectOptions when option has a group', () => {
const propsWithGroup = {
...props,
options: [
{
label: 'User Options',
group: [
{
label: 'Suboption 1',
value: 'suboption1',
},
],
},
],
};
const tree = renderer.create(reduxWrapper(<IntlEditableSelectField {...propsWithGroup} />)).toJSON();
expect(tree).toMatchSnapshot();
});
it('renders selectOptions when option does not have a group', () => {
const propsWithoutGroup = {
...props,
options: [
{
label: 'Default Option',
value: 'defaultOption',
},
],
};
const tree = renderer.create(reduxWrapper(<IntlEditableSelectField {...propsWithoutGroup} />)).toJSON();
expect(tree).toMatchSnapshot();
});
it('renders selectOptions with multiple groups', () => {
const propsWithGroups = {
...props,
options: [
{
label: 'Mixed Options',
group: [
{
label: 'Suboption 1',
value: 'suboption1',
},
{
label: 'Suboption 2',
value: 'suboption2',
},
],
},
],
};
const tree = renderer.create(reduxWrapper(<IntlEditableSelectField {...propsWithGroups} />)).toJSON();
expect(tree).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,485 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`EditableSelectField renders EditableSelectField correctly with editing disabled 1`] = `
<div
className="pgn-transition-replace-group position-relative"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="form-group"
>
<div
className="d-flex align-items-start"
>
<h6
aria-level="3"
>
Main Label
</h6>
<button
className="ml-3 btn btn-link"
disabled={false}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-pencil-alt fa-w-16 mr-1"
data-icon="pencil-alt"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"
fill="currentColor"
style={Object {}}
/>
</svg>
Edit
</button>
</div>
<p
className={null}
data-hj-suppress={true}
>
Test Field
</p>
<p
className="small text-muted mt-n2"
>
Default Confirmation Message
</p>
</div>
</div>
</div>
`;
exports[`EditableSelectField renders EditableSelectField correctly with editing enabled 1`] = `
<div
className="pgn-transition-replace-group position-relative"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<form
onSubmit={[Function]}
>
<div
className="pgn__form-group"
>
<label
className="pgn__form-label h6 d-block"
htmlFor="field-testField"
size="sm"
>
Main Label
</label>
<div
className="pgn__form-control-decorator-group"
>
<text
aria-describedby="field-testField-2"
className="has-value form-control is-invalid"
data-hj-suppress={true}
id="field-testField"
name="testField"
onBlur={[Function]}
onChange={[Function]}
type="text"
value="Test Field"
>
<option
value="defaultOption"
>
Default Option
</option>
<optgroup
label="User Options"
>
<option
value="suboption1"
>
Suboption 1
</option>
</optgroup>
<optgroup
label="Other Options"
>
<option
value="suboption2"
>
Suboption 2
</option>
<option
value="suboption3"
>
Suboption 3
</option>
</optgroup>
</text>
</div>
<div
className="pgn__form-text pgn__form-text-default"
>
<div>
Helpful Text
</div>
</div>
<div
className="pgn__form-control-description pgn__form-text pgn__form-text-invalid"
id="field-testField-2"
>
<span
className="pgn__icon"
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"
fill="currentColor"
/>
</svg>
</span>
<div>
</div>
</div>
</div>
<p>
<button
aria-disabled={false}
aria-live="assertive"
className="pgn__stateful-btn pgn__stateful-btn-state-default mr-2 btn btn-primary"
disabled={false}
onClick={[Function]}
type="submit"
>
<span
className="d-flex align-items-center justify-content-center"
>
<span>
Save
</span>
</span>
</button>
<button
className="btn btn-outline-primary"
disabled={false}
onClick={[Function]}
type="button"
>
Cancel
</button>
</p>
</form>
</div>
</div>
`;
exports[`EditableSelectField renders EditableSelectField with an error 1`] = `
<div
className="pgn-transition-replace-group position-relative"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="form-group"
>
<div
className="d-flex align-items-start"
>
<h6
aria-level="3"
>
Main Label
</h6>
<button
className="ml-3 btn btn-link"
disabled={false}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-pencil-alt fa-w-16 mr-1"
data-icon="pencil-alt"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"
fill="currentColor"
style={Object {}}
/>
</svg>
Edit
</button>
</div>
<p
className={null}
data-hj-suppress={true}
>
Test Field
</p>
<p
className="small text-muted mt-n2"
>
Default Confirmation Message
</p>
</div>
</div>
</div>
`;
exports[`EditableSelectField renders selectOptions when option does not have a group 1`] = `
<div
className="pgn-transition-replace-group position-relative"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="form-group"
>
<div
className="d-flex align-items-start"
>
<h6
aria-level="3"
>
Main Label
</h6>
<button
className="ml-3 btn btn-link"
disabled={false}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-pencil-alt fa-w-16 mr-1"
data-icon="pencil-alt"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"
fill="currentColor"
style={Object {}}
/>
</svg>
Edit
</button>
</div>
<p
className={null}
data-hj-suppress={true}
>
Test Field
</p>
<p
className="small text-muted mt-n2"
>
Default Confirmation Message
</p>
</div>
</div>
</div>
`;
exports[`EditableSelectField renders selectOptions when option has a group 1`] = `
<div
className="pgn-transition-replace-group position-relative"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="form-group"
>
<div
className="d-flex align-items-start"
>
<h6
aria-level="3"
>
Main Label
</h6>
<button
className="ml-3 btn btn-link"
disabled={false}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-pencil-alt fa-w-16 mr-1"
data-icon="pencil-alt"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"
fill="currentColor"
style={Object {}}
/>
</svg>
Edit
</button>
</div>
<p
className={null}
data-hj-suppress={true}
>
Test Field
</p>
<p
className="small text-muted mt-n2"
>
Default Confirmation Message
</p>
</div>
</div>
</div>
`;
exports[`EditableSelectField renders selectOptions with multiple groups 1`] = `
<div
className="pgn-transition-replace-group position-relative"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="form-group"
>
<div
className="d-flex align-items-start"
>
<h6
aria-level="3"
>
Main Label
</h6>
<button
className="ml-3 btn btn-link"
disabled={false}
onClick={[Function]}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-pencil-alt fa-w-16 mr-1"
data-icon="pencil-alt"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"
fill="currentColor"
style={Object {}}
/>
</svg>
Edit
</button>
</div>
<p
className={null}
data-hj-suppress={true}
>
Test Field
</p>
<p
className="small text-muted mt-n2"
>
Default Confirmation Message
</p>
</div>
</div>
</div>
`;

View File

@@ -2,180 +2,196 @@
exports[`JumpNav should not render Optional Information or delete account link 1`] = `
<div
className="jump-nav px-2.25 jump-nav-sm position-sticky pt-3"
data-testid="redux-provider"
>
<ul
className="list-unstyled"
style={Object {}}
<div
data-testid="browser-router"
>
<li
className=""
<div
className="jump-nav px-2.25 jump-nav-sm position-sticky pt-3"
>
<a
aria-current="page"
className="active"
href="/#basic-information"
isActive={[Function]}
onClick={[Function]}
<ul
className="list-unstyled"
style={Object {}}
>
Account Information
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#profile-information"
isActive={[Function]}
onClick={[Function]}
>
Profile Information
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#social-media"
isActive={[Function]}
onClick={[Function]}
>
Social Media Links
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#site-preferences"
isActive={[Function]}
onClick={[Function]}
>
Site Preferences
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#linked-accounts"
isActive={[Function]}
onClick={[Function]}
>
Linked Accounts
</a>
</li>
</ul>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#basic-information"
isActive={[Function]}
onClick={[Function]}
>
Account Information
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#profile-information"
isActive={[Function]}
onClick={[Function]}
>
Profile Information
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#social-media"
isActive={[Function]}
onClick={[Function]}
>
Social Media Links
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#site-preferences"
isActive={[Function]}
onClick={[Function]}
>
Site Preferences
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#linked-accounts"
isActive={[Function]}
onClick={[Function]}
>
Linked Accounts
</a>
</li>
</ul>
</div>
</div>
</div>
`;
exports[`JumpNav should render Optional Information and delete account link 1`] = `
<div
className="jump-nav px-2.25 jump-nav-sm position-sticky pt-3"
data-testid="redux-provider"
>
<ul
className="list-unstyled"
style={Object {}}
<div
data-testid="browser-router"
>
<li
className=""
<div
className="jump-nav px-2.25 jump-nav-sm position-sticky pt-3"
>
<a
aria-current="page"
className="active"
href="/#basic-information"
isActive={[Function]}
onClick={[Function]}
<ul
className="list-unstyled"
style={Object {}}
>
Account Information
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#profile-information"
isActive={[Function]}
onClick={[Function]}
>
Profile Information
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#demographics-information"
isActive={[Function]}
onClick={[Function]}
>
Optional Information
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#social-media"
isActive={[Function]}
onClick={[Function]}
>
Social Media Links
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#site-preferences"
isActive={[Function]}
onClick={[Function]}
>
Site Preferences
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#linked-accounts"
isActive={[Function]}
onClick={[Function]}
>
Linked Accounts
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#delete-account"
isActive={[Function]}
onClick={[Function]}
>
Delete My Account
</a>
</li>
</ul>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#basic-information"
isActive={[Function]}
onClick={[Function]}
>
Account Information
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#profile-information"
isActive={[Function]}
onClick={[Function]}
>
Profile Information
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#demographics-information"
isActive={[Function]}
onClick={[Function]}
>
Optional Information
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#social-media"
isActive={[Function]}
onClick={[Function]}
>
Social Media Links
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#site-preferences"
isActive={[Function]}
onClick={[Function]}
>
Site Preferences
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#linked-accounts"
isActive={[Function]}
onClick={[Function]}
>
Linked Accounts
</a>
</li>
<li
className=""
>
<a
aria-current="page"
className="active"
href="/#delete-account"
isActive={[Function]}
onClick={[Function]}
>
Delete My Account
</a>
</li>
</ul>
</div>
</div>
</div>
`;

View File

@@ -0,0 +1,112 @@
const mockData = {
accountSettings: {
loading: false,
loaded: true,
loadingError: null,
data: null,
values: {
username: 'test_username',
country: 'AD',
accomplishments_shared: false,
name: 'test_name',
email: 'test_email@test.com',
id: 533,
verified_name: null,
extended_profile: [
{
field_name: 'work_experience',
field_value: '',
},
],
gender: null,
'pref-lang': 'en',
shouldDisplayDemographicsSection: false,
demographicsOptions: false,
},
errors: {},
confirmationValues: {},
drafts: {},
saveState: null,
timeZones: [
{
time_zone: 'Africa/Abidjan',
description: 'Africa/Abidjan (GMT, UTC+0000)',
},
],
countryTimeZones: [
{
time_zone: 'Europe/Andorra',
description: 'Europe/Andorra (CET, UTC+0100)',
},
],
previousSiteLanguage: null,
deleteAccount: {
status: null,
errorType: null,
},
siteLanguage: {
loading: false,
loaded: true,
loadingError: null,
siteLanguageList: [
{
code: 'en',
name: 'English',
released: true,
},
],
},
resetPassword: {
status: null,
},
nameChange: {
saveState: null,
errors: {},
},
thirdPartyAuth: {
providers: [
{
id: 'oa2-google-oauth2',
name: 'Google',
connected: false,
accepts_logins: true,
connectUrl: 'http://localhost:18000/auth/login/google-oauth2/?auth_entry=account_settings&next=%2Faccount%2Fsettings',
disconnectUrl: 'http://localhost:18000/auth/disconnect/google-oauth2/?',
},
],
disconnectionStatuses: {},
errors: {},
},
verifiedName: null,
mostRecentVerifiedName: {},
verifiedNameHistory: {
use_verified_name_for_certs: false,
results: [],
},
profileDataManager: null,
},
notificationPreferences: {
showPreferences: false,
courses: {
status: 'success',
courses: [],
pagination: {
count: 0,
currentPage: 1,
hasMore: false,
totalPages: 1,
},
},
preferences: {
status: 'idle',
updatePreferenceStatus: 'idle',
selectedCourse: null,
preferences: [],
apps: [],
nonEditable: {},
},
},
};
export default mockData;

View File

@@ -2,7 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { Hyperlink, StatefulButton } from '@edx/paragon';
import { Hyperlink, StatefulButton } from '@openedx/paragon';
import Alert from '../Alert';
import { disconnectAuth } from './data/actions';

View File

@@ -1,14 +1,14 @@
import React from 'react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { Helmet } from 'react-helmet';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import { getConfig } from '@edx/frontend-platform';
import Head from './Head';
describe('Head', () => {
const props = {};
it('should match render title tag and fivicon with the site configuration values', () => {
mount(<IntlProvider locale="en"><Head {...props} /></IntlProvider>);
render(<IntlProvider locale="en"><Head {...props} /></IntlProvider>);
const helmet = Helmet.peek();
expect(helmet.title).toEqual(`Account | ${getConfig().SITE_NAME}`);
expect(helmet.linkTags[0].rel).toEqual('shortcut icon');

View File

@@ -1,6 +1,6 @@
import { messages as headerMessages } from '@edx/frontend-component-header';
import { messages as footerMessages } from '@edx/frontend-component-footer';
import { messages as paragonMessages } from '@edx/paragon';
import { messages as paragonMessages } from '@openedx/paragon';
import arMessages from './messages/ar.json';
import deMessages from './messages/de.json';
import es419Messages from './messages/es_419.json';

View File

@@ -115,6 +115,9 @@
"account.settings.static.field.empty": "لا قيمة محددة. رجاءً اتصل بمديرك في {enterprise} ليقوم بالتعديلات.",
"account.settings.static.field.empty.no.admin": "لا قيمة محددة.",
"notification.preferences.notifications.label": "Notifications",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "الصفحة التي تبحث عنها غير متوفرة أو هناك خطأ في العنوان. رجاءً تحقق من العنوان و حاول مجددًا.",
"account.page.title": "الحساب | {siteName}",
"id.verification.access.blocked.denied": "لا يمكننا التحقق من هويتك في الوقت الراهن. إن لم تكن قد فعّلت حسابك بعد، فيرجى تفقد مجلد الرسائل غير المرغوب فيها بحثًا عن بريد التفعيل الإلكتروني من {email}.",

View File

@@ -115,6 +115,9 @@
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
"account.settings.static.field.empty.no.admin": "No value set.",
"notification.preferences.notifications.label": "Notifications",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
"account.page.title": "Account | {siteName}",
"id.verification.access.blocked.denied": "We cannot verify your identity at this time. If you have yet to activate your account, please check your spam folder for the activation email from {email}.",

View File

@@ -115,6 +115,9 @@
"account.settings.static.field.empty": "Kein Wert eingestellt. Wenden Sie sich an Ihren {enterprise} Administrator, um Änderungen vorzunehmen.",
"account.settings.static.field.empty.no.admin": "Kein Wert eingestellt.",
"notification.preferences.notifications.label": "Notifications",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "Die gesuchte Seite ist nicht verfügbar oder es liegt ein Fehler in der URL vor. Bitte überprüfen Sie die URL und versuchen Sie es erneut.",
"account.page.title": "Konto | {siteName}",
"id.verification.access.blocked.denied": "Wir können Ihre Identität derzeit nicht überprüfen. Wenn Sie Ihr Konto noch nicht aktiviert haben, suchen Sie bitte in Ihrem Spam-Ordner nach der Aktivierungs-E-Mail von {email}.",

View File

@@ -115,6 +115,9 @@
"account.settings.static.field.empty": "No hay valor establecido. Contacte su administrador {enterprise} para hacer cambios.",
"account.settings.static.field.empty.no.admin": "No hay valor establecido.",
"notification.preferences.notifications.label": "Notificaciones",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "La página que estas buscando no está disponible o hay un error en la URL. Por favor, comprueba la URL y vuelve a intentarlo.",
"account.page.title": "Cuenta | {siteName}",
"id.verification.access.blocked.denied": "No podemos verificar tu identidad en este momento. Si aún no has activado tu cuenta, comprueba en tu carpeta de correo no deseado el correo de activación de {email}.",

View File

@@ -115,6 +115,9 @@
"account.settings.static.field.empty": "مقداری برای آن تعیین نشده است. برای ایجاد تغییرات با مدیر {enterprise} خود تماس بگیرید.",
"account.settings.static.field.empty.no.admin": "هیچ مقداری تنظیم نشده است.",
"notification.preferences.notifications.label": "اعلان‌ها",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "صفحه مورد نظر شما در دسترس نیست یا خطایی در نشانی اینترنتی وجود دارد. لطفاً نشانی را بررسی کرده و دوباره امتحان کنید.",
"account.page.title": "حساب کاربری | {siteName}",
"id.verification.access.blocked.denied": "اکنون امکان تایید هویت شما را نداریم. اگر هنوز حساب کاربری خود را فعال نکرده‌اید، لطفاً پوشه هرزنامه خود را برای رایانامه فعالسازی از {email} بررسی کنید.",

View File

@@ -115,6 +115,9 @@
"account.settings.static.field.empty": "Aucune valeur n'a été fixée. Contactez l'administrateur de votre {entreprise} pour effectuer des modifications.",
"account.settings.static.field.empty.no.admin": "Pas de valeur définie.",
"notification.preferences.notifications.label": "Notifications",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "La page que vous recherchez n'est pas disponible ou il y a une erreur dans l'URL. Veuillez vérifier l'URL et réessayer.",
"account.page.title": "Account | {siteName}",
"id.verification.access.blocked.denied": "Nous ne pouvez pas vérifier votre identité pour le moment. Si vous n'avez pas encore activé votre compte, veuillez vérifier votre dossier de pourriels pour le courriel d'activation de {email}.",

View File

@@ -115,6 +115,9 @@
"account.settings.static.field.empty": "Aucune valeur définie. Contactez l'administrateur de votre {enterprise} pour effectuer des modifications.",
"account.settings.static.field.empty.no.admin": "Aucune valeur définie.",
"notification.preferences.notifications.label": "Notifications",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "La page que vous recherchez n'est pas disponible ou il y a une erreur dans l'URL. Veuillez vérifier l'URL et réessayer.",
"account.page.title": "Compte | {siteName}",
"id.verification.access.blocked.denied": "Nous ne pouvons pas vérifier votre identité en ce moment. Si vous n'avez pas encore activé votre compte, veuillez regarder dans vos pourriels pour trouver le courriel d'activation provenant de {email}.",

View File

@@ -1,354 +1,357 @@
{
"account.settings.message.duplicate.tpa.provider": "The {provider} account you selected is already linked to another {siteName} account.",
"account.settings.message.managed.settings": "Your profile settings are managed by {managerTitle}. Contact your administrator or {support} for help.",
"account.settings.message.managed.settings.support": "support",
"account.settings.page.heading": "Account Settings",
"account.settings.loading.message": "Loading...",
"account.settings.loading.error": "Error: {error}",
"account.settings.banner.beta.language": "You have set your language to {beta_language}, which is currently not fully translated. You can help us translate this language fully by joining the Transifex community and adding translations from English for learners that speak {beta_language}.",
"account.settings.banner.beta.language.action.switch.back": "Switch Back to {previous_language}",
"account.settings.banner.beta.language.action.help.translate": "Help Translate into {beta_language}",
"account.settings.section.account.information": "Account Information",
"account.settings.section.account.information.description": "These settings include basic information about your account.",
"account.settings.section.profile.information": "Profile Information",
"account.settings.section.demographics.information": "Optional Information",
"account.settings.section.site.preferences": "Site Preferences",
"account.settings.section.linked.accounts": "Linked Accounts",
"account.settings.section.linked.accounts.description": "You can link your identity accounts to simplify signing in to {siteName}.",
"account.settings.field.username": "Username",
"account.settings.field.username.help.text": "The name that identifies you on {siteName}. You cannot change your username.",
"account.settings.field.full.name": "Full name",
"account.settings.field.full.name.empty": "Add name",
"account.settings.field.full.name.help.text": "The name that is used for ID verification and that appears on your certificates.",
"account.settings.field.full.name.help.text.default": "The name that appears on your public profile.",
"account.settings.field.full.name.help.text.default.certificate": "This name is selected to appear on your certificates and public-facing records.",
"account.settings.field.name.verified": "Verified name",
"account.settings.field.name.verified.help.text.verified": "This name has been verified by photo ID.",
"account.settings.field.name.verified.help.text.verified.proctored": "This name has been verified by proctoring.",
"account.settings.field.name.verified.help.text.verified.certificate": "This name has been verified by photo ID, and is selected to appear on your certificates and public-facing records.",
"account.settings.field.name.verified.help.text.verified.proctored.certificate": "This name has been verified by proctoring, and is selected to appear on your certificates and public-facing records.",
"account.settings.field.name.verified.help.text.submitted": "Verification has been submitted. This usually takes 48 hours or less. Verified name cannot be changed at this time.",
"account.settings.field.name.verified.help.text.submitted.proctored": "Your proctored exam has been submitted. Verified name cannot be changed at this time. Please check back in 2-5 days.",
"account.settings.field.name.verified.help.text.submitted.certificate": "When identity verification is successful, this name will appear on your certificates and public-facing records. Verified name cannot be changed at this time.",
"account.settings.field.name.verified.help.text.submitted.proctored.certificate": "Once your proctored exam passes review, this name will appear on your certificate and public-facing records. Verified Name cannot be changed at this time.",
"account.settings.field.name.verified.verification.help": "Enter your name as it appears on your unexpired student, work, or government-issued identification card.",
"account.settings.field.full.name.help.text.submitted": "Verification has been submitted. This usually takes 48 hours or less. Full name cannot be changed at this time.",
"account.settings.field.full.name.help.text.submitted.proctored": "Your proctored exam has been submitted. Full name cannot be changed at this time. Please check back in 2-5 days.",
"account.settings.field.full.name.help.text.submitted.certificate": "When identity verification is successful, this name will appear on your certificates and public-facing records. Full name cannot be changed at this time.",
"account.settings.field.full.name.help.text.submitted.proctored.certificate": "Once your proctored exam passes review, this name will appear on your certificates and public-facing records. Full name cannot be changed at this time.",
"account.settings.field.name.verified.success.message": "Your identity verification request has successfully completed. You now have the option of selecting which name you prefer to appear on your certificates and public-records.",
"account.settings.field.name.verified.success.message.header": "Your name change request is complete!",
"account.settings.field.name.verified.failure.message": "Your most recent identity verification attempt did not pass. Related account settings have been restored.",
"account.settings.field.name.verified.failure.message.header": "We were not able to verify your identity.",
"account.settings.field.name.verified.failure.message.help.link": "Learn more about ID verification",
"account.settings.field.name.verified.submitted.message": "Your identity verification request has been submitted and usually takes between 24 and 48 hours to complete.",
"account.settings.field.name.verified.submitted.message.certificate": "When your request is approved, your updated name will appear on all associated certificates and public-facing records.",
"account.settings.field.name.verified.submitted.message.header": "Your name change request is almost complete!",
"account.settings.field.email": "Email address (Sign in)",
"account.settings.field.email.empty": "Add email address",
"account.settings.field.email.confirmation": "Weve sent a confirmation message to {value}. Click the link in the message to update your email address.",
"account.settings.field.email.help.text": "You receive messages from {siteName} and course teams at this address.",
"account.settings.field.secondary.email": "Recovery email address",
"account.settings.field.secondary.email.empty": "Add a recovery email address",
"account.settings.field.secondary.email.confirmation": "Weve sent a confirmation message to {value}. Click the link in the message to update your recovery email address.",
"account.settings.email.field.confirmation.header": "Pending confirmation",
"account.settings.field.dob": "Year of birth",
"account.settings.field.dob.empty": "Add year of birth",
"account.settings.field.year_of_birth.options.empty": "Select a year of birth",
"account.settings.field.dob.month": "Month",
"account.settings.field.dob.year": "Year",
"account.settings.field.month.year.default": "Select month",
"account.settings.field.dob.year.default": "Select year",
"account.settings.field.dob.form.button": "Please confirm your date of birth",
"account.settings.field.dob.form.title": "Enter your birth month and year",
"account.settings.field.dob.form.help.text": "We ask for birth month and year information to help us comply with our legal obligations.",
"account.settings.field.dob.form.success": "Thank you for entering your information.",
"account.settings.field.month_of_birth.options.empty": "Select a month of birth",
"account.settingsfield.dob.error.general": "A technical error occurred. Please try again.",
"account.settings.field.country": "Country",
"account.settings.field.country.empty": "Add country",
"account.settings.field.country.options.empty": "Select a Country",
"account.settings.field.state": "State",
"account.settings.field.state.empty": "Add state",
"account.settings.field.state.options.empty": "Select a State",
"account.settings.field.site.language": "Site language",
"account.settings.field.site.language.help.text": "The language used throughout this site. This site is currently available in a limited number of languages.",
"account.settings.field.education": "Education",
"account.settings.field.education.empty": "Add level of education",
"account.settings.field.education.levels.empty": "Select a level of education",
"account.settings.field.education.levels.p": "Doctorate",
"account.settings.field.education.levels.m": "Master's or professional degree",
"account.settings.field.education.levels.b": "Bachelor's Degree",
"account.settings.field.education.levels.a": "Associate's degree",
"account.settings.field.education.levels.hs": "Secondary/high school",
"account.settings.field.education.levels.jhs": "Junior secondary/junior high/middle school",
"account.settings.field.education.levels.el": "Elementary/primary school",
"account.settings.field.education.levels.none": "No formal education",
"account.settings.field.education.levels.other": "Other education",
"account.settings.field.gender": "Gender",
"account.settings.field.gender.empty": "Add gender",
"account.settings.field.gender.options.empty": "Select a gender",
"account.settings.field.gender.options.f": "Female",
"account.settings.field.gender.options.m": "Male",
"account.settings.field.gender.options.o": "Other",
"account.settings.field.language.proficiencies": "Spoken language",
"account.settings.field.language.proficiencies.empty": "Add a spoken language",
"account.settings.field.language_proficiencies.options.empty": "Select a Language",
"account.settings.field.time.zone": "Time zone",
"account.settings.field.time.zone.empty": "Set time zone",
"account.settings.field.time.zone.description": "Select the time zone for displaying course dates. If you do not specify a time zone, course dates, including assignment deadlines, will be displayed in your browsers local time zone.",
"account.settings.field.time.zone.default": "Default (Local Time Zone)",
"account.settings.field.time.zone.all": "All time zones",
"account.settings.field.time.zone.country": "Country time zones",
"account.settings.section.social.media": "Social Media Links",
"account.settings.section.social.media.description": "Optionally, link your personal accounts to the social media icons on your {siteName} profile.",
"account.settings.field.social.platform.name.linkedin": "LinkedIn",
"account.settings.field.social.platform.name.linkedin.empty": "Add LinkedIn profile",
"account.settings.jump.nav.delete.account": "Delete My Account",
"account.settings.field.social.platform.name.twitter": "Twitter",
"account.settings.field.social.platform.name.twitter.empty": "Add Twitter profile",
"account.settings.field.social.platform.name.facebook": "Facebook",
"account.settings.field.social.platform.name.facebook.empty": "Add Facebook profile",
"account.settings.editable.field.action.save": "Save",
"account.settings.editable.field.action.cancel": "Cancel",
"account.settings.editable.field.action.edit": "Edit",
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
"account.settings.static.field.empty.no.admin": "No value set.",
"notification.preferences.notifications.label": "Notifications",
"error.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
"account.page.title": "Account | {siteName}",
"id.verification.access.blocked.denied": "We cannot verify your identity at this time. If you have yet to activate your account, please check your spam folder for the activation email from {email}.",
"id.verification.next": "Next",
"id.verification.support": "support",
"id.verification.example.card.alt": "Example of a valid identification card with a full name and photo.",
"id.verification.requirements.title": "Photo Verification Requirements",
"id.verification.requirements.description": "In order to complete Photo Verification, you will need the following:",
"id.verification.requirements.card.device.title": "Device with Camera",
"id.verification.requirements.card.device.allow": "Allow",
"id.verification.requirements.card.id.title": "Photo Identification Card",
"id.verification.requirements.card.id.text": "You need a valid identification card that contains your full name and photo, such as a drivers license or passport.",
"id.verification.privacy.title": "Privacy Information",
"id.verification.privacy.need.photo.question": "Why does {siteName} need my photo?",
"id.verification.privacy.need.photo.answer": "We use your verification photos to confirm your identity and ensure the validity of your certificate.",
"id.verification.privacy.do.with.photo.question": "What does {siteName} do with this photo?",
"id.verification.privacy.do.with.photo.answer": "We securely encrypt your photo and send it our authorization service for review. Your photo and information are not saved or visible anywhere on {siteName} after the verification process is complete.",
"id.verification.access.blocked.title": "Identity Verification",
"id.verification.access.blocked.enrollment": "You are not currently enrolled in a course that requires identity verification.",
"id.verification.access.blocked.pending": "You have already submitted your verification information. You will see a message on your dashboard when the verification process is complete (usually within 5 days).",
"id.verification.photo.take": "Take Photo",
"id.verification.photo.retake": "Retake Photo?",
"id.verification.photo.enable.detection": "Enable Face Detection",
"id.verification.photo.enable.detection.portrait.help.text": "If checked, a box will appear around your face. Your face can be seen clearly if the box around it is blue. If your face is not in a good position or undetectable, the box will be red.",
"id.verification.photo.enable.detection.id.help.text": "If checked, a box will appear around the face on your ID card. The face can be seen clearly if the box around it is blue. If the face is not in a good position or undetectable, the box will be red.",
"id.verification.photo.feedback.correct": "Face is in a good position.",
"id.verification.photo.feedback.two.faces": "More than one face detected.",
"id.verification.photo.feedback.no.faces": "No face detected.",
"id.verification.photo.feedback.top.left": "Incorrect position. Top left.",
"id.verification.photo.feedback.top.center": "Incorrect position. Top center.",
"id.verification.photo.feedback.top.right": "Incorrect position. Top right.",
"id.verification.photo.feedback.center.left": "Incorrect position. Center left.",
"id.verification.photo.feedback.center.center": "Incorrect position. Too close to camera.",
"id.verification.photo.feedback.center.right": "Incorrect position. Center right.",
"id.verification.photo.feedback.bottom.left": "Incorrect position. Bottom left.",
"id.verification.photo.feedback.bottom.center": "Incorrect position. Bottom center.",
"id.verification.photo.feedback.bottom.right": "Incorrect position. Bottom right.",
"id.verification.camera.access.title": "Camera Permissions",
"id.verification.camera.access.title.success": "Camera Access Enabled",
"id.verification.camera.access.title.failed": "Camera Access Failed",
"id.verification.camera.access.click.allow": "Please make sure to click \"Allow\"",
"id.verification.camera.access.enable": "Enable Camera",
"id.verification.camera.access.problems": "Having problems?",
"id.verification.camera.access.skip": "Skip and upload image files instead",
"id.verification.camera.access.success": "Looks like your camera is working and ready.",
"id.verification.camera.access.failure": "It looks like we're unable to access your camera. You will need to upload image files of you and your photo id.",
"id.verification.camera.access.failure.temporary": "It looks like we're unable to access your camera. Please verify that your webcam is connected and that you have allowed your browser to access it.",
"id.verification.camera.access.failure.temporary.chrome": "To enable camera access in Chrome:",
"id.verification.camera.access.failure.temporary.chrome.step1": "Open Chrome.",
"id.verification.camera.access.failure.temporary.chrome.step2": "Navigate to More > Settings.",
"id.verification.camera.access.failure.temporary.chrome.step2.windows": "For Windows: Alt+F, Alt+E, or F10 followed by the spacebar",
"id.verification.camera.access.failure.temporary.chrome.step2.mac": "For Mac: Command+,",
"id.verification.camera.access.failure.temporary.chrome.step3": "Under the \"Privacy and security\" tab, select \"Site Settings\" and then \"Camera.\"",
"id.verification.camera.access.failure.temporary.chrome.step4": "Under \"Blocked,\" find \"edx.org\" and select it.",
"id.verification.camera.access.failure.temporary.chrome.step5": "In the \"Permissions\" section, update the camera permissions to \"Allow.\"",
"id.verification.camera.access.failure.temporary.ie11": "To enable camera access in Internet Explorer:",
"id.verification.camera.access.failure.temporary.ie11.step1": "Open the Flash Player Settings Manager by navigating to Windows Settings > Control Panel > Flash Player.",
"id.verification.camera.access.failure.temporary.ie11.step2": "Select the \"Camera and Mic\" tab, and then select the \"Camera and Microphone Settings by Site\" button.",
"id.verification.camera.access.failure.temporary.ie11.step3": "Choose \"edx.org\" from the list of websites and change the permissions by selecting \"Allow\" in the dropdown menu.",
"id.verification.camera.access.failure.temporary.firefox": "To enable camera access in Firefox:",
"id.verification.camera.access.failure.temporary.firefox.step1": "Open Firefox.",
"id.verification.camera.access.failure.temporary.firefox.step2": "Enter \"about:preferences\" in the URL bar.",
"id.verification.camera.access.failure.temporary.firefox.step3": "Select the \"Privacy & Security\" tab, and navigate to the \"Permissions\" section.",
"id.verification.camera.access.failure.temporary.firefox.step4": "Next to \"Camera,\" select the \"Settings…\" button.",
"id.verification.camera.access.failure.temporary.firefox.step5": "In the search bar, enter \"edx.org.\"",
"id.verification.camera.access.failure.temporary.firefox.step6": "In the status column for \"edx.org,\" select \"Allow\" from the drop down.",
"id.verification.camera.access.failure.temporary.firefox.step7": "Select \"Save Changes.\"",
"id.verification.camera.access.failure.temporary.safari": "To enable camera access in Safari:",
"id.verification.camera.access.failure.temporary.safari.step1": "Open Safari.",
"id.verification.camera.access.failure.temporary.safari.step2": "Click on the Safari app menu, then select \"Preferences.\" You can also use Command+, as a keyboard shortcut.",
"id.verification.camera.access.failure.temporary.safari.step3": "Select the \"Websites\" tab and then select \"Camera.\"",
"id.verification.camera.access.failure.temporary.safari.step4": "Select \"edx.org\" and change the camera permissions to \"Allow.\"",
"id.verification.camera.access.failure.unsupported": "It looks like your browser does not support camera access.",
"id.verification.camera.access.failure.unsupported.chrome.explanation": "The Chrome browser currently does not support camera access on iOS devices, such as iPhones and iPads.",
"id.verification.camera.access.failure.unsupported.instructions": "Please use another browser to complete Identity Verification.",
"id.verification.photo.tips.title": "Helpful Photo Tips",
"id.verification.photo.tips.description": "Next, we'll need you to take a photo of your face. Please review the helpful tips below.",
"id.verification.photo.tips.list.title": "Photo Tips",
"id.verification.photo.tips.list.description": "To take a successful photo, make sure that:",
"id.verification.photo.tips.list.well.lit": "Your face is well-lit.",
"id.verification.photo.tips.list.inside.frame": "Your entire face fits inside the frame.",
"id.verification.portrait.photo.title.camera": "Take a Photo of Yourself",
"id.verification.portrait.photo.instructions.camera": "When your face is in position, use the Take Photo button below to take your photo.",
"id.verification.camera.help.sight.question": "What if I can't see the camera image or if I can't see my photo to determine which side is visible?",
"id.verification.camera.help.sight.answer.portrait": "You may be able to complete the image capture procedure without assistance, but it may take a couple of submission attempts to get the camera positioning right. Optimal camera positioning varies with each computer, but generally the best position for a headshot is approximately 12-18 inches (30-45 centimeters) from the camera, with your head centered relative to the computer screen. If the photos you submit are rejected, try moving the computer or camera orientation to change the lighting angle.",
"id.verification.camera.help.sight.answer.id": "You may be able to complete the image capture procedure without assistance, but it may take a couple of submission attempts to get the camera positioning right. Optimal camera positioning varies with each computer, but generally, the best position for a photo of an ID card is 8-12 inches (20-30 centimeters) from the camera, with the ID card centered relative to the camera. If the photos you submit are rejected, try moving the computer or camera orientation to change the lighting angle. The most common reason for rejection is inability to read the text on the ID card.",
"id.verification.camera.help.difficulty.question.portrait": "What if I have difficulty holding my head in position relative to the camera?",
"id.verification.camera.help.difficulty.question.id": "What if I have difficulty holding my ID in position relative to the camera?",
"id.verification.camera.help.difficulty.answer": "If you require assistance with taking a photo for submission, contact {siteName} support for additional suggestions.",
"id.verification.id.photo.unclear.question": "Is your ID card image not clear or too blurry?",
"id.verification.id.tips.title": "Helpful Identification Card Tips",
"id.verification.id.tips.description": "Next, we'll need you to take a photo of a valid identification card that includes your full name and photo, such as a drivers license or passport. Please have your ID ready.",
"id.verification.id.tips.list.well.lit": "Your identification card is well-lit.",
"id.verification.id.tips.list.clear": "Ensure that you can see your photo and clearly read your name.",
"id.verification.id.photo.title.camera": "Take a Photo of Your Identification Card",
"id.verification.id.photo.title.upload": "Upload a Photo of Your Identification Card",
"id.verification.id.photo.preview.alt": "Preview of photo ID.",
"id.verification.id.photo.instructions.camera": "When your ID is in position, use the Take Photo button below to take your photo. Please use a passport, drivers license, or another identification card that includes your full name and a picture of your face.",
"id.verification.id.photo.instructions.upload": "Please upload a photo of your identification card. Ensure the entire ID fits inside the frame and is well-lit. The file size must be under 10 MB. Supported formats:",
"id.verification.id.photo.instructions.upload.error.invalidFileType": "The file you have selected is not a supported image type. Please choose from the following formats:",
"id.verification.id.photo.instructions.upload.error.fileTooLarge": "The file you have selected is too large. Please try again with a file less than 10MB.",
"id.verification.name.check.title": "Double-Check Your Name",
"id.verification.name.check.instructions": "Does the name below match the name on your photo ID? If not, update the name below to match your photo ID.",
"id.verification.name.check.mismatch.information": "If the name below does not match your photo ID, your identity verification will be denied.",
"id.verification.name.error": "Please enter your name as it appears on your photo ID.",
"id.verification.account.name.warning.prefix": "Please Note:",
"id.verification.account.name.settings": "Account Settings",
"id.verification.name.label": "Name",
"id.verification.account.name.photo.alt": "Photo of your ID to be submitted.",
"id.verification.review.title": "Review Your Photos",
"id.verification.review.description": "Make sure we can verify your identity with the photos and information you have provided.",
"id.verification.review.portrait.label": "Your Portrait",
"id.verification.review.portrait.alt": "Photo of your face to be submitted.",
"id.verification.review.portrait.retake": "Retake Portrait Photo",
"id.verification.review.id.label": "Your Identification Card",
"id.verification.review.id.alt": "Photo of your identification card to be submitted.",
"id.verification.review.id.retake": "Retake ID Photo",
"id.verification.review.confirm": "Submit",
"id.verification.submission.alert.error.face": "A photo of your face is required. Please retake your portrait photo.",
"id.verification.submission.alert.error.id": "A photo of your ID card is required. Please retake your ID photo.",
"id.verification.submission.alert.error.name": "A valid account name is required. Please update your account name to match the name on your ID.",
"id.verification.submission.alert.error.unsupported": "One or more of the files you have uploaded is in an unsupported format. Please choose from the following:",
"id.verification.review.error": "{siteName} Support Page",
"id.verification.submitted.title": "Identity Verification in Progress",
"id.verification.submitted.text": "We have received your information and are verifying your identity. You will be notified when the verification process is complete (usually within 5 days). In the meantime, you can still access all available course content.",
"id.verification.return.dashboard": "Return to Your Dashboard",
"id.verification.return.course": "Return to Course",
"id.verification.return.generic": "Return",
"id.verification.photo.upload.help.title": "Upload a Photo Instead",
"id.verification.photo.camera.help.title": "Use Your Camera Instead",
"id.verification.photo.upload.help.text": "If you are having trouble using the photo capture above, you may want to upload a photo instead. To upload a photo, click the button below.",
"id.verification.photo.camera.help.text": "If you are having trouble uploading a photo above, you may want to use your camera instead. To use your camera, click the button below.",
"id.verification.upload.help.button": "Switch to Upload Mode",
"id.verification.camera.help.button": "Switch to Camera Mode",
"notification.preference.heading": "Notifications",
"account.settings.message.duplicate.tpa.provider": "आपके द्वारा चुना गया {provider} खाता पहले से ही दूसरे {siteName} खाते से लिंक है।",
"account.settings.message.managed.settings": "आपकी प्रोफ़ाइल सेटिंग्स {managerTitle} द्वारा प्रबंधित की जाती हैं। सहायता के लिए अपने प्रशासक या {support} से संपर्क करें।",
"account.settings.message.managed.settings.support": "सहायता",
"account.settings.page.heading": "खाता सेटिंग्स",
"account.settings.loading.message": "लोड हो रहा है...",
"account.settings.loading.error": "त्रुटि: {error}",
"account.settings.banner.beta.language": "आपने अपनी भाषा को {beta_language} पर सेट किया है, जो वर्तमान में पूरी तरह से अनुवादित नहीं है। आप ट्रांसिफ़ेक्स समुदाय में शामिल होकर और {beta_language} बोलने वाले शिक्षार्थियों के लिए अंग्रेजी से अनुवाद जोड़कर इस भाषा का पूर्ण अनुवाद करने में हमारी सहायता कर सकते हैं।",
"account.settings.banner.beta.language.action.switch.back": "वापस {previous_language} पर स्विच करें",
"account.settings.banner.beta.language.action.help.translate": "{beta_language} में अनुवाद करने में सहायता करें",
"account.settings.section.account.information": "खाता संबंधी जानकारी",
"account.settings.section.account.information.description": "इन सेटिंग्स में आपके खाते के बारे में मूलभूत जानकारी शामिल है।",
"account.settings.section.profile.information": "प्रोफाइल की जानकारी",
"account.settings.section.demographics.information": "वैकल्पिक जानकारी",
"account.settings.section.site.preferences": "साइट प्राथमिकताएँ",
"account.settings.section.linked.accounts": "जुड़े हुए खाते",
"account.settings.section.linked.accounts.description": "{siteName} में साइन इन करना आसान बनाने के लिए आप अपने पहचान खातों को लिंक कर सकते हैं।",
"account.settings.field.username": "उपयोगकर्ता",
"account.settings.field.username.help.text": "वह नाम जो आपको {siteName} पर पहचानता है। आप अपना उपयोक्तानाम नहीं बदल सकते।",
"account.settings.field.full.name": "पूरा नाम",
"account.settings.field.full.name.empty": "नाम जोड़ें",
"account.settings.field.full.name.help.text": "वह नाम जो आईडी सत्यापन के लिए उपयोग किया जाता है और जो आपके प्रमाणपत्रों पर दिखाई देता है।",
"account.settings.field.full.name.help.text.default": "वह नाम जो आपके सार्वजनिक प्रोफ़ाइल पर दिखाई देता है।",
"account.settings.field.full.name.help.text.default.certificate": "यह नाम आपके प्रमाणपत्रों और सार्वजनिक-सामना वाले रिकॉर्ड पर प्रदर्शित होने के लिए चुना गया है।",
"account.settings.field.name.verified": "सत्यापित नाम",
"account.settings.field.name.verified.help.text.verified": "इस नाम को फ़ोटो आईडी द्वारा सत्यापित किया गया है।",
"account.settings.field.name.verified.help.text.verified.proctored": "यह नाम प्रोक्टोरिंग द्वारा सत्यापित किया गया है।",
"account.settings.field.name.verified.help.text.verified.certificate": "यह नाम फोटो आईडी द्वारा सत्यापित किया गया है, और आपके प्रमाणपत्रों और सार्वजनिक-सामना वाले रिकॉर्ड पर प्रदर्शित होने के लिए चुना गया है।",
"account.settings.field.name.verified.help.text.verified.proctored.certificate": "इस नाम को प्रॉक्टरिंग द्वारा सत्यापित किया गया है, और इसे आपके प्रमाणपत्रों और सार्वजनिक-सामना वाले रिकॉर्ड पर प्रदर्शित होने के लिए चुना गया है।",
"account.settings.field.name.verified.help.text.submitted": "सत्यापन प्रस्तुत कर दिया गया है। इसमें आमतौर पर 48 घंटे या उससे कम समय लगता है। सत्यापित नाम इस समय नहीं बदला जा सकता।",
"account.settings.field.name.verified.help.text.submitted.proctored": "आपकी प्रॉक्टर्ड परीक्षा सबमिट कर दी गई है। सत्यापित नाम इस समय नहीं बदला जा सकता। कृपया 2-5 दिनों में दोबारा जांचें।",
"account.settings.field.name.verified.help.text.submitted.certificate": "जब पहचान सत्यापन सफल हो जाएगा, तो यह नाम आपके प्रमाणपत्रों और सार्वजनिक-सामना वाले रिकॉर्ड पर दिखाई देगा। सत्यापित नाम इस समय नहीं बदला जा सकता।",
"account.settings.field.name.verified.help.text.submitted.proctored.certificate": "एक बार जब आपकी प्रॉक्टर परीक्षा समीक्षा में उत्तीर्ण हो जाती है, तो यह नाम आपके प्रमाणपत्र और सार्वजनिक-सामना वाले रिकॉर्ड पर दिखाई देगा। सत्यापित नाम इस समय नहीं बदला जा सकता।",
"account.settings.field.name.verified.verification.help": "अपना नाम दर्ज करें जैसा कि यह आपके अप्रयुक्त छात्र, कार्य या सरकार द्वारा जारी पहचान पत्र पर दिखाई देता है।",
"account.settings.field.full.name.help.text.submitted": "सत्यापन प्रस्तुत कर दिया गया है। इसमें आमतौर पर 48 घंटे या उससे कम समय लगता है। पूरा नाम इस समय नहीं बदला जा सकता।",
"account.settings.field.full.name.help.text.submitted.proctored": "आपकी प्रॉक्टर्ड परीक्षा सबमिट कर दी गई है। इस समय पूरा नाम नहीं बदला जा सकता। कृपया 2-5 दिनों में दोबारा जांचें।",
"account.settings.field.full.name.help.text.submitted.certificate": "जब पहचान सत्यापन सफल हो जाएगा, तो यह नाम आपके प्रमाणपत्रों और सार्वजनिक-सामना वाले रिकॉर्ड पर दिखाई देगा। इस समय पूरा नाम नहीं बदला जा सकता।",
"account.settings.field.full.name.help.text.submitted.proctored.certificate": "एक बार जब आपकी प्रॉक्टर परीक्षा समीक्षा में उत्तीर्ण हो जाती है, तो यह नाम आपके प्रमाणपत्रों और सार्वजनिक-सामना वाले रिकॉर्ड पर दिखाई देगा। इस समय पूरा नाम नहीं बदला जा सकता।",
"account.settings.field.name.verified.success.message": "आपकी पहचान सत्यापन अनुरोध सफलतापूर्वक पूरा हुआ है। अब आपके पास यह विकल्प है कि आप प्रमाणपत्रों और सार्वजनिक रिकॉर्ड में दिखने वाला नाम कौन सा चुनना पसंद करते हैं।",
"account.settings.field.name.verified.success.message.header": "आपका नाम परिवर्तन अनुरोध पूरा हो गया है!",
"account.settings.field.name.verified.failure.message": "आपका नवीनतम पहचान सत्यापन प्रयास सफल नहीं हुआ। संबंधित खाता सेटिंग पुनर्स्थापित कर दी गई हैं।",
"account.settings.field.name.verified.failure.message.header": "हम आपकी पहचान सत्यापित करने में सक्षम नहीं थे।",
"account.settings.field.name.verified.failure.message.help.link": "आईडी सत्यापन के बारे में और अधिक जानें",
"account.settings.field.name.verified.submitted.message": "आपका पहचान सत्यापन अनुरोध सबमिट कर दिया गया है और इसे पूरा होने में आमतौर पर 24 से 48 घंटे लगते हैं।",
"account.settings.field.name.verified.submitted.message.certificate": "जब आपका अनुरोध स्वीकृत हो जाता है, तो आपका अद्यतन नाम सभी संबद्ध प्रमाणपत्रों और सार्वजनिक-सामना वाले रिकॉर्ड पर दिखाई देगा।",
"account.settings.field.name.verified.submitted.message.header": "आपका नाम परिवर्तन अनुरोध लगभग पूरा हो गया है!",
"account.settings.field.email": "ईमेल पता (साइन इन करें)",
"account.settings.field.email.empty": "ईमेल पता जोड़ें",
"account.settings.field.email.confirmation": "हमने {value} पर एक पुष्टिकरण संदेश भेजा है। अपना ईमेल पता अपडेट करने के लिए संदेश में दिए गए लिंक पर क्लिक करें।",
"account.settings.field.email.help.text": "आपको इस पते पर {siteName} और पाठ्यक्रम टीमों से संदेश प्राप्त होते हैं।",
"account.settings.field.secondary.email": "रिकवरी ईमेल पता",
"account.settings.field.secondary.email.empty": "एक रिकवरी ईमेल पता जोड़ें",
"account.settings.field.secondary.email.confirmation": "हमने {value} पर एक पुष्टिकरण संदेश भेजा है। अपना रिकवरी ईमेल पता अपडेट करने के लिए संदेश में दिए गए लिंक पर क्लिक करें।",
"account.settings.email.field.confirmation.header": "पुष्टिकरण लंबित है",
"account.settings.field.dob": "जन्म का वर्ष",
"account.settings.field.dob.empty": "जन्म का वर्ष जोड़ें",
"account.settings.field.year_of_birth.options.empty": "जन्म का वर्ष चुनें",
"account.settings.field.dob.month": "महीना",
"account.settings.field.dob.year": "वर्ष",
"account.settings.field.month.year.default": "महीना चुनें",
"account.settings.field.dob.year.default": "वर्ष चुनें",
"account.settings.field.dob.form.button": "कृपया अपनी जन्मतिथि की पुष्टि करें",
"account.settings.field.dob.form.title": "अपना जन्म माह और वर्ष दर्ज करें",
"account.settings.field.dob.form.help.text": "हम अपने कानूनी दायित्वों का पालन करने में मदद के लिए जन्म माह और वर्ष की जानकारी मांगते हैं।",
"account.settings.field.dob.form.success": "आपकी जानकारी दर्ज करने के लिए धन्यवाद।",
"account.settings.field.month_of_birth.options.empty": "जन्म का महीना चुनें",
"account.settingsfield.dob.error.general": "एक तकनीकी त्रुटि हुई। कृपया दोबारा प्रयास करें।",
"account.settings.field.country": "देश",
"account.settings.field.country.empty": "कंट्री एड करे",
"account.settings.field.country.options.empty": "एक देश का चयन करें",
"account.settings.field.state": "राज्य",
"account.settings.field.state.empty": "राज्य जोड़ें",
"account.settings.field.state.options.empty": "एक राज्य का चयन करें",
"account.settings.field.site.language": "साइट भाषा",
"account.settings.field.site.language.help.text": "इस साइट पर उपयोग की जाने वाली भाषा। यह साइट वर्तमान में सीमित संख्या में भाषाओं में उपलब्ध है।",
"account.settings.field.education": "शिक्षा",
"account.settings.field.education.empty": "शिक्षा का स्तर जोड़ें",
"account.settings.field.education.levels.empty": "एक शिक्षा स्तर का चयन करें",
"account.settings.field.education.levels.p": "डॉक्टरेट",
"account.settings.field.education.levels.m": "मास्टर की या पेशेवर डिग्री",
"account.settings.field.education.levels.b": "बैचलर्स डिग्री",
"account.settings.field.education.levels.a": "सहयोगी की डिग्री",
"account.settings.field.education.levels.hs": "माध्यमिक/उच्च माध्यमिक विद्यालय",
"account.settings.field.education.levels.jhs": "जूनियर माध्यमिक/जूनियर हाई/मिडिल स्कूल",
"account.settings.field.education.levels.el": "प्राथमिक/प्राथमिक विद्यालय",
"account.settings.field.education.levels.none": "कोई औपचारिक शिक्षा नहीं",
"account.settings.field.education.levels.other": "अन्य शिक्षा",
"account.settings.field.gender": "लिंग",
"account.settings.field.gender.empty": "लिंग जोड़ें",
"account.settings.field.gender.options.empty": "एक लिंग का चयन करें",
"account.settings.field.gender.options.f": "महिला",
"account.settings.field.gender.options.m": "पुरुष",
"account.settings.field.gender.options.o": "अन्य",
"account.settings.field.language.proficiencies": "बोली गई भाषा",
"account.settings.field.language.proficiencies.empty": "एक बोली जाने वाली भाषा जोड़ें",
"account.settings.field.language_proficiencies.options.empty": "भाषा चुनें",
"account.settings.field.time.zone": "समय क्षेत्र",
"account.settings.field.time.zone.empty": "समय क्षेत्र सेट करें",
"account.settings.field.time.zone.description": "पाठ्यक्रम तिथियाँ प्रदर्शित करने के लिए समय क्षेत्र का चयन करें। यदि आप समय क्षेत्र निर्दिष्ट नहीं करते हैं, तो असाइनमेंट की समय सीमा सहित पाठ्यक्रम तिथियां, आपके ब्राउज़र के स्थानीय समय क्षेत्र में प्रदर्शित की जाएंगी।",
"account.settings.field.time.zone.default": "डिफ़ॉल्ट (स्थानीय समय क्षेत्र)",
"account.settings.field.time.zone.all": "सभी समय क्षेत्र",
"account.settings.field.time.zone.country": "देश समय क्षेत्र",
"account.settings.section.social.media": "सोशल मीडिया लिंक",
"account.settings.section.social.media.description": "वैकल्पिक रूप से, अपने व्यक्तिगत खातों को अपनी {siteName} प्रोफ़ाइल पर सोशल मीडिया आइकन से लिंक करें।",
"account.settings.field.social.platform.name.linkedin": "लिंक्डइन",
"account.settings.field.social.platform.name.linkedin.empty": "लिंक्डइन प्रोफ़ाइल जोड़ें",
"account.settings.jump.nav.delete.account": "मेरा खाता हटाएं",
"account.settings.field.social.platform.name.twitter": "ट्विटर",
"account.settings.field.social.platform.name.twitter.empty": "ट्विटर प्रोफ़ाइल जोड़ें",
"account.settings.field.social.platform.name.facebook": "फेसबुक",
"account.settings.field.social.platform.name.facebook.empty": "फेसबुक प्रोफ़ाइल जोड़ें",
"account.settings.editable.field.action.save": "सहेजें",
"account.settings.editable.field.action.cancel": "रद्द करना",
"account.settings.editable.field.action.edit": "संपादित करें",
"account.settings.static.field.empty": "कोई मूल्य निर्धारित नहीं। परिवर्तन करने के लिए अपने {enterprise} व्यवस्थापक से संपर्क करें।",
"account.settings.static.field.empty.no.admin": "कोई मान्यता सेट नहीं।",
"notification.preferences.notifications.label": "अधिसूचनाएं",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "आप जो पृष्ठ खोज रहे हैं वह अनुपलब्ध है या URL में कोई त्रुटि है। कृपया यूआरएल जांचें और पुनः प्रयास करें।",
"account.page.title": "खाता | {siteName}",
"id.verification.access.blocked.denied": "हम इस समय आपकी पहचान सत्यापित नहीं कर सकते। यदि आपने अभी तक अपना खाता सक्रिय नहीं किया है, तो कृपया {email} से सक्रियण ईमेल के लिए अपना स्पैम फ़ोल्डर जांचें।",
"id.verification.next": "अगला",
"id.verification.support": "सहायता",
"id.verification.example.card.alt": "एक मान्य पहचान पत्र का उदाहरण जिसमें पूरा नाम और फ़ोटो होती है।",
"id.verification.requirements.title": "फोटो सत्यापन आवश्यकताएँ",
"id.verification.requirements.description": "फोटो सत्यापन पूरा करने के लिए, आपको निम्नलिखित की आवश्यकता होगी:",
"id.verification.requirements.card.device.title": "कैमरे के साथ डिवाइस",
"id.verification.requirements.card.device.allow": "अनुमति",
"id.verification.requirements.card.id.title": "फोटो पहचान पत्र",
"id.verification.requirements.card.id.text": "आपको एक वैध पहचान पत्र की आवश्यकता है जिसमें आपका पूरा नाम और फोटो हो, जैसे ड्राइवर का लाइसेंस या पासपोर्ट।",
"id.verification.privacy.title": "गोपनीयता सूचना",
"id.verification.privacy.need.photo.question": "{siteName} को मेरे फ़ोटो की आवश्यकता क्यों है?",
"id.verification.privacy.need.photo.answer": "हम आपकी पहचान की पुष्टि करने और आपके प्रमाणपत्र की वैधता सुनिश्चित करने के लिए आपके सत्यापन फ़ोटो का उपयोग करते हैं।",
"id.verification.privacy.do.with.photo.question": "इस फोटो के साथ {siteName} क्या करता है?",
"id.verification.privacy.do.with.photo.answer": "हम आपकी तस्वीर को सुरक्षित रूप से एन्क्रिप्ट करते हैं और इसे समीक्षा के लिए हमारी प्राधिकरण सेवा में भेजते हैं। सत्यापन प्रक्रिया पूरी होने के बाद आपकी फ़ोटो और जानकारी {siteName} पर कहीं भी सहेजी या दिखाई नहीं देगी।",
"id.verification.access.blocked.title": "पहचान की जाँच",
"id.verification.access.blocked.enrollment": "आप वर्तमान में ऐसे पाठ्यक्रम में नामांकित नहीं हैं जिसके लिए पहचान सत्यापन की आवश्यकता है।",
"id.verification.access.blocked.pending": "आपने अपनी सत्यापन जानकारी पहले ही सबमिट कर दी है। सत्यापन प्रक्रिया पूरी होने पर (आमतौर पर 5 दिनों के भीतर) आपको अपने डैशबोर्ड पर एक संदेश दिखाई देगा।",
"id.verification.photo.take": "तस्वीर खींचें",
"id.verification.photo.retake": "फ़ोटो दोबारा लें?",
"id.verification.photo.enable.detection": "फेस डिटेक्शन सक्षम करें",
"id.verification.photo.enable.detection.portrait.help.text": "यदि चेक किया जाए तो आपके चेहरे के चारों ओर एक बॉक्स दिखाई देगा। यदि इसके चारों ओर का बॉक्स नीला है तो आपका चेहरा स्पष्ट रूप से देखा जा सकता है। यदि आपका चेहरा अच्छी स्थिति में नहीं है या पता नहीं चल पा रहा है, तो बॉक्स लाल हो जाएगा।",
"id.verification.photo.enable.detection.id.help.text": "यदि चेक किया गया है, तो आपके आईडी कार्ड पर चेहरे के चारों ओर एक बॉक्स दिखाई देगा। यदि उसके चारों ओर का बॉक्स नीला है तो चेहरा स्पष्ट रूप से देखा जा सकता है। यदि चेहरा अच्छी स्थिति में नहीं है या पता नहीं चल पा रहा है, तो बॉक्स लाल हो जाएगा।",
"id.verification.photo.feedback.correct": "चेहरा एक अच्छी स्थिति में है।",
"id.verification.photo.feedback.two.faces": "एक से अधिक चेहरा पहचाना गया।",
"id.verification.photo.feedback.no.faces": "किसी चेहरे का पता नहीं चला।",
"id.verification.photo.feedback.top.left": "गलत स्थिति। ऊपर बायीं ओर।",
"id.verification.photo.feedback.top.center": "गलत स्थिति। शीर्ष केंद्र।",
"id.verification.photo.feedback.top.right": "गलत स्थिति। ऊपर दाहिनी ओर।",
"id.verification.photo.feedback.center.left": "गलत स्थिति। केंद्र बाएं।",
"id.verification.photo.feedback.center.center": "गलत स्थिति। कैमरे के बहुत नजदीक।",
"id.verification.photo.feedback.center.right": "गलत स्थिति। दाईं ओर केन्द्रित करें।",
"id.verification.photo.feedback.bottom.left": "गलत स्थिति। नीचे बायीं ओर।",
"id.verification.photo.feedback.bottom.center": "गलत स्थिति। नीचे का केंद्र।",
"id.verification.photo.feedback.bottom.right": "गलत स्थान। नीचे दाएं।",
"id.verification.camera.access.title": "कैमरे की अनुमतियाँ",
"id.verification.camera.access.title.success": "कैमरा एक्सेस सक्षम",
"id.verification.camera.access.title.failed": "कैमरा एक्सेस विफल हुआ",
"id.verification.camera.access.click.allow": "कृपया सुनिश्चित करें कि \"Allow\" पर क्लिक करें",
"id.verification.camera.access.enable": "कैमरा सक्षम करें",
"id.verification.camera.access.problems": "समस्याएं आ रही हैं?",
"id.verification.camera.access.skip": "इसके बजाय छवि फ़ाइलें छोड़ें और अपलोड करें",
"id.verification.camera.access.success": "लगता है कि आपका कैमरा काम कर रहा है और तैयार है।",
"id.verification.camera.access.failure": "ऐसा लगता है कि हम आपके कैमरे तक पहुंचने में असमर्थ हैं। आपको अपनी और अपनी फोटो आईडी की छवि फ़ाइलें अपलोड करनी होंगी।",
"id.verification.camera.access.failure.temporary": "ऐसा लगता है कि हम आपके कैमरे तक पहुंचने में असमर्थ हैं। कृपया सत्यापित करें कि आपका वेबकैम कनेक्ट है और आपने अपने ब्राउज़र को उस तक पहुंचने की अनुमति दी है।",
"id.verification.camera.access.failure.temporary.chrome": "क्रोम में कैमरा एक्सेस सक्षम करने के लिए:",
"id.verification.camera.access.failure.temporary.chrome.step1": "क्रोम को ओपेन करे।",
"id.verification.camera.access.failure.temporary.chrome.step2": "नेविगेट करें और अधिक > सेटिंग्स पर जाएं।",
"id.verification.camera.access.failure.temporary.chrome.step2.windows": "विंडोज़ के लिए: Alt+F, Alt+E, या F10 और उसके बाद स्पेसबार",
"id.verification.camera.access.failure.temporary.chrome.step2.mac": "मैक के लिए: कमांड+,",
"id.verification.camera.access.failure.temporary.chrome.step3": "\"गोपनीयता और सुरक्षा\" टैब के अंतर्गत, \"साइट सेटिंग्स\" और फिर \"कैमरा\" चुनें",
"id.verification.camera.access.failure.temporary.chrome.step4": "\"अवरुद्ध\" के अंतर्गत, \"edx.org\" ढूंढें और उसका चयन करें।",
"id.verification.camera.access.failure.temporary.chrome.step5": "\"अनुमतियाँ\" अनुभाग में, कैमरा अनुमतियों को \"अनुमति दें\" में अपडेट करें",
"id.verification.camera.access.failure.temporary.ie11": "कैमरा एक्सेस को इंटरनेट एक्सप्लोरर में सक्षम करने के लिए:",
"id.verification.camera.access.failure.temporary.ie11.step1": "विंडोज़ सेटिंग्स > कंट्रोल पैनल > फ़्लैश प्लेयर पर नेविगेट करके फ़्लैश प्लेयर सेटिंग्स मैनेजर खोलें।",
"id.verification.camera.access.failure.temporary.ie11.step2": "\"कैमरा और माइक\" टैब चुनें, और फिर \"साइट के अनुसार कैमरा और माइक्रोफ़ोन सेटिंग्स\" बटन चुनें।",
"id.verification.camera.access.failure.temporary.ie11.step3": "वेबसाइटों की सूची से \"edx.org\" चुनें और ड्रॉपडाउन मेनू में \"अनुमति दें\" का चयन करके अनुमतियाँ बदलें।",
"id.verification.camera.access.failure.temporary.firefox": "फ़ायरफ़ॉक्स में कैमरा एक्सेस सक्षम करने के लिए:",
"id.verification.camera.access.failure.temporary.firefox.step1": "फ़ायरफ़ॉक्स खोलें।",
"id.verification.camera.access.failure.temporary.firefox.step2": "URL बार में \"about:preferences\" दर्ज करें।",
"id.verification.camera.access.failure.temporary.firefox.step3": "\"गोपनीयता और सुरक्षा\" टैब चुनें, और \"अनुमतियाँ\" अनुभाग पर जाएँ।",
"id.verification.camera.access.failure.temporary.firefox.step4": "\"कैमरा\" के आगे, \"सेटिंग्स...\" बटन चुनें।",
"id.verification.camera.access.failure.temporary.firefox.step5": "सर्च बार में, \"edx.org\" दर्ज करें",
"id.verification.camera.access.failure.temporary.firefox.step6": "\"edx.org\" के स्थिति कॉलम में, ड्रॉप डाउन से \"अनुमति दें\" चुनें।",
"id.verification.camera.access.failure.temporary.firefox.step7": "\"परिवर्तन सहेजें\" चुनें",
"id.verification.camera.access.failure.temporary.safari": "Safari में कैमरा एक्सेस सक्षम करने के लिए:",
"id.verification.camera.access.failure.temporary.safari.step1": "ओपन सफारी।",
"id.verification.camera.access.failure.temporary.safari.step2": "सफ़ारी ऐप मेनू पर क्लिक करें, फिर \"प्राथमिकताएँ\" चुनें। आप Command+ का उपयोग कीबोर्ड शॉर्टकट के रूप में भी कर सकते हैं।",
"id.verification.camera.access.failure.temporary.safari.step3": "\"वेबसाइटें\" टैब चुनें और फिर \"कैमरा\" चुनें",
"id.verification.camera.access.failure.temporary.safari.step4": "\"edx.org\" चुनें और कैमरा अनुमतियों को \"अनुमति दें\" में बदलें",
"id.verification.camera.access.failure.unsupported": "ऐसा लगता है कि आपका ब्राउज़र कैमरा एक्सेस का समर्थन नहीं करता है।",
"id.verification.camera.access.failure.unsupported.chrome.explanation": "Chrome ब्राउज़र वर्तमान में iPhones और iPads जैसे iOS उपकरणों पर कैमरा एक्सेस का समर्थन नहीं करता है।",
"id.verification.camera.access.failure.unsupported.instructions": "कृपया पहचान सत्यापन पूरा करने के लिए किसी अन्य ब्राउज़र का उपयोग करें।",
"id.verification.photo.tips.title": "उपयोगी फोटो टिप्स",
"id.verification.photo.tips.description": "इसके बाद, हमें आपके चेहरे की एक तस्वीर लेने की आवश्यकता होगी। कृपया नीचे दी गई उपयोगी युक्तियों की समीक्षा करें।",
"id.verification.photo.tips.list.title": "फोटो टिप्स",
"id.verification.photo.tips.list.description": "एक सफल फोटो लेने के लिए, सुनिश्चित करें कि:",
"id.verification.photo.tips.list.well.lit": "आपका चेहरा अच्छी तरह से चमक रहा है।",
"id.verification.photo.tips.list.inside.frame": "आपका पूरा चेहरा फ्रेम के अंदर फिट बैठता है।",
"id.verification.portrait.photo.title.camera": "अपना एक फोटो लें",
"id.verification.portrait.photo.instructions.camera": "जब आपका चेहरा सही स्थिति में हो, तो अपना फोटो लेने के लिए नीचे दिए गए फोटो लें बटन का उपयोग करें।",
"id.verification.camera.help.sight.question": "यदि मैं कैमरे की छवि नहीं देख पाऊं या मैं यह निर्धारित करने के लिए अपनी तस्वीर नहीं देख पाऊं कि कौन सा पक्ष दिखाई दे रहा है तो क्या होगा?",
"id.verification.camera.help.sight.answer.portrait": "आप सहायता के बिना छवि कैप्चर प्रक्रिया को पूरा करने में सक्षम हो सकते हैं, लेकिन कैमरे की स्थिति को सही करने के लिए कुछ सबमिशन प्रयासों की आवश्यकता हो सकती है। कैमरे की इष्टतम स्थिति प्रत्येक कंप्यूटर के साथ अलग-अलग होती है, लेकिन आम तौर पर हेडशॉट के लिए सबसे अच्छी स्थिति कैमरे से लगभग 12-18 इंच (30-45 सेंटीमीटर) होती है, जिसमें आपका सिर कंप्यूटर स्क्रीन के सापेक्ष केंद्रित होता है। यदि आपके द्वारा सबमिट की गई तस्वीरें अस्वीकार कर दी जाती हैं, तो प्रकाश कोण को बदलने के लिए कंप्यूटर या कैमरा ओरिएंटेशन को स्थानांतरित करने का प्रयास करें।",
"id.verification.camera.help.sight.answer.id": "आप सहायता के बिना छवि कैप्चर प्रक्रिया को पूरा करने में सक्षम हो सकते हैं, लेकिन कैमरे की स्थिति को सही करने के लिए कुछ सबमिशन प्रयासों की आवश्यकता हो सकती है। इष्टतम कैमरा स्थिति प्रत्येक कंप्यूटर के साथ भिन्न होती है, लेकिन आम तौर पर, आईडी कार्ड की तस्वीर के लिए सबसे अच्छी स्थिति कैमरे से 8-12 इंच (20-30 सेंटीमीटर) होती है, जिसमें आईडी कार्ड कैमरे के सापेक्ष केंद्रित होता है। यदि आपके द्वारा सबमिट की गई तस्वीरें अस्वीकार कर दी जाती हैं, तो प्रकाश कोण को बदलने के लिए कंप्यूटर या कैमरा ओरिएंटेशन को स्थानांतरित करने का प्रयास करें। अस्वीकृति का सबसे आम कारण आईडी कार्ड पर पाठ को पढ़ने में असमर्थता है।",
"id.verification.camera.help.difficulty.question.portrait": "यदि मुझे कैमरे के सापेक्ष अपना सिर रखने में कठिनाई होती है तो क्या होगा?",
"id.verification.camera.help.difficulty.question.id": "यदि मुझे अपनी आईडी को कैमरे के सापेक्ष स्थिति में रखने में कठिनाई हो तो क्या होगा?",
"id.verification.camera.help.difficulty.answer": "यदि आपको सबमिशन के लिए फोटो लेने में सहायता की आवश्यकता है, तो अतिरिक्त सुझावों के लिए {siteName} समर्थन से संपर्क करें।",
"id.verification.id.photo.unclear.question": "क्या आपके आईडी कार्ड की छवि स्पष्ट या बहुत धुंधली नहीं है?",
"id.verification.id.tips.title": "सहायक पहचान पत्र नुस्खे",
"id.verification.id.tips.description": "इसके बाद, हमें आपसे एक वैध पहचान पत्र की फोटो लेने की आवश्यकता होगी जिसमें आपका पूरा नाम और फोटो शामिल हो, जैसे कि ड्राइवर का लाइसेंस या पासपोर्ट। कृपया अपनी आईडी तैयार रखें।",
"id.verification.id.tips.list.well.lit": "आपका पहचान पत्र अच्छी तरह से प्रकाशित है।",
"id.verification.id.tips.list.clear": "सुनिश्चित करें कि आप अपना फोटो देख सकें और अपना नाम स्पष्ट रूप से पढ़ सकें।",
"id.verification.id.photo.title.camera": "अपने पहचान पत्र का एक फोटो लें",
"id.verification.id.photo.title.upload": "अपने पहचान पत्र की एक फोटो अपलोड करें",
"id.verification.id.photo.preview.alt": "फोटो पहचान पत्र का पूर्वावलोकन।",
"id.verification.id.photo.instructions.camera": "जब आपकी आईडी स्थिति में हो, तो अपना फोटो लेने के लिए नीचे दिए गए फोटो लें बटन का उपयोग करें। कृपया पासपोर्ट, ड्राइवर का लाइसेंस, या किसी अन्य पहचान पत्र का उपयोग करें जिसमें आपका पूरा नाम और आपके चेहरे की तस्वीर शामिल हो।",
"id.verification.id.photo.instructions.upload": "कृपया अपने पहचान पत्र की एक फोटो अपलोड करें। सुनिश्चित करें कि संपूर्ण आईडी फ़्रेम के अंदर फिट हो और अच्छी रोशनी हो। फ़ाइल का आकार 10 एमबी से कम होना चाहिए। समर्थित प्रारूप:",
"id.verification.id.photo.instructions.upload.error.invalidFileType": "आपके द्वारा चुनी गई फ़ाइल समर्थित छवि प्रकार नहीं है। कृपया निम्नलिखित प्रारूपों में से चुनें:",
"id.verification.id.photo.instructions.upload.error.fileTooLarge": "आपके द्वारा चुनी गई फ़ाइल बहुत बड़ी है। कृपया 10 एमबी से कम फ़ाइल के साथ पुनः प्रयास करें।",
"id.verification.name.check.title": "अपने नाम की जांच दोबारा करें",
"id.verification.name.check.instructions": "क्या नीचे दिया गया नाम आपकी फोटो आईडी पर दिए गए नाम से मेल खाता है? यदि नहीं, तो अपनी फोटो आईडी से मिलान करने के लिए नीचे दिए गए नाम को अपडेट करें।",
"id.verification.name.check.mismatch.information": "यदि नीचे दिया गया नाम आपकी फोटो आईडी से मेल नहीं खाता है, तो आपकी पहचान सत्यापन अस्वीकार कर दिया जाएगा।",
"id.verification.name.error": "कृपया अपना नाम वही दर्ज करें जो आपकी फोटो आईडी पर दिखाई देता है।",
"id.verification.account.name.warning.prefix": "कृपया ध्यान दें:",
"id.verification.account.name.settings": "खाता सेटिंग्स",
"id.verification.name.label": "नाम",
"id.verification.account.name.photo.alt": "अपनी आईडी का फोटो जमा करना होगा।",
"id.verification.review.title": "अपनी तस्वीरों की समीक्षा करें",
"id.verification.review.description": "सुनिश्चित करें कि हम आपके द्वारा प्रदान की गई तस्वीरों और जानकारी के साथ आपकी पहचान को सत्यापित कर सकते हैं।",
"id.verification.review.portrait.label": "आपका पोर्ट्रेट",
"id.verification.review.portrait.alt": "आपके चेहरे का फोटो जमा करना होगा।",
"id.verification.review.portrait.retake": "पोर्ट्रेट फ़ोटो को फिर से लें",
"id.verification.review.id.label": "आपका पहचान पत्र",
"id.verification.review.id.alt": "अपने पहचान पत्र का फोटो जमा करना होगा।",
"id.verification.review.id.retake": "पुनः आईडी फोटो लें",
"id.verification.review.confirm": "प्रस्तुत",
"id.verification.submission.alert.error.face": "आपके चेहरे की एक फ़ोटो की आवश्यकता है। कृपया अपनी पोर्ट्रेट फ़ोटो को फिर से लें।",
"id.verification.submission.alert.error.id": "आपकी आईडी कार्ड की एक फ़ोटो की आवश्यकता है। कृपया अपनी आईडी फ़ोटो को फिर से लें।",
"id.verification.submission.alert.error.name": "एक वैध खाता नाम आवश्यक है। कृपया अपनी आईडी पर दिए गए नाम से मेल खाने के लिए अपने खाते का नाम अपडेट करें।",
"id.verification.submission.alert.error.unsupported": "आपके द्वारा अपलोड की गई एक या अधिक फ़ाइलें असमर्थित प्रारूप में हैं। कृपया निम्नलिखित में से चुनें:",
"id.verification.review.error": "{siteName} सहायता पृष्ठ",
"id.verification.submitted.title": "पहचान सत्यापन प्रगति पर है",
"id.verification.submitted.text": "हमें आपकी जानकारी मिल गई है और हम आपकी पहचान की पुष्टि कर रहे हैं। सत्यापन प्रक्रिया पूरी होने पर (आमतौर पर 5 दिनों के भीतर) आपको सूचित किया जाएगा। इस बीच, आप अभी भी सभी उपलब्ध पाठ्यक्रम सामग्री तक पहुंच सकते हैं।",
"id.verification.return.dashboard": "अपने डैशबोर्ड पर लौटें",
"id.verification.return.course": "पाठ्यक्रम पर लौटें",
"id.verification.return.generic": "वापसी",
"id.verification.photo.upload.help.title": "इसके बजाय एक फोटो अपलोड करें",
"id.verification.photo.camera.help.title": "इसके बजाय अपने कैमरे का उपयोग करें",
"id.verification.photo.upload.help.text": "यदि आपको उपरोक्त फोटो कैप्चर का उपयोग करने में परेशानी हो रही है, तो आप इसके बजाय एक फोटो अपलोड करना चाह सकते हैं। फोटो अपलोड करने के लिए नीचे दिए गए बटन पर क्लिक करें।",
"id.verification.photo.camera.help.text": "यदि आपको उपरोक्त फोटो अपलोड करने में परेशानी हो रही है, तो आप इसके बजाय अपने कैमरे का उपयोग करना चाह सकते हैं। अपने कैमरे का उपयोग करने के लिए, नीचे दिए गए बटन पर क्लिक करें।",
"id.verification.upload.help.button": "अपलोड मोड पर स्विच करें",
"id.verification.camera.help.button": "कैमरा मोड पर स्विच करें",
"notification.preference.heading": "अधिसूचनाएं",
"notification.preference.app.title": "{ key, select, discussion {Discussions} coursework {Course Work} other {{key}} }",
"notification.preference.title": "{ text, select, core {Core notifications} newDiscussionPost {New discussion posts} newQuestionPost {New question posts} other {{text}} }",
"notification.preference.type.label": "Type",
"notification.preference.web.label": "Web",
"notification.preference.help.email": "Email",
"notification.preference.help.push": "Push",
"notification.preference.load.more.courses": "Load more courses",
"notification.preference.guide.link": "as detailed here",
"notification.preference.guide.body": "Notifications for certain activities are enabled by default,",
"account.settings.field.name.certificate.select": "If checked, this name will appear on your certificates and public-facing records.",
"account.settings.field.name.modal.certificate.title": "Choose a preferred name for certificates and public-facing records",
"account.settings.field.name.modal.certificate.select": "Select a name",
"account.settings.field.name.modal.certificate.option.full": "Full Name",
"account.settings.field.name.modal.certificate.option.verified": "Verified Name",
"account.settings.field.name.modal.certificate.button.choose": "Choose name",
"account.settings.delete.account.before.proceeding": "Before proceeding, please {actionLink}.",
"account.settings.delete.account.text.3.edX": "You may also lose access to verified certificates and other program credentials like MicroMasters certificates. You can make a copy of these for your records before proceeding with deletion. {actionLink}.",
"account.settings.delete.account.text.3": "You may also lose access to verified certificates and other program credentials. You can make a copy of these for your records before proceeding with deletion.",
"account.settings.delete.account.header": "Delete My Account",
"account.settings.delete.account.subheader": "We're sorry to see you go!",
"account.settings.delete.account.text.1": "Please note: Deletion of your account and personal data is permanent and cannot be undone. {siteName} will not be able to recover your account or the data that is deleted.",
"account.settings.delete.account.text.2": "Once your account is deleted, you cannot use it to take courses on {siteName}.",
"account.settings.delete.account.text.2.edX": "Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employers or universitys system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.",
"account.settings.delete.account.text.3.link": "Follow these instructions for printing or downloading a certificate",
"account.settings.delete.account.text.warning": "Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on {siteName}.",
"account.settings.delete.account.text.change.instead": "Want to change your email, name, or password instead?",
"account.settings.delete.account.button": "Delete My Account",
"account.settings.delete.account.please.activate": "activate your account",
"account.settings.delete.account.please.confirm": "confirm your account",
"account.settings.delete.account.please.unlink": "unlink all social media accounts",
"account.settings.delete.account.modal.header": "Are you sure?",
"account.settings.delete.account.modal.text.1": "You have selected \"Delete My Account\". Deletion of your account and personal data is permanent and cannot be undone. {siteName} will not be able to recover your account or the data that is deleted.",
"account.settings.delete.account.modal.text.2": "If you proceed, you will be unable to use this account to take courses on {siteName}.",
"account.settings.delete.account.modal.text.2.edX": "If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer's or university's system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.",
"account.settings.delete.account.modal.enter.password": "If you still wish to continue and delete your account, please enter your account password:",
"account.settings.delete.account.modal.confirm.delete": "Yes, Delete",
"account.settings.delete.account.modal.confirm.cancel": "Cancel",
"account.settings.delete.account.error.unable.to.delete": "Unable to delete account",
"account.settings.delete.account.error.no.password": "A password is required",
"account.settings.delete.account.error.invalid.password": "Password is incorrect",
"account.settings.delete.account.error.unable.to.delete.details": "Sorry, there was an error trying to process your request. Please try again later.",
"account.settings.delete.account.modal.after.header": "We're sorry to see you go! Your account will be deleted shortly.",
"account.settings.delete.account.modal.after.text": "Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email.",
"account.settings.delete.account.modal.after.button": "Close",
"account.settings.message.demographics.service.issue": "An error occurred attempting to retrieve or save your account information. Please try again later.",
"account.settings.field.demographics.gender": "Gender identity",
"account.settings.field.demographics.gender.empty": "Add gender identity",
"account.settings.field.demographics.gender.options.empty": "Select a gender identity",
"account.settings.field.demographics.gender_description": "Gender identity description",
"account.settings.field.demographics.gender_description.empty": "Enter description",
"account.settings.field.demographics.ethnicity": "Race/Ethnicity identity",
"account.settings.field.demographics.ethnicity.empty": "Add race/ethnicity identity",
"account.settings.field.demographics.ethnicity.options.empty": "Select all that apply",
"account.settings.field.demographics.income": "Family income",
"account.settings.field.demographics.income.empty": "Add family income",
"account.settings.field.demographics.income.options.empty": "Select a family income range",
"account.settings.field.demographics.military_history": "U.S. Military status",
"account.settings.field.demographics.military_history.empty": "Add military status",
"account.settings.field.demographics.military_history.options.empty": "Select military status",
"account.settings.field.demographics.learner_education_level": "Your education level",
"account.settings.field.demographics.learner_education_level.empty": "Add education level",
"account.settings.field.demographics.parent_education_level": "Parents/Guardians education level",
"account.settings.field.demographics.parent_education_level.empty": "Add education level",
"account.settings.field.demographics.education_level.options.empty": "Select education level",
"account.settings.field.demographics.work_status": "Employment status",
"account.settings.field.demographics.work_status.empty": "Add employment status",
"account.settings.field.demographics.work_status.options.empty": "Select employment status",
"account.settings.field.demographics.work_status_description": "Employment status description",
"account.settings.field.demographics.work_status_description.empty": "Enter description",
"account.settings.field.demographics.current_work_sector": "Current work industry",
"account.settings.field.demographics.current_work_sector.empty": "Add work industry",
"account.settings.field.demographics.future_work_sector": "Future work industry",
"account.settings.field.demographics.future_work_sector.empty": "Add work industry",
"account.settings.field.demographics.work_sector.options.empty": "Select work industry",
"account.settings.section.demographics.why": "Why does {siteName} collect this information?",
"account.settings.name.change.title.id": "This name change requires identity verification",
"account.settings.name.change.title.begin": "Before we begin",
"account.settings.name.change.warning.one": "Warning: This action updates the name that appears on all certificates that have been earned on this account in the past and any certificates you are currently earning or will earn in the future.",
"account.settings.name.change.warning.two": "This action cannot be undone without verifying your identity.",
"account.settings.name.change.id.name.label": "Enter your name as it appears on your unexpired student, work, or government-issued identification card.",
"account.settings.name.change.id.name.placeholder": "Enter the name on your photo ID",
"account.settings.name.change.error.valid.name": "Please enter a valid name.",
"account.settings.name.change.error.general": "A technical error occurred. Please try again.",
"account.settings.name.change.continue": "Continue",
"account.settings.name.change.cancel": "Cancel",
"account.settings.editable.field.password.reset.button.confirmation.support.link": "technical support",
"account.settings.editable.field.password.reset.button.confirmation": "We've sent a message to {email}. Click the link in the message to reset your password. Didn't receive the message? Contact {technicalSupportLink}.",
"account.settings.editable.field.password.reset.button.forbidden": "Your previous request is in progress, please try again in few moments.",
"account.settings.editable.field.password.reset.label": "Password",
"account.settings.editable.field.password.reset.button": "Reset Password",
"account.settings.sso.link.account": "Sign in with {name}",
"account.settings.sso.account.connected": "Linked",
"account.settings.sso.account.disconnect.error": "There was a problem disconnecting this account. Contact support if the problem persists.",
"account.settings.sso.unlink.account": "Unlink {name} account",
"account.settings.sso.no.providers": "No accounts can be linked at this time.",
"id.verification.request.camera.access.instructions": "In order to take a photo using your webcam, you may receive a browser prompt for access to your camera. {clickAllow}",
"id.verification.requirements.account.managed.alert": "Your account settings are managed by {managerTitle}. If the name on your photo ID does not match the name on your account, please contact your {profileDataManager} administrator or {support} for help before completing the Photo Verification process.",
"id.verification.requirements.card.device.text": "You need a device that has a camera. If you receive a browser prompt for access to your camera, please make sure to click {allow}.",
"id.verification.account.name.summary.alert": "Your account settings are managed by {managerTitle}. If the name on your photo ID does not match the name on your account, please contact your {profileDataManager} administrator or {support} for help.",
"idv.submission.alert.error": "We encountered a technical error while trying to submit ID verification. This might be a temporary issue, so please try again in a few minutes. If the problem persists, please go to {support_link} for help.",
"id.verification.account.name.edit": "Edit {sr}"
"notification.preference.type.label": "प्रकार",
"notification.preference.web.label": "वेब",
"notification.preference.help.email": "ईमेल",
"notification.preference.help.push": "धकेलना",
"notification.preference.load.more.courses": "अधिक पाठ्यक्रम लोड करें",
"notification.preference.guide.link": "जैसा कि यहां विस्तृत है",
"notification.preference.guide.body": "कुछ गतिविधियों के लिए सूचनाएं डिफ़ॉल्ट रूप से सक्षम होती हैं,",
"account.settings.field.name.certificate.select": "यदि जाँच की जाए, तो यह नाम आपके प्रमाणपत्रों और सार्वजनिक-सामना वाले रिकॉर्ड पर दिखाई देगा।",
"account.settings.field.name.modal.certificate.title": "प्रमाणपत्रों और सार्वजनिक-सामना वाले रिकॉर्ड के लिए पसंदीदा नाम चुनें",
"account.settings.field.name.modal.certificate.select": "एक नाम चुनें",
"account.settings.field.name.modal.certificate.option.full": "पूरा नाम",
"account.settings.field.name.modal.certificate.option.verified": "सत्यापित नाम",
"account.settings.field.name.modal.certificate.button.choose": "नाम चुनें",
"account.settings.delete.account.before.proceeding": "आगे बढ़ने से पहले कृपया {actionLink}",
"account.settings.delete.account.text.3.edX": "आप सत्यापित प्रमाणपत्रों और माइक्रोमास्टर प्रमाणपत्रों जैसे अन्य प्रोग्राम क्रेडेंशियल्स तक पहुंच भी खो सकते हैं। हटाने की प्रक्रिया आगे बढ़ाने से पहले आप अपने रिकॉर्ड के लिए इनकी एक प्रति बना सकते हैं। {actionLink}",
"account.settings.delete.account.text.3": "आप सत्यापित प्रमाणपत्रों और अन्य प्रोग्राम क्रेडेंशियल्स तक पहुंच भी खो सकते हैं। हटाने की प्रक्रिया आगे बढ़ाने से पहले आप अपने रिकॉर्ड के लिए इनकी एक प्रति बना सकते हैं।",
"account.settings.delete.account.header": "मेरा खाता हटाएं",
"account.settings.delete.account.subheader": "हमें आपको जाते हुए देखकर दुःख हुआ!",
"account.settings.delete.account.text.1": "कृपया ध्यान दें: आपके खाते और व्यक्तिगत डेटा का विलोपन स्थायी है और इसे पूर्ववत नहीं किया जा सकता है। {siteName} आपके खाते या हटाए गए डेटा को पुनर्प्राप्त नहीं कर पाएगा।",
"account.settings.delete.account.text.2": "एक बार आपका खाता हटा दिए जाने के बाद, आप इसका उपयोग {siteName} पर पाठ्यक्रम लेने के लिए नहीं कर सकते।",
"account.settings.delete.account.text.2.edX": "एक बार जब आपका खाता हटा दिया जाता है, तो आप इसका उपयोग edX ऐप, edx.org, या edX द्वारा होस्ट की गई किसी अन्य साइट पर पाठ्यक्रम लेने के लिए नहीं कर सकते। इसमें आपके नियोक्ता या विश्वविद्यालय के सिस्टम से edx.org तक पहुंच और एमआईटी ओपन लर्निंग, व्हार्टन एक्जीक्यूटिव एजुकेशन और हार्वर्ड मेडिकल स्कूल द्वारा प्रदान की जाने वाली निजी साइटों तक पहुंच शामिल है।",
"account.settings.delete.account.text.3.link": "किसी प्रमाणपत्र को प्रिंट करने या डाउनलोड करने के लिए इन निर्देशों का पालन करें",
"account.settings.delete.account.text.warning": "चेतावनी: खाता विलोपन स्थायी है. कृपया आगे बढ़ने से पहले उपरोक्त को ध्यान से पढ़ें। यह एक अपरिवर्तनीय कार्रवाई है, और अब आप {siteName} पर उसी ईमेल का उपयोग नहीं कर पाएंगे।",
"account.settings.delete.account.text.change.instead": "अपना ईमेल, नाम या पासवर्ड बदलना चाहते हैं?",
"account.settings.delete.account.button": "मेरा खाता हटाएं",
"account.settings.delete.account.please.activate": "अपने खाते को सक्रिय करें",
"account.settings.delete.account.please.confirm": "अपना खाता पुष्टि करें",
"account.settings.delete.account.please.unlink": "सभी सोशल मीडिया खातों को अनलिंक करें",
"account.settings.delete.account.modal.header": "क्या आप सुनिश्चित हैं?",
"account.settings.delete.account.modal.text.1": "आपने \"मेरा खाता हटाएं\" चुना है। आपके खाते और व्यक्तिगत डेटा का विलोपन स्थायी है और इसे पूर्ववत नहीं किया जा सकता है। {siteName} आपके खाते या हटाए गए डेटा को पुनर्प्राप्त नहीं कर पाएगा।",
"account.settings.delete.account.modal.text.2": "यदि आप आगे बढ़ते हैं, तो आप {siteName} पर पाठ्यक्रम लेने के लिए इस खाते का उपयोग करने में असमर्थ होंगे।",
"account.settings.delete.account.modal.text.2.edX": "यदि आप आगे बढ़ते हैं, तो आप edX ऐप, edx.org, या edX द्वारा होस्ट की गई किसी अन्य साइट पर पाठ्यक्रम लेने के लिए इस खाते का उपयोग करने में असमर्थ होंगे। इसमें आपके नियोक्ता या विश्वविद्यालय के सिस्टम से edx.org तक पहुंच और एमआईटी ओपन लर्निंग, व्हार्टन एक्जीक्यूटिव एजुकेशन और हार्वर्ड मेडिकल स्कूल द्वारा प्रस्तावित निजी साइटों तक पहुंच शामिल है।",
"account.settings.delete.account.modal.enter.password": "यदि आप अभी भी अपना खाता जारी रखना और हटाना चाहते हैं, तो कृपया अपना खाता पासवर्ड दर्ज करें:",
"account.settings.delete.account.modal.confirm.delete": "हाँ, हटाएं",
"account.settings.delete.account.modal.confirm.cancel": "रद्द करना",
"account.settings.delete.account.error.unable.to.delete": "खाता हटाने में असमर्थ",
"account.settings.delete.account.error.no.password": "एक पासवर्ड की आवश्यकता होती है",
"account.settings.delete.account.error.invalid.password": "पासवर्ड गलत है",
"account.settings.delete.account.error.unable.to.delete.details": "क्षमा करें, आपके अनुरोध को संसाधित करने में एक त्रुटि हुई। बाद में पुन: प्रयास करें।",
"account.settings.delete.account.modal.after.header": "हमें खेद है कि आप जा रहे हैं! आपका खाता शीघ्र ही हटा दिया जाएगा।",
"account.settings.delete.account.modal.after.text": "खाता हटाना, ईमेल सूची से हटाने सहित, हमारे सिस्टम के माध्यम से पूरी तरह से प्रोसेस करने में कुछ हफ्ते लग सकते हैं। अगर आप उससे पहले ही ईमेल से बाहर निकलना चाहते हैं, तो कृपया किसी भी ईमेल के फूटर से सदस्यता रद्द करें।",
"account.settings.delete.account.modal.after.button": "बंद करना",
"account.settings.message.demographics.service.issue": "आपके खाते की जानकारी पुनः प्राप्त करने या सहेजने का प्रयास करते समय एक त्रुटि उत्पन्न हुई। कृपया बाद में पुन: प्रयास करें।",
"account.settings.field.demographics.gender": "लिंग की पहचान",
"account.settings.field.demographics.gender.empty": "लिंग पहचान जोड़ें",
"account.settings.field.demographics.gender.options.empty": "एक लिंग पहचान का चयन करें",
"account.settings.field.demographics.gender_description": "लिंग पहचान वर्णन",
"account.settings.field.demographics.gender_description.empty": "विवरण दर्ज करें",
"account.settings.field.demographics.ethnicity": "नस्ल/जातीयता की पहचान",
"account.settings.field.demographics.ethnicity.empty": "नस्ल/जातीयता की पहचान जोड़ें",
"account.settings.field.demographics.ethnicity.options.empty": "लागू होने वाले सभी का चयन करें",
"account.settings.field.demographics.income": "परिवार की आय",
"account.settings.field.demographics.income.empty": "परिवार की आय जोड़ें",
"account.settings.field.demographics.income.options.empty": "एक परिवार आय सीमा का चयन करें",
"account.settings.field.demographics.military_history": "यूएस सैन्य स्थिति",
"account.settings.field.demographics.military_history.empty": "युद्ध स्थिति जोड़ें",
"account.settings.field.demographics.military_history.options.empty": "चुनें सैन्य स्थिति",
"account.settings.field.demographics.learner_education_level": "आपका शिक्षा स्तर",
"account.settings.field.demographics.learner_education_level.empty": "शिक्षा स्तर जोड़ें",
"account.settings.field.demographics.parent_education_level": "माता-पिता/अभिभावकों का शिक्षा स्तर",
"account.settings.field.demographics.parent_education_level.empty": "शिक्षा स्तर जोड़ें",
"account.settings.field.demographics.education_level.options.empty": "शिक्षा स्तर का चयन करें",
"account.settings.field.demographics.work_status": "रोजगार की स्थिति",
"account.settings.field.demographics.work_status.empty": "रोजगार की स्थिति जोड़ें",
"account.settings.field.demographics.work_status.options.empty": "उपलब्ध रोजगार स्थिति का चयन करें",
"account.settings.field.demographics.work_status_description": "रोजगार स्थिति विवरण",
"account.settings.field.demographics.work_status_description.empty": "विवरण दर्ज करें",
"account.settings.field.demographics.current_work_sector": "वर्तमान कार्य उद्योग",
"account.settings.field.demographics.current_work_sector.empty": "कार्य उद्योग जोड़ें",
"account.settings.field.demographics.future_work_sector": "भविष्य कार्य उद्योग",
"account.settings.field.demographics.future_work_sector.empty": "कार्य उद्योग जोड़ें",
"account.settings.field.demographics.work_sector.options.empty": "कार्य उद्योग का चयन करें",
"account.settings.section.demographics.why": "{siteName} यह जानकारी क्यों एकत्र करता है?",
"account.settings.name.change.title.id": "इस नाम परिवर्तन के लिए पहचान सत्यापन की आवश्यकता है",
"account.settings.name.change.title.begin": "इससे पहले कि हम शुरू करें",
"account.settings.name.change.warning.one": "चेतावनी: यह क्रिया उस नाम को अद्यतन करती है जो इस खाते पर अतीत में अर्जित किए गए सभी प्रमाणपत्रों और आपके द्वारा वर्तमान में अर्जित किए जा रहे या भविष्य में अर्जित किए जाने वाले किसी भी प्रमाणपत्र पर दिखाई देता है।",
"account.settings.name.change.warning.two": "इस कार्रवाई को आपकी पहचान की पुष्टि के बिना पूर्ण नहीं किया जा सकता।",
"account.settings.name.change.id.name.label": "अपना नाम दर्ज करें जैसा कि यह आपके अप्रयुक्त छात्र, कार्य या सरकार द्वारा जारी पहचान पत्र पर दिखाई देता है।",
"account.settings.name.change.id.name.placeholder": "अपने फ़ोटो आईडी पर नाम दर्ज करें",
"account.settings.name.change.error.valid.name": "कृपया एक मान्य नाम दर्ज करें।",
"account.settings.name.change.error.general": "एक तकनीकी त्रुटि हुई। कृपया दोबारा प्रयास करें।",
"account.settings.name.change.continue": "जारी रखें",
"account.settings.name.change.cancel": "रद्द करना",
"account.settings.editable.field.password.reset.button.confirmation.support.link": "तकनीकी सहायता",
"account.settings.editable.field.password.reset.button.confirmation": "हमने {email} पर एक संदेश भेजा है। अपना पासवर्ड रीसेट करने के लिए संदेश में दिए गए लिंक पर क्लिक करें। संदेश प्राप्त नहीं हुआ? {technicalSupportLink} से संपर्क करें।",
"account.settings.editable.field.password.reset.button.forbidden": "आपका पिछला अनुरोध प्रगति में है, कृपया कुछ क्षणों में पुनः प्रयास करें।",
"account.settings.editable.field.password.reset.label": "पासवर्ड",
"account.settings.editable.field.password.reset.button": "पासवर्ड रीसेट करें",
"account.settings.sso.link.account": "{name} से साइन इन करें",
"account.settings.sso.account.connected": "लिंक किया हुआ",
"account.settings.sso.account.disconnect.error": "इस खाते को डिस्कनेक्ट करने में समस्या हुई थी। अगर समस्या बरकरार रहती है तो सहायता से संपर्क करें।",
"account.settings.sso.unlink.account": "{name} खाता अनलिंक करें",
"account.settings.sso.no.providers": "इस समय कोई भी खाता लिंक नहीं किया जा सकता।",
"id.verification.request.camera.access.instructions": "अपनी वेबकैम का उपयोग करके फ़ोटो लेने के लिए, आपको अपने कैमरे तक पहुंच के लिए ब्राउज़र प्रॉम्प्ट प्राप्त हो सकता है। {clickAllow}",
"id.verification.requirements.account.managed.alert": "आपकी खाता सेटिंग {managerTitle} द्वारा प्रबंधित की जाती है। यदि आपकी फोटो आईडी पर नाम आपके खाते के नाम से मेल नहीं खाता है, तो कृपया फोटो सत्यापन प्रक्रिया पूरी करने से पहले मदद के लिए अपने {profileDataManager} व्यवस्थापक या {support} से संपर्क करें।",
"id.verification.requirements.card.device.text": "आपको एक ऐसे उपकरण की आवश्यकता है जिसमें एक कैमरा हो। यदि आपको अपने कैमरे तक पहुंच के लिए ब्राउज़र संकेत मिलता है, तो कृपया {allow} पर क्लिक करना सुनिश्चित करें।",
"id.verification.account.name.summary.alert": "आपकी खाता सेटिंग {managerTitle} द्वारा प्रबंधित की जाती हैं। आपकी फोटो आईडी पर दिया गया नाम आपके खाते के नाम से मेल नहीं खाता है, कृपया सहायता के लिए अपने {profileDataManager} व्यवस्थापक या {support} से संपर्क करें।",
"idv.submission.alert.error": "आईडी सत्यापन सबमिट करने का प्रयास करते समय हमें एक तकनीकी त्रुटि का सामना करना पड़ा। यह एक अस्थायी समस्या हो सकती है, इसलिए कृपया कुछ मिनटों में पुनः प्रयास करें। यदि समस्या बनी रहती है, तो कृपया सहायता के लिए {support_link} पर जाएँ।",
"id.verification.account.name.edit": "संपादित करें {sr}"
}

View File

@@ -115,6 +115,9 @@
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
"account.settings.static.field.empty.no.admin": "No value set.",
"notification.preferences.notifications.label": "Notifications",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
"account.page.title": "Account | {siteName}",
"id.verification.access.blocked.denied": "We cannot verify your identity at this time. If you have yet to activate your account, please check your spam folder for the activation email from {email}.",

View File

@@ -115,6 +115,9 @@
"account.settings.static.field.empty": "Nessun valore impostato. Contatta il tuo amministratore {enterprise} per apportare modifiche. ",
"account.settings.static.field.empty.no.admin": "Nessun valore impostato. ",
"notification.preferences.notifications.label": "Notifications",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "La pagina che stai cercando non è disponibile o si è verificato un errore nell'URL. Controlla l'URL e riprova. ",
"account.page.title": "Conto | {siteName}",
"id.verification.access.blocked.denied": "Non possiamo verificare la tua identità in questo momento. Se devi ancora attivare il tuo account, controlla la tua cartella spam per l&#39;email di attivazione da {email}.",

View File

@@ -115,6 +115,9 @@
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
"account.settings.static.field.empty.no.admin": "No value set.",
"notification.preferences.notifications.label": "Notifications",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
"account.page.title": "Account | {siteName}",
"id.verification.access.blocked.denied": "We cannot verify your identity at this time. If you have yet to activate your account, please check your spam folder for the activation email from {email}.",

View File

@@ -115,6 +115,9 @@
"account.settings.static.field.empty": "Sem conjunto de valores. Contacte o seu administrador {enterprise} para fazer alterações.",
"account.settings.static.field.empty.no.admin": "Nenhum valor definido.",
"notification.preferences.notifications.label": "Notifications",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "A página não foi encontrada ou o URL está errado. Por favor, verifique o URL e tente novamente.",
"account.page.title": "Conta | {siteName}",
"id.verification.access.blocked.denied": "Não conseguimos verificar a sua identidade neste momento. Se ainda não ativou a sua conta, por favor verifique a sua pasta de spam para o email de ativação de {email}.",

View File

@@ -115,6 +115,9 @@
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
"account.settings.static.field.empty.no.admin": "No value set.",
"notification.preferences.notifications.label": "Notifications",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
"account.page.title": "Account | {siteName}",
"id.verification.access.blocked.denied": "We cannot verify your identity at this time. If you have yet to activate your account, please check your spam folder for the activation email from {email}.",

View File

@@ -115,6 +115,9 @@
"account.settings.static.field.empty": "No value set. Contact your {enterprise} administrator to make changes.",
"account.settings.static.field.empty.no.admin": "No value set.",
"notification.preferences.notifications.label": "Notifications",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
"account.page.title": "Account | {siteName}",
"id.verification.access.blocked.denied": "We cannot verify your identity at this time. If you have yet to activate your account, please check your spam folder for the activation email from {email}.",

View File

@@ -84,7 +84,7 @@
"account.settings.field.education.levels.jhs": "初中",
"account.settings.field.education.levels.el": "小学",
"account.settings.field.education.levels.none": "未受正规教育",
"account.settings.field.education.levels.other": "Other education",
"account.settings.field.education.levels.other": "其他教育程度",
"account.settings.field.gender": "性别",
"account.settings.field.gender.empty": "添加性别",
"account.settings.field.gender.options.empty": "请选择性别",
@@ -115,6 +115,9 @@
"account.settings.static.field.empty": "没有设置值。请联系您的{enterprise}管理员进行更改。",
"account.settings.static.field.empty.no.admin": "尚无值",
"notification.preferences.notifications.label": "通知",
"account.settings.work.experience": "Work Experience",
"account.settings.field.work.experience.empty": "Add work experience",
"account.settings.field.work.experience.options.empty": "Select work experience",
"error.notfound.message": "您访问的地址不存在或有误。请检查URL后重新尝试访问。",
"account.page.title": "帐户 | {siteName}",
"id.verification.access.blocked.denied": "我们目前无法验证您的身份。如果您尚未激活您的帐户,请检查您的垃圾邮件文件夹中是否有来自 {email} 的激活电子邮件。",
@@ -252,8 +255,8 @@
"notification.preference.heading": "通知",
"notification.preference.app.title": "{ key, select, discussion {Discussions} coursework {Course Work} other {{key}} }",
"notification.preference.title": "{ text, select, core {Core notifications} newDiscussionPost {New discussion posts} newQuestionPost {New question posts} other {{text}} }",
"notification.preference.type.label": "Type",
"notification.preference.web.label": "Web",
"notification.preference.type.label": "类型",
"notification.preference.web.label": "网页",
"notification.preference.help.email": "邮箱",
"notification.preference.help.push": "Push",
"notification.preference.load.more.courses": "Load more courses",
@@ -291,7 +294,7 @@
"account.settings.delete.account.error.no.password": "需要输入密码",
"account.settings.delete.account.error.invalid.password": "密码错误",
"account.settings.delete.account.error.unable.to.delete.details": "抱歉,处理您的请求时发生错误,请稍后重试。",
"account.settings.delete.account.modal.after.header": "We're sorry to see you go! Your account will be deleted shortly.",
"account.settings.delete.account.modal.after.header": "很遗憾您要离开!您的账号将很快被删除。",
"account.settings.delete.account.modal.after.text": "删除账号包括从邮箱列表中移除您的邮箱,我们的系统可能需要耗时数周才能完成处理。如果在此期间您不希望收到邮件,请从任意邮件的页脚取消订阅。",
"account.settings.delete.account.modal.after.button": "关闭",
"account.settings.message.demographics.service.issue": "尝试检索或保存您的帐户信息时发生错误。请稍后再试。",

View File

@@ -7,7 +7,7 @@ import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import * as blazeface from '@tensorflow-models/blazeface';
import CameraPhoto, { FACING_MODES } from 'jslib-html5-camera-photo';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Form, Spinner } from '@edx/paragon';
import { Form, Spinner } from '@openedx/paragon';
import shutter from './data/camera-shutter.base64.json';
import messages from './IdVerification.messages';

View File

@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Collapsible } from '@edx/paragon';
import { Collapsible } from '@openedx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';

View File

@@ -1,6 +1,6 @@
import React, { useState, useContext } from 'react';
import PropTypes from 'prop-types';
import { Collapsible } from '@edx/paragon';
import { Collapsible } from '@openedx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';

View File

@@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Button, Collapsible } from '@edx/paragon';
import { Button, Collapsible } from '@openedx/paragon';
import IdVerificationContext from './IdVerificationContext';
import messages from './IdVerification.messages';

View File

@@ -6,7 +6,7 @@ import {
import camelCase from 'lodash.camelcase';
import qs from 'qs';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Button, ModalDialog, ActionRow } from '@edx/paragon';
import { Button, ModalDialog, ActionRow } from '@openedx/paragon';
import { getConfig } from '@edx/frontend-platform';
import { idVerificationSelector } from './data/selectors';
import './getUserMediaShim';

View File

@@ -1,7 +1,7 @@
import React, { useCallback, useState } from 'react';
import { intlShape } from '@edx/frontend-platform/i18n';
import PropTypes from 'prop-types';
import { Alert } from '@edx/paragon';
import { Alert } from '@openedx/paragon';
import messages from './IdVerification.messages';
import SupportedMediaTypes from './SupportedMediaTypes';

View File

@@ -1,7 +1,7 @@
import React, {
useContext, useEffect, useRef,
} from 'react';
import { Form } from '@edx/paragon';
import { Form } from '@openedx/paragon';
import { Link, useNavigate, useLocation } from 'react-router-dom';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';

View File

@@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
import { getConfig } from '@edx/frontend-platform';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
import { Alert, Hyperlink } from '@edx/paragon';
import { Alert, Hyperlink } from '@openedx/paragon';
import { useNextPanelSlug } from '../routing-utilities';
import BasePanel from './BasePanel';

View File

@@ -2,7 +2,7 @@ import React, { useState, useContext, useEffect } from 'react';
import { getConfig } from '@edx/frontend-platform';
import {
Alert, Hyperlink, Form, Button, Spinner,
} from '@edx/paragon';
} from '@openedx/paragon';
import { Link, useNavigate } from 'react-router-dom';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';

View File

@@ -31,7 +31,7 @@ subscribe(APP_READY, () => {
<Route element={(
<div className="d-flex flex-column" style={{ minHeight: '100vh' }}>
<Header />
<main className="flex-grow-1">
<main className="flex-grow-1" id="main">
<Outlet />
</main>
<Footer />

View File

@@ -3,7 +3,7 @@ $fa-font-path: "~font-awesome/fonts";
@import "~@edx/brand/paragon/fonts";
@import "~@edx/brand/paragon/variables";
@import "~@edx/paragon/scss/core/core";
@import "~@openedx/paragon/scss/core/core";
@import "~@edx/brand/paragon/overrides";
@import "~@edx/frontend-component-header/dist/index";
@import "~@edx/frontend-component-footer/dist/footer";

View File

@@ -4,8 +4,8 @@ import { Link } from 'react-router-dom';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
Container, Icon, Spinner, Button,
} from '@edx/paragon';
import { ArrowForwardIos } from '@edx/paragon/icons';
} from '@openedx/paragon';
import { ArrowForwardIos } from '@openedx/paragon/icons';
import { fetchCourseList } from './data/thunks';
import { selectCourseListStatus, selectCourseList, selectPagination } from './data/selectors';
import {

View File

@@ -2,16 +2,21 @@ import React, { useCallback, useMemo } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Collapsible } from '@edx/paragon';
import { Collapsible, NavItem } from '@edx/paragon';
import classNames from 'classnames';
import messages from './messages';
import ToggleSwitch from './ToggleSwitch';
import {
selectPreferenceAppToggleValue,
selectNonEditablePreferences,
selectPreferencesOfApp,
selectSelectedCourseId,
selectUpdatePreferencesStatus,
} from './data/selectors';
import NotificationPreferenceRow from './NotificationPreferenceRow';
import { updateAppPreferenceToggle } from './data/thunks';
import { updateAppPreferenceToggle, updateChannelPreferenceToggle } from './data/thunks';
import { LOADING_STATUS } from '../constants';
import NOTIFICATION_CHANNELS from './data/constants';
const NotificationPreferenceApp = ({ appId }) => {
const dispatch = useDispatch();
@@ -19,6 +24,19 @@ const NotificationPreferenceApp = ({ appId }) => {
const courseId = useSelector(selectSelectedCourseId());
const appPreferences = useSelector(selectPreferencesOfApp(appId));
const appToggle = useSelector(selectPreferenceAppToggleValue(appId));
const updatePreferencesStatus = useSelector(selectUpdatePreferencesStatus());
const nonEditable = useSelector(selectNonEditablePreferences(appId));
const onChannelToggle = useCallback((event) => {
const { id: notificationChannel } = event.target;
const isPreferenceNonEditable = (preference) => nonEditable?.[preference.id]?.includes(notificationChannel);
const hasActivePreferences = appPreferences.some(
(preference) => preference[notificationChannel] && !isPreferenceNonEditable(preference),
);
dispatch(updateChannelPreferenceToggle(courseId, appId, notificationChannel, !hasActivePreferences));
}, [appId, appPreferences, courseId, dispatch, nonEditable]);
const preferences = useMemo(() => (
appPreferences.map(preference => (
@@ -33,10 +51,10 @@ const NotificationPreferenceApp = ({ appId }) => {
dispatch(updateAppPreferenceToggle(courseId, appId, event.target.checked));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [appId]);
if (!courseId) {
return null;
}
return (
<Collapsible.Advanced open={appToggle} data-testid="notification-app" className="mb-5">
<Collapsible.Trigger>
@@ -49,6 +67,7 @@ const NotificationPreferenceApp = ({ appId }) => {
name={appId}
value={appToggle}
onChange={onChangeAppSettings}
disabled={updatePreferencesStatus === LOADING_STATUS}
/>
</span>
</div>
@@ -58,7 +77,22 @@ const NotificationPreferenceApp = ({ appId }) => {
<div className="d-flex flex-row header-label">
<span className="col-8 px-0">{intl.formatMessage(messages.typeLabel)}</span>
<span className="d-flex col-4 px-0">
<span className="ml-auto">{intl.formatMessage(messages.webLabel)}</span>
{NOTIFICATION_CHANNELS.map((channel) => (
<NavItem
id={channel}
key={channel}
className={classNames(
'd-flex',
{ 'ml-auto': channel === 'web' },
{ 'mx-auto': channel === 'email' },
{ 'ml-auto mr-0': channel === 'push' },
)}
role="button"
onClick={onChannelToggle}
>
{intl.formatMessage(messages.notificationChannel, { text: channel })}
</NavItem>
))}
</span>
</div>
<div className="my-3">

View File

@@ -3,8 +3,8 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Icon, OverlayTrigger, Tooltip } from '@edx/paragon';
import { InfoOutline } from '@edx/paragon/icons';
import { Icon, OverlayTrigger, Tooltip } from '@openedx/paragon';
import { InfoOutline } from '@openedx/paragon/icons';
import messages from './messages';
import ToggleSwitch from './ToggleSwitch';
import {

View File

@@ -4,8 +4,8 @@ import { Link, useParams } from 'react-router-dom';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Container, Icon, Spinner, Hyperlink,
} from '@edx/paragon';
import { ArrowBack } from '@edx/paragon/icons';
} from '@openedx/paragon';
import { ArrowBack } from '@openedx/paragon/icons';
import {
selectCourseListStatus,
selectCourse,

View File

@@ -1,4 +1,4 @@
import { Form } from '@edx/paragon';
import { Form } from '@openedx/paragon';
import React from 'react';
import PropTypes from 'prop-types';

View File

@@ -118,6 +118,7 @@ const notificationPreferencesReducer = (state = defaultState, action = {}) => {
? { ...app, enabled: value }
: app
)),
updatePreferenceStatus: LOADING_STATUS,
},
};
default:

View File

@@ -54,6 +54,10 @@ export const selectPreferenceNonEditableChannels = (appId, name) => state => (
state?.notificationPreferences.preferences.nonEditable[appId]?.[name] || []
);
export const selectNonEditablePreferences = appId => state => (
state?.notificationPreferences.preferences.nonEditable[appId] || []
);
export const selectSelectedCourseId = () => state => (
state.notificationPreferences.preferences.selectedCourse
);

View File

@@ -42,3 +42,10 @@ export const patchPreferenceToggle = async (
const { data } = await getAuthenticatedHttpClient().patch(url, patchData);
return data;
};
export const patchChannelPreferenceToggle = async (courseId, notificationApp, notificationChannel, value) => {
const patchData = snakeCaseObject({ notificationApp, notificationChannel, value });
const url = `${getConfig().LMS_BASE_URL}/api/notifications/channel/configurations/${courseId}`;
const { data } = await getAuthenticatedHttpClient().patch(url, patchData);
return data;
};

View File

@@ -14,6 +14,7 @@ import {
getCourseList,
getCourseNotificationPreferences,
patchAppPreferenceToggle,
patchChannelPreferenceToggle,
patchPreferenceToggle,
} from './service';
@@ -148,3 +149,15 @@ export const updatePreferenceToggle = (
}
}
);
export const updateChannelPreferenceToggle = (courseId, notificationApp, notificationChannel, value) => (
async (dispatch) => {
try {
const data = await patchChannelPreferenceToggle(courseId, notificationApp, notificationChannel, value);
const normalizedData = normalizePreferences(camelCaseObject(data));
dispatch(fetchNotificationPreferenceSuccess(courseId, normalizedData));
} catch (errors) {
dispatch(fetchNotificationPreferenceFailed());
}
}
);

View File

@@ -23,10 +23,22 @@ const messages = defineMessages({
core {Core notifications}
newDiscussionPost {New discussion posts}
newQuestionPost {New question posts}
contentReported {Reported content}
other {{text}}
}`,
description: 'Display text for Notification Types',
},
notificationChannel: {
id: 'notification.preference.channel',
defaultMessage: `{
text, select,
web {Web}
email {Email}
push {Push}
other {{text}}
}`,
description: 'Display text for Notification Channel',
},
typeLabel: {
id: 'notification.preference.type.label',
defaultMessage: 'Type',

View File

@@ -1,8 +1,3 @@
import 'core-js/stable';
import 'regenerator-runtime/runtime';
import '@testing-library/jest-dom';
import Enzyme from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
Enzyme.configure({ adapter: new Adapter() });