fix(freelancer): the 15-point rule is now one shared constant, in both forms

The edit form's legend read "Skill areas" and carried no directive, while the
application form read "Skill profile — 15 points to distribute" and said what to
do. Same rule, worded in two places — and the edit form lost the sentence.

- New static/focus-rule.js exposes window.O4PFocus: LEGEND, DIRECTIVE, TOTAL, MAX
  and message(sum). Both forms render the legend, the directive and the
  wrong-total message from it, so the two cannot drift apart again.
- Edit form: legend from the constant, the application form's directive under it,
  and the blocked-save message now names the direction.
- Application form: legend set from the constant, live warn and sum gate use the
  same message(); the all-zero gate keeps the directive.
- Message wording: over -> "remove N", under -> "add N", zero -> "place all 15
  points"; the total, aria-live, data-bad, the blocked save and the
  fieldset/legend structure are unchanged.

The rule text now appears once: "You have 17 of 15 — remove 2."
This commit is contained in:
webmaster committed 2026-11-03 18:34:43 +00:00
1 parent 1a1b62136f
commit 768dc1151a
3 files changed
+40 -22

No files matched your search

+9 -18
View File
@@ -1949,6 +1949,7 @@
</footer>
<script defer src="/static/lang.js"></script>
<script src="/static/focus-rule.js"></script>
<script>
/* ============================================================================
Odoo freelancer directory — page script.
@@ -1994,8 +1995,8 @@
// The five self-assessed focus axes — each a SEPARATE numeric field (0-5) on the
// wire, no CSV, no encoding. They must total exactly FOCUS_TOTAL.
var FOCUS_AXES = ["functional", "system", "modules", "integration", "ai"];
var FOCUS_TOTAL = 15;
var FOCUS_MAX = 5;
var FOCUS_TOTAL = O4PFocus.TOTAL; // the shared rule (static/focus-rule.js)
var FOCUS_MAX = O4PFocus.MAX;
var FOCUS_AXIS_LABELS = {
functional: "Functional", system: "System", modules: "Modules",
integration: "Integration", ai: "AI"
@@ -2089,22 +2090,17 @@
savable — an all-zero block is simply left unsent). */
function focusError(form, required) {
var sum = focusSum(form);
if (sum === 0) {
return required
? "Distribute all 15 points across the five areas — they cannot all be 0."
: "";
}
if (sum !== FOCUS_TOTAL) {
return "Your five focus numbers total " + sum + ". They must total exactly " +
FOCUS_TOTAL + ".";
}
return "";
if (sum === 0) return required ? O4PFocus.DIRECTIVE : "";
return O4PFocus.message(sum); // "" when right; names the direction when not
}
/* Wire one form's sliders: five <input type=range> with an adjacent <output>, a
live running total, and a live warning while the number is wrong. */
function initFocusUI(form, warnId, totalId) {
if (!form) return;
// The legend text comes from the shared rule (static/focus-rule.js), not the markup.
var legendEl = form.querySelector(".focus-field legend");
if (legendEl) legendEl.textContent = O4PFocus.LEGEND;
var wraps = form.querySelectorAll(".focus-field");
Array.prototype.forEach.call(wraps, function (w) {
var hidden = w.querySelector(".focus-sliders");
@@ -2125,12 +2121,7 @@
el.setAttribute("aria-valuetext", n + " of " + FOCUS_MAX);
});
if (total) total.textContent = sum + " of " + FOCUS_TOTAL + " points";
if (warn) {
warn.textContent = sum === FOCUS_TOTAL
? ""
: (sum === 0 ? "You have 0 of 15 — place all 15 points."
: "You have " + sum + " of 15. The five must total exactly 15.");
}
if (warn) warn.textContent = O4PFocus.message(sum);
}
Array.prototype.forEach.call(form.querySelectorAll(".focus-field input"), function (el) {
el.addEventListener("input", refresh);