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
aa980554
Commit
aa980554
authored
Oct 25, 2017
by
Tasawer Nawaz
Committed by
GitHub
Oct 25, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #16240 from edx/tasawer/learner-2804/add-reactjs-for-single-support-form
add reactjs for single support form
parents
168b6b36
d839d177
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
614 additions
and
179 deletions
+614
-179
lms/djangoapps/support/static/support/jsx/.eslintrc.js
+10
-0
lms/djangoapps/support/static/support/jsx/errors_list.jsx
+28
-0
lms/djangoapps/support/static/support/jsx/file_upload.jsx
+170
-0
lms/djangoapps/support/static/support/jsx/logged_in_user.jsx
+49
-0
lms/djangoapps/support/static/support/jsx/logged_out_user.jsx
+48
-0
lms/djangoapps/support/static/support/jsx/single_support_form.jsx
+211
-0
lms/djangoapps/support/static/support/jsx/upload_progress.jsx
+41
-0
lms/djangoapps/support/tests/test_views.py
+0
-43
lms/static/sass/views/_support.scss
+25
-2
lms/templates/support/contact_us.html
+29
-134
webpack.config.js
+3
-0
No files found.
lms/djangoapps/support/static/support/jsx/.eslintrc.js
0 → 100644
View file @
aa980554
module
.
exports
=
{
extends
:
'eslint-config-edx'
,
root
:
true
,
settings
:
{
'import/resolver'
:
'webpack'
,
},
rules
:
{
'import/prefer-default-export'
:
'off'
,
},
};
lms/djangoapps/support/static/support/jsx/errors_list.jsx
0 → 100644
View file @
aa980554
/* eslint react/no-array-index-key: 0 */
import
React
from
'react'
;
import
PropTypes
from
'prop-types'
;
class
ShowErrors
extends
React
.
Component
{
render
()
{
window
.
scrollTo
(
0
,
0
);
return
this
.
props
.
errorList
.
length
>
0
&&
<
div
className=
"col-sm-12"
>
<
div
className=
"alert alert-danger"
role=
"alert"
>
<
strong
>
{
gettext
(
'Please fix the following errors:'
)
}
</
strong
>
<
ul
>
{
this
.
props
.
errorList
.
map
(
error
=>
<
li
>
{
error
}
</
li
>,
)
}
</
ul
>
</
div
>
</
div
>;
}
}
ShowErrors
.
propTypes
=
{
errorList
:
PropTypes
.
arrayOf
(
PropTypes
.
object
).
isRequired
,
};
export
default
ShowErrors
;
lms/djangoapps/support/static/support/jsx/file_upload.jsx
0 → 100644
View file @
aa980554
/* global gettext */
/* eslint one-var: ["error", "always"] */
import
React
from
'react'
;
import
PropTypes
from
'prop-types'
;
import
ShowProgress
from
'./upload_progress'
;
class
FileUpload
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
uploadFile
=
this
.
uploadFile
.
bind
(
this
);
this
.
removeFile
=
this
.
removeFile
.
bind
(
this
);
this
.
state
=
{
fileList
:
[],
fileInProgress
:
null
,
};
}
removeFile
(
e
)
{
e
.
preventDefault
();
const
fileToken
=
e
.
target
.
id
,
$this
=
this
,
url
=
`https://arbisoft.zendesk.com/api/v2/uploads/
${
fileToken
}
.json`
,
accessToken
=
'd6ed06821334b6584dd9607d04007c281007324ed07e087879c9c44835c684da'
,
request
=
new
XMLHttpRequest
();
request
.
open
(
'DELETE'
,
url
,
true
);
request
.
setRequestHeader
(
'Authorization'
,
`Bearer
${
accessToken
}
`
);
request
.
setRequestHeader
(
'Content-Type'
,
'application/json;charset=UTF-8'
);
request
.
send
();
request
.
onreadystatechange
=
function
removeFile
()
{
if
(
request
.
readyState
===
4
&&
request
.
status
===
204
)
{
$this
.
setState
({
fileList
:
$this
.
state
.
fileList
.
filter
(
file
=>
file
.
fileToken
!==
fileToken
),
});
}
};
}
uploadFile
(
e
)
{
const
url
=
'https://arbisoft.zendesk.com/api/v2/uploads.json?filename='
,
fileReader
=
new
FileReader
(),
request
=
new
XMLHttpRequest
(),
errorList
=
[],
$this
=
this
,
file
=
e
.
target
.
files
[
0
],
accessToken
=
'd6ed06821334b6584dd9607d04007c281007324ed07e087879c9c44835c684da'
,
maxFileSize
=
5000000
,
// 5mb is max limit
allowedFileTypes
=
[
'gif'
,
'png'
,
'jpg'
,
'jpeg'
,
'pdf'
];
// remove file from input and upload it to zendesk after validation
$
(
e
.
target
).
val
(
''
);
if
(
file
.
size
>
maxFileSize
)
{
errorList
.
push
(
gettext
(
'Files that you upload must be smaller than 5MB in size.'
));
}
else
if
(
$
.
inArray
(
file
.
name
.
split
(
'.'
).
pop
().
toLowerCase
(),
allowedFileTypes
)
===
-
1
)
{
errorList
.
push
(
gettext
(
'Files that you upload must be PDFs or image files in .gif, .jpg, .jpeg, or .png format.'
));
}
this
.
props
.
setErrorState
(
errorList
);
if
(
errorList
.
length
>
0
)
{
return
;
}
request
.
open
(
'POST'
,
(
url
+
file
.
name
),
true
);
request
.
setRequestHeader
(
'Authorization'
,
`Bearer
${
accessToken
}
`
);
request
.
setRequestHeader
(
'Content-Type'
,
'application/binary'
);
fileReader
.
readAsArrayBuffer
(
file
);
fileReader
.
onloadend
=
function
success
()
{
$this
.
setState
({
fileInProgress
:
file
.
name
,
currentRequest
:
request
,
});
request
.
send
(
fileReader
.
result
);
};
request
.
upload
.
onprogress
=
function
renderProgress
(
event
)
{
if
(
event
.
lengthComputable
)
{
const
percentComplete
=
(
event
.
loaded
/
event
.
total
)
*
100
;
$
(
'.progress-bar-striped'
).
css
({
width
:
`
${
percentComplete
}
%`
});
}
};
request
.
onreadystatechange
=
function
success
()
{
if
(
request
.
readyState
===
4
&&
request
.
status
===
201
)
{
const
uploadedFile
=
{
fileName
:
file
.
name
,
fileToken
:
JSON
.
parse
(
request
.
response
).
upload
.
token
,
};
$this
.
setState
(
{
fileList
:
$this
.
state
.
fileList
.
concat
(
uploadedFile
),
fileInProgress
:
null
,
},
);
}
};
request
.
onerror
=
function
error
()
{
$this
.
setState
({
fileInProgress
:
null
,
errorList
:
[
gettext
(
'Something went wrong. Please try again later.'
)],
});
};
request
.
onabort
=
function
abortUpload
()
{
$this
.
setState
({
fileInProgress
:
null
,
});
};
}
render
()
{
return
(
<
div
className=
"file-container"
>
<
div
className=
"row"
>
<
div
className=
"col-sm-12"
>
<
div
className=
"form-group"
>
<
label
htmlFor=
"attachment"
>
{
gettext
(
'Add Attachment'
)
}
<
span
>
{
gettext
(
'(Optional)'
)
}
</
span
>
</
label
>
<
input
id=
"attachment"
className=
"file file-loading"
type=
"file"
accept=
".pdf, .jpeg, .png, .jpg, .gif"
onChange=
{
this
.
uploadFile
}
/>
</
div
>
</
div
>
</
div
>
<
div
className=
"progress-container"
>
{
this
.
state
.
fileInProgress
&&
<
ShowProgress
fileName=
{
this
.
state
.
fileInProgress
}
request=
{
this
.
state
.
currentRequest
}
/>
}
</
div
>
<
div
className=
"uploaded-files"
>
{
this
.
state
.
fileList
.
map
(
file
=>
(<
div
key=
{
file
.
fileToken
}
className=
"row"
>
<
div
className=
"col-sm-12"
>
<
span
className=
"file-name"
>
{
file
.
fileName
}
</
span
>
<
span
className=
"file-action remove-upload"
>
<
button
className=
"btn btn-link"
id=
{
file
.
fileToken
}
onClick=
{
this
.
removeFile
}
>
{
gettext
(
'Remove file'
)
}
</
button
>
</
span
>
</
div
>
</
div
>),
)
}
</
div
>
</
div
>
);
}
}
FileUpload
.
propTypes
=
{
setErrorState
:
PropTypes
.
func
.
isRequired
,
};
export
default
FileUpload
;
lms/djangoapps/support/static/support/jsx/logged_in_user.jsx
0 → 100644
View file @
aa980554
/* global gettext */
import
React
from
'react'
;
import
PropTypes
from
'prop-types'
;
function
LoggedInUser
({
userInformation
})
{
return
(<
div
>
<
div
className=
"row"
>
<
div
className=
"col-sm-12 user-info"
data
-
username=
{
userInformation
.
username
}
data
-
email=
{
userInformation
.
email
}
>
<
p
>
{
gettext
(
`What can we help you with, ${userInformation.username}?`
)
}
</
p
>
</
div
>
</
div
>
<
div
className=
"row"
>
<
div
className=
"col-sm-12"
>
<
div
className=
"form-group"
>
{
userInformation
.
enrollments
.
length
===
0
&&
<
div
>
<
label
htmlFor=
"course"
>
{
gettext
(
'Course Name'
)
}
<
span
>
{
gettext
(
'(Optional)'
)
}
</
span
></
label
>
<
input
type=
"text"
className=
"form-control"
id=
"course"
/>
</
div
>
}
{
userInformation
.
enrollments
.
length
>
0
&&
<
div
>
<
label
className=
"label-course"
htmlFor=
"course"
>
{
gettext
(
'Course Name'
)
}
</
label
>
<
select
className=
"form-control select-course"
id=
"course"
>
{
userInformation
.
enrollments
.
map
(
enrollment
=>
(<
option
key=
{
enrollment
.
course_id
}
value=
{
enrollment
.
course_id
}
>
{
enrollment
.
course_name
}
</
option
>),
)
}
</
select
>
</
div
>
}
</
div
>
</
div
>
</
div
>
</
div
>);
}
LoggedInUser
.
propTypes
=
{
userInformation
:
PropTypes
.
arrayOf
(
PropTypes
.
object
).
isRequired
,
};
export
default
LoggedInUser
;
lms/djangoapps/support/static/support/jsx/logged_out_user.jsx
0 → 100644
View file @
aa980554
/* global gettext */
import
React
from
'react'
;
import
PropTypes
from
'prop-types'
;
function
LoggedOutUser
({
loginUrl
})
{
return
(
<
div
>
<
div
className=
"row"
>
<
div
className=
"col-sm-12"
>
<
p
>
{
gettext
(
'Sign in to edX so we can help you better.'
)
}
</
p
>
</
div
>
</
div
>
<
div
className=
"row"
>
<
div
className=
"col-sm-12"
>
<
a
href=
{
loginUrl
}
className=
"btn btn-primary btn-signin"
>
{
gettext
(
'Sign in'
)
}
</
a
>
</
div
>
</
div
>
<
div
className=
"row"
>
<
div
className=
"col-sm-12"
>
<
div
className=
"form-group"
>
<
label
htmlFor=
"email"
>
{
gettext
(
'Your Email Address'
)
}
</
label
>
<
input
type=
"text"
className=
"form-control"
id=
"email"
/>
</
div
>
</
div
>
</
div
>
<
div
className=
"row"
>
<
div
className=
"col-sm-12"
>
<
div
className=
"form-group"
>
<
label
htmlFor=
"course"
>
{
gettext
(
'Course Name'
)
}
<
span
>
{
gettext
(
'(Optional)'
)
}
</
span
></
label
>
<
input
type=
"text"
className=
"form-control"
id=
"course"
/>
</
div
>
</
div
>
</
div
>
</
div
>
);
}
LoggedOutUser
.
propTypes
=
{
loginUrl
:
PropTypes
.
string
.
isRequired
,
};
export
default
LoggedOutUser
;
lms/djangoapps/support/static/support/jsx/single_support_form.jsx
0 → 100644
View file @
aa980554
/* global gettext */
/* eslint one-var: ["error", "always"] */
/* eslint no-alert: "error" */
import
PropTypes
from
'prop-types'
;
import
React
from
'react'
;
import
ReactDOM
from
'react-dom'
;
import
FileUpload
from
'./file_upload'
;
import
ShowErrors
from
'./errors_list'
;
import
LoggedInUser
from
'./logged_in_user'
;
import
LoggedOutUser
from
'./logged_out_user'
;
// TODO
// edx zendesk APIs
// access token
// custom fields ids
// https://openedx.atlassian.net/browse/LEARNER-2736
// https://openedx.atlassian.net/browse/LEARNER-2735
class
RenderForm
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
state
=
{
currentRequest
:
null
,
errorList
:
[],
};
this
.
submitForm
=
this
.
submitForm
.
bind
(
this
);
this
.
setErrorState
=
this
.
setErrorState
.
bind
(
this
);
}
setErrorState
(
errors
)
{
this
.
setState
({
errorList
:
errors
,
});
}
submitForm
()
{
const
url
=
'https://arbisoft.zendesk.com/api/v2/tickets.json'
,
$userInfo
=
$
(
'.user-info'
),
request
=
new
XMLHttpRequest
(),
$course
=
$
(
'#course'
),
accessToken
=
'd6ed06821334b6584dd9607d04007c281007324ed07e087879c9c44835c684da'
,
data
=
{
subject
:
$
(
'#subject'
).
val
(),
comment
:
{
body
:
$
(
'#message'
).
val
(),
uploads
:
$
.
map
(
$
(
'.uploaded-files button'
),
n
=>
n
.
id
),
},
};
let
course
;
if
(
$userInfo
.
length
)
{
data
.
requester
=
$userInfo
.
data
(
'email'
);
course
=
$course
.
find
(
':selected'
).
text
();
if
(
!
course
.
length
)
{
course
=
$course
.
val
();
}
}
else
{
data
.
requester
=
$
(
'#email'
).
val
();
course
=
$course
.
val
();
}
data
.
custom_fields
=
[{
id
:
'114099484092'
,
value
:
course
,
}];
if
(
this
.
validateData
(
data
))
{
request
.
open
(
'POST'
,
url
,
true
);
request
.
setRequestHeader
(
'Authorization'
,
`Bearer
${
accessToken
}
`
);
request
.
setRequestHeader
(
'Content-Type'
,
'application/json;charset=UTF-8'
);
request
.
send
(
JSON
.
stringify
({
ticket
:
data
,
}));
request
.
onreadystatechange
=
function
success
()
{
if
(
request
.
readyState
===
4
&&
request
.
status
===
201
)
{
// TODO needs to remove after implementing success page
const
alert
=
'Request submitted successfully.'
;
alert
();
}
};
request
.
onerror
=
function
error
()
{
this
.
setErrorState
([
gettext
(
'Something went wrong. Please try again later.'
)]);
}.
bind
(
this
);
}
}
validateData
(
data
)
{
const
errors
=
[],
regex
=
/^
([
a-zA-Z0-9_.+-
])
+@
(([
a-zA-Z0-9-
])
+
\.)
+
([
a-zA-Z0-9
]{2,4})
+$/
;
if
(
!
data
.
requester
)
{
errors
.
push
(
gettext
(
'Enter a valid email address.'
));
$
(
'#email'
).
closest
(
'.form-group'
).
addClass
(
'has-error'
);
}
else
if
(
!
regex
.
test
(
data
.
requester
))
{
errors
.
push
(
gettext
(
'Enter a valid email address.'
));
$
(
'#email'
).
closest
(
'.form-group'
).
addClass
(
'has-error'
);
}
if
(
!
data
.
subject
)
{
errors
.
push
(
gettext
(
'Enter a subject for your support request.'
));
$
(
'#subject'
).
closest
(
'.form-group'
).
addClass
(
'has-error'
);
}
if
(
!
data
.
comment
.
body
)
{
errors
.
push
(
gettext
(
'Enter some details for your support request.'
));
$
(
'#message'
).
closest
(
'.form-group'
).
addClass
(
'has-error'
);
}
if
(
!
errors
.
length
)
{
return
true
;
}
this
.
setErrorState
(
errors
);
return
false
;
}
render
()
{
let
userElement
;
if
(
this
.
props
.
context
.
user
)
{
userElement
=
<
LoggedInUser
userInformation=
{
this
.
props
.
context
.
user
}
/>;
}
else
{
userElement
=
<
LoggedOutUser
loginUrl=
{
this
.
props
.
context
.
loginQuery
}
/>;
}
return
(
<
div
className=
"contact-us-wrapper"
>
<
div
className=
"row"
>
<
div
className=
"col-sm-12"
>
<
h2
>
{
gettext
(
'Contact Us'
)
}
</
h2
>
</
div
>
</
div
>
<
div
className=
"row form-errors"
>
<
ShowErrors
errorList=
{
this
.
state
.
errorList
}
/>
</
div
>
<
div
className=
"row"
>
<
div
className=
"col-sm-12"
>
<
p
>
{
gettext
(
'Your question might have already been answered.'
)
}
</
p
>
</
div
>
</
div
>
<
div
className=
"row"
>
<
div
className=
"col-sm-12"
>
<
a
href=
{
this
.
props
.
context
.
marketingUrl
}
className=
"btn btn-secondary help-button"
>
{
gettext
(
'Search the edX Help Center'
)
}
</
a
>
</
div
>
</
div
>
{
userElement
}
<
div
className=
"row"
>
<
div
className=
"col-sm-12"
>
<
div
className=
"form-group"
>
<
label
htmlFor=
"subject"
>
{
gettext
(
'Subject'
)
}
</
label
>
<
input
type=
"text"
className=
"form-control"
id=
"subject"
/>
</
div
>
</
div
>
</
div
>
<
div
className=
"row"
>
<
div
className=
"col-sm-12"
>
<
div
className=
"form-group"
>
<
label
htmlFor=
"message"
>
{
gettext
(
'Details'
)
}
</
label
>
<
p
className=
"message-desc"
>
{
gettext
(
'The more you tell us, the more quickly and helpfully we can respond!'
)
}
</
p
>
<
textarea
aria
-
describedby=
"message"
className=
"form-control"
rows=
"7"
id=
"message"
/>
</
div
>
</
div
>
</
div
>
<
FileUpload
setErrorState=
{
this
.
setErrorState
}
/>
<
div
className=
"row"
>
<
div
className=
"col-sm-12"
>
<
button
className=
"btn btn-primary btn-submit"
onClick=
{
this
.
submitForm
}
>
{
gettext
(
'Submit'
)
}
</
button
>
</
div
>
</
div
>
</
div
>
);
}
}
RenderForm
.
propTypes
=
{
context
:
PropTypes
.
arrayOf
(
PropTypes
.
object
).
isRequired
,
};
export
class
SingleSupportForm
{
constructor
(
context
)
{
ReactDOM
.
render
(
<
RenderForm
context=
{
context
}
/>,
document
.
getElementById
(
'root'
),
);
}
}
lms/djangoapps/support/static/support/jsx/upload_progress.jsx
0 → 100644
View file @
aa980554
/* global gettext */
import
React
from
'react'
;
import
PropTypes
from
'prop-types'
;
class
ShowProgress
extends
React
.
Component
{
constructor
(
props
)
{
super
(
props
);
this
.
abortRequest
=
this
.
abortRequest
.
bind
(
this
);
}
abortRequest
(
e
)
{
e
.
preventDefault
();
this
.
props
.
request
.
abort
();
}
render
()
{
return
(
<
div
className=
"row"
>
<
div
className=
"col-sm-12"
>
<
div
className=
"form-group"
>
<
span
className=
"file-name"
>
{
this
.
props
.
fileName
}
</
span
>
<
span
className=
"file-action abort-upload"
>
<
button
className=
"btn btn-link"
onClick=
{
this
.
abortRequest
}
>
{
gettext
(
'Cancel upload'
)
}
</
button
>
</
span
>
<
div
className=
"progress"
>
<
div
className=
"progress-bar progress-bar-striped zero-width"
role=
"progressbar"
/>
</
div
>
</
div
>
</
div
>
</
div
>
);
}
}
ShowProgress
.
propTypes
=
{
fileName
:
PropTypes
.
string
.
isRequired
,
request
:
PropTypes
.
objectOf
(
XMLHttpRequest
).
isRequired
,
};
export
default
ShowProgress
;
lms/djangoapps/support/tests/test_views.py
View file @
aa980554
...
...
@@ -380,46 +380,3 @@ class SupportViewEnrollmentsTests(SharedModuleStoreTestCase, SupportViewTestCase
)
verified_mode
.
expiration_datetime
=
datetime
(
year
=
1970
,
month
=
1
,
day
=
9
,
tzinfo
=
UTC
)
verified_mode
.
save
()
class
ContactUsViewTests
(
ModuleStoreTestCase
):
url
=
reverse
(
'support:contact_us'
)
def
setUp
(
self
):
super
(
ContactUsViewTests
,
self
)
.
setUp
()
self
.
user
=
UserFactory
()
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
'test'
)
self
.
user_enrollment
=
CourseEnrollmentFactory
.
create
(
user
=
self
.
user
,
)
def
test_get_with_logged_in_user
(
self
):
""" Verify that logged in users will see courses dropdown."""
response
=
self
.
client
.
get
(
self
.
url
)
expected
=
'<option value="{course_id}">'
.
format
(
course_id
=
self
.
user_enrollment
.
course
.
id
)
self
.
assertContains
(
response
,
expected
)
def
test_get_without_course_enrollment
(
self
):
""" Verify that logged in users will see not courses dropdown,
if they are not enrolled in any course.
"""
self
.
client
.
logout
()
new_user
=
UserFactory
()
self
.
client
.
login
(
username
=
new_user
.
username
,
password
=
'test'
)
response
=
self
.
client
.
get
(
self
.
url
)
self
.
_assert_without_course_enrollment
(
response
)
def
test_get_with_anonymous_user
(
self
):
""" Verify that logged out users will see not courses dropdown.
They will see sign in button.
"""
self
.
client
.
logout
()
response
=
self
.
client
.
get
(
self
.
url
)
self
.
assertContains
(
response
,
'class="btn btn-primary btn-signin">Sign in</a>'
)
self
.
_assert_without_course_enrollment
(
response
)
def
_assert_without_course_enrollment
(
self
,
response
):
""" Assert that users will not see simple course text input."""
expected
=
'<input type="text" class="form-control" id="course">'
self
.
assertContains
(
response
,
expected
)
lms/static/sass/views/_support.scss
View file @
aa980554
...
...
@@ -165,7 +165,6 @@
.help-button
{
margin-bottom
:
$baseline
;
width
:
$baseline
*
8
;
height
:
$baseline
*
2
;
font-weight
:
$font-regular
;
font-size
:
$support-form-base-font-size
+
2
;
...
...
@@ -203,7 +202,10 @@
.progress-bar
{
background-color
:
$blue
;
width
:
25%
;
}
.zero-width
{
width
:
0
;
}
}
...
...
@@ -231,6 +233,10 @@
font-size
:
$support-form-base-font-size
+
2
;
margin-bottom
:
$baseline
-
10
;
button
{
padding
:
0
;
}
}
.btn-signin
{
...
...
@@ -245,6 +251,23 @@
}
}
input
[
type
=
'text'
]
{
font-size
:
$support-form-base-font-size
-
2
;
font-family
:
$sans-serif
;
font-style
:
normal
;
font-weight
:
$font-regular
;
}
.alert-danger
{
color
:
$palette-error-text
!
important
;
font-size
:
$support-form-base-font-size
;
}
.has-error
{
label
{
color
:
$danger-red
;
}
}
@media
only
screen
and
(
min-width
:
768px
)
{
.row
{
max-width
:
$baseline
*
25
;
...
...
lms/templates/support/contact_us.html
View file @
aa980554
...
...
@@ -2,10 +2,12 @@
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
from
openedx
.
core
.
djangolib
.
js_utils
import
js_escaped_string
%
>
<
%
inherit
file=
"../main.html"
/>
<
%
namespace
file=
'../main.html'
import=
"login_query"
/>
<
%
namespace
name=
'static'
file=
'../static_content.html'
/>
<
%
block
name=
"title"
>
<title>
...
...
@@ -19,142 +21,35 @@ from django.utils.translation import ugettext as _
<
%
block
name=
"body"
>
<div
class=
"container contact-us-wrapper"
>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<h2>
${_("Contact Us")}
</h2>
</div>
</div>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<p>
${_("Your question may have already been answered.")}
</p>
</div>
</div>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<a
href=
"${marketing_link('FAQ')}"
class=
"btn btn-secondary help-button"
>
${_("Visit edX Help")}
</a>
</div>
</div>
<!--logged out users-->
% if not user.is_authenticated():
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<p>
${_("Sign in for a faster response")}
</p>
</div>
</div>
<!-- Sign-in button brings user to sign-in page. After signing in, user is brough to logged in state of contact form.-->
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<a
href=
"/login${login_query()}"
class=
"btn btn-primary btn-signin"
>
${_("Sign in")}
</a>
</div>
</div>
<!-- No autofilled email in logged out state.-->
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<div
class=
"form-group"
>
<label
for=
"email"
>
${_("Email")}
</label>
<input
type=
"text"
class=
"form-control"
id=
"email"
>
</div>
</div>
</div>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<div
class=
"form-group"
>
<label
for=
"course"
>
${_("Course Name")}
<span>
${_("(Optional)")}
</span></label>
<input
type=
"text"
class=
"form-control"
id=
"course"
>
</div>
</div>
</div>
% else:
<!--logged in users-->
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<p>
${_("What can we help you with, {username}?").format(username=user.username)}
</p>
</div>
</div>
<br>
<div
id=
"root"
class=
"container"
>
</div>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<div
class=
"form-group"
>
% if user_enrollments:
<label
class=
"label-course"
for=
"course"
>
${_("Course Name")}
</label>
<select
class=
"form-control select-course"
id=
"course"
>
% for enrollment in user_enrollments:
<option
value=
"${enrollment.course.id}"
>
${enrollment.course.display_name}
</option>
% endfor
</select>
% else:
<label
for=
"course"
>
${_("Course Name")}
<span>
${_("(Optional)")}
</span></label>
<input
type=
"text"
class=
"form-control"
id=
"course"
>
% endif
</div>
</div>
</div>
<
%
static:webpack
entry=
"SingleSupportForm"
>
var context = {
'marketingUrl': "${marketing_link('FAQ') | n, js_escaped_string}",
'loginQuery': "/login${login_query() | n, js_escaped_string}",
}
% if user.is_authenticated():
context['user'] = {
'username': "${user.username | n, js_escaped_string}",
'email': "${user.email | n, js_escaped_string}"
}
% if user_enrollments:
enrollments = []
% for enrollment in user_enrollments:
enrollments.push({
'course_id': "${enrollment.course.id | n, js_escaped_string}",
'course_name': "${enrollment.course.display_name | n, js_escaped_string}",
})
%endfor
context['user']['enrollments'] = enrollments
% endif
% endif
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<div
class=
"form-group"
>
<label
for=
"message"
>
${_("Message")}
</label>
<p
class=
"message-desc"
>
${_("The more you tell us, themore quickly and helpfully we can respond!")}
</p>
<textarea
aria-describedby=
"message-desc"
class=
"form-control"
rows=
"7"
id=
"message"
></textarea>
</div>
</div>
</div>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<div
class=
"form-group"
>
<label
for=
"attachment"
>
${_("Add Attachment")}
<span>
${_("(Optional)")}
</span>
</label>
<input
id=
"attachment"
multiple
type=
"file"
class=
"file file-loading"
data-allowed-file-extensions=
'["png", "jpg", "gif", "tif", "jpeg"]'
>
</div>
</div>
</div>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<p>
${_("1 file uploaded:")}
</p>
</div>
</div>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<span
class=
"file-name"
>
my_image1.png
</span>
<span
class=
"file-action"
><a
href=
"#"
>
${_("Remove file")}
</a></span>
</div>
</div>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<span
class=
"file-name"
>
my_image2.png
</span>
<span
class=
"file-action"
><a
href=
"#"
>
${_("Cancel upload")}
</a></span>
<div
class=
"progress"
>
<div
class=
"progress-bar progress-bar-striped"
role=
"progressbar"
></div>
</div>
</div>
</div>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<button
class=
"btn btn-primary"
>
${_("Submit")}
</button>
</div>
</div>
</div>
new SingleSupportForm(context);
</
%
static:webpack>
</
%
block>
webpack.config.js
View file @
aa980554
...
...
@@ -28,6 +28,9 @@ var wpconfig = {
Import
:
'./cms/static/js/features/import/factories/import.js'
,
StudioIndex
:
'./cms/static/js/features_jsx/studio/index.jsx'
,
// LMS: single support form
SingleSupportForm
:
'./lms/static/support/jsx/single_support_form.jsx'
,
// Features
CourseGoals
:
'./openedx/features/course_experience/static/course_experience/js/CourseGoals.js'
,
CourseHome
:
'./openedx/features/course_experience/static/course_experience/js/CourseHome.js'
,
...
...
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