import { ref, reactive, computed, onMounted } from 'https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js'; import { useQuasar } from 'https://cdn.jsdelivr.net/npm/quasar@2.16.0/dist/quasar.umd.prod.js'; import { HUBS, DEPARTMENTS, ROLES, SKILLS, SHIFTS, MOCK_COMMENTS_TEXT, MOCK_NOTES_TEXT } from './data-constants.js'; const state = reactive({ // Data agents: [], assignments: {}, comments: {}, notes: {}, holidays: {}, specialDays: {}, lockedCells: new Set(), loading: false, // UI Controls isCompact: false, weekendsAreWorkingDays: false, viewScope: 8, pickerStartDay: 1, showEodTargets: false, showAvailability: false, crosshairActive: true, // Filtering & Grouping search: "", activeDept: "All", activeHub: "All", filterRoles: [], filterSkills: [], filterByAvailability: false, filterDate: null, // Initialized in setup filterShiftTypes: [], groupingSelection: ['hub', 'dept'], // Interaction State startDate: null, // Initialized in setup selectedAgent: null, selectedDate: null, pendingShift: null, highlightedRowId: null, highlightedDateStr: null, hoveredDateStr: null, }); const formatDateForId = (date) => { if (!date) return ''; const d = new Date(date); const month = '' + (d.getMonth() + 1); const day = '' + d.getDate(); const year = d.getFullYear(); return [year, month.padStart(2, '0'), day.padStart(2, '0')].join('-'); }; const getStartOfWeek = (date) => { const d = new Date(date); const day = d.getDay(); const diff = (day < state.pickerStartDay ? 7 : 0) + day - state.pickerStartDay; d.setDate(d.getDate() - diff); d.setHours(0, 0, 0, 0); return d; }; // Initialize date-dependent state state.filterDate = formatDateForId(new Date()); state.startDate = getStartOfWeek(new Date()); const generateMockAgents = (count) => { return Array.from({ length: count }, (_, i) => { const hub = HUBS[Math.floor(Math.random() * HUBS.length)]; return { id: i + 1, name: `Agent ${i + 1}`, dept: DEPARTMENTS[i % DEPARTMENTS.length], role: ROLES[i % ROLES.length], hub: hub.id, hubName: hub.name, skills: [SKILLS[i % SKILLS.length], SKILLS[(i + 2) % SKILLS.length]], avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=Agent${i}` }; }); }; const methods = { loadDataFromDatabase: ($q) => { state.loading = true; setTimeout(() => { const fetchedAgents = generateMockAgents(800); state.agents = fetchedAgents; const mockStartDate = new Date(state.startDate); fetchedAgents.forEach(agent => { state.assignments[agent.id] = {}; state.comments[agent.id] = {}; state.notes[agent.id] = {}; for (let i = 0; i < 60; i++) { const d = new Date(mockStartDate); d.setDate(d.getDate() + i); const dStr = formatDateForId(d); if ((agent.id + i) % 7 === 0) state.assignments[agent.id][dStr] = SHIFTS[0].id; else if ((agent.id + i) % 5 === 0) state.assignments[agent.id][dStr] = SHIFTS[1].id; if ((agent.id + i) % 20 === 0) state.comments[agent.id][dStr] = MOCK_COMMENTS_TEXT[(agent.id + i) % MOCK_COMMENTS_TEXT.length]; if ((agent.id + i) % 25 === 0) state.notes[agent.id][dStr] = MOCK_NOTES_TEXT[(agent.id + i) % MOCK_NOTES_TEXT.length]; } }); Object.keys(state.holidays).forEach(key => delete state.holidays[key]); Object.keys(state.specialDays).forEach(key => delete state.specialDays[key]); const today = new Date(); const tomorrow = new Date(today); tomorrow.setDate(today.getDate() + 1); state.holidays[formatDateForId(tomorrow)] = 'Regional Holiday'; const eventDay = new Date(today); eventDay.setDate(today.getDate() + 5); state.specialDays[formatDateForId(eventDay)] = 'Quarterly Planning'; state.loading = false; if ($q) $q.notify({ message: `Data Loaded: ${fetchedAgents.length} Agents`, color: 'positive', position: 'top', timeout: 1000 }); }, 2000); }, simulateWssLock: ($q) => { if (state.agents.length === 0) return; const randomAgent = state.agents[Math.floor(Math.random() * state.agents.length)]; let targetDate = new Date(); const day = targetDate.getDay(); if (day === 0) targetDate.setDate(targetDate.getDate() + 1); else if (day === 6) targetDate.setDate(targetDate.getDate() + 2); const dateStr = formatDateForId(targetDate); const key = `${randomAgent.id}:${dateStr}`; state.lockedCells.add(key); if ($q) $q.notify({ message: `WSS: Cell Locked for ${randomAgent.name} on ${dateStr}`, color: 'negative', position: 'top', icon: 'lock' }); }, isCellLocked: (agentId, date) => state.lockedCells.has(`${agentId}:${formatDateForId(date)}`), getAssignment: (agentId, date) => { const dateStr = formatDateForId(date); const assignmentId = state.assignments[agentId]?.[dateStr]; return assignmentId ? SHIFTS.find(s => s.id === assignmentId) : null; }, hasComment: (agentId, date) => !!state.comments[agentId]?.[formatDateForId(date)], getCommentText: (agentId, date) => state.comments[agentId]?.[formatDateForId(date)] || '', hasNote: (agentId, date) => !!state.notes[agentId]?.[formatDateForId(date)], getNoteText: (agentId, date) => state.notes[agentId]?.[formatDateForId(date)] || '', getHoliday: (date) => state.holidays[formatDateForId(date)] || null, getSpecialDay: (date) => state.specialDays[formatDateForId(date)] || null, isWeekend: (date) => { const day = date.getDay(); return day === 0 || day === 6; }, openAssignment: (agent, date) => { if (methods.isCellLocked(agent.id, date) || (!state.weekendsAreWorkingDays && methods.isWeekend(date))) return; state.selectedAgent = agent; state.selectedDate = date; state.pendingShift = null; }, openProfile: (agent) => { state.selectedAgent = { ...agent }; state.selectedDate = null; }, saveAssignment: () => { if (!state.selectedAgent || !state.selectedDate) return; const { id } = state.selectedAgent; const dateStr = formatDateForId(state.selectedDate); if (!state.assignments[id]) state.assignments[id] = {}; state.assignments[id][dateStr] = state.pendingShift ? state.pendingShift.id : null; state.pendingShift = null; state.selectedAgent = null; state.selectedDate = null; }, clearFilters: () => { state.search = ""; state.activeDept = "All"; state.activeHub = "All"; state.filterRoles = []; state.filterSkills = []; state.filterByAvailability = false; state.filterShiftTypes = []; state.filterDate = formatDateForId(new Date()); }, resetToToday: () => { const today = new Date(); state.startDate = getStartOfWeek(today); }, applyDateSelection: (proxyDate) => { if (!proxyDate) return; const target = new Date(proxyDate); state.startDate = getStartOfWeek(target); }, toggleRowHighlight: (agentId) => { state.highlightedRowId = state.highlightedRowId === agentId ? null : agentId; }, toggleColHighlight: (date) => { const str = formatDateForId(date); state.highlightedDateStr = state.highlightedDateStr === str ? null : str; }, clearHighlights: () => { state.highlightedRowId = null; state.highlightedDateStr = null; }, formatDateForId, }; const getters = { dates: computed(() => { const res = []; const now = new Date(state.startDate); for (let i = 0; i < state.viewScope * 7; i++) { const d = new Date(now); d.setDate(now.getDate() + i); res.push(d); } return res; }), filteredAgents: computed(() => { // This logic remains complex and is well-suited for a computed property. // It depends on many reactive state properties. return state.agents.filter(a => { const term = (state.search || "").toLowerCase(); const matchSearch = term === "" || a.name.toLowerCase().includes(term); const matchDept = state.activeDept === 'All' || a.dept === state.activeDept; const matchHub = state.activeHub === 'All' || a.hub === state.activeHub; const matchRoles = state.filterRoles.length === 0 || state.filterRoles.includes(a.role); const matchSkills = state.filterSkills.length === 0 || a.skills.some(s => state.filterSkills.includes(s)); let matchAvailability = true; if (state.filterByAvailability && state.filterDate) { const d = new Date(state.filterDate); const assignment = methods.getAssignment(a.id, d); if (!state.weekendsAreWorkingDays && methods.isWeekend(d)) matchAvailability = false; else if (!assignment) matchAvailability = false; else if (state.filterShiftTypes.length > 0 && !state.filterShiftTypes.includes(assignment.id)) matchAvailability = false; } return matchSearch && matchDept && matchHub && matchRoles && matchSkills && matchAvailability; }); }), // Other computed properties can be added here following the same pattern. }; export function usePlannerState() { const $q = useQuasar(); onMounted(() => methods.loadDataFromDatabase($q)); return { state, methods, getters }; }