Refactor Lucide icon rendering by replacing nodes with root across components and fix unintended character encoding issues in placeholders and text.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { api } from '../../lib/api.mjs';
|
||||
import { api } from '../../lib/api.mjs';
|
||||
|
||||
class WoChecklist extends HTMLElement {
|
||||
#woId = null;
|
||||
@@ -120,7 +120,7 @@ class WoChecklist extends HTMLElement {
|
||||
${allDone ? `
|
||||
<div class="all-done">
|
||||
<i data-lucide="check-circle-2" style="width:18px;height:18px;flex-shrink:0"></i>
|
||||
All ${total} steps complete — ready for review!
|
||||
All ${total} steps complete — ready for review!
|
||||
</div>` : ''}
|
||||
|
||||
<div class="progress-wrap">
|
||||
@@ -133,7 +133,7 @@ class WoChecklist extends HTMLElement {
|
||||
|
||||
<div class="steps-list">
|
||||
${total === 0
|
||||
? '<div class="empty-steps">No steps yet — add your first step below</div>'
|
||||
? '<div class="empty-steps">No steps yet — add your first step below</div>'
|
||||
: this.#steps.map(st => `
|
||||
<div class="step-row${st.completed ? ' done' : ''}">
|
||||
<div class="step-check-wrap">
|
||||
@@ -150,7 +150,7 @@ class WoChecklist extends HTMLElement {
|
||||
<div class="step-meta">
|
||||
<i data-lucide="user-check" style="width:12px;height:12px"></i>
|
||||
${this.#esc(st.completed_by)}
|
||||
${st.completed_at ? '· ' + new Date(st.completed_at).toLocaleString() : ''}
|
||||
${st.completed_at ? '· ' + new Date(st.completed_at).toLocaleString() : ''}
|
||||
</div>` : ''}
|
||||
${st.notes ? `<div class="step-note">"${this.#esc(st.notes)}"</div>` : ''}
|
||||
</div>
|
||||
@@ -169,14 +169,14 @@ class WoChecklist extends HTMLElement {
|
||||
<div class="add-section">
|
||||
<div class="add-section-label">Add Step</div>
|
||||
<div class="add-row">
|
||||
<input class="add-input" id="new-step-title" placeholder="Step title…" maxlength="200">
|
||||
<input class="add-input" id="new-step-title" placeholder="Step title…" maxlength="200">
|
||||
<button class="add-btn" id="add-step-btn">
|
||||
<i data-lucide="plus" style="width:14px;height:14px;vertical-align:middle"></i> Add
|
||||
</button>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
if (window.lucide) lucide.createIcons({ nodes: [s] });
|
||||
if (window.lucide) lucide.createIcons({ root: s });
|
||||
|
||||
s.querySelectorAll('.step-check').forEach(cb => {
|
||||
cb.addEventListener('change', () => {
|
||||
|
||||
@@ -43,7 +43,7 @@ class WoDetail extends HTMLElement {
|
||||
const s = this.shadowRoot;
|
||||
s.innerHTML = `<style>${this.#css()}</style>${this.#html()}`;
|
||||
this.#bind();
|
||||
if (window.lucide) lucide.createIcons({ nodes: [s] });
|
||||
if (window.lucide) lucide.createIcons({ root: s });
|
||||
}
|
||||
|
||||
#css() { return `
|
||||
@@ -297,7 +297,7 @@ class WoDetail extends HTMLElement {
|
||||
this.#tab = tab.dataset.tab;
|
||||
s.querySelectorAll('.tab').forEach(t => t.classList.toggle('active', t.dataset.tab === this.#tab));
|
||||
s.querySelector('#tab-content').innerHTML = this.#tabContent(this.#wo);
|
||||
if (window.lucide) lucide.createIcons({ nodes: [s.querySelector('#tab-content')] });
|
||||
if (window.lucide) lucide.createIcons({ root: s.querySelector('#tab-content') });
|
||||
}));
|
||||
|
||||
// Status dropdown
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { api } from '../../lib/api.mjs';
|
||||
import { api } from '../../lib/api.mjs';
|
||||
import { toLocalDatetime } from '../../lib/format.mjs';
|
||||
|
||||
const STATUSES = ['draft','assigned','scheduled','in_progress','pending_review','closed'];
|
||||
@@ -30,7 +30,7 @@ class WoForm extends HTMLElement {
|
||||
const s = this.shadowRoot;
|
||||
s.innerHTML = `<style>${this.#css()}</style>${this.#html(wo, isNew)}`;
|
||||
this.#bind();
|
||||
if (window.lucide) lucide.createIcons({ nodes: [s] });
|
||||
if (window.lucide) lucide.createIcons({ root: s });
|
||||
}
|
||||
|
||||
#css() { return `
|
||||
@@ -97,14 +97,14 @@ class WoForm extends HTMLElement {
|
||||
</button>` : ''}
|
||||
</div>
|
||||
<label>Title *<input name="title" required value="${v('title')}" placeholder="Brief description of the work"></label>
|
||||
<label>Description<textarea name="description" rows="3" placeholder="What needs to be done…">${v('description')}</textarea></label>
|
||||
<label>Instructions<textarea name="instructions" rows="5" placeholder="Step-by-step instructions for the field crew…">${v('instructions')}</textarea></label>
|
||||
<label>Description<textarea name="description" rows="3" placeholder="What needs to be done…">${v('description')}</textarea></label>
|
||||
<label>Instructions<textarea name="instructions" rows="5" placeholder="Step-by-step instructions for the field crew…">${v('instructions')}</textarea></label>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-title">Location</div>
|
||||
<label>Site Name<input name="site_name" value="${v('site_name')}" placeholder="e.g. Downtown Office"></label>
|
||||
<label>Address<input name="address" value="${v('address')}" placeholder="Street address"></label>
|
||||
<label>Access Notes<textarea name="access_notes" rows="2" placeholder="Gate codes, road conditions, parking info…">${v('access_notes')}</textarea></label>
|
||||
<label>Access Notes<textarea name="access_notes" rows="2" placeholder="Gate codes, road conditions, parking info…">${v('access_notes')}</textarea></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -164,8 +164,8 @@ class WoForm extends HTMLElement {
|
||||
<button class="lp-close" id="lp-close"><i data-lucide="x" style="width:16px;height:16px"></i></button>
|
||||
</div>
|
||||
<div class="lp-body">
|
||||
<input class="lp-search" id="lp-search" placeholder="Search profiles…" autocomplete="off">
|
||||
<div class="lp-list" id="lp-list"><div class="lp-empty">Loading…</div></div>
|
||||
<input class="lp-search" id="lp-search" placeholder="Search profiles…" autocomplete="off">
|
||||
<div class="lp-list" id="lp-list"><div class="lp-empty">Loading…</div></div>
|
||||
</div>
|
||||
<div class="lp-footer">
|
||||
<button class="btn btn-ghost" id="lp-cancel">Cancel</button>
|
||||
@@ -262,7 +262,7 @@ class WoForm extends HTMLElement {
|
||||
listEl.innerHTML = items.map(p => `
|
||||
<div class="lp-item${selectedProfile?.id === p.id ? ' selected' : ''}" data-id="${p.id}">
|
||||
<div class="lp-item-name">${this.#esc(p.name)}</div>
|
||||
<div class="lp-item-meta">${p.category ? `${p.category} · ` : ''}${p.step_count} step${p.step_count !== 1 ? 's' : ''} · ${p.default_priority} priority${p.default_duration_hours ? ` · ${p.default_duration_hours}h` : ''}</div>
|
||||
<div class="lp-item-meta">${p.category ? `${p.category} · ` : ''}${p.step_count} step${p.step_count !== 1 ? 's' : ''} · ${p.default_priority} priority${p.default_duration_hours ? ` · ${p.default_duration_hours}h` : ''}</div>
|
||||
</div>`).join('');
|
||||
listEl.querySelectorAll('.lp-item').forEach(item => {
|
||||
item.addEventListener('click', () => {
|
||||
@@ -277,7 +277,7 @@ class WoForm extends HTMLElement {
|
||||
selectedProfile = null;
|
||||
confirmBtn.disabled = true;
|
||||
searchEl.value = '';
|
||||
listEl.innerHTML = '<div class="lp-empty">Loading…</div>';
|
||||
listEl.innerHTML = '<div class="lp-empty">Loading…</div>';
|
||||
dialog.showModal();
|
||||
try {
|
||||
allProfiles = await api.get('/profiles') || [];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { api } from '../../lib/api.mjs';
|
||||
import { api } from '../../lib/api.mjs';
|
||||
import { formatDateTime } from '../../lib/format.mjs';
|
||||
|
||||
const STATUSES = ['draft','assigned','scheduled','in_progress','pending_review','closed'];
|
||||
@@ -38,7 +38,7 @@ class WoList extends HTMLElement {
|
||||
const s = this.shadowRoot;
|
||||
s.innerHTML = `<style>${this.#css()}</style>${this.#html()}`;
|
||||
this.#bind();
|
||||
if (window.lucide) lucide.createIcons({ nodes: [s] });
|
||||
if (window.lucide) lucide.createIcons({ root: s });
|
||||
}
|
||||
|
||||
#css() { return `
|
||||
@@ -139,7 +139,7 @@ class WoList extends HTMLElement {
|
||||
return `<div class="filter-bar">
|
||||
<div class="search-wrap">
|
||||
<i data-lucide="search"></i>
|
||||
<input class="search-input" id="search" type="search" placeholder="Search work orders…" value="${this.#filters.search}">
|
||||
<input class="search-input" id="search" type="search" placeholder="Search work orders…" value="${this.#filters.search}">
|
||||
</div>
|
||||
<select class="filter" id="filter-status">
|
||||
<option value="">All statuses</option>
|
||||
@@ -167,7 +167,7 @@ class WoList extends HTMLElement {
|
||||
<tr class="wo-row" data-id="${wo.id}">
|
||||
<td class="wo-num">${wo.wo_number}</td>
|
||||
<td class="wo-title">${this.#esc(wo.title)}</td>
|
||||
<td>${this.#esc(wo.site_name) || '<span style="color:var(--text-muted)">—</span>'}</td>
|
||||
<td>${this.#esc(wo.site_name) || '<span style="color:var(--text-muted)">—</span>'}</td>
|
||||
<td><ui-badge type="status" value="${wo.status}"></ui-badge></td>
|
||||
<td>
|
||||
<span class="priority-dot" style="background:${PRI_COLOR[wo.priority]}"></span>
|
||||
@@ -200,7 +200,7 @@ class WoList extends HTMLElement {
|
||||
</div>
|
||||
<ui-badge type="status" value="${wo.status}"></ui-badge>
|
||||
</div>
|
||||
<div class="card-meta">${this.#esc(wo.site_name) || '—'} · ${wo.steps_done}/${wo.step_count} steps</div>
|
||||
<div class="card-meta">${this.#esc(wo.site_name) || '—'} · ${wo.steps_done}/${wo.step_count} steps</div>
|
||||
</div>`).join('')}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class WoMap extends HTMLElement {
|
||||
</div>` : ''}
|
||||
</div>`;
|
||||
|
||||
if (window.lucide) lucide.createIcons({ nodes: [this] });
|
||||
if (window.lucide) lucide.createIcons({ root: this });
|
||||
|
||||
if (lat && lng) {
|
||||
this.#initMap(lat, lng, siteName, woNumber);
|
||||
|
||||
@@ -202,7 +202,7 @@ class WoPhotoPanel extends HTMLElement {
|
||||
</div>
|
||||
</dialog>`;
|
||||
|
||||
if (window.lucide) lucide.createIcons({ nodes: [s] });
|
||||
if (window.lucide) lucide.createIcons({ root: s });
|
||||
this.#bindEvents(filtered);
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ class WoPhotoPanel extends HTMLElement {
|
||||
preview.innerHTML = Array.from(files).map(f =>
|
||||
`<div class="preview-item"><i data-lucide="image" style="width:13px;height:13px"></i> ${this.#esc(f.name)}</div>`
|
||||
).join('');
|
||||
if (window.lucide) lucide.createIcons({ nodes: [preview] });
|
||||
if (window.lucide) lucide.createIcons({ root: preview });
|
||||
btn.disabled = files.length === 0;
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ class WoPhotoPanel extends HTMLElement {
|
||||
<button class="lightbox-next"><i data-lucide="chevron-right" style="width:20px;height:20px"></i></button>` : ''}
|
||||
${p.caption ? `<div class="lightbox-caption">${this.#esc(p.caption)}</div>` : ''}`;
|
||||
s.appendChild(lb);
|
||||
if (window.lucide) lucide.createIcons({ nodes: [lb] });
|
||||
if (window.lucide) lucide.createIcons({ root: lb });
|
||||
lb.querySelector('.lightbox-close').addEventListener('click', () => lb.remove());
|
||||
lb.addEventListener('click', e => { if (e.target === lb) lb.remove(); });
|
||||
lb.querySelector('.lightbox-prev')?.addEventListener('click', e => { e.stopPropagation(); show(i - 1); });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { api } from '../../lib/api.mjs';
|
||||
import { api } from '../../lib/api.mjs';
|
||||
|
||||
const SECTIONS = [
|
||||
{ type: 'person', label: 'People', icon: 'users', endpoint: 'people' },
|
||||
@@ -67,8 +67,8 @@ class WoResourcePanel extends HTMLElement {
|
||||
}
|
||||
|
||||
#resourceName(item, type) {
|
||||
if (type === 'vehicle') return `${item.unit_number}${item.description ? ' – ' + item.description : ''}`;
|
||||
return item.name || item.unit_number || '—';
|
||||
if (type === 'vehicle') return `${item.unit_number}${item.description ? ' – ' + item.description : ''}`;
|
||||
return item.name || item.unit_number || '—';
|
||||
}
|
||||
|
||||
#sectionHTML(section) {
|
||||
@@ -166,7 +166,7 @@ class WoResourcePanel extends HTMLElement {
|
||||
<button class="dialog-close" id="picker-close"><i data-lucide="x" style="width:16px;height:16px"></i></button>
|
||||
</div>
|
||||
<div class="dialog-body">
|
||||
<input class="picker-search" id="picker-search" placeholder="Search…" autocomplete="off">
|
||||
<input class="picker-search" id="picker-search" placeholder="Search…" autocomplete="off">
|
||||
<div class="picker-list" id="picker-list"></div>
|
||||
<div class="qty-row" id="qty-row" style="display:none">
|
||||
<label class="qty-label" for="qty-input">Quantity:</label>
|
||||
@@ -179,7 +179,7 @@ class WoResourcePanel extends HTMLElement {
|
||||
</div>
|
||||
</dialog>`;
|
||||
|
||||
if (window.lucide) lucide.createIcons({ nodes: [s] });
|
||||
if (window.lucide) lucide.createIcons({ root: s });
|
||||
this.#bindEvents();
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ class WoResourcePanel extends HTMLElement {
|
||||
|
||||
container.innerHTML = available.map(item => {
|
||||
const name = type === 'vehicle'
|
||||
? `${item.unit_number}${item.description ? ' – ' + item.description : ''}`
|
||||
? `${item.unit_number}${item.description ? ' – ' + item.description : ''}`
|
||||
: item.name;
|
||||
const sub = item.role || item.vehicle_type || item.category || (item.part_number ? `#${item.part_number}` : '') || '';
|
||||
return `
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { api } from '../../lib/api.mjs';
|
||||
import { api } from '../../lib/api.mjs';
|
||||
|
||||
const ACTION_ICONS = {
|
||||
status_change: 'refresh-cw',
|
||||
@@ -122,7 +122,7 @@ class WoTimeline extends HTMLElement {
|
||||
${this.#detailHTML(e)}
|
||||
<div class="entry-meta">
|
||||
<span class="entry-user">${this.#esc(e.performed_by)}</span>
|
||||
<span>·</span>
|
||||
<span>·</span>
|
||||
<span title="${new Date(e.performed_at).toLocaleString()}">${this.#relativeTime(e.performed_at)}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -134,7 +134,7 @@ class WoTimeline extends HTMLElement {
|
||||
<i data-lucide="rotate-cw" style="width:13px;height:13px"></i> Refresh
|
||||
</button>`;
|
||||
|
||||
if (window.lucide) lucide.createIcons({ nodes: [s] });
|
||||
if (window.lucide) lucide.createIcons({ root: s });
|
||||
s.querySelector('#refresh-btn').addEventListener('click', () => this.#load());
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ class WoTimeline extends HTMLElement {
|
||||
return `<div class="entry-detail">
|
||||
<span class="status-change">
|
||||
<span class="status-badge">${this.#esc(e.old_value.replace('_',' '))}</span>
|
||||
<span class="status-arrow">→</span>
|
||||
<span class="status-arrow">→</span>
|
||||
<span class="status-badge" style="background:var(--surface-2)">${this.#esc(e.new_value.replace('_',' '))}</span>
|
||||
</span>
|
||||
</div>`;
|
||||
|
||||
Reference in New Issue
Block a user