Skip to content

Commit

Permalink
Add Event Places to the Event creation page (#108)
Browse files Browse the repository at this point in the history
* wip

* area and place (select-view)

* Event Creation additions

- Added searchable dropdown/select
- Added role check for StoreEventRequest
- Added EventPlace relationship to Event

* Lint fixes

---------

Co-authored-by: Yusef Aslan <gurutosh@web.de>
  • Loading branch information
stevenobird and Yusef Aslan authored Oct 18, 2024
1 parent 8842ccb commit 0b72467
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 11 deletions.
8 changes: 7 additions & 1 deletion app/Http/Controllers/EventController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Http\Requests\StoreEventRequest;
use App\Http\Requests\UpdateEventRequest;
use App\Models\Area;
use App\Models\Event;
use Illuminate\Support\Facades\Auth;

Expand All @@ -24,7 +25,11 @@ public function index()
*/
public function create()
{
return view('events.create');
return view('events.create', [
'areas' => Area::with(['eventPlaces' => function ($query) {
$query->orderBy('name');
}])->orderBy('name')->get(),
]);
}

/**
Expand All @@ -39,6 +44,7 @@ public function store(StoreEventRequest $request)
'organizer_id' => Auth::user()->organizer->id,
'starts_at' => $request->starts_at,
'ends_at' => $request->ends_at,
'event_place_id' => $request->event_place,
]);

return redirect()->route('events.show', ['event' => $event]);
Expand Down
4 changes: 2 additions & 2 deletions app/Http/Requests/StoreEventRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class StoreEventRequest extends FormRequest
*/
public function authorize(): bool
{
return true;
return $this->user()->hasRole('Veranstalter');
}

/**
Expand All @@ -26,7 +26,7 @@ public function rules(): array
'description' => 'required|string|min:3|max:4096',
'event_type' => 'integer|in:event_types',
'event_category' => 'integer|in:event_categories',
'event_place' => 'integer|in:event_places',
'event_place' => 'required',
'starts_at' => 'required|date',
'ends_at' => 'date|after:starts_at',
];
Expand Down
28 changes: 28 additions & 0 deletions app/Livewire/SearchableDropdown.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace App\Livewire;

use Livewire\Component;

class SearchableDropdown extends Component
{
public $options;

public $property;

public $childProperty;

public $search;

public $value;

public function mount($value = null)
{
$this->value = $value;
}

public function render()
{
return view('components.searchable-dropdown');
}
}
5 changes: 5 additions & 0 deletions app/Models/Area.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,9 @@ class Area extends Model
protected $fillable = ['id', 'name'];

public $timestamps = false;

public function eventPlaces()
{
return $this->hasMany(EventPlace::class);
}
}
6 changes: 6 additions & 0 deletions app/Models/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,11 @@ protected function casts(): array
'organizer_id',
'starts_at',
'ends_at',
'event_place_id',
];

public function eventPlace(): \Illuminate\Database\Eloquent\Relations\HasOne
{
return $this->hasOne(EventPlace::class, 'id', 'event_place_id');
}
}
5 changes: 5 additions & 0 deletions app/Models/EventPlace.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,9 @@ class EventPlace extends Model
protected $fillable = ['area_id', 'name', 'zip_code'];

public $timestamps = false;

public function area()
{
return $this->belongsTo(Area::class);
}
}
1 change: 1 addition & 0 deletions lang/de/messages.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
'form' => [
'description' => 'Beschreibung',
'ends_at' => 'Ende',
'place' => 'Ort',
'starts_at' => 'Beginn',
'submit' => 'Veranstaltung erstellen',
'title' => 'Name',
Expand Down
4 changes: 2 additions & 2 deletions resources/views/components/event/card.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
{{ $event->ends_at->format('D, d.m.Y H:i') }}
</p>
<p class="text-gray-300">
<i class="bi bi-geo-alt"></i> München
<i class="bi bi-geo-alt"></i> {{ $event->eventPlace->name }}
</p>
<div class="flex justify-between">
<div class="align-middle">
<i class="bi bi-people-fill text-green-400 placeholder-amber-500"></i>
<span>{{ random_int(1, 10) }} Teilnehmer</span>
<span>{{ random_int(1, 10) }} Teilnehmer</span>
</div>
<div>
<i class="bi bi-ticket-perforated-fill align-middle"></i> Kostenlos
Expand Down
79 changes: 79 additions & 0 deletions resources/views/components/searchable-dropdown.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
@props(['options', 'property', 'child-property', 'value' => ''])

<div x-data="{
open: false,
search: '',
selectedId: null,
options: @js($options),
childProperty: @js($childProperty),
get filteredOptions() {
if (!this.search.trim()) return this.options;
return filteredParents = this.options.filter((option) => {
return option[this.childProperty].some((item) => {
return item.name.toLowerCase().includes(this.search.toLowerCase());
});
});
},
filteredChildren(headId) {
let items = this.filteredOptions.find(option => option.id === headId)[this.childProperty];
return items.filter(item => item.name.toLowerCase().includes(this.search.toLowerCase()))
},
get selectedOption() {
if (!this.selectedId) return null;
return this.options.find(option => option[this.childProperty].id === this.selectedId);
},
selectOption(option) {
this.selectedId = option.id;
this.search = option.name;
this.open = false;
},
clear() {
this.selectedId = null;
this.search = '';
}
}"
>
<div class="relative mt-2">
<div class="relative cursor-pointer text-normal ">
<x-input
class="block mt-1 w-full truncate"
@click="open = !open" type="text"
@input.type.defer="open = true"
x-model="search" wire:model="search"
x-value="selectedOption ? selectedOption.name : 'Select an option"
autocomplete="off"
/>
<x-input type="hidden" :name="$property" x-model="selectedId" :value="$value" wire:model="value" />

<span class="cursor-default absolute inset-y-0 right-0 flex items-center pr-2">
<svg x-show="!selectedId"
class="h-5 w-5 text-gray-800 pointer-events-none" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z" clip-rule="evenodd" />
</svg>
<template x-if="selectedId">
<i class="bi bi-x-circle-fill text-gray-400 pr-1.5 hover:cursor-pointer h-5 w-5"
title="Clear"
@click="clear"></i>
</template>
</span>
</div>

<div x-show="open" @click.away="open = false" x-cloak class="absolute z-50 mt-1 max-h-60 w-full py-1 text-base ">
<ul class="max-h-60 overflow-auto pl-2 bg-white dark:bg-gray-600 dark:text-gray-100 rounded-md shadow-lg">
<template x-for="option in filteredOptions" :key="option.id">
<li class="cursor-default select-none py-2 pl-0.5 text-gray-300">
<span x-text="option.name" class="font-normal block truncate"></span>
<ul class="pl-3">
<template x-for="nestedOption in filteredChildren(option.id)" :key="nestedOption.id">
<li class="cursor-pointer select-none py-2 pl-0.5 hover:bg-gray-100 hover:text-gray-800" :class="{'bg-sky-600 text-white': selectedOption && selectedOption.id === nestedOption.id}" @click="selectOption(nestedOption)">
<span x-text="`${nestedOption.name} - ${nestedOption.zip_code}`" class="font-normal block truncate text-gray-700"></span>
</li>
</template>
</ul>
</li>
</template>
</ul>
</div>
</div>
</div>
2 changes: 1 addition & 1 deletion resources/views/components/textarea.blade.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

@props(['disabled' => false])

<textarea {{ $disabled ? 'disabled' : '' }} {!! $attributes->merge(['class' => 'border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm']) !!}></textarea>
<textarea {{ $disabled ? 'disabled' : '' }} {!! $attributes->merge(['class' => 'border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm']) !!}>{{ $slot }}</textarea>
13 changes: 8 additions & 5 deletions resources/views/events/create.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
<div class="col-10 col-lg-4 mx-auto">
<h1>{{ __('messages.events.heading.create') }}</h1>

<x-validation-errors class="mb-4" />

<form method="POST" action="{{ route('events.store') }}">
@csrf

Expand All @@ -17,7 +15,7 @@

<div class="mt-4">
<x-label for="description" value="{{ __('messages.events.form.description') }}" />
<x-textarea id="description" class="block mt-1 w-full" type="text" name="description" :value="old('description')" min="3" max="4096" required />
<x-textarea id="description" class="block mt-1 w-full" type="text" name="description" min="3" max="4096" required>{{ old('description') }}</x-textarea>
<div>@error('description') <span class="text-red-600 dark:text-red-400">{{ $message }}</span> @enderror</div>
</div>

Expand All @@ -26,13 +24,18 @@
<x-input id="starts_at" class="block mt-1 w-full" type="datetime-local" name="starts_at" :value="old('starts_at')" min="{{ \Carbon\Carbon::now()->format('Y-m-d\TH:i') }}" value="{{ \Carbon\Carbon::now()->format('Y-m-d\TH:i') }}" required />
<div>@error('starts_at') <span class="text-red-600 dark:text-red-400">{{ $message }}</span> @enderror</div>
</div>

<div class="mt-4">
<x-label for="ends_at" value="{{ __('messages.events.form.ends_at') }}" />
<x-input id="ends_at" class="block mt-1 w-full" type="datetime-local" name="ends_at" :value="old('ends_at')" min="{{ \Carbon\Carbon::now()->format('Y-m-d\TH:i') }}" value="{{ \Carbon\Carbon::now()->addDay(1)->format('Y-m-d\TH:i') }}" required />
<div>@error('ends_at') <span class="text-red-600 dark:text-red-400">{{ $message }}</span> @enderror</div>
</div>

<div class="mt-4">
<x-label for="event_place">{{ __('messages.events.form.place') }}</x-label>
<livewire:searchable-dropdown :options="$areas" property="event_place" child-property="event_places" value="{{ old('event_place') }}" />
<div>@error('event_place') <span class="text-red-600 dark:text-red-400">{{ $message }}</span> @enderror</div>
</div>

<div class="mt-4">
<x-button class="col-12 mx-auto justify-center border-0 bg-primary normal-case tracking-normal">
{{ __('messages.events.form.submit') }}
Expand All @@ -42,4 +45,4 @@
<a href="{{ route('events.index') }}">CANCEL</a>
</div>
</section>
</x-guest-layout>
</x-guest-layout>

0 comments on commit 0b72467

Please sign in to comment.