#116 - integration with slack (#129)

* wip

* wip

* wip

* wip

* fix

* wip

* wip

* fix

* fix

* cs fix

* #116 - fix

* #116 - changed home-office icon

* Apply suggestions from code review

Co-authored-by: Krzysztof Rewak <krzysztof.rewak@gmail.com>

* #116 - cr fix

* #116 - cs fix

* #116 - cs fix

* Apply suggestions from code review

Co-authored-by: Ewelina Lasowy <56546832+EwelinaLasowy@users.noreply.github.com>

* #5 - bump codestyle

Co-authored-by: EwelinaLasowy <ewelina.lasowy@blumilk.pl>
Co-authored-by: Krzysztof Rewak <krzysztof.rewak@gmail.com>
Co-authored-by: Ewelina Lasowy <56546832+EwelinaLasowy@users.noreply.github.com>
This commit is contained in:
Adrian Hopek
2022-04-27 09:57:13 +02:00
committed by GitHub
parent d60dc75f99
commit c69866bb52
78 changed files with 1597 additions and 243 deletions

View File

@@ -53,7 +53,6 @@ class CreateAction
$vacationRequest->save();
$days = $this->vacationDaysCalculator->calculateDays(
$vacationRequest->yearPeriod,
$vacationRequest->from,
$vacationRequest->to,
);

View File

@@ -57,6 +57,6 @@ class CalendarGenerator
->approved()
->with("vacationRequest")
->get()
->groupBy(fn(Vacation $vacation) => $vacation->date->toDateString());
->groupBy(fn(Vacation $vacation): string => $vacation->date->toDateString());
}
}

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Toby\Domain;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Toby\Domain\Enums\VacationType;
use Toby\Eloquent\Models\User;
use Toby\Eloquent\Models\Vacation;
class DailySummaryRetriever
{
public function __construct(
protected VacationTypeConfigRetriever $configRetriever,
) {}
public function getAbsences(Carbon $date): Collection
{
return Vacation::query()
->with(["user", "vacationRequest"])
->whereDate("date", $date)
->approved()
->whereTypes(
VacationType::all()->filter(fn(VacationType $type): bool => $this->configRetriever->isVacation($type)),
)
->get();
}
public function getRemoteDays(Carbon $date): Collection
{
return Vacation::query()
->with(["user", "vacationRequest"])
->whereDate("date", $date)
->approved()
->whereTypes(
VacationType::all()->filter(fn(VacationType $type): bool => !$this->configRetriever->isVacation($type)),
)
->get();
}
public function getBirthdays(Carbon $date): Collection
{
return User::query()
->whereRelation("profile", "birthday", $date)
->get();
}
}

View File

