Turn the crank on add/edit/delete of issues.
Turn deletion of an issue into a GET rather than a DELETE. This breaks
HTTP idioms, but I have not found a way to perform a DELETE (via
fetch() in the browser) and properly handle a 303 redirect (it can
work, but does a double-fetch of the Location target)
Iterate the add/edit endpoints to fetch the form data.
do_delete_issue_endpoint() now calls .delete_issue() to actually
delete the issue from the Election.
add flash messages to the issue-handling endpoints.
fix the JS in manage.ezt for the dynamic add/edit modal, and add an
id= to the delete buttons for deleteIssue() to find.
diff --git a/v3/server/pages.py b/v3/server/pages.py
index 557101b..cd83822 100644
--- a/v3/server/pages.py
+++ b/v3/server/pages.py
@@ -317,11 +317,21 @@
### check authz
- ### do stuff
+ data = edict(await quart.request.get_json())
+ print('FORM:', data)
- _LOGGER.info(f'User[U:{result.uid}] added issue[I:{iid}]'
+ ### do stuff
+ ### add_issue(iid, title, description, vtype, kv)
+ ### the IID should be created by add_issue. Do this for now.
+ issue = edict(iid=steve.crypto.create_id(),
+ title='<placeholder>')
+
+ _LOGGER.info(f'User[U:{result.uid}] added issue[I:{issue.iid}]'
f' to election[E:{election.eid}]')
+ ### fill in the real title from the form data
+ await flash_success(f'Issue "{issue.title}" has been added.')
+
# Return to the management page for this Election.
return quart.redirect(f'/manage/{election.eid}', code=303)
@@ -334,16 +344,23 @@
### check authz
+ data = edict(await quart.request.get_json())
+ print('FORM:', data)
+
### do stuff
+ ### add_issue(iid, title, description, vtype, kv)
_LOGGER.info(f'User[U:{result.uid}] edited issue[I:{issue.iid}]'
f' in election[E:{election.eid}]')
+ ### this is old title. switch to new title.
+ await flash_success(f'Issue "{issue.title}" has been updated.')
+
# Return to the management page for this Election.
return quart.redirect(f'/manage/{election.eid}', code=303)
-@APP.delete('/do-delete-issue/<eid>/<iid>')
+@APP.get('/do-delete-issue/<eid>/<iid>')
@asfquart.auth.require({R.committer}) ### need general solution
@load_election_issue
async def do_delete_issue_endpoint(election, issue):
@@ -351,11 +368,14 @@
### check authz
- ### do stuff
+ # Issue exists, and was loaded. No errors to handle?
+ election.delete_issue(issue.iid)
_LOGGER.info(f'User[U:{result.uid}] deleted issue[I:{issue.iid}]'
f' from election[E:{election.eid}]')
+ await flash_success(f'Issue "{issue.title}" has been deleted.')
+
# Return to the management page for this Election.
return quart.redirect(f'/manage/{election.eid}', code=303)
diff --git a/v3/server/templates/manage.ezt b/v3/server/templates/manage.ezt
index de0768f..9a1cd6f 100644
--- a/v3/server/templates/manage.ezt
+++ b/v3/server/templates/manage.ezt
@@ -127,6 +127,7 @@
<i class="bi bi-pencil"></i>
</button>
<button type="button" class="btn btn-outline-danger btn-sm"
+ id="delete-[issues.iid]"
onclick="deleteIssue('[issues.iid]')" aria-label="Delete Issue">
<i class="bi bi-trash"></i>
</button>
@@ -238,15 +239,17 @@
body: JSON.stringify({ title, description }),
})
.then(response => {
- if (!response.ok) throw new Error('Network response was not ok');
- return response.json();
- })
- .then(data => {
- bootstrap.Modal.getInstance(document.getElementById('issueModal')).hide();
- // Server will refresh page with flash messages
+ if (response.status === 303) {
+ const redirectUrl = response.headers.get('Location');
+ window.location.href = redirectUrl; // Navigate to server’s Location
+ } else {
+ // Fallback for any non-303 response (success or error)
+ window.location.reload(); // Let server render flash messages or error page
+ }
})
.catch(error => {
- // Server will handle error and flash message
+ console.error('Network error:', error);
+ window.location.reload(); // Reload to show server’s error page or flash message
});
}
@@ -254,19 +257,10 @@
function deleteIssue(issueId) {
if (!confirm('Are you sure you want to delete this issue?')) return;
- fetch(`/do-delete-issue/[eid]/${issueId}`, {
- method: 'DELETE',
- })
- .then(response => {
- if (!response.ok) throw new Error('Network response was not ok');
- return response.json();
- })
- .then(data => {
- // Server will refresh page with flash messages
- })
- .catch(error => {
- // Server will handle error and flash message
- });
+ const button = document.querySelector(`#delete-${issueId}`);
+ button.disabled = true; // Prevent multiple clicks
+
+ window.location.href = `/do-delete-issue/[eid]/${issueId}`;
}
</script>