Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…n-portal into mashal-m/react-upgrade-to-v17
  • Loading branch information
mashal-m committed Dec 18, 2023
2 parents 0db49c7 + 27a7d6f commit 09b7848
Show file tree
Hide file tree
Showing 195 changed files with 12,681 additions and 2,904 deletions.
3 changes: 3 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
APP_ID='admin-portal'
NODE_ENV='development'
BASE_URL='http://localhost:1991'
LMS_BASE_URL='http://localhost:18000'
Expand All @@ -17,6 +18,7 @@ ENTERPRISE_LEARNER_PORTAL_URL='http://localhost:8734'
ENTERPRISE_SUPPORT_URL='https://edx.org'
ENTERPRISE_SUPPORT_REVOKE_LICENSE_URL='https://edx.org'
ENTERPRISE_SUPPORT_PROGRAM_OPTIMIZATION_URL='https://edx.org'
ENTERPRISE_SUPPORT_LEARNER_CREDIT_URL='https://edx.org'
SEGMENT_KEY=''
ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
USER_INFO_COOKIE_NAME='edx-user-info'
Expand Down Expand Up @@ -53,3 +55,4 @@ USE_API_CACHE='true'
SUBSCRIPTION_LPR='true'
PLOTLY_SERVER_URL='http://localhost:8050'
AUTH0_SELF_SERVICE_INTEGRATION='true'
MFE_CONFIG_API_URL='http://localhost:18000/api/mfe_config/v1'
116 changes: 116 additions & 0 deletions .env.development-stage
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# App Specific

# Note: The Algolia APP_ID and SEARCH_API_KEY are secret and must be configured via `.env.private`.
ALGOLIA_APP_ID=""
ALGOLIA_SEARCH_API_KEY=""

BASE_URL="https://localhost.stage.edx.org:1991"
LICENSE_MANAGER_BASE_URL="https://license-manager.stage.edx.org"
ENTERPRISE_ACCESS_BASE_URL="https://enterprise-access.stage.edx.org"
ENTERPRISE_CATALOG_BASE_URL="https://enterprise-catalog.stage.edx.org"
ENTERPRISE_SUBSIDY_BASE_URL="https://enterprise-subsidy.stage.edx.org"
DISCOVERY_BASE_URL="https://discovery.stage.edx.org"
PLOTLY_SERVER_URL="https://enterprise-dash.edx.org/admin-analytics/"
SEGMENT_KEY=""
NEW_RELIC_AGENT_ID=""
NEW_RELIC_APP_ID=""
ALGOLIA_INDEX_NAME="enterprise_catalog_new"
FEATURE_PROGRAM_TITLES_FACET='true'
FEATURE_LANGUAGE_FACET='true'
FEATURE_CODE_MANAGEMENT='true'
FEATURE_REPORTING_CONFIGURATIONS='true'
FEATURE_ANALYTICS='true'
FEATURE_SUPPORT='true'
FEATURE_SAML_CONFIGURATION=''
FEATURE_CODE_VISIBILITY='true'
FEATURE_EXTERNAL_LMS_CONFIGURATION=''
FEATURE_BULK_ENROLLMENT='true'
FEATURE_FILE_ATTACHMENT='true'
FEATURE_SETTINGS_PAGE='true'
FEATURE_SETTINGS_PAGE_LMS_TAB='true'
FEATURE_SETTINGS_PAGE_APPEARANCE_TAB='true'
FEATURE_LEARNER_CREDIT_MANAGEMENT='true'
FEATURE_CONTENT_HIGHLIGHTS='true'
FEATURE_AUTH0_SELF_SERVICE_INTEGRATION='false'
HOTJAR_DEBUG=''
HOTJAR_APP_ID=''
FEATURE_SSO_SETTINGS_TAB='true'
FEATURE_API_CREDENTIALS_TAB='true'
FEATURE_PENDING_ENROLLMENT_ACTIONS='false'
# maintenance alert
IS_MAINTENANCE_ALERT_ENABLED=''
MAINTENANCE_ALERT_MESSAGE='edX is currently in a brief maintenance window. Functionality involving course enrollments is unavailable at this time, including enrollment and assignment. Please check back shortly to continue the learning journey.'
MAINTENANCE_ALERT_START_TIMESTAMP=''

# Common

