Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 19 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

113 changes: 77 additions & 36 deletions src/pages/tasking/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,46 @@ map.createPane('pane-tippy-top'); map.getPane('pane-tippy-top').style.zIndex = 7
map.createPane('pane-tippy-top-plus'); map.getPane('pane-tippy-top-plus').style.zIndex = 701;


var osm2 = new L.TileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { minZoom: 0, maxZoom: 13 });
new MiniMap(osm2, { toggleDisplay: true }).addTo(map);
function buildBasemapLayer(key) {
// --- NSW VECTOR BASEMAP (Topographic style) ---
if (key === "nsw-vector") {
return esriVector.vectorTileLayer(
"https://portal.spatial.nsw.gov.au/vectortileservices/rest/services/Hosted/NSW_BaseMap_VectorTile/VectorTileServer",
{
attribution: "© NSW Spatial Services"
}
);
}

// --- NSW RASTER BASEMAPS (tile MapServer) ---
if (key === "nsw-base") {
return L.tileLayer(
"https://maps.six.nsw.gov.au/arcgis/rest/services/public/NSW_Base_Map/MapServer/tile/{z}/{y}/{x}",
{
maxZoom: 20,
attribution: "© NSW Spatial Services"
}
);
}

if (key === "nsw-imagery") {
return L.tileLayer(
"https://maps.six.nsw.gov.au/arcgis/rest/services/public/NSW_Imagery/MapServer/tile/{z}/{y}/{x}",
{
maxZoom: 20,
attribution: "© NSW Spatial Services"
}
);
}

// --- EXISTING ESRI BASEMAPS ---
return esri.basemapLayer(key, { ignoreDeprecationWarning: true });
}


const minimapInitialKey = localStorage.getItem("map.base") || "Topographic";
var minimapLayer = buildBasemapLayer(minimapInitialKey) || buildBasemapLayer("Topographic");
const miniMapControl = new MiniMap(minimapLayer, { toggleDisplay: true }).addTo(map);

const legend = new LegendControl({ collapsed: true, persist: true });
legend.addTo(map);
Expand Down Expand Up @@ -3226,44 +3264,16 @@ function VM() {
this._currentBase = null;
}

// --- NSW VECTOR BASEMAP (Topographic style) ---
if (key === "nsw-vector") {
// Uses the NSW_BaseMap_VectorTile VectorTileServer
this._currentBase = esriVector.vectorTileLayer(
"https://portal.spatial.nsw.gov.au/vectortileservices/rest/services/Hosted/NSW_BaseMap_VectorTile/VectorTileServer",
{
attribution: "© NSW Spatial Services"
}
);
}

// --- NSW RASTER BASEMAPS (tile MapServer) ---
else if (key === "nsw-base") {
this._currentBase = L.tileLayer(
"https://maps.six.nsw.gov.au/arcgis/rest/services/public/NSW_Base_Map/MapServer/tile/{z}/{y}/{x}",
{
maxZoom: 20,
attribution: "© NSW Spatial Services"
}
);
} else if (key === "nsw-imagery") {
this._currentBase = L.tileLayer(
"https://maps.six.nsw.gov.au/arcgis/rest/services/public/NSW_Imagery/MapServer/tile/{z}/{y}/{x}",
{
maxZoom: 20,
attribution: "© NSW Spatial Services"
}
);
}

// --- EXISTING ESRI BASEMAPS ---
else {
this._currentBase = esri.basemapLayer(key, { ignoreDeprecationWarning: true });
}
this._currentBase = buildBasemapLayer(key);

if (this._currentBase) {
this._currentBase.addTo(map);
}

const nextMiniMapLayer = buildBasemapLayer(key);
if (nextMiniMapLayer) {
miniMapControl.changeLayer(nextMiniMapLayer);
}
}


Expand Down Expand Up @@ -3314,6 +3324,34 @@ function VM() {
}
});

// --- Settings button (visible in map-only mode to open config modal)
const MapSettingsControl = L.Control.extend({
options: { position: "topleft" },

onAdd(_map) {
const container = L.DomUtil.create("div", "leaflet-control map-settings-control leaflet-bar");
container.innerHTML = `
<button type="button" class="btn btn-light btn-sm shadow-sm" title="Open page config"
style="width:30px;height:30px;padding:0;display:flex;align-items:center;justify-content:center;">
<i class="fas fa-cog"></i>
</button>
`;

const btn = container.querySelector(".btn");
L.DomEvent.on(btn, "click", (ev) => {
L.DomEvent.stop(ev);
const modalEl = document.getElementById('configModal');
if (modalEl) {
const modal = bootstrap.Modal.getOrCreateInstance(modalEl);
modal.show();
}
});

L.DomEvent.disableClickPropagation(container);
return container;
}
});