@@ -21,7 +21,7 @@ enum EmploymentForm: string
$cases = collect(EmploymentForm::cases());
return $cases->map(
fn(EmploymentForm $enum) => [
fn(EmploymentForm $enum): array => [
"label" => $enum->label(),
"value" => $enum->value,
],

View File

@@ -21,7 +21,7 @@ enum Role: string
$cases = collect(Role::cases());
return $cases->map(
fn(Role $enum) => [
fn(Role $enum): array => [
"label" => $enum->label(),
"value" => $enum->value,
],

View File

@@ -30,7 +30,7 @@ enum VacationType: string
$cases = VacationType::all();
return $cases->map(
fn(VacationType $enum) => [
fn(VacationType $enum): array => [
"label" => $enum->label(),
"value" => $enum->value,
],

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\Notifications;
class Channels
{
public const MAIL = "mail";
public const SLACK = "slack";
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Toby\Eloquent\Models\User;
class KeyHasBeenGivenNotification extends Notification
{
use Queueable;
public function __construct(
protected User $sender,
protected User $recipient,
) {}
public function via(): array
{
return [Channels::SLACK];
}
public function toSlack(Notifiable $notifiable): string
{
return __(":sender gives key no :key to :recipient", [
"sender" => $this->getName($this->sender),
"recipient" => $this->getName($this->recipient),
"key" => $notifiable->id,
]);
}
protected function getName(User $user): string
{
if ($user->profile->slack_id !== null) {
return "<@{$user->profile->slack_id}>";
}
return $user->profile->full_name;
}
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Toby\Eloquent\Models\User;
class KeyHasBeenTakenNotification extends Notification
{
use Queueable;
public function __construct(
protected User $recipient,
protected User $sender,
) {}
public function via(): array
{
return [Channels::SLACK];
}
public function toSlack(Notifiable $notifiable): string
{
return __(":recipient takes key no :key from :sender", [
"recipient" => $this->getName($this->recipient),
"sender" => $this->getName($this->sender),
"key" => $notifiable->id,
]);
}
protected function getName(User $user): string
{
if ($user->profile->slack_id !== null) {
return "<@{$user->profile->slack_id}>";
}
return $user->profile->full_name;
}
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Toby\Domain\Notifications;
interface Notifiable
{
public function notify($instance);
}

View File

@@ -20,7 +20,17 @@ class VacationRequestCreatedNotification extends Notification
public function via(): array
{
return ["mail"];
return [Channels::MAIL, Channels::SLACK];
}
public function toSlack(): string
{
$url = route("vacation.requests.show", ["vacationRequest" => $this->vacationRequest->id]);
return implode("\n", [
$this->buildDescription(),
"<${url}|Zobacz szczegóły>",
]);
}
/**
@@ -80,18 +90,16 @@ class VacationRequestCreatedNotification extends Notification
protected function buildDescription(): string
{
$name = $this->vacationRequest->name;
$appName = config("app.name");
if ($this->vacationRequest->creator()->is($this->vacationRequest->user)) {
return __("The vacation request :title has been created correctly in the :appName.", [
return __("The vacation request :title from user :user has been created successfully.", [
"user" => $this->vacationRequest->user->profile->full_name,
"title" => $name,
"appName" => $appName,
]);
}
return __("The vacation request :title has been created correctly by user :creator on your behalf in the :appName.", [
return __("The vacation request :title has been created successfully by user :creator on your behalf.", [
"title" => $this->vacationRequest->name,
"appName" => $appName,
"creator" => $this->vacationRequest->creator->profile->full_name,
]);
}

View File

@@ -22,7 +22,17 @@ class VacationRequestStatusChangedNotification extends Notification
public function via(): array
{
return ["mail"];
return [Channels::MAIL, Channels::SLACK];
}
public function toSlack(): string
{
$url = route("vacation.requests.show", ["vacationRequest" => $this->vacationRequest->id]);
return implode("\n", [
$this->buildDescription(),
"<${url}|Zobacz szczegóły>",
]);
}
/**
@@ -43,27 +53,17 @@ class VacationRequestStatusChangedNotification extends Notification
protected function buildMailMessage(string $url): MailMessage
{
$user = $this->user->profile->first_name;
$title = $this->vacationRequest->name;
$type = $this->vacationRequest->type->label();
$status = $this->vacationRequest->state->label();
$from = $this->vacationRequest->from->toDisplayString();
$to = $this->vacationRequest->to->toDisplayString();
$days = $this->vacationRequest->vacations()->count();
$requester = $this->vacationRequest->user->profile->full_name;
return (new MailMessage())
->greeting(__("Hi :user!", [
"user" => $user,
]))
->subject(__("Vacation request :title has been :status", [
"title" => $title,
"status" => $status,
]))
->line(__("The vacation request :title from user :requester has been :status.", [
"title" => $title,
"requester" => $requester,
"status" => $status,
]))
->subject($this->buildSubject())
->line($this->buildDescription())
->line(__("Vacation type: :type", [
"type" => $type,
]))
@@ -74,4 +74,21 @@ class VacationRequestStatusChangedNotification extends Notification
]))
->action(__("Click here for details"), $url);
}
protected function buildSubject(): string
{
return __("Vacation request :title has been :status", [
"title" => $this->vacationRequest->name,
"status" => $this->vacationRequest->state->label(),
]);
}
protected function buildDescription(): string
{
return __("The vacation request :title from user :requester has been :status.", [
"title" => $this->vacationRequest->name,
"requester" => $this->vacationRequest->user->profile->full_name,
"status" => $this->vacationRequest->state->label(),
]);
}
}

View File

@@ -23,7 +23,17 @@ class VacationRequestWaitsForApprovalNotification extends Notification
public function via(): array
{
return ["mail"];
return [Channels::MAIL, Channels::SLACK];
}
public function toSlack(): string
{
$url = route("vacation.requests.show", ["vacationRequest" => $this->vacationRequest->id]);
return implode("\n", [
$this->buildDescription(),
"<${url}|Zobacz szczegóły>",
]);
}
/**
@@ -99,4 +109,3 @@ class VacationRequestWaitsForApprovalNotification extends Notification
]);
}
}

View File

@@ -26,7 +26,7 @@ class PolishHolidaysRetriever
protected function prepareHolidays(array $holidays): Collection
{
return collect($holidays)->map(fn(Holiday $holiday) => [
return collect($holidays)->map(fn(Holiday $holiday): array => [
"name" => $holiday->getName([static::LANG_KEY]),
"date" => Carbon::createFromTimestamp($holiday->getTimestamp()),
])->values();

View File

@@ -18,7 +18,7 @@ class TimesheetExport implements WithMultipleSheets
public function sheets(): array
{
return $this->users
->map(fn(User $user) => new TimesheetPerUserSheet($user, $this->month, $this->types))
->map(fn(User $user): TimesheetPerUserSheet => new TimesheetPerUserSheet($user, $this->month, $this->types))
->toArray();
}

View File

@@ -193,8 +193,8 @@ class TimesheetPerUserSheet implements WithTitle, WithHeadings, WithEvents, With
->get()
->groupBy(
[
fn(Vacation $vacation) => $vacation->date->toDateString(),
fn(Vacation $vacation) => $vacation->vacationRequest->type->value,
fn(Vacation $vacation): string => $vacation->date->toDateString(),
fn(Vacation $vacation): string => $vacation->vacationRequest->type->value,
],
);
}

View File

@@ -42,8 +42,8 @@ class UserVacationStatsRetriever
->states(VacationRequestStatesRetriever::successStates()),
)
->get()
->groupBy(fn(Vacation $vacation) => strtolower($vacation->date->englishMonth))
->map(fn(Collection $items) => $items->count());
->groupBy(fn(Vacation $vacation): string => strtolower($vacation->date->englishMonth))
->map(fn(Collection $items): int => $items->count());
}
public function getPendingVacationDays(User $user, YearPeriod $yearPeriod): int
@@ -107,13 +107,13 @@ class UserVacationStatsRetriever
{
$types = VacationType::all();
return $types->filter(fn(VacationType $type) => $this->configRetriever->hasLimit($type));
return $types->filter(fn(VacationType $type): bool => $this->configRetriever->hasLimit($type));
}
protected function getNotLimitableVacationTypes(): Collection
{
$types = VacationType::all();
return $types->filter(fn(VacationType $type) => !$this->configRetriever->hasLimit($type));
return $types->filter(fn(VacationType $type): bool => !$this->configRetriever->hasLimit($type));
}
}

View File

@@ -11,9 +11,10 @@ use Toby\Eloquent\Models\YearPeriod;
class VacationDaysCalculator
{
public function calculateDays(YearPeriod $yearPeriod, CarbonInterface $from, CarbonInterface $to): Collection
public function calculateDays(CarbonInterface $from, CarbonInterface $to): Collection
{
$period = CarbonPeriod::create($from, $to);
$yearPeriod = YearPeriod::findByYear($from->year);
$holidays = $yearPeriod->holidays()->pluck("date");
$validDays = new Collection();

View File

@@ -29,7 +29,7 @@ class DoesNotExceedLimitRule implements VacationRequestRule
$limit = $this->getUserVacationLimit($vacationRequest->user, $vacationRequest->yearPeriod);
$vacationDays = $this->getVacationDaysWithLimit($vacationRequest->user, $vacationRequest->yearPeriod);
$estimatedDays = $this->vacationDaysCalculator->calculateDays($vacationRequest->yearPeriod, $vacationRequest->from, $vacationRequest->to)->count();
$estimatedDays = $this->vacationDaysCalculator->calculateDays($vacationRequest->from, $vacationRequest->to)->count();
return $limit >= ($vacationDays + $estimatedDays);
}
@@ -64,6 +64,6 @@ class DoesNotExceedLimitRule implements VacationRequestRule
{
$types = VacationType::all();
return $types->filter(fn(VacationType $type) => $this->configRetriever->hasLimit($type));
return $types->filter(fn(VacationType $type): bool => $this->configRetriever->hasLimit($type));
}
}

View File

@@ -16,7 +16,7 @@ class MinimumOneVacationDayRule implements VacationRequestRule
public function check(VacationRequest $vacationRequest): bool
{
return $this->vacationDaysCalculator
->calculateDays($vacationRequest->yearPeriod, $vacationRequest->from, $vacationRequest->to)
->calculateDays($vacationRequest->from, $vacationRequest->to)
->isNotEmpty();
}

View File

@@ -9,5 +9,6 @@ use Toby\Eloquent\Models\VacationRequest;
interface VacationRequestRule
{
public function check(VacationRequest $vacationRequest): bool;
public function errorMessage(): string;
}

View File

@@ -19,7 +19,7 @@ class VacationTypeCanBeSelected implements VacationRequestRule
$employmentForm = $vacationRequest->user->profile->employment_form;
$availableTypes = VacationType::all()
->filter(fn(VacationType $type) => $this->configRetriever->isAvailableFor($type, $employmentForm));
->filter(fn(VacationType $type): bool => $this->configRetriever->isAvailableFor($type, $employmentForm));
return $availableTypes->contains($vacationRequest->type);
}