/** * grid-cell.js * ============= * A single shift cell inside an agent row. * Shows time ranges + activity labels, special statuses. */ import { formatDateForId, isWeekend, isToday, weekendsAreWorkingDays, isCompact, crosshairActive, hoveredDateStr, highlightedRowId, highlightedDateStr } from '../../services/planner-state.js'; import { getShifts, hasComment, hasNote, getCommentText, getNoteText, SPECIAL_STATUSES } from '../../services/data-service.js'; import { isCellLocked } from '../../services/socket-service.js'; export default { name: 'GridCell', props: { agentId: { type: Number, required: true }, date: { type: Date, required: true } }, emits: ['open-assignment'], setup(props, { emit }) { const today = Vue.computed(() => isToday(props.date)); const cellClass = Vue.computed(() => { const agentId = props.agentId; const date = props.date; const isRow = highlightedRowId.value === agentId; const isCol = highlightedDateStr.value === formatDateForId(date); const isHoverCol = crosshairActive.value && hoveredDateStr.value === formatDateForId(date); const isWknd = isWeekend(date) && !weekendsAreWorkingDays.value; const locked = isCellLocked(agentId, date); const classes = ['grid-cell-root']; if (isCompact.value) classes.push('grid-cell-compact'); if (isWknd) classes.push('grid-cell-bg-weekend'); if (today.value) classes.push('grid-cell-today'); if (isWknd && !weekendsAreWorkingDays.value) classes.push('cursor-not-allowed'); else if (locked) classes.push('cursor-not-allowed'); else classes.push('cursor-pointer'); if (isRow && isCol) classes.push('grid-cell-bg-reading-mode-intersection'); else if (isRow || isCol) classes.push('grid-cell-bg-reading-mode'); if (isHoverCol) classes.push('grid-cell-col-hovered'); return classes.join(' '); }); const shifts = Vue.computed(() => getShifts(props.agentId, props.date)); const locked = Vue.computed(() => isCellLocked(props.agentId, props.date)); const showShift = Vue.computed(() => weekendsAreWorkingDays.value || !isWeekend(props.date)); const comment = Vue.computed(() => hasComment(props.agentId, props.date)); const note = Vue.computed(() => hasNote(props.agentId, props.date)); const commentTxt = Vue.computed(() => getCommentText(props.agentId, props.date)); const noteTxt = Vue.computed(() => getNoteText(props.agentId, props.date)); // Check if this is a special status (sick leave, off, etc.) const specialStatus = Vue.computed(() => { if (!shifts.value || shifts.value.length === 0) return null; const first = shifts.value[0]; if (first.type === 'special') { return SPECIAL_STATUSES.find(s => s.id === first.statusId) || null; } return null; }); // Get regular shift entries only const regularShifts = Vue.computed(() => { if (!shifts.value) return []; return shifts.value.filter(s => s.type === 'shift'); }); const onEnter = () => { hoveredDateStr.value = formatDateForId(props.date); }; const onLeave = () => { hoveredDateStr.value = null; }; const onClick = () => { emit('open-assignment', props.date); }; return { cellClass, shifts, regularShifts, specialStatus, locked, showShift, comment, note, commentTxt, noteTxt, today, onEnter, onLeave, onClick }; }, template: `
This cell is currently being edited by another user.
User Comment
{{ commentTxt }}
Technical Note
{{ noteTxt }}
` };