Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#169 - ability to edit and delete opinions for administrator #201

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions app/Events/ChangeInFavouriteCityEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace App\Events;

use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class ChangeInFavouriteCityEvent
{
use Dispatchable;
use InteractsWithSockets;
use SerializesModels;

/** Create a new event instance. */
public int $city_id;

public string $provider_name;
public string $change;

public function __construct($city_id, $provider_name, $change)
{
$this->city_id = $city_id;
$this->provider_name = $provider_name;
$this->change = $change;
}

/**
* Get the channels the event should broadcast on.
*
* @return array<int, \Illuminate\Broadcasting\Channel>
*/
public function broadcastOn(): array
{
return [
new PrivateChannel("channel-name"),
];
}
}
13 changes: 10 additions & 3 deletions app/Http/Controllers/CityOpinionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,20 @@ public function store(CityOpinionRequest $request): void
public function update(CityOpinionRequest $request, CityOpinion $cityOpinion): void
{
$opinion = $request->only(["rating", "content", "city_id"]);
$opinion["user_id"] = Auth::id();

$cityOpinion->update($opinion);
if ($cityOpinion->user_id === Auth::id() || Auth::user()->hasRole("admin")) {
$cityOpinion->update($opinion);
kamilpiech97 marked this conversation as resolved.
Show resolved Hide resolved
} else {
abort(403);
}
}

public function destroy(CityOpinion $cityOpinion): void
{
$cityOpinion->delete();
if ($cityOpinion->user_id === Auth::id() || Auth::user()->hasRole("admin")) {
$cityOpinion->delete();
} else {
abort(403);
}
kamilpiech97 marked this conversation as resolved.
Show resolved Hide resolved
}
}
9 changes: 9 additions & 0 deletions app/Importers/DataImporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Importers;

use App\Events\ChangeInFavouriteCityEvent;
use App\Models\City;
use App\Models\CityAlternativeName;
use App\Models\CityProvider;
Expand Down Expand Up @@ -76,6 +77,9 @@ protected function countryNotFound(string $cityName, string $countryName): void

protected function createProvider(int $cityId, string $providerName): void
{
if (!CityProvider::query()->where("city_id", $cityId)->where("provider_name", $providerName)->exists()) {
event(new ChangeInFavouriteCityEvent($cityId, $providerName, "added to"));
}
CityProvider::query()->updateOrCreate([
"city_id" => $cityId,
"provider_name" => $providerName,
Expand All @@ -90,6 +94,11 @@ protected function deleteMissingProviders(string $providerName, array $existingC
->whereNotIn("city_id", $existingCityProviders)
->whereNot("created_by", "admin")
->get();

foreach ($cityProvidersToDelete as $cityProvider) {
event(new ChangeInFavouriteCityEvent($cityProvider->city_id, $providerName, "removed from"));
}

$cityProvidersToDelete->each(fn($cityProvider) => $cityProvider->delete());
}

Expand Down
33 changes: 33 additions & 0 deletions app/Listeners/ChangeInFavouriteCityListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace App\Listeners;

use App\Mail\ChangeInFavourites;
use App\Models\City;
use App\Models\User;
use Illuminate\Support\Facades\Mail;

class ChangeInFavouriteCityListener
{
/**
* Create the event listener.
*/
public function __construct() {}

/**
* Handle the event.
*/
public function handle(object $event): void
{
$users = User::query()->whereHas("favorites", function ($query) use ($event): void {
$query->where("city_id", $event->city_id);
})->get();
$city_name = City::query()->where("id", $event->city_id)->first()->name;

foreach ($users as $user) {
Mail::to($user->email)->send(new ChangeInFavourites($city_name, $event->provider_name, $event->change));
}
}
}
67 changes: 67 additions & 0 deletions app/Mail/ChangeInFavourites.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

declare(strict_types=1);

namespace App\Mail;

use AllowDynamicProperties;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;

#[AllowDynamicProperties] class ChangeInFavourites extends Mailable
{
use Queueable;
use SerializesModels;

/**
* Create a new message instance.
*/
public function __construct($city, $provider, $change)
{
$this->city = $city;
$this->provider = $provider;
$this->change = $change;
$this->url = env("APP_URL");
}

/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
from: new Address(env("MAIL_FROM_ADDRESS"), env("MAIL_FROM_NAME")),
subject: "Change In Favourites",
);
}

/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: "emails.changeInFavouritesMailTemplate",
with: [
"city" => $this->city,
"provider" => $this->provider,
"change" => $this->change,
"url" => $this->url,
],
);
}

/**
* Get the attachments for the message.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [];
}
}
3 changes: 3 additions & 0 deletions app/Providers/EventServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ class EventServiceProvider extends ServiceProvider
Registered::class => [
SendEmailVerificationNotification::class,
],
'App\Events\ChangeInFavouriteCityEvent' => [
'App\Listeners\ChangeInFavouriteCityListener',
],
];

/**
Expand Down
1 change: 0 additions & 1 deletion resources/js/Pages/City/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import InfoPopup from '@/Shared/Components/InfoPopup.vue'
import Opinion from '@/Shared/Components/Opinion.vue'

const toast = useToast()

const page = usePage()
const isAuth = computed(() => page.props.auth.isAuth)

Expand Down
3 changes: 2 additions & 1 deletion resources/js/Shared/Components/Opinion.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import DeleteModal from './DeleteModal.vue'
import { useToast } from 'vue-toastification'
import ErrorMessage from './ErrorMessage.vue'

const isAdmin = computed(() => page.props.auth.isAdmin)
const toast = useToast()
const page = usePage()
const user = computed(() => page.props.auth.user)
Expand Down Expand Up @@ -103,7 +104,7 @@ const emptyRatingError = ref('')
{{ opinion.content }}
</div>

<div v-if="user.id === props.opinion.user.id" class="mt-1 flex justify-end">
<div v-if="user.id === props.opinion.user.id || isAdmin" class="mt-1 flex justify-end">
<button class="flex px-1 hover:drop-shadow" @click="toggleUpdateOpinionDialog">
<PencilIcon class="h-5 w-5 text-blumilk-500 hover:drop-shadow sm:h-4 sm:w-4" />
</button>
Expand Down
58 changes: 58 additions & 0 deletions resources/views/emails/changeInFavouritesMailTemplate.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: 'Roboto', sans-serif;
color: #333333;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.header {
background: linear-gradient(to right, #6dd5ed, #2193b0);
color: white;
text-align: center;
padding: 10px 0;
}
.content {
background-color: white;
padding: 20px;
margin: 10px;
}
.highlight {
color: #2193b0;
font-weight: bold;
}
.footer {
text-align: center;
padding: 10px;
font-size: small;
color: grey;
}
.cta-button {
display: inline-block;
padding: 10px 20px;
margin: 10px 0;
background-color: #2193b0;
color: white;
text-decoration: none;
border-radius: 5px;
}
</style>
</head>
<body>
<div class="header">
<h1>Your Favorite Cities Update</h1>
</div>
<div class="content">
<p>Hi,</p>
<p>There was a change in one of your favourite cities.</p>
<p><span class="highlight">{{$provider}}</span> has been <span class="highlight">{{$change}}</span> <span class="highlight">{{$city}}</span>.</p>
<a href="{{$url}}" class="cta-button">Learn More</a>
</div>
<div class="footer">
We'll update you again when there's another change.
</div>
</body>
</html>
Loading