Now with edits, delete confirmations

- and also a better alignment on actions
- and konami code that works before login
This commit is contained in:
Paul Mathieu 2025-08-06 19:12:44 +02:00
parent 3d35d47254
commit 3474239727
5 changed files with 124 additions and 50 deletions

View File

@ -30,7 +30,6 @@ def auth_only(f):
def __f(request):
# check that email is valid
# exp?
print(request.META, file=sys.stderr)
if 'user_data' not in request.session:
raise PermissionDenied('Not logged in')
email = request.session['user_data']['email']
@ -57,6 +56,7 @@ def get_list(request):
'id': x.id,
'title': x.title,
'category': x.category.name,
'category_id': x.category.id,
'prototempalte': x.category.prototempalte.name,
'landscape': x.category.landscape,
'designation': x.designation,

View File

@ -29,6 +29,7 @@ urlpatterns = [
path('generate', tikette.views.generate),
path('newtikette', tikette.views.newtikette),
path('deletetikette', tikette.views.deletetikette),
path('updatetikette', tikette.views.newtikette), # yes, we use newtikette
path('signin', tikette.views.signin),
path('signout', tikette.views.signout),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -21,14 +21,15 @@
<script src="zetikettes.js"></script>
<style>
body {
display: flex;
min-height: 100vh;
flex-direction: column;
.actions {
margin-left: auto;
white-space: nowrap;
}
main {
flex: 1 0 auto;
.confirm {
width: 35%;
min-width: 20em;
max-width: 85%;
}
</style>
</head>
@ -38,7 +39,7 @@ main {
<a href="#" class="brand-logo">Zětikwett's</a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li>
<a class="modal-trigger btn orange" href="#newproduct">
<a class="modal-trigger btn orange disabled" href="#newproduct">
<i class="material-icons left">add</i>Nouveau produit
</a>
</li>
@ -49,8 +50,8 @@ main {
<p></p>
<div class="col s4 offset-s4" id="signin-prompt" style="display: none"></div>
<div class="col m6 offset-m3 s12">
<a class="modal-trigger btn orange hide-on-large-only" href="#newproduct"><i class="material-icons left">add</i>Nouveau produit</a>
<ul class="collapsible" id="appbody">
<a class="modal-trigger btn orange hide-on-large-only disabled" href="#newproduct"><i class="material-icons left">add</i>Nouveau produit</a>
<ul class="collapsible collection" id="appbody">
<div class="shimmer">
<li><div class="collapsible-header"><h6 class="faux-text short"></h6></div></li>
<li><div class="collapsible-header"><h6 class="faux-text"></h6></div></li>
@ -58,6 +59,15 @@ main {
</div>
</ul>
</div>
<div id="confirmdelete" class="modal confirm">
<div class="modal-content">
Supprimer le produit <b id="delete-title">blařg titre long de test sa mère</b> ?
</div>
<div class="modal-footer">
<button class="modal-close waves-effect btn-flat" id="delete-cancel">annuler</button>
<button type="submit" class="modal-close waves-effect waves-green btn" id="delete-confirm">supprimer</button>
</div>
</div>
<!-- Modal Structure -->
<div id="newproduct" class="modal">

View File

@ -30,6 +30,56 @@ function post(url, data) {
});
}
function disableButtons() {
$('.btn').addClass("disabled");
}
function enableButtons() {
$('.btn').removeClass('disabled');
}
function resetEditModal(force = false) {
if ($("#new-add").text() === "Ajouter" && !force) {
return;
}
$("#new-name").val("");
$("#new-type").val("").formSelect();
$("#new-designation").val("");
$("#new-ingredients").val("");
$("#new-description").val("");
$("#new-color")[0].jscolor.fromString("#97a1cc");
$("#new-organic").prop("checked", false);
M.updateTextFields();
$("#new-add").text("Ajouter").off('click').click(() => {
const req = getTiketteData();
post(backend_api + 'newtikette', req).then(() => {
resetEditModal(true);
reload();
});
});
}
function openEditModal(zett) {
M.Modal.getInstance($("#newproduct")[0]).open();
$("#new-name").val(zett.title);
$("#new-type").val(zett.category_id).formSelect();
$("#new-designation").val(zett.designation);
$("#new-ingredients").val(zett.ingredients);
$("#new-description").val(zett.description);
$("#new-color")[0].jscolor.fromString(zett.color);
$("#new-organic").prop("checked", zett.ab === "inline");
M.updateTextFields();
$("#new-add").text("Modifier").off('click').click(() => {
const req = getTiketteData();
req.id = zett.id;
resetEditModal();
post(backend_api + 'updatetikette', req).then(reload);
});
}
function addProduct(tikette) {
const zett = tikette;
const appbody = $("#appbody");
@ -41,7 +91,7 @@ function addProduct(tikette) {
.hide();
const action = $('<div class="section">')
.append($('<a class="btn">generate<a>')
.append($('<a class="btn">générer<a>')
.click(() => {
const subs = block.find(':text')
.toArray()
@ -58,11 +108,11 @@ function addProduct(tikette) {
};
loader.show();
$('.btn').addClass("disabled");
disableButtons();
post(backend_api + 'generate', req)
.then(data => {
const pdfbtn = $(`<a class="btn" href="${backend_api}data/${data.file}" target="_blank">open pdf</a>`);
const pdfbtn = $(`<a class="btn" href="${backend_api}data/${data.file}" target="_blank">ouvrir le pdf</a>`);
action.append(pdfbtn);
})
.catch(err => {
@ -70,27 +120,37 @@ function addProduct(tikette) {
})
.always(() => {
loader.hide();
$('.btn').removeClass('disabled');
enableButtons();
});
})
.append(loader));
const deleteAction = $('<a class="btn-flat grey-text"><b class="material-icons">delete</b>');
deleteAction.click(() => {
const req = {
id: zett.id,
};
post(backend_api + 'deletetikette', req).then(reload);
const deleteAction = $('<b class="material-icons">delete</b>').click(() => {
$("#delete-title").text(zett.title);
$("#delete-confirm").off('click').click(() => {
const req = {
id: zett.id,
};
post(backend_api + 'deletetikette', req).then(reload);
});
M.Modal.getInstance($("#confirmdelete")[0]).open();
return false;
});
const editAction = $('<b class="material-icons">edit</b>').click(() => {
openEditModal(zett);
return false;
});
appbody
.append($('<li>')
.append($('<div class="collapsible-header valign-wrapper">')
.append(`<h6 class="blue-text">${zett.title}</h6>`)
.append($('<span class="badge">')
.append(deleteAction)))
.append($('<span class="actions grey-text">')
.append(editAction)
.append(deleteAction)
))
.append($('<div class="collapsible-body">')
.append(block)
.append(action)));
@ -103,6 +163,28 @@ function setCategories() {
for (let kat of tikats) {
katsel.append($(`<option value="${kat.id}">${kat.name}</option>`));
}
$('select').formSelect();
}
function getTiketteData() {
const title = $("#new-name").val();
const category_id = $("#new-type").val();
const designation = $("#new-designation").val();
const ingredients = $("#new-ingredients").val();
const description = $("#new-description").val();
const color = $("#new-color").val().substring(1);
const ab = $("#new-organic").is(":checked") ? 'inline' : 'none';
return {
title,
category_id,
designation,
ingredients,
description,
color,
ab,
};
}
function loadAll(zetikettes) {
@ -112,33 +194,6 @@ function loadAll(zetikettes) {
addProduct(zett);
}
setCategories();
$('.collapsible').collapsible();
$('.modal').modal();
$('select').formSelect();
konami();
$('#new-add').off('click').click(() => {
const title = $("#new-name").val();
const category_id = $("#new-type").val();
const designation = $("#new-designation").val();
const ingredients = $("#new-ingredients").val();
const description = $("#new-description").val();
const color = $("#new-color").val().substring(1);
const ab = $("#new-organic").is(":checked") ? 'visible' : 'none';
const req = {
title,
category_id,
designation,
ingredients,
description,
color,
ab,
};
post(backend_api + 'newtikette', req).then(reload);
});
}
function konami() {
@ -164,6 +219,8 @@ async function googleCred(creds) {
}
async function reload() {
disableButtons();
try {
const resp = await $.ajax({
url: backend_api + 'list',
@ -176,6 +233,7 @@ async function reload() {
xhrFields: { withCredentials: true },
})).tikats.sort((a, b) => a.name > b.name ? 1 : -1);
loadAll(resp.tikettes.sort((a, b) => (a.title < b.title) ? -1 : 1));
enableButtons();
} catch(e) {
if (e.status === 403) {
$("#signin-prompt").show();
@ -196,5 +254,10 @@ $(document).ready(() => {
document.getElementById("signin-prompt"),
{ theme: "outline", size: "large" } // customization attributes
);
konami();
$('.collapsible').collapsible();
$('.modal').modal({onOpenStart: resetEditModal});
reload();
});

View File

@ -26,7 +26,7 @@ class Tikette:
'ingredients': self.ingredients,
'description': self.description,
'color': self.color,
'ab': 'inline' if self.ab else 'none',
'ab': 'inline' if self.ab is 'True' else 'none',
}