209 lines
7.3 KiB
JavaScript
209 lines
7.3 KiB
JavaScript
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 {};
|
|
}
|
|
});
|