An adversarial review (four lenses, every finding independently verified by two skeptics told to refute it) ran over2a5f6b3and8cf8c0f. Seven findings survived; all seven are fixed here. Against the C4 fix: - help.js: the nav hover was renamed onto its own surface token, keeping a no-op T9.9 had introduced (two different grays had been mapped to one name). Hover is now --cds-layer-hover, the token that exists for exactly this. - wp-creation-app.js: the drawer's critical CSS pre-painted --cds-layer-accent while the stylesheet paints --wp-nav-bg; now both paint --wp-nav-bg. Against D11: - wp-sections.js: the Assets toggle note still described the pre-D11 card ('Asset tags and controls.dev links') with a rationale the picker inverts. - runAssetSearch: the result cap counted contains-matches before the exact and prefix tiers finished, so 500 alphabetically-early substring hits could evict the exact match - and Enter then added the wrong asset, ID-locked. The cap now bounds each tier; the scan always sees the whole catalog. - addCatalogAsset: the one mutation in the section with no announced outcome was the successful pick. It now toasts (role=status), matching every sibling path (C1). - assets_db.py: failures are remembered for FAIL_CACHE_SECONDS (default 30s) and a stale catalog is served over an error, so a Micron outage costs one CONNECT_TIMEOUT per window instead of one per page load stacking up in the shared sync threadpool until login itself stalls. - assets_db.py: MICRON_ASSETS_CACHE_SECONDS='5m' no longer crashes the boot - a malformed knob on an OPTIONAL feature degrades to its default, loudly. assets_check grows four regressions for these (27 -> 31): per-tier cap against 600 decoys, the announced pick, boot with a malformed knob, and the stable cached 503. Battery: assets_check 31/31, color_check 5/5, sections_check ALL PASS. Items: C4, D11. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
158 lines
6.8 KiB
JavaScript
158 lines
6.8 KiB
JavaScript
/* Work package section toggles — CR-006 / T5.5.
|
||
---------------------------------------------------------------------------
|
||
The structural fix behind most of the removal requests in the plan. Rather
|
||
than deleting fields globally, each project turns on only the sections it
|
||
uses: it is what lets Micron drop Kitting and Assets while another project
|
||
keeps them, and it is why CR-002 and CR-016 are toggles rather than deletions.
|
||
|
||
THE ONE RULE: toggling a section OFF never deletes anything. It stops the
|
||
section rendering — in the creation form, in the detail view and in the PDF
|
||
export — and that is all. Whatever was captured stays on the package, and
|
||
toggling back on shows it again, intact. Everything in this file is about
|
||
what is DISPLAYED; nothing here writes to a package.
|
||
|
||
This list lives in its own file because three surfaces read it and they must
|
||
not drift: the SOP wizard renders the toggles, the creator applies them to
|
||
its form and its printed output, and a work package's detail view honours
|
||
them. A fourth copy is how "Assets is off" and "Assets is off, except in the
|
||
export" happen.
|
||
|
||
IDS ARE PERMANENT. They are written into every SOP that has ever been saved,
|
||
so renaming one silently turns that section back on for every existing
|
||
project. Change `label` freely; never change `id`.
|
||
*/
|
||
(function (window) {
|
||
'use strict';
|
||
|
||
var LIST = [
|
||
{ id: 'general', label: 'General Information',
|
||
note: 'Holds the WP number, subject and type. Turning this off leaves nothing to identify a package by — it is listed for completeness, not as a suggestion.' },
|
||
{ id: 'location', label: 'Location',
|
||
note: 'Where the work happens. CR-004 gives this its own structured fields; today it is the location field inside General Information.' },
|
||
{ id: 'scope', label: 'Scope of Work',
|
||
note: 'The ordered steps the crew performs, and the labour estimate.' },
|
||
{ id: 'assets', label: 'Assets',
|
||
note: 'Asset IDs picked read-only from the Micron DB (D11), with manual entry for anything not listed. Off for Micron EUV — the customer’s own database stays the source of truth; this section only references it (CR-016).' },
|
||
{ id: 'materials', label: 'Materials',
|
||
note: 'The bill of materials that feeds kitting.' },
|
||
{ id: 'kitting', label: 'Kitting',
|
||
note: 'Kitting status, warehouse owner and MIMO. Off for Micron EUV, which is not kitting today (CR-009).' },
|
||
{ id: 'drawings', label: 'Drawings and Attachments',
|
||
note: 'Drawing references and attachment links.' },
|
||
{ id: 'constraints', label: 'Constraints',
|
||
note: 'Release-readiness items. A package cannot be issued while one is open.' },
|
||
{ id: 'qaqc', label: 'QA/QC',
|
||
note: 'Quality requirements, photo standard and hold points.' },
|
||
{ id: 'closeout', label: 'Closeout',
|
||
note: 'Actual hours, installed quantity, redlines and lessons learned. Actual Hours stays here — its removal was proposed and rejected (CR-017).' },
|
||
];
|
||
|
||
var IDS = LIST.map(function (s) { return s.id; });
|
||
|
||
/* Individual fields that can be switched off inside a section — CR-002.
|
||
|
||
A second, narrower list rather than more sections, because a section is a
|
||
block of the document and these are two rows inside one. BL-000b asks
|
||
whether General Information wants per-field toggles generally; this is not
|
||
that. It is the two fields CR-002 names, expressed as toggles because
|
||
CLAUDE.md says removals are expressed through toggles and the data is
|
||
retained — the columns and the model stay exactly as they are.
|
||
|
||
Same rule as sections: an id is permanent, absent means ON. */
|
||
var FIELDS = [
|
||
{ id: 'costCode', section: 'general', label: 'Acumatica cost code',
|
||
note: 'Effectively constant on a job, so it is noise on a field work package (CR-002). The value stays on every package that has one.' },
|
||
{ id: 'acumaticaTask', section: 'general', label: 'Acumatica task',
|
||
note: 'A PM concern rather than a field one (CR-002). The cost visibility the team actually wants is by building and floor — CR-004 and CR-018.' },
|
||
];
|
||
|
||
var FIELD_IDS = FIELDS.map(function (f) { return f.id; });
|
||
|
||
function fieldDefaults() {
|
||
var out = {};
|
||
FIELD_IDS.forEach(function (id) { out[id] = true; });
|
||
return out;
|
||
}
|
||
|
||
function normalizeFields(stored) {
|
||
var out = fieldDefaults();
|
||
if (stored && typeof stored === 'object') {
|
||
FIELD_IDS.forEach(function (id) {
|
||
if (Object.prototype.hasOwnProperty.call(stored, id)) out[id] = stored[id] !== false;
|
||
});
|
||
}
|
||
return out;
|
||
}
|
||
|
||
function fieldsFor(sectionId) {
|
||
return FIELDS.filter(function (f) { return f.section === sectionId; });
|
||
}
|
||
|
||
function defaults() {
|
||
// A new SOP has everything on. A project opts OUT of what it does not use;
|
||
// it does not have to discover and opt in to what it does.
|
||
var out = {};
|
||
IDS.forEach(function (id) { out[id] = true; });
|
||
return out;
|
||
}
|
||
|
||
/* Fill in anything a stored SOP does not mention.
|
||
|
||
This is what makes adding an eleventh section safe: every SOP saved before
|
||
it existed says nothing about it, and "says nothing" has to mean ON. The
|
||
alternative — absent meaning off — would switch a brand-new section off for
|
||
every project in the estate the moment it shipped. */
|
||
function normalize(stored) {
|
||
var out = defaults();
|
||
if (stored && typeof stored === 'object') {
|
||
IDS.forEach(function (id) {
|
||
if (Object.prototype.hasOwnProperty.call(stored, id)) out[id] = stored[id] !== false;
|
||
});
|
||
}
|
||
return out;
|
||
}
|
||
|
||
function isOn(stored, id) {
|
||
if (IDS.indexOf(id) < 0) return true; // not a section we govern
|
||
return normalize(stored)[id];
|
||
}
|
||
|
||
function offList(stored) {
|
||
var s = normalize(stored);
|
||
return LIST.filter(function (x) { return !s[x.id]; }).map(function (x) { return x.label; });
|
||
}
|
||
|
||
/* A field is on only if its own toggle is on AND the section holding it is.
|
||
Asked as one question so no caller has to remember to ask both — a field
|
||
showing inside a hidden section is not a state anyone wants to reason
|
||
about. */
|
||
function fieldOn(sections, fields, id) {
|
||
var f = FIELDS.filter(function (x) { return x.id === id; })[0];
|
||
if (!f) return true;
|
||
if (!isOn(sections, f.section)) return false;
|
||
return normalizeFields(fields)[id];
|
||
}
|
||
|
||
function offFieldList(fields) {
|
||
var s = normalizeFields(fields);
|
||
return FIELDS.filter(function (x) { return !s[x.id]; }).map(function (x) { return x.label; });
|
||
}
|
||
|
||
window.WPSections = {
|
||
LIST: LIST,
|
||
IDS: IDS,
|
||
defaults: defaults,
|
||
normalize: normalize,
|
||
isOn: isOn,
|
||
offList: offList,
|
||
|
||
FIELDS: FIELDS,
|
||
FIELD_IDS: FIELD_IDS,
|
||
fieldDefaults: fieldDefaults,
|
||
normalizeFields: normalizeFields,
|
||
fieldsFor: fieldsFor,
|
||
fieldOn: fieldOn,
|
||
offFieldList: offFieldList,
|
||
};
|
||
})(window);
|