diff --git a/app/Http/Controllers/Api/MeetupController.php b/app/Http/Controllers/Api/MeetupController.php new file mode 100644 index 00000000..85d9a28a --- /dev/null +++ b/app/Http/Controllers/Api/MeetupController.php @@ -0,0 +1,96 @@ +find($request->input('user_id'))->meetups->pluck('id'); + + return Meetup::query() + ->select('id', 'name', 'city_id') + ->with([ + 'city', + ]) + ->whereIn('id', $myMeetupIds->toArray()) + ->orderBy('name') + ->when( + $request->search, + fn(Builder $query) => $query + ->where('name', 'like', "%{$request->search}%") + ->orWhereHas('city', + fn(Builder $query) => $query->where('cities.name', 'ilike', "%{$request->search}%")) + ) + ->when( + $request->exists('selected'), + fn(Builder $query) => $query->whereIn('id', $request->input('selected', [])), + fn(Builder $query) => $query->limit(10) + ) + ->get() + ->map(function (Meetup $meetup) { + $meetup->profile_image = $meetup->getFirstMediaUrl('logo', 'thumb'); + + return $meetup; + }); + } + + /** + * Store a newly created resource in storage. + * + * @param \Illuminate\Http\Request $request + * + * @return \Illuminate\Http\Response + */ + public function store(Request $request) + { + // + } + + /** + * Display the specified resource. + * + * @param \App\Models\meetup $meetup + * + * @return \Illuminate\Http\Response + */ + public function show(meetup $meetup) + { + // + } + + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param \App\Models\meetup $meetup + * + * @return \Illuminate\Http\Response + */ + public function update(Request $request, meetup $meetup) + { + // + } + + /** + * Remove the specified resource from storage. + * + * @param \App\Models\meetup $meetup + * + * @return \Illuminate\Http\Response + */ + public function destroy(meetup $meetup) + { + // + } +} diff --git a/app/Http/Livewire/Meetup/Form/MeetupEventForm.php b/app/Http/Livewire/Meetup/Form/MeetupEventForm.php new file mode 100644 index 00000000..b9ad2452 --- /dev/null +++ b/app/Http/Livewire/Meetup/Form/MeetupEventForm.php @@ -0,0 +1,128 @@ + 'required', + 'meetupEvent.start' => 'required', + 'meetupEvent.location' => 'string|nullable', + 'meetupEvent.description' => 'string|nullable', + 'meetupEvent.link' => 'string|url|nullable', + + 'series.*.start' => 'required', + + 'recurring' => 'bool', + 'repetitions' => 'numeric|min:1', + ]; + } + + public function mount() + { + if (!$this->meetupEvent) { + $this->meetupEvent = new MeetupEvent( + [ + 'start' => now() + ->startOfDay() + ->addHours(17), + ] + ); + } + } + + public function updatedMeetupEventStart($value) + { + if ($this->recurring) { + $this->updatedRecurring(true); + } + } + + public function updatedRecurring($value) + { + if ($value && $this->meetupEvent->start) { + $this->series = []; + for ($i = 0; $i < $this->repetitions; $i++) { + $this->series[] = [ + 'start' => $this->meetupEvent->start->addWeeks($i + 1) + ->toDateTimeString(), + ]; + } + } + } + + public function updatedRepetitions($value) + { + if ($this->recurring) { + $this->updatedRecurring(true); + } + } + + public function submit() + { + $this->validate(); + if (!$this->meetupEvent->id) { + $hasAppointmentsOnThisDate = MeetupEvent::query() + ->where('meetup_id', $this->meetupEvent->meetup_id) + ->where('start', '>', Carbon::parse($this->meetupEvent->start) + ->startOfDay()) + ->where('start', '<', Carbon::parse($this->meetupEvent->start) + ->endOfDay()) + ->exists(); + if ($hasAppointmentsOnThisDate) { + $this->notification() + ->warning(__('There is already an event on this date. Please choose another date or delete the existing events.')); + + return; + } + } + + $this->meetupEvent->save(); + + if (!$this->meetupEvent->id && $this->recurring) { + foreach ($this->series as $event) { + $hasAppointmentsOnThisDate = MeetupEvent::query() + ->where('meetup_id', $this->meetupEvent->meetup_id) + ->where('start', '>', Carbon::parse($event['start']) + ->startOfDay()) + ->where('start', '<', Carbon::parse($event['start']) + ->endOfDay()) + ->exists(); + + if ($hasAppointmentsOnThisDate) { + continue; + } + + $this->meetupEvent->replicate() + ->fill($event) + ->saveQuietly(); + } + } + + $this->notification() + ->success(__('Event saved successfully.')); + + return to_route('meetup.table.meetupEvent', ['country' => $this->country]); + } + + public function render() + { + return view('livewire.meetup.form.meetup-event-form'); + } +} diff --git a/app/Http/Livewire/Tables/MeetupEventTable.php b/app/Http/Livewire/Tables/MeetupEventTable.php index 9a8a706f..67da208e 100644 --- a/app/Http/Livewire/Tables/MeetupEventTable.php +++ b/app/Http/Livewire/Tables/MeetupEventTable.php @@ -32,7 +32,14 @@ class MeetupEventTable extends DataTableComponent ]; }) ->setColumnSelectStatus(false) - ->setPerPage(10); + ->setPerPage(10) + ->setConfigurableAreas([ + 'toolbar-left-end' => [ + 'columns.meetup_events.areas.toolbar-left-end', [ + 'country' => $this->country, + ], + ], + ]); } public function filters(): array @@ -53,7 +60,7 @@ class MeetupEventTable extends DataTableComponent public function columns(): array { - return [ + $columns = [ Column::make(__('Meetup'), 'meetup.name') ->format( fn($value, $row, Column $column) => view('columns.meetup_events.name') @@ -72,12 +79,22 @@ class MeetupEventTable extends DataTableComponent ) ->sortable() ->collapseOnMobile(), - Column::make(__('Link'), 'link') + Column::make(__('Link')) ->label( fn($row, Column $column) => view('columns.meetup_events.link') ->withRow($row) ), ]; + + $adminColumns = auth()->check() ? [ + Column::make(__('Actions')) + ->label( + fn($row, Column $column) => view('columns.meetup_events.manage') + ->withRow($row) + ), + ] : []; + + return array_merge($columns, $adminColumns); } public function builder(): Builder diff --git a/app/Observers/MeetupEventObserver.php b/app/Observers/MeetupEventObserver.php index 4d1f6d81..ad7b2067 100644 --- a/app/Observers/MeetupEventObserver.php +++ b/app/Observers/MeetupEventObserver.php @@ -4,6 +4,7 @@ namespace App\Observers; use App\Models\MeetupEvent; use App\Traits\TwitterTrait; +use Illuminate\Support\Facades\Log; class MeetupEventObserver { @@ -18,23 +19,27 @@ class MeetupEventObserver */ public function created(MeetupEvent $meetupEvent) { - if (config('feeds.services.twitterAccountId')) { - $this->setNewAccessToken(1); + try { + if (config('feeds.services.twitterAccountId')) { + $this->setNewAccessToken(1); - $meetupName = $meetupEvent->meetup->name; - if ($meetupEvent->meetup->twitter_username) { - $meetupName .= ' @'.$meetupEvent->meetup->twitter_username; + $meetupName = $meetupEvent->meetup->name; + if ($meetupEvent->meetup->twitter_username) { + $meetupName .= ' @'.$meetupEvent->meetup->twitter_username; + } + + $text = sprintf("%s hat einen neuen Termin eingestellt:\n\n%s\n\n%s\n\n%s\n\n#Bitcoin #Meetup #Einundzwanzig #gesundesgeld", + $meetupName, + $meetupEvent->start->asDateTime(), + $meetupEvent->location, + url()->route('meetup.event.landing', + ['country' => 'de', 'meetup' => $meetupEvent->id]), + ); + + $this->postTweet($text); } - - $text = sprintf("%s hat einen neuen Termin eingestellt:\n\n%s\n\n%s\n\n%s\n\n#Bitcoin #Meetup #Einundzwanzig #gesundesgeld", - $meetupName, - $meetupEvent->start->asDateTime(), - $meetupEvent->location, - url()->route('meetup.event.landing', - ['country' => 'de', 'meetup' => $meetupEvent->id]), - ); - - $this->postTweet($text); + } catch (\Exception $e) { + Log::error($e->getMessage()); } } diff --git a/app/Support/Carbon.php b/app/Support/Carbon.php index 76404fa8..6dba325a 100644 --- a/app/Support/Carbon.php +++ b/app/Support/Carbon.php @@ -18,6 +18,17 @@ class Carbon extends CarbonImmutable ->format('H:i'); } + public function asDayNameAndMonthName(): string + { + $dt = $this->timezone(config('app.user-timezone'))->locale('de'); + return sprintf("%s, %s. week of %s [%s]", + $dt->dayName, + $dt->weekNumberInMonth, + $dt->monthName, + $dt->timezoneAbbreviatedName + ); + } + public function asDateTime(): string { $dt = $this->timezone(config('app.user-timezone'))->locale('de'); diff --git a/resources/lang/de.json b/resources/lang/de.json index dd98f807..2a8e028f 100644 --- a/resources/lang/de.json +++ b/resources/lang/de.json @@ -711,5 +711,12 @@ "Unfortunately I cannot come": "Leider kann ich doch nicht kommen", "Link to participate": "Link zur Teilnahme", "Copied!": "Kopiert!", - "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "Zum Beispiel ein Link zu einer Location auf Google Maps oder ein Link zu einer Website. (nicht dein Telegram-Gruppen-Link)" + "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "Zum Beispiel ein Link zu einer Location auf Google Maps oder ein Link zu einer Website. (nicht dein Telegram-Gruppen-Link)", + "There is already an event on this date. Please choose another date or delete the existing events.": "Es gibt bereits einen Termin an diesem Datum. Bitte wähle ein anderes Datum oder lösche die bestehenden Termine.", + "Event saved successfully.": "Termin erfolgreich gespeichert.", + "no authorization": "keine Berechtigung", + "Recurring appointment": "Wiederkehrender Termin", + "The recurring appointments are created in the database as new entries. Please be careful with this function, otherwise you will have to change or delete all the appointments you have created manually if you make an error.": "Die Termine werden in der Datenbank als neue Einträge angelegt. Bitte sei vorsichtig mit dieser Funktion, sonst musst du alle Termine, ändern oder löschen, wenn du einen Fehler machst.", + "Number of repetitions": "Anzahl der Wiederholungen", + "Recurring appointments": "Wiederkehrende Termine" } diff --git a/resources/lang/en.json b/resources/lang/en.json index 6106c627..46d12c9f 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -706,5 +706,12 @@ "Unfortunately I cannot come": "", "Link to participate": "", "Copied!": "", - "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "" + "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "", + "There is already an event on this date. Please choose another date or delete the existing events.": "", + "Event saved successfully.": "", + "no authorization": "", + "Recurring appointment": "", + "The recurring appointments are created in the database as new entries. Please be careful with this function, otherwise you will have to change or delete all the appointments you have created manually if you make an error.": "", + "Number of repetitions": "", + "Recurring appointments": "" } \ No newline at end of file diff --git a/resources/lang/es.json b/resources/lang/es.json index 3d6c0a10..fe1b3be2 100644 --- a/resources/lang/es.json +++ b/resources/lang/es.json @@ -706,5 +706,12 @@ "Unfortunately I cannot come": "", "Link to participate": "", "Copied!": "", - "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "" + "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "", + "There is already an event on this date. Please choose another date or delete the existing events.": "", + "Event saved successfully.": "", + "no authorization": "", + "Recurring appointment": "", + "The recurring appointments are created in the database as new entries. Please be careful with this function, otherwise you will have to change or delete all the appointments you have created manually if you make an error.": "", + "Number of repetitions": "", + "Recurring appointments": "" } \ No newline at end of file diff --git a/resources/lang/fr.json b/resources/lang/fr.json index d77642f2..6449277e 100644 --- a/resources/lang/fr.json +++ b/resources/lang/fr.json @@ -707,5 +707,12 @@ "Unfortunately I cannot come": "", "Link to participate": "", "Copied!": "", - "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "" + "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "", + "There is already an event on this date. Please choose another date or delete the existing events.": "", + "Event saved successfully.": "", + "no authorization": "", + "Recurring appointment": "", + "The recurring appointments are created in the database as new entries. Please be careful with this function, otherwise you will have to change or delete all the appointments you have created manually if you make an error.": "", + "Number of repetitions": "", + "Recurring appointments": "" } \ No newline at end of file diff --git a/resources/lang/hr.json b/resources/lang/hr.json index 8e985016..f8f86801 100644 --- a/resources/lang/hr.json +++ b/resources/lang/hr.json @@ -707,5 +707,12 @@ "Unfortunately I cannot come": "", "Link to participate": "", "Copied!": "", - "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "" + "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "", + "There is already an event on this date. Please choose another date or delete the existing events.": "", + "Event saved successfully.": "", + "no authorization": "", + "Recurring appointment": "", + "The recurring appointments are created in the database as new entries. Please be careful with this function, otherwise you will have to change or delete all the appointments you have created manually if you make an error.": "", + "Number of repetitions": "", + "Recurring appointments": "" } \ No newline at end of file diff --git a/resources/lang/it.json b/resources/lang/it.json index 6a901846..d2065d05 100644 --- a/resources/lang/it.json +++ b/resources/lang/it.json @@ -707,5 +707,12 @@ "Unfortunately I cannot come": "", "Link to participate": "", "Copied!": "", - "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "" + "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "", + "There is already an event on this date. Please choose another date or delete the existing events.": "", + "Event saved successfully.": "", + "no authorization": "", + "Recurring appointment": "", + "The recurring appointments are created in the database as new entries. Please be careful with this function, otherwise you will have to change or delete all the appointments you have created manually if you make an error.": "", + "Number of repetitions": "", + "Recurring appointments": "" } \ No newline at end of file diff --git a/resources/lang/mk.json b/resources/lang/mk.json index 8185dad7..502b247f 100644 --- a/resources/lang/mk.json +++ b/resources/lang/mk.json @@ -707,5 +707,12 @@ "Unfortunately I cannot come": "", "Link to participate": "", "Copied!": "", - "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "" + "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "", + "There is already an event on this date. Please choose another date or delete the existing events.": "", + "Event saved successfully.": "", + "no authorization": "", + "Recurring appointment": "", + "The recurring appointments are created in the database as new entries. Please be careful with this function, otherwise you will have to change or delete all the appointments you have created manually if you make an error.": "", + "Number of repetitions": "", + "Recurring appointments": "" } \ No newline at end of file diff --git a/resources/lang/pl.json b/resources/lang/pl.json index e8053110..33e6ab5b 100644 --- a/resources/lang/pl.json +++ b/resources/lang/pl.json @@ -707,5 +707,12 @@ "Unfortunately I cannot come": "", "Link to participate": "", "Copied!": "", - "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "" + "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "", + "There is already an event on this date. Please choose another date or delete the existing events.": "", + "Event saved successfully.": "", + "no authorization": "", + "Recurring appointment": "", + "The recurring appointments are created in the database as new entries. Please be careful with this function, otherwise you will have to change or delete all the appointments you have created manually if you make an error.": "", + "Number of repetitions": "", + "Recurring appointments": "" } \ No newline at end of file diff --git a/resources/lang/pt.json b/resources/lang/pt.json index 7e23cbe3..5c4b07c9 100644 --- a/resources/lang/pt.json +++ b/resources/lang/pt.json @@ -707,5 +707,12 @@ "Unfortunately I cannot come": "", "Link to participate": "", "Copied!": "", - "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "" + "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "", + "There is already an event on this date. Please choose another date or delete the existing events.": "", + "Event saved successfully.": "", + "no authorization": "", + "Recurring appointment": "", + "The recurring appointments are created in the database as new entries. Please be careful with this function, otherwise you will have to change or delete all the appointments you have created manually if you make an error.": "", + "Number of repetitions": "", + "Recurring appointments": "" } \ No newline at end of file diff --git a/resources/lang/tr.json b/resources/lang/tr.json index 77fc0697..97df687d 100644 --- a/resources/lang/tr.json +++ b/resources/lang/tr.json @@ -681,5 +681,12 @@ "Unfortunately I cannot come": "", "Link to participate": "", "Copied!": "", - "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "" + "For example, a link to a location on Google Maps or a link to a website. (not your Telegram group link)": "", + "There is already an event on this date. Please choose another date or delete the existing events.": "", + "Event saved successfully.": "", + "no authorization": "", + "Recurring appointment": "", + "The recurring appointments are created in the database as new entries. Please be careful with this function, otherwise you will have to change or delete all the appointments you have created manually if you make an error.": "", + "Number of repetitions": "", + "Recurring appointments": "" } \ No newline at end of file diff --git a/resources/views/columns/meetup_events/areas/toolbar-left-end.blade.php b/resources/views/columns/meetup_events/areas/toolbar-left-end.blade.php new file mode 100644 index 00000000..3fbaa302 --- /dev/null +++ b/resources/views/columns/meetup_events/areas/toolbar-left-end.blade.php @@ -0,0 +1,6 @@ +