diff --git a/lms/djangoapps/support/static/support/jsx/entitlements/index.jsx b/lms/djangoapps/support/static/support/jsx/entitlements/index.jsx
new file mode 100644
index 0000000000..ce5b7487cc
--- /dev/null
+++ b/lms/djangoapps/support/static/support/jsx/entitlements/index.jsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+const EntitlementSupportPage = () => (
+
+ Base Entitlement Support Page
+
+);
+
+export default EntitlementSupportPage;
diff --git a/lms/djangoapps/support/tests/test_views.py b/lms/djangoapps/support/tests/test_views.py
index 32b8489ad9..9e853c9081 100644
--- a/lms/djangoapps/support/tests/test_views.py
+++ b/lms/djangoapps/support/tests/test_views.py
@@ -437,82 +437,3 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
)
verified_mode.expiration_datetime = datetime(year=1970, month=1, day=9, tzinfo=UTC)
verified_mode.save()
-
-
-@ddt.ddt
-class SupportViewCourseEntitlementsTests(SupportViewTestCase):
- """ Tests for the course entitlement support view."""
-
- def setUp(self):
- super(SupportViewCourseEntitlementsTests, self).setUp()
- self.user = UserFactory(is_staff=True)
- SupportStaffRole().add_users(self.user)
- self.client.login(username=self.user.username, password=TEST_PASSWORD)
-
- self.student = UserFactory.create(username='student', email='test@example.com', password='test')
- self.course_uuid = uuid4()
-
- self.url = reverse('support:course_entitlement')
-
- @ddt.data('username', 'email')
- def test_get_entitlements(self, search_string_type):
- CourseEntitlementFactory.create(mode=CourseMode.VERIFIED, user=self.student, course_uuid=self.course_uuid)
- url = self.url + getattr(self.student, search_string_type)
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
- data = json.loads(response.content)
- self.assertEqual(len(data), 1)
- self.assertDictContainsSubset({
- 'user': self.student.username,
- 'course_uuid': unicode(self.course_uuid),
- 'enrollment_course_run': None,
- 'mode': CourseMode.VERIFIED,
- 'support_details': []
- }, data[0])
-
- def test_reinstate_entitlement(self):
- selected_run = CourseEnrollmentFactory(mode=CourseMode.VERIFIED, user=self.student)
- expired_entitlement = CourseEntitlementFactory.create(
- mode=CourseMode.VERIFIED, user=self.student, enrollment_course_run=selected_run, expired_at=datetime.now()
- )
- url = self.url + self.student.username
- response = self.client.put(url, data=json.dumps(
- {
- 'entitlement_uuid': unicode(expired_entitlement.uuid),
- 'reason': CourseEntitlementSupportDetail.LEAVE_SESSION
- }),
- content_type='application/json'
- )
- self.assertEqual(response.status_code, 200)
- data = json.loads(response.content)
- self.assertEqual(len(data['support_details']), 1)
- self.assertDictContainsSubset({
- 'support_user': self.user.username,
- 'reason': CourseEntitlementSupportDetail.LEAVE_SESSION,
- 'comments': None,
- 'unenrolled_run': unicode(selected_run.course_id)
- }, data['support_details'][0])
-
- def test_create_entitlement(self):
- CourseEntitlementFactory.create(
- mode=CourseMode.VERIFIED, user=self.student, course_uuid=self.course_uuid, expired_at=datetime.now()
- )
- url = self.url + self.student.username
- response = self.client.post(
- url,
- data=json.dumps({
- 'course_uuid': unicode(self.course_uuid),
- 'reason': CourseEntitlementSupportDetail.LEARNER_REQUEST_NEW,
- 'mode': CourseMode.VERIFIED
- }),
- content_type='application/json',
- )
- self.assertEqual(response.status_code, 201)
- data = json.loads(response.content)
- self.assertEqual(len(data['support_details']), 1)
- self.assertDictContainsSubset({
- 'support_user': self.user.username,
- 'reason': CourseEntitlementSupportDetail.LEARNER_REQUEST_NEW,
- 'comments': None,
- 'unenrolled_run': None
- }, data['support_details'][0])
diff --git a/lms/djangoapps/support/urls.py b/lms/djangoapps/support/urls.py
index 1bd324d23a..494bfcb1a4 100644
--- a/lms/djangoapps/support/urls.py
+++ b/lms/djangoapps/support/urls.py
@@ -11,21 +11,13 @@ from support.views.index import index
from support.views.manage_user import ManageUserDetailView, ManageUserSupportView
from support.views.refund import RefundSupportView
-COURSE_ENTITLEMENTS_VIEW = EntitlementSupportView.as_view({
- 'get': 'list',
- 'post': 'create',
- 'put': 'update'
-})
+COURSE_ENTITLEMENTS_VIEW = EntitlementSupportView.as_view()
urlpatterns = [
url(r'^$', index, name="index"),
url(r'^certificates/?$', CertificatesSupportView.as_view(), name="certificates"),
url(r'^refund/?$', RefundSupportView.as_view(), name="refund"),
- url(
- r'^course_entitlement/(?P[\w.@+-]+)?$',
- COURSE_ENTITLEMENTS_VIEW,
- name="course_entitlement"
- ),
+ url(r'^course_entitlement/?$', COURSE_ENTITLEMENTS_VIEW, name="course_entitlement"),
url(r'^enrollment/?$', EnrollmentSupportView.as_view(), name="enrollment"),
url(r'^contact_us/?$', ContactUsView.as_view(), name="contact_us"),
url(
diff --git a/lms/djangoapps/support/views/course_entitlements.py b/lms/djangoapps/support/views/course_entitlements.py
index 79d64fdced..6a93c96464 100644
--- a/lms/djangoapps/support/views/course_entitlements.py
+++ b/lms/djangoapps/support/views/course_entitlements.py
@@ -6,20 +6,43 @@ from django.db import DatabaseError, transaction
from django.db.models import Q
from django.http import HttpResponseBadRequest
from django.utils.decorators import method_decorator
+from django.views.generic import View
from edx_rest_framework_extensions.authentication import JwtAuthentication
from rest_framework import permissions, status, viewsets
from rest_framework.response import Response
+from edxmako.shortcuts import render_to_response
from entitlements.api.v1.permissions import IsAdminOrAuthenticatedReadOnly
from entitlements.api.v1.serializers import SupportCourseEntitlementSerializer
from entitlements.models import CourseEntitlement, CourseEntitlementSupportDetail
+from lms.djangoapps.commerce.utils import EcommerceService
from lms.djangoapps.support.decorators import require_support_permission
from openedx.core.djangoapps.cors_csrf.authentication import SessionAuthenticationCrossDomainCsrf
REQUIRED_CREATION_FIELDS = ['course_uuid', 'reason', 'mode']
-class EntitlementSupportView(viewsets.ModelViewSet):
+class EntitlementSupportView(View):
+ """
+ View for viewing and changing learner enrollments, used by the
+ support team.
+ """
+ @method_decorator(require_support_permission)
+ def get(self, request):
+ """Render the enrollment support tool view."""
+ support_actions = CourseEntitlementSupportDetail.get_support_actions_list()
+
+ ecommerce_url = EcommerceService().get_order_dashboard_url()
+ context = {
+ 'username': request.GET.get('user', ''),
+ 'uses_bootstrap': True,
+ 'ecommerce_url': ecommerce_url,
+ 'support_actions': support_actions
+ }
+ return render_to_response('support/entitlement.html', context)
+
+
+class EntitlementSupportListView(viewsets.ModelViewSet):
"""
Allows viewing and changing learner course entitlements, used the support team.
"""
diff --git a/lms/templates/support/entitlement.html b/lms/templates/support/entitlement.html
new file mode 100644
index 0000000000..f193160f4c
--- /dev/null
+++ b/lms/templates/support/entitlement.html
@@ -0,0 +1,34 @@
+<%page expression_filter="h"/>
+
+<%!
+from django.utils.translation import ugettext as _
+from openedx.core.djangolib.js_utils import js_escaped_string
+%>
+
+## Override the default styles_version to use Bootstrap
+<%! main_css = "css/bootstrap/lms-main.css" %>
+
+<%namespace name='static' file='../static_content.html'/>
+
+<%inherit file="../main.html" />
+
+<%block name="js_extra">
+%block>
+
+<%block name="pagetitle">
+ ${_("Entitlements")}
+%block>
+
+<%block name="content">
+
+ ${static.renderReact(
+ component="EntitlementSupportPage",
+ id="entitlement-support-page",
+ props={
+ 'ecommerceUrl': ecommerce_url,
+ 'supportReasons': support_actions
+ }
+ )
+ }
+
+%block>
diff --git a/package-lock.json b/package-lock.json
index 06fd3ea667..43d52557cc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7981,11 +7981,10 @@
"integrity": "sha1-eHTi04kR0nGeoncx0yRF2l7EOUw="
},
"react": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz",
- "integrity": "sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=",
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-16.1.0.tgz",
+ "integrity": "sha512-hvKYlKqde2JNnNiEzORvSA0J1L7uSZ43l+J89ZNoP4EXxQrVNH0CFj8vorfPou3w+1ou1BNMBir2VVsuXtETRA==",
"requires": {
- "create-react-class": "15.6.3",
"fbjs": "0.8.16",
"loose-envify": "1.3.1",
"object-assign": "4.1.1",
@@ -8003,9 +8002,9 @@
}
},
"react-dom": {
- "version": "15.6.2",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.2.tgz",
- "integrity": "sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=",
+ "version": "16.1.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.1.0.tgz",
+ "integrity": "sha512-i9in5qW3H2PDinUPD9bnQK7tLAD8LhjYQ+fXi3nJOvVnxOO3ErHq6RNEnKY7pbjTPt155e74q7al8eBUuyLtew==",
"requires": {
"fbjs": "0.8.16",
"loose-envify": "1.3.1",
diff --git a/package.json b/package.json
index c6d2a49b73..9030300d23 100644
--- a/package.json
+++ b/package.json
@@ -40,8 +40,8 @@
"popper.js": "1.12.9",
"prop-types": "15.6.0",
"raw-loader": "0.5.1",
- "react": "15.6.2",
- "react-dom": "15.6.2",
+ "react": "16.1.0",
+ "react-dom": "16.1.0",
"react-slick": "0.16.0",
"requirejs": "2.3.5",
"rtlcss": "2.2.1",
diff --git a/webpack.common.config.js b/webpack.common.config.js
index 31c0cc1bd8..2c71829b2c 100644
--- a/webpack.common.config.js
+++ b/webpack.common.config.js
@@ -29,6 +29,7 @@ module.exports = {
LearnerAnalyticsDashboard: './lms/static/js/learner_analytics_dashboard/LearnerAnalyticsDashboard.jsx',
UpsellExperimentModal: './lms/static/common/js/components/UpsellExperimentModal.jsx',
PortfolioExperimentUpsellModal: './lms/static/common/js/components/PortfolioExperimentUpsellModal.jsx',
+ EntitlementSupportPage: './lms/djangoapps/support/static/support/jsx/entitlements/index.jsx',
// Learner Dashboard
EntitlementFactory: './lms/static/js/learner_dashboard/course_entitlement_factory.js',