const { reactive, computed } = Vue; const DEPARTMENTS = ["Support", "Technical", "Sales", "VIP", "Billing"]; const ROLES = ["Senior Lead", "Specialist", "Agent"]; const SKILLS = ["English", "German", "Technical Support", "VIP Handling", "Molecular App", "Hardware", "Billing Specialist"]; // Mock Data Generators const generateMoreUsers = (startId, count) => { return Array.from({ length: count }, (_, i) => ({ id: startId + i, name: `Colleague ${startId + i}`, email: `colleague.${startId + i}@company.com`, role: "Contributor", img: `https://i.pravatar.cc/150?u=${startId + i}` })); }; const CORE_USERS = [ { id: 1, name: "Alice Freeman", email: "alice.f@company.com", role: "Product Manager", img: "https://i.pravatar.cc/150?u=1" }, { id: 2, name: "Bob Smith", email: "bob.smith@company.com", role: "Senior Developer", img: "https://i.pravatar.cc/150?u=2" }, { id: 3, name: "Charlie Kim", email: "charlie.k@company.com", role: "UX Designer", img: "https://i.pravatar.cc/150?u=3" }, { id: 4, name: "Diana Prince", email: "diana.p@company.com", role: "QA Lead", img: "https://i.pravatar.cc/150?u=4" }, { id: 5, name: "Evan Wright", email: "evan.w@company.com", role: "Frontend Dev", img: "https://i.pravatar.cc/150?u=5" }, ]; const ALL_ONLINE_USERS = [...CORE_USERS, ...generateMoreUsers(6, 27)]; const AGENTS = Array.from({ length: 100 }, (_, i) => ({ id: i + 1, name: `Agent ${i + 1}`, dept: DEPARTMENTS[i % DEPARTMENTS.length], role: ROLES[i % ROLES.length], skills: [SKILLS[i % SKILLS.length]], avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=Agent${i}`, // Map of date string (YYYY-MM-DD) -> Array of shift IDs/Codes shifts: {} })); // Helper to format date key const getDateKey = (date) => { return date.toISOString().split('T')[0]; }; // Global Store const store = reactive({ // State agents: AGENTS, onlineUsers: ALL_ONLINE_USERS, // View Settings viewSettings: { leftDrawerOpen: false, rightDrawerOpen: false, isCompact: false, weekendsAreWorkingDays: false, viewScope: 4, // Weeks startDate: new Date(), searchQuery: "", activeDept: "All", weekStart: 1 // 1 = Mon, 0 = Sun }, // Selection State selection: { agent: null, date: null, mode: 'assignment' // 'assignment' | 'profile' }, // Constant Data consts: { DEPARTMENTS, ROLES, SKILLS, SHIFTS: [ { id: 'm', label: 'Morning', color: 'green-7' }, { id: 'a', label: 'Afternoon', color: 'blue-7' }, { id: 'e', label: 'EOD Only', color: 'pink-7' } ] }, // Computed Helpers (as methods for now since they depend on state) get dates() { const res = []; const now = new Date(this.viewSettings.startDate); for (let i = 0; i < this.viewSettings.viewScope * 7; i++) { const d = new Date(now); d.setDate(now.getDate() + i); res.push(d); } return res; }, get filteredAgents() { let result = this.agents; // Filter by Search if (this.viewSettings.searchQuery) { const lower = this.viewSettings.searchQuery.toLowerCase(); result = result.filter(a => a.name.toLowerCase().includes(lower)); } // Filter by Dept if (this.viewSettings.activeDept !== 'All') { result = result.filter(a => a.dept === this.viewSettings.activeDept); } return result; }, // Actions setStartDate(dateStr) { this.viewSettings.startDate = new Date(dateStr); }, toggleLeftDrawer() { this.viewSettings.leftDrawerOpen = !this.viewSettings.leftDrawerOpen; }, openAssignment(agent, date) { this.selection.mode = 'assignment'; this.selection.agent = agent; this.selection.date = date; this.viewSettings.rightDrawerOpen = true; }, openProfile(agent) { this.selection.mode = 'profile'; this.selection.agent = agent; // In a real app, might want a clone here this.selection.date = null; this.viewSettings.rightDrawerOpen = true; }, // Mock WebSocket Handler handleMockUpdate(payload) { console.log("Store received update:", payload); // Payload: { type: 'SHIFT_UPDATE', agentId: 1, date: '2023-10-27', shifts: ['m', 'vip_support'] } if (payload.type === 'SHIFT_UPDATE') { const agent = this.agents.find(a => a.id === payload.agentId); if (agent) { // Vue 3 reactive update // If the agent is visible/in-memory, we update directly if (!agent.shifts) agent.shifts = {}; agent.shifts[payload.date] = payload.shifts; console.log(`Updated shifts for ${agent.name} on ${payload.date} to`, payload.shifts); // Trigger Quasar notification if available (accessed via global Q or just console) if (window.Quasar) { window.Quasar.Notify.create({ message: `Update received for ${agent.name}`, color: 'indigo', position: 'bottom-right' }); } } } }, // Action to Assign Shifts (used by UI) assignShifts(agentId, date, shiftCodes, meta = {}) { const agent = this.agents.find(a => a.id === agentId); if (agent && date) { const key = getDateKey(date); if (!agent.shifts) agent.shifts = {}; // Store as object if meta exists, otherwise just array for backward compat? // Better to standardize on object now? // For now, let's store: { codes: [], meta: {} } // But previous code expects array. Let's keep it simple: // We'll store an object keyed by date, but the value will be { codes: [], ...meta } // Wait, getAgentShifts returns array. I should update that too or store meta separately. // Let's store meta in a parallel structure or modify the shift storage format. // Requirement: "complex inputs... specific start/end times" // Let's change the storage format to: // agent.shifts[key] = { codes: ['m'], start: '10:00', end: '12:00' } // And update getAgentShifts to return .codes for compatibility with existing UI loop agent.shifts[key] = { codes: shiftCodes, ...meta }; } }, getAgentShifts(agentId, date) { const agent = this.agents.find(a => a.id === agentId); if (agent && agent.shifts && date) { const key = getDateKey(date); const entry = agent.shifts[key]; if (!entry) return []; // If legacy array (from mock update potentially), wrap it if (Array.isArray(entry)) return entry; return entry.codes || []; } return []; }, // New helper for meta getAgentShiftMeta(agentId, date) { const agent = this.agents.find(a => a.id === agentId); if (agent && agent.shifts && date) { const key = getDateKey(date); const entry = agent.shifts[key]; if (entry && !Array.isArray(entry)) return entry; } return {}; } });