Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
badb1f84
Unverified
Commit
badb1f84
authored
Dec 12, 2017
by
Harry Rein
Committed by
GitHub
Dec 12, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #16783 from edx/HarryRein/course-run-selection-program-dashboard
Harry rein/course run selection program dashboard
parents
a9506c1a
2711fb52
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
310 additions
and
158 deletions
+310
-158
common/djangoapps/entitlements/models.py
+8
-0
lms/static/js/learner_dashboard/models/course_card_model.js
+18
-7
lms/static/js/learner_dashboard/models/course_entitlement_model.js
+1
-2
lms/static/js/learner_dashboard/views/course_card_view.js
+28
-1
lms/static/js/learner_dashboard/views/course_enroll_view.js
+1
-1
lms/static/js/learner_dashboard/views/course_entitlement_view.js
+57
-38
lms/static/js/learner_dashboard/views/program_details_view.js
+0
-1
lms/static/lms/js/spec/main.js
+6
-0
lms/static/sass/_build-learner-dashboard.scss
+3
-0
lms/static/sass/_build-lms-v1.scss
+3
-2
lms/static/sass/multicourse/_dashboard.scss
+1
-83
lms/static/sass/views/_course-entitlements.scss
+127
-0
lms/static/sass/views/_program-details.scss
+11
-3
lms/templates/dashboard.html
+4
-3
lms/templates/dashboard/_dashboard_course_listing.html
+1
-2
lms/templates/learner_dashboard/course_card.underscore
+8
-4
lms/templates/learner_dashboard/course_enroll.underscore
+1
-1
lms/templates/learner_dashboard/course_entitlement.underscore
+2
-2
lms/templates/learner_dashboard/program_details_fragment.html
+4
-0
openedx/core/djangoapps/programs/utils.py
+16
-5
themes/edx.org/lms/templates/dashboard.html
+4
-3
webpack.common.config.js
+6
-0
No files found.
common/djangoapps/entitlements/models.py
View file @
badb1f84
...
...
@@ -213,6 +213,14 @@ class CourseEntitlement(TimeStampedModel):
"""
return
self
.
policy
.
is_entitlement_redeemable
(
self
)
def
to_dict
(
self
):
""" Convert entitlement to dictionary representation. """
return
{
'uuid'
:
str
(
self
.
uuid
),
'course_uuid'
:
str
(
self
.
course_uuid
),
'expired_at'
:
self
.
expired_at
}
@classmethod
def
set_enrollment
(
cls
,
entitlement
,
enrollment
):
"""
...
...
lms/static/js/learner_dashboard/models/course_card_model.js
View file @
badb1f84
...
...
@@ -42,6 +42,11 @@
return
desiredCourseRun
;
},
isEnrolledInSession
:
function
()
{
// Returns true if the user is currently enrolled in a session of the course
return
_
.
findWhere
(
this
.
context
.
course_runs
,
{
is_enrolled
:
true
})
!==
undefined
;
},
getUnselectedCourseRun
:
function
(
courseRuns
)
{
var
unselectedRun
=
{},
courseRun
;
...
...
@@ -143,8 +148,9 @@
formatDateString
:
function
(
run
)
{
var
pacingType
=
run
.
pacing_type
,
dateString
,
start
=
this
.
get
(
'start_date'
)
||
run
.
start_date
,
end
=
this
.
get
(
'end_date'
)
||
run
.
end_date
,
start
=
this
.
valueIsDefined
(
run
.
start_date
)
?
run
.
advertised_start
||
run
.
start_date
:
this
.
get
(
'start_date'
),
end
=
this
.
valueIsDefined
(
run
.
end_date
)
?
run
.
end_date
:
this
.
get
(
'end_date'
),
now
=
new
Date
(),
startDate
=
new
Date
(
start
),
endDate
=
new
Date
(
end
);
...
...
@@ -178,26 +184,27 @@
},
setActiveCourseRun
:
function
(
courseRun
,
userPreferences
)
{
var
startDateString
;
var
startDateString
,
isEnrolled
=
this
.
isEnrolledInSession
()
&&
courseRun
.
key
;
if
(
courseRun
)
{
if
(
this
.
valueIsDefined
(
courseRun
.
advertised_start
))
{
startDateString
=
courseRun
.
advertised_start
;
}
else
{
startDateString
=
this
.
formatDate
(
courseRun
.
start
,
userPreferences
);
}
this
.
set
({
certificate_url
:
courseRun
.
certificate_url
,
course_run_key
:
courseRun
.
key
,
course_run_key
:
courseRun
.
key
||
''
,
course_url
:
courseRun
.
course_url
||
''
,
title
:
this
.
context
.
title
,
end_date
:
this
.
formatDate
(
courseRun
.
end
,
userPreferences
),
enrollable_course_runs
:
this
.
getEnrollableCourseRuns
(),
is_course_ended
:
courseRun
.
is_course_ended
,
is_enrolled
:
courseRun
.
is_e
nrolled
,
is_enrolled
:
isE
nrolled
,
is_enrollment_open
:
courseRun
.
is_enrollment_open
,
course_key
:
this
.
context
.
key
,
user_entitlement
:
this
.
context
.
user_entitlement
,
is_unfulfilled_entitlement
:
this
.
context
.
user_entitlement
&&
!
isEnrolled
,
marketing_url
:
courseRun
.
marketing_url
,
mode_slug
:
courseRun
.
type
,
start_date
:
startDateString
,
...
...
@@ -220,6 +227,10 @@
updateCourseRun
:
function
(
courseRunKey
)
{
var
selectedCourseRun
=
_
.
findWhere
(
this
.
get
(
'course_runs'
),
{
key
:
courseRunKey
});
if
(
selectedCourseRun
)
{
// Update the current context to set the course run to the enrolled state
_
.
each
(
this
.
context
.
course_runs
,
function
(
run
)
{
if
(
run
.
key
===
selectedCourseRun
.
key
)
run
.
is_enrolled
=
true
;
// eslint-disable-line no-param-reassign, max-len
});
this
.
setActiveCourseRun
(
selectedCourseRun
);
}
}
...
...
lms/static/js/learner_dashboard/models/course_entitlement_model.js
View file @
badb1f84
/**
* Store data for the current
* Store data for the current
entitlement.
*/
(
function
(
define
)
{
'use strict'
;
...
...
@@ -13,7 +13,6 @@
availableSessions
:
[],
entitlementUUID
:
''
,
currentSessionId
:
''
,
userId
:
''
,
courseName
:
''
}
});
...
...
lms/static/js/learner_dashboard/views/course_card_view.js
View file @
badb1f84
...
...
@@ -11,6 +11,7 @@
'js/learner_dashboard/views/certificate_status_view'
,
'js/learner_dashboard/views/expired_notification_view'
,
'js/learner_dashboard/views/course_enroll_view'
,
'js/learner_dashboard/views/course_entitlement_view'
,
'text!../../../templates/learner_dashboard/course_card.underscore'
],
function
(
...
...
@@ -24,6 +25,7 @@
CertificateStatusView
,
ExpiredNotificationView
,
CourseEnrollView
,
EntitlementView
,
pageTpl
)
{
return
Backbone
.
View
.
extend
({
...
...
@@ -41,6 +43,8 @@
this
.
grade
=
this
.
context
.
courseData
.
grades
[
this
.
model
.
get
(
'course_run_key'
)];
this
.
grade
=
this
.
grade
*
100
;
this
.
collectionCourseStatus
=
this
.
context
.
collectionCourseStatus
||
''
;
this
.
entitlement
=
this
.
model
.
get
(
'user_entitlement'
);
this
.
render
();
this
.
listenTo
(
this
.
model
,
'change'
,
this
.
render
);
},
...
...
@@ -57,7 +61,9 @@
var
$upgradeMessage
=
this
.
$
(
'.upgrade-message'
),
$certStatus
=
this
.
$
(
'.certificate-status'
),
$expiredNotification
=
this
.
$
(
'.expired-notification'
),
expired
=
this
.
model
.
get
(
'expired'
);
expired
=
this
.
model
.
get
(
'expired'
),
courseUUID
=
this
.
model
.
get
(
'uuid'
),
containerSelector
=
'#course-'
+
courseUUID
;
this
.
enrollView
=
new
CourseEnrollView
({
$parentEl
:
this
.
$
(
'.course-actions'
),
...
...
@@ -68,6 +74,27 @@
enrollModel
:
this
.
enrollModel
});
if
(
this
.
entitlement
)
{
this
.
sessionSelectionView
=
new
EntitlementView
({
el
:
this
.
$
(
containerSelector
+
' .course-entitlement-selection-container'
),
$parentEl
:
this
.
$el
,
courseCardModel
:
this
.
model
,
enrollModel
:
this
.
enrollModel
,
triggerOpenBtn
:
'.course-details .change-session'
,
courseCardMessages
:
''
,
courseImageLink
:
''
,
courseTitleLink
:
containerSelector
+
' .course-details .course-title'
,
dateDisplayField
:
containerSelector
+
' .course-details .course-text'
,
enterCourseBtn
:
containerSelector
+
' .view-course-button'
,
availableSessions
:
JSON
.
stringify
(
this
.
model
.
get
(
'course_runs'
)),
entitlementUUID
:
this
.
entitlement
.
uuid
,
currentSessionId
:
this
.
model
.
isEnrolledInSession
()
?
this
.
model
.
get
(
'course_run_key'
)
:
null
,
enrollUrl
:
this
.
model
.
get
(
'enroll_url'
),
courseHomeUrl
:
this
.
model
.
get
(
'course_url'
)
});
}
if
(
this
.
model
.
get
(
'upgrade_url'
)
&&
!
(
expired
===
true
))
{
this
.
upgradeMessage
=
new
UpgradeMessageView
({
$el
:
$upgradeMessage
,
...
...
lms/static/js/learner_dashboard/views/course_enroll_view.js
View file @
badb1f84
...
...
@@ -57,7 +57,7 @@
// Enrollment click event handled here
var
courseRunKey
=
$
(
'.run-select'
).
val
()
||
this
.
model
.
get
(
'course_run_key'
);
this
.
model
.
updateCourseRun
(
courseRunKey
);
if
(
!
this
.
model
.
get
(
'is_enrolled'
))
{
if
(
this
.
model
.
get
(
'is_enrolled'
))
{
// Create the enrollment.
this
.
enrollModel
.
save
({
course_id
:
courseRunKey
...
...
lms/static/js/learner_dashboard/views/course_entitlement_view.js
View file @
badb1f84
...
...
@@ -37,12 +37,12 @@
initialize
:
function
(
options
)
{
// Set up models and reload view on change
this
.
courseCardModel
=
new
CourseCardModel
();
this
.
courseCardModel
=
options
.
courseCardModel
||
new
CourseCardModel
();
this
.
enrollModel
=
options
.
enrollModel
;
this
.
entitlementModel
=
new
EntitlementModel
({
availableSessions
:
this
.
formatDates
(
JSON
.
parse
(
options
.
availableSessions
)),
entitlementUUID
:
options
.
entitlementUUID
,
currentSessionId
:
options
.
currentSessionId
,
userId
:
options
.
userId
,
courseName
:
options
.
courseName
});
this
.
listenTo
(
this
.
entitlementModel
,
'change'
,
this
.
render
);
...
...
@@ -51,13 +51,18 @@
this
.
enrollUrl
=
options
.
enrollUrl
;
this
.
courseHomeUrl
=
options
.
courseHomeUrl
;
// Grab elements from the parent card that work with this view and bind associated events
this
.
$triggerOpenBtn
=
$
(
options
.
triggerOpenBtn
);
// Opens/closes session selection view
this
.
$dateDisplayField
=
$
(
options
.
dateDisplayField
);
// Displays current session dates
// Grab elements from the parent card that work with this view
this
.
$parentEl
=
options
.
$parentEl
;
// Containing course card (must be a backbone view root el)
this
.
$enterCourseBtn
=
$
(
options
.
enterCourseBtn
);
// Button link to course home page
this
.
$courseCardMessages
=
$
(
options
.
courseCardMessages
);
// Additional session messages
this
.
$courseTitleLink
=
$
(
options
.
courseTitleLink
);
// Title link to course home page
this
.
$courseImageLink
=
$
(
options
.
courseImageLink
);
// Image link to course home page
// Bind action elements with associated events to objects outside this view
this
.
$dateDisplayField
=
this
.
$parentEl
?
this
.
$parentEl
.
find
(
options
.
dateDisplayField
)
:
$
(
options
.
dateDisplayField
);
// Displays current session dates
this
.
$triggerOpenBtn
=
this
.
$parentEl
?
this
.
$parentEl
.
find
(
options
.
triggerOpenBtn
)
:
$
(
options
.
triggerOpenBtn
);
// Opens/closes session selection view
this
.
$triggerOpenBtn
.
on
(
'click'
,
this
.
toggleSessionSelectionPanel
.
bind
(
this
));
this
.
render
(
options
);
...
...
@@ -72,15 +77,17 @@
},
postRender
:
function
()
{
// Close
popover
on click-away
// Close
any visible popovers
on click-away
$
(
document
).
on
(
'click'
,
function
(
e
)
{
if
(
!
(
$
(
e
.
target
).
closest
(
'.enroll-btn-initial, .popover'
).
length
))
{
if
(
this
.
$
(
'.popover:visible'
).
length
&&
!
(
$
(
e
.
target
).
closest
(
'.enroll-btn-initial, .popover'
).
length
))
{
this
.
hideDialog
(
this
.
$
(
'.enroll-btn-initial'
));
}
}.
bind
(
this
));
this
.
$
(
'.enroll-btn-initial'
).
click
(
function
(
e
)
{
this
.
showDialog
(
$
(
e
.
target
));
// Initialize focus to cancel button on popover load
$
(
document
).
on
(
'shown.bs.popover'
,
function
()
{
this
.
$
(
'.final-confirmation-btn:first'
).
focus
();
}.
bind
(
this
));
},
...
...
@@ -130,7 +137,13 @@
*/
var
successIconEl
=
'<span class="fa fa-check" aria-hidden="true"></span>'
;
// Update the model with the new session Id;
// With a containing backbone view, we can simply re-render the parent card
if
(
this
.
$parentEl
)
{
this
.
courseCardModel
.
updateCourseRun
(
this
.
currentSessionSelection
);
return
;
}
// Update the model with the new session Id
this
.
entitlementModel
.
set
({
currentSessionId
:
this
.
currentSessionSelection
});
// Allow user to change session
...
...
@@ -161,6 +174,11 @@
3) Remove the messages associated with the enrolled state.
4) Remove the link from the course card image and title.
*/
// With a containing backbone view, we can simply re-render the parent card
if
(
this
.
$parentEl
)
{
this
.
courseCardModel
.
setUnselected
();
return
;
}
// Update the model with the new session Id;
this
.
entitlementModel
.
set
({
currentSessionId
:
this
.
currentSessionSelection
});
...
...
@@ -198,13 +216,18 @@
},
enrollError
:
function
()
{
var
errorMsgEl
=
HtmlUtils
.
HTML
(
gettext
(
'There was an error. Please reload the page and try again.'
)
// Display a success indicator
var
errorMsgEl
=
HtmlUtils
.
joinHtml
(
HtmlUtils
.
HTML
(
'<span class="enroll-error">'
),
gettext
(
'There was an error. Please reload the page and try again.'
),
HtmlUtils
.
HTML
(
'</spandiv>'
)
).
text
;
this
.
$dateDisplayField
.
find
(
'.fa.fa-spin'
)
.
removeClass
(
'fa-spin fa-spinner'
)
.
addClass
(
'fa-close'
);
this
.
$dateDisplayField
.
append
(
errorMsgEl
);
this
.
hideDialog
(
this
.
$
(
'.enroll-btn-initial'
));
},
...
...
@@ -237,7 +260,6 @@
enrollText
=
gettext
(
'Leave Current Session'
);
}
enrollBtnInitial
.
text
(
enrollText
);
this
.
removeDialog
(
enrollBtnInitial
);
this
.
initializeVerificationDialog
(
enrollBtnInitial
);
},
...
...
@@ -263,7 +285,6 @@
*/
var
confirmationMsgTitle
,
confirmationMsgBody
,
popoverDialogHtml
,
currentSessionId
=
this
.
entitlementModel
.
get
(
'currentSessionId'
),
newSessionId
=
this
.
$
(
'.session-select'
).
find
(
'option:selected'
).
data
(
'session_id'
);
...
...
@@ -279,38 +300,35 @@
confirmationMsgBody
=
gettext
(
'Any course progress or grades from your current session will be lost.'
);
// eslint-disable-line max-len
}
// Remove existing popover and re-initialize
popoverDialogHtml
=
this
.
verificationTpl
({
confirmationMsgTitle
:
confirmationMsgTitle
,
confirmationMsgBody
:
confirmationMsgBody
});
// Re-initialize the popover
invokingElement
.
popover
({
placement
:
'bottom'
,
container
:
this
.
$el
,
html
:
true
,
trigger
:
'click'
,
content
:
popoverDialogHtml
.
text
content
:
this
.
verificationTpl
({
confirmationMsgTitle
:
confirmationMsgTitle
,
confirmationMsgBody
:
confirmationMsgBody
}).
text
});
},
removeDialog
:
function
(
invokingElement
)
{
removeDialog
:
function
(
el
)
{
/* Removes the Bootstrap v4 dialog modal from the update session enrollment button. */
invokingElement
.
popover
(
'dispose'
);
},
showDialog
:
function
(
invokingElement
)
{
/* Given an element with an associated dialog modal, shows the modal. */
invokingElement
.
popover
(
'show'
);
this
.
$
(
'.final-confirmation-btn:first'
).
focus
();
var
$el
=
el
instanceof
jQuery
?
el
:
this
.
$
(
'.enroll-btn-initial'
);
if
(
this
.
$
(
'popover'
).
length
)
{
$el
.
popover
(
'dispose'
);
}
},
hideDialog
:
function
(
el
,
returnFocus
)
{
/* Hides the
modal
without removing it from the DOM. */
/* Hides the
modal if it is visible
without removing it from the DOM. */
var
$el
=
el
instanceof
jQuery
?
el
:
this
.
$
(
'.enroll-btn-initial'
);
$el
.
popover
(
'hide'
);
if
(
returnFocus
)
{
$el
.
focus
();
if
(
this
.
$
(
'.popover:visible'
).
length
)
{
$el
.
popover
(
'hide'
);
if
(
returnFocus
)
{
$el
.
focus
();
}
}
},
...
...
@@ -363,12 +381,13 @@
return
_
.
map
(
formattedSessionData
,
function
(
session
)
{
var
formattedSession
=
session
;
startDate
=
this
.
formatDate
(
formattedSession
.
s
ession_s
tart
,
dateFormat
);
endDate
=
this
.
formatDate
(
formattedSession
.
session_
end
,
dateFormat
);
startDate
=
this
.
formatDate
(
formattedSession
.
start
,
dateFormat
);
endDate
=
this
.
formatDate
(
formattedSession
.
end
,
dateFormat
);
formattedSession
.
enrollment_end
=
this
.
formatDate
(
formattedSession
.
enrollment_end
,
dateFormat
);
formattedSession
.
session_dates
=
this
.
courseCardModel
.
formatDateString
({
start_date
:
session
.
session_start_advertised
||
startDate
,
end_date
:
session
.
session_start_advertised
?
null
:
endDate
,
start_date
:
startDate
,
advertised_start
:
session
.
advertised_start
,
end_date
:
endDate
,
pacing_type
:
formattedSession
.
pacing_type
});
return
formattedSession
;
...
...
@@ -376,7 +395,7 @@
},
formatDate
:
function
(
date
,
dateFormat
)
{
return
date
?
moment
((
new
Date
(
date
))).
format
(
dateFormat
)
:
null
;
return
date
?
moment
((
new
Date
(
date
))).
format
(
dateFormat
)
:
''
;
},
getAvailableSessionWithId
:
function
(
sessionId
)
{
...
...
lms/static/js/learner_dashboard/views/program_details_view.js
View file @
badb1f84
...
...
@@ -36,7 +36,6 @@
initialize
:
function
(
options
)
{
this
.
options
=
options
;
this
.
programModel
=
new
Backbone
.
Model
(
this
.
options
.
programData
);
this
.
courseData
=
new
Backbone
.
Model
(
this
.
options
.
courseData
);
this
.
certificateCollection
=
new
Backbone
.
Collection
(
this
.
options
.
certificateData
);
...
...
lms/static/lms/js/spec/main.js
View file @
badb1f84
...
...
@@ -46,6 +46,8 @@
'backbone.associations'
:
'xmodule_js/common_static/js/vendor/backbone-associations-min'
,
'backbone.paginator'
:
'common/js/vendor/backbone.paginator'
,
'backbone-super'
:
'js/vendor/backbone-super'
,
'popper'
:
'common/js/vendor/popper'
,
'bootstrap'
:
'common/js/vendor/bootstrap'
,
'URI'
:
'xmodule_js/common_static/js/vendor/URI.min'
,
'tinymce'
:
'xmodule_js/common_static/js/vendor/tinymce/js/tinymce/tinymce.full.min'
,
'jquery.tinymce'
:
'xmodule_js/common_static/js/vendor/tinymce/js/tinymce/jquery.tinymce'
,
...
...
@@ -197,6 +199,10 @@
'backbone-super'
:
{
deps
:
[
'backbone'
]
},
'bootstrap'
:
{
deps
:
[
'jquery'
,
'popper'
],
exports
:
'bootstrap'
},
'paging-collection'
:
{
deps
:
[
'jquery'
,
'underscore'
,
'backbone.paginator'
]
},
...
...
lms/static/sass/_build-learner-dashboard.scss
View file @
badb1f84
...
...
@@ -10,5 +10,8 @@
@import
'elements/program-card'
;
@import
'elements-v2/icons'
;
@import
'elements/progress-circle'
;
// Various View Styling
@import
'views/course-entitlements'
;
@import
'views/program-details'
;
@import
'views/program-list'
;
lms/static/sass/_build-lms-v1.scss
View file @
badb1f84
...
...
@@ -51,7 +51,8 @@
@import
'multicourse/survey-page'
;
// base - specific views
@import
"views/account-settings"
;
@import
'views/account-settings'
;
@import
'views/course-entitlements'
;
@import
'views/login-register'
;
@import
'views/verification'
;
@import
'views/decoupled-verification'
;
...
...
@@ -59,7 +60,7 @@
@import
'views/homepage'
;
@import
'views/support'
;
@import
'views/oauth2'
;
@import
"views/financial-assistance"
;
@import
'views/financial-assistance'
;
@import
'course/auto-cert'
;
@import
'views/api-access'
;
...
...
lms/static/sass/multicourse/_dashboard.scss
View file @
badb1f84
...
...
@@ -1045,6 +1045,7 @@
.related-programs-preface
{
@include
float
(
left
);
margin
:
0
$baseline
/
2
;
font-weight
:
bold
;
}
...
...
@@ -1095,89 +1096,6 @@
@include
padding
(
$baseline
/
2
,
$baseline
,
$baseline
/
2
,
$baseline
/
2
);
}
}
// Course Entitlement Session Selection
.course-entitlement-selection-container
{
background-color
:
theme-color
(
"inverse"
);
.action-header
{
padding-bottom
:
$baseline
/
4
;
font-weight
:
$font-weight-bold
;
color
:
theme-color
(
"dark"
);
}
.action-controls
{
display
:
flex
;
.session-select
{
background-color
:
theme-color
(
"inverse"
);
height
:
$baseline
*
1
.5
;
flex-grow
:
5
;
margin-bottom
:
$baseline
*
0
.4
;
}
.enroll-btn-initial
{
@include
margin-left
(
$baseline
);
height
:
$baseline
*
1
.5
;
flex-grow
:
1
;
letter-spacing
:
0
;
background
:
theme-color
(
"inverse"
);
border-color
:
theme-color
(
"primary"
);
color
:
theme-color
(
"primary"
);
text-shadow
:
none
;
font-size
:
$font-size-base
;
padding
:
0
;
box-shadow
:
none
;
border-radius
:
$border-radius-sm
;
transition
:
all
0
.4s
ease-out
;
&
:hover
{
background
:
theme-color
(
"primary"
);
border-color
:
theme-color
(
"primary"
);
color
:
theme-color
(
"inverse"
);
}
}
@include
media-breakpoint-down
(
xs
)
{
flex-direction
:
column
;
.enroll-btn-initial
{
margin
:
$baseline
/
4
0
$baseline
/
4
;
}
}
}
.popover
{
.popover-title
{
margin-bottom
:
$baseline
/
2
;
}
.action-items
{
display
:
flex
;
justify-content
:
space-between
;
margin-top
:
$baseline
/
2
;
.final-confirmation-btn
{
box-shadow
:
none
;
border
:
1px
solid
theme-color
(
"dark"
);
background
:
none
;
color
:
theme-color
(
"dark"
);
text-shadow
:
none
;
letter-spacing
:
0
;
flex-grow
:
1
;
margin
:
0
$baseline
/
4
;
padding
:
$baseline
/
10
$baseline
;
font-size
:
$font-size-base
;
&
:hover
{
background
:
theme-color
(
"primary"
);
color
:
theme-color
(
"inverse"
);
}
}
}
}
}
}
// CASE: empty dashboard
...
...
lms/static/sass/views/_course-entitlements.scss
0 → 100644
View file @
badb1f84
// Shared styling between courses and programs dashboard
.course-entitlement-selection-container
{
width
:
100%
;
position
:
relative
;
flex-grow
:
1
;
.action-header
{
padding-bottom
:
$baseline
/
4
;
font-weight
:
$font-weight-bold
;
color
:
theme-color
(
"dark"
);
}
.action-controls
{
display
:
flex
;
.session-select
{
background-color
:
theme-color
(
"inverse"
);
height
:
$baseline
*
1
.5
;
flex-grow
:
5
;
margin-bottom
:
$baseline
*
0
.4
;
max-width
:
calc
(
100%
-
200px
);
}
.enroll-btn-initial
{
@include
margin-left
(
$baseline
);
height
:
$baseline
*
1
.5
;
flex-grow
:
1
;
letter-spacing
:
0
;
background
:
theme-color
(
"inverse"
);
border-color
:
theme-color
(
"primary"
);
color
:
theme-color
(
"primary"
);
text-shadow
:
none
;
font-size
:
$font-size-base
;
padding
:
0
;
box-shadow
:
none
;
border-radius
:
$border-radius-sm
;
transition
:
all
0
.4s
ease-out
;
&
:hover
{
background
:
theme-color
(
"primary"
);
border-color
:
theme-color
(
"primary"
);
color
:
theme-color
(
"inverse"
);
}
&
.disabled
{
pointer-events
:
none
;
opacity
:
0
.5
;
}
}
@include
media-breakpoint-down
(
xs
)
{
flex-direction
:
column
;
.session-select
{
max-width
:
100%
;
}
.enroll-btn-initial
{
margin
:
$baseline
/
4
0
;
}
}
}
.popover
{
.popover-title
{
margin-bottom
:
$baseline
/
2
;
}
.action-items
{
display
:
flex
;
justify-content
:
space-between
;
margin-top
:
$baseline
/
2
;
.final-confirmation-btn
{
box-shadow
:
none
;
border
:
1px
solid
theme-color
(
"dark"
);
background
:
none
;
color
:
theme-color
(
"dark"
);
text-shadow
:
none
;
letter-spacing
:
0
;
flex-grow
:
1
;
margin
:
0
$baseline
/
4
;
padding
:
$baseline
/
10
$baseline
;
font-size
:
$font-size-base
;
&
:hover
{
background
:
theme-color
(
"primary"
);
color
:
theme-color
(
"inverse"
);
}
}
}
}
}
// Styling overrides specific to the programs dashboard
.program-course-card
{
.course-text
{
.fa-close
{
color
:
theme-color
(
"error"
);
}
.enroll-error
{
@include
margin-left
(
$baseline
/
4
);
font-size
:
$font-size-sm
;
}
.change-session
{
@include
margin
(
0
,
0
,
$baseline
/
4
,
$baseline
/
4
);
padding
:
0
;
font-size
:
$font-size-sm
;
letter-spacing
:
normal
;
}
}
.course-entitlement-selection-container
{
padding-top
:
$baseline
/
2
;
.action-header
,
.action-controls
.session-select
{
font-size
:
$font-size-sm
;
}
}
}
lms/static/sass/views/_program-details.scss
View file @
badb1f84
...
...
@@ -437,7 +437,6 @@
.program-course-card
{
width
:
100%
;
padding
:
15px
;
margin-bottom
:
10px
;
@media
(
min-width
:
$bp-screen-md
)
{
height
:
auto
;
...
...
@@ -445,6 +444,7 @@
.section
{
display
:
flex
;
flex-direction
:
column
;
justify-content
:
space-between
;
@media
(
min-width
:
$bp-screen-md
)
{
...
...
@@ -452,6 +452,10 @@
}
}
.section
:not
(
:last-child
)
{
margin-bottom
:
$baseline
/
2
;
}
.section
:not
(
:first-child
)
{
margin-top
:
0
;
}
...
...
@@ -461,10 +465,14 @@
.course-title
{
font-size
:
1em
;
color
:
palette
(
primary
,
base
);
font-weight
:
600
;
text-decoration
:
underline
;
margin
:
0
;
.course-title-link
,
.course-title-link
:visited
{
color
:
palette
(
primary
,
base
);
text-decoration
:
underline
;
}
}
.run-period
{
...
...
lms/templates/dashboard.html
View file @
badb1f84
...
...
@@ -34,6 +34,7 @@ from student.models import CourseEnrollment
</script>
% endfor
% if course_entitlements:
<!-- This is a temporary solution before we land a fix to load these through Webpack, tracked by LEARNER-3483 -->
<script
type=
"text/javascript"
src=
"${static.url('common/js/vendor/popper.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('common/js/vendor/bootstrap.js')}"
></script>
% endif
...
...
@@ -141,9 +142,9 @@ from student.models import CourseEnrollment
'
session_id
'
:
course
['
key
'],
'
enrollment_end
'
:
course
['
enrollment_end
'],
'
pacing_type
'
:
course
['
pacing_type
'],
'
session_start_advertised
'
:
CourseOverview
.
get_from_id
(
CourseKey
.
from_string
(
course
['
key
'])).
advertised_start
,
'
s
ession_s
tart
'
:
CourseOverview
.
get_from_id
(
CourseKey
.
from_string
(
course
['
key
'])).
start
,
'
session_
end
'
:
CourseOverview
.
get_from_id
(
CourseKey
.
from_string
(
course
['
key
'])).
end
,
'
advertised_start
'
:
CourseOverview
.
get_from_id
(
CourseKey
.
from_string
(
course
['
key
'])).
advertised_start
,
'
start
'
:
CourseOverview
.
get_from_id
(
CourseKey
.
from_string
(
course
['
key
'])).
start
,
'
end
'
:
CourseOverview
.
get_from_id
(
CourseKey
.
from_string
(
course
['
key
'])).
end
,
}
for
course
in
course_entitlement_available_sessions
[
str
(
entitlement
.
uuid
)]]
if
is_fulfilled_entitlement:
#
If
the
user
has
a
fulfilled
entitlement
,
pass
through
the
entitlements
CourseEnrollment
object
...
...
lms/templates/dashboard/_dashboard_course_listing.html
View file @
badb1f84
...
...
@@ -291,8 +291,7 @@ from util.course import get_link_for_about_page, get_encoded_course_sharing_utm_
enterCourseBtn: '${ '#course-card-' + str(course_card_index) + ' .enter-course' | n, js_escaped_string }',
availableSessions: '${ entitlement_available_sessions | n, dump_js_escaped_json }',
entitlementUUID: '${ entitlement.course_uuid | n, js_escaped_string }',
currentSessionId: '${ entitlement_session.course_id if entitlement_session else '' | n, js_escaped_string }',
userId: '${ user.id | n, js_escaped_string }',
currentSessionId: '${ entitlement_session.course_id if entitlement_session else "" | n, js_escaped_string }',
enrollUrl: '${ reverse('entitlements_api:v1:enrollments', args=[str(entitlement.uuid)]) | n, js_escaped_string }',
courseHomeUrl: '${ course_target | n, js_escaped_string }'
});
...
...
lms/templates/learner_dashboard/course_card.underscore
View file @
badb1f84
<div class="section">
<div class="section"
id="course-<%-uuid%>"
>
<div class="course-meta-container">
<div class="course-content">
<div class="course-details">
<h5 class="course-title">
<% if (
marketing_url || course_url
) { %>
<% if (
(marketing_url || course_url) && !is_unfulfilled_entitlement
) { %>
<a href="<%- marketing_url || course_url %>" class="course-title-link">
<%- title %>
</a>
...
...
@@ -12,11 +12,14 @@
<% } %>
</h5>
<div class="course-text">
<% if (enrolled) { %>
<% if (enrolled
&& !user_entitlement
) { %>
<span class="enrolled"><%- enrolled %>: </span>
<% } %>
<% if (dateString) { %>
<% if (dateString
&& !is_unfulfilled_entitlement
) { %>
<span class="run-period"><%- dateString %></span>
<% if (user_entitlement && !is_unfulfilled_entitlement) { %>
<button class="change-session btn-link" aria-controls="change-session-<%-user_entitlement.uuid%>"> <%- gettext('Change Session')%></button>
<% } %>
<% } %>
</div>
</div>
...
...
@@ -24,6 +27,7 @@
</div>
<div class="course-certificate certificate-status"></div>
</div>
<div class="course-entitlement-selection-container<% if (!is_unfulfilled_entitlement && user_entitlement) { %> hidden <% } %>"></div>
</div>
<div class="section action-msg-view"></div>
<div class="section upgrade-message"></div>
...
...
lms/templates/learner_dashboard/course_enroll.underscore
View file @
badb1f84
...
...
@@ -11,7 +11,7 @@
<%- gettext('View Course') %>
<% } %>
</a>
<% } else { %>
<% } else
if (!user_entitlement)
{ %>
<% if (enrollable_course_runs.length > 0) { %>
<% if (enrollable_course_runs.length > 1) { %>
<div class="run-select-container">
...
...
lms/templates/learner_dashboard/course_entitlement.underscore
View file @
badb1f84
...
...
@@ -9,8 +9,8 @@
<div class="action-controls">
<select class="session-select" aria-label="<%- StringUtils.interpolate( gettext('Session Selection Dropdown for {courseName}'), { courseName: courseName }) %>">
<% _.each(availableSessions, function(session) { %>
<option data-session_id="<%- session.session_id %>">
<% if (
session.session_id
=== currentSessionId) { %>
<option data-session_id="<%- session.session_id
|| session.key
%>">
<% if (
(session.session_id || session.key)
=== currentSessionId) { %>
<%- StringUtils.interpolate( gettext('{sessionDates} - Currently Selected'), {sessionDates: session.session_dates}) %>
<% } else if (session.enrollment_end){ %>
<%- StringUtils.interpolate( gettext('{sessionDates} (Open until {enrollmentEnd})'), {sessionDates: session.session_dates, enrollmentEnd: session.enrollment_end}) %>
...
...
lms/templates/learner_dashboard/program_details_fragment.html
View file @
badb1f84
...
...
@@ -9,6 +9,10 @@ from openedx.core.djangolib.js_utils import (
%
>
<
%
block
name=
"js_extra"
>
<!-- This is a temporary solution before we land a fix to load these through Webpack, tracked by LEARNER-3483 -->
<script
type=
"text/javascript"
src=
"${static.url('common/js/vendor/popper.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('common/js/vendor/bootstrap.js')}"
></script>
<
%
static:require_module
module_name=
"js/learner_dashboard/program_details_factory"
class_name=
"ProgramDetailsFactory"
>
ProgramDetailsFactory({
programData: ${program_data | n, dump_js_escaped_json},
...
...
openedx/core/djangoapps/programs/utils.py
View file @
badb1f84
...
...
@@ -222,15 +222,26 @@ class ProgramProgressMeter(object):
completed
,
in_progress
,
not_started
=
[],
[],
[]
for
course
in
program_copy
[
'courses'
]:
try
:
entitlement
=
CourseEntitlement
.
objects
.
get
(
user
=
self
.
user
,
course_uuid
=
course
[
'uuid'
])
except
CourseEntitlement
.
DoesNotExist
:
entitlement
=
None
if
self
.
_is_course_complete
(
course
):
completed
.
append
(
course
)
elif
self
.
_is_course_enrolled
(
course
):
course_in_progress
=
self
.
_is_course_in_progress
(
now
,
course
)
if
course_in_progress
:
elif
self
.
_is_course_enrolled
(
course
)
or
entitlement
:
# Show all currently enrolled courses and entitlements as in progress
if
entitlement
:
course
[
'user_entitlement'
]
=
entitlement
.
to_dict
()
course
[
'enroll_url'
]
=
reverse
(
'entitlements_api:v1:enrollments'
,
args
=
[
str
(
entitlement
.
uuid
)])
in_progress
.
append
(
course
)
else
:
course
[
'expired'
]
=
not
course_in_progress
not_started
.
append
(
course
)
course_in_progress
=
self
.
_is_course_in_progress
(
now
,
course
)
if
course_in_progress
:
in_progress
.
append
(
course
)
else
:
course
[
'expired'
]
=
not
course_in_progress
not_started
.
append
(
course
)
else
:
not_started
.
append
(
course
)
...
...
themes/edx.org/lms/templates/dashboard.html
View file @
badb1f84
...
...
@@ -35,6 +35,7 @@ from student.models import CourseEnrollment
</script>
% endfor
% if course_entitlements:
<!-- This is a temporary solution before we land a fix to load these through Webpack, tracked by LEARNER-3483 -->
<script
type=
"text/javascript"
src=
"${static.url('common/js/vendor/popper.js')}"
></script>
<script
type=
"text/javascript"
src=
"${static.url('common/js/vendor/bootstrap.js')}"
></script>
% endif
...
...
@@ -136,9 +137,9 @@ from student.models import CourseEnrollment
'
session_id
'
:
course
['
key
'],
'
enrollment_end
'
:
course
['
enrollment_end
'],
'
pacing_type
'
:
course
['
pacing_type
'],
'
session_start_advertised
'
:
CourseOverview
.
get_from_id
(
CourseKey
.
from_string
(
course
['
key
'])).
advertised_start
,
'
s
ession_s
tart
'
:
CourseOverview
.
get_from_id
(
CourseKey
.
from_string
(
course
['
key
'])).
start
,
'
session_
end
'
:
CourseOverview
.
get_from_id
(
CourseKey
.
from_string
(
course
['
key
'])).
end
,
'
advertised_start
'
:
CourseOverview
.
get_from_id
(
CourseKey
.
from_string
(
course
['
key
'])).
advertised_start
,
'
start
'
:
CourseOverview
.
get_from_id
(
CourseKey
.
from_string
(
course
['
key
'])).
start
,
'
end
'
:
CourseOverview
.
get_from_id
(
CourseKey
.
from_string
(
course
['
key
'])).
end
,
}
for
course
in
course_entitlement_available_sessions
[
str
(
entitlement
.
uuid
)]]
if
is_fulfilled_entitlement:
#
If
the
user
has
a
fulfilled
entitlement
,
pass
through
the
entitlements
CourseEnrollment
object
...
...
webpack.common.config.js
View file @
badb1f84
...
...
@@ -25,6 +25,8 @@ module.exports = {
// LMS
SingleSupportForm
:
'./lms/static/support/jsx/single_support_form.jsx'
,
AlertStatusBar
:
'./lms/static/js/accessible_components/StatusBarAlert.jsx'
,
Bootstrap
:
'./lms/static/common/js/vendor/bootstrap.js'
,
EntitlementView
:
'./lms/static/js/learner_dashboard/views/course_entitlement_view.js'
,
// Features
CourseGoals
:
'./openedx/features/course_experience/static/course_experience/js/CourseGoals.js'
,
...
...
@@ -63,6 +65,9 @@ module.exports = {
jQuery
:
'jquery'
,
'window.jQuery'
:
'jquery'
}),
new
webpack
.
ProvidePlugin
({
Popper
:
'popper.js'
}),
// Note: Until karma-webpack releases v3, it doesn't play well with
// the CommonsChunkPlugin. We have a kludge in karma.common.conf.js
...
...
@@ -169,6 +174,7 @@ module.exports = {
gettext
:
'gettext'
,
jquery
:
'jQuery'
,
logger
:
'Logger'
,
popper
:
'Popper'
,
underscore
:
'_'
,
URI
:
'URI'
},
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment