Added new modules and updated existing logic
This commit is contained in:
153
dev/ui-ux/Opus w images/src/components/grid-cell/grid-cell.js
Normal file
153
dev/ui-ux/Opus w images/src/components/grid-cell/grid-cell.js
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* 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: `
|
||||
<div :class="cellClass"
|
||||
@mouseenter="onEnter"
|
||||
@mouseleave="onLeave"
|
||||
@click="onClick">
|
||||
<template v-if="showShift">
|
||||
|
||||
<!-- Special Status (Sick Leave, Off, etc.) -->
|
||||
<div v-if="specialStatus" class="grid-cell-special"
|
||||
:style="{ backgroundColor: specialStatus.bgColor, borderColor: specialStatus.borderColor }">
|
||||
<div class="grid-cell-special-emoji" v-if="specialStatus.emoji">{{ specialStatus.emoji }}</div>
|
||||
<div class="grid-cell-special-label" :style="{ color: specialStatus.textColor }">{{ specialStatus.label }}</div>
|
||||
<div class="grid-cell-special-sub" :style="{ color: specialStatus.textColor }" v-if="specialStatus.id === 'sick_leave'">Full Day</div>
|
||||
</div>
|
||||
|
||||
<!-- Regular Shifts -->
|
||||
<template v-else-if="regularShifts.length > 0">
|
||||
<div v-for="(shift, idx) in regularShifts" :key="idx" class="grid-cell-shift"
|
||||
:class="{ 'grid-cell-shift-second': idx > 0 }">
|
||||
<div class="grid-cell-time">{{ shift.timeLabel }}</div>
|
||||
<div class="grid-cell-activity-badge"
|
||||
:style="{ backgroundColor: shift.activityBgColor, color: shift.activityTextColor }">
|
||||
{{ shift.activityLabel }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<!-- Lock overlay -->
|
||||
<div v-if="locked" class="grid-cell-locked-overlay">
|
||||
<q-icon name="lock" color="blue-grey-3" size="14px">
|
||||
<q-tooltip class="bg-grey-9 text-white shadow-4 q-pa-sm">This cell is currently being edited by another user.</q-tooltip>
|
||||
</q-icon>
|
||||
</div>
|
||||
|
||||
<!-- Comment / Note indicators -->
|
||||
<div class="grid-cell-indicators">
|
||||
<q-icon v-if="comment" name="chat_bubble" size="8px" color="blue-grey-3" class="cursor-help">
|
||||
<q-tooltip class="bg-white text-grey-9 border shadow-4 grid-cell-tooltip" anchor="top middle" self="bottom middle">
|
||||
<div class="text-weight-bold text-caption text-indigo-9 q-mb-xs">User Comment</div>
|
||||
<div class="text-caption text-grey-8">{{ commentTxt }}</div>
|
||||
</q-tooltip>
|
||||
</q-icon>
|
||||
<q-icon v-if="note" name="info" size="8px" color="orange-4" class="cursor-help">
|
||||
<q-tooltip class="bg-white text-grey-9 border shadow-4 grid-cell-tooltip" anchor="top middle" self="bottom middle">
|
||||
<div class="text-weight-bold text-caption text-orange-9 q-mb-xs">Technical Note</div>
|
||||
<div class="text-caption text-grey-8">{{ noteTxt }}</div>
|
||||
</q-tooltip>
|
||||
</q-icon>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
};
|
||||
Reference in New Issue
Block a user