Add HL3D branding, Etsy Live/Draft paging and Projects lifecycle
Deploy HickLab3D Dashboard / Deploy to production (push) Failing after 23s
Deploy HickLab3D Dashboard / Deploy to production (push) Failing after 23s
Replace the old touchmark/PNG identity with the supplied lockup and monogram, segmented role-aware navigation, Live/Draft listing filters with generation-based cache invalidation, and admin Projects covering immutable file revisions, listing live pointers, and exact-decimal filament/packaging/shipping costing. Additive schema only; no Etsy publication. Isolated clone tests: 301 tests, 1162 assertions.
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
const costLines = document.querySelector('[data-cost-lines]');
|
||||
if (costLines) {
|
||||
const template = costLines.firstElementChild.cloneNode(true);
|
||||
let nextIndex = costLines.children.length;
|
||||
document.querySelector('[data-add-cost-line]')?.addEventListener('click', () => {
|
||||
if (costLines.children.length >= 50) { return; }
|
||||
const line = template.cloneNode(true);
|
||||
line.querySelector('legend').textContent = `Cost line ${nextIndex + 1}`;
|
||||
line.querySelectorAll('[name]').forEach((input) => {
|
||||
input.name = input.name.replace(/lines\[\d+\]/, `lines[${nextIndex}]`);
|
||||
if (input.tagName === 'INPUT') { input.value = input.name.includes('units_per_parcel') ? '1' : input.name.includes('waste_quantity') ? '0' : ''; }
|
||||
});
|
||||
nextIndex += 1;
|
||||
costLines.append(line);
|
||||
line.querySelector('select').focus();
|
||||
});
|
||||
costLines.addEventListener('click', (event) => {
|
||||
if (event.target.matches('[data-remove-cost-line]') && costLines.children.length > 1) { event.target.closest('[data-cost-line]').remove(); }
|
||||
});
|
||||
}
|
||||
|
||||
const sidebar = document.querySelector('[data-sidebar]');
|
||||
const sidebarToggle = document.querySelector('[data-sidebar-toggle]');
|
||||
|
||||
if (sidebar && sidebarToggle) {
|
||||
const collapsedKey = 'hl3d-sidebar-collapsed';
|
||||
|
||||
const applyCollapsed = (collapsed) => {
|
||||
sidebar.classList.toggle('lg:w-20', collapsed);
|
||||
sidebar.querySelectorAll('[data-sidebar-label]').forEach((label) => {
|
||||
label.classList.toggle('hidden', collapsed);
|
||||
});
|
||||
sidebar.querySelectorAll('[data-sidebar-expanded-mark]').forEach((mark) => {
|
||||
mark.classList.toggle('hidden', collapsed);
|
||||
});
|
||||
sidebar.querySelectorAll('[data-sidebar-collapsed-mark]').forEach((mark) => {
|
||||
mark.classList.toggle('hidden', !collapsed);
|
||||
});
|
||||
sidebarToggle.setAttribute('aria-pressed', collapsed ? 'true' : 'false');
|
||||
sidebarToggle.textContent = collapsed ? 'Expand' : 'Collapse';
|
||||
};
|
||||
|
||||
applyCollapsed(localStorage.getItem(collapsedKey) === '1');
|
||||
|
||||
sidebarToggle.addEventListener('click', () => {
|
||||
const collapsed = !sidebar.classList.contains('lg:w-20');
|
||||
localStorage.setItem(collapsedKey, collapsed ? '1' : '0');
|
||||
applyCollapsed(collapsed);
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-nav-group]').forEach((details) => {
|
||||
const storageKey = `hl3d-nav-group-${details.dataset.navGroup}`;
|
||||
const stored = localStorage.getItem(storageKey);
|
||||
|
||||
if (stored !== null) {
|
||||
details.open = stored === '1';
|
||||
}
|
||||
|
||||
details.addEventListener('toggle', () => {
|
||||
localStorage.setItem(storageKey, details.open ? '1' : '0');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user