2018-10-13 05:04:44 +00:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
//
|
|
|
|
// UI ELEMENTS
|
|
|
|
//
|
|
|
|
|
|
|
|
class SaveButton {
|
|
|
|
constructor(div) {
|
|
|
|
this.root = div
|
|
|
|
this.root.className = "savebutton"
|
|
|
|
this.root.style.display = "block"
|
|
|
|
|
|
|
|
this.element = {
|
|
|
|
root: this.root,
|
|
|
|
button: this.root.querySelector('.savebutton--button'),
|
|
|
|
icon: this.root.querySelector('.savebutton--icon'),
|
|
|
|
details: this.root.querySelector('.savebutton--details')
|
|
|
|
}
|
|
|
|
|
|
|
|
this.switchStateInitial()
|
|
|
|
}
|
|
|
|
|
|
|
|
changeState(data) {
|
|
|
|
var data = data || {}
|
|
|
|
this.element.button.innerText = data.buttonText || "Save"
|
|
|
|
this.element.button.disabled = data.buttonClickable === false ? true : false
|
|
|
|
this.element.icon.className = 'savebutton--icon ' + (data.iconClasses || "")
|
|
|
|
this.element.details.innerText = data.detailsText || ""
|
|
|
|
}
|
|
|
|
|
|
|
|
switchStateInitial() {
|
|
|
|
this.changeState({
|
|
|
|
buttonClickable: false
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
switchStateUnsaved() {
|
|
|
|
this.changeState({
|
|
|
|
detailsText: django.gettext("You have unsaved changes. Click here to save a draft, which you can access next time you are here.")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
switchStateSaving() {
|
|
|
|
this.changeState({
|
|
|
|
buttonText: django.gettext("Saving..."),
|
|
|
|
iconClasses: "fa fa-spinner fa-spin"
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
switchStateSaveSuccess() {
|
|
|
|
this.changeState({
|
|
|
|
buttonText: django.gettext("Saved"),
|
|
|
|
detailsText: django.gettext("Saved successfully."),
|
|
|
|
iconClasses: "fa fa-check",
|
|
|
|
buttonClickable: false
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
switchStateSaveFailed(reason = "") {
|
|
|
|
this.changeState({
|
|
|
|
detailsText: django.gettext("Save failed! ") + reason,
|
|
|
|
iconClasses: "fa fa-exclamation-triangle savebutton--icon-failed"
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class DraftPrompt {
|
|
|
|
constructor(opts) {
|
|
|
|
this.restoreDraft = opts.restoreFn
|
|
|
|
this.deleteDraft = opts.deleteFn
|
|
|
|
|
|
|
|
this.root = opts.root
|
|
|
|
this.element = {
|
|
|
|
root: this.root,
|
|
|
|
restore: this.root.querySelector('.draftprompt--restore'),
|
|
|
|
delete: this.root.querySelector('.draftprompt--delete'),
|
|
|
|
details: this.root.querySelector('.draftprompt--details')
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restore should restore, then hide the prompt
|
|
|
|
this.element.restore.addEventListener('click', () => {
|
|
|
|
this.restoreDraft()
|
|
|
|
this.switchStateHidden()
|
|
|
|
})
|
|
|
|
|
|
|
|
// Delete button will delete the draft
|
|
|
|
this.element.delete.addEventListener('click', () => {
|
|
|
|
if (window.confirm(django.gettext('Are you sure you want to delete your draft?'))) {
|
|
|
|
this.switchStateDeleting()
|
|
|
|
this.deleteDraft().then(ok => ok ?
|
|
|
|
this.switchStateHidden() :
|
|
|
|
this.switchStateDeleteFailed()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
this.switchStateShown()
|
|
|
|
}
|
|
|
|
|
|
|
|
changeState(data) {
|
|
|
|
var data = data || {}
|
|
|
|
this.element.root.style.display = data.visible ? "block" : "none"
|
|
|
|
this.element.details.innerHTML = data.detailsText || ""
|
|
|
|
}
|
|
|
|
|
|
|
|
switchStateHidden() {
|
|
|
|
this.changeState({
|
|
|
|
visible: false
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
switchStateShown() {
|
|
|
|
this.changeState({
|
|
|
|
visible: true
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
switchStateDeleting() {
|
|
|
|
this.changeState({
|
|
|
|
visible: true,
|
|
|
|
details: '<i classs="fa fa-spinner fa-spin"></i>'
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
switchStateDeleteFailed() {
|
|
|
|
this.changeState({
|
|
|
|
details: django.gettext("Delete failed!")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// API UTILITIES
|
|
|
|
//
|
|
|
|
|
|
|
|
function apiGetDraft() {
|
|
|
|
return fetch('/en-gb/case-study/draft', {
|
|
|
|
method: 'GET',
|
|
|
|
credentials: "same-origin",
|
|
|
|
headers: {
|
|
|
|
"X-CSRFToken": jQuery("[name=csrfmiddlewaretoken]").val(),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function apiPutDraft(formSaver) {
|
|
|
|
if (!formSaver) {
|
|
|
|
throw new Error("apiPutDraft: parameter not provided")
|
|
|
|
}
|
|
|
|
|
|
|
|
return fetch('/en-gb/case-study/draft', {
|
|
|
|
method: 'PUT',
|
|
|
|
credentials: "same-origin",
|
|
|
|
headers: {
|
|
|
|
"X-CSRFToken": jQuery("[name=csrfmiddlewaretoken]").val(),
|
|
|
|
"Accept": "application/json",
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
},
|
|
|
|
body: JSON.stringify({ version: 1, data: formSaver.serialise() })
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function apiDeleteDraft() {
|
|
|
|
return fetch('/en-gb/case-study/draft', {
|
|
|
|
method: 'DELETE',
|
|
|
|
credentials: "same-origin",
|
|
|
|
headers: {
|
|
|
|
"X-CSRFToken": jQuery("[name=csrfmiddlewaretoken]").val(),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// GNARLY BITS TYING API & UI STUFF TOGETHER
|
|
|
|
//
|
|
|
|
|
|
|
|
function showDraftPrompt(formSaver, serialisedForm) {
|
|
|
|
let prompt = new DraftPrompt({
|
|
|
|
root: document.querySelector('.draftprompt'),
|
|
|
|
restoreFn: () => formSaver.deserialise(serialisedForm),
|
|
|
|
deleteFn: () => apiDeleteDraft().then(response => response.ok)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function initDrafts() {
|
|
|
|
const formSaver = new FormSaver({
|
|
|
|
formId: 'case-study-form',
|
|
|
|
except: [ 'csrfmiddlewaretoken' ]
|
|
|
|
})
|
|
|
|
|
|
|
|
// Use whether the form has errors as a proxy for whether the server has
|
|
|
|
// returned us data in the form. In this case, don't show a draft.
|
|
|
|
const formHasErrors = document.getElementById('form_errors').value === 'true' ? true : false
|
|
|
|
if (!formHasErrors) {
|
|
|
|
// Get the previous draft, if any
|
|
|
|
apiGetDraft()
|
2019-04-07 16:18:55 +00:00
|
|
|
.then(response => {
|
|
|
|
if (response.status == 404) {
|
|
|
|
console.log("No draft stored on server");
|
|
|
|
} else if (response.ok) {
|
|
|
|
response.json().then(json => {
|
|
|
|
// Handle the case where we didn't get a response
|
|
|
|
if (!json) return
|
|
|
|
|
|
|
|
if (json.version !== 1) {
|
|
|
|
throw new Error("Bad JSON response version")
|
|
|
|
}
|
|
|
|
|
|
|
|
showDraftPrompt(formSaver, json.data.form)
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
throw new Error("Error getting draft", response)
|
2018-10-13 05:04:44 +00:00
|
|
|
}
|
2019-04-07 16:18:55 +00:00
|
|
|
})
|
|
|
|
.catch(err => {
|
2018-10-13 05:04:44 +00:00
|
|
|
console.error(err);
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Init the button controller
|
|
|
|
var button = new SaveButton(document.querySelector('.savebutton'))
|
|
|
|
|
|
|
|
// Dirty forms set up
|
|
|
|
$('#case-study-form').dirtyForms();
|
|
|
|
$('#case-study-form').on('dirty.dirtyforms', ev => {
|
|
|
|
button.switchStateUnsaved()
|
|
|
|
});
|
|
|
|
|
|
|
|
// Save button
|
|
|
|
button.element.button.addEventListener('click', evt => {
|
|
|
|
button.switchStateSaving()
|
|
|
|
|
|
|
|
apiPutDraft(formSaver).then(response => {
|
|
|
|
if (response.ok) {
|
|
|
|
button.switchStateSaveSuccess();
|
|
|
|
$('#case-study-form').dirtyForms('setClean');
|
|
|
|
document.querySelector('.draftprompt').style.display = "none"
|
|
|
|
} else {
|
|
|
|
button.switchStateSaveFailed();
|
|
|
|
}
|
|
|
|
}).catch(err => {
|
|
|
|
console.error(err);
|
|
|
|
button.switchStateSaveFailed();
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// https://github.com/snikch/jquery.dirtyforms
|