diff --git a/setup/web_required_field_indicator/odoo/addons/web_required_field_indicator b/setup/web_required_field_indicator/odoo/addons/web_required_field_indicator new file mode 120000 index 000000000000..352a209ee384 --- /dev/null +++ b/setup/web_required_field_indicator/odoo/addons/web_required_field_indicator @@ -0,0 +1 @@ +../../../../web_required_field_indicator \ No newline at end of file diff --git a/setup/web_required_field_indicator/setup.py b/setup/web_required_field_indicator/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/web_required_field_indicator/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/web_required_field_indicator/README.rst b/web_required_field_indicator/README.rst new file mode 100644 index 000000000000..f4d31cc2dba1 --- /dev/null +++ b/web_required_field_indicator/README.rst @@ -0,0 +1,103 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +======================== +Required Field Indicator +======================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:61b97ab34b9c23d96ef14338c633cb98b7f337ccc4ad8b77fae895c78db4b2b7 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github + :target: https://github.com/OCA/web/tree/16.0/web_required_field_indicator + :alt: OCA/web +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_required_field_indicator + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +When a required field is left empty, Odoo shows a notification titled +"Invalid fields:", which is misleading: the field is not invalid, it is +simply not filled in. This module also makes it easier to locate such +fields when they are hidden inside a notebook page. + +Features +-------- + +- Renames the notification title from "Invalid fields:" to "Required + fields not filled:". +- Highlights, in red, the notebook tab of any page that contains a + required field that is not filled in, the same way the field itself is + highlighted. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +No configuration is needed. Once installed, try to save a form with an +empty required field: + +- The notification that pops up now reads "Required fields not filled:" + instead of "Invalid fields:". +- If the empty required field is on a notebook page other than the one + currently open, that page's tab turns red so it is easy to spot. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Bruno Corredato Botti + +Contributors +------------ + +- Bruno Corredato Botti + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/web `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/web_required_field_indicator/__init__.py b/web_required_field_indicator/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/web_required_field_indicator/__manifest__.py b/web_required_field_indicator/__manifest__.py new file mode 100644 index 000000000000..41451d43d0f0 --- /dev/null +++ b/web_required_field_indicator/__manifest__.py @@ -0,0 +1,22 @@ +# Copyright 2026 Bruno Corredato Botti +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Required Field Indicator", + "summary": "Clearer wording and notebook page highlighting for unfilled " + "required fields", + "version": "16.0.1.0.0", + "author": "Bruno Corredato Botti, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/web", + "license": "AGPL-3", + "category": "Usability", + "depends": ["web"], + "installable": True, + "assets": { + "web.assets_backend": [ + "web_required_field_indicator/static/src/scss/" + "notebook_required_field_indicator.scss", + "web_required_field_indicator/static/src/js/**/*", + ], + }, +} diff --git a/web_required_field_indicator/i18n/pt_BR.po b/web_required_field_indicator/i18n/pt_BR.po new file mode 100644 index 000000000000..eb70248c3545 --- /dev/null +++ b/web_required_field_indicator/i18n/pt_BR.po @@ -0,0 +1,20 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_required_field_indicator +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: web_required_field_indicator +#. odoo-javascript +#: code:addons/web_required_field_indicator/static/src/js/record_invalid_fields_notification.esm.js:0 +#, python-format +msgid "Required fields not filled: " +msgstr "Campos obrigatórios não preenchidos: " diff --git a/web_required_field_indicator/i18n/web_required_field_indicator.pot b/web_required_field_indicator/i18n/web_required_field_indicator.pot new file mode 100644 index 000000000000..54a4894931dc --- /dev/null +++ b/web_required_field_indicator/i18n/web_required_field_indicator.pot @@ -0,0 +1,21 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_required_field_indicator +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: web_required_field_indicator +#. odoo-javascript +#: code:addons/web_required_field_indicator/static/src/js/record_invalid_fields_notification.esm.js:0 +#, python-format +msgid "Required fields not filled: " +msgstr "" diff --git a/web_required_field_indicator/readme/CONTRIBUTORS.md b/web_required_field_indicator/readme/CONTRIBUTORS.md new file mode 100644 index 000000000000..62befa932a37 --- /dev/null +++ b/web_required_field_indicator/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Bruno Corredato Botti diff --git a/web_required_field_indicator/readme/DESCRIPTION.md b/web_required_field_indicator/readme/DESCRIPTION.md new file mode 100644 index 000000000000..dedd3ecef4c5 --- /dev/null +++ b/web_required_field_indicator/readme/DESCRIPTION.md @@ -0,0 +1,12 @@ +When a required field is left empty, Odoo shows a notification titled +"Invalid fields:", which is misleading: the field is not invalid, it is +simply not filled in. This module also makes it easier to locate such +fields when they are hidden inside a notebook page. + +## Features + +- Renames the notification title from "Invalid fields:" to "Required + fields not filled:". +- Highlights, in red, the notebook tab of any page that contains a + required field that is not filled in, the same way the field itself + is highlighted. diff --git a/web_required_field_indicator/readme/USAGE.md b/web_required_field_indicator/readme/USAGE.md new file mode 100644 index 000000000000..495310a02d0a --- /dev/null +++ b/web_required_field_indicator/readme/USAGE.md @@ -0,0 +1,7 @@ +No configuration is needed. Once installed, try to save a form with an +empty required field: + +- The notification that pops up now reads "Required fields not + filled:" instead of "Invalid fields:". +- If the empty required field is on a notebook page other than the one + currently open, that page's tab turns red so it is easy to spot. diff --git a/web_required_field_indicator/static/description/icon.png b/web_required_field_indicator/static/description/icon.png new file mode 100644 index 000000000000..3a0328b516c4 Binary files /dev/null and b/web_required_field_indicator/static/description/icon.png differ diff --git a/web_required_field_indicator/static/description/index.html b/web_required_field_indicator/static/description/index.html new file mode 100644 index 000000000000..3601e28d69e0 --- /dev/null +++ b/web_required_field_indicator/static/description/index.html @@ -0,0 +1,449 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Required Field Indicator

