fix/update form store, utils, jsdoc and components

* fix advanced form template condition (check if keys in object instead of if exists)
* refactor store in order to avoid replace template when remount by adding a force param
* form store now has a rawData variable to store misc elements, use this to store format raw data (from json) and retrive it when the component is unmount and mount again
* replace computed to check validity by a regular function that run every time there is an editor input
* add internal store function to format the template to key-value pair (setting_id-value) in order to send it on backend
* check if there is at least one setting update to allow save on template (advanced and easy mode only)
* enhance jsdoc by adding returns args and by enhancing params on stores
* update builder and refactor store in order to check a prev_value key to determine if setting is update
* submit now working on advanced and raw mode
This commit is contained in:
Jordan Blasenhauer 2024-07-23 18:52:40 +02:00
parent 7f26dd233b
commit 96f139e94d
15 changed files with 545 additions and 120 deletions

View file

@ -529,6 +529,9 @@ def format_setting(
setting_value["value"] = settings[setting_name].get("value", setting_value.get("value", setting_value.get("default")))
setting_value["method"] = settings[setting_name].get("method", "ui")
# Add prev_value in order to check if value has changed to submit it
setting_value["prev_value"] = setting_value.get("value")
# Then override by service settings
if setting_name in settings:
setting_value["disabled"] = False if settings[setting_name].get("method", "ui") in ("ui", "default", "manual") else True

View file

@ -14,6 +14,7 @@ import { plugin_types } from "@utils/variables";
import { useAdvancedForm } from "@store/form.js";
import { useCheckPluginsValidity } from "@utils/form.js";
import { v4 as uuidv4 } from "uuid";
/**
@name Form/Advanced.vue
@description This component is used to create a complete advanced form with plugin selection.
@ -190,13 +191,17 @@ const filters = [
function filter(filterData) {
advancedForm.templateUIFormat = filterData;
setValidity();
updateStates();
}
function updateStates() {
data.plugins = getPluginNames(advancedForm.templateUIFormat);
// Check after a filter if previous plugin is still in the list and if at least one plugin is available
// Update if not the case
data.currPlugin = data.plugins.includes(data.currPlugin)
? data.currPlugin
: getFirstPlugin(advancedForm.templateUIFormat);
setValidity();
}
function setValidity() {
@ -231,13 +236,9 @@ function getPluginNames(template) {
}
onMounted(() => {
// SetTemplate only if first time we mount it
advancedForm.setTemplate(props.template);
// Get first props.forms template name
data.currPlugin = getFirstPlugin(advancedForm.templateUIFormat);
data.plugins = getPluginNames(advancedForm.templateUIFormat);
setValidity();
// Store update data on
updateStates();
// I want updatInp to access event, data.base and the container attribut
advancedForm.useListenTempFields();
});
@ -271,7 +272,7 @@ onUnmounted(() => {
@inp="data.currPlugin = $event"
/>
</Filter>
<MessageUnmatch v-if="!advancedForm.templateUIFormat.length" />
<MessageUnmatch v-if="!Object.keys(advancedForm.templateUIFormat).length" />
<template v-for="(plugin, pluginId) in advancedForm.templateUIFormat">
<Container
data-is="content"
@ -301,15 +302,23 @@ onUnmounted(() => {
</Container>
</template>
<Button
v-if="advancedForm.templateUIFormat.length"
v-if="Object.keys(advancedForm.templateUIFormat).length"
v-bind="buttonSave"
:disabled="data.isReqErr || data.isRegErr ? true : false"
:disabled="
data.isReqErr || data.isRegErr
? true
: advancedForm.isUpdateData
? false
: true
"
@click="advancedForm.submit()"
/>
<div class="flex justify-center items-center" data-is="form-error">
<Text
v-if="
(advancedForm.templateUIFormat.length && data.isRegErr) ||
(advancedForm.templateUIFormat.length && data.isReqErr)
(Object.keys(advancedForm.templateUIFormat).length &&
data.isRegErr) ||
(Object.keys(advancedForm.templateUIFormat).length && data.isReqErr)
"
:text="
data.isReqErr

View file

@ -15,10 +15,10 @@ import { useRawForm } from "@store/form.js";
@example
{
"IS_LOADING": "no",
"NGINX_PREFIX": "/etc/nginx/",
"HTTP_PORT": "8080",
"HTTPS_PORT": "8443",
"MULTISITE": "yes"
"NGINX_PREFIX": "/etc/nginx/",
"HTTP_PORT": "8080",
"HTTPS_PORT": "8443",
"MULTISITE": "yes"
}
@param {object} template - Template object with plugin and settings data.
@param {string} containerClass - Container
@ -47,48 +47,48 @@ const props = defineProps({
});
const data = reactive({
str: "",
// Check if the raw data is valid when trying to revert from raw to JSON
// Case this is invalid, we will display an error message and disabled save button
// Case this is valid, we will store the JSON in the store and enable the save button
isValid: computed(() => {
// Transform to a possible valid JSON
let dataToCheck = data.str;
// Replace quotes "" with quotes ''
dataToCheck = dataToCheck.replace(/"/g, "'");
let isValidRaw = true;
let jsonReady = "";
// loop on each line
dataToCheck = dataToCheck.split("\n");
dataToCheck = dataToCheck.map((line) => {
// Get index of the first equal sign
const index = line.indexOf("=");
// Case no equal sign in a line, this is invalid
if (index === -1) isValidRaw = false;
// Update at this index with a colon
jsonReady +=
'"' + line.slice(0, index) + '":"' + line.slice(index + 1) + '",';
});
if (!isValidRaw) return false;
// Try to parse the JSON
jsonReady = "{" + jsonReady.slice(0, -1) + "}";
try {
const json = JSON.parse(jsonReady);
rawForm.setTemplate(json);
return true;
} catch (e) {
console.log(e);
return false;
}
}),
isValid: true,
});
function updateRaw(v) {
console.log("update");
// Transform to a possible valid JSON
rawForm.setRawData(v, true);
let dataToCheck = v;
// Replace quotes "" with quotes ''
dataToCheck = dataToCheck.replace(/"/g, "'");
let isValidRaw = true;
let jsonReady = "";
// loop on each line
dataToCheck = dataToCheck.split("\n");
dataToCheck = dataToCheck.map((line) => {
// Get index of the first equal sign
const index = line.indexOf("=");
// Case no equal sign in a line, this is invalid
if (index === -1) isValidRaw = false;
// Update at this index with a colon
jsonReady +=
'"' + line.slice(0, index) + '":"' + line.slice(index + 1) + '",';
});
if (!isValidRaw) return (data.isValid = false);
// Try to parse the JSON
jsonReady = "{" + jsonReady.slice(0, -1) + "}";
try {
const json = JSON.parse(jsonReady);
rawForm.setTemplate(json, true);
data.isValid = true;
} catch (e) {
console.log(e);
data.isValid = false;
}
}
function json2raw(json) {
let dataStr = JSON.stringify(json);
// Remove first and last curly brackets
@ -133,7 +133,7 @@ const buttonSave = {
};
onBeforeMount(() => {
data.str = json2raw(props.template);
rawForm.setRawData(json2raw(props.template));
});
</script>
@ -151,12 +151,16 @@ onBeforeMount(() => {
<Container class="form-raw-editor-container layout-settings">
<Editor
@inp="(v) => (data.str = v)"
@inp="(v) => updateRaw(v)"
v-bind="editorData"
:value="data.str"
:value="rawForm.rawData"
/>
</Container>
<Button :disabled="data.isValid ? false : true" v-bind="buttonSave" />
<Button
@click="rawForm.submit()"
:disabled="data.isValid ? false : rawForm.isUpdateData ? false : true"
v-bind="buttonSave"
/>
<div class="flex justify-center items-center" data-is="form-error">
<Text

File diff suppressed because one or more lines are too long

View file

@ -1,13 +1,15 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { useSubmitForm } from "@utils/form.js";
/**
@name createFormStore
@description This is a factory function that will create a form store.
This store contains all the logic to manage the form template and update it.
By defining the form type, this will update some function to avoid errors.
@param storeName - Name of the store, must be unique.
@param formType - Type of form, can be "advanced", "raw" or "easy".
@param {string} storeName - Name of the store, must be unique.
@param {string} formType - Type of form, can be "advanced", "raw" or "easy".
@returns {store} - Return a form store with all the logic to manage the form template and update it.
*/
export const createFormStore = (storeName, formType) => {
return defineStore(storeName, () => {
@ -22,33 +24,44 @@ export const createFormStore = (storeName, formType) => {
const templateUI = ref({});
// UI template will keep the data that will be render on UI with additionnal format like filtering.
const templateUIFormat = ref({});
// Store any raw information that can be usefull for the form.
const rawData = ref("");
// Increment when some functions are updating template to force rerendering when attach to a component using the reactive value.
const updateCount = ref(0);
// After a submit attempt or an event listener updating a template, check if a date is updating (different from default and previous value).
const isUpdateData = ref(false);
// Data we gonna submit
const formattedData = ref({});
/**
@name setTemplate
@description Set the template we are going to use to generate the form and update it (like adding multiples).
@param template - Template with plugins list and detail settings
@param {object} tempData - Template with plugins list and detail settings
@param {boolean} [force=false] - Force to update the template even if already set before
@returns {void}
*/
function setTemplate(template) {
function setTemplate(tempData, force = false) {
if (!_isFormTypeAllowed(["advanced", "easy", "raw"])) return;
if (Object.keys(template.value).length > 0 && !force) return;
// Unlink the template
template.value = JSON.parse(JSON.stringify(tempData));
templateBase.value = JSON.parse(JSON.stringify(tempData));
templateUI.value = JSON.parse(JSON.stringify(tempData));
templateUIFormat.value = templateUI.value;
_updateTempState();
}
const copyTemplate = JSON.parse(JSON.stringify(template));
template.value = copyTemplate;
templateBase.value = template;
templateUI.value = template;
templateUIFormat.value = template;
// console.log("template", type.value, template);
// console.log(typeof template);
// const formattedData = {};
// // Loop dict items
// for (const [key, value] of Object.entries(template)) {
// // Case key "value" is here, we are directly on the right level (and maybe on the raw mode)
// if (value?.value) {
// }
// console.log(key, value);
// formattedData[key] = value;
// if (!value?.settings || value?.multiples) continue;
// }
/**
@name setRawData
@description Set raw data that can be usefull for the form.
@param {array} data - Template with plugins list and detail settings
@param {boolean} [force=false] - Template with plugins list and detail settings
@returns {void}
*/
function setRawData(data, force = false) {
if (!_isFormTypeAllowed(["advanced", "easy", "raw"])) return;
if (rawData.value && !force) return;
rawData.value = data;
}
/**
@ -57,9 +70,10 @@ export const createFormStore = (storeName, formType) => {
The way the backend is working is that to delete a group, we need to send the group name with all default values.
This function needs to be call from the multiples component parent with the template and the group name to delete.
We will update the values of the group to default values.
@param pluginId - id of the plugin on the template array.
@param multName - Input id to update
@param groupName - Input value to update
@param {string} pluginId - id of the plugin on the template array.
@param {string} multName - Input id to update
@param {string|number} groupName - Input value to update
@returns {void}
*/
function delMultiple(pluginId, multName, groupName) {
if (!_isFormTypeAllowed(["advanced", "easy"])) return;
@ -79,6 +93,8 @@ export const createFormStore = (storeName, formType) => {
// For UI, we can delete the group to avoid rendering it
delete templateUI.value[index].multiples[multName][groupName];
updateCount.value++;
_updateTempState();
}
/**
@ -86,8 +102,9 @@ export const createFormStore = (storeName, formType) => {
@description This function will add a group of multiple in the template with default values.
Each plugin has a key "multiples_schema" with each multiples group and their default values.
We will retrieve the wanted multiple group and add it on the "multiples" key that contains the multiples that apply to the plugin.
@param pluginId - id of the plugin on the template array.
@param multName - multiple group name to add
@param {string} pluginId - id of the plugin on the template array.
@param {string} multName - multiple group name to add
@returns {void}
*/
function addMultiple(pluginId, multName) {
if (!_isFormTypeAllowed(["advanced", "easy"])) return;
@ -119,6 +136,7 @@ export const createFormStore = (storeName, formType) => {
// We need to show the new group on UI too
templateUI.value[index].multiples[multName][nextGroupId] = newMultiple;
updateCount.value++;
_updateTempState();
}
/**
@ -130,6 +148,7 @@ export const createFormStore = (storeName, formType) => {
if (!e.target.closest("[data-advanced-form-plugin]")) return;
_useUpdateTemp(e, data.base);
}
@returns {void}
*/
function useListenTempFields() {
if (!_isFormTypeAllowed(["advanced", "easy"])) return;
@ -147,6 +166,7 @@ export const createFormStore = (storeName, formType) => {
if (!e.target.closest("[data-advanced-form-plugin]")) return;
_useUpdateTemp(e, data.base);
}
@returns {void}
*/
function useUnlistenTempFields() {
if (!_isFormTypeAllowed(["advanced", "easy"])) return;
@ -172,6 +192,7 @@ export const createFormStore = (storeName, formType) => {
},
];
@param e - Event object, get it by default in the event listener.
@returns {void}
*/
function _useUpdateTemp(e) {
if (!_isFormTypeAllowed(["advanced", "easy"])) return;
@ -208,6 +229,7 @@ export const createFormStore = (storeName, formType) => {
// update settings
_useUpdateTempSettings(templates, inpId, inpValue, e.target);
_useUpdateTempMultiples(templates, inpId, inpValue, e.target);
_updateTempState();
}, 50);
}
@ -216,9 +238,10 @@ export const createFormStore = (storeName, formType) => {
@description This function will loop on template settings in order to update the setting value.
This will check each plugin.settings (what I call regular) instead of other type of settings like multiples (in plugin.multiples).
This function needs to be call in _useUpdateTemp.
@param templates - Templates array with plugins list and detail settings
@param inpId - Input id to update
@param inpValue - Input value to update
@param {array} templates - Templates array with plugins list and detail settings
@param {string|number} inpId - Input id to update
@param {string|number} inpValue - Input value to update
@returns {void}
*/
function _useUpdateTempSettings(templates, inpId, inpValue, target) {
if (!_isFormTypeAllowed(["advanced", "easy"])) return;
@ -252,9 +275,10 @@ export const createFormStore = (storeName, formType) => {
@description This function will loop on template multiples in order to update the setting value.
This will check each plugin.multiples that can be found in the template.
This function needs to be call in _useUpdateTemp.
@param templates - Templates array with plugins list and detail settings
@param inpId - Input id to update
@param inpValue - Input value to update
@param {array} templates - Templates array with plugins list and detail settings
@param {string|number} inpId - Input id to update
@param {string|number} inpValue - Input value to update
@returns {void}
*/
function _useUpdateTempMultiples(templates, inpId, inpValue, target) {
if (!_isFormTypeAllowed(["advanced", "easy"])) return;
@ -298,22 +322,101 @@ export const createFormStore = (storeName, formType) => {
}
/**
@name submitForm
@description This function will format the template base on the form type in order to render a form to submit.
The send data will change depending on the form type.
Case raw mode, we will send the raw data as it is.
Case easy / advanced mode, we will filter value to send only the needed one (enabled and not default).
After formatting, we will use the utils useSubmitForm from @utils/form to submit the form.
@name submit
@description Case we have at least one setting updating, we will allow to submit the form.
@returns {void}
*/
function submitForm() {
function submit() {
if (!_isFormTypeAllowed(["advanced", "easy", "raw"])) return;
console.log("submitForm");
const formattedData = {};
_updateTempState();
if (!isUpdateData.value) return;
return useSubmitForm(formattedData.value);
}
/**
@name _updateTempState
@description This function will run after a template update and will do two things :
1. Format the template to send needed data to the backend.
2. Check if at least one setting is updating. Case true, we will allow to submit the form.
@returns {void}
*/
function _updateTempState() {
formattedData.value = {};
// Loop dict items
for (const [key, value] of Object.entries(templateBase.value)) {
// Case we have a primitive value as value, we can stop here
if (typeof value !== "object") {
formattedData.value = templateBase.value;
break;
}
// Case no wanted keys, continue
if (!value?.settings && !value?.multiples) continue;
_getPluginSettingsValue(value, formattedData.value);
_getPluginMultiplesValue(value, formattedData.value);
}
isUpdateData.value = Object.keys(formattedData.value).length > 0;
}
/**
@name _getPluginMultiplesValue
@description Case we have a multiples key, we have a plugin object.
We will loop on each multiples settings and check if the value is different from the previous value in order to add it to the formattedData.
@returns {void}
*/
function _getPluginMultiplesValue(value) {
// Get multiples value
if (!value?.multiples) return;
for (const [multName, multGroups] of Object.entries(value.multiples)) {
for (const [groupName, group] of Object.entries(multGroups)) {
_checkSettingToAddValue(group, groupName);
}
}
}
/**
@name _getPluginSettingsValue
@description Case we have a settings key, we have a plugin object.
We will loop on each settings and check if the value is different from the previous value in order to add it to the formattedData.
@returns {void}
*/
function _getPluginSettingsValue(value) {
if (!value?.settings) return;
for (const [settName, setting] of Object.entries(value.settings)) {
_checkSettingToAddValue(setting, settName);
}
}
/**
@name _checkSettingToAddValue
@description Check if the setting value is different from the previous value in order to add it to the formattedData.
@returns {void}
*/
function _checkSettingToAddValue(setting, settingName) {
// Case current value is the same as previous, we don't need to send it
if (setting?.value === setting?.prev_value) return;
formattedData.value[settingName] = setting?.value;
}
/**
@name _isFormTypeAllowed
@description Set in on the top of other functions, this will get the function name that called it and check if the form type is allowed to execute this function.
Case a function is not register, we will not allow it.
@returns {boolean} - Return true if the form type is allowed to execute the function.
*/
function _isFormTypeAllowed(allowTypes) {
if (!allowTypes.includes(type.value)) return false;
return true;
}
/**
@name $reset
@description Will reset the template to the original one using the default template. The default template need to be set once.
@returns {void}
*/
function $reset() {
if (!_isFormTypeAllowed(["advanced", "easy", "raw"])) return;
@ -323,26 +426,18 @@ export const createFormStore = (storeName, formType) => {
updateCount.value++;
}
/**
@name _isFormTypeAllowed
@description Set in on the top of other functions, this will get the function name that called it and check if the form type is allowed to execute this function.
Case a function is not register, we will not allow it.
*/
function _isFormTypeAllowed(allowTypes) {
if (!allowTypes.includes(type.value)) return false;
return true;
}
return {
templateBase,
templateUI,
templateUIFormat,
rawData,
setTemplate,
setRawData,
addMultiple,
delMultiple,
useListenTempFields,
useUnlistenTempFields,
submitForm,
submit,
$reset,
};
});

View file

@ -5,6 +5,7 @@ import { ref } from "vue";
@name useBannerStore
@description Store to share the current banner state (visible or not).
This is useful to update components, specially fixed ones, related to the banner visibility.
@returns {object{boolean, string, function}} - Object with the banner state, banner class and function to set the banner visibility.
*/
export const useBannerStore = defineStore("banner", () => {
const isBanner = ref(true);
@ -22,6 +23,7 @@ export const useBannerStore = defineStore("banner", () => {
@name useReadonlyStore
@description Store to share the current readonly state (true or false).
This is useful to unable or enable some inputs or actions related to the readonly state.
@returns {object{boolean, function}} - Object with the readonly state and function to set the readonly state.
*/
export const useReadonlyStore = defineStore("readonly", () => {
const isReadOnly = ref(true);

View file

@ -36,6 +36,7 @@
];
@param {object} plugins - Object with the plugins data.
@param {array} filters - Array with the filters data.
@returns {array} - Array with the filtered data.
*/
function useFilter(items, filters) {
// loop on filters to determine types
@ -82,6 +83,7 @@ function useFilter(items, filters) {
},
];
@param filters - Array of filters to remove default filters
@returns {array} - Array of filters without default filters
*/
function removeDefaultFilters(filters) {
// Remove filters with type "select" and "all" as value
@ -113,6 +115,7 @@ function removeDefaultFilters(filters) {
}
@param filters - Array of filters
@param items - Array of items
@returns {boolean} - True if at least one key match with the filter value
*/
function isItemKeyword(filters, item) {
// Match if at least one match
@ -166,6 +169,7 @@ function isItemKeyword(filters, item) {
}
@param filters - Array of filters
@param items - Array of items
@returns {boolean} - True if at least one key match exactly the filter value
*/
function isItemSelect(filters, item) {
for (let j = 0; j < filters.length; j++) {

View file

@ -8,6 +8,7 @@
@name useForm
@description This function is a composable wrapper that contains all the form utils functions.
This function will for example look for JSON-type in the data-submit-form attribute of an element and submit the form with the data object.
@returns {void}
*/
function useForm() {
window.addEventListener("click", (e) => {
@ -31,6 +32,7 @@ function useForm() {
operation: "delete",
}
@param {object} data - Object with the form data to submit.
@returns {void}
*/
function useSubmitForm(data) {
// Create a form element
@ -73,6 +75,7 @@ function useSubmitForm(data) {
},
},
@param template - Template with plugins list and detail settings
@returns {array} - Array with error flags and error details
*/
function useCheckPluginsValidity(template) {
let isRegErr = false;
@ -108,4 +111,4 @@ function useCheckPluginsValidity(template) {
return [isRegErr, isReqErr, settingErr, settingNameErr, pluginErr, id];
}
export { useForm, useCheckPluginsValidity };
export { useForm, useSubmitForm, useCheckPluginsValidity };

View file

@ -10,6 +10,7 @@ import { v4 as uuidv4 } from "uuid";
@name useGlobal
@description This function is a wrapper that contains all the global utils functions.
This function handle global click and keydown events to manage some states like show/hide elements, focus modals, and close modals.
@returns {void}
*/
function useGlobal() {
setShowHideElA11y();
@ -38,6 +39,7 @@ function useGlobal() {
@name setShowHideElA11y
@description This function will check if aria-controls and aria-expanded attributes are present on elements that controls an element visibility.
Case they are not present, the function will create them.
@returns {void}
*/
function setShowHideElA11y() {
// Wait that elements are mounted and ids are set
@ -65,6 +67,7 @@ function setShowHideElA11y() {
<button data-close-el="modal">Close modal</button>
<div id="modal" class="">Modal content</div>
@param {Event} e - The event object.
@returns {void}
*/
function useHideEl(e) {
if (!e.target.hasAttribute("data-hide-el")) return;
@ -86,6 +89,7 @@ function useHideEl(e) {
<button data-show-el="modal">Open modal</button>
<div id="modal" class="hidden">Modal content</div>
@param {Event} e - The event object.
@returns {void}
*/
function useShowEl(e) {
if (!e.target.hasAttribute("data-show-el")) return;
@ -103,6 +107,7 @@ function useShowEl(e) {
If it's the case, the function will focus the element.
Case there is already a focused element inside the modal, avoid to focus it again.
@param {String} modalId - The id of the modal element.
@returns {void}
*/
function useFocusModal() {
setTimeout(() => {
@ -123,6 +128,7 @@ function useFocusModal() {
@name useCloseModal
@description This function check if a modal is present and will close it.
This is a shortcut to close a modal when the escape key is pressed, for example.
@returns {void}
*/
function useCloseModal() {
// Check if a data-modal element without hidden class is present
@ -136,6 +142,7 @@ function useCloseModal() {
@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.
@returns {boolean} - True if the element is hidden, false if not.
*/
function isElHidden(el) {
return el.hasAttribute("aria-hidden")
@ -159,6 +166,7 @@ function isElHidden(el) {
Adding random number to avoid duplicate uuids when some components are rendered at the same time.
We can pass a possible existing id, the function will only generate one if the id is empty.
@param {String} [id=""] - Possible existing id, check if it's empty to generate a new one.
@retrurns {String} - The unique identifier.
*/
function useUUID(id = "") {
if (id) return id;

View file

@ -16,6 +16,7 @@ const availablesLangs = ["en", "fr"];
/**
@name getAllLang
@description Return all the languages json data available in the application.
@returns {object} - Object with all the languages data.
*/
function getAllLang() {
return { en: en, fr: fr };
@ -26,6 +27,7 @@ function getAllLang() {
@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.
@returns {object} - Object with the languages data for the current page.
*/
function getAllLangCurrPage(pagesArr) {
const langs = getAllLang();
@ -47,6 +49,7 @@ function getAllLangCurrPage(pagesArr) {
@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.
@returns {object} - Object with the i18n object.
*/
function getI18n(pagesArr = []) {
const messages =
@ -73,6 +76,7 @@ function getI18n(pagesArr = []) {
/**
@name getLocalLang
@description This will return the user langage checking the store, the browser, or the default lang.
@returns {string} - The user langage.
*/
function getLocalLang() {
// get store lang, or local, or default

File diff suppressed because it is too large Load diff

View file

@ -3667,6 +3667,9 @@ def format_setting(
if setting_name in settings and not "multiple" in setting_value:
setting_value["value"] = settings[setting_name].get("value", setting_value.get("value", setting_value.get("default")))
setting_value["method"] = settings[setting_name].get("method", "ui")
# Add prev_value in order to check if value has changed to submit it
setting_value["prev_value"] = setting_value.get("value")
# Then override by service settings
if setting_name in settings:

File diff suppressed because one or more lines are too long

View file

@ -7,10 +7,10 @@
<link rel="stylesheet" href="css/flag-icons.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BunkerWeb | Global config</title>
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/global_config-BRF_V6yP.js"></script>
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/global_config-BRcItP4U.js"></script>
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Title-DELxfw1F.js">
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Text-Dad4BU7k.js">
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/form-BpBCT1YO.js">
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/form-DBYbte-L.js">
<link rel="stylesheet" crossorigin href="assets/global_config-D2kv0NCW.css">
</head>

View file

@ -7,9 +7,9 @@
<link rel="stylesheet" href="css/flag-icons.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BunkerWeb | Instances</title>
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/instances-DNq7VNrL.js"></script>
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/instances-DaTfTNdd.js"></script>
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Title-DELxfw1F.js">
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/form-BpBCT1YO.js">
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/form-DBYbte-L.js">
</head>
<body>