mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
add form logic + update build.js
* build.js will add csrf token input * add a global submit form using attributs * add JSdoc on utils * remove useless backdrop event
This commit is contained in:
parent
4ddceff055
commit
df1a17445c
9 changed files with 127 additions and 23 deletions
|
|
@ -109,12 +109,16 @@ function setFlaskData() {
|
|||
let attributs = "";
|
||||
matches.forEach((match) => {
|
||||
const matchFormat = match.replace('="', "").replace("='", "");
|
||||
attributs += `<div class="hidden" ${matchFormat}={{${matchFormat}}}></div>\n`;
|
||||
attributs += `<div class="hidden" ${matchFormat}={{${matchFormat.replaceAll(
|
||||
"-",
|
||||
"_"
|
||||
)}}}></div>\n`;
|
||||
});
|
||||
// insert the new content
|
||||
updateData =
|
||||
data.slice(0, bodyIndex) +
|
||||
`\n<body>\n` +
|
||||
`<div class="hidden" data-csrf-token={{ csrf_token() }}></div>\n` +
|
||||
attributs +
|
||||
`<div id="app"></div>\n</body>\n</html>`;
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<body>
|
||||
<div class="hidden" data-server-global='{"username" : "admin"}'></div>
|
||||
<div class="hidden"
|
||||
data-server-flash='[{"type" : "success", "title" : "title", "message" : "Success feedback"}, {"type" : "error", "title" : "title", "message" : "Error feedback"}, {"type" : "warning", "title" : "title", "message" : "Warning feedback"}, {"type" : "info", "title" : "title", "message" : "Info feedback"}]'>
|
||||
data-server-flash='[{"type" : "success", "title" : "success", "message" : "Success feedback"}, {"type" : "error", "title" : "error", "message" : "Error feedback"}, {"type" : "warning", "title" : "warning", "message" : "Warning feedback"}, {"type" : "info", "title" : "info", "message" : "Info feedback"}]'>
|
||||
</div>
|
||||
<div class="hidden"
|
||||
data-server-builder='[{"type":"card", "link" : "/services", "containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat", "link": "https://github.com/bunkerity/bunkerweb","data":{"title":"home_version","subtitle":"home_all_features_available","subtitleColor":"success","stat":"home_pro","iconName":"crown","iconColor":"amber"}}]},{"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_version_number","subtitle":"home_latest_version","subtitleColor":"success","stat":"1.5.7","iconName":"wire","iconColor":"teal"}}]},{"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_instances","subtitle":"home_total_number","subtitleColor":"info", "stat":"1","iconName":"box","iconColor":"dark"}}]}, {"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_services","subtitle":"home_all_methods_included","subtitleColor":"info","stat":"2","iconName":"disk","iconColor":"orange"}}]}, {"type":"card","containerClass":"","containerColumns":{"pc":4,"tablet":6,"mobile":12},"widgets":[{"type":"Stat","data":{"title":"home_plugins","subtitle":"home_no_error","subtitleColor":"success","stat":"42","iconName":"puzzle","iconColor":"yellow"}}]}]'>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { reactive, onBeforeMount, onMounted } from "vue";
|
|||
import DashboardLayout from "@components/Dashboard/Layout.vue";
|
||||
import Builder from "@components/Builder.vue";
|
||||
import { useGlobal } from "@utils/global.js";
|
||||
import { useForm } from "@utils/form.js";
|
||||
|
||||
/**
|
||||
@name Page/Home.vue
|
||||
|
|
@ -27,6 +28,7 @@ onBeforeMount(() => {
|
|||
|
||||
onMounted(() => {
|
||||
useGlobal();
|
||||
useForm();
|
||||
});
|
||||
|
||||
// const data = [
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
data-server-flash='[{"type" : "success", "title" : "title", "message" : "Success feedback"}, {"type" : "error", "title" : "title", "message" : "Error feedback"}, {"type" : "warning", "title" : "title", "message" : "Warning feedback"}, {"type" : "info", "title" : "title", "message" : "Info feedback"}]'>
|
||||
</div>
|
||||
<div class="hidden"
|
||||
data-server-builder='[{"type":"card","link":"/services","containerClass":"","containerColumns":{"pc":6,"tablet":6,"mobile":12},"widgets":[{"type":"Instance","data":{"details":[{"key":"instances_hostname","value":"www.example.com"},{"key":"instances_method","value":"dashboard_ui"},{"key":"instances_port","value":"1084"},{"key":"instances_status","value":"instances_active"}],"status":"success","title":"www.example.com","buttons":[{"attrs" : {"aria-controls" : "test-el", "aria-expanded" : "false"}, "text":"action_edit","color":"edit","size":"normal"}]}}]},{"type":"card","link":"/services","containerClass":"","containerColumns":{"pc":6,"tablet":6,"mobile":12},"widgets":[{"type":"Instance","data":{"details":[{"key":"instances_hostname","value":"www.example.com"},{"key":"instances_method","value":"dashboard_ui"},{"key":"instances_port","value":"1084"},{"key":"instances_status","value":"instances_active"}],"status":"error","title":"www.example.com","buttons":[{ "text":"action_start","color":"valid","size":"normal"}]}}]}]'>
|
||||
data-server-builder='[{"type":"card","link":"/services","containerClass":"","containerColumns":{"pc":6,"tablet":6,"mobile":12},"widgets":[{"type":"Instance","data":{"details":[{"key":"instances_hostname","value":"www.example.com"},{"key":"instances_method","value":"dashboard_ui"},{"key":"instances_port","value":"1084"},{"key":"instances_status","value":"instances_active"}],"status":"success","title":"www.example.com","buttons":[{"attrs" : {"data-form-INSTANCE_ID" : "instance-1", "data-form-operation" : "edit", "data-submit-form" : "true"}, "text":"action_edit","color":"edit","size":"normal"}]}}]},{"type":"card","link":"/services","containerClass":"","containerColumns":{"pc":6,"tablet":6,"mobile":12},"widgets":[{"type":"Instance","data":{"details":[{"key":"instances_hostname","value":"www.example.com"},{"key":"instances_method","value":"dashboard_ui"},{"key":"instances_port","value":"1084"},{"key":"instances_status","value":"instances_active"}],"status":"error","title":"www.example.com","buttons":[{ "text":"action_start","color":"valid","size":"normal"}]}}]}]'>
|
||||
</div>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="instances.js"></script>
|
||||
|
|
|
|||
|
|
@ -17,14 +17,3 @@ export const useBannerStore = defineStore("banner", () => {
|
|||
|
||||
return { isBanner, bannerClass, setBannerVisible };
|
||||
});
|
||||
|
||||
/**
|
||||
@name useBackdropStore
|
||||
@description Store to share the current backdrop state (visible or not).
|
||||
This backdrop avoid to click on the main content when we want to show a modal or a dialog.
|
||||
*/
|
||||
export const useBackdropStore = defineStore("backdrop", () => {
|
||||
const clickCount = ref(0);
|
||||
|
||||
return { clickCount };
|
||||
});
|
||||
|
|
|
|||
76
vuejs/client/src/utils/form.js
Normal file
76
vuejs/client/src/utils/form.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
@name utils/form.js
|
||||
@description This file contains form utils that will be used in the application by default.
|
||||
This file contains functions related to form validation, form submission, and other form utils.
|
||||
*/
|
||||
|
||||
/**
|
||||
@name useForm
|
||||
@description This function is a composable wrapper that contains all the form utils functions.
|
||||
This function will for example look for elements with data-submit-form attribute and submit the form with the data attributes.
|
||||
*/
|
||||
function useForm() {
|
||||
window.addEventListener("click", (e) => {
|
||||
if (!e.target.hasAttribute("data-submit-form")) return;
|
||||
const data = useGetFormDataAttr(e.target);
|
||||
useSubmitForm(data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@name useSubmitForm
|
||||
@description Create programmatically a form element and submit it with the given data object of type {key: value}.
|
||||
This will create a FormData and append data arguments to it, retrieve the csrf token and send it with a regular form.
|
||||
@example
|
||||
{
|
||||
instance: "1",
|
||||
operation: "delete",
|
||||
}
|
||||
@param {object} data - Object with the form data to submit.
|
||||
*/
|
||||
function useSubmitForm(data) {
|
||||
// Create a form element
|
||||
const form = document.createElement("form");
|
||||
form.style.display = "none";
|
||||
form.method = "POST";
|
||||
// Retrieve csrf token form data-crfs-token
|
||||
try {
|
||||
const csrfToken = document.querySelector("[data-csrf-token]");
|
||||
if (csrfToken) {
|
||||
data.csrf_token = csrfToken.getAttribute("data-csrf-token");
|
||||
}
|
||||
} catch (e) {}
|
||||
// Add input elements with the data object
|
||||
for (const key in data) {
|
||||
const input = document.createElement("input");
|
||||
input.type = "hidden";
|
||||
input.name = key;
|
||||
input.value = data[key];
|
||||
form.appendChild(input);
|
||||
}
|
||||
// Append the form to the body and submit it
|
||||
document.querySelector("body").appendChild(form);
|
||||
console.log(form);
|
||||
form.submit();
|
||||
}
|
||||
|
||||
/**
|
||||
@name useGetFormDataAttr
|
||||
@description /Get the form data store on attributes of the element.
|
||||
Format is data-form-<key>="<value>"
|
||||
@example document.querySelector("[data-submit-form]")
|
||||
@param {DOMElement} el - Element to get the data attributes.
|
||||
*/
|
||||
function useGetFormDataAttr(el) {
|
||||
const data = {};
|
||||
const attributes = el.attributes;
|
||||
for (let i = 0; i < attributes.length; i++) {
|
||||
if (attributes[i].name.includes("data-form-")) {
|
||||
const key = attributes[i].name.replace("data-form-", "");
|
||||
data[key] = attributes[i].value;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
export { useForm };
|
||||
|
|
@ -1,17 +1,24 @@
|
|||
/**
|
||||
@name global.js
|
||||
@name utils/global.js
|
||||
@description This file contains global utils that will be used in the application by default.
|
||||
This file contains functions related to accessibilities, cookies, and other global utils.
|
||||
*/
|
||||
|
||||
// This function is a wrapper that contains all the global utils functions.
|
||||
/**
|
||||
@name useGlobal
|
||||
@description This function is a wrapper that contains all the global utils functions.
|
||||
This function will for example update the aria-expanded attribute of an element in case we have an aria-controls attribute.
|
||||
*/
|
||||
function useGlobal() {
|
||||
window.addEventListener("click", (e) => {
|
||||
updateExpanded();
|
||||
});
|
||||
}
|
||||
|
||||
// This function updates the aria-expanded attribute of an element in case we have an aria-controls attribute.
|
||||
/**
|
||||
@name useGlobal
|
||||
@description This function updates the aria-expanded attribute of an element in case we have an aria-controls attribute.
|
||||
*/
|
||||
function updateExpanded() {
|
||||
// Wait for previous event and element visibility update
|
||||
setTimeout(() => {
|
||||
|
|
@ -35,7 +42,11 @@ function updateExpanded() {
|
|||
}, 50);
|
||||
}
|
||||
|
||||
// Check all the possible ways to hide an element
|
||||
/**
|
||||
@name isElHidden
|
||||
@description This function is a util that checks if an element is hidden.
|
||||
This will check for multiple ways to hide an element like aria-hidden, hidden class, display none, visibility hidden, and !hidden class.
|
||||
*/
|
||||
function isElHidden(el) {
|
||||
return el.hasAttribute("aria-hidden")
|
||||
? el.getAttribute("aria-hidden") === "true"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import en from "@lang/en.json" assert { type: "json" };
|
|||
import fr from "@lang/fr.json" assert { type: "json" };
|
||||
|
||||
/**
|
||||
@name lang.js
|
||||
@name utils/lang.js
|
||||
@description This file contains utils to manage the language of the application.
|
||||
This is here that we retrieve json files to add translations.
|
||||
This lang.js works with vue-i18n.
|
||||
|
|
@ -12,10 +12,20 @@ import fr from "@lang/fr.json" assert { type: "json" };
|
|||
|
||||
const availablesLangs = ["en", "fr"];
|
||||
|
||||
/**
|
||||
@name getAllLang
|
||||
@description Return all the languages json data available in the application.
|
||||
*/
|
||||
function getAllLang() {
|
||||
return { en: en, fr: fr };
|
||||
}
|
||||
|
||||
/**
|
||||
@name getAllLangCurrPage
|
||||
@description Filter the needed translations for the current page in order to reduce the size of the i18n object.
|
||||
@example ["dashboard", "settings", "profile"]
|
||||
@param {array} pagesArr - Array of strings with the names of the prefixes of the translations needed.
|
||||
*/
|
||||
function getAllLangCurrPage(pagesArr) {
|
||||
const langs = getAllLang();
|
||||
// for each lang
|
||||
|
|
@ -31,7 +41,13 @@ function getAllLangCurrPage(pagesArr) {
|
|||
return langs;
|
||||
}
|
||||
|
||||
export function getI18n(pagesArr = []) {
|
||||
/**
|
||||
@name getI18n
|
||||
@description Return the i18n object with the translations needed for the current page for all available languages.
|
||||
@example ["dashboard", "settings", "profile"]
|
||||
@param {array} pagesArr - Array of strings with the names of the prefixes of the translations needed.
|
||||
*/
|
||||
function getI18n(pagesArr = []) {
|
||||
const messages =
|
||||
pagesArr.length > 0 ? getAllLangCurrPage(pagesArr) : getAllLang();
|
||||
|
||||
|
|
@ -48,7 +64,11 @@ export function getI18n(pagesArr = []) {
|
|||
return i18n;
|
||||
}
|
||||
|
||||
export function getLocalLang() {
|
||||
/**
|
||||
@name getLocalLang
|
||||
@description This will return the user langage checking the store, the browser, or the default lang.
|
||||
*/
|
||||
function getLocalLang() {
|
||||
// get store lang, or local, or default
|
||||
if (
|
||||
sessionStorage.getItem("lang") &&
|
||||
|
|
@ -77,3 +97,5 @@ export function getLocalLang() {
|
|||
|
||||
return "en";
|
||||
}
|
||||
|
||||
export { getI18n, getLocalLang };
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
/**
|
||||
@name tabIndex.js
|
||||
@name utils/tabIndex.js
|
||||
@description This file contains the tab indexes for the main components of the application.
|
||||
Tab indexes are used to navigate through the application using the keyboard.
|
||||
This is useful for people with disabilities or people who prefer to use the keyboard instead of the mouse.
|
||||
*/
|
||||
|
||||
const bannerIndex = "-1";
|
||||
const menuIndex = "1";
|
||||
const langIndex = "2";
|
||||
|
|
|
|||
Loading…
Reference in a new issue