diff --git a/app/Http/Controllers/CalendarController.php b/app/Http/Controllers/CalendarController.php index bc173bf..972e6c2 100644 --- a/app/Http/Controllers/CalendarController.php +++ b/app/Http/Controllers/CalendarController.php @@ -140,7 +140,8 @@ class CalendarController extends Controller $allDayEvents = $events->filter(fn ($event) => !empty($event['all_day'])); $timeEvents = $events->filter(fn ($event) => empty($event['all_day'])); - $hasAllDayEvents = $allDayEvents->isNotEmpty(); + $allDayDisplay = $this->buildAllDayDisplay($allDayEvents); + $hasAllDayEvents = $allDayDisplay->isNotEmpty(); if ($request->boolean('debug_all_day')) { $calendarIds = $calendars->pluck('id')->values(); @@ -297,7 +298,7 @@ class CalendarController extends Controller 'hgroup' => $viewBuilder->viewHeaders($view, $range, $tz, $weekStart), 'events' => $events, // keyed by occurrence 'events_time' => $timeEvents, - 'events_all_day'=> $allDayEvents, + 'events_all_day'=> $allDayDisplay, 'has_all_day' => $hasAllDayEvents, 'grid' => $grid, // day objects hold only ID-sets 'mini' => $mini, // mini calendar days with events for indicators @@ -371,6 +372,52 @@ class CalendarController extends Controller return view('calendar.index', $payload); } + private function buildAllDayDisplay($allDayEvents) + { + $items = collect(); + $groups = $allDayEvents->groupBy(fn ($event) => (int) ($event['start_col'] ?? 1)); + + foreach ($groups as $col => $events) { + $sorted = $events->sortBy('start')->values(); + $total = $sorted->count(); + + if ($total <= 3) { + foreach ($sorted as $index => $event) { + $items->push(array_merge($event, [ + 'all_day_row' => $index + 1, + 'all_day_more' => false, + 'all_day_overflow' => false, + ])); + } + continue; + } + + foreach ($sorted as $index => $event) { + $items->push(array_merge($event, [ + 'all_day_row' => $index + 1, + 'all_day_more' => false, + 'all_day_overflow' => $index >= 2, + ])); + } + + $items->push([ + 'id' => 'more-'.$col, + 'calendar_slug' => $sorted[0]['calendar_slug'] ?? null, + 'start_col' => $col, + 'all_day_row' => 3, + 'all_day_more' => true, + 'all_day_overflow' => false, + 'all_day_more_count' => $total - 2, + 'visible' => true, + ]); + } + + return $items->sortBy([ + ['start_col', 'asc'], + ['all_day_row', 'asc'], + ])->values(); + } + /** * create sabre calendar + meta */ diff --git a/app/Http/Controllers/EventController.php b/app/Http/Controllers/EventController.php index 2ae7efd..6e69e04 100644 --- a/app/Http/Controllers/EventController.php +++ b/app/Http/Controllers/EventController.php @@ -58,6 +58,7 @@ class EventController extends Controller 'tz', 'rrule', ); + $data = array_merge($data, $this->buildRecurrenceFormData($request, $start, $tz, $rrule)); if ($request->header('HX-Request') === 'true') { return view('event.partials.form-modal', $data); @@ -96,6 +97,7 @@ class EventController extends Controller ?? ''; $data = compact('calendar', 'instance', 'event', 'start', 'end', 'tz', 'rrule'); + $data = array_merge($data, $this->buildRecurrenceFormData($request, $start ?? '', $tz, $rrule)); if ($request->header('HX-Request') === 'true') { return view('event.partials.form-modal', $data); @@ -561,6 +563,112 @@ class EventController extends Controller : Carbon::createFromFormat('Y-m-d\\TH:i', $value, $tz)->seconds(0); } + private function buildRecurrenceFormData(Request $request, string $startValue, string $tz, ?string $rrule): array + { + $rruleValue = trim((string) ($rrule ?? '')); + $rruleParts = []; + foreach (array_filter(explode(';', $rruleValue)) as $chunk) { + if (!str_contains($chunk, '=')) { + continue; + } + [$key, $value] = explode('=', $chunk, 2); + $rruleParts[strtoupper($key)] = $value; + } + + $freq = strtolower($rruleParts['FREQ'] ?? ''); + $interval = (int) ($rruleParts['INTERVAL'] ?? 1); + if ($interval < 1) { + $interval = 1; + } + + $byday = array_filter(explode(',', $rruleParts['BYDAY'] ?? '')); + $bymonthday = array_filter(explode(',', $rruleParts['BYMONTHDAY'] ?? '')); + $bysetpos = $rruleParts['BYSETPOS'] ?? null; + + $startDate = null; + if ($startValue !== '') { + try { + $startDate = Carbon::parse($startValue, $tz); + } catch (\Throwable $e) { + $startDate = null; + } + } + $startDate = $startDate ?? Carbon::now($tz); + + $weekdayMap = [ + 'Sun' => 'SU', + 'Mon' => 'MO', + 'Tue' => 'TU', + 'Wed' => 'WE', + 'Thu' => 'TH', + 'Fri' => 'FR', + 'Sat' => 'SA', + ]; + $defaultWeekday = $weekdayMap[$startDate->format('D')] ?? 'MO'; + $defaultMonthDay = (int) $startDate->format('j'); + + $weekMap = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth']; + $startWeek = $startDate->copy(); + $isLastWeek = $startWeek->copy()->addWeek()->month !== $startWeek->month; + $defaultMonthWeek = $isLastWeek ? 'last' : ($weekMap[$startDate->weekOfMonth] ?? 'first'); + + $monthMode = 'days'; + if (!empty($bymonthday)) { + $monthMode = 'days'; + } elseif (!empty($byday) && $bysetpos) { + $monthMode = 'weekday'; + } + + $setposMap = ['1' => 'first', '2' => 'second', '3' => 'third', '4' => 'fourth', '-1' => 'last']; + + $repeatFrequency = $request->old('repeat_frequency', $freq ?: ''); + $repeatInterval = $request->old('repeat_interval', $interval); + $repeatWeekdays = $request->old('repeat_weekdays', $byday ?: [$defaultWeekday]); + $repeatMonthDays = $request->old('repeat_month_days', $bymonthday ?: [$defaultMonthDay]); + $repeatMonthMode = $request->old('repeat_monthly_mode', $monthMode); + $repeatMonthWeek = $request->old('repeat_month_week', $setposMap[(string) $bysetpos] ?? $defaultMonthWeek); + $repeatMonthWeekday = $request->old('repeat_month_weekday', $byday[0] ?? $defaultWeekday); + + $rruleOptions = [ + 'daily' => __('calendar.event.recurrence.daily'), + 'weekly' => __('calendar.event.recurrence.weekly'), + 'monthly' => __('calendar.event.recurrence.monthly'), + 'yearly' => __('calendar.event.recurrence.yearly'), + ]; + + $weekdayOptions = [ + 'SU' => __('calendar.event.recurrence.weekdays.sun_short'), + 'MO' => __('calendar.event.recurrence.weekdays.mon_short'), + 'TU' => __('calendar.event.recurrence.weekdays.tue_short'), + 'WE' => __('calendar.event.recurrence.weekdays.wed_short'), + 'TH' => __('calendar.event.recurrence.weekdays.thu_short'), + 'FR' => __('calendar.event.recurrence.weekdays.fri_short'), + 'SA' => __('calendar.event.recurrence.weekdays.sat_short'), + ]; + $weekdayLong = [ + 'SU' => __('calendar.event.recurrence.weekdays.sun'), + 'MO' => __('calendar.event.recurrence.weekdays.mon'), + 'TU' => __('calendar.event.recurrence.weekdays.tue'), + 'WE' => __('calendar.event.recurrence.weekdays.wed'), + 'TH' => __('calendar.event.recurrence.weekdays.thu'), + 'FR' => __('calendar.event.recurrence.weekdays.fri'), + 'SA' => __('calendar.event.recurrence.weekdays.sat'), + ]; + + return compact( + 'repeatFrequency', + 'repeatInterval', + 'repeatWeekdays', + 'repeatMonthDays', + 'repeatMonthMode', + 'repeatMonthWeek', + 'repeatMonthWeekday', + 'rruleOptions', + 'weekdayOptions', + 'weekdayLong' + ); + } + private function mergeRecurrenceExtra(array $extra, ?string $rrule, string $tz, Request $request): array { if ($rrule === null) { diff --git a/app/Services/Calendar/CalendarViewBuilder.php b/app/Services/Calendar/CalendarViewBuilder.php index d2bd987..afee0ee 100644 --- a/app/Services/Calendar/CalendarViewBuilder.php +++ b/app/Services/Calendar/CalendarViewBuilder.php @@ -58,14 +58,40 @@ class CalendarViewBuilder } if (empty($occurrences) && !$isRecurring) { - $startUtc = $e->meta?->start_at - ? Carbon::parse($e->meta->start_at)->utc() - : Carbon::createFromTimestamp($e->firstoccurence, 'UTC'); - $endUtc = $e->meta?->end_at - ? Carbon::parse($e->meta->end_at)->utc() - : ($e->lastoccurence - ? Carbon::createFromTimestamp($e->lastoccurence, 'UTC') - : $startUtc->copy()); + $meta = $e->meta; + $isAllDay = (bool) ($meta?->all_day ?? false); + $startUtc = null; + $endUtc = null; + + if ($isAllDay && $meta?->start_on && $meta?->end_on) { + $allDayTz = $meta?->tzid + ?? ($meta?->extra['tzid'] ?? null) + ?? $timezone; + $startUtc = Carbon::parse($meta->start_on->toDateString(), $allDayTz) + ->startOfDay() + ->utc(); + $endUtc = Carbon::parse($meta->end_on->toDateString(), $allDayTz) + ->startOfDay() + ->utc(); + } else { + if ($meta?->start_at instanceof Carbon) { + $startUtc = $meta->start_at->copy()->utc(); + } elseif ($meta?->start_at) { + $startUtc = Carbon::parse($meta->start_at, 'UTC'); + } else { + $startUtc = Carbon::createFromTimestamp($e->firstoccurence, 'UTC'); + } + + if ($meta?->end_at instanceof Carbon) { + $endUtc = $meta->end_at->copy()->utc(); + } elseif ($meta?->end_at) { + $endUtc = Carbon::parse($meta->end_at, 'UTC'); + } else { + $endUtc = $e->lastoccurence + ? Carbon::createFromTimestamp($e->lastoccurence, 'UTC') + : $startUtc->copy(); + } + } $occurrences[] = [ 'start' => $startUtc, @@ -86,12 +112,18 @@ class CalendarViewBuilder $colorFg, $gridStartMinutes, $gridEndMinutes, - $daytimeHours + $daytimeHours, + $spanStartUtc, + $spanEndUtc ) { $startUtc = $occ['start']; $endUtc = $occ['end']; $isAllDay = (bool) ($e->meta?->all_day ?? false); + if ($endUtc->lte($spanStartUtc) || $startUtc->gte($spanEndUtc)) { + return null; + } + $startLocal = $startUtc->copy()->timezone($timezone); $endLocal = $endUtc->copy()->timezone($timezone); diff --git a/lang/en/calendar.php b/lang/en/calendar.php index 6371bae..bb2c1f1 100644 --- a/lang/en/calendar.php +++ b/lang/en/calendar.php @@ -82,6 +82,7 @@ return [ 'notes' => 'Notes', 'no_description' => 'No description yet.', 'all_day_events' => 'All-day events', + 'show_more' => ':count more', 'recurrence' => [ 'label' => 'Repeat', 'frequency' => 'Frequency', diff --git a/lang/it/calendar.php b/lang/it/calendar.php index d15bf96..54ba410 100644 --- a/lang/it/calendar.php +++ b/lang/it/calendar.php @@ -82,6 +82,7 @@ return [ 'notes' => 'Note', 'no_description' => 'Nessuna descrizione.', 'all_day_events' => 'Eventi di tutto il giorno', + 'show_more' => 'Mostra altri :count', 'recurrence' => [ 'label' => 'Ripeti', 'frequency' => 'Frequenza', diff --git a/resources/css/etc/layout.css b/resources/css/etc/layout.css index 9270799..6ee82b1 100644 --- a/resources/css/etc/layout.css +++ b/resources/css/etc/layout.css @@ -271,6 +271,16 @@ main { &#settings { /* */ } + + /* main content */ + section { + + /* content footer defaults */ + footer { + transition: padding 250ms ease-in-out; + } + + } } /* expanded */ @@ -404,6 +414,10 @@ main { @apply pl-6; } } + + footer { + @apply pb-3; + } } } } diff --git a/resources/css/lib/button.css b/resources/css/lib/button.css index 3c65dad..46c3e35 100644 --- a/resources/css/lib/button.css +++ b/resources/css/lib/button.css @@ -83,6 +83,14 @@ button, box-shadow: var(--shadows); --shadows: none; + &.button--icon { + @apply rounded-none h-full top-0; + + &:hover { + @apply bg-cyan-200; + } + } + &:hover { @apply bg-cyan-200; } diff --git a/resources/css/lib/calendar.css b/resources/css/lib/calendar.css index a9a6e3d..f95356c 100644 --- a/resources/css/lib/calendar.css +++ b/resources/css/lib/calendar.css @@ -1,6 +1,12 @@ +/** + * calendar + * + * z-index: top is currently 10; overlapping events increment, assuming this won't be 10! + */ + /** * month view - **/ + */ .calendar.month { @apply grid col-span-3 pb-6 2xl:pb-8 pt-2; /*grid-template-rows: 2rem 1fr; */ @@ -166,7 +172,7 @@ /* top day bar */ hgroup { - @apply bg-white col-span-2 border-b-2 border-primary pl-24 sticky z-10; + @apply bg-white col-span-2 border-b-2 border-primary pl-24 sticky z-11; @apply top-20; span.name { @@ -206,24 +212,29 @@ /* all day bar */ ol.day { - @apply sticky top-42 grid col-span-2 bg-white py-2 border-b border-primary col-span-2 pl-24 z-2 overflow-x-hidden; - box-shadow: 0 0.25rem 0.5rem -0.25rem var(--color-gray-200); + @apply sticky top-42 grid col-span-2 bg-white border-b border-primary z-10 overflow-x-hidden; + box-shadow: 0 0.25rem 0.5rem -0.25rem rgba(0,0,0,0.15); + padding: 0.25rem 0 0.2rem 6rem; &::before { - @apply absolute left-0 top-1/2 -translate-y-1/2 w-24 pr-4 text-right; + @apply absolute left-0 top-1.5 w-24 pr-4 text-right; @apply uppercase text-xs font-mono text-secondary font-medium; content: 'All day'; } - li.events { - @apply flex flex-col gap-1 relative overflow-x-hidden; + li.event-wrapper { + @apply flex relative overflow-x-hidden; grid-row-start: var(--event-row); grid-row-end: var(--event-end); grid-column-start: var(--event-col); grid-column-end: calc(var(--event-col) + 1); + &.event-wrapper--overflow { + @apply hidden; + } + a.event { - @apply flex items-center text-xs gap-1 px-1 py-px font-medium rounded-sm w-full; + @apply flex items-center text-xs gap-1 px-1 py-px mb-2px font-medium rounded-sm w-full; background-color: var(--event-bg); color: var(--event-fg); @@ -235,6 +246,27 @@ background-color: color-mix(in srgb, var(--event-bg) 100%, #000 10%); } } + + &.event-wrapper--more { + label.event--more { + @apply text-xs cursor-pointer rounded m-1 h-3.5 leading-none; + @apply focus:outline-2 focus:outline-offset-2 focus:outline-cyan-600; + + input.more-checkbox { + @apply hidden; + } + } + } + } + + &:has(input.more-checkbox:checked) { + li.event-wrapper--overflow { + @apply flex; + } + + li.event-wrapper--more { + @apply hidden; + } } } diff --git a/resources/css/lib/modal.css b/resources/css/lib/modal.css index 855e880..c2deeda 100644 --- a/resources/css/lib/modal.css +++ b/resources/css/lib/modal.css @@ -21,10 +21,10 @@ dialog { @apply relative rounded-xl bg-white border-gray-200 p-0; @apply flex flex-col items-start col-start-1 translate-y-4; @apply overscroll-contain overflow-y-auto; - max-height: calc(100vh - 5em); + max-height: calc(100dvh - 5rem); width: 91.666667%; max-width: 36rem; - transition: all 150ms cubic-bezier(0,0,.2,1); + transition: translate 150ms cubic-bezier(0,0,.2,1); box-shadow: 0 1.5rem 4rem -0.5rem rgba(0, 0, 0, 0.4); > .close-modal { @@ -36,7 +36,7 @@ dialog { /* modal header */ header { - @apply relative flex items-center px-6 h-20 z-2; + @apply sticky top-0 bg-white flex items-center px-6 h-20 z-2; h2 { @apply pr-12; @@ -55,7 +55,7 @@ dialog { /* footer */ footer { - @apply px-6 py-4 border-t-md border-gray-400 flex justify-between; + @apply sticky bottom-0 bg-white px-6 py-4 border-t-md border-gray-400 flex justify-between; } /* event modal with a map */ @@ -84,3 +84,60 @@ dialog { } } } + +/** + * tabs using
and + */ +dialog:has(.modal-tabs) { + @apply items-start justify-items-center; + + #modal { + @apply relative; + max-height: 85dvh; + max-width: 48rem; + top: 10dvh; + } + + form.modal { + @apply !block; + } +} + +.modal-tab { + + > summary { + @apply sticky left-6 w-32 flex flex-row gap-2; + top: 6rem; + + &::before { + @apply bg-gray-10; + } + + &::after { + @apply hidden; + } + } + + &:nth-of-type(2) { + > summary { top: 8.75rem; } + } + &:nth-of-type(3) { + > summary { top: 11.5rem; } + } + &:nth-of-type(4) { + > summary { top: 14.25rem; } + } + &:nth-of-type(5) { + > summary { top: 17rem; } + } + + &[open] { + > summary::before { + @apply bg-gray-100; + } + } + + div.tab-content { + @apply flex flex-col gap-4 pl-40 pt-4 min-h-48; + } +} diff --git a/resources/js/app.js b/resources/js/app.js index f5a48b9..409dd6d 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -19,6 +19,11 @@ const SELECTORS = { monthlyMode: '[data-monthly-mode]', monthlyDays: '[data-monthly-days]', monthlyWeekday: '[data-monthly-weekday]', + modalDialog: 'dialog', + modalContent: '#modal', + tabsRoot: '[data-tabs]', + tabButton: '[role=\"tab\"]', + tabPanel: '[role=\"tabpanel\"]', monthDay: '.calendar.month .day', monthDayEvent: 'a.event', monthDayMore: '[data-day-more]', @@ -43,6 +48,14 @@ document.addEventListener('htmx:configRequest', (evt) => { if (token) evt.detail.headers['X-CSRF-TOKEN'] = token }) +// modal htmx tracking +document.addEventListener('htmx:beforeSwap', (evt) => { + const target = evt.detail?.target || evt.target; + if (target && target.id === 'modal') { + target.dataset.prevUrl = window.location.href; + } +}); + /** * * global auth expiry redirect (fetch/axios) @@ -254,6 +267,74 @@ function initRecurrenceControls(root = document) { } /** + * + * modal behaviors (backdrop close + url restore) + */ +function initModalHandlers(root = document) { + const dialog = root.querySelector(SELECTORS.modalDialog); + if (!dialog || dialog.__modalWired) return; + dialog.__modalWired = true; + + dialog.addEventListener('click', (event) => { + if (event.target === dialog) { + dialog.close(); + } + }); + + dialog.addEventListener('close', () => { + const modal = dialog.querySelector(SELECTORS.modalContent); + if (!modal) return; + + const isEvent = modal.querySelector('[data-modal-kind="event"]'); + const prevUrl = modal.dataset.prevUrl; + modal.innerHTML = ''; + + if (isEvent && prevUrl) { + history.replaceState({}, '', prevUrl); + } + }); +} + +/** + * tabs (simple modal panels) + */ +function initTabs(root = document) { + root.querySelectorAll(SELECTORS.tabsRoot).forEach((tabs) => { + if (tabs.__tabsWired) return; + tabs.__tabsWired = true; + + const buttons = tabs.querySelectorAll(SELECTORS.tabButton); + const panels = tabs.querySelectorAll(SELECTORS.tabPanel); + if (!buttons.length || !panels.length) return; + + const activate = (button) => { + buttons.forEach((btn) => { + const isActive = btn === button; + btn.setAttribute('aria-selected', isActive ? 'true' : 'false'); + }); + + panels.forEach((panel) => { + const id = button.getAttribute('aria-controls'); + panel.hidden = panel.id !== id; + }); + }; + + buttons.forEach((btn) => { + btn.addEventListener('click', (event) => { + event.preventDefault(); + activate(btn); + }); + }); + + const current = tabs.querySelector('[role="tab"][aria-selected="true"]') || buttons[0]; + if (current) { + activate(current); + } + }); +} + +/** + * * auto-scroll time views to 8am on load (when daytime hours are disabled) */ function initTimeViewAutoScroll(root = document) @@ -266,8 +347,8 @@ function initTimeViewAutoScroll(root = document) if (calendar.dataset.autoscrolled === '1') return; if (calendar.dataset.daytimeHoursEnabled === '1') return; - // find the target minute (7:45am) - const target = calendar.querySelector('[data-slot-minutes="465"]'); + // find the target minute (8:00am) + const target = calendar.querySelector('[data-slot-minutes="480"]'); if (!target) return; // get the scroll container and offset @@ -540,6 +621,8 @@ function initUI() { initColorPickers(); initEventAllDayToggles(); initRecurrenceControls(); + initModalHandlers(); + initTabs(); initTimeViewAutoScroll(); initMonthOverflow(); } @@ -549,9 +632,16 @@ document.addEventListener('DOMContentLoaded', initUI); // rebind in htmx for swapped content document.addEventListener('htmx:afterSwap', (e) => { + const target = e.detail?.target || e.target; + if (target && target.id === 'modal') { + target.closest('dialog')?.showModal(); + } + initColorPickers(e.target); initEventAllDayToggles(e.target); initRecurrenceControls(e.target); + initModalHandlers(e.target); + initTabs(e.target); initTimeViewAutoScroll(e.target); initMonthOverflow(e.target); }); diff --git a/resources/svg/icons/repeat.svg b/resources/svg/icons/repeat.svg new file mode 100644 index 0000000..83ccc48 --- /dev/null +++ b/resources/svg/icons/repeat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/views/calendar/index.blade.php b/resources/views/calendar/index.blade.php index f0df88d..50dcd7f 100644 --- a/resources/views/calendar/index.blade.php +++ b/resources/views/calendar/index.blade.php @@ -121,6 +121,7 @@ type="submit" name="date" value="{{ $nav['prev'] }}" + class="button--icon" aria-label="Go back 1 month"> @@ -135,6 +136,7 @@ type="submit" name="date" value="{{ $nav['next'] }}" + class="button--icon" aria-label="Go forward 1 month"> diff --git a/resources/views/components/calendar/time/day-event.blade.php b/resources/views/components/calendar/time/day-event.blade.php index 59ccd39..9487efe 100644 --- a/resources/views/components/calendar/time/day-event.blade.php +++ b/resources/views/components/calendar/time/day-event.blade.php @@ -2,26 +2,46 @@ 'event' => [], ]) -
  • - @php - $showParams = [$event['calendar_slug'], $event['id']]; - if (!empty($event['occurrence'])) { - $showParams['occurrence'] = $event['occurrence']; - } - @endphp - - {{ $event['title'] }} - + --event-row: {{ $row }}; + --event-end: {{ $end }}; + --event-bg: {{ $event['color'] ?? 'var(--color-gray-100)' }}; + --event-fg: {{ $event['color_fg'] ?? 'var(--color-primary)' }};"> + @if($isMore) + @php + $moreId = 'more-col-'.$event['start_col']; + @endphp + + @else + @php + $showParams = [$event['calendar_slug'], $event['id']]; + if (!empty($event['occurrence'])) { + $showParams['occurrence'] = $event['occurrence']; + } + @endphp + + {{ $event['title'] }} + + @endif
  • diff --git a/resources/views/components/modal/index.blade.php b/resources/views/components/modal/index.blade.php index 9b51ed9..4b406f0 100644 --- a/resources/views/components/modal/index.blade.php +++ b/resources/views/components/modal/index.blade.php @@ -1,11 +1,6 @@ - + diff --git a/resources/views/event/form.blade.php b/resources/views/event/form.blade.php index bee8032..4cd4f47 100644 --- a/resources/views/event/form.blade.php +++ b/resources/views/event/form.blade.php @@ -2,7 +2,7 @@

    - {{ $event->exists ? __('Edit Event') : __('Create Event') }} + {{ $event->exists ? __('Edit event details') : __('Create a new event') }}

    {{-- “Back” breadcrumb --}} diff --git a/resources/views/event/partials/form-modal.blade.php b/resources/views/event/partials/form-modal.blade.php index f3ce176..741b8f0 100644 --- a/resources/views/event/partials/form-modal.blade.php +++ b/resources/views/event/partials/form-modal.blade.php @@ -1,6 +1,6 @@ - + -

    {{ $event->exists ? __('Edit Event') : __('Create Event') }}

    +

    {{ $event->exists ? __('Edit event details') : __('Create a new event') }}

    @include('event.partials.form', [ @@ -17,7 +17,7 @@ {{ __('common.cancel') }} - {{ $event->exists ? __('Save') : __('Create') }} + {{ $event->exists ? __('common.save') : __('Create event') }}
    diff --git a/resources/views/event/partials/form.blade.php b/resources/views/event/partials/form.blade.php index c9629bb..054846c 100644 --- a/resources/views/event/partials/form.blade.php +++ b/resources/views/event/partials/form.blade.php @@ -3,83 +3,6 @@ $formAction = $event->exists ? route('calendar.event.update', [$calendar, $event]) : route('calendar.event.store', $calendar); - $rruleValue = trim((string) ($rrule ?? '')); - $rruleParts = []; - foreach (array_filter(explode(';', $rruleValue)) as $chunk) { - if (!str_contains($chunk, '=')) continue; - [$key, $value] = explode('=', $chunk, 2); - $rruleParts[strtoupper($key)] = $value; - } - - $freq = strtolower($rruleParts['FREQ'] ?? ''); - $interval = (int) ($rruleParts['INTERVAL'] ?? 1); - if ($interval < 1) $interval = 1; - - $byday = array_filter(explode(',', $rruleParts['BYDAY'] ?? '')); - $bymonthday = array_filter(explode(',', $rruleParts['BYMONTHDAY'] ?? '')); - $bysetpos = $rruleParts['BYSETPOS'] ?? null; - - $startDefault = old('start_at', $start ?? null); - $startDate = $startDefault ? \Carbon\Carbon::parse($startDefault) : \Carbon\Carbon::now(); - $weekdayMap = [ - 'Sun' => 'SU', - 'Mon' => 'MO', - 'Tue' => 'TU', - 'Wed' => 'WE', - 'Thu' => 'TH', - 'Fri' => 'FR', - 'Sat' => 'SA', - ]; - $defaultWeekday = $weekdayMap[$startDate->format('D')] ?? 'MO'; - $defaultMonthDay = (int) $startDate->format('j'); - - $weekMap = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth']; - $startWeek = $startDate->copy(); - $isLastWeek = $startWeek->copy()->addWeek()->month !== $startWeek->month; - $defaultMonthWeek = $isLastWeek ? 'last' : ($weekMap[$startDate->weekOfMonth] ?? 'first'); - - $monthMode = 'days'; - if (!empty($bymonthday)) { - $monthMode = 'days'; - } elseif (!empty($byday) && $bysetpos) { - $monthMode = 'weekday'; - } - - $repeatFrequency = old('repeat_frequency', $freq ?: ''); - $repeatInterval = old('repeat_interval', $interval); - $repeatWeekdays = old('repeat_weekdays', $byday ?: [$defaultWeekday]); - $repeatMonthDays = old('repeat_month_days', $bymonthday ?: [$defaultMonthDay]); - $repeatMonthMode = old('repeat_monthly_mode', $monthMode); - - $setposMap = ['1' => 'first', '2' => 'second', '3' => 'third', '4' => 'fourth', '-1' => 'last']; - $repeatMonthWeek = old('repeat_month_week', $setposMap[(string) $bysetpos] ?? $defaultMonthWeek); - $repeatMonthWeekday = old('repeat_month_weekday', $byday[0] ?? $defaultWeekday); - - $rruleOptions = [ - 'daily' => __('calendar.event.recurrence.daily'), - 'weekly' => __('calendar.event.recurrence.weekly'), - 'monthly' => __('calendar.event.recurrence.monthly'), - 'yearly' => __('calendar.event.recurrence.yearly'), - ]; - - $weekdayOptions = [ - 'SU' => __('calendar.event.recurrence.weekdays.sun_short'), - 'MO' => __('calendar.event.recurrence.weekdays.mon_short'), - 'TU' => __('calendar.event.recurrence.weekdays.tue_short'), - 'WE' => __('calendar.event.recurrence.weekdays.wed_short'), - 'TH' => __('calendar.event.recurrence.weekdays.thu_short'), - 'FR' => __('calendar.event.recurrence.weekdays.fri_short'), - 'SA' => __('calendar.event.recurrence.weekdays.sat_short'), - ]; - $weekdayLong = [ - 'SU' => __('calendar.event.recurrence.weekdays.sun'), - 'MO' => __('calendar.event.recurrence.weekdays.mon'), - 'TU' => __('calendar.event.recurrence.weekdays.tue'), - 'WE' => __('calendar.event.recurrence.weekdays.wed'), - 'TH' => __('calendar.event.recurrence.weekdays.thu'), - 'FR' => __('calendar.event.recurrence.weekdays.fri'), - 'SA' => __('calendar.event.recurrence.weekdays.sat'), - ]; @endphp