Browse Source

Merge pull request #9901 from nocodb/nc-calendar-followup

fix: calendar followups
pull/9906/head
Anbarasu 2 days ago committed by GitHub
parent
commit
ead832b7f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 64
      packages/nc-gui/components/dlg/ViewCreate.vue
  2. 2
      packages/nc-gui/components/smartsheet/PlainCell.vue
  3. 22
      packages/nc-gui/components/smartsheet/calendar/DayView/DateField.vue
  4. 18
      packages/nc-gui/components/smartsheet/calendar/MonthView.vue
  5. 23
      packages/nc-gui/components/smartsheet/calendar/RecordCard.vue
  6. 12
      packages/nc-gui/components/smartsheet/calendar/VRecordCard.vue
  7. 60
      packages/nc-gui/components/smartsheet/calendar/WeekView/DateField.vue
  8. 18
      packages/nc-gui/components/smartsheet/calendar/WeekView/DateTimeField.vue
  9. 77
      packages/nc-gui/components/smartsheet/toolbar/Calendar/Range.vue
  10. 2
      packages/nc-gui/composables/useBetaFeatureToggle.ts
  11. 5
      packages/nc-gui/composables/useCalendarViewStore.ts
  12. 2
      packages/nc-gui/lang/en.json
  13. 1
      packages/nc-gui/lib/types.ts
  14. 23
      packages/noco-docs/docs/090.views/040.view-types/050.calendar.md
  15. BIN
      packages/noco-docs/static/img/v2/views/calendar/cal-range-1.png
  16. BIN
      packages/noco-docs/static/img/v2/views/calendar/cal-range-2.png
  17. BIN
      packages/noco-docs/static/img/v2/views/calendar/cal-range-3.png
  18. BIN
      packages/noco-docs/static/img/v2/views/calendar/cal-range-4.png

64
packages/nc-gui/components/dlg/ViewCreate.vue

