mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-app.git
synced 2025-12-14 12:06:46 +00:00
✨ Refactor event scheduling to separate date and time inputs
- Split `from` and `to` fields in courses into `fromDate`, `fromTime`, `toDate`, and `toTime` for improved flexibility. - Refactored meetups to use `startDate` and `startTime` instead of `start`. - Enhanced validation to handle new date and time fields. - Applied user-specific timezone conversions for event dates and times. - Updated views to utilize new date and time pickers for consistent scheduling.
This commit is contained in:
@@ -19,11 +19,10 @@ class extends Component {
|
||||
|
||||
public $country = 'de';
|
||||
|
||||
#[Validate('required|date')]
|
||||
public string $from = '';
|
||||
|
||||
#[Validate('required|date|after:from')]
|
||||
public string $to = '';
|
||||
public string $fromDate = '';
|
||||
public string $fromTime = '';
|
||||
public string $toDate = '';
|
||||
public string $toTime = '';
|
||||
|
||||
#[Validate('required|exists:venues,id')]
|
||||
public ?int $venue_id = null;
|
||||
@@ -39,31 +38,69 @@ class extends Component {
|
||||
public function mount(): void
|
||||
{
|
||||
$this->country = request()->route('country');
|
||||
$timezone = auth()->user()->timezone ?? 'Europe/Berlin';
|
||||
|
||||
if ($this->event) {
|
||||
$this->from = $this->event->from->format('Y-m-d\TH:i');
|
||||
$this->to = $this->event->to->format('Y-m-d\TH:i');
|
||||
$localFrom = $this->event->from->setTimezone($timezone);
|
||||
$localTo = $this->event->to->setTimezone($timezone);
|
||||
|
||||
$this->fromDate = $localFrom->format('Y-m-d');
|
||||
$this->fromTime = $localFrom->format('H:i');
|
||||
$this->toDate = $localTo->format('Y-m-d');
|
||||
$this->toTime = $localTo->format('H:i');
|
||||
$this->venue_id = $this->event->venue_id;
|
||||
$this->link = $this->event->link;
|
||||
} else {
|
||||
// Set default start time to next Monday at 09:00
|
||||
$nextMonday = now()->next('Monday')->setTime(9, 0);
|
||||
$this->from = $nextMonday->format('Y-m-d\TH:i');
|
||||
$this->to = $nextMonday->copy()->addHours(3)->format('Y-m-d\TH:i');
|
||||
// Set default start time to next Monday at 09:00 in user's timezone
|
||||
$nextMonday = now($timezone)->next('Monday')->setTime(9, 0);
|
||||
$this->fromDate = $nextMonday->format('Y-m-d');
|
||||
$this->fromTime = $nextMonday->format('H:i');
|
||||
$this->toDate = $nextMonday->format('Y-m-d');
|
||||
$this->toTime = $nextMonday->copy()->addHours(3)->format('H:i');
|
||||
}
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$validated = $this->validate();
|
||||
$this->validate([
|
||||
'fromDate' => 'required|date',
|
||||
'fromTime' => 'required',
|
||||
'toDate' => 'required|date|after_or_equal:fromDate',
|
||||
'toTime' => 'required',
|
||||
'venue_id' => 'required|exists:venues,id',
|
||||
'link' => 'required|url|max:255',
|
||||
]);
|
||||
|
||||
$timezone = auth()->user()->timezone ?? 'Europe/Berlin';
|
||||
|
||||
// Combine date and time in user's timezone, then convert to UTC
|
||||
$localFrom = \Carbon\Carbon::createFromFormat('Y-m-d H:i', $this->fromDate . ' ' . $this->fromTime, $timezone);
|
||||
$utcFrom = $localFrom->setTimezone('UTC');
|
||||
|
||||
$localTo = \Carbon\Carbon::createFromFormat('Y-m-d H:i', $this->toDate . ' ' . $this->toTime, $timezone);
|
||||
$utcTo = $localTo->setTimezone('UTC');
|
||||
|
||||
// Additional validation: to must be after from
|
||||
if ($utcTo->lte($utcFrom)) {
|
||||
$this->addError('toTime', __('Die Endzeit muss nach der Startzeit liegen.'));
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'from' => $utcFrom,
|
||||
'to' => $utcTo,
|
||||
'venue_id' => $this->venue_id,
|
||||
'link' => $this->link,
|
||||
];
|
||||
|
||||
if ($this->event) {
|
||||
// Update existing event
|
||||
$this->event->update($validated);
|
||||
$this->event->update($data);
|
||||
session()->flash('status', __('Event erfolgreich aktualisiert!'));
|
||||
} else {
|
||||
// Create new event
|
||||
$this->course->courseEvents()->create([
|
||||
...$validated,
|
||||
...$data,
|
||||
'created_by' => auth()->id(),
|
||||
]);
|
||||
session()->flash('status', __('Event erfolgreich erstellt!'));
|
||||
@@ -108,8 +145,18 @@ class extends Component {
|
||||
public function with(): array
|
||||
{
|
||||
return [
|
||||
'venues' => Venue::query()->orderBy('name')->get(),
|
||||
'cities' => City::query()->orderBy('name')->get(),
|
||||
'venues' => Venue::query()
|
||||
->with([
|
||||
'city',
|
||||
])
|
||||
->orderBy('name')
|
||||
->get(),
|
||||
'cities' => City::query()
|
||||
->with([
|
||||
'country',
|
||||
])
|
||||
->orderBy('name')
|
||||
->get(),
|
||||
];
|
||||
}
|
||||
}; ?>
|
||||
@@ -126,18 +173,34 @@ class extends Component {
|
||||
<flux:legend>{{ __('Event Details') }}</flux:legend>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Startdatum') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:date-picker wire:model="fromDate" required/>
|
||||
<flux:description>{{ __('An welchem Tag beginnt das Event?') }}</flux:description>
|
||||
<flux:error name="fromDate"/>
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Startzeit') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="from" type="datetime-local" required/>
|
||||
<flux:description>{{ __('Wann beginnt das Event?') }}</flux:description>
|
||||
<flux:error name="from"/>
|
||||
<flux:time-picker wire:model="fromTime" required/>
|
||||
<flux:description>{{ __('Um wie viel Uhr beginnt das Event?') }} ({{ auth()->user()->timezone ?? 'Europe/Berlin' }})</flux:description>
|
||||
<flux:error name="fromTime"/>
|
||||
</flux:field>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Enddatum') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:date-picker wire:model="toDate" required/>
|
||||
<flux:description>{{ __('An welchem Tag endet das Event?') }}</flux:description>
|
||||
<flux:error name="toDate"/>
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Endzeit') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="to" type="datetime-local" required/>
|
||||
<flux:description>{{ __('Wann endet das Event?') }}</flux:description>
|
||||
<flux:error name="to"/>
|
||||
<flux:time-picker wire:model="toTime" required/>
|
||||
<flux:description>{{ __('Um wie viel Uhr endet das Event?') }} ({{ auth()->user()->timezone ?? 'Europe/Berlin' }})</flux:description>
|
||||
<flux:error name="toTime"/>
|
||||
</flux:field>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ class extends Component {
|
||||
|
||||
public $country = 'de';
|
||||
|
||||
#[Validate('required|date')]
|
||||
public string $start = '';
|
||||
public string $startDate = '';
|
||||
public string $startTime = '';
|
||||
|
||||
#[Validate('required|string|max:255')]
|
||||
public ?string $location = null;
|
||||
@@ -32,29 +32,54 @@ class extends Component {
|
||||
public function mount(): void
|
||||
{
|
||||
$this->country = request()->route('country');
|
||||
$timezone = auth()->user()->timezone ?? 'Europe/Berlin';
|
||||
|
||||
if ($this->event) {
|
||||
$this->start = $this->event->start->format('Y-m-d\TH:i');
|
||||
$localStart = $this->event->start->setTimezone($timezone);
|
||||
$this->startDate = $localStart->format('Y-m-d');
|
||||
$this->startTime = $localStart->format('H:i');
|
||||
$this->location = $this->event->location;
|
||||
$this->description = $this->event->description;
|
||||
$this->link = $this->event->link;
|
||||
} else {
|
||||
// Set default start time to next Monday at 19:00
|
||||
$this->start = now()->next('Monday')->setTime(19, 0)->format('Y-m-d\TH:i');
|
||||
// Set default start time to next Monday at 19:00 in user's timezone
|
||||
$defaultStart = now($timezone)->next('Monday')->setTime(19, 0);
|
||||
$this->startDate = $defaultStart->format('Y-m-d');
|
||||
$this->startTime = $defaultStart->format('H:i');
|
||||
}
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$validated = $this->validate();
|
||||
$this->validate([
|
||||
'startDate' => 'required|date',
|
||||
'startTime' => 'required',
|
||||
'location' => 'required|string|max:255',
|
||||
'description' => 'required|string',
|
||||
'link' => 'required|url|max:255',
|
||||
]);
|
||||
|
||||
$timezone = auth()->user()->timezone ?? 'Europe/Berlin';
|
||||
|
||||
// Combine date and time in user's timezone, then convert to UTC
|
||||
$localDateTime = \Carbon\Carbon::createFromFormat('Y-m-d H:i', $this->startDate . ' ' . $this->startTime, $timezone);
|
||||
$utcDateTime = $localDateTime->setTimezone('UTC');
|
||||
|
||||
$data = [
|
||||
'start' => $utcDateTime,
|
||||
'location' => $this->location,
|
||||
'description' => $this->description,
|
||||
'link' => $this->link,
|
||||
];
|
||||
|
||||
if ($this->event) {
|
||||
// Update existing event
|
||||
$this->event->update($validated);
|
||||
$this->event->update($data);
|
||||
session()->flash('status', __('Event erfolgreich aktualisiert!'));
|
||||
} else {
|
||||
// Create new event
|
||||
$this->meetup->meetupEvents()->create([
|
||||
...$validated,
|
||||
...$data,
|
||||
'created_by' => auth()->id(),
|
||||
'attendees' => [],
|
||||
'might_attendees' => [],
|
||||
@@ -90,19 +115,26 @@ class extends Component {
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Startzeit') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:input wire:model="start" type="datetime-local" required/>
|
||||
<flux:description>{{ __('Wann findet das Event statt?') }}</flux:description>
|
||||
<flux:error name="start"/>
|
||||
<flux:label>{{ __('Datum') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:date-picker min="today" wire:model="startDate" required locale="{{ session('lang_country', 'de-DE') }}"/>
|
||||
<flux:description>{{ __('An welchem Tag findet das Event statt?') }}</flux:description>
|
||||
<flux:error name="startDate"/>
|
||||
</flux:field>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Uhrzeit') }} <span class="text-red-500">*</span></flux:label>
|
||||
<flux:time-picker wire:model="startTime" required locale="{{ session('lang_country', 'de-DE') }}"/>
|
||||
<flux:description>{{ __('Um wie viel Uhr startet das Event?') }} ({{ auth()->user()->timezone ?? 'Europe/Berlin' }})</flux:description>
|
||||
<flux:error name="startTime"/>
|
||||
</flux:field>
|
||||
</div>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Ort') }}</flux:label>
|
||||
<flux:input wire:model="location" placeholder="{{ __('z.B. Café Mustermann, Hauptstr. 1') }}"/>
|
||||
<flux:description>{{ __('Wo findet das Event statt?') }}</flux:description>
|
||||
<flux:error name="location"/>
|
||||
</flux:field>
|
||||
</div>
|
||||
|
||||
<flux:field>
|
||||
<flux:label>{{ __('Beschreibung') }}</flux:label>
|
||||
|
||||
@@ -100,8 +100,15 @@ class extends Component {
|
||||
public function with(): array
|
||||
{
|
||||
return [
|
||||
'cities' => City::query()->orderBy('name')->get(),
|
||||
'countries' => Country::query()->orderBy('countries.name')->get(),
|
||||
'cities' => City::query()
|
||||
->with([
|
||||
'country',
|
||||
])
|
||||
->orderBy('name')
|
||||
->get(),
|
||||
'countries' => Country::query()
|
||||
->orderBy('countries.name')
|
||||
->get(),
|
||||
];
|
||||
}
|
||||
}; ?>
|
||||
@@ -120,20 +127,20 @@ class extends Component {
|
||||
<flux:file-upload wire:model="logo">
|
||||
<!-- Custom logo uploader -->
|
||||
<div class="
|
||||
relative flex items-center justify-center size-20 rounded-full transition-colors cursor-pointer
|
||||
relative flex items-center justify-center size-20 rounded transition-colors cursor-pointer
|
||||
border border-zinc-200 dark:border-white/10 hover:border-zinc-300 dark:hover:border-white/10
|
||||
bg-zinc-100 hover:bg-zinc-200 dark:bg-white/10 hover:dark:bg-white/15 in-data-dragging:dark:bg-white/15
|
||||
">
|
||||
@if($logo)
|
||||
<img src="{{ $logo?->temporaryUrl() }}" alt="Logo"
|
||||
class="size-full object-cover rounded-full"/>
|
||||
class="size-full object-cover rounded"/>
|
||||
@else
|
||||
<!-- Show the default icon if no file is uploaded -->
|
||||
<flux:icon name="user-group" variant="solid" class="text-zinc-500 dark:text-zinc-400"/>
|
||||
@endif
|
||||
|
||||
<!-- Corner upload icon -->
|
||||
<div class="absolute bottom-0 right-0 bg-white dark:bg-zinc-800 rounded-full">
|
||||
<div class="absolute bottom-0 right-0 bg-white dark:bg-zinc-800 rounded">
|
||||
<flux:icon name="arrow-up-circle" variant="solid" class="text-zinc-500 dark:text-zinc-400"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user