Ashwin
Implement role-based access control, admin portal, and global loader UI
8dbbe64
Raw
History Blame Contribute Delete
35.3 kB
function initTaskManagement() {
const taskContainer = document.getElementById('task-items-container');
const quickAddInput = document.getElementById('task-quick-add-input');
const taskSummaryCount = document.getElementById('task-summary-count');
// State initialization
if (typeof window.calendarView === 'undefined') {
window.calendarView = 'month';
}
if (typeof window.calendarCurrentDate === 'undefined') {
window.calendarCurrentDate = new Date();
}
// Ensure no mock tasks are loaded in local storage
let tasks = JSON.parse(localStorage.getItem('prospectiq-tasks') || '[]');
if (tasks.length > 0 && tasks.some(t => t.title === 'Upload primary contact data sheets' || t.title === 'Read the learning playbook on MapleMPSS')) {
tasks = [];
localStorage.setItem('prospectiq-tasks', JSON.stringify(tasks));
}
// Install Sourcing Duty Calendar Modal if not present
let calendarModal = document.getElementById('calendar-task-modal');
if (!calendarModal) {
calendarModal = document.createElement('div');
calendarModal.id = 'calendar-task-modal';
calendarModal.className = 'modal fade';
calendarModal.tabIndex = -1;
calendarModal.setAttribute('aria-hidden', 'true');
calendarModal.innerHTML = `
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content shadow-lg border-0">
<div class="modal-header bg-dark text-white">
<h5 class="modal-title" id="calendar-modal-title">Schedule Task</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="calendar-task-form">
<input type="hidden" id="cal-task-id">
<div class="modal-body p-4">
<div class="mb-3">
<label class="form-label fw-semibold text-dark">Task Title *</label>
<input type="text" class="form-control shadow-none" id="cal-task-title" required placeholder="e.g. Review CNC supplier quotes">
</div>
<div class="row g-2 mb-3">
<div class="col-6">
<label class="form-label fw-semibold text-dark">Date *</label>
<input type="date" class="form-control shadow-none" id="cal-task-date" required>
</div>
<div class="col-6">
<label class="form-label fw-semibold text-dark">Time</label>
<input type="time" class="form-control shadow-none" id="cal-task-time" value="09:00">
</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold text-dark">Notes / Description</label>
<textarea class="form-control shadow-none" id="cal-task-notes" rows="3" placeholder="Describe the task or next actions..."></textarea>
</div>
<div class="form-check">
<input class="form-check-input shadow-none" type="checkbox" id="cal-task-completed">
<label class="form-check-label fw-semibold text-dark" for="cal-task-completed">
Mark as Completed
</label>
</div>
</div>
<div class="modal-footer border-0 p-3 bg-light d-flex justify-content-between">
<button type="button" class="btn btn-danger" id="cal-task-delete-btn" style="display: none;">Delete</button>
<div class="ms-auto">
<button type="button" class="btn btn-outline-secondary me-2" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-dark">Save Task</button>
</div>
</div>
</form>
</div>
</div>
`;
document.body.appendChild(calendarModal);
const form = document.getElementById('calendar-task-form');
form.onsubmit = function(e) {
e.preventDefault();
saveCalendarTask();
};
const delBtn = document.getElementById('cal-task-delete-btn');
delBtn.onclick = function() {
deleteCalendarTask();
};
}
// Helper functions for dates arithmetic
function getDaysInMonth(year, month) {
return new Date(year, month + 1, 0).getDate();
}
function getFirstDayOfMonthIndex(year, month) {
let day = new Date(year, month, 1).getDay();
return day === 0 ? 6 : day - 1; // Mon-Sun (0-6)
}
function getStartOfWeek(date) {
const diff = date.getDate() - date.getDay() + (date.getDay() === 0 ? -6 : 1);
return new Date(date.setDate(diff));
}
function formatDate(date) {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
}
function formatDisplayTime(timeStr) {
if (!timeStr) return '';
const parts = timeStr.split(':');
let hours = parseInt(parts[0]);
const minutes = parts[1];
const ampm = hours >= 12 ? 'pm' : 'am';
hours = hours % 12;
hours = hours ? hours : 12;
return `${hours}:${minutes}${ampm}`;
}
function formatDisplayDateTime(dateStr, timeStr) {
if (!dateStr) return '';
const dateParts = dateStr.split('-');
const d = new Date(dateParts[0], dateParts[1] - 1, dateParts[2]);
const dateFormatted = d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
if (!timeStr) return dateFormatted;
return `${dateFormatted} at ${formatDisplayTime(timeStr)}`;
}
// ── GOOGLE CALENDAR VIEW ENGINE ──
function renderHomeCalendar() {
const container = document.getElementById('calendar-view-container');
const monthTitle = document.getElementById('calendar-month-title');
if (!container) return;
const currentTasks = JSON.parse(localStorage.getItem('prospectiq-tasks') || '[]');
const year = window.calendarCurrentDate.getFullYear();
const month = window.calendarCurrentDate.getMonth();
// Update Title Header
const monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
if (window.calendarView === 'month') {
monthTitle.textContent = `${monthNames[month]} ${year}`;
} else {
const startOfWeek = getStartOfWeek(new Date(window.calendarCurrentDate));
const endOfWeek = new Date(startOfWeek);
endOfWeek.setDate(startOfWeek.getDate() + 6);
monthTitle.textContent = `${monthNames[startOfWeek.getMonth()]} ${startOfWeek.getDate()}${monthNames[endOfWeek.getMonth()]} ${endOfWeek.getDate()}, ${endOfWeek.getFullYear()}`;
}
const todayBtn = document.getElementById('cal-today-btn');
if (todayBtn) {
todayBtn.textContent = window.calendarView === 'month' ? 'This Month' : 'This Week';
}
// Render Month View Grid
if (window.calendarView === 'month') {
const cells = [];
const daysInMonth = getDaysInMonth(year, month);
const firstDayIndex = getFirstDayOfMonthIndex(year, month);
// Prev month padding
const prevMonthYear = month === 0 ? year - 1 : year;
const prevMonth = month === 0 ? 11 : month - 1;
const daysInPrevMonth = getDaysInMonth(prevMonthYear, prevMonth);
for (let i = firstDayIndex - 1; i >= 0; i--) {
const d = daysInPrevMonth - i;
cells.push({ dayNum: d, dateStr: `${prevMonthYear}-${String(prevMonth + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`, isCurrentMonth: false });
}
// Current month cells
for (let d = 1; d <= daysInMonth; d++) {
cells.push({ dayNum: d, dateStr: `${year}-${String(month + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`, isCurrentMonth: true });
}
// Next month padding
const nextMonthYear = month === 11 ? year + 1 : year;
const nextMonth = month === 11 ? 0 : month + 1;
let nextIndex = 1;
while (cells.length < 42) {
cells.push({ dayNum: nextIndex, dateStr: `${nextMonthYear}-${String(nextMonth + 1).padStart(2, '0')}-${String(nextIndex).padStart(2, '0')}`, isCurrentMonth: false });
nextIndex++;
}
let html = `
<div class="calendar-month-grid">
<div class="calendar-grid-header">Mon</div>
<div class="calendar-grid-header">Tue</div>
<div class="calendar-grid-header">Wed</div>
<div class="calendar-grid-header">Thu</div>
<div class="calendar-grid-header">Fri</div>
<div class="calendar-grid-header">Sat</div>
<div class="calendar-grid-header">Sun</div>
`;
cells.forEach(cell => {
const dayTasks = currentTasks.filter(t => t.date === cell.dateStr);
dayTasks.sort((a, b) => (a.time || '00:00').localeCompare(b.time || '00:00'));
const isToday = cell.dateStr === formatDate(new Date());
const tasksHtml = dayTasks.map(t => `
<div class="calendar-task-card" draggable="true" data-id="${t.id}" style="${t.completed ? 'text-decoration: line-through; opacity: 0.6;' : ''}">
<span class="fw-bold">${formatDisplayTime(t.time)}</span>
<span class="calendar-task-title flex-grow-1 ms-1">${escapeHtml(t.title)}</span>
</div>
`).join('');
html += `
<div class="calendar-month-cell ${cell.isCurrentMonth ? '' : 'outside'} ${isToday ? 'today' : ''}" data-date="${cell.dateStr}">
<div class="calendar-cell-date">${cell.dayNum}</div>
<div class="calendar-cell-tasks-container">
${tasksHtml}
</div>
</div>
`;
});
html += `</div>`;
container.innerHTML = html;
} else {
// Render Week View Grid
const startOfWeek = getStartOfWeek(new Date(window.calendarCurrentDate));
const weekDays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
let html = `<div class="calendar-week-row-grid">`;
for (let i = 0; i < 7; i++) {
const d = new Date(startOfWeek);
d.setDate(startOfWeek.getDate() + i);
const dateStr = formatDate(d);
const isToday = dateStr === formatDate(new Date());
const dayTasks = currentTasks.filter(t => t.date === dateStr);
dayTasks.sort((a, b) => (a.time || '00:00').localeCompare(b.time || '00:00'));
const tasksHtml = dayTasks.map(t => `
<div class="calendar-task-card mb-2" draggable="true" data-id="${t.id}" style="${t.completed ? 'text-decoration: line-through; opacity: 0.6;' : ''}">
<span class="fw-bold text-dark me-1">${formatDisplayTime(t.time)}</span>
<span class="calendar-task-title flex-grow-1">${escapeHtml(t.title)}</span>
</div>
`).join('');
html += `
<div class="calendar-week-column ${isToday ? 'today' : ''}" data-date="${dateStr}">
<div class="calendar-week-col-header">
<div class="day-name">${weekDays[i].substring(0, 3)}</div>
<div class="day-num">${d.getDate()}</div>
</div>
<div class="calendar-week-tasks-container">
${tasksHtml || '<span class="text-muted small text-center my-auto w-100 d-block" style="font-size: 0.72rem; font-style: italic;">No duties</span>'}
</div>
</div>
`;
}
html += `</div>`;
container.innerHTML = html;
}
// Wire click triggers on calendar cell blocks
document.querySelectorAll('.calendar-month-cell, .calendar-week-column').forEach(cell => {
cell.addEventListener('click', (e) => {
// If clicked task card itself, do not open add modal
if (e.target.closest('.calendar-task-card')) return;
const dStr = cell.getAttribute('data-date');
openCalendarTaskModal(null, dStr);
});
});
// Wire click triggers on task cards inside the calendar
document.querySelectorAll('.calendar-task-card').forEach(card => {
card.addEventListener('click', (e) => {
e.stopPropagation();
const taskId = card.getAttribute('data-id');
openCalendarTaskModal(taskId);
});
// Make calendar cards draggable to reschedule
card.addEventListener('dragstart', (e) => {
const id = card.getAttribute('data-id');
e.dataTransfer.setData('text/plain', id);
});
});
// Wire dropzones
document.querySelectorAll('.calendar-month-cell, .calendar-week-column').forEach(cell => {
cell.addEventListener('dragover', (e) => {
e.preventDefault();
cell.classList.add('dragover');
});
cell.addEventListener('dragleave', () => {
cell.classList.remove('dragover');
});
cell.addEventListener('drop', (e) => {
e.preventDefault();
cell.classList.remove('dragover');
const taskId = parseInt(e.dataTransfer.getData('text/plain'));
const dateStr = cell.getAttribute('data-date');
if (taskId && dateStr) {
assignTaskToDate(taskId, dateStr);
}
});
});
}
window.renderHomeCalendar = renderHomeCalendar;
function assignTaskToDate(taskId, dateStr) {
let tasks = JSON.parse(localStorage.getItem('prospectiq-tasks') || '[]');
const index = tasks.findIndex(t => t.id === taskId);
if (index !== -1) {
tasks[index].date = dateStr;
tasks[index].time = tasks[index].time || '09:00';
localStorage.setItem('prospectiq-tasks', JSON.stringify(tasks));
if (typeof addNotification === 'function') {
addNotification("Task Scheduled", `Rescheduled "${tasks[index].title}" to ${dateStr}.`);
}
renderTasks();
renderHomeCalendar();
}
}
function getTaskChipBorderColor(dateStr) {
if (!dateStr) return '';
const today = new Date();
today.setHours(0, 0, 0, 0);
const taskDate = new Date(dateStr);
taskDate.setHours(0, 0, 0, 0);
const diffTime = taskDate - today;
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
if (diffDays <= 1) {
return 'border-danger text-danger bg-danger-subtle';
} else if (diffDays <= 3) {
return 'border-warning text-warning-emphasis bg-warning-subtle';
} else {
return 'border-success text-success bg-success-subtle';
}
}
// ── RENDER REGULAR TASKS LIST ──
function renderTasks() {
const currentTasks = JSON.parse(localStorage.getItem('prospectiq-tasks') || '[]');
const openTasksCount = currentTasks.filter(t => !t.completed).length;
if (taskSummaryCount) {
taskSummaryCount.textContent = `${openTasksCount} open task${openTasksCount === 1 ? '' : 's'} (${currentTasks.length} total)`;
}
if (taskContainer) {
if (currentTasks.length === 0) {
taskContainer.innerHTML = '<p class="text-muted text-center py-4 m-0">No tasks left. Write above to create one.</p>';
} else {
taskContainer.innerHTML = currentTasks.map(task => `
<div class="task-row d-flex flex-column py-3 border-bottom ${task.completed ? 'completed' : ''}" id="task-row-${task.id}" draggable="true" style="cursor: grab; gap: 8px;">
<!-- Top Row: Date and Time Chip (on top left) -->
<div class="d-flex justify-content-between align-items-center w-100">
<div class="task-date-container">
${task.date ? `<span class="badge border ${getTaskChipBorderColor(task.date)}" id="task-date-chip-${task.id}" style="font-size: 0.72rem; font-weight: 500; border-width: 1px !important; white-space: nowrap;">${escapeHtml(formatDisplayDateTime(task.date, task.time))}</span>` : '<span class="badge border border-secondary text-secondary bg-light" style="font-size: 0.72rem; font-weight: 500; border-width: 1px !important;">Unassigned</span>'}
</div>
</div>
<!-- Bottom Row: Checkbox, Separator, Description, and Dropdown -->
<div class="d-flex align-items-center w-100" style="gap: 4px;">
<!-- Checkbox Circle -->
<div class="task-checkbox-custom flex-shrink-0 ${task.completed ? 'checked' : ''}" data-id="${task.id}"></div>
<!-- Vertical line separator -->
<span class="text-muted px-1" style="font-weight: 300; font-size: 1rem; user-select: none;">|</span>
<!-- Task Description Section (fixed max-width, sentence breaking) -->
<div class="flex-grow-1" style="min-width: 0; max-width: 75%; word-break: break-word; white-space: normal;">
<span class="task-title-text" id="task-title-text-${task.id}" style="font-weight: 500; display: block; line-height: 1.4; white-space: normal; word-break: break-word; overflow-wrap: break-word;">${escapeHtml(task.title)}</span>
<input type="text" class="task-inline-edit-input d-none" id="task-inline-edit-${task.id}" value="${escapeHtml(task.title)}" data-id="${task.id}" style="width: 100%;">
</div>
<!-- Three-dot dropdown menu (at last/right end) -->
<div class="ms-auto task-row-actions d-flex gap-2 align-items-center flex-shrink-0">
<button class="btn btn-sm btn-dark done-edit-btn d-none" id="btn-done-action-${task.id}" data-id="${task.id}" style="font-size: 0.75rem; padding: 2px 8px;">Save</button>
<div class="dropdown" id="dropdown-menu-${task.id}">
<button class="btn btn-link text-muted p-0 shadow-none border-0" type="button" data-bs-toggle="dropdown" aria-expanded="false" style="font-size: 1.1rem; line-height: 1; border-radius: 50%; width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;">
<i class="bi bi-three-dots-vertical"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end shadow border-0 py-1" style="font-size: 0.82rem; min-width: 100px; border-radius: 8px;">
<li><button class="dropdown-item edit-task-btn py-1 px-3 d-flex align-items-center gap-2" type="button" data-id="${task.id}"><i class="bi bi-pencil" style="font-size: 0.8rem;"></i> Edit</button></li>
<li><hr class="dropdown-divider my-1"></li>
<li><button class="dropdown-item delete-task-btn text-danger py-1 px-3 d-flex align-items-center gap-2" type="button" data-id="${task.id}"><i class="bi bi-trash" style="font-size: 0.8rem;"></i> Delete</button></li>
</ul>
</div>
</div>
</div>
</div>
`).join('');
}
}
// Wire checkbox toggles
document.querySelectorAll('.task-checkbox-custom').forEach(cb => {
cb.addEventListener('click', () => {
const id = parseInt(cb.getAttribute('data-id'));
toggleTaskComplete(id);
});
});
// Wire inline edit toggle
document.querySelectorAll('.edit-task-btn').forEach(btn => {
btn.addEventListener('click', () => {
const id = parseInt(btn.getAttribute('data-id'));
enterEditMode(id);
});
});
// Wire inline save action
document.querySelectorAll('.done-edit-btn').forEach(btn => {
btn.addEventListener('click', () => {
const id = parseInt(btn.getAttribute('data-id'));
saveInlineEdit(id);
});
});
// Wire enter-key listener for inline inputs
document.querySelectorAll('.task-inline-edit-input').forEach(input => {
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
const id = parseInt(input.getAttribute('data-id'));
saveInlineEdit(id);
} else if (e.key === 'Escape') {
const id = parseInt(input.getAttribute('data-id'));
exitEditMode(id);
}
});
});
// Wire delete action
document.querySelectorAll('.delete-task-btn').forEach(btn => {
btn.addEventListener('click', () => {
const id = parseInt(btn.getAttribute('data-id'));
deleteTask(id);
});
});
// Wire drag events on task rows
document.querySelectorAll('.task-row').forEach(row => {
row.addEventListener('dragstart', (e) => {
const id = row.id.replace('task-row-', '');
e.dataTransfer.setData('text/plain', id);
row.classList.add('dragging');
});
row.addEventListener('dragend', () => {
row.classList.remove('dragging');
});
});
// Wire dropzone on the task list itself
if (taskContainer) {
taskContainer.addEventListener('dragover', (e) => {
e.preventDefault();
taskContainer.classList.add('dragover');
});
taskContainer.addEventListener('dragleave', () => {
taskContainer.classList.remove('dragover');
});
taskContainer.addEventListener('drop', (e) => {
e.preventDefault();
taskContainer.classList.remove('dragover');
const taskId = parseInt(e.dataTransfer.getData('text/plain'));
if (taskId) {
unassignTaskFromDate(taskId);
}
});
}
}
function unassignTaskFromDate(taskId) {
let tasksList = JSON.parse(localStorage.getItem('prospectiq-tasks') || '[]');
const index = tasksList.findIndex(t => t.id === taskId);
if (index !== -1) {
tasksList[index].date = null;
tasksList[index].time = null;
localStorage.setItem('prospectiq-tasks', JSON.stringify(tasksList));
if (typeof addNotification === 'function') {
addNotification("Task Unscheduled", `Removed "${tasksList[index].title}" from calendar.`);
}
renderTasks();
renderHomeCalendar();
}
}
// 1. Quick Add Input handler
if (quickAddInput) {
quickAddInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
const title = quickAddInput.value.trim();
if (!title) return;
const currentTasks = JSON.parse(localStorage.getItem('prospectiq-tasks') || '[]');
const newTask = {
id: Date.now(),
title,
completed: false,
date: null,
time: null
};
currentTasks.unshift(newTask);
localStorage.setItem('prospectiq-tasks', JSON.stringify(currentTasks));
quickAddInput.value = '';
renderTasks();
renderHomeCalendar();
}
});
}
// Helper functions for toggles & inline edit
function toggleTaskComplete(id) {
let currentTasks = JSON.parse(localStorage.getItem('prospectiq-tasks') || '[]');
currentTasks = currentTasks.map(t => t.id === id ? { ...t, completed: !t.completed } : t);
localStorage.setItem('prospectiq-tasks', JSON.stringify(currentTasks));
renderTasks();
renderHomeCalendar();
}
function enterEditMode(id) {
const textSpan = document.getElementById(`task-title-text-${id}`);
const inputField = document.getElementById(`task-inline-edit-${id}`);
const dropdownMenu = document.getElementById(`dropdown-menu-${id}`);
const doneBtn = document.getElementById(`btn-done-action-${id}`);
if (textSpan && inputField && dropdownMenu && doneBtn) {
textSpan.classList.add('d-none');
inputField.classList.remove('d-none');
dropdownMenu.classList.add('d-none');
doneBtn.classList.remove('d-none');
inputField.focus();
inputField.setSelectionRange(inputField.value.length, inputField.value.length);
}
}
function exitEditMode(id) {
const textSpan = document.getElementById(`task-title-text-${id}`);
const inputField = document.getElementById(`task-inline-edit-${id}`);
const dropdownMenu = document.getElementById(`dropdown-menu-${id}`);
const doneBtn = document.getElementById(`btn-done-action-${id}`);
if (textSpan && inputField && dropdownMenu && doneBtn) {
textSpan.classList.remove('d-none');
inputField.classList.add('d-none');
dropdownMenu.classList.remove('d-none');
doneBtn.classList.add('d-none');
}
}
function saveInlineEdit(id) {
const inputField = document.getElementById(`task-inline-edit-${id}`);
if (!inputField) return;
const newTitle = inputField.value.trim();
if (!newTitle) return;
let currentTasks = JSON.parse(localStorage.getItem('prospectiq-tasks') || '[]');
currentTasks = currentTasks.map(t => t.id === id ? { ...t, title: newTitle } : t);
localStorage.setItem('prospectiq-tasks', JSON.stringify(currentTasks));
renderTasks();
renderHomeCalendar();
}
function deleteTask(id) {
let currentTasks = JSON.parse(localStorage.getItem('prospectiq-tasks') || '[]');
currentTasks = currentTasks.filter(t => t.id !== id);
localStorage.setItem('prospectiq-tasks', JSON.stringify(currentTasks));
renderTasks();
renderHomeCalendar();
}
// ── CALENDAR NAVIGATION WIREUP ──
const prevBtn = document.getElementById('cal-prev-btn');
if (prevBtn) {
prevBtn.onclick = () => {
if (window.calendarView === 'month') {
window.calendarCurrentDate.setMonth(window.calendarCurrentDate.getMonth() - 1);
} else {
window.calendarCurrentDate.setDate(window.calendarCurrentDate.getDate() - 7);
}
renderHomeCalendar();
};
}
const nextBtn = document.getElementById('cal-next-btn');
if (nextBtn) {
nextBtn.onclick = () => {
if (window.calendarView === 'month') {
window.calendarCurrentDate.setMonth(window.calendarCurrentDate.getMonth() + 1);
} else {
window.calendarCurrentDate.setDate(window.calendarCurrentDate.getDate() + 7);
}
renderHomeCalendar();
};
}
const todayBtn = document.getElementById('cal-today-btn');
if (todayBtn) {
todayBtn.onclick = () => {
window.calendarCurrentDate = new Date();
renderHomeCalendar();
};
}
const toggleMonthBtn = document.getElementById('toggle-month-view-btn');
const toggleWeekBtn = document.getElementById('toggle-week-view-btn');
if (toggleMonthBtn && toggleWeekBtn) {
toggleMonthBtn.onclick = () => {
window.calendarView = 'month';
toggleMonthBtn.className = 'btn btn-dark';
toggleWeekBtn.className = 'btn btn-outline-secondary';
renderHomeCalendar();
};
toggleWeekBtn.onclick = () => {
window.calendarView = 'week';
toggleMonthBtn.className = 'btn btn-outline-secondary';
toggleWeekBtn.className = 'btn btn-dark';
renderHomeCalendar();
};
}
const addDutyBtn = document.getElementById('cal-add-duty-btn');
if (addDutyBtn) {
addDutyBtn.onclick = () => {
openCalendarTaskModal(null, formatDate(new Date()));
};
}
// Initial render call
renderTasks();
renderHomeCalendar();
}
// Global Calendar Modal CRUD Methods
function openCalendarTaskModal(taskId, dateStr) {
const modalTitle = document.getElementById('calendar-modal-title');
const taskIdField = document.getElementById('cal-task-id');
const titleField = document.getElementById('cal-task-title');
const dateField = document.getElementById('cal-task-date');
const timeField = document.getElementById('cal-task-time');
const notesField = document.getElementById('cal-task-notes');
const completedField = document.getElementById('cal-task-completed');
const delBtn = document.getElementById('cal-task-delete-btn');
if (taskId) {
modalTitle.textContent = 'Edit Task Details';
taskIdField.value = taskId;
const tasks = JSON.parse(localStorage.getItem('prospectiq-tasks') || '[]');
const task = tasks.find(t => t.id === parseInt(taskId));
if (task) {
titleField.value = task.title || '';
dateField.value = task.date || '';
timeField.value = task.time || '09:00';
notesField.value = task.notes || '';
completedField.checked = task.completed || false;
}
delBtn.style.display = 'block';
} else {
modalTitle.textContent = 'Schedule Task';
taskIdField.value = '';
titleField.value = '';
dateField.value = dateStr || '';
timeField.value = '09:00';
notesField.value = '';
completedField.checked = false;
delBtn.style.display = 'none';
}
const modalEl = document.getElementById('calendar-task-modal');
const modalInst = bootstrap.Modal.getOrCreateInstance(modalEl);
modalInst.show();
}
window.openCalendarTaskModal = openCalendarTaskModal;
function saveCalendarTask() {
const taskId = document.getElementById('cal-task-id').value;
const title = document.getElementById('cal-task-title').value.trim();
const date = document.getElementById('cal-task-date').value;
const time = document.getElementById('cal-task-time').value;
const notes = document.getElementById('cal-task-notes').value.trim();
const completed = document.getElementById('cal-task-completed').checked;
let tasks = JSON.parse(localStorage.getItem('prospectiq-tasks') || '[]');
if (taskId) {
const idNum = parseInt(taskId);
tasks = tasks.map(t => t.id === idNum ? { ...t, title, date, time, notes, completed } : t);
} else {
const newTask = {
id: Date.now(),
title,
date,
time,
notes,
completed
};
tasks.unshift(newTask);
if (typeof addNotification === 'function') {
addNotification("Task Scheduled", `Task scheduled for ${date} at ${time}.`);
}
}
localStorage.setItem('prospectiq-tasks', JSON.stringify(tasks));
const modalEl = document.getElementById('calendar-task-modal');
const modalInst = bootstrap.Modal.getOrCreateInstance(modalEl);
modalInst.hide();
if (typeof initTaskManagement === 'function') {
initTaskManagement();
}
}
window.saveCalendarTask = saveCalendarTask;
function deleteCalendarTask() {
const taskId = document.getElementById('cal-task-id').value;
if (!taskId) return;
const idNum = parseInt(taskId);
let tasks = JSON.parse(localStorage.getItem('prospectiq-tasks') || '[]');
tasks = tasks.filter(t => t.id !== idNum);
localStorage.setItem('prospectiq-tasks', JSON.stringify(tasks));
const modalEl = document.getElementById('calendar-task-modal');
const modalInst = bootstrap.Modal.getOrCreateInstance(modalEl);
modalInst.hide();
if (typeof initTaskManagement === 'function') {
initTaskManagement();
}
}
window.deleteCalendarTask = deleteCalendarTask;