|
|
@ -11,6 +11,7 @@ const { |
|
|
|
formattedSideBarData, |
|
|
|
formattedSideBarData, |
|
|
|
calendarRange, |
|
|
|
calendarRange, |
|
|
|
displayField, |
|
|
|
displayField, |
|
|
|
|
|
|
|
viewMetaProperties, |
|
|
|
selectedTime, |
|
|
|
selectedTime, |
|
|
|
updateRowProperty, |
|
|
|
updateRowProperty, |
|
|
|
sideBarFilterOption, |
|
|
|
sideBarFilterOption, |
|
|
@ -25,6 +26,8 @@ const scrollContainer = ref<null | HTMLElement>(null) |
|
|
|
|
|
|
|
|
|
|
|
const { width: containerWidth } = useElementSize(container) |
|
|
|
const { width: containerWidth } = useElementSize(container) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const isPublic = inject(IsPublicInj, ref(false)) |
|
|
|
|
|
|
|
|
|
|
|
const { isUIAllowed } = useRoles() |
|
|
|
const { isUIAllowed } = useRoles() |
|
|
|
|
|
|
|
|
|
|
|
const meta = inject(MetaInj, ref()) |
|
|
|
const meta = inject(MetaInj, ref()) |
|
|
@ -47,6 +50,50 @@ const fieldStyles = computed(() => { |
|
|
|
) |
|
|
|
) |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const getDayIndex = (date: dayjs.Dayjs) => { |
|
|
|
|
|
|
|
let dayIndex = date.day() - 1 |
|
|
|
|
|
|
|
if (dayIndex === -1) { |
|
|
|
|
|
|
|
dayIndex = 6 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return dayIndex |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const maxVisibleDays = computed(() => { |
|
|
|
|
|
|
|
return viewMetaProperties.value?.hide_weekend ? 5 : 7 |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const currTime = ref(dayjs()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const overlayStyle = computed(() => { |
|
|
|
|
|
|
|
if (!containerWidth.value) |
|
|
|
|
|
|
|
return { |
|
|
|
|
|
|
|
top: 0, |
|
|
|
|
|
|
|
left: 0, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const left = (containerWidth.value / maxVisibleDays.value) * getDayIndex(currTime.value) |
|
|
|
|
|
|
|
const minutes = currTime.value.hour() * 60 + currTime.value.minute() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const top = (52 / 60) * minutes |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return { |
|
|
|
|
|
|
|
width: `${containerWidth.value / maxVisibleDays.value}px`, |
|
|
|
|
|
|
|
top: `${top}px`, |
|
|
|
|
|
|
|
left: `${left}px`, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => { |
|
|
|
|
|
|
|
const intervalId = setInterval(() => { |
|
|
|
|
|
|
|
currTime.value = dayjs() |
|
|
|
|
|
|
|
}, 10000) // 10000 ms = 10 seconds |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Clean up the interval when the component is unmounted |
|
|
|
|
|
|
|
onUnmounted(() => { |
|
|
|
|
|
|
|
clearInterval(intervalId) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
const getFieldStyle = (field: ColumnType) => { |
|
|
|
const getFieldStyle = (field: ColumnType) => { |
|
|
|
return fieldStyles.value.get(field.id) |
|
|
|
return fieldStyles.value.get(field.id) |
|
|
|
} |
|
|
|
} |
|
|
@ -85,7 +132,11 @@ const calculateNewDates = useMemoize( |
|
|
|
const datesHours = computed(() => { |
|
|
|
const datesHours = computed(() => { |
|
|
|
const datesHours: Array<Array<dayjs.Dayjs>> = [] |
|
|
|
const datesHours: Array<Array<dayjs.Dayjs>> = [] |
|
|
|
let startOfWeek = dayjs(selectedDateRange.value.start) ?? dayjs().startOf('week') |
|
|
|
let startOfWeek = dayjs(selectedDateRange.value.start) ?? dayjs().startOf('week') |
|
|
|
const endOfWeek = dayjs(selectedDateRange.value.end) ?? dayjs().endOf('week') |
|
|
|
let endOfWeek = dayjs(selectedDateRange.value.end) ?? dayjs().endOf('week') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (maxVisibleDays.value === 5) { |
|
|
|
|
|
|
|
endOfWeek = endOfWeek.subtract(2, 'day') |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
while (startOfWeek.isSameOrBefore(endOfWeek)) { |
|
|
|
while (startOfWeek.isSameOrBefore(endOfWeek)) { |
|
|
|
const hours: Array<dayjs.Dayjs> = [] |
|
|
|
const hours: Array<dayjs.Dayjs> = [] |
|
|
@ -107,14 +158,6 @@ const datesHours = computed(() => { |
|
|
|
return datesHours |
|
|
|
return datesHours |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
const getDayIndex = (date: dayjs.Dayjs) => { |
|
|
|
|
|
|
|
let dayIndex = date.day() - 1 |
|
|
|
|
|
|
|
if (dayIndex === -1) { |
|
|
|
|
|
|
|
dayIndex = 6 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return dayIndex |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const getGridTime = (date: dayjs.Dayjs, round = false) => { |
|
|
|
const getGridTime = (date: dayjs.Dayjs, round = false) => { |
|
|
|
const gridCalc = date.hour() * 60 + date.minute() |
|
|
|
const gridCalc = date.hour() * 60 + date.minute() |
|
|
|
if (round) { |
|
|
|
if (round) { |
|
|
@ -201,8 +244,20 @@ const getMaxOverlaps = ({ |
|
|
|
|
|
|
|
|
|
|
|
let maxOverlaps = 1 |
|
|
|
let maxOverlaps = 1 |
|
|
|
if (graph.has(id)) { |
|
|
|
if (graph.has(id)) { |
|
|
|
maxOverlaps = dfs(id) |
|
|
|
dfs(id) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const overlapIterations: Array<number> = [] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
columnArray[dayIndex] |
|
|
|
|
|
|
|
.flat() |
|
|
|
|
|
|
|
.filter((record) => visited.has(record.rowMeta.id!)) |
|
|
|
|
|
|
|
.forEach((record) => { |
|
|
|
|
|
|
|
overlapIterations.push(record.rowMeta.overLapIteration!) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
maxOverlaps = Math.max(...overlapIterations) |
|
|
|
|
|
|
|
|
|
|
|
return { maxOverlaps, dayIndex, overlapIndex } |
|
|
|
return { maxOverlaps, dayIndex, overlapIndex } |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -224,11 +279,15 @@ const recordsAcrossAllRange = computed<{ |
|
|
|
records: [], |
|
|
|
records: [], |
|
|
|
gridTimeMap: new Map(), |
|
|
|
gridTimeMap: new Map(), |
|
|
|
} |
|
|
|
} |
|
|
|
const perWidth = containerWidth.value / 7 |
|
|
|
const perWidth = containerWidth.value / maxVisibleDays.value |
|
|
|
const perHeight = 52 |
|
|
|
const perHeight = 52 |
|
|
|
|
|
|
|
|
|
|
|
const scheduleStart = dayjs(selectedDateRange.value.start).startOf('day') |
|
|
|
const scheduleStart = dayjs(selectedDateRange.value.start).startOf('day') |
|
|
|
const scheduleEnd = dayjs(selectedDateRange.value.end).endOf('day') |
|
|
|
let scheduleEnd = dayjs(selectedDateRange.value.end).endOf('day') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (maxVisibleDays.value === 5) { |
|
|
|
|
|
|
|
scheduleEnd = scheduleEnd.subtract(2, 'day') |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const columnArray: Array<Array<Array<Row>>> = [[[]]] |
|
|
|
const columnArray: Array<Array<Array<Row>>> = [[[]]] |
|
|
|
const gridTimeMap = new Map< |
|
|
|
const gridTimeMap = new Map< |
|
|
@ -489,17 +548,25 @@ const recordsAcrossAllRange = computed<{ |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
const dayIndex = record.rowMeta.dayIndex ?? tDayIndex |
|
|
|
const dayIndex = record.rowMeta.dayIndex ?? tDayIndex |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let display = 'block' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (maxVisibleDays.value === 5) { |
|
|
|
|
|
|
|
if (dayIndex === 5 || dayIndex === 6) { |
|
|
|
|
|
|
|
display = 'none' |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
record.rowMeta.numberOfOverlaps = maxOverlaps |
|
|
|
record.rowMeta.numberOfOverlaps = maxOverlaps |
|
|
|
|
|
|
|
|
|
|
|
let width = 0 |
|
|
|
let width = 0 |
|
|
|
let left = 100 |
|
|
|
let left = 100 |
|
|
|
const majorLeft = dayIndex * perWidth |
|
|
|
const majorLeft = dayIndex * perWidth |
|
|
|
let display = 'block' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (record.rowMeta.overLapIteration! - 1 > 2) { |
|
|
|
if (record.rowMeta.overLapIteration! - 1 > 2) { |
|
|
|
display = 'none' |
|
|
|
display = 'none' |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
width = 100 / Math.min(maxOverlaps, 3) / 7 |
|
|
|
width = 100 / Math.min(maxOverlaps, 3) / maxVisibleDays.value |
|
|
|
left = width * (overlapIndex - 1) |
|
|
|
left = width * (overlapIndex - 1) |
|
|
|
} |
|
|
|
} |
|
|
|
record.rowMeta.style = { |
|
|
|
record.rowMeta.style = { |
|
|
@ -563,7 +630,7 @@ const onResize = (event: MouseEvent) => { |
|
|
|
const ogEndDate = dayjs(resizeRecord.value.row[toCol.title!]) |
|
|
|
const ogEndDate = dayjs(resizeRecord.value.row[toCol.title!]) |
|
|
|
const ogStartDate = dayjs(resizeRecord.value.row[fromCol.title!]) |
|
|
|
const ogStartDate = dayjs(resizeRecord.value.row[fromCol.title!]) |
|
|
|
|
|
|
|
|
|
|
|
const day = Math.floor(percentX * 7) |
|
|
|
const day = Math.floor(percentX * maxVisibleDays.value) |
|
|
|
const hour = Math.floor(percentY * 23) |
|
|
|
const hour = Math.floor(percentY * 23) |
|
|
|
const minutes = Math.round((percentY * 24 * 60) % 60) |
|
|
|
const minutes = Math.round((percentY * 24 * 60) % 60) |
|
|
|
|
|
|
|
|
|
|
@ -656,7 +723,7 @@ const calculateNewRow = ( |
|
|
|
|
|
|
|
|
|
|
|
if (!fromCol) return { newRow: null, updatedProperty: [] } |
|
|
|
if (!fromCol) return { newRow: null, updatedProperty: [] } |
|
|
|
|
|
|
|
|
|
|
|
const day = Math.max(0, Math.min(6, Math.floor(percentX * 7))) |
|
|
|
const day = Math.max(0, Math.min(6, Math.floor(percentX * maxVisibleDays.value))) |
|
|
|
const hour = Math.max(0, Math.min(23, Math.floor(percentY * 24))) |
|
|
|
const hour = Math.max(0, Math.min(23, Math.floor(percentY * 24))) |
|
|
|
|
|
|
|
|
|
|
|
const minutes = Math.round(((percentY * 24 * 60) % 60) / 15) * 15 |
|
|
|
const minutes = Math.round(((percentY * 24 * 60) % 60) / 15) * 15 |
|
|
@ -877,14 +944,32 @@ watch( |
|
|
|
data-testid="nc-calendar-week-view" |
|
|
|
data-testid="nc-calendar-week-view" |
|
|
|
@drop="dropEvent" |
|
|
|
@drop="dropEvent" |
|
|
|
> |
|
|
|
> |
|
|
|
|
|
|
|
<div |
|
|
|
|
|
|
|
v-if="!isPublic && dayjs().isBetween(selectedDateRange.start, selectedDateRange.end)" |
|
|
|
|
|
|
|
class="absolute ml-16 mt-7 pointer-events-none z-4" |
|
|
|
|
|
|
|
:style="overlayStyle" |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<div class="flex w-full items-center"> |
|
|
|
|
|
|
|
<span |
|
|
|
|
|
|
|
class="text-brand-500 rounded-md text-xs border-1 pointer-events-auto px-0.5 border-brand-200 cursor-pointer bg-brand-50" |
|
|
|
|
|
|
|
@click="addRecord(dayjs())" |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
{{ dayjs().format('hh:mm A') }} |
|
|
|
|
|
|
|
</span> |
|
|
|
|
|
|
|
<div class="flex-1 border-b-1 border-brand-500"></div> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="flex sticky h-6 z-1 top-0 pl-16 bg-gray-50 w-full"> |
|
|
|
<div class="flex sticky h-6 z-1 top-0 pl-16 bg-gray-50 w-full"> |
|
|
|
<div |
|
|
|
<div |
|
|
|
v-for="date in datesHours" |
|
|
|
v-for="date in datesHours" |
|
|
|
:key="date[0].toISOString()" |
|
|
|
:key="date[0].toISOString()" |
|
|
|
:class="{ |
|
|
|
:class="{ |
|
|
|
'text-brand-500': date[0].isSame(dayjs(), 'date'), |
|
|
|
'text-brand-500': date[0].isSame(dayjs(), 'date'), |
|
|
|
|
|
|
|
'w-1/5': maxVisibleDays === 5, |
|
|
|
|
|
|
|
'w-1/7': maxVisibleDays === 7, |
|
|
|
}" |
|
|
|
}" |
|
|
|
class="w-1/7 text-center text-[10px] font-semibold leading-4 flex items-center justify-center uppercase text-gray-500 w-full py-1 border-gray-200 last:border-r-0 border-b-1 border-l-1 border-r-0 bg-gray-50" |
|
|
|
class="text-center text-[10px] font-semibold leading-4 flex items-center justify-center uppercase text-gray-500 w-full py-1 border-gray-200 last:border-r-0 border-b-1 border-l-1 border-r-0 bg-gray-50" |
|
|
|
> |
|
|
|
> |
|
|
|
{{ dayjs(date[0]).format('DD ddd') }} |
|
|
|
{{ dayjs(date[0]).format('DD ddd') }} |
|
|
|
</div> |
|
|
|
</div> |
|
|
@ -899,15 +984,27 @@ watch( |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div ref="container" class="absolute ml-16 flex w-[calc(100%-64px)]"> |
|
|
|
<div ref="container" class="absolute ml-16 flex w-[calc(100%-64px)]"> |
|
|
|
<div v-for="(date, index) in datesHours" :key="index" class="h-full w-1/7 mt-7.1" data-testid="nc-calendar-week-day"> |
|
|
|
<div |
|
|
|
|
|
|
|
v-for="(date, index) in datesHours" |
|
|
|
|
|
|
|
:key="index" |
|
|
|
|
|
|
|
:class="{ |
|
|
|
|
|
|
|
'w-1/5': maxVisibleDays === 5, |
|
|
|
|
|
|
|
'w-1/7': maxVisibleDays === 7, |
|
|
|
|
|
|
|
}" |
|
|
|
|
|
|
|
class="h-full mt-5.95" |
|
|
|
|
|
|
|
data-testid="nc-calendar-week-day" |
|
|
|
|
|
|
|
> |
|
|
|
<div |
|
|
|
<div |
|
|
|
v-for="(hour, hourIndex) in date" |
|
|
|
v-for="(hour, hourIndex) in date" |
|
|
|
:key="hourIndex" |
|
|
|
:key="hourIndex" |
|
|
|
:class="{ |
|
|
|
:class="{ |
|
|
|
|
|
|
|
'border-1 !border-brand-500 !bg-gray-100': |
|
|
|
|
|
|
|
hour.isSame(selectedTime, 'hour') && (hour.get('day') === 6 || hour.get('day') === 0), |
|
|
|
'border-1 !border-brand-500 bg-gray-50': hour.isSame(selectedTime, 'hour'), |
|
|
|
'border-1 !border-brand-500 bg-gray-50': hour.isSame(selectedTime, 'hour'), |
|
|
|
'!bg-gray-50': hour.get('day') === 0 || hour.get('day') === 6, |
|
|
|
'bg-gray-50 hover:bg-gray-100': hour.get('day') === 0 || hour.get('day') === 6, |
|
|
|
|
|
|
|
'hover:bg-gray-50': hour.get('day') !== 0 && hour.get('day') !== 6, |
|
|
|
}" |
|
|
|
}" |
|
|
|
class="text-center relative transition h-13 text-sm text-gray-500 w-full hover:bg-gray-50 py-1 border-transparent border-1 border-x-gray-100 border-t-gray-100 border-l-gray-200" |
|
|
|
class="text-center relative transition h-13 text-sm text-gray-500 w-full py-1 border-transparent border-1 border-x-gray-100 border-t-gray-100 border-l-gray-200" |
|
|
|
data-testid="nc-calendar-week-hour" |
|
|
|
data-testid="nc-calendar-week-hour" |
|
|
|
@dblclick="addRecord(hour)" |
|
|
|
@dblclick="addRecord(hour)" |
|
|
|
@click=" |
|
|
|
@click=" |
|
|
@ -934,17 +1031,18 @@ watch( |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div |
|
|
|
<div class="absolute pointer-events-none inset-0 overflow-hidden !mt-5.95" data-testid="nc-calendar-week-record-container"> |
|
|
|
class="absolute pointer-events-none inset-0 overflow-hidden !mt-[29px]" |
|
|
|
|
|
|
|
data-testid="nc-calendar-week-record-container" |
|
|
|
|
|
|
|
> |
|
|
|
|
|
|
|
<template v-for="(record, rowIndex) in recordsAcrossAllRange.records" :key="rowIndex"> |
|
|
|
<template v-for="(record, rowIndex) in recordsAcrossAllRange.records" :key="rowIndex"> |
|
|
|
<div |
|
|
|
<div |
|
|
|
v-if="record.rowMeta.style?.display !== 'none'" |
|
|
|
v-if="record.rowMeta.style?.display !== 'none'" |
|
|
|
:data-testid="`nc-calendar-week-record-${record.row[displayField!.title!]}`" |
|
|
|
:data-testid="`nc-calendar-week-record-${record.row[displayField!.title!]}`" |
|
|
|
:data-unique-id="record.rowMeta!.id" |
|
|
|
:data-unique-id="record.rowMeta!.id" |
|
|
|
:style="record.rowMeta!.style " |
|
|
|
:style="record.rowMeta!.style " |
|
|
|
class="absolute transition draggable-record w-1/7 group cursor-pointer pointer-events-auto" |
|
|
|
:class="{ |
|
|
|
|
|
|
|
'w-1/5': maxVisibleDays === 5, |
|
|
|
|
|
|
|
'w-1/7': maxVisibleDays === 7, |
|
|
|
|
|
|
|
}" |
|
|
|
|
|
|
|
class="absolute transition draggable-record group cursor-pointer pointer-events-auto" |
|
|
|
@mousedown.stop="dragStart($event, record)" |
|
|
|
@mousedown.stop="dragStart($event, record)" |
|
|
|
@mouseleave="hoverRecord = null" |
|
|
|
@mouseleave="hoverRecord = null" |
|
|
|
@mouseover="hoverRecord = record.rowMeta.id" |
|
|
|
@mouseover="hoverRecord = record.rowMeta.id" |
|
|
|