+ +

Beta License: AGPL-3 OCA/web Translate me on Weblate Try me on Runboat

+

When a required field is left empty, Odoo shows a notification titled +“Invalid fields:”, which is misleading: the field is not invalid, it is +simply not filled in. This module also makes it easier to locate such +fields when they are hidden inside a notebook page.

+
+

Features

+
    +
  • Renames the notification title from “Invalid fields:” to “Required +fields not filled:”.
  • +
  • Highlights, in red, the notebook tab of any page that contains a +required field that is not filled in, the same way the field itself is +highlighted.
  • +
+

Table of contents

+ +
+

Usage

+

No configuration is needed. Once installed, try to save a form with an +empty required field:

+
    +
  • The notification that pops up now reads “Required fields not filled:” +instead of “Invalid fields:”.
  • +
  • If the empty required field is on a notebook page other than the one +currently open, that page’s tab turns red so it is easy to spot.
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+ +
+
+

Authors

+
    +
  • Bruno Corredato Botti
  • +
+
+
+

Contributors

+
    +
  • Bruno Corredato Botti
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/web project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/web_required_field_indicator/static/src/js/form_compiler.esm.js b/web_required_field_indicator/static/src/js/form_compiler.esm.js new file mode 100644 index 000000000000..a41a39e95e3e --- /dev/null +++ b/web_required_field_indicator/static/src/js/form_compiler.esm.js @@ -0,0 +1,65 @@ +/** @odoo-module **/ + +import {FormCompiler} from "@web/views/form/form_compiler"; +import {getModifier} from "@web/views/view_compiler"; +import {getTag} from "@web/core/utils/xml"; +import {patch} from "@web/core/utils/patch"; + +const INVALID_PAGE_CLASS = "o_web_required_field_indicator_page_invalid"; + +// Collects the names of the fields that belong directly to a , so +// their validity can be checked at render time. Fields nested inside +// another (x2many list/kanban columns) are skipped: they belong +// to sub-records, not to the main record whose validity we are checking. +function collectPageFieldNames(pageEl) { + const fieldNames = new Set(); + for (const fieldEl of pageEl.querySelectorAll("field[name]")) { + let ancestor = fieldEl.parentElement; + let nested = false; + while (ancestor && ancestor !== pageEl) { + if (getTag(ancestor, true) === "field") { + nested = true; + break; + } + ancestor = ancestor.parentElement; + } + if (!nested) { + fieldNames.add(fieldEl.getAttribute("name")); + } + } + return [...fieldNames]; +} + +patch(FormCompiler.prototype, "web_required_field_indicator.FormCompiler", { + compileNotebook(el, params) { + const noteBook = this._super(el, params); + // Same filtering as the original compileNotebook, so this + // list lines up 1-to-1 with the children it produced. + const pageElements = [...el.children].filter((child) => { + if (getTag(child, true) !== "page") { + return false; + } + const invisible = getModifier(child, "invisible"); + return !this.isAlwaysInvisible(invisible, params); + }); + const pageSlots = [...noteBook.children]; + pageElements.forEach((pageEl, index) => { + const pageSlot = pageSlots[index]; + const fieldNames = collectPageFieldNames(pageEl); + if (!pageSlot || !fieldNames.length) { + return; + } + const invalidExpr = `(${JSON.stringify( + fieldNames + )}.some((fname) => props.record.isInvalid(fname)) ? "${INVALID_PAGE_CLASS}" : "")`; + const existingClassName = pageSlot.getAttribute("className"); + pageSlot.setAttribute( + "className", + existingClassName + ? `((${existingClassName}) + " " + ${invalidExpr})` + : invalidExpr + ); + }); + return noteBook; + }, +}); diff --git a/web_required_field_indicator/static/src/js/record_invalid_fields_notification.esm.js b/web_required_field_indicator/static/src/js/record_invalid_fields_notification.esm.js new file mode 100644 index 000000000000..9bce8ac1c98a --- /dev/null +++ b/web_required_field_indicator/static/src/js/record_invalid_fields_notification.esm.js @@ -0,0 +1,28 @@ +/** @odoo-module **/ + +import {Record} from "@web/views/basic_relational_model"; +import {escape} from "@web/core/utils/strings"; +import {markup} from "@odoo/owl"; +import {patch} from "@web/core/utils/patch"; + +// Core hardcodes the notification title as "Invalid fields: ", which is +// misleading: an empty required field is not invalid, it is simply +// unfilled. There is no hook to override only the title, so the method is +// replicated here with corrected wording (odoo/addons/web/static/src/views +// /basic_relational_model.js, Record.openInvalidFieldsNotification). +patch(Record.prototype, "web_required_field_indicator.Record", { + openInvalidFieldsNotification() { + if (this._invalidFields.size) { + const invalidFields = [...this._invalidFields].map((fieldName) => { + return `
  • ${escape(this.fields[fieldName].string || fieldName)}
  • `; + }, this); + this._closeInvalidFieldsNotification = this.model.notificationService.add( + markup(`
      ${invalidFields.join("")}
    `), + { + title: this.model.env._t("Required fields not filled: "), + type: "danger", + } + ); + } + }, +}); diff --git a/web_required_field_indicator/static/src/scss/notebook_required_field_indicator.scss b/web_required_field_indicator/static/src/scss/notebook_required_field_indicator.scss new file mode 100644 index 000000000000..7781259e582b --- /dev/null +++ b/web_required_field_indicator/static/src/scss/notebook_required_field_indicator.scss @@ -0,0 +1,4 @@ +.o_notebook_headers .nav-link.o_web_required_field_indicator_page_invalid { + color: map-get($theme-colors, "danger") !important; + border-bottom-color: map-get($theme-colors, "danger"); +}