diff --git a/docs/reference/file-map.md b/docs/reference/file-map.md index 5871c2a..c2ab155 100644 --- a/docs/reference/file-map.md +++ b/docs/reference/file-map.md @@ -301,6 +301,7 @@ Wave 9 adds these: ```bash python tests/export_check.py # CR-008/CR-017 - export walk + hours guard 20 checks python tests/sample_check.py # S7 - one sample affordance, confirmed+fenced 10 checks +python tests/icon_check.py # S6 - one icon system, no emoji, mapped 5 checks ``` **Three probes were re-pointed at `T7.1`.** `sections_check.py` 5b drove the live diff --git a/docs/reference/tokens.md b/docs/reference/tokens.md index 200de90..a0b5919 100644 --- a/docs/reference/tokens.md +++ b/docs/reference/tokens.md @@ -961,3 +961,51 @@ Column `#` is the line number in `html/theme-light.css`. Read down each column p **120 declarations.** Lines 3–116 are the Carbon g10 set; lines 170–175 are the app-shell group added by the suite. Many share a literal by design — that is Carbon's v10→v11 alias layer, not the `S5` defect. See the note at the end of §3. + +## Icons (S6 / T9.3) + +One system: **monochrome text-presentation glyphs**, chosen because the suite +is classic-script vanilla HTML with no bundler — an SVG sprite or icon font is +a new asset pipeline, while text glyphs render through the same font stack as +the words beside them. The trade accepted: text glyphs vary slightly by font, +but never flip into per-platform colour artwork the way emoji do, which is the +failure S6 names. **No emoji anywhere in the UI**; `tests/icon_check.py` +sweeps every page for emoji-range codepoints and the U+FE0F emoji-presentation +selector on every run. + +One meaning per glyph, one glyph per meaning: + +| Meaning | Glyph | Codepoint | Notes | +|---|---|---|---| +| done / ok / success | ✓ | U+2713 | replaced emoji ✅ | +| close / remove / failure | ✕ | U+2715 | replaced emoji ❌ | +| needs attention / warning | ⚠ | U+26A0 | text presentation | +| blocked / on hold | ⊘ | U+2298 | replaced emoji ⛔ | +| edit with a logged reason | ✎ | U+270E | replaced emoji 🔒; same meaning on sign-off overrides | +| revert / refresh | ↺ / ↻ | U+21BA / U+21BB | direction distinguishes undo from reload | +| settings / admin | ⚙ | U+2699 | replaced ⚡ on the side nav | +| export / download | ⤓ | U+2913 | | +| import / upload | ⤒ | U+2912 | | +| key point (help copy) | ◆ | U+25C6 | replaced emoji ⭐ | +| reference link | ▸ | U+25B8 | replaced emoji 📁 | +| info tooltip badge | ⓘ | U+24D8 | the `.help-tip` component (S8/T9.5) | +| back / navigation | ← ‹ › → | U+2190 U+2039 U+203A U+2192 | | +| views (nav) | ◔ ▤ ▦ ▧ | U+25D4 U+25A4 U+25A6 U+25A7 | one per view, never reused | +| drag handle | ⣿ | U+283F | sequence reordering | +| home | ⌂ | U+2302 | side nav | +| search | ⌕ | U+2315 | app bar | +| print / save PDF | ⎙ | U+2399 | | +| power / sign out | ⏻ | U+23FB | side nav | +| sort direction | ▲ ▼ | U+25B2 U+25BC | column headers, paired | +| in the QA queue | ◉ | U+25C9 | the CR-014 banner | +| clock / language & time | ◷ | U+25F7 | replaced ⌚ U+231A, which is emoji-presentation by default | +| menu (off-canvas) | ☰ | U+2630 | app bar | +| people / directory | ☺ | U+263A | side nav; text presentation | +| field view | ⚒ | U+2692 | side nav; text presentation | +| password / key | ⚿ | U+26BF | side nav | + +Dropped rather than mapped: ⚡ on action buttons (the label carries the +action), 📷 on “Add photo” (labelled), 📄/🖼 on file rows (the filename is the +label; a pictogram repeating “this is a file” said nothing). Every remaining +glyph sits beside visible text or carries an accessible name — none is the +only carrier of meaning. diff --git a/html/admin.js b/html/admin.js index dc20c26..7805658 100644 --- a/html/admin.js +++ b/html/admin.js @@ -36,13 +36,13 @@ async function checkHealth(){ b.className='banner'; b.textContent='Checking…'; const { status, json } = await api('GET','/api/health'); if(status===200 && json && json.ok){ - b.className='banner ok'; b.textContent='✅ API reachable — /api/health returned ok.'; + b.className='banner ok'; b.textContent='✓ API reachable — /api/health returned ok.'; } else if(status===404){ - b.className='banner bad'; b.textContent='❌ /api/ returns 404 — the reverse proxy is not routing /api/ to the API. The site loads but the API is unreachable from the browser.'; + b.className='banner bad'; b.textContent='✕ /api/ returns 404 — the reverse proxy is not routing /api/ to the API. The site loads but the API is unreachable from the browser.'; } else if(status===0){ - b.className='banner bad'; b.textContent='❌ Could not reach the server: '+json; + b.className='banner bad'; b.textContent='✕ Could not reach the server: '+json; } else { - b.className='banner bad'; b.textContent='❌ Unexpected response: HTTP '+status; + b.className='banner bad'; b.textContent='✕ Unexpected response: HTTP '+status; } } @@ -121,9 +121,9 @@ function stdConstraints(open){ return ['Safety & Permitting','Quality Control / async function seedDemo(){ const o=document.getElementById('demo-out'); o.innerHTML=''; let r = await api('GET','/api/health'); - if(!(r.status===200 && r.json && r.json.ok)){ demoLog('❌ API unreachable — fix /api/ routing first.'); return; } + if(!(r.status===200 && r.json && r.json.ok)){ demoLog('✕ API unreachable — fix /api/ routing first.'); return; } r = await api('POST','/api/projects',{name:'DEMO — Micron INC (test data)',number:'DEMO-001',client:'Micron Technology, Inc.',division:'Semiconductor',site:'Boise, ID — Fab',created_by:'admin-console'}); - if(r.status!==200){ demoLog('❌ create project failed (HTTP '+r.status+')'); return; } + if(r.status!==200){ demoLog('✕ create project failed (HTTP '+r.status+')'); return; } const pid=r.json.id; demoLog('Project created: '+r.json.name); r = await api('POST','/api/sops',{project_id:pid,name:'DEMO SOP',number:'DEMO-001',complete:true,data:{governance:{woFormat:'WP##-[Sector]-[TYPE]',disciplines:['Mechanical','Electrical','Tech'],discMode:'choice',instanceSuffix:'letter',woSize:'Standard — 3–5 days (≈40–80 hrs)',sizeHoursMax:'80'}}}); const sid=r.json && r.json.id; demoLog('SOP created (complete).'); @@ -142,7 +142,7 @@ async function seedDemo(){ await mk('WP05-3P-PANEL','3P panel install','Panel Install','Draft',{disciplines:['Electrical'],hours:'120',constraints:stdConstraints(['Schedule']),due:'2026-07-20'}); r = await api('GET','/api/wps/metrics?project_id='+pid); demoLog('\nMetrics (masters excluded): '+JSON.stringify(r.json)); - demoLog('\n✅ Done — "DEMO — Micron INC (test data)" now appears in the home picker.'); + demoLog('\n✓ Done — "DEMO — Micron INC (test data)" now appears in the home picker.'); snapshot(); } async function cleanDemo(){ @@ -151,11 +151,11 @@ async function cleanDemo(){ // archived=all, or an archived DEMO-/SMOKE- project becomes unreachable from // this button — the default list hides it and nothing else here can delete it. const r = await api('GET','/api/projects?archived=all'); - if(r.status!==200){ demoLog('❌ API unreachable (HTTP '+r.status+').'); return; } + if(r.status!==200){ demoLog('✕ API unreachable (HTTP '+r.status+').'); return; } const targets=(r.json||[]).filter(p=>/^(DEMO-|SMOKE-)/.test(String(p.number||''))); if(!targets.length){ demoLog('Nothing to remove.'); return; } for(const p of targets){ await api('DELETE','/api/projects/'+p.id); demoLog('Deleted: '+p.name+' ('+p.number+')'); } - demoLog('\n✅ Removed '+targets.length+' project(s).'); + demoLog('\n✓ Removed '+targets.length+' project(s).'); snapshot(); } @@ -175,14 +175,14 @@ async function loadProjects(){ const { status, json } = await api('GET','/api/projects?archived=all'); if(status===403){ banner.className='banner bad'; - banner.textContent='❌ Your account is not an admin, so you can’t archive or delete projects here.'; + banner.textContent='✕ Your account is not an admin, so you can’t archive or delete projects here.'; wrap.innerHTML=''; return; } if(status===401){ - banner.className='banner bad'; banner.textContent='❌ Not signed in. Reload and log in again.'; wrap.innerHTML=''; return; + banner.className='banner bad'; banner.textContent='✕ Not signed in. Reload and log in again.'; wrap.innerHTML=''; return; } if(status!==200 || !Array.isArray(json)){ - banner.className='banner bad'; banner.textContent='❌ Could not load projects (HTTP '+status+').'; wrap.innerHTML=''; return; + banner.className='banner bad'; banner.textContent='✕ Could not load projects (HTTP '+status+').'; wrap.innerHTML=''; return; } banner.style.display='none'; _adminProjects = json; @@ -284,14 +284,14 @@ async function loadDefaultMembers(){ const { status, json } = await api('GET','/api/auth/users'); if(status===403){ banner.className='banner bad'; - banner.textContent='❌ Your account is not an admin, so you can’t change who is added to new projects.'; + banner.textContent='✕ Your account is not an admin, so you can’t change who is added to new projects.'; wrap.innerHTML=''; return; } if(status===401){ - banner.className='banner bad'; banner.textContent='❌ Not signed in. Reload and log in again.'; wrap.innerHTML=''; return; + banner.className='banner bad'; banner.textContent='✕ Not signed in. Reload and log in again.'; wrap.innerHTML=''; return; } if(status!==200 || !Array.isArray(json)){ - banner.className='banner bad'; banner.textContent='❌ Could not load users (HTTP '+status+').'; wrap.innerHTML=''; return; + banner.className='banner bad'; banner.textContent='✕ Could not load users (HTTP '+status+').'; wrap.innerHTML=''; return; } banner.style.display='none'; _defMemUsers = json; @@ -560,7 +560,7 @@ async function saveLocalization(){ const m = document.getElementById('l10n-msg'); if(m){ m.textContent = 'Saved.'; m.style.color = 'var(--green)'; } } else { - msg.textContent = '❌ '+((json && json.detail) || ('HTTP '+status)); + msg.textContent = '✕ '+((json && json.detail) || ('HTTP '+status)); msg.style.color = 'var(--red)'; } } @@ -627,8 +627,8 @@ async function saveSettings(){ async function testEmail(){ const msg = document.getElementById('set-msg'); msg.textContent = 'Sending test…'; msg.style.color = 'var(--muted)'; const { status, json } = await api('POST','/api/settings/test-email', {}); - if(status===200) { msg.textContent = '✅ Test sent to '+((json&&json.to)||'you')+'.'; msg.style.color = 'var(--green)'; } - else { msg.textContent = '❌ '+((json && json.detail) || ('HTTP '+status)); msg.style.color = 'var(--red)'; } + if(status===200) { msg.textContent = '✓ Test sent to '+((json&&json.to)||'you')+'.'; msg.style.color = 'var(--green)'; } + else { msg.textContent = '✕ '+((json && json.detail) || ('HTTP '+status)); msg.style.color = 'var(--red)'; } } async function loadNotifications(){ const box = document.getElementById('notif-box'); if(!box) return; diff --git a/html/field.js b/html/field.js index 090edcf..53d9ad9 100644 --- a/html/field.js +++ b/html/field.js @@ -149,11 +149,11 @@ function renderDetail() { (((p.files) || []).length ? '

Drawings

' + p.files.map(function (f) { return '' + - '📄 ' + esc(f.name || 'drawing') + (f.description ? ' — ' + esc(f.description) : '') + ''; + '' + esc(f.name || 'drawing') + (f.description ? ' — ' + esc(f.description) : '') + ''; }).join('') + '
' : '') + '

Add field update

' + '' + - '
' + + '
' + '' + (pendingPhoto ? 'Photo attached ✓' : '') + '
' + '
' + '
' + diff --git a/html/help.js b/html/help.js index 8e23341..361dd5f 100644 --- a/html/help.js +++ b/html/help.js @@ -93,7 +93,7 @@
  • Dashboard — track status, hours, due dates, and what's gating each package across the project.
  • Moving around

    -

    From the home page, open SOP Configuration, the Work Package Creator, or the Dashboard. Inside the suite, switch any time using the top tabs: ⚙️ SOP Configuration, 📋 Work Package Creation, and 📊 Dashboard. The active project and SOP follow you across all of them.

    +

    From the home page, open SOP Configuration, the Work Package Creator, or the Dashboard. Inside the suite, switch any time using the top tabs: ⚙SOP Configuration, Work Package Creation, and Dashboard. The active project and SOP follow you across all of them.

    Quick start

    1. Open “SOP Configuration” and complete the 10 steps for your project (~15 minutes).
    2. @@ -101,7 +101,7 @@
    3. Open “Work Package Creation” to author packages with your SOP defaults pre-populated.
    4. Update from the field using the Field View, and leave feedback on any page with the Feedback button.
    -
    New here? On the home page choose the Sample Project, then click ⭐ Load sample in the suite to see a fully filled-out SOP and an example Work Package.
    ` }, +
    New here? On the home page choose the Sample Project, then click Load sample data in the suite to see a fully filled-out SOP and an example Work Package.
    ` }, { id: 'projects', title: 'Projects', body: `

    Projects

    @@ -133,7 +133,7 @@
  • Release Gate Constraints — choose which standard AWP constraints apply and add custom ones (see Constraints).
  • Engineering Sources & References — labelled links (Design Drawings, Specs, …) that appear as quick-access buttons in the WP Creator's Drawings & Attachments.
  • -
    Fields a WP inherits from the SOP show a "from SOP" tag and are locked. You can override a locked field with 🔒 Edit, which requires a logged reason.
    ` }, +
    Fields a WP inherits from the SOP show a "from SOP" tag and are locked. You can override a locked field with ✎ Edit, which requires a logged reason.
    ` }, { id: 'wps', title: 'Work Packages', body: `

    Creating Work Packages

    @@ -152,7 +152,7 @@
  • Quality / Hold Points, Approvals & Sign-offs, and Closeout (actual hours, as-builts, lessons learned — shown at QC/Closed).
  • Saving

    -

    Save Draft stores the package; ⚡ Save & View saves and renders the print-ready output. Drafts auto-save to your browser as you type, so nothing is lost if you close the tab.

    ` }, +

    Save Draft stores the package; Save & View saves and renders the print-ready output. Drafts auto-save to your browser as you type, so nothing is lost if you close the tab.

    ` }, { id: 'statuses', title: 'Statuses', body: `

    Work Package statuses

    @@ -223,7 +223,7 @@ { id: 'dashboard', title: 'Dashboard', body: `

    Dashboard & metrics

    -

    The dashboard aggregates every (non-master) package in the active project. Open it from the home page, the suite's 📊 Dashboard tab, or the Creator header.

    +

    The dashboard aggregates every (non-master) package in the active project. Open it from the home page, the suite's Dashboard tab, or the Creator header.

    Metric cards (click to filter)