LMS_BASE_URL="https://courses.stage.edx.org"
STUDIO_BASE_URL="https://studio.stage.edx.org"
DATA_API_BASE_URL="https://analyticsapi.stage.edx.org"
ECOMMERCE_BASE_URL="https://ecommerce.stage.edx.org"
DISCOVERY_API_BASE_URL="https://discovery.stage.edx.org"
PUBLISHER_BASE_URL="https://publisher.stage.edx.org/"
CREDENTIALS_BASE_URL="https://credentials.stage.edx.org"
ENTERPRISE_CATALOG_API_BASE_URL="https://enterprise-catalog.stage.edx.org"
INSIGHTS_BASE_URL="https://stage-insights.edx.org"
LEARNING_BASE_URL="https://learning.stage.edx.org"
LOGIN_URL="https://courses.stage.edx.org/login"
LOGOUT_URL="https://courses.stage.edx.org/logout"
MARKETING_SITE_BASE_URL="https://stage.edx.org"
ORDER_HISTORY_URL="https://orders.stage.edx.org/orders"
ENTERPRISE_MARKETING_URL="https://business.edx.org"
REGISTRAR_API_BASE_URL="https://registrar.stage.edx.org/api"
OPTIMIZELY_PROJECT_ID="1706490390"
ENTERPRISE_LEARNER_PORTAL_HOSTNAME="enterprise.stage.edx.org"
ENTERPRISE_LEARNER_PORTAL_URL="https://enterprise.stage.edx.org"
DEMOGRAPHICS_BASE_URL="https://demographics.stage.edx.org"
EXAMS_BASE_URL="https://edx-exams.stage.edx.org"
ACCOUNT_SETTINGS_URL="https://account.stage.edx.org"
ACCOUNT_PROFILE_URL="https://profile.stage.edx.org"
SUPPORT_URL="https://support.edx.org"
ENTERPRISE_SUPPORT_URL="https://business-support.edx.org/hc/en-us"
ENTERPRISE_SUPPORT_PROGRAM_OPTIMIZATION_URL="https://business.edx.org/hubfs/Onboarding%20and%20Engagement/Onboarding%20Assets/Admin%20Resources/Program%20Optimization.pdf?hsLang=en"
ENTERPRISE_SUPPORT_LEARNER_CREDIT_URL='http://stage.edx.org'
CONTACT_URL="https://courses.stage.edx.org/support/contact_us"
OPEN_SOURCE_URL="https://open.edx.org"
TERMS_OF_SERVICE_URL="https://stage.edx.org/edx-terms-service"
PRIVACY_POLICY_URL="https://stage.edx.org/edx-privacy-policy"
SPANISH_PRIVACY_POLICY_URL="https://stage.edx.org/es/edx-privacy-policy"
SEARCH_CATALOG_URL="https://stage.edx.org/search"
FACEBOOK_URL="https://www.facebook.com/edX"
TWITTER_URL="https://twitter.com/edXOnline"
YOU_TUBE_URL="https://www.youtube.com/user/edxonline"
LINKED_IN_URL="https://www.linkedin.com/school/edx/"
GOOGLE_PLUS_URL="https://plus.google.com/+edXOnline"
REDDIT_URL="https://www.reddit.com/r/edx"
APPLE_APP_STORE_URL="https://itunes.apple.com/us/app/edx/id945480667?mt=8"
GOOGLE_PLAY_URL="https://play.google.com/store/apps/details?id=org.edx.mobile"
ENTERPRISE_SUPPORT_REVOKE_LICENSE_URL="https://business-support.edx.org/hc/en-us/articles/4409008473495"
LANGUAGE_PREFERENCE_COOKIE_NAME="stage-edx-language-preference"
NEW_RELIC_ACCOUNT_ID=""
NEW_RELIC_LICENSE_KEY=""
NEW_RELIC_TRUST_KEY=""
ACCESS_TOKEN_COOKIE_NAME="stage-edx-jwt-cookie-header-payload"
USER_INFO_COOKIE_NAME="stage-edx-user-info"
REFRESH_ACCESS_TOKEN_ENDPOINT="https://courses.stage.edx.org/login_refresh"
CSRF_TOKEN_API_PATH="/csrf/api/v1/token"
SITE_NAME="edX"
FAVICON_URL="https://edx-cdn.org/v3/stage/favicon.ico"
LOGO_TRADEMARK_URL="https://edx-cdn.org/v3/stage/logo-trademark.svg"
LOGO_TRADEMARK_URL_PNG="https://edx-cdn.org/v3/stage/logo-trademark.png"
LOGO_TRADEMARK_URL_SVG="https://edx-cdn.org/v3/stage/logo-trademark.svg"
LOGO_URL="https://edx-cdn.org/v3/stage/logo.svg"
LOGO_URL_PNG="https://edx-cdn.org/v3/stage/logo.png"
LOGO_URL_SVG="https://edx-cdn.org/v3/stage/logo.svg"
LOGO_WHITE_URL="https://edx-cdn.org/v3/stage/logo-white.svg"
LOGO_WHITE_URL_PNG="https://edx-cdn.org/v3/stage/logo-white.png"
LOGO_WHITE_URL_SVG="https://edx-cdn.org/v3/stage/logo-white.svg"
LOGO_POWERED_BY_OPEN_EDX_URL="https://edx-cdn.org/v3/stage/open-edx-tag.png"
LOGO_POWERED_BY_OPEN_EDX_URL_PNG="https://edx-cdn.org/v3/stage/open-edx-tag.png"
LOGO_POWERED_BY_OPEN_EDX_URL_SVG="https://edx-cdn.org/v3/stage/open-edx-tag.svg"
HOTJAR_VERSION=6
SESSION_COOKIE_DOMAIN=".stage.edx.org"