// --- Zoom-to-fit button (sits directly below the zoom control)
const ZoomToFit = L.Control.extend({
options: { position: "topleft" },
Expand Down Expand Up @@ -3363,6 +3401,9 @@ function VM() {
const sidebarToggle = new SidebarToggle();
sidebarToggle.addTo(map);

const mapSettingsControl = new MapSettingsControl();
mapSettingsControl.addTo(map);

const layersDrawer = new LayersDrawer();
layersDrawer.addTo(map);
self.mapVM.layersDrawer = layersDrawer;
Expand Down
84 changes: 70 additions & 14 deletions src/pages/tasking/resize.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,80 @@ const sidebarEl = document.querySelector('.sidebar');
const vsplitEl = document.getElementById('vsplit');

const paneTopEl = document.getElementById('paneTop');
const paneBottomEl = document.getElementById('paneBottom');
const hsplitEl = document.getElementById('hsplit');

const LAYOUT_PRESETS = [
'map-right-teams-top',
'map-right-tasking-top',
'map-left-teams-top',
'map-left-tasking-top',
'map-right-teams-only',
'map-right-tasking-only',
'map-left-teams-only',
'map-left-tasking-only',
'map-only'
];
const DEFAULT_LAYOUT_PRESET = 'map-right-teams-top';
const normalizeLayoutPreset = (value) => {
const key = String(value || '').trim();
return LAYOUT_PRESETS.includes(key) ? key : DEFAULT_LAYOUT_PRESET;
};

// Leaflet map instance must exist; guard invalidateSize calls
// eslint-disable-next-line no-empty
function invalidateMap() { try { map.invalidateSize(); } catch(_) {} }

let currentLayoutPreset = normalizeLayoutPreset(localStorage.getItem('lh.layoutPreset'));

function layoutStorageKey(suffix) {
return `lh.layout.${currentLayoutPreset}.${suffix}`;
}

function applyLayoutPresetClass(preset) {
const normalized = normalizeLayoutPreset(preset);
currentLayoutPreset = normalized;
LAYOUT_PRESETS.forEach(layout => appEl.classList.remove(`layout-${layout}`));
appEl.classList.add(`layout-${normalized}`);
localStorage.setItem('lh.layoutPreset', normalized);
}

function topPaneForCurrentLayout() {
if (currentLayoutPreset.endsWith('tasking-top') || currentLayoutPreset.endsWith('tasking-only')) {
return paneBottomEl;
}
return paneTopEl;
}

function resetPaneHeights() {
paneTopEl.style.height = '';
paneBottomEl.style.height = '';
}

// ===== Restore persisted sizes on load =====
(function restoreSizes() {
function restoreSizes() {
resetPaneHeights();

// Left↔Right
const savedSidebarPx = Number(localStorage.getItem('lh.sidebarWidthPx')) || 800;
const savedSidebarPx = Number(localStorage.getItem(layoutStorageKey('sidebarWidthPx'))) || 800;
if (Number.isFinite(savedSidebarPx) && savedSidebarPx > 0) {
sidebarEl.style.width = savedSidebarPx + 'px';
appEl.style.setProperty('--sidebar-w', savedSidebarPx + 'px');
}

// Top↔Bottom
const savedTopPx = Number(localStorage.getItem('lh.paneTopHeightPx') || 500);
const savedTopPx = Number(localStorage.getItem(layoutStorageKey('paneTopHeightPx')) || 500);
if (Number.isFinite(savedTopPx) && savedTopPx > 0) {
paneTopEl.style.height = savedTopPx + 'px';
topPaneForCurrentLayout().style.height = savedTopPx + 'px';
appEl.style.setProperty('--pane-top-h', savedTopPx + 'px');
}

// Let layout settle then fix map size
setTimeout(invalidateMap, 0);
})();
}

applyLayoutPresetClass(currentLayoutPreset);
restoreSizes();

// ===== Left↔Right (vertical splitter) =====
let resizingLR = false, rafLR = 0;
Expand All @@ -50,17 +99,17 @@ function applyLR(clientX) {
appEl.style.setProperty('--sidebar-w', w + 'px');

const mapW = appRect.width - w - splitW;
localStorage.setItem('lh.sidebarWidthPx', String(w));
localStorage.setItem('lh.mapWidthPx', String(Math.max(0, Math.floor(mapW))));
localStorage.setItem(layoutStorageKey('sidebarWidthPx'), String(w));
localStorage.setItem(layoutStorageKey('mapWidthPx'), String(Math.max(0, Math.floor(mapW))));
}

function startLR() { resizingLR = true; vsplitEl.classList.add('resizing'); document.body.style.cursor = 'col-resize'; }
function startLR() { resizingLR = true; vsplitEl.classList.add('resizing'); document.body.classList.add('sidebar-resizing'); document.body.style.cursor = 'col-resize'; }
function moveLR(x) {
if (!resizingLR) return;
if (rafLR) return;
rafLR = requestAnimationFrame(() => { rafLR = 0; applyLR(x); invalidateMap(); });
}
function endLR() { if (!resizingLR) return; resizingLR = false; vsplitEl.classList.remove('resizing'); document.body.style.cursor=''; invalidateMap(); }
function endLR() { if (!resizingLR) return; resizingLR = false; vsplitEl.classList.remove('resizing'); document.body.classList.remove('sidebar-resizing'); document.body.style.cursor=''; invalidateMap(); }

if (vsplitEl) {
vsplitEl.addEventListener('mousedown', e => { e.preventDefault(); startLR(); });
Expand Down Expand Up @@ -88,8 +137,8 @@ function applyTB(clientY) {
appEl.style.setProperty('--pane-top-h', topH + 'px');

const botH = Math.max(minBot, total - splitH - topH);
localStorage.setItem('lh.paneTopHeightPx', String(Math.floor(topH)));
localStorage.setItem('lh.paneBottomHeightPx', String(Math.floor(botH)));
localStorage.setItem(layoutStorageKey('paneTopHeightPx'), String(Math.floor(topH)));
localStorage.setItem(layoutStorageKey('paneBottomHeightPx'), String(Math.floor(botH)));
}

function startTB() { resizingTB = true; hsplitEl.classList.add('resizing'); document.body.style.cursor = 'row-resize'; }
Expand Down Expand Up @@ -117,7 +166,7 @@ window.addEventListener('resize', () => {
const splitW = vsplitEl.getBoundingClientRect().width;
const minSidebar = 260, minMap = 260;
const maxSidebar = Math.min(appRect.width - splitW - minMap, appRect.width * 0.7);
const savedW = Number(localStorage.getItem('lh.sidebarWidthPx')) || sidebarEl.getBoundingClientRect().width;
const savedW = Number(localStorage.getItem(layoutStorageKey('sidebarWidthPx'))) || sidebarEl.getBoundingClientRect().width;
if (Number.isFinite(savedW) && savedW > 0) {
const clamped = Math.max(minSidebar, Math.min(savedW, maxSidebar));
sidebarEl.style.width = clamped + 'px';
Expand All @@ -129,14 +178,21 @@ window.addEventListener('resize', () => {
const splitH = hsplitEl.getBoundingClientRect().height;
const minTop = 120, minBot = 120;
const maxTop = Math.max(minTop, sbRect.height - splitH - minBot);
const savedTop = Number(localStorage.getItem('lh.paneTopHeightPx')) || paneTopEl.getBoundingClientRect().height;
const topPaneEl = topPaneForCurrentLayout();
const savedTop = Number(localStorage.getItem(layoutStorageKey('paneTopHeightPx'))) || topPaneEl.getBoundingClientRect().height;
if (Number.isFinite(savedTop) && savedTop > 0) {
const clampedTop = Math.max(minTop, Math.min(savedTop, maxTop));
paneTopEl.style.height = clampedTop + 'px';
topPaneEl.style.height = clampedTop + 'px';
appEl.style.setProperty('--pane-top-h', clampedTop + 'px');
}

// Map may need a relayout after width changes
setTimeout(invalidateMap, 0);
});

window.addEventListener('lh:layoutPresetChanged', (event) => {
const nextPreset = normalizeLayoutPreset(event?.detail?.preset);
applyLayoutPresetClass(nextPreset);
restoreSizes();
});
}
9 changes: 8 additions & 1 deletion src/pages/tasking/utils/jobTypesToUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export function jobsToUI(job) {
result.fillcolor = priorityStroke[p?.Name] || "#6b7280ff"; // default gray
}

result.shape = jobTypeParentCategoryShape[jobTypeParentCategoryKey(job)] || jobTypeParentCategoryShape.default; //shape is from the parent
result.shape = jobTypeShape[type]
|| jobTypeParentCategoryShape[jobTypeParentCategoryKey(job)]
|| jobTypeParentCategoryShape.default;

result.strokecolor = "#000000"; // default stroke color

Expand All @@ -35,6 +37,11 @@ const floodCatStroke = {
"Category5": "#16A34A", // Animal rescue
};

// Concrete job type overrides take precedence over parent-category shapes.
const jobTypeShape = {
"FR": "pentagon",
};

const jobTypeParentCategoryShape = {
"Storm": "circle",
"Support": "square",
Expand Down
Loading
Loading