From 0caabb0bb7f32792c6834526325bea89fa807f2c Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Mon, 16 Sep 2024 15:31:51 +0200 Subject: [PATCH 01/33] Add macro for button Implements #312 --- root/common_templates/macros.tt | 49 +++++++++++++++++++++++++++++++++ root/static/css/print.css | 3 +- root/static/css/style.css | 14 +++++++++- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/root/common_templates/macros.tt b/root/common_templates/macros.tt index 4654ceb25..1abd02f43 100644 --- a/root/common_templates/macros.tt +++ b/root/common_templates/macros.tt @@ -107,4 +107,53 @@ length=items.size ~%] [% END %] +[%~ END; + +# Arguments for button and tooltip_button +# opts: +# - variant: optional, str - one of the bootstrap variants, default: primary +# - disabled: bool, optional - triggering the "disabled" HTML attribute, default: false +# - href: str, optional - if set the button is rendered as Tag with the given href instead of - + [% tooltip_button( { + variant => 'outline-primary', + icon => 'add', + disabled => !create_url, + tooltip => { + content => 'Create ' _ (purchase_lists.size > 0 ? 'another' : 'a') _ ' purchase list', + line_through => 1, + }, + attrs => { + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#addList', + }, + container_classes => 'd-grid', + button_classes => 'btn-no-quad', + } ) %] [% END %] @@ -41,50 +53,62 @@ BLOCK new_list %] [% purchase_list.ingredients_count %]
- - - + [% tooltip_button( { + variant => 'outline-dark', + disabled => !purchase_list.update_url, + icon => 'edit', + tooltip => { content => 'Rename purchase list', line_through => 1 }, + attrs => { + 'data-url' => purchase_list.update_url, + 'data-name' => purchase_list.name, + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#updateList', + }, + button_classes => 'btn-sm update-trigger', + } ); - [% IF purchase_lists.size > 1; - IF purchase_list.make_default_url %] + IF purchase_lists.size > 1 %]
- + [% tooltip_button( { + variant => 'outline-dark', + disabled => !purchase_list.make_default_url, + icon => 'shopping_cart_checkout', + tooltip => { + content => purchase_list.is_default ? 'Is already the default purchase list' : 'Make purchase list the default purchase list', + line_through => purchase_list.is_default ? 0 : 1, + }, + attrs => { + type => 'submit', + }, + button_classes => 'btn-sm', + } ) %]
- [% ELSE %] - - - - [% END; - END; + [% END; - IF purchase_list.delete_url; - IF is_in_use %] - - - - [% ELSE %] -
- -
- [% END; + button_classes = [ 'btn-sm' ]; + button_classes.push('delete-trigger') IF is_in_use; - ELSE %] - - - - [% END %] + tooltip_button( { + button_classes => button_classes, + variant => 'outline-danger', + disabled => !purchase_list.delete_url || (purchase_lists.size > 1 && purchase_list.is_default), + icon => 'delete_forever', + tooltip => { + content => purchase_lists.size > 1 && purchase_list.is_default ? 'Can’t delete default purchase list' : 'Delete purchase list', + line_through => purchase_lists.size > 1 && purchase_list.is_default ? 0 : 1, + }, + attrs => is_in_use ? { + 'data-url' => purchase_list.delete_url, + 'data-name' => purchase_list.name, + 'data-count' => purchase_list.items_count, + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#delete-list', + } : { + form => 'delete-list-form-' _ purchase_list.id, + type => 'submit', + }, + } ) %] +
-- GitLab From 80f4284ff2da8713c577ee446d13c6d02de80466 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Mon, 16 Sep 2024 20:52:24 +0200 Subject: [PATCH 05/33] Respect project state und capabilities in purchase list editor --- lib/Coocook/Controller/PurchaseList.pm | 8 + root/static/css/style.css | 5 - root/static/js/purchase_list/edit.js | 343 +++++++++++--------- root/static/js/script.js | 30 ++ root/templates/purchase_list/_edit_table.tt | 39 ++- root/templates/purchase_list/edit.tt | 56 +++- 6 files changed, 298 insertions(+), 183 deletions(-) diff --git a/lib/Coocook/Controller/PurchaseList.pm b/lib/Coocook/Controller/PurchaseList.pm index 610ab6f10..81903f84d 100644 --- a/lib/Coocook/Controller/PurchaseList.pm +++ b/lib/Coocook/Controller/PurchaseList.pm @@ -98,6 +98,14 @@ sub edit : GET HEAD Chained('base') PathPart('') Args(0) RequiresCapability('vie $dish->{url} = $c->project_uri( '/dish/edit', $dish->{id} ); } + $c->stash( can_edit => !!$c->has_capability('edit_project') ); + $c->json_stash( + list_permissions => { + can_edit => !!$c->has_capability('edit_project'), + is_archived => !!$c->project->archived, + }, + ); + $c->has_capability('edit_project') or return; diff --git a/root/static/css/style.css b/root/static/css/style.css index b5b0b7150..48de23d01 100644 --- a/root/static/css/style.css +++ b/root/static/css/style.css @@ -390,11 +390,6 @@ cc-autocomplete { border-width: 0; } -.tt-disabled-btn { - pointer-events: auto !important; - cursor: default; -} - .purchase-list-table .selected { --bs-table-accent-bg: var(--bs-table-striped-bg); color: var(--bs-table-striped-color); diff --git a/root/static/js/purchase_list/edit.js b/root/static/js/purchase_list/edit.js index 749124fdc..7bbdaa9c9 100644 --- a/root/static/js/purchase_list/edit.js +++ b/root/static/js/purchase_list/edit.js @@ -1,14 +1,20 @@ // Purchase list moving constants -const moveBtn = document.getElementById("move-btn"); -const moveBtnDisabled = document.getElementById("move-btn-disabled"); +const moveBtn = getBtn( + "#move-btn", + "Move selected items/ingredients to different purchase list", + "No items/ingredients selected to move" +); const moveItemsForm = document.getElementById("move-items-form"); const moveModal = document.getElementById("move-items"); const selectPl = document.getElementById("move-items-target"); const currentPurchaseListId = parseInt(location.pathname.split("/").pop()); // Shop section moving constants -const assignBtn = document.getElementById("assign-btn"); -const assignBtnDisabled = document.getElementById("assign-btn-disabled"); +const assignBtn = getBtn( + "#assign-btn", + "Assign selected articles to a shop section", + "No articles selected to assign" +); const assignArticlesForm = document.getElementById("assign-articles-form"); const assignModal = document.getElementById("assign-articles"); const selectShopSection = document.getElementById("assign-articles-target"); @@ -21,13 +27,183 @@ const articleSelectList = document.getElementById("article-select-list"); const messages = document.getElementById("messages"); const purchaseLists = getJsonData("purchase_lists"); -const singleList = purchaseLists.length === 1; +const singleList = (purchaseLists || []).length === 1; let shopSections = getJsonData("shop_sections"); let articles = {}; let preselectedPurchaseList = null; +const permissions = getJsonData("list_permissions"); let SKIP = false; +(() => { + if (permissions.is_archived || !permissions.can_edit) { + moveBtn.disable(`Can't move items because ${getDisabledReason()}`); + assignBtn.disable( + `Can't assign articles because ${getDisabledReason()}` + ); + return; + } + + init(); + + // Purchase lists + buildPurchaseListOptions(); + + moveModal.addEventListener("shown.bs.modal", () => selectPl.focus()); + moveModal.addEventListener("hidden.bs.modal", () => { + if (preselectedPurchaseList !== null) { + selectPl.value = preselectedPurchaseList; + selectPl.setCustomValidity(""); + } else { + selectPl.value = ""; + selectPl.setCustomValidity("Please select a target purchase list"); + } + }); + + moveItemsForm.addEventListener("submit", async (e) => { + e.preventDefault(); + const items = document.querySelectorAll( + `input[id^="move-item"]:checked` + ); + const ingredients = document.querySelectorAll( + `input[id^="move-ingredient"]:checked` + ); + const data = `target_purchase_list=${selectPl.value}${Array.from(items) + .map((elem) => `&item=${elem.id.split("-").pop()}`) + .join("")}${Array.from(ingredients) + .map((elem) => `&ingredient=${elem.id.split("-").pop()}`) + .join("")}`; + const url = + location.pathname + + (location.pathname.endsWith("/") ? "" : "/") + + "move_items_ingredients"; + const res = await fetch(url, { + method: "post", + headers: { + Accept: "text/html", + "Content-Type": "application/x-www-form-urlencoded", + }, + body: data, + }); + + if (!res.ok) { + showMessage( + "An error occured during moving the selected items/ingredients", + "danger" + ); + } else { + document.getElementById("list-container").innerHTML = + await res.text(); + showMessage( + "Successfully moved the selected items/ingredients", + "success" + ); + init(); + } + + bootstrap.Modal.getInstance(moveModal).hide(); + }); + + // Shop sections + initAutocomplete(); + + assignModal.addEventListener("shown.bs.modal", () => + selectShopSection.focus() + ); + assignModal.addEventListener("show.bs.modal", () => { + const items = document.querySelectorAll( + `input[id^="move-item"]:checked` + ); + const selectedArticles = mapArticleAmount(items); + const error = []; + + for (const [id, data] of Object.entries(selectedArticles)) { + if (articles[id].num !== data.num) { + error.push({ + name: data.name, + selected: data.num, + total: articles[id].num, + }); + } + } + + if (error.length === 0) { + articleSelectWarning.classList.add("d-none"); + articleSelectWarning.classList.remove("d-flex"); + } else { + articleSelectNum.innerText = `article${ + error.length === 1 ? "" : "s" + }`; + articleSelectList.innerHTML = error + .map( + (elem) => + `
  • ${elem.name} - ${elem.selected} of ${elem.total} items selected
  • ` + ) + .join(""); + articleSelectWarning.classList.remove("d-none"); + articleSelectWarning.classList.add("d-flex"); + } + }); + assignModal.addEventListener("hidden.bs.modal", () => { + selectShopSection.clear(); + }); + + assignArticlesForm.addEventListener("submit", async (e) => { + e.preventDefault(); + + const section = String( + new FormData(e.target).get("target-shop-section") + ).trim(); + const numSection = parseInt(section); + + const shopSection = `${ + !(section === "null" || Number.isInteger(numSection)) ? "new_" : "" + }shop_section=${section}`; + + const items = document.querySelectorAll( + `input[id^="move-item"]:checked` + ); + + const data = `${shopSection}${Array.from(items) + .map((elem) => `&article=${elem.dataset.articleId}`) + .join("")}`; + const url = + location.pathname + + (location.pathname.endsWith("/") ? "" : "/") + + "assign_articles_to_shop_section"; + const res = await fetch(url, { + method: "post", + headers: { + Accept: "text/html", + "Content-Type": "application/x-www-form-urlencoded", + }, + body: data, + }); + + if (!res.ok) { + showMessage( + "An error occured during moving the selected articles", + "danger" + ); + } else { + document.getElementById("list-container").innerHTML = + await res.text(); + showMessage("Successfully moved the selected articles", "success"); + init(); + selectShopSection.options = getShopSectionOptions(); + selectShopSection.clear(); + } + + bootstrap.Modal.getInstance(assignModal).hide(); + }); +})(); + +function getDisabledReason() { + return permissions.is_archived + ? "the project is archived" + : "you don't have enough permissions for this action"; +} + function showMessage(msg, type) { const msgElem = document.createElement("div"); msgElem.className = `alert alert-${type}`; @@ -111,12 +287,6 @@ function mapArticleAmount(items) { } function init() { - if (shopSections === undefined || purchaseLists === undefined) { - if (!singleList) moveBtnDisabled.classList.add("d-none"); - assignBtnDisabled.classList.add("d-none"); - return; - } - const items = document.querySelectorAll(`input[id^="move-item"]`); const ingredients = document.querySelectorAll( `input[id^="move-ingredient"]` @@ -186,26 +356,20 @@ function checkDisabled() { } if (items > 0 && !singleIngredientSelected) { - assignBtn?.classList.remove("d-none"); - assignBtnDisabled?.classList.add("d-none"); + assignBtn?.enable(); } else { - assignBtn?.classList.add("d-none"); - assignBtnDisabled?.classList.remove("d-none"); + assignBtn?.disable(); } if (!singleList) { if (items > 0 || ingredients.length > 0) { - moveBtn?.classList.remove("d-none"); - moveBtnDisabled?.classList.add("d-none"); + moveBtn?.enable(); } else { - moveBtn?.classList.add("d-none"); - moveBtnDisabled?.classList.remove("d-none"); + moveBtn?.disable(); } } } -init(); - // Purchase lists function buildPurchaseListOptions() { for (const pl of purchaseLists) { @@ -244,60 +408,6 @@ function buildPurchaseListOptions() { }); } -buildPurchaseListOptions(); - -moveModal.addEventListener("shown.bs.modal", () => selectPl.focus()); -moveModal.addEventListener("hidden.bs.modal", () => { - if (preselectedPurchaseList !== null) { - selectPl.value = preselectedPurchaseList; - selectPl.setCustomValidity(""); - } else { - selectPl.value = ""; - selectPl.setCustomValidity("Please select a target purchase list"); - } -}); - -moveItemsForm.addEventListener("submit", async (e) => { - e.preventDefault(); - const items = document.querySelectorAll(`input[id^="move-item"]:checked`); - const ingredients = document.querySelectorAll( - `input[id^="move-ingredient"]:checked` - ); - const data = `target_purchase_list=${selectPl.value}${Array.from(items) - .map((elem) => `&item=${elem.id.split("-").pop()}`) - .join("")}${Array.from(ingredients) - .map((elem) => `&ingredient=${elem.id.split("-").pop()}`) - .join("")}`; - const url = - location.pathname + - (location.pathname.endsWith("/") ? "" : "/") + - "move_items_ingredients"; - const res = await fetch(url, { - method: "post", - headers: { - Accept: "text/html", - "Content-Type": "application/x-www-form-urlencoded", - }, - body: data, - }); - - if (!res.ok) { - showMessage( - "An error occured during moving the selected items/ingredients", - "danger" - ); - } else { - document.getElementById("list-container").innerHTML = await res.text(); - showMessage( - "Successfully moved the selected items/ingredients", - "success" - ); - init(); - } - - bootstrap.Modal.getInstance(moveModal).hide(); -}); - // Shop sections function getShopSectionOptions() { return [ @@ -321,86 +431,3 @@ function initAutocomplete() { selectShopSection.clear(); }, 1_000); } - -initAutocomplete(); - -assignModal.addEventListener("shown.bs.modal", () => selectShopSection.focus()); -assignModal.addEventListener("show.bs.modal", () => { - const items = document.querySelectorAll(`input[id^="move-item"]:checked`); - const selectedArticles = mapArticleAmount(items); - const error = []; - - for (const [id, data] of Object.entries(selectedArticles)) { - if (articles[id].num !== data.num) { - error.push({ - name: data.name, - selected: data.num, - total: articles[id].num, - }); - } - } - - if (error.length === 0) { - articleSelectWarning.classList.add("d-none"); - articleSelectWarning.classList.remove("d-flex"); - } else { - articleSelectNum.innerText = `article${error.length === 1 ? "" : "s"}`; - articleSelectList.innerHTML = error - .map( - (elem) => - `
  • ${elem.name} - ${elem.selected} of ${elem.total} items selected
  • ` - ) - .join(""); - articleSelectWarning.classList.remove("d-none"); - articleSelectWarning.classList.add("d-flex"); - } -}); -assignModal.addEventListener("hidden.bs.modal", () => { - selectShopSection.clear(); -}); - -assignArticlesForm.addEventListener("submit", async (e) => { - e.preventDefault(); - - const section = String( - new FormData(e.target).get("target-shop-section") - ).trim(); - const numSection = parseInt(section); - - const shopSection = `${ - !(section === "null" || Number.isInteger(numSection)) ? "new_" : "" - }shop_section=${section}`; - - const items = document.querySelectorAll(`input[id^="move-item"]:checked`); - - const data = `${shopSection}${Array.from(items) - .map((elem) => `&article=${elem.dataset.articleId}`) - .join("")}`; - const url = - location.pathname + - (location.pathname.endsWith("/") ? "" : "/") + - "assign_articles_to_shop_section"; - const res = await fetch(url, { - method: "post", - headers: { - Accept: "text/html", - "Content-Type": "application/x-www-form-urlencoded", - }, - body: data, - }); - - if (!res.ok) { - showMessage( - "An error occured during moving the selected articles", - "danger" - ); - } else { - document.getElementById("list-container").innerHTML = await res.text(); - showMessage("Successfully moved the selected articles", "success"); - init(); - selectShopSection.options = getShopSectionOptions(); - selectShopSection.clear(); - } - - bootstrap.Modal.getInstance(assignModal).hide(); -}); diff --git a/root/static/js/script.js b/root/static/js/script.js index cb87d3161..d4c3338d1 100644 --- a/root/static/js/script.js +++ b/root/static/js/script.js @@ -170,3 +170,33 @@ function getJsonData(id) { return undefined; } } + +function getBtn(selector, enabledMsg, disabledMsg) { + const btn = document.querySelector(selector); + + if (btn === null) { + return null; + } + + const tt = btn.parentElement; + + const changeTooltip = (msg) => { + const bsTt = bootstrap.Tooltip.getInstance(tt); + bsTt.dispose(); + tt.dataset.bsTitle = msg; + bootstrap.Tooltip.getOrCreateInstance(tt); + }; + + return { + elem: btn, + disable: (msg = disabledMsg) => { + btn.disabled = true; + changeTooltip(msg); + }, + enable: (msg = enabledMsg) => { + btn.disabled = false; + changeTooltip(msg); + }, + changeTooltip, + }; +} diff --git a/root/templates/purchase_list/_edit_table.tt b/root/templates/purchase_list/_edit_table.tt index dcf7037b3..bb3320066 100644 --- a/root/templates/purchase_list/_edit_table.tt +++ b/root/templates/purchase_list/_edit_table.tt @@ -48,7 +48,17 @@ [% display_unit( item.unit, {html=>1} ) %] - + [% tooltip_button( { + variant => 'outline-primary', + icon => 'done', + tooltip => { + content => 'Update amount', + }, + attrs => { + type => 'submit', + }, + button_classes => 'ms-1 pl-form', + } ) %] [% ELSE; IF item.offset < 0; '⊖'; @@ -58,10 +68,20 @@ END; IF item.convertible_into.size %] -
    +
    [% FOREACH unit IN item.convertible_into %]
    - + [% tooltip_button( { + variant => 'outline-secondary', + text => display_value_unit(unit.total, unit, {html=>1}), + tooltip => { + content => 'Convert to ' _ display_value_unit(unit.total, unit, {print=>1}), + }, + attrs => { + type => 'submit', + }, + button_classes => 'btn-sm', + } ) %]
    [% END %]
    @@ -125,7 +145,18 @@ [% IF item.update_offset_url %]
    - + [% tooltip_button( { + variant => 'outline-danger', + icon => 'undo', + tooltip => { + content => 'Reset rounding', + }, + attrs => { + type => 'submit', + name => 'offset', + value => '0', + }, + } ) %]
    [% END %] diff --git a/root/templates/purchase_list/edit.tt b/root/templates/purchase_list/edit.tt index 80c0eca1c..f067940b2 100644 --- a/root/templates/purchase_list/edit.tt +++ b/root/templates/purchase_list/edit.tt @@ -9,23 +9,47 @@ js.push('/lib/coocook-web-components/dist/autocomplete/autocomplete.es.js'); js_push_template_path(); %] - -[% IF purchase_lists.size > 1 %] - - -[% END %] - - +
    + [% tooltip_button( { + icon => 'print', + tooltip => { content => 'Print' }, + attrs => { onclick => 'print()' }, + } ); -
    + IF purchase_lists.size > 1; + tooltip_button( { + disabled => 1, + icon => 'drive_file_move', + text => 'Move items/ingredients to other purchase list', + tooltip => { + content => 'Move selected items/ingredients to different purchase list', + disabled_content => 'No items/ingredients selected to move', + }, + attrs => { + id => 'move-btn', + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#move-items', + }, + } ); + END; + + tooltip_button( { + disabled => 1, + icon => 'move_to_inbox', + text => 'Assign articles to shop section', + tooltip => { + content => 'Assign selected articles to a shop section', + disabled_content => 'No articles selected to assign', + }, + attrs => { + id => 'assign-btn', + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#assign-articles', + }, + } ) %] +
    + +
    [% INCLUDE purchase_list/_edit_table.tt %]
    -- GitLab From ae7bbd8ece82c38187bea8e2c7cd8daaa15a57c1 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Thu, 24 Oct 2024 11:23:10 +0200 Subject: [PATCH 06/33] Use button macro for faq pages --- root/templates/admin/faq/edit.tt | 13 +++++++++++-- root/templates/admin/faq/index.tt | 19 +++++++++++++++++-- root/templates/faq/index.tt | 11 ++++++++++- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/root/templates/admin/faq/edit.tt b/root/templates/admin/faq/edit.tt index f465b63e6..22d8fc7cc 100644 --- a/root/templates/admin/faq/edit.tt +++ b/root/templates/admin/faq/edit.tt @@ -3,7 +3,11 @@ [% IF faq %]
    - Show the FAQ entry + [% button( { + text => 'Show the FAQ entry', + variant => 'outline-primary', + href => faq.url, + } ) %]
    [% END %] @@ -34,7 +38,12 @@
    - + [% button( { + text => (faq.in_storage ? 'Update' : 'Create') _ ' Entry', + attrs => { + type => 'submit', + }, + } ) %]
    diff --git a/root/templates/admin/faq/index.tt b/root/templates/admin/faq/index.tt index 81b6a053d..66c410bdd 100644 --- a/root/templates/admin/faq/index.tt +++ b/root/templates/admin/faq/index.tt @@ -14,13 +14,28 @@ USE Markdown; %] [% faq.question_md | markdown %] - edit + [% tooltip_button( { + icon => 'edit', + tooltip => { + content => 'Edit FAQ entry', + }, + href => faq.url, + button_classes => 'btn-sm', + } ) %] [% END %] - add + [% tooltip_button( { + variant => 'outline-primary', + icon => 'add', + tooltip => { + content => 'Create FAQ entry', + }, + href => new_faq_url, + container_classes => 'd-grid', + } ) %] diff --git a/root/templates/faq/index.tt b/root/templates/faq/index.tt index fb53d2bd6..0766303ec 100644 --- a/root/templates/faq/index.tt +++ b/root/templates/faq/index.tt @@ -21,7 +21,16 @@ FOR faq IN faqs %] [% IF faq.edit_url %]
    - + [% tooltip_button( { + icon => 'edit', + tooltip => { + content => 'Edit', + }, + container_classes => 'ms-1', + attrs => { + type => 'submit', + }, + } ) %]
    [% END %] -- GitLab From 6aa2cc42547a111553d3e7ee33dd7b536c47bc77 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Thu, 24 Oct 2024 12:01:23 +0200 Subject: [PATCH 07/33] Use button macro on admin term pages --- root/templates/admin/terms/edit.tt | 11 ++++++-- root/templates/admin/terms/index.tt | 41 ++++++++++++++++++++++------- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/root/templates/admin/terms/edit.tt b/root/templates/admin/terms/edit.tt index 463554c8c..8afbe7db4 100644 --- a/root/templates/admin/terms/edit.tt +++ b/root/templates/admin/terms/edit.tt @@ -3,7 +3,7 @@

    Valid from: - +

    @@ -11,5 +11,12 @@
    -

    +
    + [% button( { + text => (terms.in_storage ? 'Update' : 'Create') _ ' terms', + attrs => { + type => 'submit', + }, + } ) %] +
    diff --git a/root/templates/admin/terms/index.tt b/root/templates/admin/terms/index.tt index e7f4388b3..5918f923e 100644 --- a/root/templates/admin/terms/index.tt +++ b/root/templates/admin/terms/index.tt @@ -5,22 +5,45 @@
    [% IF item.edit_url %]
    - + [% tooltip_button( { + variant => 'outline-dark', + icon => 'edit', + tooltip => { + content => 'Edit terms', + }, + attrs => { + type => 'submit', + }, + button_classes => 'btn-sm', + } ) %]
    [% END; IF item.delete_url %]
    - + [% tooltip_button( { + variant => 'outline-danger', + icon => 'delete_forever', + tooltip => { + content => 'Delete terms', + }, + attrs => { + type => 'submit', + }, + button_classes => 'btn-sm', + } ) %]
    [% END %]
    [% END; list(terms, 'term_item', { item_classes => 'd-flex gap-3 justify-content-between align-items-center parent'}) %] -
    - add -
    +[% tooltip_button( { + variant => 'outline-primary', + icon => 'add', + tooltip => { + content => 'Create new terms', + }, + href => new_url, + container_classes => 'd-grid mt-4', + button_classes => 'btn-no-square', +} ) %] -- GitLab From 0c4aa39f5fb7e51959b70c90fdbb4abe3eb9247f Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Thu, 24 Oct 2024 13:23:21 +0200 Subject: [PATCH 08/33] Use button macro on article pages --- root/templates/article/edit.tt | 7 ++++++- root/templates/article/index.tt | 31 +++++++++++++++++++++---------- t/controller_Article.t | 2 +- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/root/templates/article/edit.tt b/root/templates/article/edit.tt index 7adab80d6..f51d9afd0 100644 --- a/root/templates/article/edit.tt +++ b/root/templates/article/edit.tt @@ -116,7 +116,12 @@ IF article; escape_title( 'Article', article.name ); ELSE; title="New Article";
    - + [% button( { + text => (article ? 'Update' : 'Create'), + attrs => { + type => 'submit', + }, + } ) %] diff --git a/root/templates/article/index.tt b/root/templates/article/index.tt index eceb1194d..bb9368163 100644 --- a/root/templates/article/index.tt +++ b/root/templates/article/index.tt @@ -1,7 +1,13 @@ [% title = "Articles" %] [% new = BLOCK %] -

    add New article

    +

    + [% button( { + icon => 'add', + text => 'New article', + href => new_url, + } ) %] +

    [% END; new %] [% WRAPPER table length=articles.size classes='align-middle' %] @@ -42,17 +48,22 @@ - [% IF article.delete_url %]
    - + [% article_name = article.name | html; + tooltip_button( { + variant => 'outline-danger', + icon => 'delete_forever', + disabled => !article.delete_url, + tooltip => { + content => 'Delete ' _ article_name _ '', + disabled_content => 'This article is used in dishes/recipes', + }, + attrs => { + type => article.delete_url ? 'submit' : 'button', + }, + button_classes => 'btn-sm', + } ) %]
    - [% ELSE %] - - [% END %] [% END %] diff --git a/t/controller_Article.t b/t/controller_Article.t index 701a8c1e1..364b1d665 100644 --- a/t/controller_Article.t +++ b/t/controller_Article.t @@ -14,7 +14,7 @@ $t->follow_link_ok( { text => 'public Test Project' } ); $t->follow_link_ok( { text => 'Articles' } ); $t->content_contains('https://localhost/project/1/Test-Project/tag/1'); -$t->follow_link_ok( { text => 'add New article' } ); +$t->follow_link_ok( { text => 'addNew article' } ); $t->submit_form_ok( { with_fields => { name => 'aether' } }, "create article" ); -- GitLab From dd137aaa342e84426062f17ec4e3f61f0a8ee3ec Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Thu, 24 Oct 2024 14:25:46 +0200 Subject: [PATCH 09/33] Use button macro on browse pages --- root/templates/browse/recipe/import.tt | 11 ++++++++--- root/templates/browse/recipe/index.tt | 12 +++++++++++- root/templates/browse/recipe/show.tt | 27 +++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/root/templates/browse/recipe/import.tt b/root/templates/browse/recipe/import.tt index 275e8809e..b77551a51 100644 --- a/root/templates/browse/recipe/import.tt +++ b/root/templates/browse/recipe/import.tt @@ -10,9 +10,14 @@
    [% link_project(proj) %]
    - + [% button( { + text => 'Import', + icon => 'east', + attrs => { + type => 'submit', + }, + icon_after_text => 1, + } ) %]
    diff --git a/root/templates/browse/recipe/index.tt b/root/templates/browse/recipe/index.tt index 4b51a8865..a4fd7b0a7 100644 --- a/root/templates/browse/recipe/index.tt +++ b/root/templates/browse/recipe/index.tt @@ -23,7 +23,17 @@ END %] [% IF item.import_url %]
    - + [% tooltip_button( { + icon => 'east', + text => 'Import', + tooltip => { + content => 'Import this recipe into one of your projects', + }, + attrs => { + type => 'submit', + }, + icon_after_text => 1, + } ) %]
    [% END; END; diff --git a/root/templates/browse/recipe/show.tt b/root/templates/browse/recipe/show.tt index 306bc0bd0..95f2f1cc6 100644 --- a/root/templates/browse/recipe/show.tt +++ b/root/templates/browse/recipe/show.tt @@ -3,7 +3,12 @@ js_push_template_path(); css.push('/css/print.css') %] - +[% tooltip_button( { + icon => 'print', + tooltip => { content => 'Print' }, + attrs => { onclick => 'print()' }, + container_classes => 'd-inline-block my-3 no-print', +} ) %]
    @@ -17,7 +22,17 @@ css.push('/css/print.css') %]

    [% IF import_url %]
    - + [% tooltip_button( { + icon => 'east', + text => 'Import', + tooltip => { + content => 'Import this recipe into one of your projects', + }, + attrs => { + type => 'submit', + }, + icon_after_text => 1, + } ) %]
    [% END %]
    @@ -31,7 +46,13 @@ css.push('/css/print.css') %]
    - + [% button( { + text => 'Show', + icon => 'calculate', + attrs => { + type => 'submit', + }, + } ) %]
    -- GitLab From 395a85bdf96fd15b049e0d7b82c302df3e1edb02 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Thu, 24 Oct 2024 14:41:50 +0200 Subject: [PATCH 10/33] Use button macro on dish page --- root/templates/dish/edit.tt | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/root/templates/dish/edit.tt b/root/templates/dish/edit.tt index d0872d66a..58ebfdcd5 100644 --- a/root/templates/dish/edit.tt +++ b/root/templates/dish/edit.tt @@ -18,9 +18,14 @@ js.push('/js/tagAutocomplete.js');
    - + [% button( { + variant => 'danger', + text => 'Delete', + icon => 'delete_forever', + attrs => { + type => 'submit', + }, + } ) %]
    @@ -86,7 +91,12 @@ js.push('/js/tagAutocomplete.js');
    - + [% button( { + text => 'Update dish', + attrs => { + type => 'submit', + }, + } ) %]
    @@ -111,7 +121,12 @@ class="col-sm-12 py-3">
    - + [% button( { + text => 'Recalculate values', + attrs => { + type => 'submit', + }, + } ) %]
    -- GitLab From 101e2911e1393704a7a3fef83ba0c7b16f224464 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Thu, 24 Oct 2024 15:20:29 +0200 Subject: [PATCH 11/33] Use button macro organization pages --- root/templates/organization/members.tt | 34 +++++++++++++++++++--- root/templates/organization/show.tt | 37 ++++++++++++++++-------- root/templates/settings/organizations.tt | 7 ++++- t/organization_lifecycle.t | 2 +- 4 files changed, 62 insertions(+), 18 deletions(-) diff --git a/root/templates/organization/members.tt b/root/templates/organization/members.tt index 3348f5afd..a39db3db9 100644 --- a/root/templates/organization/members.tt +++ b/root/templates/organization/members.tt @@ -31,7 +31,8 @@ has_admin = 0 %] [% END %] - + [%# We don't use the button macro here. Because when we would use the macro the styling would break %] + [% ELSE %] [% o_u.role %] @@ -42,12 +43,32 @@ has_admin = 0 %] [% IF o_u.transfer_ownership_url; has_admin = 1 %]
    - + [% tooltip_button( { + variant => 'outline-secondary', + icon => 'sync_alt', + tooltip => { + content => 'Transfer ownership', + }, + attrs => { + type => 'submit', + }, + button_classes => 'btn-sm', + } ) %]
    [% END %] [% IF o_u.remove_url %]
    - + [% tooltip_button( { + variant => 'outline-danger', + icon => 'delete_forever', + tooltip => { + content => 'Remove', + }, + attrs => { + type => 'submit', + }, + button_classes => 'btn-sm', + } ) %]
    [% END %] @@ -93,7 +114,12 @@ has_admin = 0 %]
    - + [% button( { + text => 'Add', + attrs => { + type => 'submit', + }, + } ) %]
    [% ELSE %] diff --git a/root/templates/organization/show.tt b/root/templates/organization/show.tt index e8eb8cd49..0718bce4f 100644 --- a/root/templates/organization/show.tt +++ b/root/templates/organization/show.tt @@ -12,7 +12,12 @@ - + [% button( { + text => 'Change display name', + attrs => { + type => 'submit', + }, + } ) %] [% ELSE %]
    @@ -35,7 +40,12 @@
    - + [% button( { + text => 'Update description', + attrs => { + type => 'submit', + }, + } ) %] [% ELSE %] [% USE Markdown; organization.description_md | markdown %] @@ -56,15 +66,12 @@ BLOCK project_item ~%]

    Members

    - [% IF members_url %] - - manage_accounts Manage memberships - - [% ELSE %] - - manage_accounts Manage memberships - - [% END %] + [% button( { + text => 'Manage memberships', + icon => 'manage_accounts', + href => members_url, + disabled => members_url, + } ) %]
    [% list(organizations_users, 'user_item', {list_classes => 'list-group-flush rounded'}) %] @@ -98,7 +105,13 @@ BLOCK project_item ~%]

    Deleting an organization also removes all memberships and the organization’s permissions on projects.

    - + [% button( { + variant => 'danger', + text => 'Delete organization', + attrs => { + type => 'submit', + }, + } ) %]
    diff --git a/root/templates/settings/organizations.tt b/root/templates/settings/organizations.tt index 8c5759c4d..dd9a0711d 100644 --- a/root/templates/settings/organizations.tt +++ b/root/templates/settings/organizations.tt @@ -14,7 +14,12 @@ BLOCK new_org %]
    - + [% button( { + text => 'Create organization', + attrs => { + name => 'create' + } + } ) %]
    diff --git a/t/organization_lifecycle.t b/t/organization_lifecycle.t index 12d6d564e..79e723f1e 100644 --- a/t/organization_lifecycle.t +++ b/t/organization_lifecycle.t @@ -36,7 +36,7 @@ $t->text_lacks( my $display_name = 'Test Orga' ); $t->submit_form_ok( { with_fields => { display_name => $display_name } } ); $t->text_contains("Organization $display_name"); -$t->follow_link_ok( { text => 'manage_accounts Manage memberships' } ); +$t->follow_link_ok( { text => 'manage_accountsManage memberships' } ); $t->submit_form_ok( { form_id => 'add-member', with_fields => { name => 'other', role => 'member' } } ); -- GitLab From 0882609537dae697c87023449317865f160ed3d4 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Thu, 14 Nov 2024 11:08:29 +0100 Subject: [PATCH 12/33] Add button macro to print day page --- root/templates/print/day.tt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/root/templates/print/day.tt b/root/templates/print/day.tt index 11da82873..d31cb15d6 100644 --- a/root/templates/print/day.tt +++ b/root/templates/print/day.tt @@ -1,6 +1,12 @@ [% css.push('/css/print.css') %] - +
    + [% tooltip_button( { + icon => 'print', + tooltip => { content => 'Print' }, + attrs => { onclick => 'print()' }, + } ) %] +
    [% FOR meal IN meals %]

    [% meal.name | html %]

    -- GitLab From e3344874b5bc3f8eb2d819a15f2b320bfbcd9305 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Sat, 1 Mar 2025 07:50:02 +0100 Subject: [PATCH 13/33] Use button macro on project pages --- lib/Coocook/Controller/Permission.pm | 10 ++- root/static/css/style.css | 9 +- root/static/js/project/show.js | 29 ++++-- root/templates/project/import.tt | 10 ++- root/templates/project/permissions.tt | 121 ++++++++++++++++---------- root/templates/project/show.tt | 22 ++++- 6 files changed, 138 insertions(+), 63 deletions(-) diff --git a/lib/Coocook/Controller/Permission.pm b/lib/Coocook/Controller/Permission.pm index e8b6b5a91..c47233cbc 100644 --- a/lib/Coocook/Controller/Permission.pm +++ b/lib/Coocook/Controller/Permission.pm @@ -39,6 +39,9 @@ sub index : GET HEAD Chained('/project/submenu') PathPart('permissions') Args(0) my $projects_users = $c->project->projects_users->prefetch('user'); while ( my $project_user = $projects_users->next ) { + my $can_transfer_onership = + $c->has_capability( 'transfer_project_ownership', { permission => $project_user } ); + push @permissions, { role => $project_user->role, sort_key => $project_user->user->name_fc, @@ -51,8 +54,7 @@ sub index : GET HEAD Chained('/project/submenu') PathPart('permissions') Args(0) ? $c->project_uri( $self->action_for('edit'), $project_user->user->name ) : undef, - make_owner_url => - $c->has_capability( 'transfer_project_ownership', { permission => $project_user } ) + make_owner_url => $can_transfer_onership ? $c->project_uri( $self->action_for('make_owner'), $project_user->user->name ) : undef, @@ -60,6 +62,10 @@ sub index : GET HEAD Chained('/project/submenu') PathPart('permissions') Args(0) ? $c->project_uri( $self->action_for('revoke'), $project_user->user->name ) : undef, }; + + if ($can_transfer_onership) { + $c->stash( can_transfer_ownership => 1 ); + } } } diff --git a/root/static/css/style.css b/root/static/css/style.css index 48de23d01..e95fba649 100644 --- a/root/static/css/style.css +++ b/root/static/css/style.css @@ -367,13 +367,20 @@ cc-autocomplete { .extra-col button { font-weight: bold; - padding: 1px; +} + +.extra-col > * { + height: 34px; } .print { display: none; } +.role-form select { + width: unset; +} + /* Show/hide action buttons on hover/no hover */ .parent:hover .action-btn, .purchase-list-table:hover .pl-form { diff --git a/root/static/js/project/show.js b/root/static/js/project/show.js index 0995be311..5cfebd4a8 100644 --- a/root/static/js/project/show.js +++ b/root/static/js/project/show.js @@ -18,23 +18,34 @@ const openEdit = (e) => { const addCol = (e) => { e.preventDefault(); - let name = document.getElementById("col-name").value; - let head = table.querySelector("th:last-child"); - let btn = document.createElement("button"); + const name = document.getElementById("col-name").value; + const head = table.querySelector("th:last-child"); + + const btn = document.createElement("button"); btn.innerText = name; - btn.className = "btn btn-outline-dark"; - btn.title = "Edit column"; + btn.className = + "btn btn-outline-dark btn-sm d-inline-flex justify-content-center align-items-center"; btn.onclick = openEdit; - let th = document.createElement("th"); + + const btnContainer = document.createElement("span"); + btnContainer.className = "d-grid"; + btnContainer.append(btn); + + const th = document.createElement("th"); th.className = "extra-col"; - th.append(btn); + th.append(btnContainer); + head.parentElement.insertBefore(th, head); addModal.hide(); - for (let row of table.querySelectorAll("tbody tr")) { - let tmp = document.createElement("td"); + for (const row of table.querySelectorAll("tbody tr")) { + const tmp = document.createElement("td"); tmp.innerHTML = " "; row.append(tmp); } + + bootstrap.Tooltip.getOrCreateInstance(btnContainer, { + title: "Edit column", + }); }; const saveCol = (e) => { diff --git a/root/templates/project/import.tt b/root/templates/project/import.tt index 1dcad3a8d..152ef155d 100644 --- a/root/templates/project/import.tt +++ b/root/templates/project/import.tt @@ -41,8 +41,14 @@ js_push_template_path() %] [% END %] - - +
    + [% button( { + text => 'Import', + attrs => { + type => 'submit', + }, + } ) %] +
    [% END %] diff --git a/root/templates/project/permissions.tt b/root/templates/project/permissions.tt index 7798a281d..ae5d4ab3c 100644 --- a/root/templates/project/permissions.tt +++ b/root/templates/project/permissions.tt @@ -10,13 +10,12 @@ has_admin = 0 %] User/Organization Role - - + [% FOR permission IN permissions %] - + [% IF permission.organization; link_organization(permission.organization); @@ -27,64 +26,91 @@ has_admin = 0 %] END %] - [% IF permission.edit_url %] -
    -
    -
    + [% IF permission.edit_url %] + +
    -
    -
    - -
    + [% tooltip_button( { + icon => 'check', + tooltip => { content => 'Save' }, + attrs => { + 'type' => 'submit', + }, + container_classes => 'action-btn', + } ) %]
    - [% ELSE %] + [% ELSE %] [% permission.role %] - [% END %] - - - [% IF permission.make_owner_url %] -
    -
    - -
    -
    - [% END %] + [% END %] - [% IF permission.revoke_url %] -
    -
    - -
    -
    - [% END %] +
    + [% IF permission.make_owner_url %] +
    + [% tooltip_button( { + variant => 'outline-primary', + icon => 'transfer_within_a_station', + tooltip => { content => 'Transfer ownership' }, + attrs => { + 'type' => 'submit', + 'onsubmit' => 'return confirm("Do you really want to transfer the project ownership? You cannot undo this action.");', + }, + button_classes => 'btn-sm', + } ) %] +
    + [% END; + IF permission.revoke_url %] +
    + [% tooltip_button( { + variant => 'outline-danger', + icon => 'delete_forever', + tooltip => { content => 'Revoke permission' }, + attrs => { + 'type' => 'submit', + }, + button_classes => 'btn-sm', + } ) %] +
    + [% END %] +
    [% END %] - + public anyone [% project.is_public ? "viewer (project is public)" : "— (project is private)" %] - -
    - [% IF project.is_public %] - - [% ELSE %] + + [% IF project.is_public %] + [% tooltip_button( { + variant => 'outline-warning', + icon => 'lock', + tooltip => { content => 'Make project private' }, + attrs => { + 'type' => 'submit', + }, + button_classes => 'btn-sm', + } ) %] + [% ELSE %] - - [% END %] + [% tooltip_button( { + variant => 'outline-warning', + icon => 'public', + tooltip => { content => 'Make project public' }, + attrs => { + 'type' => 'submit', + }, + button_classes => 'btn-sm', + } ) %] + [% END %]
    + @@ -125,7 +151,12 @@ has_admin = 0 %]
    - + [% button( { + text => 'Add', + attrs => { + type => 'submit', + }, + } ) %]
    [% ELSE %] diff --git a/root/templates/project/show.tt b/root/templates/project/show.tt index 7be863739..bdf24102d 100644 --- a/root/templates/project/show.tt +++ b/root/templates/project/show.tt @@ -57,7 +57,13 @@ IF project.archived;

    [% project.name | html %]

    - +
    + [% tooltip_button( { + icon => 'print', + tooltip => { content => 'Print' }, + attrs => { onclick => 'print()' }, + } ) %] +
    [% USE Markdown; @@ -71,9 +77,17 @@ IF project.archived; Meal Dishes -
    - -
    + [% tooltip_button( { + variant => 'outline-primary', + icon => 'add', + tooltip => { content => 'Add extra column' }, + attrs => { + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#addCol', + }, + button_classes => 'btn-sm btn-no-quad', + container_classes => 'd-grid', + } ) %] -- GitLab From 7c57930c289ec38743862432ad5d2cb7342d9704 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Sun, 2 Mar 2025 10:19:20 +0100 Subject: [PATCH 14/33] Fix formatting --- root/static/js/recipe/index.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/root/static/js/recipe/index.js b/root/static/js/recipe/index.js index 663524c41..fa186decd 100644 --- a/root/static/js/recipe/index.js +++ b/root/static/js/recipe/index.js @@ -8,5 +8,13 @@ const setDuplicateFormValues = (actionURL, name) => { recipeNameBodyElement.textContent = name; } -document.getElementById("duplicate-recipe-modal").addEventListener("shown.bs.modal", () => document.getElementById("new-name").focus()); -document.getElementById("create-recipe-modal").addEventListener("shown.bs.modal", () => document.getElementById("create-name").focus()); +document + .getElementById("duplicate-recipe-modal") + .addEventListener("shown.bs.modal", () => + document.getElementById("new-name").focus() + ); +document + .getElementById("create-recipe-modal") + .addEventListener("shown.bs.modal", () => + document.getElementById("create-name").focus() + ); -- GitLab From a0837ee557b60496f46bf586aaf7f66c5fe7520c Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Sun, 2 Mar 2025 10:20:02 +0100 Subject: [PATCH 15/33] Use button macro on recipe pages --- root/static/js/recipe/index.js | 13 +++-- root/templates/recipe/edit.tt | 59 ++++++++++++++++----- root/templates/recipe/importable_recipes.tt | 14 +++-- root/templates/recipe/index.tt | 54 ++++++++++++++----- 4 files changed, 109 insertions(+), 31 deletions(-) diff --git a/root/static/js/recipe/index.js b/root/static/js/recipe/index.js index fa186decd..8d98d25c7 100644 --- a/root/static/js/recipe/index.js +++ b/root/static/js/recipe/index.js @@ -2,11 +2,18 @@ const formElement = document.getElementById("duplicate-form"); const recipeNameTitleElement = document.getElementById("recipe-name-title"); const recipeNameBodyElement = document.getElementById("recipe-name-body"); -const setDuplicateFormValues = (actionURL, name) => { - formElement.setAttribute("action", actionURL); +const setDuplicateFormValues = (e) => { + const url = e.currentTarget.dataset.url; + const name = e.currentTarget.dataset.name; + + formElement.setAttribute("action", url); recipeNameTitleElement.textContent = name; recipeNameBodyElement.textContent = name; -} +}; + +document + .querySelectorAll(".duplicate-trigger") + .forEach((elem) => elem.addEventListener("click", setDuplicateFormValues)); document .getElementById("duplicate-recipe-modal") diff --git a/root/templates/recipe/edit.tt b/root/templates/recipe/edit.tt index db31b1401..f696f79dd 100644 --- a/root/templates/recipe/edit.tt +++ b/root/templates/recipe/edit.tt @@ -10,22 +10,50 @@ IF import_url %]
    - + [% + button_label = 'Used in ' _ dishes.size _ ' dish' _ (dishes.size != 1 ? 'es' : ''); + button( { + text => button_label, + attrs => { + 'data-bs-toggle' => 'collapse', + 'data-bs-target' => '#usage', + 'aria-expanded' => 'false', + 'aria-controls' => 'usage', + }, + button_classes => 'dropdown-toggle align-items-center', + disabled => dishes.size == 0, + } ) + %]
    [% IF public_url %] - - - - [% END; - IF import_url %]
    - + [% button( { + text => 'Share link', + href => public_url, + variant => 'secondary', + attrs => { + 'data-bs-toggle' => 'collapse', + 'data-bs-target' => '#usage', + 'aria-expanded' => 'false', + 'aria-controls' => 'usage', + }, + } ) %]
    - [% END %] + [% END; + IF import_url; + tooltip_button( { + variant => 'secondary', + text => 'Import into another project', + icon => 'east', + icon_after_text => 1, + tooltip => { content => 'Import this recipe into another one of your projects' }, + attrs => { + type =>'submit', + form => 'import', + }, + container_classes => 'btn-group col-xxl-3 col-md-4 col-sm-6', + } ); + END %]
    [% IF dishes.size > 0 %] @@ -86,7 +114,12 @@ IF import_url %]
    - + [% button( { + text => 'Update recipe', + attrs => { + type => 'submit', + }, + } ) %]
    diff --git a/root/templates/recipe/importable_recipes.tt b/root/templates/recipe/importable_recipes.tt index 27aa33085..1033f0667 100644 --- a/root/templates/recipe/importable_recipes.tt +++ b/root/templates/recipe/importable_recipes.tt @@ -13,9 +13,17 @@ [% IF recipe.import_url %]
    - + [% + project_name = project.name | html; + tooltip_button( { + text => 'Import', + icon => 'east', + tooltip => { content => 'Import this recipe into project "' _ project_name _ '"' }, + attrs => { + type => 'submit', + }, + } ) + %]
    [% END %] diff --git a/root/templates/recipe/index.tt b/root/templates/recipe/index.tt index dbcb46de0..34662f37c 100644 --- a/root/templates/recipe/index.tt +++ b/root/templates/recipe/index.tt @@ -4,12 +4,25 @@ js_push_template_path(); new = BLOCK %]

    - - - east Import recipe - +[% + tooltip_button( { + icon => 'add', + text => 'New recipe', + tooltip => { content => 'Add new recipe' }, + attrs => { + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#create-recipe-modal', + }, + } ); + + tooltip_button( { + icon => 'east', + text => 'Import recipe', + variant => 'secondary', + href => import_recipe_url, + tooltip => { content => 'Import recipe' }, + } ); +%]

    [% END; new %] @@ -34,13 +47,30 @@ new = BLOCK %]
    - + [% + recipe_name = recipe.name | html; + tooltip_button( { + icon => 'file_copy', + variant => 'outline-dark', + tooltip => { content => 'Duplicate "' _ recipe_name _ '"' }, + attrs => { + 'data-name' => recipe_name, + 'data-url' => recipe.duplicate_url, + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#duplicate-recipe-modal', + }, + button_classes => 'btn-sm duplicate-trigger', + } ) + %]
    - + [% button( { + icon => 'delete_forever', + variant => 'outline-danger', + attrs => { + type => 'submit', + }, + button_classes => 'btn-sm', + } ) %]
    -- GitLab From 862554472f2cef1f4cff67dfdedf751bdd2f564f Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Sun, 2 Mar 2025 10:23:03 +0100 Subject: [PATCH 16/33] Use button macro on sign in page --- root/templates/session/login.tt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/root/templates/session/login.tt b/root/templates/session/login.tt index 7a607ec48..467997ec5 100644 --- a/root/templates/session/login.tt +++ b/root/templates/session/login.tt @@ -20,7 +20,9 @@ - + [% button( { + text => 'Sign in', + } ) %] -- GitLab From d7ef4b8bcd0f11778ccb42225cd8abc65839c386 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Sun, 2 Mar 2025 11:06:12 +0100 Subject: [PATCH 17/33] Use button macro on setting pages --- root/templates/settings/account.tt | 47 ++++++++++++++---------- root/templates/settings/organizations.tt | 34 +++++++++++------ 2 files changed, 51 insertions(+), 30 deletions(-) diff --git a/root/templates/settings/account.tt b/root/templates/settings/account.tt index 4d00b4a35..c1b2fdc9a 100644 --- a/root/templates/settings/account.tt +++ b/root/templates/settings/account.tt @@ -5,19 +5,21 @@ js.push('/lib/zxcvbn.js'); %] [% IF confirm_email_change_url %] -
    -
    -
    - You can confirm to change your email address to - [% user.new_email_fc | html %] - -
    -
    -
    +
    + You can confirm to change your email address to + [% user.new_email_fc | html %] + [% button( { + text => 'Confirm email change', + } ) %] +
    [% END %]

    - View your profile + [% button( { + text => 'View your profile', + variant => 'outline-primary', + href => profile_url, + } ) %]

    @@ -41,7 +43,9 @@ js.push('/lib/zxcvbn.js');
    - + [% button( { + text => 'Change display name', + } ) %] @@ -67,7 +71,10 @@ js.push('/lib/zxcvbn.js'); - + [% button( { + text => 'Change password', + button_classes => 'mb-2', + } ) %]
    If you don’t know your current password anymore, you can @@ -90,12 +97,12 @@ js.push('/lib/zxcvbn.js');
    [% IF user.new_email_fc %] -
    -
    - You’ve requested change to [% user.new_email_fc | html %] - at [% display_datetime(user.token_created, {short=>1}) %] UTC - -
    + + You’ve requested change to [% user.new_email_fc | html %] + at [% display_datetime(user.token_created, {short=>1}) %] UTC + [% button( { + text => 'Cancel', + } ) %]
    [% END %] @@ -105,7 +112,9 @@ js.push('/lib/zxcvbn.js');
    - + [% button( { + text => 'Change email address', + } ) %]
    diff --git a/root/templates/settings/organizations.tt b/root/templates/settings/organizations.tt index dd9a0711d..6dba001ba 100644 --- a/root/templates/settings/organizations.tt +++ b/root/templates/settings/organizations.tt @@ -17,8 +17,8 @@ BLOCK new_org %] [% button( { text => 'Create organization', attrs => { - name => 'create' - } + name => 'create', + }, } ) %] @@ -33,21 +33,33 @@ BLOCK user_item %] [% link_organization(item.organization) %] ([% item.role %]) [% IF item.leave_url %]
    - + [% button( { + text => 'Leave organization', + icon => 'logout', + variant => 'outline-danger', + } ) %]
    -[% ELSE %] - -[% END; +[% ELSE; + tooltip_button( { + text => 'Leave organization', + icon => 'logout', + variant => 'outline-danger', + tooltip => { + content => 'You are the owner of this organization', + }, + disabled => 1, + button_classes => 'action-btn', + } ); +END; END; IF organizations_users.size > 0 %]

    Before you can leave an organization you need to transfer organization ownership to another organization member.

    - [% list(organizations_users, 'user_item', {item_classes => 'd-flex gap-3 justify-content-between align-items-center flex-wrap parent'}) %] + [% list(organizations_users, 'user_item', { + list_classes => 'mb-3', + item_classes => 'd-flex gap-3 justify-content-between align-items-center flex-wrap parent', + }) %] [% ELSE %]

    You are not member of any organization yet.

    [% END; -- GitLab From aef467221d1fd9bef9208b2bfba269dab3570f91 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Sun, 2 Mar 2025 11:24:50 +0100 Subject: [PATCH 18/33] Use button macro on shop section page --- root/templates/shop_section/index.tt | 55 ++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/root/templates/shop_section/index.tt b/root/templates/shop_section/index.tt index e879e9a3b..284956459 100644 --- a/root/templates/shop_section/index.tt +++ b/root/templates/shop_section/index.tt @@ -5,9 +5,19 @@ js_push_template_path(); BLOCK new_section %] -
    - -
    + [% tooltip_button( { + variant => 'outline-primary', + icon => 'add', + tooltip => { + content => 'Create shop section', + }, + attrs => { + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#add-section', + }, + container_classes => 'd-grid', + button_classes => 'btn-no-quad', + } ) %] [% END %] @@ -28,20 +38,33 @@ BLOCK new_section %] [% section.articles_count %]
    - - [% IF section.delete_url %] -
    - + [% + section_name = section.name | html; + tooltip_button( { + variant => 'outline-dark', + icon => 'edit', + tooltip => { content => 'Rename shop section', line_through => 1 }, + attrs => { + 'data-url' => section.update_url, + 'data-name' => section_name, + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#update-section', + }, + button_classes => 'btn-sm update', + } ) + %] + + [% tooltip_button( { + variant => 'outline-danger', + icon => 'delete_forever', + disabled => !section.delete_url, + tooltip => { + content => 'Delete shop section', + disabled_content => numerus(section.articles_count, 'article', 'articles') _ ' are assigned to this shop section', + }, + button_classes => 'btn-sm', + } ) %]
    - [% ELSE %] - - [% END %]
    -- GitLab From 694588510f6636d4bc796fe11d60572f6a94e0e7 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Sun, 2 Mar 2025 12:09:14 +0100 Subject: [PATCH 19/33] Use button macro on tag pages --- root/templates/tag/edit.tt | 30 ++++---- root/templates/tag/index.tt | 133 ++++++++++++++++++++---------------- 2 files changed, 91 insertions(+), 72 deletions(-) diff --git a/root/templates/tag/edit.tt b/root/templates/tag/edit.tt index d500619e4..23310e3d1 100644 --- a/root/templates/tag/edit.tt +++ b/root/templates/tag/edit.tt @@ -10,20 +10,26 @@ js_push_template_path() %]

    - - [% IF is_in_use %] - - [% ELSE %] + [% button( { + text => 'Edit', + icon => 'edit', + attrs => { + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#edit-tag', + }, + button_classes => 'update', + } ) %]
    - + [% button( { + text => 'Delete', + icon => 'delete', + disabled => is_in_use, + variant => 'danger', + attrs => { + id => 'delete-btn', + }, + } ) %]
    - [% END %]
    diff --git a/root/templates/tag/index.tt b/root/templates/tag/index.tt index 23964924d..02c73c14a 100644 --- a/root/templates/tag/index.tt +++ b/root/templates/tag/index.tt @@ -1,63 +1,83 @@ [% title = "Tags"; -js_push_template_path() %] +js_push_template_path(); -

    - - -

    +BLOCK new_element %] + +
    +[% + button( { + text => 'New Tag', + icon => 'add', + attrs => { + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#add-tag', + }, + } ); + button( { + text => 'New Tag group', + icon => 'add', + attrs => { + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#add-tg', + }, + } ); +%] +
    +[% END; + +PROCESS new_element; -[% FOR group IN groups %] +FOR group IN groups %]

    Tag Group [% group.name | html %]

    - - - [% IF group.tags.size %] -
    - -
    - [% ELSE %] + [% + group_name = group.name | html; + group_comment = group.comment | html; + tooltip_button( { + variant => 'outline-dark', + icon => 'add', + tooltip => { + content => 'Add tag to tag group '_ group_name, + }, + attrs => { + 'data-id' => group.id, + 'data-name' => group_name, + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#add-to-group', + }, + button_classes => 'btn-sm atg', + } ); + tooltip_button( { + variant => 'outline-dark', + icon => 'edit', + tooltip => { + content => 'Edit tag group '_ group_name, + }, + attrs => { + 'data-url' => group.update_url, + 'data-name' => group_name, + 'data-comment' => group_comment, + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#tg-edit', + }, + button_classes => 'btn-sm edit-tg', + } ) + %]
    - + [% tooltip_button( { + variant => 'outline-danger', + icon => 'delete', + disabled => group.tags.size, + tooltip => { + content => 'Delete tag group '_ group_name, + disabled_content => "Can't delete tag group which contains tags", + }, + button_classes => 'btn-sm', + } ) %]
    - [% END %]
    @@ -83,18 +103,11 @@ js_push_template_path() %] [% END %]
    -[% END %] +[% END; -

    - - -

    +PROCESS new_element; -[% WRAPPER includes/infobox.tt %] +WRAPPER includes/infobox.tt %] Tags are keywords that can be attached to recipes, dishes and articles. They can be used to filter them during search or to highlight important aspects.

    -- GitLab From 8468724f83faf64ea339bf3a2bd4a414d1a073d2 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Sun, 2 Mar 2025 13:04:33 +0100 Subject: [PATCH 20/33] Use button macro on unit pages --- root/templates/unit/edit.tt | 31 +++++++++++++++++++++++-------- root/templates/unit/index.tt | 30 ++++++++++++++++++------------ 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/root/templates/unit/edit.tt b/root/templates/unit/edit.tt index 583985fca..2c2141bcf 100644 --- a/root/templates/unit/edit.tt +++ b/root/templates/unit/edit.tt @@ -14,7 +14,9 @@ END %]
    - + [% button( { + text => 'Update', + } ) %]

    Conversions

    @@ -40,7 +42,10 @@ END %]
    - + [% button( { + text => 'Add', + button_classes => 'btn-sm', + } ) %]
    @@ -53,7 +58,7 @@ END %]
    1 [% unit.long_name | html %] is equal to
    [% IF conversion.update_url %] -
    +
    [% ELSE; @@ -62,13 +67,23 @@ END %] [% conversion.long_name | html %]
    - [% IF conversion.update_url %] - - [% END %] + [% IF conversion.update_url; + button( { + text => 'Update', + button_classes => 'btn-sm', + attrs => { + form => 'update-conversion-to-unit-' _ conversion.id, + }, + } ); + END; - [% IF conversion.delete_url %] + IF conversion.delete_url %]
    - + [% button( { + text => 'Delete', + variant => 'danger', + button_classes => 'btn-sm', + } ) %]
    [% END %] [% ELSE %] diff --git a/root/templates/unit/index.tt b/root/templates/unit/index.tt index ad0a6ff45..e99acf51e 100644 --- a/root/templates/unit/index.tt +++ b/root/templates/unit/index.tt @@ -4,9 +4,14 @@ js_push_template_path(); new = BLOCK %]

    - + [% button( { + text => 'New unit', + icon => 'add', + attrs => { + 'data-bs-toggle' => 'modal', + 'data-bs-target' => '#create-unit-modal', + }, + } ) %]

    [% END; new %] @@ -47,17 +52,18 @@ new = BLOCK %] [% END; IF loop.first %] - [% IF unit.delete_url %]
    - + [% tooltip_button( { + icon => 'delete', + variant => 'outline-danger', + disabled => !unit.delete_url, + tooltip => { + content => 'Delete unit', + disabled_content => 'This is used for dish/recipe ingredients or purchase list items', + }, + button_classes => 'btn-sm', + } ) %]
    - [% ELSE %] - - [% END %] [% END %] -- GitLab From e85209167f564e4c6dd58c1cc6fe7d1e54dbc6ca Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Sun, 2 Mar 2025 13:24:04 +0100 Subject: [PATCH 21/33] Use button macro on user pages --- root/templates/user/recover.tt | 5 ++++- root/templates/user/register.tt | 5 ++++- root/templates/user/reset_password.tt | 5 ++++- root/templates/user/show.tt | 22 ++++++++++++++++------ 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/root/templates/user/recover.tt b/root/templates/user/recover.tt index 69e808dd6..d156d2437 100644 --- a/root/templates/user/recover.tt +++ b/root/templates/user/recover.tt @@ -14,7 +14,10 @@
    - + + [% button( { + text => 'Send recovery link', + } ) %] diff --git a/root/templates/user/register.tt b/root/templates/user/register.tt index 4b133ed08..c989dff98 100644 --- a/root/templates/user/register.tt +++ b/root/templates/user/register.tt @@ -51,7 +51,10 @@ IF terms %] By clicking [% label %] you accept our terms as of [% display_date(terms.valid_from, {html=>1}) %]. [% END %] You will receive an email with a web link to verify your email address. - + + [% button( { + text => label, + } ) %] diff --git a/root/templates/user/reset_password.tt b/root/templates/user/reset_password.tt index bdab62d3a..43adcc6e5 100644 --- a/root/templates/user/reset_password.tt +++ b/root/templates/user/reset_password.tt @@ -23,7 +23,10 @@ js.push('/lib/zxcvbn.js');
    - + + [% button( { + text => 'Reset password', + } ) %] diff --git a/root/templates/user/show.tt b/root/templates/user/show.tt index bab048182..0969b2970 100644 --- a/root/templates/user/show.tt +++ b/root/templates/user/show.tt @@ -1,13 +1,23 @@ [% escape_title( 'User', user_object.display_name ) %]

    - [% IF my_settings_url %] - edit Edit your profile - [% END %] + [% IF my_settings_url; + button( { + text => 'Edit your profile', + icon => 'edit', + variant => 'outline-primary', + href => my_settings_url, + } ); + END %] - [% IF profile_admin_url %] - supervisor_account View profile as admin - [% END %] + [% IF profile_admin_url; + button( { + text => 'View profile as admin', + icon => 'supervisor_account', + variant => 'outline-secondary', + href => profile_admin_url, + } ); + END %]

    Registered: [% display_date(user_object.created) %]

    -- GitLab From 108247bc30eda08f68235adc7bc1c07494c6e335 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Sun, 2 Mar 2025 13:25:54 +0100 Subject: [PATCH 22/33] Use button macro on dashboard page --- root/templates/dashboard.tt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/root/templates/dashboard.tt b/root/templates/dashboard.tt index c95272a86..e0aa0b256 100644 --- a/root/templates/dashboard.tt +++ b/root/templates/dashboard.tt @@ -16,7 +16,9 @@
    - + [% button( { + text => 'Create project', + } ) %]
    -- GitLab From 3b290d84c084accee9e190874a2f7b4bfaffb50c Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Mon, 24 Mar 2025 17:11:30 +0100 Subject: [PATCH 23/33] Rename btn-quad to btn-square and btn-no-quad to btn-no-square --- root/common_templates/macros.tt | 2 +- root/static/css/style.css | 8 ++++---- root/templates/faq/index.tt | 10 ++++++---- root/templates/organization/members.tt | 2 +- root/templates/project/show.tt | 2 +- root/templates/purchase_list/index.tt | 2 +- root/templates/shop_section/index.tt | 2 +- root/templates/terms/show.tt | 8 ++++---- 8 files changed, 19 insertions(+), 17 deletions(-) diff --git a/root/common_templates/macros.tt b/root/common_templates/macros.tt index bf5ba0a67..741b419f0 100644 --- a/root/common_templates/macros.tt +++ b/root/common_templates/macros.tt @@ -132,7 +132,7 @@ MACRO button(opts) BLOCK; icon = '' _ opts.icon _ ''; END ~%] <[% opts.href ? 'a' : 'button' %] - class="btn btn-[% opts.variant || 'primary' %] [% 'btn-quad' IF !opts.text %] d-inline-flex justify-content-center gap-1 [% opts.button_classes.list.join(' ') %]" + class="btn btn-[% opts.variant || 'primary' %] [% 'btn-square' IF !opts.text %] d-inline-flex justify-content-center gap-1 [% opts.button_classes.list.join(' ') %]" [%~ ' disabled ' IF opts.disabled; FOR attr IN opts.attrs; attr.key %]="[% attr.value | html %]" diff --git a/root/static/css/style.css b/root/static/css/style.css index e95fba649..9d3576ab9 100644 --- a/root/static/css/style.css +++ b/root/static/css/style.css @@ -17,19 +17,19 @@ textarea { resize: vertical; } -.btn-quad { +.btn-square { aspect-ratio: 1; } -.btn-no-quad { +.btn-no-square { aspect-ratio: unset; } -.btn.btn-quad { +.btn.btn-square { padding: 0.375em; } -.btn.btn-sm.btn-quad { +.btn.btn-sm.btn-square { padding: 0.25rem; } diff --git a/root/templates/faq/index.tt b/root/templates/faq/index.tt index 0766303ec..96167fb89 100644 --- a/root/templates/faq/index.tt +++ b/root/templates/faq/index.tt @@ -2,10 +2,12 @@ html_title = 'Frequently Asked Questions' %] [% IF admin_faq_url %] -
    -
    - Edit FAQ -
    +
    + [% button( { + text => 'Edit FAQ', + variant => 'outline-primary', + href => admin_faq_url, + } ) %]
    [% END; diff --git a/root/templates/organization/members.tt b/root/templates/organization/members.tt index a39db3db9..9968c5db1 100644 --- a/root/templates/organization/members.tt +++ b/root/templates/organization/members.tt @@ -32,7 +32,7 @@ has_admin = 0 %] [% END %] [%# We don't use the button macro here. Because when we would use the macro the styling would break %] - + [% ELSE %] [% o_u.role %] diff --git a/root/templates/project/show.tt b/root/templates/project/show.tt index bdf24102d..23619e0b5 100644 --- a/root/templates/project/show.tt +++ b/root/templates/project/show.tt @@ -85,7 +85,7 @@ IF project.archived; 'data-bs-toggle' => 'modal', 'data-bs-target' => '#addCol', }, - button_classes => 'btn-sm btn-no-quad', + button_classes => 'btn-sm btn-no-square', container_classes => 'd-grid', } ) %] diff --git a/root/templates/purchase_list/index.tt b/root/templates/purchase_list/index.tt index 2a5f72375..d77603849 100644 --- a/root/templates/purchase_list/index.tt +++ b/root/templates/purchase_list/index.tt @@ -18,7 +18,7 @@ BLOCK new_list %] 'data-bs-target' => '#addList', }, container_classes => 'd-grid', - button_classes => 'btn-no-quad', + button_classes => 'btn-no-square', } ) %] diff --git a/root/templates/shop_section/index.tt b/root/templates/shop_section/index.tt index 284956459..0917613bd 100644 --- a/root/templates/shop_section/index.tt +++ b/root/templates/shop_section/index.tt @@ -16,7 +16,7 @@ BLOCK new_section %] 'data-bs-target' => '#add-section', }, container_classes => 'd-grid', - button_classes => 'btn-no-quad', + button_classes => 'btn-no-square', } ) %] diff --git a/root/templates/terms/show.tt b/root/templates/terms/show.tt index 9f829f119..d18758b92 100644 --- a/root/templates/terms/show.tt +++ b/root/templates/terms/show.tt @@ -2,11 +2,11 @@
    [% IF previous_url %] - + chevron_left [% ELSE %] - + chevron_left [% END %] @@ -15,11 +15,11 @@ ' until ' _ display_date( terms.valid_until, {html=>1} ) IF terms.valid_until %]

    [% IF next_url %] - + chevron_right [% ELSE %] - + chevron_right [% END %] -- GitLab From 5a7a6848808834981f9899f355bfccfb01893039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20B=C3=B6hmer?= Date: Sat, 14 Sep 2024 15:02:51 +0200 Subject: [PATCH 24/33] Move global nav links to 1st bar and project nav links to 2nd bar --- root/templates/wrapper.tt | 55 +++++++++++++++------------------------ 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/root/templates/wrapper.tt b/root/templates/wrapper.tt index 3ff1b4c68..49fef2315 100644 --- a/root/templates/wrapper.tt +++ b/root/templates/wrapper.tt @@ -59,23 +59,8 @@
      - [% IF project_urls %] - - - - - - - [% IF user %] - - [% END %] - [% END %] + + [% IF submenu_items.size > 0 %]
      @@ -141,6 +126,25 @@ @@ -197,14 +192,6 @@
      -
      -

      Browse

      - -
      -

      Get Help

        -- GitLab From b98c38a4dc81c1e628c3d16144c80e73f4c97014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20B=C3=B6hmer?= Date: Sat, 14 Sep 2024 15:40:13 +0200 Subject: [PATCH 25/33] Move project main navigation to project homepage --- lib/Coocook/Controller/Dish.pm | 2 +- lib/Coocook/Controller/Permission.pm | 2 +- lib/Coocook/Controller/Project.pm | 24 +++++++----------------- root/templates/project/edit.tt | 2 ++ root/templates/project/show.tt | 4 ++++ 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/lib/Coocook/Controller/Dish.pm b/lib/Coocook/Controller/Dish.pm index 0a380a1fb..c26a991c3 100644 --- a/lib/Coocook/Controller/Dish.pm +++ b/lib/Coocook/Controller/Dish.pm @@ -16,7 +16,7 @@ Catalyst Controller. =cut -sub base : Chained('/project/submenu') PathPart('dish') CaptureArgs(1) { +sub base : Chained('/project/base') PathPart('dish') CaptureArgs(1) { my ( $self, $c, $id ) = @_; $c->stash( dish => $c->project->dishes->find( $id, { prefetch => [ 'meal', 'recipe' ] } ) diff --git a/lib/Coocook/Controller/Permission.pm b/lib/Coocook/Controller/Permission.pm index c47233cbc..d9162f67c 100644 --- a/lib/Coocook/Controller/Permission.pm +++ b/lib/Coocook/Controller/Permission.pm @@ -4,7 +4,7 @@ use Coocook::Base qw(Moose); BEGIN { extends 'Coocook::Controller' } -sub index : GET HEAD Chained('/project/submenu') PathPart('permissions') Args(0) +sub index : GET HEAD Chained('/project/base') PathPart('permissions') Args(0) RequiresCapability('view_project_permissions') { my ( $self, $c ) = @_; diff --git a/lib/Coocook/Controller/Project.pm b/lib/Coocook/Controller/Project.pm index 1e6ecd03b..734bcbb31 100644 --- a/lib/Coocook/Controller/Project.pm +++ b/lib/Coocook/Controller/Project.pm @@ -113,21 +113,7 @@ sub base : Chained('/base') PathPart('project') CaptureArgs(2) { } } -sub submenu : Chained('base') PathPart('') CaptureArgs(0) { - my ( $self, $c ) = @_; - - $c->stash( - submenu_items => [ - { text => "Show project", action => 'project/show' }, - { text => "Meals & Dishes", action => 'project/edit' }, - { text => "Permissions", action => 'permission/index' }, - { text => "Shop sections", action => 'shop_section/index' }, - { text => "Settings", action => 'project/settings' }, - ], - ); -} - -sub show : GET HEAD Chained('submenu') PathPart('') Args(0) RequiresCapability('view_project') { +sub show : GET HEAD Chained('base') PathPart('') Args(0) RequiresCapability('view_project') { my ( $self, $c ) = @_; my $days = $c->model('Plan')->project( $c->project ); @@ -144,13 +130,17 @@ sub show : GET HEAD Chained('submenu') PathPart('') Args(0) RequiresCapability(' } $c->stash( + permissions_url => $c->project_uri('/permission/index'), + settings_url => $c->project_uri( $self->action_for('settings') ), + shop_sections_url => $c->project_uri('/shop_section/index'), + can_edit => !!$c->has_capability('edit_project'), can_unarchive => !!$c->has_capability('unarchive_project'), days => $days, ); } -sub edit : GET HEAD Chained('submenu') PathPart('edit') Args(0) RequiresCapability('edit_project') { +sub edit : GET HEAD Chained('base') PathPart('edit') Args(0) RequiresCapability('edit_project') { my ( $self, $c ) = @_; my $default_date = DateTime->today; @@ -195,7 +185,7 @@ sub edit : GET HEAD Chained('submenu') PathPart('edit') Args(0) RequiresCapabili ); } -sub settings : GET HEAD Chained('submenu') PathPart('settings') Args(0) +sub settings : GET HEAD Chained('base') PathPart('settings') Args(0) RequiresCapability('view_project_settings') { my ( $self, $c ) = @_; diff --git a/root/templates/project/edit.tt b/root/templates/project/edit.tt index 2b42bb505..b93eb3cf4 100644 --- a/root/templates/project/edit.tt +++ b/root/templates/project/edit.tt @@ -4,6 +4,8 @@ css_push_template_path(); js.push('/lib/coocook-web-components/dist/meals-dishes-editor/meals-dishes-editor.es.js'); %] +edit_off +
        [% WRAPPER includes/infobox.tt %] diff --git a/root/templates/project/show.tt b/root/templates/project/show.tt index 23619e0b5..33712a0bf 100644 --- a/root/templates/project/show.tt +++ b/root/templates/project/show.tt @@ -58,11 +58,15 @@ IF project.archived;

        [% project.name | html %]

        + edit [% tooltip_button( { icon => 'print', tooltip => { content => 'Print' }, attrs => { onclick => 'print()' }, } ) %] + list + manage_accounts + settings
        -- GitLab From 7da58b0f516e9307c575a127ecf42130dc9c6a48 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Thu, 24 Oct 2024 19:49:18 +0200 Subject: [PATCH 26/33] Design improvements for new navigation --- lib/Coocook/Controller/Admin.pm | 16 +----- root/static/css/style.css | 39 +++++--------- root/templates/wrapper.tt | 90 ++++++++++++++++++++------------- 3 files changed, 71 insertions(+), 74 deletions(-) diff --git a/lib/Coocook/Controller/Admin.pm b/lib/Coocook/Controller/Admin.pm index bcd4a98de..a3345b158 100644 --- a/lib/Coocook/Controller/Admin.pm +++ b/lib/Coocook/Controller/Admin.pm @@ -7,21 +7,7 @@ BEGIN { extends 'Coocook::Controller' } sub base : Chained('/base') PathPart('admin') CaptureArgs(0) { my ( $self, $c ) = @_; - my $admin_urls = $c->stash->{admin_urls}; - - my @items = ( - { url => $c->stash->{admin_url}, text => "Admin" }, - { url => $admin_urls->{faq}, text => "FAQ" }, - { url => $admin_urls->{organizations}, text => "Organizations" }, - { url => $admin_urls->{projects}, text => "Projects" }, - { url => $admin_urls->{recipes}, text => "Recipes" }, - { url => $admin_urls->{terms}, text => "Terms" }, - { url => $admin_urls->{users}, text => "Users" }, - ); - - $_->{url} or $_->{forbidden} = 1 for @items; - - $c->stash( submenu_items => \@items ); + $c->stash( on_admin_pages => 1 ); } sub index : GET HEAD Chained('base') PathPart('') Args(0) RequiresCapability('admin_view') { diff --git a/root/static/css/style.css b/root/static/css/style.css index 9d3576ab9..066fe664f 100644 --- a/root/static/css/style.css +++ b/root/static/css/style.css @@ -138,43 +138,32 @@ header h1 { font-size: 2.5em; } -/*** project nav ***/ -#project_nav .nav-link:not(.disabled-import) { +/*** main nav ***/ +#main_nav .nav-link:not(.disabled-import) { color: var(--bs-light); } -.nav-link.disabled-import { - color: #6c757d; - user-select: none; - cursor: pointer; +.disabled-import { + opacity: 0.7; } -#project_nav a:hover, -#project_nav a:focus { - color: var(--bs-primary); +#main_nav .nav-link:hover, +#main_nav .nav-link:focus { + background: var(--bs-gray-700); } /*** submenu nav ***/ -#submenu_nav .nav-link:hover, -#submenu_nav .nav-link.active, -#submenu_nav .nav-link:focus { +#sub_nav .nav-link:hover, +#sub_nav .nav-link.active, +#sub_nav .nav-link:focus { color: var(--bs-light) !important; background: var(--cc-green-dark); } -/*** login logout section ***/ -.login-logout-btn { - background-color: transparent; - border: 0px; - -webkit-box-shadow: 0px 0px 0px rgba(255, 255, 255, 0) !important; - -moz-box-shadow: 0px 0px 0px rgba(255, 255, 255, 0) !important; - box-shadow: 0px 0px 0px rgba(255, 255, 255, 0) !important; -} - -.login-logout-btn:active, -.login-logout-btn:hover, -.login-logout-btn:focus { - color: var(--bs-light); +.project-pill { + background: #d8fccf; + display: inline-block; + margin-left: 0.25em; } /******** end header part ********/ diff --git a/root/templates/wrapper.tt b/root/templates/wrapper.tt index 49fef2315..ab50c6a78 100644 --- a/root/templates/wrapper.tt +++ b/root/templates/wrapper.tt @@ -57,37 +57,56 @@
        -
      -
    diff --git a/t/controller_Article.t b/t/controller_Article.t index 364b1d665..05babf7e5 100644 --- a/t/controller_Article.t +++ b/t/controller_Article.t @@ -11,7 +11,7 @@ $t->get('https://localhost/'); $t->login_ok( 'john_doe', 'P@ssw0rd' ); $t->follow_link_ok( { text => 'public Test Project' } ); -$t->follow_link_ok( { text => 'Articles' } ); +$t->follow_link_ok( { text => 'Articles 5' } ); $t->content_contains('https://localhost/project/1/Test-Project/tag/1'); $t->follow_link_ok( { text => 'addNew article' } ); @@ -21,7 +21,7 @@ $t->submit_form_ok( { with_fields => { name => 'aether' } }, "create article" ); is $t->schema->resultset('Article')->find( { name => 'aether' } )->units->count => 0, "... new article has no units"; -$t->follow_link_ok( { text => 'Articles' } ); +$t->follow_link_ok( { text => 'Articles 6' } ); $t->text_contains('aether'); diff --git a/t/controller_Unit.t b/t/controller_Unit.t index 9b9a96ab6..d0667930b 100644 --- a/t/controller_Unit.t +++ b/t/controller_Unit.t @@ -9,11 +9,10 @@ my $t = Test::Coocook->new(); $t->get_ok('https://localhost/project/1/Test-Project/units'); $t->text_contains( - "1\N{THIN SPACE}g = 0.001\N{THIN SPACE}kg | 1\N{THIN SPACE}kg = 1000\N{THIN SPACE}g" - . "delete_forever" ); + "1\N{THIN SPACE}g = 0.001\N{THIN SPACE}kg | 1\N{THIN SPACE}kg = 1000\N{THIN SPACE}g" . "delete" ); # make sure 1:1 conversions are displayed only once -$t->text_contains( "btl (bottles) 1\N{THIN SPACE}btl = 1\N{THIN SPACE}pcs" . "delete_forever" ); -$t->text_contains( "pcs (pieces) 1\N{THIN SPACE}pcs = 1\N{THIN SPACE}btl" . "delete_forever" ); +$t->text_contains( "btl (bottles) 1\N{THIN SPACE}btl = 1\N{THIN SPACE}pcs" . "delete" ); +$t->text_contains( "pcs (pieces) 1\N{THIN SPACE}pcs = 1\N{THIN SPACE}btl" . "delete" ); $t->content_contains('https://localhost/project/1/Test-Project/unit/1'); diff --git a/t/postgresql.t b/t/postgresql.t index 7f95b9f13..21d7c23ae 100644 --- a/t/postgresql.t +++ b/t/postgresql.t @@ -289,8 +289,8 @@ subtest "timestamps are stored in UTC" => sub { $t->get_ok('/'); $t->submit_form_ok( { with_fields => { name => 'Project UTC' } }, "create project" ); - $t->follow_link_ok( { text => 'Project' } ); - $t->follow_link_ok( { text => 'Settings', class_regex => qr/nav-link/ } ); + $t->follow_link_ok( { text => 'lock Project UTC' } ); + $t->follow_link_ok( { text => 'settings', class_regex => qr/btn/ } ); $t->submit_form_ok( { with_fields => { archive => 'on' } } ); my $project = $schema->resultset('Project')->find( { name => 'Project UTC' } ) || die; like( $project->get_column($_) => $utc_regex, "column '$_' is in UTC" ) for qw< created archived >; -- GitLab From 3e960abdeb2f517e8ccd2f92e9c3ed136ffe7260 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Thu, 24 Oct 2024 21:13:49 +0200 Subject: [PATCH 28/33] Remove no longer needed submenu item code --- lib/Coocook/Controller/Root.pm | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/lib/Coocook/Controller/Root.pm b/lib/Coocook/Controller/Root.pm index e6eb5b32a..b8e4ab7fa 100644 --- a/lib/Coocook/Controller/Root.pm +++ b/lib/Coocook/Controller/Root.pm @@ -337,39 +337,6 @@ Attempt to render a view, if needed. sub end : ActionClass('RenderView') { my ( $self, $c ) = @_; - for my $item ( $c->stash->{submenu_items}->@* ) { - next if $item->{forbidden}; - next if $item->{url}; - - my $action = $item->{action}; - - my $capabilities = $self->action_for($action)->attributes->{RequiresCapability}; - - for my $capability (@$capabilities) { - if ( not $c->has_capability($capability) ) { - $item->{forbidden} = 1; - next; - } - } - - if ( $c->action ne $action ) { - if ( $action =~ m/ ^ (admin|settings) \/ /x ) { # TODO how to distinguish this in a generic way? - $item->{url} = $c->uri_for_action($action); - } - else { - if ( $c->project ) { - $item->{url} = $c->project_uri($action); - } - else { - $item->{forbidden} = 1; - } - } - } - } - - # remove subitems that have the 'forbidden' flag - $c->stash->{submenu_items}->@* = grep { not $_->{forbidden} } $c->stash->{submenu_items}->@*; - { my $errors = $c->stash->{errors}; my $status = $c->res->status; -- GitLab From b6a378004a125003447452aaaea93f2e48369360 Mon Sep 17 00:00:00 2001 From: Kurt Roscher Date: Thu, 14 Nov 2024 08:07:37 +0100 Subject: [PATCH 29/33] Center pills in navigation --- root/templates/wrapper.tt | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/root/templates/wrapper.tt b/root/templates/wrapper.tt index 91666f3aa..31ceece87 100644 --- a/root/templates/wrapper.tt +++ b/root/templates/wrapper.tt @@ -74,11 +74,11 @@ [% END %] [% IF user %] -