# Cookie Policy Banner
COOKIE_POLICY_BANNER_VIEWED_NAME="edx-cookie-policy-viewed"
MARKETING_SITE_NAME="edx.org"
82 changes: 81 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
![Build Status](https://github.com/openedx/frontend-app-admin-portal/actions/workflows/ci.yml/badge.svg)
![Codecov](https://codecov.io/gh/edx/frontend-app-admin-portal/branch/master/graph/badge.svg)

## Overview
# Purpose
frontend-app-admin-portal is a frontend that provides branded learning experiences as well as a dashboard for enterprise learning administrators.

# Getting Started

## Setting up a dev environment

### The Short Story
Expand Down Expand Up @@ -101,3 +103,81 @@ module.exports = {
```

NB: In order for webpack to properly resolve scss imports locally, you must use a `~` before the import, like so: `@import "~@edx/brand/paragon/fonts";`

### Running tests

You can run all tests as follows:
```
nvm use
npm install
npm run test
```

Additionally, you can run a single test file with
```
npm run test -- ProvisioningFormSubmissionButton.test.jsx
```

or run a given test function by appending a `.only` to the test function (or appending `.skip` to do the inverse).
For example: `test.only('your test function', () => {...})`.

You can use watch mode with tests as follows:
```
npm run test:watch BudgetDetailPage.test.jsx
# or to skip coverage reporting
npm run test:watch-no-cov BudgetDetailpage.test.jsx
```
Note the watcher has its own set of commands to help run test functions that match a regex (`t my regex`), etc.
Use the `w` command to get a list of valid watch commands.

## 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-admin-portal/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

## 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.

## 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/

## License

The code in this repository is licensed under the AGPLv3 unless otherwise
noted.

Please see `LICENSE <LICENSE>`_ for details.

## Reporting Security Issues

Please do not report security issues in public. Please email [email protected].
9 changes: 7 additions & 2 deletions __mocks__/react-instantsearch-dom.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@ const advertised_course_run = {
start: '2020-09-09T04:00:00Z',
key: 'course-v1:edX+Bee101+3T2020',
};
const mockNormalizedData = {
start_date: '2020-09-09T04:00:00Z',
end_date: '2021-09-09T04:00:00Z',
enroll_by_date: '2020-09-15T04:00:00Z',
};

/* eslint-disable camelcase */
const fakeHits = [
{ objectID: '1', aggregation_key: 'course:Bees101', title: 'bla', partners: [{ name: 'edX' }, { name: 'another_unused' }], advertised_course_run, key: 'Bees101' },
{ objectID: '2', aggregation_key: 'course:Wasps200', title: 'blp', partners: [{ name: 'edX' }, { name: 'another_unused' }], advertised_course_run, key: 'Wasps200' },
{ objectID: '1', aggregation_key: 'course:Bees101', title: 'bla', partners: [{ name: 'edX' }, { name: 'another_unused' }], advertised_course_run, key: 'Bees101', normalized_metadata: mockNormalizedData },
{ objectID: '2', aggregation_key: 'course:Wasps200', title: 'blp', partners: [{ name: 'edX' }, { name: 'another_unused' }], advertised_course_run, key: 'Wasps200', normalized_metadata: mockNormalizedData },
];
/* eslint-enable camelcase */

Expand Down
71 changes: 71 additions & 0 deletions docs/decisions/0006-tanstack-react-query.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
6. Adopting ``@tanstack/react-query`` for data fetching and client-side caching
=============================================================================

Status
******

Accepted (October 2023)

Context
*******

The ``frontend-app-admin-portal`` MFE currently relies on custom state variables when integrating with an API (e.g., managing loading states). As a result, there is generally a fair amount of boilerplate involved with each API integration. Additionally, the current approach does not provide any client-side caching out-of-the-box or any other best-in-class features like automatic query retries, which can lead to heavy reliance on approaches such as React Context or Redux in order to pass data returned by asynchronous API calls throughout the application.

The existing approach of heavily relying on React Context providers has resulted in many nested context providers that can make it difficult for contributors to understand what data is available to them from which context provider. Additionally, the reliance on context providers means accepting some performance risk that all components nested under a context provider will re-render whenever any value within the context provider changes, which can lead to performance issues if the context provider is wrapping a large number of components if not mitigated with techniques like ``React.memo``, ``useMemo``, or context selectors.

Decisions
*********

We will instead rely on ``@tanstack/react-query`` for data fetching and client-side caching. This library provides a number of benefits over the existing approach, including:

* Using ``@tanstack/react-query`` will allow us to avoid writing a significant amount of boilerplate code that is currently required to integrate with an API. We can rely on the library to handle loading states, error states, and other common API integration concerns.
* Flexible client-side caching, which will allow us to avoid using custom caching logic provided by ``@edx/frontend-platform``, which is not as full-featured.
* A number of other features out-of-the-box, including automatic query retries on failed network requests, which will allow us to avoid writing custom logic to handle these scenarios.
* Using ``@tanstack/react-query`` will allow us to avoid relying on React Context providers to pass data returned by asynchronous API calls throughout the application. Instead, we can rely on the library to handle this for us via custom hooks. For example, calling ``useQuery`` twice within the rendering lifecycle will not result in duplicate API calls. Instead, the library will first make the initial network call and then store its response in a client-side cache. The second call to ``useQuery`` will then return the cached response instead of making a duplicate network call. The cache invalidation is customizable globally for the application or by query.


We will adopt query key factories to manage the implementation of query keys such that cache invalidation for independent features (e.g., Learner Credit Management) can be managed with adequate granularity. For example:

::

// Query Key Factory
export const learnerCreditManagementQueryKeys = {
all: ['learner-credit-management'],
budgets: () => [...learnerCreditManagementQueryKeys.all, 'budgets'],
budget: (budgetId) => [...learnerCreditManagementQueryKeys.all, 'budget', budgetId],
budgetActivity: (budgetId) => [...learnerCreditManagementQueryKeys.budget(budgetId), 'activity'],
budgetActivityOverview: (budgetId) => [...learnerCreditManagementQueryKeys.budgetActivity(budgetId), 'overview'],
};

By having a query key factory as suggested above, contributors may have a structured way to manage query keys for a given feature. This approach enabled granular control over cache invalidation, query prefetching, etc. Using the above example, one could invalidate the query cache for the entire ``['learner-credit-management']`` feature or opt to only invalidate the query cache for specific individual queries or even groups of queries:

::

// Remove everything related to the learner credit management feature
queryClient.removeQueries({
queryKey: learnerCreditManagementQueryKeys.all,
})

// Invalidate all queries supporting the budget detail page route
queryClient.invalidateQueries({
queryKey: learnerCreditManagementQueryKeys.budget(budgetId),
})

// Invalidate the budget detail page route's activity tab's overview query
queryClient.invalidateQueries({
queryKey: learnerCreditManagementQueryKeys.budgetActivityOverview(budgetId),
})

The recommendation for new asynchronous network calls moving forward is to use ``@tanstack/react-query``. Additionally, it's recommended to incrementally migrate existing network calls to use ``@tanstack/react-query``. This will allow us to avoid a large refactoring effort and instead migrate to the library over time as we touch existing network calls.

Consequences
************

The entire application is wrapped within ``QueryClientProvider``, which contains a default configuration to at least extend the ``staleTime`` to be 20 seconds. This change in default behavior is to enable the use of ``@tanstack/react-query`` as a state manager. Instead of queries becoming instantly stale after success (i.e., ``staleTime: 0``) where network calls would be re-fetched on all window refocuses and component mounts, etc., having a ``staleTime`` of 20 seconds will keep the data fresh for 20 seconds before the query becomes stale, preventing unnecessary API calls (e.g., when calling duplicate ``useQuery`` hooks in the same rendering lifecycle).

By adopting ``@tanstack/react-query``, by default, queries made with ``useQuery`` will have some automatic background refetching behavior baked in. It's recommended to consider whether any specific queries should override/extend the default options provided by the library. For example, some specific queries may not want the automatic refetches, etc. that are provided by default.

Alternatives Considered
***********************

* In order to enable the same pattern of relying on ``@tanstack/react-query`` as a state manager to avoid additional context providers, it was considered whether we could make heavier use of the client-side caching provided by ``@edx/frontend-platform``. This was decided against as it is not as full-featured as ``@tanstack/react-query`` when it comes to caching alone. Additionally, it does not provide any other features that are provided by ``@tanstack/react-query``, which will enable more efficient API integrations moving forward.
Loading

0 comments on commit 09b7848

Please sign in to comment.