Add draft restore prompt (#52)
This commit is contained in:
parent
1dd2e4f316
commit
adbd550239
@ -13,6 +13,11 @@
|
|||||||
<style>
|
<style>
|
||||||
.savebutton--icon { margin-left: 15px; margin-right: 2px; }
|
.savebutton--icon { margin-left: 15px; margin-right: 2px; }
|
||||||
.savebutton--icon-failed { color: #d9534f; }
|
.savebutton--icon-failed { color: #d9534f; }
|
||||||
|
.savebutton--details { font-style: italic; }
|
||||||
|
|
||||||
|
.draftprompt { display: none; }
|
||||||
|
.draftprompt--delete { margin-left: 10px; }
|
||||||
|
.draftprompt--details { margin-left: 10px; font-weight: bold; }
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@ -20,7 +25,18 @@
|
|||||||
{% block description %}{% trans "Here you can submit a case study for review and it will be added to the map." %}{% endblock %}
|
{% block description %}{% trans "Here you can submit a case study for review and it will be added to the map." %}{% endblock %}
|
||||||
|
|
||||||
{% block inner %}
|
{% block inner %}
|
||||||
|
<div class="draftprompt alert alert-info">
|
||||||
|
<div>
|
||||||
|
<b class="lead">You have a saved draft.</b>
|
||||||
|
<p>Do you want to restore it? Restoring the draft will overwrite any other data you have input into the form below.</p>
|
||||||
|
</div>
|
||||||
|
<button class="draftprompt--restore btn btn-success">Restore draft</button>
|
||||||
|
<button class="draftprompt--delete btn btn-default">Delete draft</button>
|
||||||
|
<span class="draftprompt--details"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="savebutton"></div>
|
<div class="savebutton"></div>
|
||||||
|
|
||||||
{% crispy form %}
|
{% crispy form %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@ -189,13 +205,17 @@
|
|||||||
<script>
|
<script>
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
//
|
||||||
|
// UI ELEMENTS
|
||||||
|
//
|
||||||
|
|
||||||
class SaveButton {
|
class SaveButton {
|
||||||
constructor(div) {
|
constructor(div) {
|
||||||
this.root = div
|
this.root = div
|
||||||
this.root.className = "savebutton"
|
this.root.className = "savebutton"
|
||||||
this.root.innerHTML =
|
this.root.innerHTML =
|
||||||
`<button class="savebutton--button btn btn-success">Save</button>
|
'<button class="savebutton--button btn btn-success">Save</button>' +
|
||||||
<i class="savebutton--icon"></i><i><span class="savebutton--details"></span></i>`
|
'<i class="savebutton--icon"></i> <span class="savebutton--details"></span>'
|
||||||
|
|
||||||
this.element = {
|
this.element = {
|
||||||
root: this.root,
|
root: this.root,
|
||||||
@ -208,7 +228,7 @@ class SaveButton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
changeState(data) {
|
changeState(data) {
|
||||||
data = data || {}
|
var data = data || {}
|
||||||
this.element.button.innerText = data.buttonText || "Save"
|
this.element.button.innerText = data.buttonText || "Save"
|
||||||
this.element.button.disabled = data.buttonClickable === false ? true : false
|
this.element.button.disabled = data.buttonClickable === false ? true : false
|
||||||
this.element.icon.className = 'savebutton--icon ' + (data.iconClasses || "")
|
this.element.icon.className = 'savebutton--icon ' + (data.iconClasses || "")
|
||||||
@ -251,24 +271,93 @@ class SaveButton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initDrafts() {
|
|
||||||
var button = new SaveButton(document.querySelector('.savebutton'))
|
|
||||||
|
|
||||||
$('#case-study-form').dirtyForms()
|
class DraftPrompt {
|
||||||
$('#case-study-form').on('dirty.dirtyforms', ev => {
|
constructor(opts) {
|
||||||
button.switchStateUnsaved()
|
this.restoreDraft = opts.restoreFn
|
||||||
})
|
this.deleteDraft = opts.deleteFn
|
||||||
|
|
||||||
button.element.button.addEventListener('click', evt => {
|
this.root = opts.root
|
||||||
button.switchStateSaving()
|
this.element = {
|
||||||
|
root: this.root,
|
||||||
|
restore: this.root.querySelector('.draftprompt--restore'),
|
||||||
|
delete: this.root.querySelector('.draftprompt--delete'),
|
||||||
|
details: this.root.querySelector('.draftprompt--details')
|
||||||
|
}
|
||||||
|
|
||||||
var fs = new FormSaver({
|
// Restore should restore, then hide the prompt
|
||||||
formId: 'case-study-form',
|
this.element.restore.addEventListener('click', () => {
|
||||||
except: [ 'csrfmiddlewaretoken' ]
|
this.restoreDraft()
|
||||||
|
this.switchStateHidden()
|
||||||
})
|
})
|
||||||
var s = fs.serialise();
|
|
||||||
|
|
||||||
fetch('/en-gb/case-study/draft', {
|
// Delete button will delete the draft
|
||||||
|
this.element.delete.addEventListener('click', () => {
|
||||||
|
if (window.confirm('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: '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',
|
method: 'PUT',
|
||||||
credentials: "same-origin",
|
credentials: "same-origin",
|
||||||
headers: {
|
headers: {
|
||||||
@ -276,9 +365,69 @@ function initDrafts() {
|
|||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ data: s.form, version: 1 })
|
body: JSON.stringify({ version: 1, data: formSaver.serialise() })
|
||||||
}).then(response => {
|
})
|
||||||
console.log(response);
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
var formSaver = new FormSaver({
|
||||||
|
formId: 'case-study-form',
|
||||||
|
except: [ 'csrfmiddlewaretoken' ]
|
||||||
|
})
|
||||||
|
|
||||||
|
// Get the previous draft, if any
|
||||||
|
apiGetDraft()
|
||||||
|
.then(response => 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)
|
||||||
|
}).catch(err => {
|
||||||
|
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) {
|
if (response.ok) {
|
||||||
button.switchStateSaveSuccess();
|
button.switchStateSaveSuccess();
|
||||||
$('#case-study-form').dirtyForms('setClean');
|
$('#case-study-form').dirtyForms('setClean');
|
||||||
@ -292,7 +441,7 @@ function initDrafts() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
document.forms['case-study-form'].addEventListener('submit', () => {
|
document.forms['case-study-form'].addEventListener('submit', () => {
|
||||||
// We're submitting, so kosh the saved data
|
// XXX We're submitting, so kosh the saved data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user