@ -533,6 +533,25 @@ const isCalendarReadonly = (calendarRange?: Array<{ fk_from_column_id: string; f
})
}
const isDisabled = computed(() => {
return (
viewSelectFieldOptions.value.find((f) => f.value === form.calendar_range[0]?.fk_from_column_id)?.uidt === UITypes.DateTime &&
!isRangeEnabled.value
)
})
const onValueChange = async () => {
form.calendar_range = form.calendar_range.map((range, i) => {
if (i === 0) {
return {
fk_from_column_id: range.fk_from_column_id,
fk_to_column_id: null,
}
}
return range
})
}
const predictViews = async (): Promise<AiSuggestedViewType[]> => {
const viewType =
!isAIViewCreateMode.value && form.type && viewTypeToStringMap[form.type] ? viewTypeToStringMap[form.type] : undefined
@ -999,6 +1018,7 @@ const getPluralName = (name: string) => {
:placeholder="$t('placeholder.notSelected')"
data-testid="nc-calendar-range-from-field-select"
@click.stop
@change="onValueChange"
>
<template #suffixIcon><GeneralIcon icon="arrowDown" class="text-gray-700" /></template>
<a-select-option
@ -1034,19 +1054,17 @@ const getPluralName = (name: string) => {
</a-select>
</div>
<div class="w-full space-y-2">
<NcButton
v-if="range.fk_to_column_id === null && isRangeEnabled"
size="small"
class="w-28"
type="text"
:disabled="!isEeUI"
@click="range.fk_to_column_id = undefined"
>
<component :is="iconMap.plus" class="h-4 w-4" />
{{ $t('activity.endDate') }}
</NcButton>
<NcTooltip v-if="range.fk_to_column_id === null" placement="left" :disabled="!isDisabled">
<NcButton size="small" type="text" :disabled="!isEeUI || isDisabled" @click="range.fk_to_column_id = undefined">
<div class="flex items-center gap-1">
<component :is="iconMap.plus" class="h-4 w-4" />
{{ $t('activity.endDate') }}
</div>
</NcButton>
<template #title> Coming Soon!! Currently, range support is only available for Date field. </template>
</NcTooltip>
<template v-else-if="isEeUI && isRangeEnabled">
<template v-else-if="isEeUI">
<span class="text-gray-700">
{{ $t('activity.withEndDate') }}
</span>
@ -1054,8 +1072,9 @@ const getPluralName = (name: string) => {
<div class="flex">
<a-select
v-model:value="range.fk_to_column_id"
class="!rounded-r-none nc-select-shadow w-full flex-1 nc-to-select"
:disabled="isMetaLoading"
class="nc-select-shadow w-full flex-1"
allow-clear
:disabled="isMetaLoading || isDisabled"
:loading="isMetaLoading"
:placeholder="$t('placeholder.notSelected')"
data-testid="nc-calendar-range-to-field-select"
@ -1097,14 +1116,6 @@ const getPluralName = (name: string) => {
</div>
</a-select-option>
</a-select>
<NcButton
class="!rounded-l-none !border-l-0"
size="small"
type="secondary"
@click="range.fk_to_column_id = null"
>
<component :is="iconMap.delete" class="h-4 w-4" />
</NcButton>
</div>
<NcButton
v-if="index !== 0"
@ -1539,13 +1550,4 @@ const getPluralName = (name: string) => {
@apply !rounded-lg;
}
}
.nc-to-select {
:deep(.ant-select) {
.ant-select-selector {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
}
}
</style>

2
packages/nc-gui/components/smartsheet/PlainCell.vue

@ -376,6 +376,8 @@ const parseValue = (value: any, col: ColumnType): string => {
&::before {
content: '•';
padding: 0 4px;
display: inline-block;
text-decoration: none !important;
}
&:first-child::before {
content: '';

22
packages/nc-gui/components/smartsheet/calendar/DayView/DateField.vue

@ -37,9 +37,6 @@ const getFieldStyle = (field: ColumnType) => {
// We loop through all the records and calculate the position of each record based on the range
// We only need to calculate the top, of the record since there is no overlap in the day view of date Field
const recordsAcrossAllRange = computed<Row[]>(() => {
let dayRecordCount = 0
const perRecordHeight = 28
if (!calendarRange.value) return []
const recordsByRange: Array<Row> = []
@ -48,11 +45,18 @@ const recordsAcrossAllRange = computed<Row[]>(() => {
const fromCol = range.fk_from_col
const endCol = range.fk_to_col
if (fromCol && endCol) {
for (const record of formattedData.value) {
const filteredData = formattedData.value.filter((record) => {
const startDate = dayjs(record.row[fromCol.title!])
const endDate = dayjs(record.row[endCol.title!])
return startDate.isSameOrBefore(endDate, 'day')
})
for (const record of filteredData) {
const startDate = dayjs(record.row[fromCol.title!])
const endDate = dayjs(record.row[endCol.title!])
dayRecordCount++
const id = record.rowMeta.id ?? generateRandomNumber()
// This property is used to determine which side the record should be rounded. It can be left, right, both or none
let position = 'none'
@ -77,17 +81,20 @@ const recordsAcrossAllRange = computed<Row[]>(() => {
rowMeta: {
...record.rowMeta,
position,
id,
range: range as any,
},
})
}
} else if (fromCol) {
for (const record of formattedData.value) {
dayRecordCount++
const id = record.rowMeta.id ?? generateRandomNumber()
recordsByRange.push({
...record,
rowMeta: {
...record.rowMeta,
id,
range: range as any,
position: 'rounded',
},
@ -203,13 +210,16 @@ const newRecord = () => {
<div
:key="record.rowMeta.id"
class="mt-2"
style="line-height: 18px"
data-testid="nc-calendar-day-record-card"
@mouseleave="hoverRecord = null"
@click.prevent="emit('expandRecord', record)"
@mouseover="hoverRecord = record.rowMeta.id as string"
>
<LazySmartsheetRow :row="record">
<LazySmartsheetCalendarRecordCard
:record="record"
:hover="hoverRecord === record.rowMeta.id"
:resize="false"
:position="record.rowMeta.position"
size="small"

18
packages/nc-gui/components/smartsheet/calendar/MonthView.vue

@ -254,6 +254,7 @@ const recordsToDisplay = computed<{
rowMeta: {
...record.rowMeta,
style,
maxSpanning: 1,
position: 'rounded',
range,
id,
@ -330,7 +331,7 @@ const recordsToDisplay = computed<{
const isRecordDraggingOrResizeState = id === draggingId.value || id === resizeRecord.value?.rowMeta.id
const style: Partial<CSSStyleDeclaration> = {
left: `${startDayIndex * perWidth}px`,
left: `${startDayIndex * perWidth - 0.5}px`,
width: `${(endDayIndex - startDayIndex + 1) * perWidth}px`,
top: isRecordDraggingOrResizeState
? `${weekIndex * perHeight + perRecordHeight}px`
@ -373,6 +374,7 @@ const recordsToDisplay = computed<{
position,
style,
range,
maxSpanning: endDayIndex - startDayIndex + 1,
id,
recordIndex,
},
@ -639,11 +641,6 @@ const dragStart = (event: MouseEvent, record: Row) => {
// TODO: @DarkPhoenix2704
// const initialDragElement = document.querySelector(`[data-unique-id="${record.rowMeta.id}-0"]`)
dragOffset.value = {
x: event.clientX - target.getBoundingClientRect().left,
y: event.clientY - target.getBoundingClientRect().top,
}
// selectedDate.value = null
isDragging.value = true
@ -651,9 +648,14 @@ const dragStart = (event: MouseEvent, record: Row) => {
draggingId.value = record.rowMeta!.id!
dragRecord.value = record
dragOffset.value = {
x: dragRecord.value?.rowMeta.maxSpanning > 1 ? event.clientX - target.getBoundingClientRect().left : 0,
y: event.clientY,
}
document.addEventListener('mousemove', onDrag)
document.addEventListener('mouseup', stopDrag)
}, 200)
}, 500)
const onMouseUp = () => {
clearTimeout(dragTimeout.value)
@ -898,6 +900,8 @@ const addRecord = (date: dayjs.Dayjs) => {
:style="{
...record.rowMeta.style,
zIndex: record.rowMeta.id === draggingId ? 100 : 0,
lineHeight: '18px',
opacity:
(draggingId === null || record.rowMeta.id === draggingId) &&
(resizeRecord === null || record.rowMeta.id === resizeRecord?.rowMeta.id)

23
packages/nc-gui/components/smartsheet/calendar/RecordCard.vue

@ -31,7 +31,7 @@ const emit = defineEmits(['resize-start'])
'h-full': size === 'auto',
'rounded-l-[4px] !border-r-0 ml-1': position === 'leftRounded',
'rounded-r-[4px] !border-l-0 mr-1': position === 'rightRounded',
'rounded-[4px] mx-1': position === 'rounded',
'rounded-[4px] ml-0.8 mr-1': position === 'rounded',
'rounded-none !border-x-0': position === 'none',
'bg-maroon-50': color === 'maroon',
'bg-blue-50': color === 'blue',
@ -40,6 +40,7 @@ const emit = defineEmits(['resize-start'])
'bg-pink-50': color === 'pink',
'bg-purple-50': color === 'purple',
'bg-white border-gray-300': color === 'gray',
'!bg-nc-bg-gray-light': hover || dragging,
}"
:style="{
boxShadow:
@ -70,22 +71,27 @@ const emit = defineEmits(['resize-start'])
></div>
<div class="overflow-hidden items-center justify-center gap-2 flex w-full">
<span v-if="position === 'rightRounded' || position === 'none'" class="ml-2"> .... </span>
<span v-if="position === 'rightRounded' || position === 'none'" class="ml-2 mb-0.6"> .... </span>
<slot name="time" />
<div
:class="{
'pr-7': position === 'leftRounded',
}"
class="flex mb-0.5 overflow-x-hidden break-word whitespace-nowrap overflow-hidden text-ellipsis w-full truncate text-ellipsis flex-col gap-1"
class="flex mb-0.5 overflow-x-hidden w-full truncate flex-col gap-1"
>
<NcTooltip :disabled="selected || dragging" class="inline-block" show-on-truncate-only wrap-child="span">
<NcTooltip
:disabled="selected || dragging"
class="break-word whitespace-nowrap overflow-hidden text-ellipsis pr-1"
show-on-truncate-only
wrap-child="span"
>
<slot class="text-sm text-nowrap text-gray-800 leading-7" />
<template #title>
<slot />
</template>
</NcTooltip>
</div>
<span v-if="position === 'leftRounded' || position === 'none'" class="absolute my-0 z-10 right-5"> .... </span>
<span v-if="position === 'leftRounded' || position === 'none'" class="absolute mb-0.6 z-10 right-5"> .... </span>
</div>
<div
@ -100,4 +106,11 @@ const emit = defineEmits(['resize-start'])
.resize {
cursor: ew-resize;
}
.plain-cell {
line-height: 18px;
.bold {
@apply !text-gray-800 font-bold;
}
}
</style>

12
packages/nc-gui/components/smartsheet/calendar/VRecordCard.vue

@ -36,8 +36,9 @@ const emit = defineEmits(['resize-start'])
'bg-purple-50': color === 'purple',
'bg-white border-gray-300': color === 'gray',
'z-90': hover,
'!bg-nc-bg-gray-light': hover || dragging,
}"
class="relative flex gap-2 border-1 relative rounded-md h-full"
class="relative flex gap-1 border-1 relative rounded-md h-full"
>
<div
v-if="resize"
@ -59,7 +60,11 @@ const emit = defineEmits(['resize-start'])
<div class="flex overflow-x-hidden whitespace-nowrap text-ellipsis pt-1 w-full truncate text-ellipsis flex-col gap-1">
<div class="truncate">
<NcTooltip show-on-truncate-only :disabled="selected">
<NcTooltip
class="break-word whitespace-nowrap overflow-hidden text-ellipsis pr-1"
show-on-truncate-only
:disabled="selected"
>
<template #title>
<slot />
</template>
@ -83,8 +88,9 @@ const emit = defineEmits(['resize-start'])
}
.plain-cell {
line-height: 18px;
.bold {
@apply !text-gray-800 font-semibold;
@apply !text-gray-800 font-bold;
}
}
</style>

60
packages/nc-gui/components/smartsheet/calendar/WeekView/DateField.vue

@ -154,9 +154,10 @@ const calendarData = computed(() => {
range: { fk_from_col, fk_to_col },
position,
id,
spanningDays: Math.abs(ogStartDate.diff(endDate, 'day')) - Math.abs(startDate.diff(endDate, 'day')),
style: {
width: `calc(max(${spanDays * perDayWidth - 10}px, ${perDayWidth - 10}px))`,
left: `${startDaysDiff * perDayWidth + 4}px`,
width: `calc(max(${spanDays * perDayWidth + 0.5}px, ${perDayWidth + 0.5}px))`,
left: `${startDaysDiff * perDayWidth - 1}px`,
top: `${suitableRow * 28 + Math.max(suitableRow + 1, 1) * 8}px`,
},
},
@ -168,7 +169,12 @@ const calendarData = computed(() => {
.filter((r) => {
const startDate = dayjs(r.row[fk_from_col.title!])
const endDate = dayjs(r.row[fk_to_col.title!])
return startDate.isValid() && endDate.isValid() && !endDate.isBefore(startDate)
return (
startDate.isValid() &&
endDate.isValid() &&
!endDate.isBefore(startDate) &&
!endDate.isBefore(selectedDateRange.value.start, 'day')
)
})
.forEach(processRecord)
} else {
@ -293,18 +299,15 @@ const dragOffset = ref<{
// This method is used to calculate the new start and end date of a record when dragging and dropping
const calculateNewRow = (event: MouseEvent, updateSideBarData?: boolean) => {
const { width, left } = container.value.getBoundingClientRect()
// Calculate the percentage of the width based on the mouse position
// This is used to calculate the day index
const { width, left } = container.value?.getBoundingClientRect()
let relativeX = event.clientX - left
const relativeX = event.clientX - left
if (dragOffset.value.x) {
/* if (dragOffset.value.x && dragRecord.value?.rowMeta.spanningDays === 1) {
relativeX -= dragOffset.value.x
}
} */
const percentX = Math.max(0, Math.min(1, relativeX / width))
const percentX = relativeX / width
const fromCol = dragRecord.value.rowMeta.range?.fk_from_col
const toCol = dragRecord.value.rowMeta.range?.fk_to_col
@ -312,8 +315,9 @@ const calculateNewRow = (event: MouseEvent, updateSideBarData?: boolean) => {
if (!fromCol) return { updatedProperty: [], newRow: null }
// Calculate the day index based on the percentage of the width
// The day index is a number between 0 and 6
const day = Math.floor(percentX * maxVisibleDays.value)
const day = Math.floor(
percentX * maxVisibleDays.value - dragRecord.value.rowMeta.spanningDays - Math.max(0, Math.min(1, relativeX / width)),
)
// Calculate the new start date based on the day index by adding the day index to the start date of the selected date range
const newStartDate = dayjs(selectedDateRange.value.start).add(day, 'day')
@ -376,6 +380,8 @@ const calculateNewRow = (event: MouseEvent, updateSideBarData?: boolean) => {
const onDrag = (event: MouseEvent) => {
if (!isUIAllowed('dataEdit')) return
if (!container.value || !dragRecord.value) return
event.preventDefault()
calculateNewRow(event, false)
}
@ -401,10 +407,16 @@ const stopDrag = (event: MouseEvent) => {
dragElement.value.style.boxShadow = 'none'
dragElement.value = null
}
dragRecord.value = undefined
updateRowProperty(newRow, updateProperty, false)
dragOffset.value = {
x: null,
y: null,
}
document.removeEventListener('mousemove', onDrag)
document.removeEventListener('mouseup', stopDrag)
}
@ -415,6 +427,11 @@ const dragStart = (event: MouseEvent, record: Row) => {
isDragging.value = false
dragOffset.value = {
x: event.clientX - target.getBoundingClientRect().left,
y: event.clientY - target.getBoundingClientRect().top,
}
dragTimeout.value = setTimeout(() => {
if (!isUIAllowed('dataEdit')) return
isDragging.value = true
@ -422,11 +439,6 @@ const dragStart = (event: MouseEvent, record: Row) => {
target = target.parentElement as HTMLElement
}
dragOffset.value = {
x: event.clientX - target.getBoundingClientRect().left,
y: event.clientY - target.getBoundingClientRect().top,
}
const allRecords = document.querySelectorAll('.draggable-record')
allRecords.forEach((el) => {
if (!el.getAttribute('data-unique-id').includes(record.rowMeta.id!)) {
@ -444,6 +456,12 @@ const dragStart = (event: MouseEvent, record: Row) => {
const onMouseUp = () => {
clearTimeout(dragTimeout.value!)
dragOffset.value = {
x: null,
y: null,
}
document.removeEventListener('mouseup', onMouseUp)
if (!isDragging.value) {
emits('expandRecord', record)
@ -475,11 +493,6 @@ const dropEvent = (event: DragEvent) => {
}
updateRowProperty(newRow, updateProperty, false)
dragOffset.value = {
x: null,
y: null,
}
$e('c:calendar:day:drag-record')
}
}
@ -548,6 +561,7 @@ const addRecord = (date: dayjs.Dayjs) => {
:data-unique-id="record.rowMeta.id"
:style="{
...record.rowMeta.style,
lineHeight: '18px',
}"
class="absolute group draggable-record pointer-events-auto nc-calendar-week-record-card"
@mouseleave="hoverRecord = null"

18
packages/nc-gui/components/smartsheet/calendar/WeekView/DateTimeField.vue

@ -116,7 +116,6 @@ const calculateHourIndices = (dayIndex: number, startDate: dayjs.Dayjs, endDate:
return {
startHourIndex,
endHourIndex,
// You might also want these for more precise calculations
startMinutes: startDate.minute(),
endMinutes: endDate.minute(),
}
@ -551,17 +550,17 @@ const recordsAcrossAllRange = computed<{
left = width * (overlapIndex - 1)
width = Math.max((width / 100) * containerWidth.value - 8, 72)
width = Math.max((width / 100) * containerWidth.value + 1, 72) - 3
left = majorLeft + (left / 100) * containerWidth.value + 4
left = majorLeft + (left / 100) * containerWidth.value + 1.5
if (majorLeft + perWidth < left + width) {
left = majorLeft + (perWidth - width - 4)
left = majorLeft + (perWidth - width) - 4.5 * overlapIndex
}
}
} else {
left = majorLeft + 4
width = perWidth - 8
left = majorLeft + 1.5
width = perWidth - 3
}
record.rowMeta.style = {
@ -956,7 +955,7 @@ watch(
<div class="flex-1 border-b-1 border-brand-500"></div>
</div>
</div>
<div class="flex sticky h-6 z-3 top-0 pl-16 bg-gray-50 w-full">
<div class="flex sticky h-6 z-4 top-0 pl-16 bg-gray-50 w-full">
<div
v-for="date in datesHours"
:key="date[0].toISOString()"
@ -988,7 +987,7 @@ watch(
</div>
<div
v-if="isRangeEnabled && recordsAcrossAllRange.spanningRecords?.length"
class="sticky top-6 bg-white z-3 inset-x-0 w-full"
class="sticky top-6 bg-white z-4 inset-x-0 w-full"
>
<SmartsheetCalendarDateTimeSpanningContainer
ref="spanningRecordsContainer"
@ -1039,7 +1038,7 @@ watch(
<NcButton
v-if="isOverflowAcrossHourRange(hour).isOverflow"
v-e="`['c:calendar:week-view-more']`"
class="!absolute bottom-1 text-center w-15 ml-auto inset-x-0 z-2 text-gray-500"
class="!absolute bottom-1 text-center w-15 ml-auto inset-x-0 z-3 text-gray-500"
size="xxsmall"
type="secondary"
@click="viewMore(hour)"
@ -1064,6 +1063,7 @@ watch(
:data-unique-id="record.rowMeta!.id"
:style="{
...record.rowMeta.style,
lineHeight: '18px',
opacity:
(dragRecord === null || record.rowMeta.id === dragRecord?.rowMeta.id) &&
(resizeRecord === null || record.rowMeta.id === resizeRecord?.rowMeta.id)

77
packages/nc-gui/components/smartsheet/toolbar/Calendar/Range.vue

@ -128,7 +128,7 @@ const saveCalendarRanges = async () => {
await loadCalendarMeta()
await Promise.all([loadCalendarData(), loadSidebarData(), fetchActiveDates()])
calendarRangeDropdown.value = false
// calendarRangeDropdown.value = false
} catch (e) {
console.log(e)
message.error('There was an error while updating view!')
@ -143,9 +143,23 @@ const removeRange = async (id: number) => {
await saveCalendarRanges()
}
const saveCalendarRange = async (range: CalendarRangeType, value?) => {
range.fk_to_column_id = value
await saveCalendarRanges()
const isDisabled = computed(() => {
return (
dateFieldOptions.value.find((f) => f.value === calendarRange.value[0]?.fk_from_column_id)?.uidt === UITypes.DateTime &&
!isRangeEnabled.value
)
})
const onValueChange = async () => {
_calendar_ranges.value = _calendar_ranges.value.map((range, i) => {
if (i === 0) {
return {
fk_from_column_id: range.fk_from_column_id,
fk_to_column_id: undefined,
}
}
return range
})
}
</script>
@ -198,7 +212,12 @@ const saveCalendarRange = async (range: CalendarRangeType, value?) => {
:placeholder="$t('placeholder.notSelected')"
data-testid="nc-calendar-range-from-field-select"
:disabled="isLocked"
@change="saveCalendarRanges"
@change="
() => {
onValueChange()
saveCalendarRanges()
}
"
@click.stop
>
<template #suffixIcon><GeneralIcon icon="arrowDown" class="text-gray-700" /></template>
@ -231,31 +250,34 @@ const saveCalendarRange = async (range: CalendarRangeType, value?) => {
</div>
</a-select-option>
</a-select>
<NcTooltip v-if="range.fk_to_column_id === null && isRangeEnabled" placement="left" :disabled="!isDisabled">
<NcButton
size="small"
data-testid="nc-calendar-range-add-end-date"
class="w-23"
type="text"
:shadow="false"
:disabled="!isEeUI || isLocked || isDisabled"
@click="range.fk_to_column_id = undefined"
>
<div class="flex gap-1 items-center">
<component :is="iconMap.plus" class="h-4 w-4" />
{{ $t('activity.endDate') }}
</div>
</NcButton>
<template #title> Coming Soon!! Currently, range support is only available for Date field. </template>
</NcTooltip>
<NcButton
v-if="range.fk_to_column_id === null && isRangeEnabled"
size="small"
data-testid="nc-calendar-range-add-end-date"
class="!border-none w-28"
type="secondary"
:shadow="false"
:disabled="!isEeUI || isLocked"
@click="range.fk_to_column_id = undefined"
>
<div class="flex gap-2 items-center">
<component :is="iconMap.plus" class="h-4 w-4" />
{{ $t('activity.endDate') }}
</div>
</NcButton>
<template v-else-if="isEeUI && isRangeEnabled">
<template v-else-if="isEeUI">
<span>
{{ $t('activity.withEndDate') }}
</span>
<div class="flex">
<a-select
v-model:value="range.fk_to_column_id"
class="!rounded-r-none nc-select-shadow w-full flex-1 nc-to-select"
:disabled="!range.fk_from_column_id || isLocked"
class="!rounded-r-none nc-select-shadow w-full flex-1"
allow-clear
:disabled="!range.fk_from_column_id || isLocked || isDisabled"
:placeholder="$t('placeholder.notSelected')"
data-testid="nc-calendar-range-to-field-select"
dropdown-class-name="!rounded-lg"
@ -292,15 +314,6 @@ const saveCalendarRange = async (range: CalendarRangeType, value?) => {
</div>
</a-select-option>
</a-select>
<NcButton
class="!rounded-l-none nc-select-shadow !border-l-0"
size="small"
type="secondary"
@click="saveCalendarRange(range, null)"
>
<component :is="iconMap.delete" class="h-4 w-4" />
</NcButton>
</div>
</template>

2
packages/nc-gui/composables/useBetaFeatureToggle.ts

@ -45,7 +45,7 @@ const FEATURES = [
},
{
id: 'calendar_view_range',
title: 'Allow configuring End Date for Calendar View',
title: 'Allow configuring Date Time Field as End Date for Calendar View',
description: 'Enables the calendar to display items as date ranges by allowing configuration of both start and end dates. ',
enabled: false,
isEngineering: true,

5
packages/nc-gui/composables/useCalendarViewStore.ts

@ -847,11 +847,10 @@ const [useProvideCalendarViewStore, useCalendarViewStore] = useInjectionState(
watch(activeCalendarView, async (value, oldValue) => {
if (oldValue === 'week') {
pageDate.value = selectedDate.value
selectedMonth.value = selectedTime.value ?? selectedDate.value ?? selectedDateRange.value.start
selectedDate.value = selectedTime.value ?? selectedDateRange.value.start
selectedMonth.value = selectedDate.value ?? selectedDateRange.value.start
selectedDate.value = selectedDate.value ?? selectedDateRange.value.start
selectedTime.value = selectedDate.value ?? selectedDateRange.value.start
} else if (oldValue === 'month') {
selectedDate.value = selectedMonth.value
pageDate.value = selectedDate.value
selectedTime.value = selectedDate.value
selectedDateRange.value = {

2
packages/nc-gui/lang/en.json

@ -1070,7 +1070,7 @@
"toggleSidebar": "Toggle Sidebar",
"addEndDate": "Add end date",
"endDate": "End Date",
"withEndDate": "with end date",
"withEndDate": "With end date",
"calendar": "Calendar",
"viewSettings": "View settings",
"googleOAuth": "Google OAuth",

1
packages/nc-gui/lib/types.ts

@ -103,6 +103,7 @@ interface Row {
numberOfOverlaps?: number
minutes?: number
recordIndex?: number // For week spanning records in month view
maxSpanning?: number
}
}

23
packages/noco-docs/docs/090.views/040.view-types/050.calendar.md

@ -63,6 +63,29 @@ Use drag and drop to move the record to a different date or time. To edit any ot
### Delete Record
To delete a record, click on the record to expand it. Click on the `Delete` button from the record context menu. Confirm the deletion by clicking on `Delete` in the confirmation dialog.
## Configuring Date Range
:::note
The date range for the calendar view is supported only in the Cloud version of NocoDB.
:::
![date-range](/img/v2/views/calendar/cal-range-3.png)
In NocoDB, you can configure the date range display in the calendar view by selecting Start and End date fields. To specify the date range while setting up the calendar view, click on `+ End Date` and choose the desired date field to serve as the end date. This displays the records in the calendar view as a range block between the start and end date.
![date-range](/img/v2/views/calendar/cal-range-1.png)
![date-range](/img/v2/views/calendar/cal-range-2.png)
:::note
The date range configuration is currently supported only for the Date field type. Support for DateTime field type will be added in the future.
:::
Alternatively, you can also configure the date range display functionality during view creation. To do this, click on `+ Add Field` and choose the desired date field to serve as the end date.
![date-range](/img/v2/views/calendar/cal-range-4.png)
Once the date range display is enabled, you can drag drop the records to change the start and end dates of the records. You can also resize the records to change the duration of the records.
## Other toolbar operations
1. [Apply Filters to Display Specific Records on the Calendar](/table-operations/filter)
2. [Sort Records on the Calendar by One or More Criteria](/table-operations/sort)

BIN
packages/noco-docs/static/img/v2/views/calendar/cal-range-1.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

BIN
packages/noco-docs/static/img/v2/views/calendar/cal-range-2.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

BIN
packages/noco-docs/static/img/v2/views/calendar/cal-range-3.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 KiB

BIN
packages/noco-docs/static/img/v2/views/calendar/cal-range-4.png vendored

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Loading…
Cancel
Save