+# - icon: optional, str - name of a Material Icons icon
+# - text: optional, str - label text shown on the button
+# - tooltip: hashref, required for tooltip_button - details for Bootstrap tooltip, see below
+# - attrs: optional, hashref - additional HTML attributes and values for the button element
+# - container_classes: optional, string | string[] - additional CSS classes for the outer container
+# - button_classes: optional, string | string[] - additional CSS classes for the button element itself
+# - icon_after_text: optional, bool - if true the icon is displayed after the button text
+#
+# tooltip:
+# - content: required, string - content of the tooltip, could be html markup
+# - line_through: optional, bool - when button disabled show tooltip with linethrough, default: false
+# - disabled_content: optional, string - content of the tooltip when button disabled, could be html markup
+
+MACRO button(opts) BLOCK;
+ IF opts.icon;
+ icon = '' _ opts.icon _ ' ';
+ END ~%]
+ <[% opts.href ? 'a' : 'button' %]
+ 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 %]"
+ [%~ END;
+ 'href="' _ opts.href _ '"' IF opts.href ~%]
+ >
+ [% icon IF !opts.icon_after_text; opts.text; icon IF opts.icon_after_text %]
+ [% opts.href ? 'a' : 'button' %]>
+[%~ END;
+
+MACRO tooltip_button(opts) BLOCK;
+ IF opts.disabled;
+ IF opts.tooltip.line_through;
+ msg = '' _ (opts.tooltip.disabled_content || opts.tooltip.content) _ ' ';
+ ELSE;
+ msg = (opts.tooltip.disabled_content || opts.tooltip.content);
+ END;
+ ELSE;
+ msg = (opts.tooltip.content);
+ END ~%]
+
+ [% button( opts ) %]
+
[%~ END ~%]
diff --git a/root/static/css/print.css b/root/static/css/print.css
index 7f2ce030630682584618d52112cb13ad53be2c51..b82f0775f53a6ddbb1c07911912faeec92ae8f2f 100644
--- a/root/static/css/print.css
+++ b/root/static/css/print.css
@@ -1,7 +1,8 @@
@media print {
/* hide browser navigation elements */
header,
- footer {
+ footer,
+ .tooltip {
display: none;
}
diff --git a/root/static/css/style.css b/root/static/css/style.css
index 16916cd04be7b6e5dbf5ccdf847d1d9eb9687652..066fe664f3ab73e4c7c6ab9a0aa15037e0ffea09 100644
--- a/root/static/css/style.css
+++ b/root/static/css/style.css
@@ -17,7 +17,19 @@ textarea {
resize: vertical;
}
-.btn-sm.btn-quad {
+.btn-square {
+ aspect-ratio: 1;
+}
+
+.btn-no-square {
+ aspect-ratio: unset;
+}
+
+.btn.btn-square {
+ padding: 0.375em;
+}
+
+.btn.btn-sm.btn-square {
padding: 0.25rem;
}
@@ -126,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 ********/
@@ -355,13 +356,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 {
@@ -378,11 +386,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/project/show.js b/root/static/js/project/show.js
index 0995be311ae5be2b361e5793005b8277d47efb6e..5cfebd4a8f880085dc90207127b36e01622f0a6d 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/static/js/purchase_list/edit.js b/root/static/js/purchase_list/edit.js
index 749124fdc4dee751ea97d7180a29e47f02592598..7bbdaa9c9be8bbdfed5a50013465b126911c06ae 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/recipe/index.js b/root/static/js/recipe/index.js
index 663524c41c970d738fe4539c59c369bbe7ca3fc9..8d98d25c7c484bf42e4eb94d5a673b4c6f420241 100644
--- a/root/static/js/recipe/index.js
+++ b/root/static/js/recipe/index.js
@@ -2,11 +2,26 @@ 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").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()
+ );
diff --git a/root/static/js/script.js b/root/static/js/script.js
index cb87d3161614ec8e00331dcca009945e25724f2d..d4c3338d11eacff1f1cb366d30aad7fedcb0d693 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/admin/faq/edit.tt b/root/templates/admin/faq/edit.tt
index f465b63e62d0450d98ab63094903364806c831a8..22d8fc7cc853679d11d43616a4aaf2f52a4ecc2d 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 @@
- [% faq.in_storage ? 'Update' : 'Create' %] Entry
+ [% 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 81b6a053dccceb9006349962af9f06ac965ca8ce..66c410bdd892b5d52e0960d8fd275453b09da80a 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/admin/terms/edit.tt b/root/templates/admin/terms/edit.tt
index 463554c8ca39a71f9462de2d0f0db965bc917ef3..8afbe7db41fa697e002f1f4d9aa6b5da3ef7e84e 100644
--- a/root/templates/admin/terms/edit.tt
+++ b/root/templates/admin/terms/edit.tt
@@ -3,7 +3,7 @@
diff --git a/root/templates/admin/terms/index.tt b/root/templates/admin/terms/index.tt
index e7f4388b330d704f9921fef92ed5f81613832e6c..5918f923e3989c0173aafa35b710860fd6ac751d 100644
--- a/root/templates/admin/terms/index.tt
+++ b/root/templates/admin/terms/index.tt
@@ -5,22 +5,45 @@
[% IF item.edit_url %]
[% END;
IF item.delete_url %]
[% END %]
[% END;
list(terms, 'term_item', { item_classes => 'd-flex gap-3 justify-content-between align-items-center parent'}) %]
-
+[% 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',
+} ) %]
diff --git a/root/templates/article/edit.tt b/root/templates/article/edit.tt
index 7adab80d61e4dd99bc35767d5127c8f3bb41422b..f51d9afd082f47f49c28f45535b2042b661ef029 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 eceb1194d0f11d47fc38d0d5765547d14ab05c36..bb9368163868c5783fb9d340bd1c928d521581c8 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 %]
- [% ELSE %]
-
- delete_forever
-
- [% END %]
[% END %]
diff --git a/root/templates/browse/recipe/import.tt b/root/templates/browse/recipe/import.tt
index 275e8809ee8e068b38259687f1658805d6530ae6..b77551a51f8169564096f549eb7b7041400b108d 100644
--- a/root/templates/browse/recipe/import.tt
+++ b/root/templates/browse/recipe/import.tt
@@ -10,9 +10,14 @@
diff --git a/root/templates/browse/recipe/index.tt b/root/templates/browse/recipe/index.tt
index 4b51a8865e8a93ca10282ebcbbecf07bf784b581..a4fd7b0a7a19dc7624d87b83b1aec131dd78a938 100644
--- a/root/templates/browse/recipe/index.tt
+++ b/root/templates/browse/recipe/index.tt
@@ -23,7 +23,17 @@ END %]
[% IF item.import_url %]
[% END;
END;
diff --git a/root/templates/browse/recipe/show.tt b/root/templates/browse/recipe/show.tt
index 306bc0bd03e842e2af15e1038cbdec07bae9ca73..95f2f1cc6d0ab4acef2321ab96aeeb60de04744a 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') %]
-print
+[% tooltip_button( {
+ icon => 'print',
+ tooltip => { content => 'Print' },
+ attrs => { onclick => 'print()' },
+ container_classes => 'd-inline-block my-3 no-print',
+} ) %]
- calculate Show
+ [% button( {
+ text => 'Show',
+ icon => 'calculate',
+ attrs => {
+ type => 'submit',
+ },
+ } ) %]
diff --git a/root/templates/dashboard.tt b/root/templates/dashboard.tt
index c95272a86e431b79ad7c08a3b263e336399568ea..e0aa0b256667dedfb23952d02d91fd1733f554ab 100644
--- a/root/templates/dashboard.tt
+++ b/root/templates/dashboard.tt
@@ -16,7 +16,9 @@
lock private
-
+ [% button( {
+ text => 'Create project',
+ } ) %]
diff --git a/root/templates/dish/edit.tt b/root/templates/dish/edit.tt
index d0872d66ae6e90c195de93c77eac82aab16be5d4..58ebfdcd5557778e2497c64a0b4ca01f0c8809cf 100644
--- a/root/templates/dish/edit.tt
+++ b/root/templates/dish/edit.tt
@@ -18,9 +18,14 @@ js.push('/js/tagAutocomplete.js');
@@ -86,7 +91,12 @@ js.push('/js/tagAutocomplete.js');
- Update dish
+ [% button( {
+ text => 'Update dish',
+ attrs => {
+ type => 'submit',
+ },
+ } ) %]
@@ -111,7 +121,12 @@ class="col-sm-12 py-3">
- Recalculate values
+ [% button( {
+ text => 'Recalculate values',
+ attrs => {
+ type => 'submit',
+ },
+ } ) %]
diff --git a/root/templates/faq/index.tt b/root/templates/faq/index.tt
index fb53d2bd671dde24ed8f4d944f092bdb3a2b40e5..96167fb897d93d13f85bb6a823b010d3e74a73d6 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 %]
-
-
+
+ [% button( {
+ text => 'Edit FAQ',
+ variant => 'outline-primary',
+ href => admin_faq_url,
+ } ) %]
[% END;
@@ -21,7 +23,16 @@ FOR faq IN faqs %]
[% IF faq.edit_url %]
[% END %]
diff --git a/root/templates/organization/members.tt b/root/templates/organization/members.tt
index 3348f5afd314059829a6932ed679d0c8a3cbe202..9968c5db1ce623e88aafdc2659de78568e0aa611 100644
--- a/root/templates/organization/members.tt
+++ b/root/templates/organization/members.tt
@@ -31,7 +31,8 @@ has_admin = 0 %]
[% role %]
[% END %]
- done
+ [%# We don't use the button macro here. Because when we would use the macro the styling would break %]
+ done
[% ELSE %]
[% o_u.role %]
@@ -42,12 +43,32 @@ has_admin = 0 %]
[% IF o_u.transfer_ownership_url;
has_admin = 1 %]
[% END %]
[% IF o_u.remove_url %]
[% 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 e8eb8cd49f12d82fd2e9cec532962a8fed5d6e62..0718bce4f1838895b6c6002072e095cd388fb2f4 100644
--- a/root/templates/organization/show.tt
+++ b/root/templates/organization/show.tt
@@ -12,7 +12,12 @@
Display name
-
+ [% 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 ~%]
[% 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.
diff --git a/root/templates/print/day.tt b/root/templates/print/day.tt
index 11da828739638297594351c9b98853bc65b830d7..d31cb15d685a1c780dea74fc7431cf5a6e8be468 100644
--- a/root/templates/print/day.tt
+++ b/root/templates/print/day.tt
@@ -1,6 +1,12 @@
[% css.push('/css/print.css') %]
-
print
+
+ [% tooltip_button( {
+ icon => 'print',
+ tooltip => { content => 'Print' },
+ attrs => { onclick => 'print()' },
+ } ) %]
+
[% FOR meal IN meals %]
[% meal.name | html %]
diff --git a/root/templates/project/edit.tt b/root/templates/project/edit.tt
index 2b42bb50552432269e17f790a3b0c621e994204e..8edc045510528af315172c830445065b9c1dbcf7 100644
--- a/root/templates/project/edit.tt
+++ b/root/templates/project/edit.tt
@@ -4,6 +4,14 @@ css_push_template_path();
js.push('/lib/coocook-web-components/dist/meals-dishes-editor/meals-dishes-editor.es.js');
%]
+
+ [% tooltip_button( {
+ icon => 'edit_off',
+ href => project_urls.project,
+ tooltip => { content => 'Show' },
+ } ) %]
+
+
[% WRAPPER includes/infobox.tt %]
diff --git a/root/templates/project/import.tt b/root/templates/project/import.tt
index 1dcad3a8dd5abe8456d52658157de03ed5c2cacf..152ef155d7f477b0f692ae6eca69a5fd5b3acb2d 100644
--- a/root/templates/project/import.tt
+++ b/root/templates/project/import.tt
@@ -41,8 +41,14 @@ js_push_template_path() %]
[% END %]
-
- Import
+
+ [% button( {
+ text => 'Import',
+ attrs => {
+ type => 'submit',
+ },
+ } ) %]
+
[% END %]
diff --git a/root/templates/project/permissions.tt b/root/templates/project/permissions.tt
index 7798a281d0129389abab3302715a539a9ae05c2e..ae5d4ab3cfd42008e56b0919a68f8bc065725955 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.make_owner_url %]
-
-
-
-
-
- [% END %]
+ [% END %]
- [% IF permission.revoke_url %]
-
-
- delete_forever
-
-
- [% 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 %]
- lock
- [% 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 %]
- public
- [% 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 7be8637390ad636b890fa05f4bf0ee71b99f6c29..350ab9acdf85d56e048a7d3a2bf5fde75ca81556 100644
--- a/root/templates/project/show.tt
+++ b/root/templates/project/show.tt
@@ -1,9 +1,38 @@
[% js_push_template_path();
css.push('/css/print.css');
-show_donate_infobox = 0;
+show_donate_infobox = 0 %]
-IF project.archived;
+[% project.name | html %]
+
+ [% tooltip_button( {
+ icon => 'edit',
+ href => project_urls.edit,
+ tooltip => { content => 'Edit project' },
+ } ) %]
+ [% tooltip_button( {
+ icon => 'print',
+ tooltip => { content => 'Print' },
+ attrs => { onclick => 'print()' },
+ } ) %]
+ [% tooltip_button( {
+ icon => 'list',
+ href => project_urls.shop_sections,
+ tooltip => { content => 'Shop sections' },
+ } ) %]
+ [% tooltip_button( {
+ icon => 'manage_accounts',
+ href => project_urls.permissions,
+ tooltip => { content => 'Project permissions' },
+ } ) %]
+ [% tooltip_button( {
+ icon => 'settings',
+ href => project_urls.settings,
+ tooltip => { content => 'Project settings' },
+ } ) %]
+
+
+[% IF project.archived;
show_donate_infobox = 1 %]
[% IF can_unarchive %]
@@ -55,10 +84,6 @@ IF project.archived;
[% END %]
-[% project.name | html %]
-
-print
-
[% USE Markdown;
project.description | markdown %]
@@ -71,9 +96,17 @@ IF project.archived;
Meal
Dishes
-
- add
-
+ [% 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-square',
+ container_classes => 'd-grid',
+ } ) %]
diff --git a/root/templates/purchase_list/_edit_table.tt b/root/templates/purchase_list/_edit_table.tt
index dcf7037b319b454ed60dabf0d0df9bb90b7aa99e..bb3320066bf3646b4dc0c842144941dcaeb10e5c 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} ) %]
- done
+ [% 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 %]
- [% display_value_unit(unit.total, unit, {html=>1}) %]
+ [% 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 %]
- undo
+ [% 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 80c0eca1c2e720e89b351d39d73a425f619e66a7..f067940b2a1b4834907b35f84a651c0a78e645ca 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();
%]
-
print
-[% IF purchase_lists.size > 1 %]
-
- drive_file_move Move items/ingredients to other purchase list
-
-
- drive_file_move Move items/ingredients to other purchase list
-
-[% END %]
-
- move_to_inbox Assign articles to shop section
-
-
- move_to_inbox Assign articles to shop section
-
+
+ [% 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 %]
diff --git a/root/templates/purchase_list/index.tt b/root/templates/purchase_list/index.tt
index 3bf5a0cfb126a10637f66098b8741c7118c4815e..d77603849de15d812c150143c7b496e20c519aab 100644
--- a/root/templates/purchase_list/index.tt
+++ b/root/templates/purchase_list/index.tt
@@ -5,9 +5,21 @@ js_push_template_path();
BLOCK new_list %]
-
- add
-
+ [% 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-square',
+ } ) %]
[% END %]
@@ -41,50 +53,62 @@ BLOCK new_list %]
[% purchase_list.ingredients_count %]
-
-
- edit
-
-
+ [% 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 %]
-
- shopping_cart_checkout
-
+ [% 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 %]
-
-
- shopping_cart_checkout
-
-
- [% END;
- END;
+ [% END;
- IF purchase_list.delete_url;
- IF is_in_use %]
-
-
- delete_forever
-
-
- [% ELSE %]
-
-
- delete_forever
-
-
- [% END;
+ button_classes = [ 'btn-sm' ];
+ button_classes.push('delete-trigger') IF is_in_use;
- ELSE %]
-
-
- delete_forever
-
-
- [% 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',
+ },
+ } ) %]
+
diff --git a/root/templates/recipe/edit.tt b/root/templates/recipe/edit.tt
index db31b14014ec3073e02b1a6a61ac4ec1b42a4ba5..f696f79dd3f9ece8ad70561385c26c96c0a56ac7 100644
--- a/root/templates/recipe/edit.tt
+++ b/root/templates/recipe/edit.tt
@@ -10,22 +10,50 @@ IF import_url %]
-
- Used in [% dishes.size %] dish[% dishes.size != 1 ? "es" : "" %]
-
+ [%
+ 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 %]
-
- Share link
-
- [% END;
- IF import_url %]
-
- Import into another project east
-
+ [% 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 %]
- Update Recipe
+ [% button( {
+ text => 'Update recipe',
+ attrs => {
+ type => 'submit',
+ },
+ } ) %]
diff --git a/root/templates/recipe/importable_recipes.tt b/root/templates/recipe/importable_recipes.tt
index 27aa33085a48487d52cc8268ef030282144ff77d..1033f0667775f31b4ac0171222b42ce704098e00 100644
--- a/root/templates/recipe/importable_recipes.tt
+++ b/root/templates/recipe/importable_recipes.tt
@@ -13,9 +13,17 @@
[% IF recipe.import_url %]
-
- east Import
-
+ [%
+ 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 dbcb46de0d661659d52cebab8f3197f83b52bd85..34662f37c12734cdae842fea0b0e217ea4c4cffb 100644
--- a/root/templates/recipe/index.tt
+++ b/root/templates/recipe/index.tt
@@ -4,12 +4,25 @@ js_push_template_path();
new = BLOCK %]
-
- add New recipe
-
-
- 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 %]
-
- file_copy
-
+ [%
+ 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',
+ } )
+ %]
-
- delete_forever
-
+ [% button( {
+ icon => 'delete_forever',
+ variant => 'outline-danger',
+ attrs => {
+ type => 'submit',
+ },
+ button_classes => 'btn-sm',
+ } ) %]
diff --git a/root/templates/session/login.tt b/root/templates/session/login.tt
index 7a607ec485178a20b9729074740c024efd29431b..467997ec51078e7c132835e84380e9225b9aa15b 100644
--- a/root/templates/session/login.tt
+++ b/root/templates/session/login.tt
@@ -20,7 +20,9 @@
Remember username for next login
-
+ [% button( {
+ text => 'Sign in',
+ } ) %]
diff --git a/root/templates/settings/account.tt b/root/templates/settings/account.tt
index 4d00b4a35e18e9b3a146974fe143f33e83de00e7..c1b2fdc9a2abdd5db2514c13d2ee2d981ddb7f33 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');
Display name
-
+ [% button( {
+ text => 'Change display name',
+ } ) %]
@@ -67,7 +71,10 @@ js.push('/lib/zxcvbn.js');
Confirm new password
-
+ [% 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');
New email address
-
+ [% button( {
+ text => 'Change email address',
+ } ) %]
diff --git a/root/templates/settings/organizations.tt b/root/templates/settings/organizations.tt
index 8c5759c4de43a8881954ab668c74c6389bb57a1d..6dba001bad6ed3909163f5a7d01c157a78e79eff 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',
+ },
+ } ) %]
@@ -28,21 +33,33 @@ BLOCK user_item %]
[% link_organization(item.organization) %] ([% item.role %])
[% IF item.leave_url %]
-
- logout Leave organization
-
+ [% button( {
+ text => 'Leave organization',
+ icon => 'logout',
+ variant => 'outline-danger',
+ } ) %]
-[% ELSE %]
-
- logout Leave organization
-
-[% 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;
diff --git a/root/templates/shop_section/index.tt b/root/templates/shop_section/index.tt
index e879e9a3bb48b8d1eeba53ad4fb884bd4604a580..0917613bdf252a19bc1ff5b06f445d1c739c1903 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 %]
-
- add
-
+ [% 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-square',
+ } ) %]
[% END %]
@@ -28,20 +38,33 @@ BLOCK new_section %]
[% section.articles_count %]
-
- edit
-
- [% IF section.delete_url %]
-
-
- delete_forever
-
+ [%
+ 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 %]
-
- delete_forever
-
- [% END %]
diff --git a/root/templates/tag/edit.tt b/root/templates/tag/edit.tt
index d500619e415273b0cb72052fe82769b5bf3b39ab..23310e3d14999ea6c25e17cae39955a5a24f0ccf 100644
--- a/root/templates/tag/edit.tt
+++ b/root/templates/tag/edit.tt
@@ -10,20 +10,26 @@ js_push_template_path() %]