update
This commit is contained in:
parent
8ebd77540c
commit
8c818f8dde
17 changed files with 14799 additions and 322 deletions
|
|
@ -476,12 +476,13 @@ class Api {
|
|||
return await this.request(FRAPPE_GET_SERVICE_APPOINTMENTS_METHOD, { companies, filters });
|
||||
}
|
||||
|
||||
static async updateServiceAppointmentScheduledDates(serviceAppointmentName, startDate, endDate, crewLeadName, startTime = null, endTime = null) {
|
||||
static async updateServiceAppointmentScheduledDates(serviceAppointmentName, startDate = null, endDate = null, crewLeadName = null, skippedDays = [],startTime = null, endTime = null) {
|
||||
return await this.request(FRAPPE_UPDATE_SERVICE_APPOINTMENT_SCHEDULED_DATES_METHOD, {
|
||||
serviceAppointmentName,
|
||||
startDate,
|
||||
endDate,
|
||||
crewLeadName,
|
||||
skippedDays,
|
||||
startTime,
|
||||
endTime
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="calendar-container">
|
||||
<div class="calendar-container" :class="{ 'skip-mode': skipMode }">
|
||||
<div class="calendar-header">
|
||||
<h2>Weekly Schedule - {{ companyStore.currentCompany }}</h2>
|
||||
<div class="header-controls">
|
||||
|
|
@ -26,6 +26,16 @@
|
|||
<v-btn @click="goToThisWeek" variant="outlined" size="small" class="ml-4">
|
||||
This Week
|
||||
</v-btn>
|
||||
<v-btn
|
||||
@click="toggleSkipMode"
|
||||
:color="skipMode ? 'error' : 'default'"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
class="ml-4"
|
||||
>
|
||||
<v-icon left size="small">mdi-content-cut</v-icon>
|
||||
Skip Day
|
||||
</v-btn>
|
||||
<v-menu
|
||||
v-model="showTemplateMenu"
|
||||
:close-on-content-click="false"
|
||||
|
|
@ -158,6 +168,21 @@
|
|||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<!-- Holiday Prompt Dialog -->
|
||||
<v-dialog v-model="showHolidayPrompt" max-width="500px">
|
||||
<v-card>
|
||||
<v-card-title>Holiday Scheduling</v-card-title>
|
||||
<v-card-text>
|
||||
The event has been scheduled on or over a holiday/Sunday. Should this day be skipped?
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn @click="handleHolidayChoice(false)">Include Holiday</v-btn>
|
||||
<v-btn color="primary" @click="handleHolidayChoice(true)">Skip Holiday</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<div class="calendar-main">
|
||||
<!-- Weekly Calendar Grid -->
|
||||
<div class="calendar-section">
|
||||
|
|
@ -199,6 +224,7 @@
|
|||
@dragover="handleDragOver($event, foreman.name, day.date)"
|
||||
@dragleave="handleDragLeave"
|
||||
@drop="handleDrop($event, foreman.name, day.date)"
|
||||
@click="skipMode ? handleSkipDayClick(foreman.name, day.date) : null"
|
||||
>
|
||||
<!-- Jobs in this day -->
|
||||
<div
|
||||
|
|
@ -222,8 +248,22 @@
|
|||
<v-icon v-if="jobSpansToNextWeek(job)" size="small" class="spans-arrow-right">mdi-arrow-right</v-icon>
|
||||
<div class="resize-handle"></div>
|
||||
</div>
|
||||
|
||||
<!-- Holiday connector line for split jobs -->
|
||||
|
||||
<!-- Skipped days -->
|
||||
<div
|
||||
v-for="job in getSkippedJobsForCell(foreman.name, day.date)"
|
||||
:key="`skip-${job.name}`"
|
||||
class="skipped-day"
|
||||
:class="getPriorityClass(job.priority)"
|
||||
>
|
||||
<button
|
||||
class="remove-skip-btn"
|
||||
@click.stop="handleRemoveSkip(job, day.date)"
|
||||
title="Remove skipped day"
|
||||
>
|
||||
<v-icon size="small">mdi-close</v-icon>
|
||||
</button>
|
||||
</div>
|
||||
<template v-if="isHoliday(day.date)">
|
||||
<div
|
||||
v-for="job in getJobsWithConnector(foreman.name, day.date)"
|
||||
|
|
@ -374,6 +414,14 @@ const showForemenMenu = ref(false);
|
|||
const showDatePicker = ref(false);
|
||||
const selectedDate = ref(null);
|
||||
|
||||
// Holiday prompt
|
||||
const showHolidayPrompt = ref(false);
|
||||
const pendingAction = ref(null); // { type: 'drop' or 'resize', data: {...} }
|
||||
const holidayDates = ref([]); // dates that are holidays in the range
|
||||
|
||||
// Skip mode
|
||||
const skipMode = ref(false);
|
||||
|
||||
// Project template filter
|
||||
const selectedProjectTemplates = ref([]);
|
||||
const showTemplateMenu = ref(false);
|
||||
|
|
@ -513,21 +561,21 @@ function getCrewName(foremanId) {
|
|||
return foreman.customCrew ? `${foreman.employeeName} (Crew ${foreman.customCrew})` : foreman.employeeName;
|
||||
}
|
||||
|
||||
// Helper function to get all holidays in a date range
|
||||
function getHolidaysInRange(startDate, endDate) {
|
||||
// Helper function to get all Sundays in a date range
|
||||
function getSundaysInRange(startDate, endDate) {
|
||||
const start = parseLocalDate(startDate);
|
||||
const end = parseLocalDate(endDate);
|
||||
const holidaysInRange = [];
|
||||
const sundays = [];
|
||||
for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
|
||||
const dateStr = toLocalDateString(d);
|
||||
if (isHoliday(dateStr)) {
|
||||
holidaysInRange.push(dateStr);
|
||||
if (isSunday(dateStr)) {
|
||||
sundays.push(dateStr);
|
||||
}
|
||||
}
|
||||
return holidaysInRange;
|
||||
return sundays;
|
||||
}
|
||||
|
||||
// Helper function to calculate job segments (parts between holidays/Sundays)
|
||||
// Helper function to calculate job segments (parts between holidays/Sundays/skipDays)
|
||||
function getJobSegments(job) {
|
||||
const startDate = job.expectedStartDate;
|
||||
const endDate = job.expectedEndDate || job.expectedStartDate;
|
||||
|
|
@ -547,8 +595,9 @@ function getJobSegments(job) {
|
|||
for (let d = new Date(start); d <= effectiveEnd; d.setDate(d.getDate() + 1)) {
|
||||
const dateStr = toLocalDateString(d);
|
||||
|
||||
// If we hit a holiday or Sunday, close the current segment
|
||||
if (isHoliday(dateStr) || isSunday(dateStr)) {
|
||||
// If we hit a holiday, Sunday, or skipped day, close the current segment
|
||||
const isSkipped = job.skipDays && job.skipDays.some(skip => skip.date === dateStr);
|
||||
if (isHoliday(dateStr) || isSunday(dateStr) || isSkipped) {
|
||||
// Close previous segment if it exists
|
||||
if (segmentStart !== null) {
|
||||
const prevDate = toLocalDateString(new Date(d.getTime() - 86400000)); // Previous day
|
||||
|
|
@ -658,8 +707,24 @@ const getJobsForCell = (foremanId, date) => {
|
|||
|
||||
// Check if this date falls within the job's date range
|
||||
// AND that it's a valid segment start date
|
||||
// AND not in skipDays
|
||||
const segments = getJobSegments(job);
|
||||
return segments.some(seg => seg.start === date);
|
||||
const isSkipped = job.skipDays && job.skipDays.some(skip => skip.date === date);
|
||||
return segments.some(seg => seg.start === date) && !isSkipped;
|
||||
});
|
||||
};
|
||||
|
||||
// Get skipped jobs for a specific foreman and date
|
||||
const getSkippedJobsForCell = (foremanId, date) => {
|
||||
return scheduledServices.value.filter((job) => {
|
||||
if (job.foreman !== foremanId) return false;
|
||||
|
||||
const jobStart = job.expectedStartDate;
|
||||
const jobEnd = job.expectedEndDate || job.expectedStartDate;
|
||||
|
||||
// Check if this date is in the job's skipDays
|
||||
const isSkipped = job.skipDays && job.skipDays.some(skip => skip.date === date);
|
||||
return isSkipped && parseLocalDate(date) >= parseLocalDate(jobStart) && parseLocalDate(date) <= parseLocalDate(jobEnd);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -758,6 +823,112 @@ const goToThisWeek = () => {
|
|||
weekStartDate.value = getWeekStart(new Date());
|
||||
};
|
||||
|
||||
const toggleSkipMode = () => {
|
||||
skipMode.value = !skipMode.value;
|
||||
};
|
||||
|
||||
const handleSkipDayClick = async (foremanId, date) => {
|
||||
// Find jobs that cover this date for this foreman
|
||||
const jobs = scheduledServices.value.filter(job => {
|
||||
if (job.foreman !== foremanId) return false;
|
||||
const start = job.expectedStartDate;
|
||||
const end = job.expectedEndDate || start;
|
||||
return date >= start && date <= end;
|
||||
});
|
||||
|
||||
if (jobs.length === 0) {
|
||||
notifications.addInfo("No jobs found for this day.");
|
||||
return;
|
||||
}
|
||||
|
||||
// For now, take the first job. In future, could show selection if multiple.
|
||||
const job = jobs[0];
|
||||
|
||||
// Check if already skipped
|
||||
const isAlreadySkipped = job.skipDays && job.skipDays.some(skip => skip.date === date);
|
||||
if (isAlreadySkipped) {
|
||||
notifications.addInfo("This day is already skipped.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prompt
|
||||
const confirmed = confirm(`Are you sure you want to skip ${formatDate(date)} for job ${job.projectTemplate}?`);
|
||||
if (!confirmed) return;
|
||||
|
||||
// Add to skipDays
|
||||
const newSkipDays = [...(job.skipDays || []), { date }];
|
||||
|
||||
// Update local
|
||||
const serviceIndex = scheduledServices.value.findIndex(s => s.name === job.name);
|
||||
if (serviceIndex !== -1) {
|
||||
scheduledServices.value[serviceIndex] = {
|
||||
...scheduledServices.value[serviceIndex],
|
||||
skipDays: newSkipDays
|
||||
};
|
||||
}
|
||||
|
||||
// Call API
|
||||
try {
|
||||
await Api.updateServiceAppointmentScheduledDates(
|
||||
job.name,
|
||||
job.expectedStartDate,
|
||||
job.expectedEndDate,
|
||||
job.foreman,
|
||||
newSkipDays
|
||||
);
|
||||
notifications.addSuccess("Day skipped successfully!");
|
||||
} catch (error) {
|
||||
console.error("Error skipping day:", error);
|
||||
notifications.addError("Failed to skip day");
|
||||
// Revert
|
||||
if (serviceIndex !== -1) {
|
||||
scheduledServices.value[serviceIndex] = {
|
||||
...scheduledServices.value[serviceIndex],
|
||||
skipDays: job.skipDays
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Remove skip day
|
||||
const handleRemoveSkip = async (job, date) => {
|
||||
const confirmed = confirm(`Remove skipped day ${formatDate(date)} for job ${job.projectTemplate}?`);
|
||||
if (!confirmed) return;
|
||||
|
||||
const newSkipDays = (job.skipDays || []).filter(skip => skip.date !== date);
|
||||
|
||||
// Update local
|
||||
const serviceIndex = scheduledServices.value.findIndex(s => s.name === job.name);
|
||||
if (serviceIndex !== -1) {
|
||||
scheduledServices.value[serviceIndex] = {
|
||||
...scheduledServices.value[serviceIndex],
|
||||
skipDays: newSkipDays
|
||||
};
|
||||
}
|
||||
|
||||
// Call API
|
||||
try {
|
||||
await Api.updateServiceAppointmentScheduledDates(
|
||||
job.name,
|
||||
job.expectedStartDate,
|
||||
job.expectedEndDate,
|
||||
job.foreman,
|
||||
newSkipDays
|
||||
);
|
||||
notifications.addSuccess("Skipped day removed successfully!");
|
||||
} catch (error) {
|
||||
console.error("Error removing skipped day:", error);
|
||||
notifications.addError("Failed to remove skipped day");
|
||||
// Revert
|
||||
if (serviceIndex !== -1) {
|
||||
scheduledServices.value[serviceIndex] = {
|
||||
...scheduledServices.value[serviceIndex],
|
||||
skipDays: job.skipDays
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Foremen selection methods
|
||||
const toggleAllForemen = () => {
|
||||
if (selectedForemen.value.length === foremen.value.length) {
|
||||
|
|
@ -924,25 +1095,6 @@ const handleDrop = async (event, foremanId, date) => {
|
|||
|
||||
if (!draggedService.value) return;
|
||||
|
||||
// Prevent dropping on Sunday
|
||||
if (isSunday(date)) {
|
||||
notifications.addError("Cannot schedule jobs on Sunday. Please select a weekday.");
|
||||
isDragOver.value = false;
|
||||
dragOverCell.value = null;
|
||||
draggedService.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent dropping on holidays
|
||||
if (isHoliday(date)) {
|
||||
const holidayDesc = getHolidayDescription(date);
|
||||
notifications.addError(`Cannot schedule jobs on ${holidayDesc || 'a holiday'}. Please select a different date.`);
|
||||
isDragOver.value = false;
|
||||
dragOverCell.value = null;
|
||||
draggedService.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get foreman details
|
||||
const foreman = foremen.value.find(f => f.name === foremanId);
|
||||
if (!foreman) return;
|
||||
|
|
@ -950,16 +1102,29 @@ const handleDrop = async (event, foremanId, date) => {
|
|||
// Default to single day
|
||||
let endDate = date;
|
||||
|
||||
// Check for holidays in the range
|
||||
if (hasHolidayInRange(date, endDate)) {
|
||||
notifications.addError("Cannot schedule job on a holiday. Please select different dates.");
|
||||
// Reset drag state
|
||||
// Check for holidays or Sundays in the range
|
||||
const holidaysInRange = getHolidaysInRange(date, endDate);
|
||||
const sundaysInRange = getSundaysInRange(date, endDate);
|
||||
const conflictingDates = [...holidaysInRange, ...sundaysInRange];
|
||||
|
||||
if (conflictingDates.length > 0) {
|
||||
// Show prompt
|
||||
holidayDates.value = conflictingDates;
|
||||
pendingAction.value = {
|
||||
type: 'drop',
|
||||
data: { event, foremanId, date, endDate, foreman }
|
||||
};
|
||||
showHolidayPrompt.value = true;
|
||||
isDragOver.value = false;
|
||||
dragOverCell.value = null;
|
||||
draggedService.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Proceed with normal drop
|
||||
await performDrop(foremanId, date, endDate, foreman);
|
||||
};
|
||||
|
||||
const performDrop = async (foremanId, date, endDate, foreman, skipDays = []) => {
|
||||
// Check if this is scheduling an unscheduled job or moving a scheduled job
|
||||
const unscheduledIndex = unscheduledServices.value.findIndex(s => s.name === draggedService.value.name);
|
||||
const scheduledIndex = scheduledServices.value.findIndex(s => s.name === draggedService.value.name);
|
||||
|
|
@ -975,7 +1140,8 @@ const handleDrop = async (event, foremanId, date) => {
|
|||
draggedService.value.name,
|
||||
date,
|
||||
endDate,
|
||||
foreman.name
|
||||
foreman.name,
|
||||
skipDays
|
||||
);
|
||||
// Remove from unscheduled and add to scheduled
|
||||
const scheduledService = {
|
||||
|
|
@ -983,7 +1149,8 @@ const handleDrop = async (event, foremanId, date) => {
|
|||
expectedStartDate: date,
|
||||
expectedEndDate: endDate,
|
||||
foreman: foreman.name,
|
||||
status: 'Scheduled'
|
||||
status: 'Scheduled',
|
||||
skipDays: skipDays
|
||||
};
|
||||
unscheduledServices.value.splice(unscheduledIndex, 1);
|
||||
scheduledServices.value.push(scheduledService);
|
||||
|
|
@ -1002,14 +1169,16 @@ const handleDrop = async (event, foremanId, date) => {
|
|||
draggedService.value.name,
|
||||
date,
|
||||
date, // Reset to single day when moved
|
||||
foreman.name
|
||||
foreman.name,
|
||||
skipDays
|
||||
);
|
||||
// Update the scheduled job
|
||||
scheduledServices.value[scheduledIndex] = {
|
||||
...scheduledServices.value[scheduledIndex],
|
||||
expectedStartDate: date,
|
||||
expectedEndDate: date, // Reset to single day
|
||||
foreman: foreman.name
|
||||
foreman: foreman.name,
|
||||
skipDays: skipDays
|
||||
};
|
||||
notifications.addSuccess("Job moved successfully!");
|
||||
} catch (error) {
|
||||
|
|
@ -1024,12 +1193,57 @@ const handleDrop = async (event, foremanId, date) => {
|
|||
draggedService.value = null;
|
||||
};
|
||||
|
||||
// Handle dropping scheduled items back to unscheduled
|
||||
const handleUnscheduledDragOver = (event) => {
|
||||
// Only allow dropping if the dragged job is scheduled
|
||||
if (draggedService.value && draggedService.value.status === 'Scheduled') {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = "move";
|
||||
const handleHolidayChoice = async (skip) => {
|
||||
showHolidayPrompt.value = false;
|
||||
const action = pendingAction.value;
|
||||
if (!action) return;
|
||||
|
||||
const skipDays = skip ? holidayDates.value : [];
|
||||
|
||||
if (action.type === 'drop') {
|
||||
const { foremanId, date, endDate, foreman } = action.data;
|
||||
await performDrop(foremanId, date, endDate, foreman, skipDays);
|
||||
} else if (action.type === 'resize') {
|
||||
await performResize(action.data, skipDays);
|
||||
}
|
||||
|
||||
pendingAction.value = null;
|
||||
holidayDates.value = [];
|
||||
};
|
||||
|
||||
const performResize = async (data, skipDays) => {
|
||||
const { job, newEndDate, originalEndDate } = data;
|
||||
|
||||
// Update the job with new end date and skipDays
|
||||
const serviceIndex = scheduledServices.value.findIndex(s => s.name === job.name);
|
||||
if (serviceIndex !== -1) {
|
||||
const originalSkipDays = scheduledServices.value[serviceIndex].skipDays;
|
||||
scheduledServices.value[serviceIndex] = {
|
||||
...scheduledServices.value[serviceIndex],
|
||||
expectedEndDate: newEndDate,
|
||||
skipDays: skipDays
|
||||
};
|
||||
|
||||
// Call API to persist changes
|
||||
try {
|
||||
await Api.updateServiceAppointmentScheduledDates(
|
||||
job.name,
|
||||
job.expectedStartDate,
|
||||
newEndDate,
|
||||
job.foreman,
|
||||
skipDays
|
||||
);
|
||||
notifications.addSuccess("Job end date updated successfully!");
|
||||
} catch (error) {
|
||||
console.error("Error updating job end date:", error);
|
||||
notifications.addError("Failed to update job end date");
|
||||
// Revert on error
|
||||
scheduledServices.value[serviceIndex] = {
|
||||
...scheduledServices.value[serviceIndex],
|
||||
expectedEndDate: originalEndDate,
|
||||
skipDays: originalSkipDays
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1070,7 +1284,8 @@ const handleUnscheduledDrop = async (event) => {
|
|||
draggedService.value.name,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
null,
|
||||
[]
|
||||
)
|
||||
notifications.addSuccess("Job unscheduled successfully!");
|
||||
} catch (error) {
|
||||
|
|
@ -1112,7 +1327,8 @@ const handleScheduledDrop = async (job, event, foremanId, date) => {
|
|||
draggedService.value.name,
|
||||
date,
|
||||
draggedService.value.expectedEndDate,
|
||||
foremanId
|
||||
foremanId,
|
||||
draggedService.value.skipDays || []
|
||||
);
|
||||
scheduledServices.value[serviceIndex] = {
|
||||
...scheduledServices.value[serviceIndex],
|
||||
|
|
@ -1198,37 +1414,24 @@ const handleResize = (event) => {
|
|||
const proposedDate = parseLocalDate(proposedEndDate);
|
||||
const saturdayDate = parseLocalDate(weekEndDate);
|
||||
|
||||
// Check for holidays in the EXTENSION range only (from current end to proposed end)
|
||||
// Check for holidays and Sundays in the EXTENSION range only (from current end to proposed end)
|
||||
const holidaysInRange = getHolidaysInRange(currentEndDate, proposedEndDate);
|
||||
if (holidaysInRange.length > 0) {
|
||||
hasHolidayBlock = true;
|
||||
// Allow the extension - visual split will be handled by getJobSegments
|
||||
newEndDate = proposedEndDate;
|
||||
const sundaysInRange = getSundaysInRange(currentEndDate, proposedEndDate);
|
||||
const conflictingDates = [...holidaysInRange, ...sundaysInRange];
|
||||
|
||||
if (conflictingDates.length > 0) {
|
||||
// Show prompt
|
||||
holidayDates.value = conflictingDates;
|
||||
pendingAction.value = {
|
||||
type: 'resize',
|
||||
data: { job: resizingJob.value, newEndDate: proposedEndDate, originalEndDate: currentEndDate }
|
||||
};
|
||||
showHolidayPrompt.value = true;
|
||||
return; // Don't update yet
|
||||
}
|
||||
|
||||
// Check if the proposed end date extends past Saturday or lands on Sunday
|
||||
if (proposedDate > saturdayDate || isSunday(proposedEndDate)) {
|
||||
extendsOverSunday = true;
|
||||
// Set logical end date to next Monday, but visually cap at Saturday
|
||||
newEndDate = getNextMonday(weekEndDate);
|
||||
} else if (!hasHolidayBlock && daysToAdd > 0) {
|
||||
// Check if any date in the EXTENSION range is Sunday (only if no holiday block)
|
||||
// Only check from current end to proposed end
|
||||
const startCheck = parseLocalDate(currentEndDate);
|
||||
const endCheck = parseLocalDate(proposedEndDate);
|
||||
for (let d = new Date(startCheck.getTime() + 86400000); d <= endCheck; d.setDate(d.getDate() + 1)) {
|
||||
const checkDate = toLocalDateString(d);
|
||||
if (isSunday(checkDate)) {
|
||||
extendsOverSunday = true;
|
||||
// Extend to next Monday
|
||||
newEndDate = getNextMonday(checkDate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show popup if extending over Sunday
|
||||
showExtendToNextWeekPopup.value = extendsOverSunday;
|
||||
// No conflicts, proceed
|
||||
newEndDate = proposedEndDate;
|
||||
|
||||
const serviceIndex = scheduledServices.value.findIndex(s => s.name === resizingJob.value.name);
|
||||
if (serviceIndex !== -1) {
|
||||
|
|
@ -1272,7 +1475,8 @@ const stopResize = async () => {
|
|||
job.name,
|
||||
job.expectedStartDate,
|
||||
newEndDate,
|
||||
job.foreman
|
||||
job.foreman,
|
||||
job.skipDays || []
|
||||
);
|
||||
notifications.addSuccess("Job end date updated successfully!");
|
||||
} catch (error) {
|
||||
|
|
@ -1860,6 +2064,59 @@ onMounted(async () => {
|
|||
color: #4caf50;
|
||||
}
|
||||
|
||||
.skipped-day {
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
right: 4px;
|
||||
top: 50%;
|
||||
height: 3px;
|
||||
transform: translateY(-50%);
|
||||
border-top: 3px dotted currentColor;
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.skipped-day .remove-skip-btn {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background: white;
|
||||
border: 1px solid #ccc;
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
pointer-events: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.skipped-day:hover .remove-skip-btn {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.skipped-day .remove-skip-btn:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.skipped-day.priority-urgent {
|
||||
color: #f44336;
|
||||
}
|
||||
|
||||
.skipped-day.priority-high {
|
||||
color: #ff9800;
|
||||
}
|
||||
|
||||
.skipped-day.priority-medium {
|
||||
color: #2196f3;
|
||||
}
|
||||
|
||||
.skipped-day.priority-low {
|
||||
color: #4caf50;
|
||||
}
|
||||
|
||||
.extend-popup {
|
||||
position: fixed;
|
||||
top: 100px;
|
||||
|
|
@ -1897,4 +2154,12 @@ onMounted(async () => {
|
|||
align-items: center;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.calendar-container.skip-mode {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.calendar-container.skip-mode .day-cell {
|
||||
cursor: crosshair;
|
||||
}
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue