Compare commits

..

3 Commits

+134 -71
View File
@@ -579,39 +579,53 @@ for ($h = $startHour; $h <= $endHour; $h++) {
default => 'lg:col-span-6' default => 'lg:col-span-6'
}; };
?> ?>
<div class="<?= $colClass ?> col-span-1 min-w-0 transition-all fociz-widget-wrapper group/widget" data-widget-id="<?= htmlspecialchars($wId) ?>" data-col-span="<?= $span ?>" id="widgetWrapper_<?= htmlspecialchars($wId) ?>"> <div class="<?= $colClass ?> col-span-1 min-w-0 transition-all duration-300 fociz-widget-wrapper group/widget" data-widget-id="<?= htmlspecialchars($wId) ?>" data-col-span="<?= $span ?>" id="widgetWrapper_<?= htmlspecialchars($wId) ?>">
<!-- Standardized In-Widget Control Toolbar --> <!-- Bovenste rand van de widget: Prachtig gestyled en direct geïntegreerd in de widget -->
<div class="widget-toolbar flex items-center justify-between px-3 py-1.5 mb-2 rounded-xl bg-slate-100/80 dark:bg-[#0c0f17]/80 border border-slate-200/70 dark:border-slate-800/70 backdrop-blur-xs select-none shadow-2xs"> <div class="widget-header-bar flex items-center justify-between px-3.5 py-1.5 bg-slate-50/90 dark:bg-[#0c0f17]/90 border border-b-0 border-slate-200/80 dark:border-slate-800/80 rounded-t-2xl select-none backdrop-blur-xs transition-colors group-hover/widget:border-indigo-400/50 dark:group-hover/widget:border-indigo-500/30">
<div class="flex items-center space-x-2 min-w-0"> <!-- Links: Drag Handle Grip + Widget Titel -->
<div class="widget-drag-handle flex items-center space-x-1.5 px-2 py-1 rounded-lg cursor-grab active:cursor-grabbing text-slate-400 hover:text-indigo-600 dark:hover:text-indigo-400 hover:bg-slate-200/60 dark:hover:bg-slate-800/80 transition" title="Sleep om te verplaatsen" draggable="true" data-widget-id="<?= htmlspecialchars($wId) ?>"> <div class="widget-drag-handle flex items-center space-x-1.5 cursor-grab active:cursor-grabbing text-slate-400 hover:text-indigo-600 dark:hover:text-indigo-400 py-0.5 px-1 -ml-1 rounded-lg hover:bg-slate-200/50 dark:hover:bg-slate-800/50 transition group/drag" draggable="true" data-widget-id="<?= htmlspecialchars($wId) ?>" title="Sleep om te verplaatsen">
<i data-lucide="grip-vertical" class="w-3.5 h-3.5 flex-shrink-0"></i> <i data-lucide="grip-vertical" class="w-3.5 h-3.5 text-slate-400 group-hover/drag:text-indigo-500 transition-colors flex-shrink-0"></i>
<span class="text-xs font-bold text-slate-700 dark:text-slate-200 truncate max-w-[130px] sm:max-w-[200px]"><?= htmlspecialchars($widget['title']) ?></span> <span class="text-xs font-bold text-slate-700 dark:text-slate-300 tracking-wide truncate max-w-[160px] sm:max-w-[240px]"><?= htmlspecialchars($widget['title']) ?></span>
</div>
</div> </div>
<div class="flex items-center space-x-1 sm:space-x-1.5 flex-shrink-0"> <!-- Rechts: Tandwieltje (Formaat & Verbergen) & Inklappen -->
<!-- Breedte Knoppen (Inline Resizing) --> <div class="flex items-center space-x-1 relative">
<div class="widget-size-picker inline-flex items-center rounded-lg bg-white dark:bg-[#111624] border border-slate-200 dark:border-slate-800 p-0.5 shadow-2xs"> <!-- Tandwieltje Knop met Dropdown -->
<button type="button" onclick="setWidgetSpan('<?= $wId ?>', 4)" class="widget-span-btn px-1.5 py-0.5 rounded text-[10px] font-bold transition <?= $span == 4 ? 'bg-indigo-600 text-white shadow-2xs' : 'text-slate-500 hover:text-slate-800 dark:hover:text-slate-200' ?>" data-span="4" title="1/3 breedte (4 kolommen)">1/3</button> <div class="relative">
<button type="button" onclick="setWidgetSpan('<?= $wId ?>', 6)" class="widget-span-btn px-1.5 py-0.5 rounded text-[10px] font-bold transition <?= $span == 6 ? 'bg-indigo-600 text-white shadow-2xs' : 'text-slate-500 hover:text-slate-800 dark:hover:text-slate-200' ?>" data-span="6" title="1/2 breedte (6 kolommen)">1/2</button> <button type="button" onclick="toggleWidgetSettingsMenu('<?= $wId ?>', event)" class="widget-settings-btn p-1 rounded-lg text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 hover:bg-slate-200/60 dark:hover:bg-slate-800/60 transition cursor-pointer" title="Widget instellingen (formaat en verbergen)">
<button type="button" onclick="setWidgetSpan('<?= $wId ?>', 8)" class="widget-span-btn px-1.5 py-0.5 rounded text-[10px] font-bold transition <?= $span == 8 ? 'bg-indigo-600 text-white shadow-2xs' : 'text-slate-500 hover:text-slate-800 dark:hover:text-slate-200' ?>" data-span="8" title="2/3 breedte (8 kolommen)">2/3</button> <i data-lucide="settings" class="w-3.5 h-3.5"></i>
<button type="button" onclick="setWidgetSpan('<?= $wId ?>', 12)" class="widget-span-btn px-1.5 py-0.5 rounded text-[10px] font-bold transition <?= $span == 12 ? 'bg-indigo-600 text-white shadow-2xs' : 'text-slate-500 hover:text-slate-800 dark:hover:text-slate-200' ?>" data-span="12" title="Volledige breedte (12 kolommen)">Vol</button> </button>
<!-- Dropdown Popover Menu -->
<div id="widgetSettingsMenu_<?= htmlspecialchars($wId) ?>" class="widget-settings-dropdown hidden absolute right-0 top-full mt-1.5 z-50 w-52 p-2.5 rounded-2xl bg-white/95 dark:bg-[#111624]/95 backdrop-blur-md border border-slate-200 dark:border-slate-800 shadow-2xl space-y-2.5">
<div>
<span class="text-[10px] font-bold uppercase tracking-wider text-slate-400 block mb-1.5">Breedte instellen</span>
<div class="grid grid-cols-4 gap-1">
<button type="button" onclick="setWidgetSpan('<?= $wId ?>', 4)" class="widget-span-btn py-1 text-center text-[11px] font-bold rounded-lg border transition cursor-pointer <?= $span == 4 ? 'bg-indigo-600 border-indigo-600 text-white shadow-2xs' : 'bg-slate-50 dark:bg-slate-800/80 border-slate-200 dark:border-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700' ?>" data-span="4" title="1/3 breedte (4 kolommen)">1/3</button>
<button type="button" onclick="setWidgetSpan('<?= $wId ?>', 6)" class="widget-span-btn py-1 text-center text-[11px] font-bold rounded-lg border transition cursor-pointer <?= $span == 6 ? 'bg-indigo-600 border-indigo-600 text-white shadow-2xs' : 'bg-slate-50 dark:bg-slate-800/80 border-slate-200 dark:border-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700' ?>" data-span="6" title="1/2 breedte (6 kolommen)">1/2</button>
<button type="button" onclick="setWidgetSpan('<?= $wId ?>', 8)" class="widget-span-btn py-1 text-center text-[11px] font-bold rounded-lg border transition cursor-pointer <?= $span == 8 ? 'bg-indigo-600 border-indigo-600 text-white shadow-2xs' : 'bg-slate-50 dark:bg-slate-800/80 border-slate-200 dark:border-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700' ?>" data-span="8" title="2/3 breedte (8 kolommen)">2/3</button>
<button type="button" onclick="setWidgetSpan('<?= $wId ?>', 12)" class="widget-span-btn py-1 text-center text-[11px] font-bold rounded-lg border transition cursor-pointer <?= $span == 12 ? 'bg-indigo-600 border-indigo-600 text-white shadow-2xs' : 'bg-slate-50 dark:bg-slate-800/80 border-slate-200 dark:border-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700' ?>" data-span="12" title="Volledige breedte (12 kolommen)">Vol</button>
</div>
</div>
<div class="pt-1.5 border-t border-slate-100 dark:border-slate-800/80">
<button type="button" onclick="hideWidgetOnScreen('<?= $wId ?>')" class="w-full flex items-center space-x-2 px-2 py-1.5 rounded-xl text-slate-600 dark:text-slate-400 hover:text-rose-600 dark:hover:text-rose-400 hover:bg-rose-50 dark:hover:bg-rose-500/10 transition cursor-pointer text-left text-xs font-semibold">
<i data-lucide="eye-off" class="w-3.5 h-3.5 flex-shrink-0"></i>
<span>Widget verbergen</span>
</button>
</div>
</div>
</div> </div>
<!-- Collapse / Expand Toggle --> <!-- Inklappen / Uitklappen Knop -->
<button type="button" onclick="toggleWidgetCollapse('<?= $wId ?>')" class="p-1 rounded-lg text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 hover:bg-slate-200/60 dark:hover:bg-slate-800/60 transition cursor-pointer" title="Inklappen / Uitklappen"> <button type="button" onclick="toggleWidgetCollapse('<?= $wId ?>')" class="p-1 rounded-lg text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 hover:bg-slate-200/60 dark:hover:bg-slate-800/60 transition cursor-pointer" title="Inklappen / Uitklappen">
<i data-lucide="chevron-up" class="w-3.5 h-3.5 widget-collapse-icon transition-transform duration-200" id="collapse_icon_<?= htmlspecialchars($wId) ?>"></i> <i data-lucide="chevron-up" class="w-3.5 h-3.5 widget-collapse-icon transition-transform duration-200" id="collapse_icon_<?= htmlspecialchars($wId) ?>"></i>
</button> </button>
<!-- Verbergen -->
<button type="button" onclick="hideWidgetOnScreen('<?= $wId ?>')" class="p-1 rounded-lg text-slate-400 hover:text-rose-600 dark:hover:text-rose-400 hover:bg-rose-50 dark:hover:bg-rose-500/10 transition cursor-pointer" title="Verberg widget van dashboard">
<i data-lucide="eye-off" class="w-3.5 h-3.5"></i>
</button>
</div> </div>
</div> </div>
<!-- Widget Content Body --> <!-- Widget Content Body: sluit naadloos aan op de bovenste rand -->
<div class="widget-content-body transition-all duration-200" id="widget_body_<?= htmlspecialchars($wId) ?>"> <div class="widget-content-body transition-all duration-200 [&>.fociz-card]:rounded-t-none [&>.fociz-card]:border-t-0" id="widget_body_<?= htmlspecialchars($wId) ?>">
<?php if ($wId === 'daily_goal'): ?> <?php if ($wId === 'daily_goal'): ?>
<!-- Widget 1: Dagfocus & Hoofddoel --> <!-- Widget 1: Dagfocus & Hoofddoel -->
@@ -1970,14 +1984,14 @@ function setWidgetSpan(wId, span) {
wrapper.classList.add(`lg:col-span-${span}`); wrapper.classList.add(`lg:col-span-${span}`);
wrapper.setAttribute('data-col-span', span); wrapper.setAttribute('data-col-span', span);
// Knoppen styling bijwerken binnen deze widget // Knoppen styling bijwerken binnen het instellingen dropdown menu
const buttons = wrapper.querySelectorAll('.widget-span-btn'); const buttons = wrapper.querySelectorAll('.widget-span-btn');
buttons.forEach(btn => { buttons.forEach(btn => {
const bSpan = parseInt(btn.getAttribute('data-span'), 10); const bSpan = parseInt(btn.getAttribute('data-span'), 10);
if (bSpan === span) { if (bSpan === span) {
btn.className = "widget-span-btn px-1.5 py-0.5 rounded text-[10px] font-bold transition bg-indigo-600 text-white shadow-2xs"; btn.className = "widget-span-btn py-1 text-center text-[11px] font-bold rounded-lg border transition cursor-pointer bg-indigo-600 border-indigo-600 text-white shadow-2xs";
} else { } else {
btn.className = "widget-span-btn px-1.5 py-0.5 rounded text-[10px] font-bold transition text-slate-500 hover:text-slate-800 dark:hover:text-slate-200"; btn.className = "widget-span-btn py-1 text-center text-[11px] font-bold rounded-lg border transition cursor-pointer bg-slate-50 dark:bg-slate-800/80 border-slate-200 dark:border-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700";
} }
}); });
@@ -1987,6 +2001,9 @@ function setWidgetSpan(wId, span) {
item.col_span = span; item.col_span = span;
} }
// Sluit het instellingen menu
document.getElementById(`widgetSettingsMenu_${wId}`)?.classList.add('hidden');
// Trigger resize voor SVG en diagrammen // Trigger resize voor SVG en diagrammen
window.dispatchEvent(new Event('resize')); window.dispatchEvent(new Event('resize'));
@@ -2002,6 +2019,22 @@ function setWidgetSpan(wId, span) {
showWidgetToast(`Formaat aangepast naar ${spanDescriptions[span] || span + ' kolommen'}`, 'success'); showWidgetToast(`Formaat aangepast naar ${spanDescriptions[span] || span + ' kolommen'}`, 'success');
} }
// --- Widget Instellingen Popover Menu (Tandwieltje) ---
function toggleWidgetSettingsMenu(wId, event) {
if (event) {
event.stopPropagation();
event.preventDefault();
}
const menu = document.getElementById(`widgetSettingsMenu_${wId}`);
if (!menu) return;
const isCurrentlyHidden = menu.classList.contains('hidden');
// Sluit alle open menu's eerst
document.querySelectorAll('.widget-settings-dropdown').forEach(d => d.classList.add('hidden'));
if (isCurrentlyHidden) {
menu.classList.remove('hidden');
}
}
// --- Widget Inklappen / Uitklappen --- // --- Widget Inklappen / Uitklappen ---
function toggleWidgetCollapse(wId) { function toggleWidgetCollapse(wId) {
const body = document.getElementById(`widget_body_${wId}`); const body = document.getElementById(`widget_body_${wId}`);
@@ -2023,6 +2056,7 @@ function toggleWidgetCollapse(wId) {
// --- Widget Verbergen van Scherm met Undo Toast --- // --- Widget Verbergen van Scherm met Undo Toast ---
function hideWidgetOnScreen(wId) { function hideWidgetOnScreen(wId) {
document.getElementById(`widgetSettingsMenu_${wId}`)?.classList.add('hidden');
const wrapper = document.getElementById(`widgetWrapper_${wId}`) || document.querySelector(`.fociz-widget-wrapper[data-widget-id="${wId}"]`); const wrapper = document.getElementById(`widgetWrapper_${wId}`) || document.querySelector(`.fociz-widget-wrapper[data-widget-id="${wId}"]`);
const item = focizWidgets.find(x => x.id === wId); const item = focizWidgets.find(x => x.id === wId);
const title = item ? item.title : 'Widget'; const title = item ? item.title : 'Widget';
@@ -2056,9 +2090,9 @@ function undoHideWidget(wId) {
}, 400); }, 400);
} }
// --- Direct On-Screen Drag & Drop Engine (HTML5 Native) --- // --- Direct On-Screen Drag & Drop Engine (Live Shift & Reflow) ---
let draggedWidgetEl = null; let draggedWidgetEl = null;
let dropPlaceholderEl = null; let dragInitialIndex = -1;
function initPlannerWidgets() { function initPlannerWidgets() {
const grid = document.getElementById('focizWidgetGrid'); const grid = document.getElementById('focizWidgetGrid');
@@ -2077,13 +2111,6 @@ function initPlannerWidgets() {
} catch(e) {} } catch(e) {}
}); });
if (!dropPlaceholderEl) {
dropPlaceholderEl = document.createElement('div');
dropPlaceholderEl.id = 'widgetDropPlaceholder';
dropPlaceholderEl.className = 'col-span-1 min-w-0 border-2 border-dashed border-indigo-400/80 dark:border-indigo-500/80 bg-indigo-50/50 dark:bg-indigo-950/40 rounded-2xl flex items-center justify-center p-8 text-xs font-bold text-indigo-600 dark:text-indigo-400 pointer-events-none transition-all duration-150';
dropPlaceholderEl.innerHTML = '<div class="flex items-center space-x-2"><i data-lucide="arrow-down-circle" class="w-4 h-4"></i><span>Laat los om hier te plaatsen</span></div>';
}
// Luister naar drag handles // Luister naar drag handles
document.querySelectorAll('.widget-drag-handle').forEach(handle => { document.querySelectorAll('.widget-drag-handle').forEach(handle => {
handle.setAttribute('draggable', 'true'); handle.setAttribute('draggable', 'true');
@@ -2092,82 +2119,118 @@ function initPlannerWidgets() {
const wrapper = handle.closest('.fociz-widget-wrapper'); const wrapper = handle.closest('.fociz-widget-wrapper');
if (!wrapper) return; if (!wrapper) return;
draggedWidgetEl = wrapper; draggedWidgetEl = wrapper;
// Bewaar beginpositie om te controleren op wijzigingen
const allWrappers = Array.from(grid.querySelectorAll('.fociz-widget-wrapper'));
dragInitialIndex = allWrappers.indexOf(draggedWidgetEl);
e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/plain', wrapper.getAttribute('data-widget-id')); e.dataTransfer.setData('text/plain', wrapper.getAttribute('data-widget-id'));
// Match column span van dragged widget // Sluit eventuele open popover menu's
const curSpan = wrapper.getAttribute('data-col-span') || '6'; document.querySelectorAll('.widget-settings-dropdown').forEach(d => d.classList.add('hidden'));
dropPlaceholderEl.className = `lg:col-span-${curSpan} col-span-1 min-w-0 border-2 border-dashed border-indigo-400/80 dark:border-indigo-500/80 bg-indigo-50/60 dark:bg-indigo-950/50 rounded-2xl flex items-center justify-center p-8 text-xs font-bold text-indigo-600 dark:text-indigo-400 pointer-events-none transition-all`;
setTimeout(() => { setTimeout(() => {
if (draggedWidgetEl) { if (draggedWidgetEl) {
draggedWidgetEl.classList.add('opacity-40', 'scale-[0.98]', 'ring-2', 'ring-indigo-500/50'); draggedWidgetEl.classList.add('opacity-40', 'scale-[0.98]', 'ring-2', 'ring-indigo-500/70', 'shadow-2xl');
} }
}, 0); }, 0);
}; };
handle.ondragend = (e) => { handle.ondragend = (e) => {
if (draggedWidgetEl) { if (draggedWidgetEl) {
draggedWidgetEl.classList.remove('opacity-40', 'scale-[0.98]', 'ring-2', 'ring-indigo-500/50'); draggedWidgetEl.classList.remove('opacity-40', 'scale-[0.98]', 'ring-2', 'ring-indigo-500/70', 'shadow-2xl');
}
if (dropPlaceholderEl && dropPlaceholderEl.parentNode) {
dropPlaceholderEl.remove();
} }
saveCurrentGridOrderIfChanged();
draggedWidgetEl = null; draggedWidgetEl = null;
}; };
}); });
// Dragover op grid // Dragover op grid: verplaatst de widget live in het DOM zodat andere blokken direct opzij schuiven
grid.ondragover = (e) => { grid.ondragover = (e) => {
if (!draggedWidgetEl) return; if (!draggedWidgetEl) return;
e.preventDefault(); e.preventDefault();
e.dataTransfer.dropEffect = 'move'; e.dataTransfer.dropEffect = 'move';
// Check of cursor onderaan het grid zweeft
const gridRect = grid.getBoundingClientRect();
if (e.clientY > gridRect.bottom - 40) {
if (grid.lastElementChild !== draggedWidgetEl) {
grid.appendChild(draggedWidgetEl);
}
return;
}
// Check of cursor bovenaan het grid zweeft
if (e.clientY < gridRect.top + 40) {
if (grid.firstElementChild !== draggedWidgetEl) {
grid.prepend(draggedWidgetEl);
}
return;
}
const target = e.target.closest('.fociz-widget-wrapper'); const target = e.target.closest('.fociz-widget-wrapper');
if (target && target !== draggedWidgetEl && target !== dropPlaceholderEl) { if (target && target !== draggedWidgetEl && target.parentElement === grid) {
const rect = target.getBoundingClientRect(); const rect = target.getBoundingClientRect();
const isAfter = (e.clientY - rect.top) > (rect.height / 2) || (e.clientX - rect.left) > (rect.width / 2); const midY = rect.top + rect.height / 2;
const midX = rect.left + rect.width / 2;
// Bepaal of de muis voorbij het midden is (verticaal of horizontaal in dezelfde rij)
const isAfter = (e.clientY > midY) || (Math.abs(e.clientY - midY) < rect.height * 0.35 && e.clientX > midX);
if (isAfter) { if (isAfter) {
if (target.nextSibling !== dropPlaceholderEl) { if (target.nextElementSibling !== draggedWidgetEl) {
target.after(dropPlaceholderEl); target.after(draggedWidgetEl);
} }
} else { } else {
if (target.previousSibling !== dropPlaceholderEl) { if (target.previousElementSibling !== draggedWidgetEl) {
target.before(dropPlaceholderEl); target.before(draggedWidgetEl);
} }
} }
} }
}; };
grid.ondrop = (e) => { grid.ondrop = (e) => {
if (!draggedWidgetEl) return;
e.preventDefault(); e.preventDefault();
if (dropPlaceholderEl && dropPlaceholderEl.parentNode) {
dropPlaceholderEl.replaceWith(draggedWidgetEl);
}
if (draggedWidgetEl) { if (draggedWidgetEl) {
draggedWidgetEl.classList.remove('opacity-40', 'scale-[0.98]', 'ring-2', 'ring-indigo-500/50'); draggedWidgetEl.classList.remove('opacity-40', 'scale-[0.98]', 'ring-2', 'ring-indigo-500/70', 'shadow-2xl');
} }
saveCurrentGridOrderIfChanged();
// Volgorde bijwerken
const currentWrappers = Array.from(grid.querySelectorAll('.fociz-widget-wrapper'));
currentWrappers.forEach((el, index) => {
const wId = el.getAttribute('data-widget-id');
const item = focizWidgets.find(x => x.id === wId);
if (item) {
item.order = index + 1;
}
});
persistWidgetLayoutAuto();
showWidgetToast('Widget verplaatst & indeling opgeslagen', 'success');
draggedWidgetEl = null; draggedWidgetEl = null;
}; };
} }
function saveCurrentGridOrderIfChanged() {
const grid = document.getElementById('focizWidgetGrid');
if (!grid) return;
const currentWrappers = Array.from(grid.querySelectorAll('.fociz-widget-wrapper'));
const currentIndex = currentWrappers.indexOf(draggedWidgetEl);
let hasOrderChanged = false;
currentWrappers.forEach((el, index) => {
const wId = el.getAttribute('data-widget-id');
const item = focizWidgets.find(x => x.id === wId);
if (item) {
if (item.order !== index + 1) {
item.order = index + 1;
hasOrderChanged = true;
}
}
});
if (hasOrderChanged || (dragInitialIndex !== -1 && currentIndex !== dragInitialIndex)) {
persistWidgetLayoutAuto();
showWidgetToast('Widget verplaatst & indeling opgeslagen', 'success');
}
}
// Sluit popup menu's als er buiten wordt geklikt
document.addEventListener('click', (e) => {
if (!e.target.closest('.widget-settings-dropdown') && !e.target.closest('.widget-settings-btn')) {
document.querySelectorAll('.widget-settings-dropdown').forEach(d => d.classList.add('hidden'));
}
});
// --- Jira Queue & Tickets Widget Engine (v2.0) --- // --- Jira Queue & Tickets Widget Engine (v2.0) ---
const plannerJiraAssigned = <?= json_encode($jiraAssignedIssues, JSON_UNESCAPED_UNICODE) ?>; const plannerJiraAssigned = <?= json_encode($jiraAssignedIssues, JSON_UNESCAPED_UNICODE) ?>;
const plannerJiraAll = <?= json_encode($jiraAllIssues, JSON_UNESCAPED_UNICODE) ?>; const plannerJiraAll = <?= json_encode($jiraAllIssues, JSON_UNESCAPED_UNICODE) ?>;