431 lines
22 KiB
PHP
431 lines
22 KiB
PHP
@php
|
|
$isModal = $isModal ?? false;
|
|
$formAction = $event->exists
|
|
? route('calendar.event.update', [$calendar, $event])
|
|
: route('calendar.event.store', $calendar);
|
|
@endphp
|
|
|
|
<form method="POST" id="event-form" action="{{ $formAction }}" class="settings modal">
|
|
@csrf
|
|
@if($event->exists)
|
|
@method('PUT')
|
|
@endif
|
|
|
|
{{-- Title --}}
|
|
<div class="event-field">
|
|
<div class="event-field-icon"><!-- empty --></div>
|
|
<div class="input-row input-row--1">
|
|
<div class="input-cell">
|
|
<x-input.text
|
|
id="title"
|
|
name="title"
|
|
type="text"
|
|
:value="old('title', $event->meta?->title ?? '')"
|
|
placeholder="Event title..."
|
|
required
|
|
:autofocus="$event->exists"
|
|
/>
|
|
<x-input.error class="mt-2" :messages="$errors->get('title')" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Calendar --}}
|
|
<div class="event-field">
|
|
<div class="event-field-icon">
|
|
<x-icon-calendar />
|
|
</div>
|
|
<div class="input-row input-row--1">
|
|
<div class="input-cell">
|
|
<div class="relative" data-calendar-picker>
|
|
<input type="hidden" id="calendar_uri" name="calendar_uri" value="{{ $selectedCalendarUri ?? '' }}" data-calendar-picker-input>
|
|
|
|
<button
|
|
type="button"
|
|
class="button button--secondary w-full justify-between"
|
|
data-calendar-picker-toggle
|
|
aria-expanded="false"
|
|
>
|
|
<span class="inline-flex items-center gap-2">
|
|
<span class="inline-block h-3 w-3 rounded-full" data-calendar-picker-color style="background-color: {{ $selectedCalendarColor ?? '#64748b' }}"></span>
|
|
<span data-calendar-picker-label>{{ $selectedCalendarName ?? __('common.calendar') }}</span>
|
|
</span>
|
|
<x-icon-chevron-down width="18" />
|
|
</button>
|
|
|
|
<div class="absolute z-20 mt-2 w-full rounded-md border-md border-secondary bg-white shadow-sm hidden" data-calendar-picker-menu>
|
|
<ul class="list-none p-1 m-0 flex flex-col gap-1">
|
|
@foreach (($calendarPickerOptions ?? []) as $option)
|
|
<li>
|
|
<button
|
|
type="button"
|
|
class="button button--tertiary w-full justify-start"
|
|
data-calendar-picker-option
|
|
data-calendar-picker-uri="{{ $option['uri'] }}"
|
|
data-calendar-picker-name="{{ $option['name'] }}"
|
|
data-calendar-picker-color="{{ $option['color'] }}"
|
|
>
|
|
<span class="inline-block h-3 w-3 rounded-full" style="background-color: {{ $option['color'] }}"></span>
|
|
<span>{{ $option['name'] }}</span>
|
|
</button>
|
|
</li>
|
|
@endforeach
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Location --}}
|
|
<div class="event-field">
|
|
<div class="event-field-icon">
|
|
<x-icon-pin />
|
|
</div>
|
|
<div class="input-row input-row--1">
|
|
<div class="input-cell">
|
|
<x-input.text
|
|
id="location"
|
|
name="location"
|
|
:value="old('location', $event->meta?->location ?? '')"
|
|
placeholder="Location..."
|
|
{{-- live suggestions via htmx --}}
|
|
hx-get="{{ route('location.suggest') }}"
|
|
hx-trigger="keyup changed delay:300ms"
|
|
hx-target="#location-suggestions"
|
|
hx-swap="innerHTML"
|
|
/>
|
|
<x-input.error :messages="$errors->get('location')" />
|
|
|
|
{{-- suggestion dropdown target --}}
|
|
<div id="location-suggestions" class="relative z-20"></div>
|
|
{{-- hidden fields (filled when user clicks a suggestion; handy for step #2) --}}
|
|
<input type="hidden" id="loc_display_name" name="loc_display_name" />
|
|
<input type="hidden" id="loc_place_name" name="loc_place_name" />
|
|
<input type="hidden" id="loc_street" name="loc_street" />
|
|
<input type="hidden" id="loc_city" name="loc_city" />
|
|
<input type="hidden" id="loc_state" name="loc_state" />
|
|
<input type="hidden" id="loc_postal" name="loc_postal" />
|
|
<input type="hidden" id="loc_country" name="loc_country" />
|
|
<input type="hidden" id="loc_lat" name="loc_lat" />
|
|
<input type="hidden" id="loc_lon" name="loc_lon" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Start / End --}}
|
|
<div class="event-field">
|
|
<div class="event-field-icon">
|
|
<x-icon-calendar-clock />
|
|
</div>
|
|
<div class="input-rows">
|
|
<div class="input-row input-row--1-1">
|
|
<div class="input-cell">
|
|
<x-input.text
|
|
id="start_at"
|
|
name="start_at"
|
|
type="datetime-local"
|
|
:value="old('start_at', $start)"
|
|
data-event-start
|
|
required
|
|
aria-label="Start date and time"
|
|
/>
|
|
<x-input.error :messages="$errors->get('start_at')" />
|
|
</div>
|
|
<div class="input-cell">
|
|
<x-input.text
|
|
id="end_at"
|
|
name="end_at"
|
|
type="datetime-local"
|
|
:value="old('end_at', $end)"
|
|
data-event-end
|
|
required
|
|
aria-label="End date and time"
|
|
/>
|
|
<x-input.error :messages="$errors->get('end_at')" />
|
|
</div>
|
|
</div>
|
|
<div class="input-row input-row--1">
|
|
<div class="input-cell ml-2px">
|
|
<x-input.checkbox-label
|
|
label="{{ __('All day event') }}"
|
|
id="all_day"
|
|
name="all_day"
|
|
value="1"
|
|
data-all-day-toggle
|
|
:checked="(bool) old('all_day', $event->meta?->all_day)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- recurrence --}}
|
|
<div class="event-field">
|
|
<div class="event-field-icon">
|
|
<x-icon-repeat />
|
|
</div>
|
|
<div class="input-row input-row--1">
|
|
<div class="input-cell">
|
|
<x-input.select
|
|
id="repeat_frequency"
|
|
name="repeat_frequency"
|
|
:options="$rruleOptions"
|
|
:selected="$repeatFrequency"
|
|
:placeholder="__('calendar.event.recurrence.none')"
|
|
data-recurrence-frequency
|
|
/>
|
|
<x-input.error :messages="$errors->get('repeat_frequency')" />
|
|
|
|
<div class="mt-3 {{ $repeatFrequency === '' ? 'hidden' : '' }}" data-recurrence-interval>
|
|
<x-input.label for="repeat_interval" :value="__('calendar.event.recurrence.every')" />
|
|
<div class="flex items-center gap-2">
|
|
<x-input.text
|
|
id="repeat_interval"
|
|
name="repeat_interval"
|
|
type="number"
|
|
min="1"
|
|
max="365"
|
|
:value="$repeatInterval"
|
|
/>
|
|
<span class="text-sm text-gray-600" data-recurrence-unit></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4 {{ $repeatFrequency !== 'weekly' ? 'hidden' : '' }}" data-recurrence-section="weekly">
|
|
<p class="text-sm text-gray-600">{{ __('calendar.event.recurrence.on_days') }}</p>
|
|
<div class="flex flex-wrap gap-2 mt-2">
|
|
@foreach ($weekdayOptions as $code => $label)
|
|
<label class="inline-flex items-center gap-2 text-sm">
|
|
<x-input.checkbox
|
|
name="repeat_weekdays[]"
|
|
value="{{ $code }}"
|
|
:checked="in_array($code, (array) $repeatWeekdays, true)"
|
|
/>
|
|
<span title="{{ $weekdayLong[$code] ?? $code }}">{{ $label }}</span>
|
|
</label>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4 {{ $repeatFrequency !== 'monthly' ? 'hidden' : '' }}" data-recurrence-section="monthly">
|
|
<div class="flex flex-col gap-3">
|
|
<div class="flex items-center gap-4">
|
|
<x-input.radio-label
|
|
id="repeat_monthly_mode_days"
|
|
name="repeat_monthly_mode"
|
|
value="days"
|
|
label="{{ __('calendar.event.recurrence.on_days') }}"
|
|
:checked="$repeatMonthMode === 'days'"
|
|
data-monthly-mode
|
|
/>
|
|
<x-input.radio-label
|
|
id="repeat_monthly_mode_weekday"
|
|
name="repeat_monthly_mode"
|
|
value="weekday"
|
|
label="{{ __('calendar.event.recurrence.on_the') }}"
|
|
:checked="$repeatMonthMode === 'weekday'"
|
|
data-monthly-mode
|
|
/>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-7 gap-2 {{ $repeatMonthMode !== 'days' ? 'hidden' : '' }}" data-monthly-days>
|
|
@for ($day = 1; $day <= 31; $day++)
|
|
<label class="inline-flex items-center gap-2 text-xs">
|
|
<x-input.checkbox
|
|
name="repeat_month_days[]"
|
|
value="{{ $day }}"
|
|
:checked="in_array($day, (array) $repeatMonthDays)"
|
|
/>
|
|
<span>{{ $day }}</span>
|
|
</label>
|
|
@endfor
|
|
</div>
|
|
|
|
<div class="flex items-center gap-3 {{ $repeatMonthMode !== 'weekday' ? 'hidden' : '' }}" data-monthly-weekday>
|
|
<x-input.select
|
|
id="repeat_month_week"
|
|
name="repeat_month_week"
|
|
:options="[
|
|
'first' => __('calendar.event.recurrence.week_order.first'),
|
|
'second' => __('calendar.event.recurrence.week_order.second'),
|
|
'third' => __('calendar.event.recurrence.week_order.third'),
|
|
'fourth' => __('calendar.event.recurrence.week_order.fourth'),
|
|
'last' => __('calendar.event.recurrence.week_order.last'),
|
|
]"
|
|
:selected="$repeatMonthWeek"
|
|
/>
|
|
<x-input.select
|
|
id="repeat_month_weekday"
|
|
name="repeat_month_weekday"
|
|
:options="$weekdayLong"
|
|
:selected="$repeatMonthWeekday"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mt-4 text-sm text-gray-600 {{ $repeatFrequency !== 'yearly' ? 'hidden' : '' }}" data-recurrence-section="yearly">
|
|
{{ __('calendar.event.recurrence.yearly_hint') }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- attendees --}}
|
|
<div class="event-field">
|
|
<div class="event-field-icon">
|
|
<x-icon-user-circle />
|
|
</div>
|
|
<div class="input-row input-row--1">
|
|
<div class="input-cell">
|
|
<input type="hidden" name="attendees_present" value="1">
|
|
|
|
<div class="flex flex-col gap-3" data-attendees data-next-index="{{ count($attendees ?? []) }}">
|
|
<div class="flex flex-col gap-2">
|
|
<x-input.label for="attendee_lookup" :value="__('calendar.event.attendees.add')" />
|
|
<p class="text-sm text-gray-600">{{ __('calendar.event.attendees.help') }}</p>
|
|
<div class="flex items-center gap-2">
|
|
<x-input.text
|
|
id="attendee_lookup"
|
|
name="attendee"
|
|
type="text"
|
|
placeholder="{{ __('calendar.event.attendees.search_placeholder') }}"
|
|
data-attendee-lookup
|
|
hx-get="{{ route('attendee.suggest') }}"
|
|
hx-trigger="keyup changed delay:250ms"
|
|
hx-target="#attendee-suggestions"
|
|
hx-swap="innerHTML"
|
|
/>
|
|
<x-button type="button" variant="tertiary" data-attendee-add-manual>
|
|
{{ __('calendar.event.attendees.add_button') }}
|
|
</x-button>
|
|
</div>
|
|
<div id="attendee-suggestions"></div>
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-3" data-attendees-list>
|
|
@foreach (($attendees ?? []) as $index => $attendee)
|
|
<div class="attendee-row rounded-lg border border-gray-200 p-3 flex flex-col gap-3" data-attendee-row>
|
|
<input type="hidden" name="attendees[{{ $index }}][attendee_uri]" value="{{ $attendee['attendee_uri'] ?? '' }}" data-attendee-uri>
|
|
<input type="hidden" name="attendees[{{ $index }}][email]" value="{{ $attendee['email'] ?? '' }}" data-attendee-email>
|
|
<input type="hidden" name="attendees[{{ $index }}][name]" value="{{ $attendee['name'] ?? '' }}" data-attendee-name>
|
|
<input type="hidden" name="attendees[{{ $index }}][role]" value="{{ !empty($attendee['optional']) ? 'OPT-PARTICIPANT' : 'REQ-PARTICIPANT' }}" data-attendee-role>
|
|
<input type="hidden" name="attendees[{{ $index }}][partstat]" value="NEEDS-ACTION">
|
|
<input type="hidden" name="attendees[{{ $index }}][cutype]" value="INDIVIDUAL">
|
|
<input type="hidden" name="attendees[{{ $index }}][is_organizer]" value="0">
|
|
|
|
<div class="flex items-center justify-between gap-3">
|
|
<div class="flex flex-col">
|
|
<strong data-attendee-display>
|
|
{{ ($attendee['name'] ?? '') !== '' ? ($attendee['name'] . ' <' . ($attendee['email'] ?? '') . '>') : ($attendee['email'] ?? '') }}
|
|
</strong>
|
|
<span class="text-xs text-emerald-700 {{ !empty($attendee['verified']) ? '' : 'hidden' }}" data-attendee-verified>
|
|
{{ __('calendar.event.attendees.verified') }}
|
|
</span>
|
|
</div>
|
|
<button type="button" class="button button--tertiary" data-attendee-remove>
|
|
<x-icon-x width="18" />
|
|
<span class="sr-only">{{ __('calendar.event.attendees.remove') }}</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-4">
|
|
<label class="inline-flex items-center gap-2 text-sm">
|
|
<input type="hidden" name="attendees[{{ $index }}][optional]" value="0">
|
|
<x-input.checkbox
|
|
name="attendees[{{ $index }}][optional]"
|
|
value="1"
|
|
:checked="(bool) ($attendee['optional'] ?? false)"
|
|
data-attendee-optional
|
|
/>
|
|
<span>{{ __('calendar.event.attendees.optional') }}</span>
|
|
</label>
|
|
|
|
<label class="inline-flex items-center gap-2 text-sm">
|
|
<input type="hidden" name="attendees[{{ $index }}][rsvp]" value="0">
|
|
<x-input.checkbox
|
|
name="attendees[{{ $index }}][rsvp]"
|
|
value="1"
|
|
:checked="(bool) ($attendee['rsvp'] ?? true)"
|
|
/>
|
|
<span>{{ __('calendar.event.attendees.rsvp') }}</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
|
|
<template data-attendee-template>
|
|
<div class="attendee-row rounded-lg border border-gray-200 p-3 flex flex-col gap-3" data-attendee-row>
|
|
<input type="hidden" name="attendees[__INDEX__][attendee_uri]" value="" data-attendee-uri>
|
|
<input type="hidden" name="attendees[__INDEX__][email]" value="" data-attendee-email>
|
|
<input type="hidden" name="attendees[__INDEX__][name]" value="" data-attendee-name>
|
|
<input type="hidden" name="attendees[__INDEX__][role]" value="REQ-PARTICIPANT" data-attendee-role>
|
|
<input type="hidden" name="attendees[__INDEX__][partstat]" value="NEEDS-ACTION">
|
|
<input type="hidden" name="attendees[__INDEX__][cutype]" value="INDIVIDUAL">
|
|
<input type="hidden" name="attendees[__INDEX__][is_organizer]" value="0">
|
|
|
|
<div class="flex items-center justify-between gap-3">
|
|
<div class="flex flex-col">
|
|
<strong data-attendee-display></strong>
|
|
<span class="text-xs text-emerald-700 hidden" data-attendee-verified>
|
|
{{ __('calendar.event.attendees.verified') }}
|
|
</span>
|
|
</div>
|
|
<button type="button" class="button button--tertiary" data-attendee-remove>
|
|
<x-icon-x width="18" />
|
|
<span class="sr-only">{{ __('calendar.event.attendees.remove') }}</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-4">
|
|
<label class="inline-flex items-center gap-2 text-sm">
|
|
<input type="hidden" name="attendees[__INDEX__][optional]" value="0">
|
|
<input name="attendees[__INDEX__][optional]" type="checkbox" value="1" data-attendee-optional>
|
|
<span>{{ __('calendar.event.attendees.optional') }}</span>
|
|
</label>
|
|
|
|
<label class="inline-flex items-center gap-2 text-sm">
|
|
<input type="hidden" name="attendees[__INDEX__][rsvp]" value="0">
|
|
<input name="attendees[__INDEX__][rsvp]" type="checkbox" value="1" checked>
|
|
<span>{{ __('calendar.event.attendees.rsvp') }}</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Description --}}
|
|
<div class="event-field">
|
|
<div class="event-field-icon">
|
|
<x-icon-description />
|
|
</div>
|
|
<div class="input-row input-row--1">
|
|
<div class="input-cell">
|
|
<x-input.textarea
|
|
id="description"
|
|
name="description"
|
|
placeholder="Description..."
|
|
rows="3">{{ old('description', $event->meta?->description ?? '') }}</x-input.textarea>
|
|
<x-input.error :messages="$errors->get('description')" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Submit --}}
|
|
@if(!$isModal)
|
|
<div class="input-row input-row--actions input-row--start sticky-bottom">
|
|
<x-button type="anchor" variant="tertiary" href="{{ route('calendar.show', $calendar) }}">
|
|
{{ __('common.cancel') }}
|
|
</x-button>
|
|
<x-button variant="primary" type="submit">
|
|
{{ $event->exists ? __('Save') : __('Create') }}
|
|
</x-button>
|
|
</div>
|
|
@endif
|
|
|
|
</form>
|