Fixed annual repeats UX
This commit is contained in:
		| @@ -31,6 +31,9 @@ const eventSaved = ref(false) | ||||
| const titleInput = ref(null) | ||||
| const modalRef = ref(null) | ||||
|  | ||||
| // UI display mode - stays fixed unless user manually changes it | ||||
| const uiDisplayFrequency = ref('weeks') // 'weeks' | 'months' | 'years' | ||||
|  | ||||
| // Drag functionality | ||||
| const isDragging = ref(false) | ||||
| const dragOffset = ref({ x: 0, y: 0 }) | ||||
| @@ -107,11 +110,66 @@ const fallbackWeekdays = computed(() => { | ||||
|   return fallback | ||||
| }) | ||||
|  | ||||
| // Repeat mapping uses 'weeks' | 'months' | 'none' directly | ||||
| // Computed property to handle UI frequency (weeks/months/years) vs store frequency (weeks/months) | ||||
| const displayFrequency = computed({ | ||||
|   get() { | ||||
|     return uiDisplayFrequency.value | ||||
|   }, | ||||
|   set(val) { | ||||
|     const oldFreq = uiDisplayFrequency.value | ||||
|     const currentDisplayValue = displayInterval.value // Get the current display value before changing | ||||
|     uiDisplayFrequency.value = val | ||||
|  | ||||
|     if (oldFreq === val) return // No change | ||||
|  | ||||
|     if (oldFreq === 'years' && val === 'months') { | ||||
|       // Converting from years to months: keep the display value the same, but now it represents months | ||||
|       recurrenceFrequency.value = 'months' | ||||
|       recurrenceInterval.value = currentDisplayValue // Keep the same number, now as months | ||||
|     } else if (oldFreq === 'months' && val === 'years') { | ||||
|       // Converting from months to years: keep the display value the same, but now it represents years | ||||
|       recurrenceFrequency.value = 'months' | ||||
|       recurrenceInterval.value = currentDisplayValue * 12 // Convert to months for storage | ||||
|     } else if (val === 'years') { | ||||
|       // Converting from weeks to years | ||||
|       recurrenceFrequency.value = 'months' | ||||
|       recurrenceInterval.value = 12 // Default to 1 year | ||||
|     } else if (val === 'months') { | ||||
|       // Converting to months from weeks | ||||
|       recurrenceFrequency.value = 'months' | ||||
|       if (oldFreq === 'weeks') { | ||||
|         recurrenceInterval.value = 1 // Default to 1 month | ||||
|       } | ||||
|     } else if (val === 'weeks') { | ||||
|       // Converting to weeks | ||||
|       recurrenceFrequency.value = 'weeks' | ||||
|       recurrenceInterval.value = 1 // Default to 1 week | ||||
|     } | ||||
|   }, | ||||
| }) | ||||
|  | ||||
| // Computed property for display interval (handles years conversion) | ||||
| const displayInterval = computed({ | ||||
|   get() { | ||||
|     if (uiDisplayFrequency.value === 'years') { | ||||
|       return recurrenceInterval.value / 12 | ||||
|     } | ||||
|     return recurrenceInterval.value | ||||
|   }, | ||||
|   set(val) { | ||||
|     if (uiDisplayFrequency.value === 'years') { | ||||
|       recurrenceInterval.value = val * 12 | ||||
|     } else { | ||||
|       recurrenceInterval.value = val | ||||
|     } | ||||
|   }, | ||||
| }) | ||||
|  | ||||
| // Repeat mapping uses 'weeks' | 'months' | 'none' directly (store only supports weeks/months) | ||||
| const repeat = computed({ | ||||
|   get() { | ||||
|     if (!recurrenceEnabled.value) return 'none' | ||||
|     return recurrenceFrequency.value // 'weeks' | 'months' | ||||
|     return recurrenceFrequency.value // Always 'weeks' | 'months' for store | ||||
|   }, | ||||
|   set(val) { | ||||
|     if (val === 'none') { | ||||
| @@ -119,8 +177,16 @@ const repeat = computed({ | ||||
|       return | ||||
|     } | ||||
|     recurrenceEnabled.value = true | ||||
|     if (val === 'weeks') recurrenceFrequency.value = 'weeks' | ||||
|     else if (val === 'months') recurrenceFrequency.value = 'months' | ||||
|     if (val === 'weeks') { | ||||
|       recurrenceFrequency.value = 'weeks' | ||||
|       uiDisplayFrequency.value = 'weeks' | ||||
|       if (recurrenceInterval.value >= 12) { | ||||
|         recurrenceInterval.value = 1 // Reset to sensible weekly default | ||||
|       } | ||||
|     } else if (val === 'months') { | ||||
|       recurrenceFrequency.value = 'months' | ||||
|       // Don't change UI display frequency here - let it be determined by context | ||||
|     } | ||||
|   }, | ||||
| }) | ||||
|  | ||||
| @@ -187,6 +253,7 @@ function openCreateDialog(selectionData = null) { | ||||
|   recurrenceEnabled.value = false | ||||
|   recurrenceInterval.value = 1 | ||||
|   recurrenceFrequency.value = 'weeks' | ||||
|   uiDisplayFrequency.value = 'weeks' // Set initial UI display mode | ||||
|   recurrenceWeekdays.value = [false, false, false, false, false, false, false] | ||||
|   recurrenceOccurrences.value = 0 | ||||
|   colorId.value = calendarStore.selectEventColorId(start, end) | ||||
| @@ -294,6 +361,21 @@ function openEditDialog(payload) { | ||||
|   loadWeekdayPatternFromStore(event.repeatWeekdays) | ||||
|   repeat.value = event.repeat // triggers setter mapping into recurrence state | ||||
|   if (event.repeatInterval) recurrenceInterval.value = event.repeatInterval | ||||
|  | ||||
|   // Set UI display frequency based on loaded data | ||||
|   if (event.repeat === 'weeks') { | ||||
|     uiDisplayFrequency.value = 'weeks' | ||||
|   } else if (event.repeat === 'months') { | ||||
|     // If it's a yearly interval (multiple of 12), show as years | ||||
|     if (event.repeatInterval && event.repeatInterval % 12 === 0 && event.repeatInterval >= 12) { | ||||
|       uiDisplayFrequency.value = 'years' | ||||
|     } else { | ||||
|       uiDisplayFrequency.value = 'months' | ||||
|     } | ||||
|   } else { | ||||
|     uiDisplayFrequency.value = 'weeks' // fallback | ||||
|   } | ||||
|  | ||||
|   // Map repeatCount | ||||
|   const rc = event.repeatCount ?? 'unlimited' | ||||
|   recurrenceOccurrences.value = rc === 'unlimited' ? 0 : parseInt(rc, 10) || 0 | ||||
| @@ -510,7 +592,7 @@ const finalOccurrenceDate = computed(() => { | ||||
|   const base = editingEventId.value ? calendarStore.getEventById(editingEventId.value) : null | ||||
|   if (!base) return null | ||||
|   const start = new Date(base.startDate + 'T00:00:00') | ||||
|   if (recurrenceFrequency.value === 'weeks') { | ||||
|   if (uiDisplayFrequency.value === 'weeks') { | ||||
|     // iterate days until we count 'count-1' additional occurrences (first is base if selected weekday) | ||||
|     const pattern = buildStoreWeekdayPattern() // Sun..Sat | ||||
|     // Build Monday-first pattern again for selection clarity | ||||
| @@ -530,11 +612,16 @@ const finalOccurrenceDate = computed(() => { | ||||
|     } | ||||
|     if (occs === count) return cursor | ||||
|     return null | ||||
|   } else if (recurrenceFrequency.value === 'months') { | ||||
|     const monthsToAdd = recurrenceInterval.value * (count - 1) | ||||
|   } else if (uiDisplayFrequency.value === 'months') { | ||||
|     const monthsToAdd = displayInterval.value * (count - 1) | ||||
|     const d = new Date(start) | ||||
|     d.setMonth(d.getMonth() + monthsToAdd) | ||||
|     return d | ||||
|   } else if (uiDisplayFrequency.value === 'years') { | ||||
|     const yearsToAdd = displayInterval.value * (count - 1) | ||||
|     const d = new Date(start) | ||||
|     d.setFullYear(d.getFullYear() + yearsToAdd) | ||||
|     return d | ||||
|   } | ||||
| }) | ||||
|  | ||||
| @@ -560,15 +647,18 @@ const formattedFinalOccurrence = computed(() => { | ||||
|  | ||||
| const recurrenceSummary = computed(() => { | ||||
|   if (!recurrenceEnabled.value) return 'Does not recur' | ||||
|   if (recurrenceFrequency.value === 'weeks') { | ||||
|     return recurrenceInterval.value === 1 ? 'Weekly' : `Every ${recurrenceInterval.value} weeks` | ||||
|   if (uiDisplayFrequency.value === 'weeks') { | ||||
|     return displayInterval.value === 1 ? 'Weekly' : `Every ${displayInterval.value} weeks` | ||||
|   } else if (uiDisplayFrequency.value === 'years') { | ||||
|     return displayInterval.value === 1 ? 'Annually' : `Every ${displayInterval.value} years` | ||||
|   } | ||||
|   // months frequency | ||||
|   if (recurrenceInterval.value % 12 === 0) { | ||||
|   // For months frequency - always check the underlying recurrenceInterval for yearly patterns | ||||
|   if (recurrenceFrequency.value === 'months' && recurrenceInterval.value % 12 === 0) { | ||||
|     const years = recurrenceInterval.value / 12 | ||||
|     return years === 1 ? 'Annually' : `Every ${years} years` | ||||
|   } | ||||
|   return recurrenceInterval.value === 1 ? 'Monthly' : `Every ${recurrenceInterval.value} months` | ||||
|   // Regular monthly display | ||||
|   return displayInterval.value === 1 ? 'Monthly' : `Every ${displayInterval.value} months` | ||||
| }) | ||||
| </script> | ||||
|  | ||||
| @@ -610,15 +700,16 @@ const recurrenceSummary = computed(() => { | ||||
|       <div v-if="recurrenceEnabled" class="recurrence-form"> | ||||
|         <div class="line compact"> | ||||
|           <Numeric | ||||
|             v-model="recurrenceInterval" | ||||
|             v-model="displayInterval" | ||||
|             :prefix-values="[{ value: 1, display: 'Every' }]" | ||||
|             :min="2" | ||||
|             number-prefix="Every " | ||||
|             aria-label="Interval" | ||||
|           /> | ||||
|           <select v-model="recurrenceFrequency" class="freq-select"> | ||||
|             <option value="weeks">{{ recurrenceInterval === 1 ? 'week' : 'weeks' }}</option> | ||||
|             <option value="months">{{ recurrenceInterval === 1 ? 'month' : 'months' }}</option> | ||||
|           <select v-model="displayFrequency" class="freq-select"> | ||||
|             <option value="weeks">{{ displayInterval === 1 ? 'week' : 'weeks' }}</option> | ||||
|             <option value="months">{{ displayInterval === 1 ? 'month' : 'months' }}</option> | ||||
|             <option value="years">{{ displayInterval === 1 ? 'year' : 'years' }}</option> | ||||
|           </select> | ||||
|           <Numeric | ||||
|             class="occ-stepper" | ||||
| @@ -630,7 +721,7 @@ const recurrenceSummary = computed(() => { | ||||
|             extra-class="occ" | ||||
|           /> | ||||
|         </div> | ||||
|         <div v-if="recurrenceFrequency === 'weeks'" @click.stop> | ||||
|         <div v-if="uiDisplayFrequency === 'weeks'" @click.stop> | ||||
|           <WeekdaySelector | ||||
|             v-model="recurrenceWeekdays" | ||||
|             :fallback="fallbackWeekdays" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Leo Vasanko
					Leo Vasanko