Simplified CalendarStore
This commit is contained in:
parent
8e926c0a21
commit
fece943594
@ -20,6 +20,7 @@ import {
|
|||||||
} from '@/utils/date'
|
} from '@/utils/date'
|
||||||
import { toLocalString, fromLocalString, DEFAULT_TZ } from '@/utils/date'
|
import { toLocalString, fromLocalString, DEFAULT_TZ } from '@/utils/date'
|
||||||
import { addDays, differenceInCalendarDays } from 'date-fns'
|
import { addDays, differenceInCalendarDays } from 'date-fns'
|
||||||
|
import { getHolidayForDate } from '@/utils/holidays'
|
||||||
|
|
||||||
const calendarStore = useCalendarStore()
|
const calendarStore = useCalendarStore()
|
||||||
const viewport = ref(null)
|
const viewport = ref(null)
|
||||||
@ -232,7 +233,12 @@ function createWeek(virtualWeek) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get holiday info once per day
|
// Get holiday info once per day
|
||||||
const holiday = calendarStore.getHolidayForDate(dateStr)
|
// Ensure holidays initialized lazily
|
||||||
|
let holiday = null
|
||||||
|
if (calendarStore.config.holidays.enabled) {
|
||||||
|
calendarStore._ensureHolidaysInitialized?.()
|
||||||
|
holiday = getHolidayForDate(dateStr)
|
||||||
|
}
|
||||||
|
|
||||||
days.push({
|
days.push({
|
||||||
date: dateStr,
|
date: dateStr,
|
||||||
|
@ -4,18 +4,11 @@ import {
|
|||||||
fromLocalString,
|
fromLocalString,
|
||||||
getLocaleWeekendDays,
|
getLocaleWeekendDays,
|
||||||
getMondayOfISOWeek,
|
getMondayOfISOWeek,
|
||||||
getOccurrenceIndex,
|
|
||||||
getOccurrenceDate,
|
getOccurrenceDate,
|
||||||
DEFAULT_TZ,
|
DEFAULT_TZ,
|
||||||
} from '@/utils/date'
|
} from '@/utils/date'
|
||||||
import { differenceInCalendarDays, addDays } from 'date-fns'
|
import { differenceInCalendarDays, addDays } from 'date-fns'
|
||||||
import {
|
import { initializeHolidays, getAvailableCountries, getAvailableStates } from '@/utils/holidays'
|
||||||
initializeHolidays,
|
|
||||||
getHolidayForDate,
|
|
||||||
isHoliday,
|
|
||||||
getAvailableCountries,
|
|
||||||
getAvailableStates,
|
|
||||||
} from '@/utils/holidays'
|
|
||||||
|
|
||||||
const MIN_YEAR = 1900
|
const MIN_YEAR = 1900
|
||||||
const MAX_YEAR = 2100
|
const MAX_YEAR = 2100
|
||||||
@ -43,29 +36,23 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
// Basic configuration getters
|
|
||||||
minYear: () => MIN_YEAR,
|
minYear: () => MIN_YEAR,
|
||||||
maxYear: () => MAX_YEAR,
|
maxYear: () => MAX_YEAR,
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
// Initialize holidays based on current config
|
_resolveCountry(code) {
|
||||||
|
if (!code || code !== 'auto') return code
|
||||||
|
const locale = navigator.language || navigator.languages?.[0]
|
||||||
|
if (!locale) return null
|
||||||
|
const parts = locale.split('-')
|
||||||
|
if (parts.length < 2) return null
|
||||||
|
return parts[parts.length - 1].toUpperCase()
|
||||||
|
},
|
||||||
|
|
||||||
initializeHolidaysFromConfig() {
|
initializeHolidaysFromConfig() {
|
||||||
if (!this.config.holidays.enabled) {
|
if (!this.config.holidays.enabled) return false
|
||||||
return false
|
const country = this._resolveCountry(this.config.holidays.country)
|
||||||
}
|
|
||||||
|
|
||||||
let country = this.config.holidays.country
|
|
||||||
if (country === 'auto') {
|
|
||||||
const locale = navigator.language || navigator.languages?.[0]
|
|
||||||
if (!locale) return false
|
|
||||||
|
|
||||||
const parts = locale.split('-')
|
|
||||||
if (parts.length < 2) return false
|
|
||||||
|
|
||||||
country = parts[parts.length - 1].toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (country) {
|
if (country) {
|
||||||
return this.initializeHolidays(
|
return this.initializeHolidays(
|
||||||
country,
|
country,
|
||||||
@ -73,107 +60,55 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
this.config.holidays.region,
|
this.config.holidays.region,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
occursOnDate(event, dateStr) {
|
|
||||||
return getOccurrenceIndex(event, dateStr) !== null
|
|
||||||
},
|
|
||||||
updateCurrentDate() {
|
updateCurrentDate() {
|
||||||
const d = new Date()
|
const d = new Date()
|
||||||
this.now = d.toISOString()
|
this.now = d.toISOString()
|
||||||
const today = toLocalString(d, DEFAULT_TZ)
|
const today = toLocalString(d, DEFAULT_TZ)
|
||||||
if (this.today !== today) {
|
if (this.today !== today) this.today = today
|
||||||
this.today = today
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Holiday management
|
|
||||||
initializeHolidays(country, state = null, region = null) {
|
initializeHolidays(country, state = null, region = null) {
|
||||||
let actualCountry = country
|
const actualCountry = this._resolveCountry(country)
|
||||||
if (country === 'auto') {
|
if (this.config.holidays.country !== 'auto') this.config.holidays.country = country
|
||||||
const locale = navigator.language || navigator.languages?.[0]
|
|
||||||
if (!locale) return false
|
|
||||||
|
|
||||||
const parts = locale.split('-')
|
|
||||||
if (parts.length < 2) return false
|
|
||||||
|
|
||||||
actualCountry = parts[parts.length - 1].toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.config.holidays.country !== 'auto') {
|
|
||||||
this.config.holidays.country = country
|
|
||||||
}
|
|
||||||
this.config.holidays.state = state
|
this.config.holidays.state = state
|
||||||
this.config.holidays.region = region
|
this.config.holidays.region = region
|
||||||
|
|
||||||
this._holidayConfigSignature = null
|
this._holidayConfigSignature = null
|
||||||
this._holidaysInitialized = false
|
this._holidaysInitialized = false
|
||||||
|
|
||||||
return initializeHolidays(actualCountry, state, region)
|
return initializeHolidays(actualCountry, state, region)
|
||||||
},
|
},
|
||||||
|
|
||||||
_ensureHolidaysInitialized() {
|
_ensureHolidaysInitialized() {
|
||||||
if (!this.config.holidays.enabled) {
|
if (!this.config.holidays.enabled) return false
|
||||||
return false
|
const actualCountry = this._resolveCountry(this.config.holidays.country)
|
||||||
}
|
const sig = `${actualCountry}-${this.config.holidays.state}-${this.config.holidays.region}-${this.config.holidays.enabled}`
|
||||||
|
if (this._holidayConfigSignature !== sig || !this._holidaysInitialized) {
|
||||||
let actualCountry = this.config.holidays.country
|
const ok = initializeHolidays(
|
||||||
if (this.config.holidays.country === 'auto') {
|
|
||||||
const locale = navigator.language || navigator.languages?.[0]
|
|
||||||
if (!locale) return false
|
|
||||||
|
|
||||||
const parts = locale.split('-')
|
|
||||||
if (parts.length < 2) return false
|
|
||||||
|
|
||||||
actualCountry = parts[parts.length - 1].toUpperCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
const configSignature = `${actualCountry}-${this.config.holidays.state}-${this.config.holidays.region}-${this.config.holidays.enabled}`
|
|
||||||
|
|
||||||
if (this._holidayConfigSignature !== configSignature || !this._holidaysInitialized) {
|
|
||||||
const success = initializeHolidays(
|
|
||||||
actualCountry,
|
actualCountry,
|
||||||
this.config.holidays.state,
|
this.config.holidays.state,
|
||||||
this.config.holidays.region,
|
this.config.holidays.region,
|
||||||
)
|
)
|
||||||
if (success) {
|
if (ok) {
|
||||||
this._holidayConfigSignature = configSignature
|
this._holidayConfigSignature = sig
|
||||||
this._holidaysInitialized = true
|
this._holidaysInitialized = true
|
||||||
}
|
}
|
||||||
return success
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._holidaysInitialized
|
return this._holidaysInitialized
|
||||||
},
|
},
|
||||||
|
|
||||||
getHolidayForDate(dateStr) {
|
|
||||||
if (!this._ensureHolidaysInitialized()) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return getHolidayForDate(dateStr)
|
|
||||||
},
|
|
||||||
|
|
||||||
isHoliday(dateStr) {
|
|
||||||
if (!this._ensureHolidaysInitialized()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return isHoliday(dateStr)
|
|
||||||
},
|
|
||||||
|
|
||||||
getAvailableCountries() {
|
getAvailableCountries() {
|
||||||
return getAvailableCountries() || []
|
return getAvailableCountries() || []
|
||||||
},
|
},
|
||||||
|
|
||||||
getAvailableStates(country) {
|
getAvailableStates(country) {
|
||||||
return getAvailableStates(country) || []
|
return getAvailableStates(country) || []
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleHolidays() {
|
toggleHolidays() {
|
||||||
this.config.holidays.enabled = !this.config.holidays.enabled
|
this.config.holidays.enabled = !this.config.holidays.enabled
|
||||||
},
|
},
|
||||||
|
|
||||||
// Event management
|
|
||||||
generateId() {
|
generateId() {
|
||||||
try {
|
try {
|
||||||
if (window.crypto && typeof window.crypto.randomUUID === 'function') {
|
if (window.crypto && typeof window.crypto.randomUUID === 'function') {
|
||||||
@ -194,14 +129,12 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
eventData.colorId ?? this.selectEventColorId(eventData.startDate, eventData.endDate),
|
eventData.colorId ?? this.selectEventColorId(eventData.startDate, eventData.endDate),
|
||||||
startTime: singleDay ? eventData.startTime || '09:00' : null,
|
startTime: singleDay ? eventData.startTime || '09:00' : null,
|
||||||
durationMinutes: singleDay ? eventData.durationMinutes || 60 : null,
|
durationMinutes: singleDay ? eventData.durationMinutes || 60 : null,
|
||||||
// Normalized repeat value: only 'weeks', 'months', or 'none'
|
|
||||||
repeat: ['weeks', 'months'].includes(eventData.repeat) ? eventData.repeat : 'none',
|
repeat: ['weeks', 'months'].includes(eventData.repeat) ? eventData.repeat : 'none',
|
||||||
repeatInterval: eventData.repeatInterval || 1,
|
repeatInterval: eventData.repeatInterval || 1,
|
||||||
repeatCount: eventData.repeatCount || 'unlimited',
|
repeatCount: eventData.repeatCount || 'unlimited',
|
||||||
repeatWeekdays: eventData.repeatWeekdays,
|
repeatWeekdays: eventData.repeatWeekdays,
|
||||||
isRepeating: eventData.repeat && eventData.repeat !== 'none',
|
isRepeating: eventData.repeat && eventData.repeat !== 'none',
|
||||||
}
|
}
|
||||||
|
|
||||||
this.events.set(event.id, { ...event, isSpanning: event.startDate < event.endDate })
|
this.events.set(event.id, { ...event, isSpanning: event.startDate < event.endDate })
|
||||||
return event.id
|
return event.id
|
||||||
},
|
},
|
||||||
@ -214,53 +147,42 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
const colorCounts = [0, 0, 0, 0, 0, 0, 0, 0]
|
const colorCounts = [0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
const startDate = fromLocalString(startDateStr, DEFAULT_TZ)
|
const startDate = fromLocalString(startDateStr, DEFAULT_TZ)
|
||||||
const endDate = fromLocalString(endDateStr, DEFAULT_TZ)
|
const endDate = fromLocalString(endDateStr, DEFAULT_TZ)
|
||||||
// Count events whose ranges overlap at least one day in selected span
|
|
||||||
for (const ev of this.events.values()) {
|
for (const ev of this.events.values()) {
|
||||||
const evStart = fromLocalString(ev.startDate)
|
const evStart = fromLocalString(ev.startDate)
|
||||||
const evEnd = fromLocalString(ev.endDate)
|
const evEnd = fromLocalString(ev.endDate)
|
||||||
if (evEnd < startDate || evStart > endDate) continue
|
if (evEnd < startDate || evStart > endDate) continue
|
||||||
if (ev.colorId >= 0 && ev.colorId < 8) colorCounts[ev.colorId]++
|
if (ev.colorId >= 0 && ev.colorId < 8) colorCounts[ev.colorId]++
|
||||||
}
|
}
|
||||||
|
|
||||||
let minCount = colorCounts[0]
|
let minCount = colorCounts[0]
|
||||||
let selectedColor = 0
|
let selectedColor = 0
|
||||||
|
for (let c = 1; c < 8; c++) {
|
||||||
for (let colorId = 1; colorId < 8; colorId++) {
|
if (colorCounts[c] < minCount) {
|
||||||
if (colorCounts[colorId] < minCount) {
|
minCount = colorCounts[c]
|
||||||
minCount = colorCounts[colorId]
|
selectedColor = c
|
||||||
selectedColor = colorId
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return selectedColor
|
return selectedColor
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteEvent(eventId) {
|
deleteEvent(eventId) {
|
||||||
console.log('Deleting event', eventId)
|
|
||||||
this.events.delete(eventId)
|
this.events.delete(eventId)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Remove the first (base) occurrence of a repeating event by shifting anchor forward
|
|
||||||
deleteFirstOccurrence(baseId) {
|
deleteFirstOccurrence(baseId) {
|
||||||
console.log('Deleting first occurrence', baseId)
|
|
||||||
const base = this.getEventById(baseId)
|
const base = this.getEventById(baseId)
|
||||||
if (!base) return
|
if (!base) return
|
||||||
if (!base.isRepeating) {
|
if (!base.isRepeating) {
|
||||||
// Simple (non-repeating) event: delete entirely
|
|
||||||
this.deleteEvent(baseId)
|
this.deleteEvent(baseId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const numericCount =
|
const numericCount =
|
||||||
base.repeatCount === 'unlimited' ? Infinity : parseInt(base.repeatCount, 10)
|
base.repeatCount === 'unlimited' ? Infinity : parseInt(base.repeatCount, 10)
|
||||||
if (numericCount <= 1) {
|
if (numericCount <= 1) {
|
||||||
// Only one occurrence (or invalid count) -> delete event
|
|
||||||
this.deleteEvent(baseId)
|
this.deleteEvent(baseId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Get the next occurrence start date (index 1)
|
|
||||||
const nextStartStr = getOccurrenceDate(base, 1, DEFAULT_TZ)
|
const nextStartStr = getOccurrenceDate(base, 1, DEFAULT_TZ)
|
||||||
if (!nextStartStr) {
|
if (!nextStartStr) {
|
||||||
// No next occurrence; remove event
|
|
||||||
this.deleteEvent(baseId)
|
this.deleteEvent(baseId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -271,39 +193,29 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
addDays(fromLocalString(nextStartStr, DEFAULT_TZ), durationDays),
|
addDays(fromLocalString(nextStartStr, DEFAULT_TZ), durationDays),
|
||||||
DEFAULT_TZ,
|
DEFAULT_TZ,
|
||||||
)
|
)
|
||||||
// Mutate existing event instead of delete+recreate so references remain stable
|
|
||||||
base.startDate = nextStartStr
|
base.startDate = nextStartStr
|
||||||
base.endDate = newEndStr
|
base.endDate = newEndStr
|
||||||
if (numericCount !== Infinity) {
|
if (numericCount !== Infinity) base.repeatCount = String(Math.max(1, numericCount - 1))
|
||||||
base.repeatCount = String(Math.max(1, numericCount - 1))
|
|
||||||
}
|
|
||||||
this.events.set(baseId, { ...base, isSpanning: base.startDate < base.endDate })
|
this.events.set(baseId, { ...base, isSpanning: base.startDate < base.endDate })
|
||||||
},
|
},
|
||||||
|
|
||||||
// Delete a specific occurrence (not the first) from a repeating series, splitting into two
|
|
||||||
deleteSingleOccurrence(ctx) {
|
deleteSingleOccurrence(ctx) {
|
||||||
console.log('DeletesingleOccurrence')
|
|
||||||
const { baseId, occurrenceIndex } = ctx || {}
|
const { baseId, occurrenceIndex } = ctx || {}
|
||||||
if (occurrenceIndex === undefined || occurrenceIndex === null) return
|
if (occurrenceIndex == null) return
|
||||||
const base = this.getEventById(baseId)
|
const base = this.getEventById(baseId)
|
||||||
if (!base) return
|
if (!base) return
|
||||||
if (!base.isRepeating) {
|
if (!base.isRepeating) {
|
||||||
// Single non-repeating event deletion
|
|
||||||
if (occurrenceIndex === 0) this.deleteEvent(baseId)
|
if (occurrenceIndex === 0) this.deleteEvent(baseId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (occurrenceIndex === 0) {
|
if (occurrenceIndex === 0) {
|
||||||
// Delegate to specialized first-occurrence deletion
|
|
||||||
this.deleteFirstOccurrence(baseId)
|
this.deleteFirstOccurrence(baseId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Save copy before truncation for computing next occurrence date
|
|
||||||
const snapshot = { ...base }
|
const snapshot = { ...base }
|
||||||
// Cap original series to occurrences before the deleted one
|
|
||||||
base.repeatCount = occurrenceIndex
|
base.repeatCount = occurrenceIndex
|
||||||
const nextStartStr = getOccurrenceDate(snapshot, occurrenceIndex + 1, DEFAULT_TZ)
|
const nextStartStr = getOccurrenceDate(snapshot, occurrenceIndex + 1, DEFAULT_TZ)
|
||||||
console.log('Deleting single', occurrenceIndex, nextStartStr)
|
if (!nextStartStr) return
|
||||||
if (!nextStartStr) return // no continuation
|
|
||||||
const durationDays = Math.max(
|
const durationDays = Math.max(
|
||||||
0,
|
0,
|
||||||
differenceInCalendarDays(
|
differenceInCalendarDays(
|
||||||
@ -336,40 +248,30 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
const { baseId, occurrenceIndex } = ctx
|
const { baseId, occurrenceIndex } = ctx
|
||||||
const base = this.getEventById(baseId)
|
const base = this.getEventById(baseId)
|
||||||
if (!base || !base.isRepeating) return
|
if (!base || !base.isRepeating) return
|
||||||
|
|
||||||
// Special case: if deleting from the base occurrence (index 0), delete the entire series
|
|
||||||
if (occurrenceIndex === 0) {
|
if (occurrenceIndex === 0) {
|
||||||
this.deleteEvent(baseId)
|
this.deleteEvent(baseId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const keptTotal = occurrenceIndex
|
this._terminateRepeatSeriesAtIndex(baseId, occurrenceIndex)
|
||||||
this._terminateRepeatSeriesAtIndex(baseId, keptTotal)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Adjust start/end range of a base event (non-generated) and reindex occurrences
|
|
||||||
setEventRange(eventId, newStartStr, newEndStr, { mode = 'auto' } = {}) {
|
setEventRange(eventId, newStartStr, newEndStr, { mode = 'auto' } = {}) {
|
||||||
const snapshot = this.events.get(eventId)
|
const snapshot = this.events.get(eventId)
|
||||||
if (!snapshot) return
|
if (!snapshot) return
|
||||||
// Calculate current duration in days (inclusive)
|
|
||||||
const prevStart = fromLocalString(snapshot.startDate, DEFAULT_TZ)
|
const prevStart = fromLocalString(snapshot.startDate, DEFAULT_TZ)
|
||||||
const prevEnd = fromLocalString(snapshot.endDate, DEFAULT_TZ)
|
const prevEnd = fromLocalString(snapshot.endDate, DEFAULT_TZ)
|
||||||
const prevDurationDays = Math.max(0, differenceInCalendarDays(prevEnd, prevStart))
|
const prevDurationDays = Math.max(0, differenceInCalendarDays(prevEnd, prevStart))
|
||||||
|
|
||||||
const newStart = fromLocalString(newStartStr, DEFAULT_TZ)
|
const newStart = fromLocalString(newStartStr, DEFAULT_TZ)
|
||||||
const newEnd = fromLocalString(newEndStr, DEFAULT_TZ)
|
const newEnd = fromLocalString(newEndStr, DEFAULT_TZ)
|
||||||
const proposedDurationDays = Math.max(0, differenceInCalendarDays(newEnd, newStart))
|
const proposedDurationDays = Math.max(0, differenceInCalendarDays(newEnd, newStart))
|
||||||
|
|
||||||
let finalDurationDays = prevDurationDays
|
let finalDurationDays = prevDurationDays
|
||||||
if (mode === 'resize-left' || mode === 'resize-right') {
|
if (mode === 'resize-left' || mode === 'resize-right')
|
||||||
finalDurationDays = proposedDurationDays
|
finalDurationDays = proposedDurationDays
|
||||||
}
|
|
||||||
|
|
||||||
snapshot.startDate = newStartStr
|
snapshot.startDate = newStartStr
|
||||||
snapshot.endDate = toLocalString(
|
snapshot.endDate = toLocalString(
|
||||||
addDays(fromLocalString(newStartStr, DEFAULT_TZ), finalDurationDays),
|
addDays(fromLocalString(newStartStr, DEFAULT_TZ), finalDurationDays),
|
||||||
DEFAULT_TZ,
|
DEFAULT_TZ,
|
||||||
)
|
)
|
||||||
// Rotate weekly recurrence pattern when moving (not resizing) so selected weekdays track shift
|
|
||||||
if (
|
if (
|
||||||
mode === 'move' &&
|
mode === 'move' &&
|
||||||
snapshot.isRepeating &&
|
snapshot.isRepeating &&
|
||||||
@ -391,29 +293,20 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
snapshot.repeatWeekdays = rotated
|
snapshot.repeatWeekdays = rotated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Update the event directly
|
this.events.set(eventId, { ...snapshot, isSpanning: snapshot.startDate < snapshot.endDate })
|
||||||
this.events.set(eventId, {
|
|
||||||
...snapshot,
|
|
||||||
startDate: snapshot.startDate,
|
|
||||||
endDate: snapshot.endDate,
|
|
||||||
isSpanning: snapshot.startDate < snapshot.endDate,
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Split a repeating series at a specific occurrence date and move that occurrence (and future) to new range
|
|
||||||
splitMoveVirtualOccurrence(baseId, occurrenceDateStr, newStartStr, newEndStr) {
|
splitMoveVirtualOccurrence(baseId, occurrenceDateStr, newStartStr, newEndStr) {
|
||||||
const base = this.events.get(baseId)
|
const base = this.events.get(baseId)
|
||||||
if (!base || !base.isRepeating) return
|
if (!base || !base.isRepeating) return
|
||||||
const originalCountRaw = base.repeatCount
|
const originalCountRaw = base.repeatCount
|
||||||
// spanDays not needed for splitting logic here post-refactor
|
|
||||||
const occurrenceDate = fromLocalString(occurrenceDateStr, DEFAULT_TZ)
|
const occurrenceDate = fromLocalString(occurrenceDateStr, DEFAULT_TZ)
|
||||||
const baseStart = fromLocalString(base.startDate, DEFAULT_TZ)
|
const baseStart = fromLocalString(base.startDate, DEFAULT_TZ)
|
||||||
if (occurrenceDate <= baseStart) {
|
if (occurrenceDate <= baseStart) {
|
||||||
// Moving the base itself: just move entire series
|
|
||||||
this.setEventRange(baseId, newStartStr, newEndStr, { mode: 'move' })
|
this.setEventRange(baseId, newStartStr, newEndStr, { mode: 'move' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let keptOccurrences = 0 // number of occurrences BEFORE the moved one
|
let keptOccurrences = 0
|
||||||
if (base.repeat === 'weeks') {
|
if (base.repeat === 'weeks') {
|
||||||
const interval = base.repeatInterval || 1
|
const interval = base.repeatInterval || 1
|
||||||
const pattern = base.repeatWeekdays || []
|
const pattern = base.repeatWeekdays || []
|
||||||
@ -435,15 +328,12 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
(occurrenceDate.getFullYear() - baseStart.getFullYear()) * 12 +
|
(occurrenceDate.getFullYear() - baseStart.getFullYear()) * 12 +
|
||||||
(occurrenceDate.getMonth() - baseStart.getMonth())
|
(occurrenceDate.getMonth() - baseStart.getMonth())
|
||||||
const interval = base.repeatInterval || 1
|
const interval = base.repeatInterval || 1
|
||||||
if (diffMonths <= 0 || diffMonths % interval !== 0) return // invalid occurrence
|
if (diffMonths <= 0 || diffMonths % interval !== 0) return
|
||||||
keptOccurrences = diffMonths // base is occurrence 0; we keep all before diffMonths
|
keptOccurrences = diffMonths
|
||||||
} else {
|
} else {
|
||||||
// Unsupported repeat type
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Truncate original series to keptOccurrences
|
|
||||||
this._terminateRepeatSeriesAtIndex(baseId, keptOccurrences)
|
this._terminateRepeatSeriesAtIndex(baseId, keptOccurrences)
|
||||||
// Compute remaining occurrences count
|
|
||||||
let remainingCount = 'unlimited'
|
let remainingCount = 'unlimited'
|
||||||
if (originalCountRaw !== 'unlimited') {
|
if (originalCountRaw !== 'unlimited') {
|
||||||
const total = parseInt(originalCountRaw, 10)
|
const total = parseInt(originalCountRaw, 10)
|
||||||
@ -453,10 +343,8 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
remainingCount = String(rem)
|
remainingCount = String(rem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Determine repeat-specific adjustments
|
|
||||||
let repeatWeekdays = base.repeatWeekdays
|
let repeatWeekdays = base.repeatWeekdays
|
||||||
if (base.repeat === 'weeks' && Array.isArray(base.repeatWeekdays)) {
|
if (base.repeat === 'weeks' && Array.isArray(base.repeatWeekdays)) {
|
||||||
// Rotate pattern so that the moved occurrence weekday stays active relative to new anchor
|
|
||||||
const origWeekday = occurrenceDate.getDay()
|
const origWeekday = occurrenceDate.getDay()
|
||||||
const newWeekday = fromLocalString(newStartStr, DEFAULT_TZ).getDay()
|
const newWeekday = fromLocalString(newStartStr, DEFAULT_TZ).getDay()
|
||||||
const shift = newWeekday - origWeekday
|
const shift = newWeekday - origWeekday
|
||||||
@ -472,7 +360,6 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
repeatWeekdays = rotated
|
repeatWeekdays = rotated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Create continuation series starting at newStartStr
|
|
||||||
this.createEvent({
|
this.createEvent({
|
||||||
title: base.title,
|
title: base.title,
|
||||||
startDate: newStartStr,
|
startDate: newStartStr,
|
||||||
@ -485,15 +372,11 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// Split a repeating series at a given occurrence index; returns new series id
|
splitRepeatSeries(baseId, occurrenceIndex, newStartStr, newEndStr) {
|
||||||
splitRepeatSeries(baseId, occurrenceIndex, newStartStr, newEndStr, _grabbedWeekday) {
|
|
||||||
const base = this.events.get(baseId)
|
const base = this.events.get(baseId)
|
||||||
if (!base || !base.isRepeating) return null
|
if (!base || !base.isRepeating) return null
|
||||||
// Capture original repeatCount BEFORE truncation
|
|
||||||
const originalCountRaw = base.repeatCount
|
const originalCountRaw = base.repeatCount
|
||||||
// Truncate base to keep occurrences before split point (indices 0..occurrenceIndex-1)
|
|
||||||
this._terminateRepeatSeriesAtIndex(baseId, occurrenceIndex)
|
this._terminateRepeatSeriesAtIndex(baseId, occurrenceIndex)
|
||||||
// Compute new series repeatCount (remaining occurrences starting at occurrenceIndex)
|
|
||||||
let newSeriesCount = 'unlimited'
|
let newSeriesCount = 'unlimited'
|
||||||
if (originalCountRaw !== 'unlimited') {
|
if (originalCountRaw !== 'unlimited') {
|
||||||
const originalNum = parseInt(originalCountRaw, 10)
|
const originalNum = parseInt(originalCountRaw, 10)
|
||||||
@ -502,7 +385,7 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
newSeriesCount = String(Math.max(1, remaining))
|
newSeriesCount = String(Math.max(1, remaining))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const newId = this.createEvent({
|
return this.createEvent({
|
||||||
title: base.title,
|
title: base.title,
|
||||||
startDate: newStartStr,
|
startDate: newStartStr,
|
||||||
endDate: newEndStr,
|
endDate: newEndStr,
|
||||||
@ -512,7 +395,6 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
repeatCount: newSeriesCount,
|
repeatCount: newSeriesCount,
|
||||||
repeatWeekdays: base.repeatWeekdays,
|
repeatWeekdays: base.repeatWeekdays,
|
||||||
})
|
})
|
||||||
return newId
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_terminateRepeatSeriesAtIndex(baseId, index) {
|
_terminateRepeatSeriesAtIndex(baseId, index) {
|
||||||
@ -525,15 +407,10 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
if (!isNaN(rc)) ev.repeatCount = String(Math.min(rc, index))
|
if (!isNaN(rc)) ev.repeatCount = String(Math.min(rc, index))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// _findEventInAnyList removed (direct map access)
|
|
||||||
|
|
||||||
// NOTE: legacy dynamic getEventById for synthetic occurrences removed.
|
|
||||||
},
|
},
|
||||||
persist: {
|
persist: {
|
||||||
key: 'calendar-store',
|
key: 'calendar-store',
|
||||||
storage: localStorage,
|
storage: localStorage,
|
||||||
// Persist only events map, no dates indexing
|
|
||||||
paths: ['today', 'config', 'events'],
|
paths: ['today', 'config', 'events'],
|
||||||
serializer: {
|
serializer: {
|
||||||
serialize(value) {
|
serialize(value) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user