diff --git a/app/Architecture/ExceptionHandler.php b/app/Architecture/ExceptionHandler.php index b286bd1..a43fcab 100644 --- a/app/Architecture/ExceptionHandler.php +++ b/app/Architecture/ExceptionHandler.php @@ -16,7 +16,6 @@ class ExceptionHandler extends Handler "password", "password_confirmation", ]; - protected array $handleByInertia = [ Response::HTTP_INTERNAL_SERVER_ERROR, Response::HTTP_SERVICE_UNAVAILABLE, diff --git a/app/Architecture/Providers/AppServiceProvider.php b/app/Architecture/Providers/AppServiceProvider.php index ce611b9..fbfe920 100644 --- a/app/Architecture/Providers/AppServiceProvider.php +++ b/app/Architecture/Providers/AppServiceProvider.php @@ -4,13 +4,24 @@ declare(strict_types=1); namespace Toby\Architecture\Providers; +use Illuminate\Contracts\Foundation\Application; +use Illuminate\Notifications\ChannelManager; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\Notification; use Illuminate\Support\ServiceProvider; +use Toby\Infrastructure\Slack\Channels\SlackApiChannel; class AppServiceProvider extends ServiceProvider { + public function register(): void + { + Notification::resolved(function (ChannelManager $service): void { + $service->extend("slack", fn(Application $app): SlackApiChannel => $app->make(SlackApiChannel::class)); + }); + } + public function boot(): void { - Carbon::macro("toDisplayString", fn() => $this->translatedFormat("d.m.Y")); + Carbon::macro("toDisplayString", fn(): string => $this->translatedFormat("d.m.Y")); } } diff --git a/app/Architecture/Providers/AuthServiceProvider.php b/app/Architecture/Providers/AuthServiceProvider.php index cb5b112..62d21c7 100644 --- a/app/Architecture/Providers/AuthServiceProvider.php +++ b/app/Architecture/Providers/AuthServiceProvider.php @@ -30,10 +30,10 @@ class AuthServiceProvider extends ServiceProvider } }); - Gate::define("manageUsers", fn(User $user) => $user->role === Role::AdministrativeApprover); - Gate::define("manageHolidays", fn(User $user) => $user->role === Role::AdministrativeApprover); - Gate::define("manageVacationLimits", fn(User $user) => $user->role === Role::AdministrativeApprover); - Gate::define("generateTimesheet", fn(User $user) => $user->role === Role::AdministrativeApprover); - Gate::define("listMonthlyUsage", fn(User $user) => $user->role === Role::AdministrativeApprover); + Gate::define("manageUsers", fn(User $user): bool => $user->role === Role::AdministrativeApprover); + Gate::define("manageHolidays", fn(User $user): bool => $user->role === Role::AdministrativeApprover); + Gate::define("manageVacationLimits", fn(User $user): bool => $user->role === Role::AdministrativeApprover); + Gate::define("generateTimesheet", fn(User $user): bool => $user->role === Role::AdministrativeApprover); + Gate::define("listMonthlyUsage", fn(User $user): bool => $user->role === Role::AdministrativeApprover); } } diff --git a/app/Architecture/Providers/RouteServiceProvider.php b/app/Architecture/Providers/RouteServiceProvider.php index 2412290..ffd9a41 100644 --- a/app/Architecture/Providers/RouteServiceProvider.php +++ b/app/Architecture/Providers/RouteServiceProvider.php @@ -28,6 +28,6 @@ class RouteServiceProvider extends ServiceProvider protected function configureRateLimiting(): void { - RateLimiter::for("api", fn(Request $request) => Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip())); + RateLimiter::for("api", fn(Request $request): Limit => Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip())); } } diff --git a/app/Domain/Actions/VacationRequest/CreateAction.php b/app/Domain/Actions/VacationRequest/CreateAction.php index 8a823e3..1bb8a7a 100644 --- a/app/Domain/Actions/VacationRequest/CreateAction.php +++ b/app/Domain/Actions/VacationRequest/CreateAction.php @@ -53,7 +53,6 @@ class CreateAction $vacationRequest->save(); $days = $this->vacationDaysCalculator->calculateDays( - $vacationRequest->yearPeriod, $vacationRequest->from, $vacationRequest->to, ); diff --git a/app/Domain/CalendarGenerator.php b/app/Domain/CalendarGenerator.php index 30d42b7..b1c6c0f 100644 --- a/app/Domain/CalendarGenerator.php +++ b/app/Domain/CalendarGenerator.php @@ -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()); } } diff --git a/app/Domain/DailySummaryRetriever.php b/app/Domain/DailySummaryRetriever.php new file mode 100644 index 0000000..a2482ef --- /dev/null +++ b/app/Domain/DailySummaryRetriever.php @@ -0,0 +1,49 @@ +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(); + } +} diff --git a/app/Domain/Enums/EmploymentForm.php b/app/Domain/Enums/EmploymentForm.php index daf671d..c3d1767 100644 --- a/app/Domain/Enums/EmploymentForm.php +++ b/app/Domain/Enums/EmploymentForm.php @@ -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, ], diff --git a/app/Domain/Enums/Role.php b/app/Domain/Enums/Role.php index f4be8b9..bd2485c 100644 --- a/app/Domain/Enums/Role.php +++ b/app/Domain/Enums/Role.php @@ -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, ], diff --git a/app/Domain/Enums/VacationType.php b/app/Domain/Enums/VacationType.php index a93ca3d..7940107 100644 --- a/app/Domain/Enums/VacationType.php +++ b/app/Domain/Enums/VacationType.php @@ -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, ], diff --git a/app/Domain/Notifications/Channels.php b/app/Domain/Notifications/Channels.php new file mode 100644 index 0000000..b12d14b --- /dev/null +++ b/app/Domain/Notifications/Channels.php @@ -0,0 +1,11 @@ + $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; + } +} diff --git a/app/Domain/Notifications/KeyHasBeenTakenNotification.php b/app/Domain/Notifications/KeyHasBeenTakenNotification.php new file mode 100644 index 0000000..71e8b7b --- /dev/null +++ b/app/Domain/Notifications/KeyHasBeenTakenNotification.php @@ -0,0 +1,42 @@ + $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; + } +} diff --git a/app/Domain/Notifications/Notifiable.php b/app/Domain/Notifications/Notifiable.php new file mode 100644 index 0000000..99fef2f --- /dev/null +++ b/app/Domain/Notifications/Notifiable.php @@ -0,0 +1,10 @@ + $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, ]); } diff --git a/app/Domain/Notifications/VacationRequestStatusChangedNotification.php b/app/Domain/Notifications/VacationRequestStatusChangedNotification.php index 11594a1..746e3a0 100644 --- a/app/Domain/Notifications/VacationRequestStatusChangedNotification.php +++ b/app/Domain/Notifications/VacationRequestStatusChangedNotification.php @@ -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(), + ]); + } } diff --git a/app/Domain/Notifications/VacationRequestWaitsForApprovalNotification.php b/app/Domain/Notifications/VacationRequestWaitsForApprovalNotification.php index 109eef9..63ea770 100644 --- a/app/Domain/Notifications/VacationRequestWaitsForApprovalNotification.php +++ b/app/Domain/Notifications/VacationRequestWaitsForApprovalNotification.php @@ -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 ]); } } - diff --git a/app/Domain/PolishHolidaysRetriever.php b/app/Domain/PolishHolidaysRetriever.php index db574a4..f8709d3 100644 --- a/app/Domain/PolishHolidaysRetriever.php +++ b/app/Domain/PolishHolidaysRetriever.php @@ -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(); diff --git a/app/Domain/TimesheetExport.php b/app/Domain/TimesheetExport.php index 8d764d0..47c258d 100644 --- a/app/Domain/TimesheetExport.php +++ b/app/Domain/TimesheetExport.php @@ -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(); } diff --git a/app/Domain/TimesheetPerUserSheet.php b/app/Domain/TimesheetPerUserSheet.php index 8547a5e..891ca34 100644 --- a/app/Domain/TimesheetPerUserSheet.php +++ b/app/Domain/TimesheetPerUserSheet.php @@ -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, ], ); } diff --git a/app/Domain/UserVacationStatsRetriever.php b/app/Domain/UserVacationStatsRetriever.php index 7c11a29..42fdf50 100644 --- a/app/Domain/UserVacationStatsRetriever.php +++ b/app/Domain/UserVacationStatsRetriever.php @@ -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)); } } diff --git a/app/Domain/VacationDaysCalculator.php b/app/Domain/VacationDaysCalculator.php index 0e41a4a..2d3227e 100644 --- a/app/Domain/VacationDaysCalculator.php +++ b/app/Domain/VacationDaysCalculator.php @@ -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(); diff --git a/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php b/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php index 3fd5429..3b53892 100644 --- a/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php +++ b/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php @@ -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)); } } diff --git a/app/Domain/Validation/Rules/MinimumOneVacationDayRule.php b/app/Domain/Validation/Rules/MinimumOneVacationDayRule.php index ae9f58b..4968d4d 100644 --- a/app/Domain/Validation/Rules/MinimumOneVacationDayRule.php +++ b/app/Domain/Validation/Rules/MinimumOneVacationDayRule.php @@ -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(); } diff --git a/app/Domain/Validation/Rules/VacationRequestRule.php b/app/Domain/Validation/Rules/VacationRequestRule.php index 07af8d2..f7e3c6d 100644 --- a/app/Domain/Validation/Rules/VacationRequestRule.php +++ b/app/Domain/Validation/Rules/VacationRequestRule.php @@ -9,5 +9,6 @@ use Toby\Eloquent\Models\VacationRequest; interface VacationRequestRule { public function check(VacationRequest $vacationRequest): bool; + public function errorMessage(): string; } diff --git a/app/Domain/Validation/Rules/VacationTypeCanBeSelected.php b/app/Domain/Validation/Rules/VacationTypeCanBeSelected.php index 2a7050f..a62d675 100644 --- a/app/Domain/Validation/Rules/VacationTypeCanBeSelected.php +++ b/app/Domain/Validation/Rules/VacationTypeCanBeSelected.php @@ -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); } diff --git a/app/Eloquent/Helpers/YearPeriodRetriever.php b/app/Eloquent/Helpers/YearPeriodRetriever.php index cf512cb..d248501 100644 --- a/app/Eloquent/Helpers/YearPeriodRetriever.php +++ b/app/Eloquent/Helpers/YearPeriodRetriever.php @@ -35,7 +35,7 @@ class YearPeriodRetriever $years = YearPeriod::all(); - $navigation = $years->map(fn(YearPeriod $yearPeriod) => $this->toNavigation($yearPeriod)); + $navigation = $years->map(fn(YearPeriod $yearPeriod): array => $this->toNavigation($yearPeriod)); return [ "current" => $this->toNavigation($current), diff --git a/app/Eloquent/Models/Holiday.php b/app/Eloquent/Models/Holiday.php index be8ae49..466065c 100644 --- a/app/Eloquent/Models/Holiday.php +++ b/app/Eloquent/Models/Holiday.php @@ -21,7 +21,6 @@ class Holiday extends Model use HasFactory; protected $guarded = []; - protected $casts = [ "date" => "date", ]; diff --git a/app/Eloquent/Models/Key.php b/app/Eloquent/Models/Key.php index cb7ee23..cb7fc6f 100644 --- a/app/Eloquent/Models/Key.php +++ b/app/Eloquent/Models/Key.php @@ -8,14 +8,17 @@ use Database\Factories\KeyFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Notifications\Notifiable; +use Toby\Domain\Notifications\Notifiable as NotifiableInterface; /** * @property int $id * @property User $user */ -class Key extends Model +class Key extends Model implements NotifiableInterface { use HasFactory; + use Notifiable; protected $guarded = []; @@ -24,6 +27,11 @@ class Key extends Model return $this->belongsTo(User::class); } + public function routeNotificationForSlack(): string + { + return config("services.slack.default_channel"); + } + protected static function newFactory(): KeyFactory { return KeyFactory::new(); diff --git a/app/Eloquent/Models/Profile.php b/app/Eloquent/Models/Profile.php index df237e9..a64b272 100644 --- a/app/Eloquent/Models/Profile.php +++ b/app/Eloquent/Models/Profile.php @@ -19,6 +19,7 @@ use Toby\Eloquent\Helpers\ColorGenerator; * @property string $position * @property EmploymentForm $employment_form * @property Carbon $employment_date + * @property Carbon $birthday */ class Profile extends Model { @@ -26,12 +27,11 @@ class Profile extends Model use HasAvatar; protected $primaryKey = "user_id"; - protected $guarded = []; - protected $casts = [ "employment_form" => EmploymentForm::class, "employment_date" => "date", + "birthday" => "date", ]; public function user(): BelongsTo diff --git a/app/Eloquent/Models/User.php b/app/Eloquent/Models/User.php index 4bf2891..dcd3941 100644 --- a/app/Eloquent/Models/User.php +++ b/app/Eloquent/Models/User.php @@ -15,6 +15,7 @@ use Illuminate\Notifications\Notifiable; use Illuminate\Support\Collection; use Toby\Domain\Enums\EmploymentForm; use Toby\Domain\Enums\Role; +use Toby\Domain\Notifications\Notifiable as NotifiableInterface; /** * @property int $id @@ -26,25 +27,22 @@ use Toby\Domain\Enums\Role; * @property Collection $vacationRequests * @property Collection $vacations */ -class User extends Authenticatable +class User extends Authenticatable implements NotifiableInterface { use HasFactory; use Notifiable; use SoftDeletes; protected $guarded = []; - protected $casts = [ "role" => Role::class, "last_active_at" => "datetime", "employment_form" => EmploymentForm::class, "employment_date" => "date", ]; - protected $hidden = [ "remember_token", ]; - protected $with = [ "profile", ]; @@ -102,7 +100,7 @@ class User extends Authenticatable ->where("email", "ILIKE", "%{$text}%") ->orWhereRelation( "profile", - fn(Builder $query) => $query + fn(Builder $query): Builder => $query ->where("first_name", "ILIKE", "%{$text}%") ->orWhere("last_name", "ILIKE", "%{$text}%"), ); @@ -125,6 +123,11 @@ class User extends Authenticatable ); } + public function routeNotificationForSlack() + { + return $this->profile->slack_id; + } + protected static function newFactory(): UserFactory { return UserFactory::new(); diff --git a/app/Eloquent/Models/VacationRequest.php b/app/Eloquent/Models/VacationRequest.php index 46bd084..97f94b7 100644 --- a/app/Eloquent/Models/VacationRequest.php +++ b/app/Eloquent/Models/VacationRequest.php @@ -41,7 +41,6 @@ class VacationRequest extends Model use HasStates; protected $guarded = []; - protected $casts = [ "type" => VacationType::class, "state" => VacationRequestState::class, diff --git a/app/Eloquent/Models/VacationRequestActivity.php b/app/Eloquent/Models/VacationRequestActivity.php index 942bb96..2064e9e 100644 --- a/app/Eloquent/Models/VacationRequestActivity.php +++ b/app/Eloquent/Models/VacationRequestActivity.php @@ -22,7 +22,6 @@ class VacationRequestActivity extends Model use HasFactory; protected $guarded = []; - protected $casts = [ "from" => VacationRequestState::class, "to" => VacationRequestState::class, diff --git a/app/Infrastructure/Console/Commands/SendDailySummaryToSlack.php b/app/Infrastructure/Console/Commands/SendDailySummaryToSlack.php new file mode 100644 index 0000000..1c2a5b6 --- /dev/null +++ b/app/Infrastructure/Console/Commands/SendDailySummaryToSlack.php @@ -0,0 +1,79 @@ +option("force") && !$this->shouldHandle($now)) { + return; + } + + $attachments = new Collection([ + new AbsencesAttachment($dailySummaryRetriever->getAbsences($now)), + new RemotesAttachment($dailySummaryRetriever->getRemoteDays($now)), + new BirthdaysAttachment($dailySummaryRetriever->getBirthdays($now)), + ]); + + Http::withToken($this->getSlackClientToken()) + ->post($this->getUrl(), [ + "channel" => $this->getSlackChannel(), + "text" => "Podsumowanie dla dnia {$now->toDisplayString()}", + "attachments" => $attachments, + ]); + } + + protected function shouldHandle(CarbonInterface $day): bool + { + $holidays = Holiday::query()->whereDate("date", $day)->pluck("date"); + + if ($day->isWeekend()) { + return false; + } + + if ($holidays->contains($day)) { + return false; + } + + return true; + } + + protected function getUrl(): string + { + return "{$this->getSlackBaseUrl()}/chat.postMessage"; + } + + protected function getSlackBaseUrl(): ?string + { + return config("services.slack.url"); + } + + protected function getSlackClientToken(): ?string + { + return config("services.slack.client_token"); + } + + protected function getSlackChannel(): ?string + { + return config("services.slack.default_channel"); + } +} diff --git a/app/Infrastructure/Http/Controllers/Api/CalculateVacationDaysController.php b/app/Infrastructure/Http/Controllers/Api/CalculateVacationDaysController.php index c204be1..3f3caa5 100644 --- a/app/Infrastructure/Http/Controllers/Api/CalculateVacationDaysController.php +++ b/app/Infrastructure/Http/Controllers/Api/CalculateVacationDaysController.php @@ -14,8 +14,8 @@ class CalculateVacationDaysController extends Controller { public function __invoke(CalculateVacationDaysRequest $request, VacationDaysCalculator $calculator): JsonResponse { - $days = $calculator->calculateDays($request->yearPeriod(), $request->from(), $request->to()); + $days = $calculator->calculateDays($request->from(), $request->to()); - return new JsonResponse($days->map(fn(Carbon $day) => $day->toDateString())->all()); + return new JsonResponse($days->map(fn(Carbon $day): string => $day->toDateString())->all()); } } diff --git a/app/Infrastructure/Http/Controllers/Api/GetAvailableVacationTypesController.php b/app/Infrastructure/Http/Controllers/Api/GetAvailableVacationTypesController.php index 22a5f33..48a92a2 100644 --- a/app/Infrastructure/Http/Controllers/Api/GetAvailableVacationTypesController.php +++ b/app/Infrastructure/Http/Controllers/Api/GetAvailableVacationTypesController.php @@ -21,8 +21,8 @@ class GetAvailableVacationTypesController extends Controller $user = User::query()->find($request->get("user")); $types = VacationType::all() - ->filter(fn(VacationType $type) => $configRetriever->isAvailableFor($type, $user->profile->employment_form)) - ->map(fn(VacationType $type) => [ + ->filter(fn(VacationType $type): bool => $configRetriever->isAvailableFor($type, $user->profile->employment_form)) + ->map(fn(VacationType $type): array => [ "label" => $type->label(), "value" => $type->value, ]) diff --git a/app/Infrastructure/Http/Controllers/DashboardController.php b/app/Infrastructure/Http/Controllers/DashboardController.php index 33bf1aa..9f164a9 100644 --- a/app/Infrastructure/Http/Controllers/DashboardController.php +++ b/app/Infrastructure/Http/Controllers/DashboardController.php @@ -7,12 +7,11 @@ namespace Toby\Infrastructure\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Carbon; use Inertia\Response; -use Toby\Domain\Enums\VacationType; +use Toby\Domain\DailySummaryRetriever; use Toby\Domain\UserVacationStatsRetriever; use Toby\Domain\VacationRequestStatesRetriever; use Toby\Domain\VacationTypeConfigRetriever; use Toby\Eloquent\Helpers\YearPeriodRetriever; -use Toby\Eloquent\Models\Vacation; use Toby\Eloquent\Models\VacationRequest; use Toby\Infrastructure\Http\Resources\HolidayResource; use Toby\Infrastructure\Http\Resources\VacationRequestResource; @@ -25,24 +24,14 @@ class DashboardController extends Controller YearPeriodRetriever $yearPeriodRetriever, UserVacationStatsRetriever $vacationStatsRetriever, VacationTypeConfigRetriever $configRetriever, + DailySummaryRetriever $dailySummaryRetriever, ): Response { $user = $request->user(); $now = Carbon::now(); $yearPeriod = $yearPeriodRetriever->selected(); - $absences = Vacation::query() - ->with(["user", "vacationRequest"]) - ->whereDate("date", $now) - ->approved() - ->whereTypes(VacationType::all()->filter(fn(VacationType $type) => $configRetriever->isVacation($type))) - ->get(); - - $remoteDays = Vacation::query() - ->with(["user", "vacationRequest"]) - ->whereDate("date", $now) - ->approved() - ->whereTypes(VacationType::all()->filter(fn(VacationType $type) => !$configRetriever->isVacation($type))) - ->get(); + $absences = $dailySummaryRetriever->getAbsences($now); + $remoteDays = $dailySummaryRetriever->getRemoteDays($now); if ($user->can("listAll", VacationRequest::class)) { $vacationRequests = $yearPeriod->vacationRequests() diff --git a/app/Infrastructure/Http/Controllers/KeysController.php b/app/Infrastructure/Http/Controllers/KeysController.php index 096e540..d71c177 100644 --- a/app/Infrastructure/Http/Controllers/KeysController.php +++ b/app/Infrastructure/Http/Controllers/KeysController.php @@ -8,6 +8,8 @@ use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\Request; use Inertia\Response; use Symfony\Component\HttpFoundation\RedirectResponse; +use Toby\Domain\Notifications\KeyHasBeenGivenNotification; +use Toby\Domain\Notifications\KeyHasBeenTakenNotification; use Toby\Eloquent\Models\Key; use Toby\Eloquent\Models\User; use Toby\Infrastructure\Http\Requests\GiveKeyRequest; @@ -60,6 +62,8 @@ class KeysController extends Controller $key->save(); + $key->notify(new KeyHasBeenTakenNotification($request->user(), $previousUser)); + return redirect() ->back() ->with("success", __("Key no :number has been taken from :user.", [ @@ -81,6 +85,8 @@ class KeysController extends Controller $key->save(); + $key->notify(new KeyHasBeenGivenNotification($request->user(), $recipient)); + return redirect() ->back() ->with("success", __("Key no :number has been given to :user.", [ diff --git a/app/Infrastructure/Http/Controllers/TimesheetController.php b/app/Infrastructure/Http/Controllers/TimesheetController.php index 16af5f6..2404889 100644 --- a/app/Infrastructure/Http/Controllers/TimesheetController.php +++ b/app/Infrastructure/Http/Controllers/TimesheetController.php @@ -35,8 +35,10 @@ class TimesheetController extends Controller $types = VacationType::all() ->filter( - fn(VacationType $type) => $configRetriever->isAvailableFor($type, EmploymentForm::EmploymentContract) - && $configRetriever->isVacation($type), + fn(VacationType $type): bool => $configRetriever->isAvailableFor( + $type, + EmploymentForm::EmploymentContract, + ) && $configRetriever->isVacation($type), ); $filename = "{$carbonMonth->translatedFormat("F Y")}.xlsx"; diff --git a/app/Infrastructure/Http/Controllers/VacationLimitController.php b/app/Infrastructure/Http/Controllers/VacationLimitController.php index 29c333e..31a7d80 100644 --- a/app/Infrastructure/Http/Controllers/VacationLimitController.php +++ b/app/Infrastructure/Http/Controllers/VacationLimitController.php @@ -30,7 +30,7 @@ class VacationLimitController extends Controller ->sortBy(fn(VacationLimit $limit): string => "{$limit->user->profile->last_name} {$limit->user->profile->first_name}") ->values(); - $limitsResource = $limits->map(fn(VacationLimit $limit) => [ + $limitsResource = $limits->map(fn(VacationLimit $limit): array => [ "id" => $limit->id, "user" => new UserResource($limit->user), "hasVacation" => $limit->hasVacation(), diff --git a/app/Infrastructure/Http/Kernel.php b/app/Infrastructure/Http/Kernel.php index 5c6a238..5b1e42f 100644 --- a/app/Infrastructure/Http/Kernel.php +++ b/app/Infrastructure/Http/Kernel.php @@ -40,7 +40,6 @@ class Kernel extends HttpKernel TrimStrings::class, ConvertEmptyStringsToNull::class, ]; - protected $middlewareGroups = [ "web" => [ EncryptCookies::class, @@ -58,7 +57,6 @@ class Kernel extends HttpKernel SubstituteBindings::class, ], ]; - protected $routeMiddleware = [ "auth" => Authenticate::class, "auth.basic" => AuthenticateWithBasicAuth::class, diff --git a/app/Infrastructure/Http/Middleware/HandleInertiaRequests.php b/app/Infrastructure/Http/Middleware/HandleInertiaRequests.php index 7344b7f..15cb0dd 100644 --- a/app/Infrastructure/Http/Middleware/HandleInertiaRequests.php +++ b/app/Infrastructure/Http/Middleware/HandleInertiaRequests.php @@ -32,7 +32,7 @@ class HandleInertiaRequests extends Middleware { $user = $request->user(); - return fn() => [ + return fn(): array => [ "user" => $user ? new UserResource($user) : null, "can" => [ "manageVacationLimits" => $user ? $user->can("manageVacationLimits") : false, @@ -45,7 +45,7 @@ class HandleInertiaRequests extends Middleware protected function getFlashData(Request $request): Closure { - return fn() => [ + return fn(): array => [ "success" => $request->session()->get("success"), "error" => $request->session()->get("error"), "info" => $request->session()->get("info"), diff --git a/app/Infrastructure/Http/Requests/UserRequest.php b/app/Infrastructure/Http/Requests/UserRequest.php index d3f094f..8b74657 100644 --- a/app/Infrastructure/Http/Requests/UserRequest.php +++ b/app/Infrastructure/Http/Requests/UserRequest.php @@ -22,6 +22,8 @@ class UserRequest extends FormRequest "position" => ["required"], "employmentForm" => ["required", new Enum(EmploymentForm::class)], "employmentDate" => ["required", "date_format:Y-m-d"], + "birthday" => ["nullable", "date_format:Y-m-d"], + "slackId" => [], ]; } @@ -41,6 +43,8 @@ class UserRequest extends FormRequest "position" => $this->get("position"), "employment_form" => $this->get("employmentForm"), "employment_date" => $this->get("employmentDate"), + "birthday" => $this->get("birthday"), + "slack_id" => $this->get("slackId"), ]; } } diff --git a/app/Infrastructure/Http/Resources/UserFormDataResource.php b/app/Infrastructure/Http/Resources/UserFormDataResource.php index 219a2c9..83fd4e9 100644 --- a/app/Infrastructure/Http/Resources/UserFormDataResource.php +++ b/app/Infrastructure/Http/Resources/UserFormDataResource.php @@ -21,6 +21,8 @@ class UserFormDataResource extends JsonResource "position" => $this->profile->position, "employmentForm" => $this->profile->employment_form, "employmentDate" => $this->profile->employment_date->toDateString(), + "birthday" => $this->profile->birthday?->toDateString(), + "slackId" => $this->profile->slack_id, ]; } } diff --git a/app/Infrastructure/Slack/Channels/SlackApiChannel.php b/app/Infrastructure/Slack/Channels/SlackApiChannel.php new file mode 100644 index 0000000..d790555 --- /dev/null +++ b/app/Infrastructure/Slack/Channels/SlackApiChannel.php @@ -0,0 +1,36 @@ +getBaseUrl(); + $url = "{$baseUrl}/chat.postMessage"; + $channel = $notifiable->routeNotificationFor("slack", $notification); + + return Http::withToken($this->getClientToken()) + ->post($url, [ + "channel" => $channel, + "text" => $notification->toSlack($notifiable), + ]); + } + + protected function getClientToken(): string + { + return config("services.slack.client_token"); + } + + protected function getBaseUrl(): string + { + return config("services.slack.url"); + } +} diff --git a/app/Infrastructure/Slack/Controller.php b/app/Infrastructure/Slack/Controller.php new file mode 100644 index 0000000..a3467bb --- /dev/null +++ b/app/Infrastructure/Slack/Controller.php @@ -0,0 +1,56 @@ +verifyWithSigning($request); + + $handler = $this->determineHandler(); + + try { + $response = $handler->handle($this->request); + } catch (SlackSlashCommandException $exception) { + $response = $exception->getResponse($this->request); + } catch (ValidationException $exception) { + $response = $this->prepareValidationResponse($exception); + } catch (Exception $exception) { + $response = $this->convertToResponse($exception); + } + + return $response->getIlluminateResponse(); + } + + protected function prepareValidationResponse(ValidationException $exception): Response + { + $errors = (new Collection($exception->errors())) + ->map( + fn(array $message): Attachment => Attachment::create() + ->setColor("danger") + ->setText($message[0]), + ); + + return Response::create($this->request) + ->withText(":x: Polecenie `/{$this->request->command} {$this->request->text}` jest niepoprawne:") + ->withAttachments($errors->all()); + } +} diff --git a/app/Infrastructure/Slack/Elements/AbsencesAttachment.php b/app/Infrastructure/Slack/Elements/AbsencesAttachment.php new file mode 100644 index 0000000..3f34fac --- /dev/null +++ b/app/Infrastructure/Slack/Elements/AbsencesAttachment.php @@ -0,0 +1,22 @@ +setTitle("Nieobecności :palm_tree:") + ->setColor("#eab308") + ->setItems($absences->map(fn(Vacation $vacation): string => $vacation->user->profile->full_name)) + ->setEmptyText("Wszyscy dzisiaj pracują :muscle:"); + } +} diff --git a/app/Infrastructure/Slack/Elements/Attachment.php b/app/Infrastructure/Slack/Elements/Attachment.php new file mode 100644 index 0000000..9b223ec --- /dev/null +++ b/app/Infrastructure/Slack/Elements/Attachment.php @@ -0,0 +1,12 @@ +setTitle("Urodziny :birthday:") + ->setColor("#3c5f97") + ->setItems($birthdays->map(fn(User $user): string => $user->profile->full_name)) + ->setEmptyText("Dzisiaj nikt nie ma urodzin :cry:"); + } +} diff --git a/app/Infrastructure/Slack/Elements/KeysAttachment.php b/app/Infrastructure/Slack/Elements/KeysAttachment.php new file mode 100644 index 0000000..80819d3 --- /dev/null +++ b/app/Infrastructure/Slack/Elements/KeysAttachment.php @@ -0,0 +1,21 @@ +setColor("#3c5f97") + ->setItems($keys->map(fn(Key $key): string => "Klucz nr {$key->id} - <@{$key->user->profile->slack_id}>")) + ->setEmptyText("Nie ma żadnych kluczy w tobym"); + } +} diff --git a/app/Infrastructure/Slack/Elements/ListAttachment.php b/app/Infrastructure/Slack/Elements/ListAttachment.php new file mode 100644 index 0000000..3e3dfab --- /dev/null +++ b/app/Infrastructure/Slack/Elements/ListAttachment.php @@ -0,0 +1,36 @@ +items = $items; + + return $this; + } + + public function setEmptyText(string $emptyText): static + { + $this->emptyText = $emptyText; + + return $this; + } + + public function toArray(): array + { + $fields = parent::toArray(); + + return array_merge($fields, [ + "text" => $this->items->isNotEmpty() ? $this->items->implode("\n") : $this->emptyText, + ]); + } +} diff --git a/app/Infrastructure/Slack/Elements/RemotesAttachment.php b/app/Infrastructure/Slack/Elements/RemotesAttachment.php new file mode 100644 index 0000000..2f39ad4 --- /dev/null +++ b/app/Infrastructure/Slack/Elements/RemotesAttachment.php @@ -0,0 +1,22 @@ +setTitle("Praca zdalna :house_with_garden:") + ->setColor("#527aba") + ->setItems($remoteDays->map(fn(Vacation $vacation): string => $vacation->user->profile->full_name)) + ->setEmptyText("Wszyscy dzisiaj są w biurze :boom:"); + } +} diff --git a/app/Infrastructure/Slack/Exceptions/UserNotFoundException.php b/app/Infrastructure/Slack/Exceptions/UserNotFoundException.php new file mode 100644 index 0000000..94a3948 --- /dev/null +++ b/app/Infrastructure/Slack/Exceptions/UserNotFoundException.php @@ -0,0 +1,11 @@ +findAvailableHandlers(); + $attachmentFields = $this->mapHandlersToAttachments($handlers); + + return $this->respondToSlack(":x: Nie rozpoznaję polecenia. Lista wszystkich poleceń:") + ->withAttachment( + Attachment::create() + ->setColor("danger") + ->useMarkdown() + ->setFields($attachmentFields), + ); + } +} diff --git a/app/Infrastructure/Slack/Handlers/DailySummary.php b/app/Infrastructure/Slack/Handlers/DailySummary.php new file mode 100644 index 0000000..5a941ee --- /dev/null +++ b/app/Infrastructure/Slack/Handlers/DailySummary.php @@ -0,0 +1,36 @@ +make(DailySummaryRetriever::class); + + $now = Carbon::today(); + + $attachments = new Collection([ + new AbsencesAttachment($dailySummaryRetriever->getAbsences($now)), + new RemotesAttachment($dailySummaryRetriever->getRemoteDays($now)), + new BirthdaysAttachment($dailySummaryRetriever->getBirthdays($now)), + ]); + + return $this->respondToSlack("Podsumowanie dla dnia {$now->toDisplayString()}") + ->withAttachments($attachments->all()); + } +} diff --git a/app/Infrastructure/Slack/Handlers/GiveKeysTo.php b/app/Infrastructure/Slack/Handlers/GiveKeysTo.php new file mode 100644 index 0000000..7925844 --- /dev/null +++ b/app/Infrastructure/Slack/Handlers/GiveKeysTo.php @@ -0,0 +1,71 @@ + $from] = $this->validate(); + + $authUser = $this->findUserBySlackIdOrFail($request->userId); + $user = $this->findUserBySlackId($from); + + /** @var Key $key */ + $key = $authUser->keys()->first(); + + if (!$key) { + throw ValidationException::withMessages(["key" => "Nie masz żadnego klucza do przekazania"]); + } + + if ($user->is($authUser)) { + throw ValidationException::withMessages([ + "key" => "Nie możesz przekazać sobie kluczy :dzban:", + ]); + } + + $key->user()->associate($user); + + $key->save(); + + $key->notify(new KeyHasBeenGivenNotification($authUser, $user)); + + return $this->respondToSlack( + ":white_check_mark: Klucz nr {$key->id} został przekazany użytkownikowi <@{$user->profile->slack_id}>", + ); + } + + protected function getRules(): array + { + return [ + "user" => ["required", new SlackUserExistsRule()], + ]; + } + + protected function getMessages(): array + { + return [ + "user.required" => "Musisz podać użytkownika, któremu chcesz przekazać klucze", + ]; + } +} diff --git a/app/Infrastructure/Slack/Handlers/Help.php b/app/Infrastructure/Slack/Handlers/Help.php new file mode 100644 index 0000000..e5e7ca2 --- /dev/null +++ b/app/Infrastructure/Slack/Handlers/Help.php @@ -0,0 +1,33 @@ +findAvailableHandlers(); + + $attachmentFields = $this->mapHandlersToAttachments($handlers); + + return $this->respondToSlack("Dostępne polecenia:") + ->withAttachment( + Attachment::create() + ->setColor("good") + ->useMarkdown() + ->setFields($attachmentFields), + ); + } +} diff --git a/app/Infrastructure/Slack/Handlers/HomeOffice.php b/app/Infrastructure/Slack/Handlers/HomeOffice.php new file mode 100644 index 0000000..7674539 --- /dev/null +++ b/app/Infrastructure/Slack/Handlers/HomeOffice.php @@ -0,0 +1,45 @@ +findUserBySlackId($request->userId); + + $this->createRemoteday($user, Carbon::today()); + + return $this->respondToSlack(":white_check_mark: Pracujesz dzisiaj zdalnie"); + } + + protected function createRemoteday(User $user, Carbon $date): void + { + $yearPeriod = YearPeriod::findByYear($date->year); + + app(CreateAction::class)->execute([ + "user_id" => $user->id, + "type" => VacationType::HomeOffice, + "from" => $date, + "to" => $date, + "year_period_id" => $yearPeriod->id, + "flow_skipped" => false, + ], $user); + } +} diff --git a/app/Infrastructure/Slack/Handlers/KeyList.php b/app/Infrastructure/Slack/Handlers/KeyList.php new file mode 100644 index 0000000..87ef123 --- /dev/null +++ b/app/Infrastructure/Slack/Handlers/KeyList.php @@ -0,0 +1,26 @@ +orderBy("id") + ->get(); + + return $this->respondToSlack("Lista kluczy :key:") + ->withAttachment(new KeysAttachment($keys)); + } +} diff --git a/app/Infrastructure/Slack/Handlers/SignatureHandler.php b/app/Infrastructure/Slack/Handlers/SignatureHandler.php new file mode 100644 index 0000000..56af5da --- /dev/null +++ b/app/Infrastructure/Slack/Handlers/SignatureHandler.php @@ -0,0 +1,26 @@ +getArguments(), $this->getRules(), $this->getMessages()); + } + + protected function getRules(): array + { + return []; + } + + protected function getMessages(): array + { + return []; + } +} diff --git a/app/Infrastructure/Slack/Handlers/TakeKeysFrom.php b/app/Infrastructure/Slack/Handlers/TakeKeysFrom.php new file mode 100644 index 0000000..5d89eed --- /dev/null +++ b/app/Infrastructure/Slack/Handlers/TakeKeysFrom.php @@ -0,0 +1,70 @@ + $from] = $this->validate(); + + $authUser = $this->findUserBySlackIdOrFail($request->userId); + $user = $this->findUserBySlackId($from); + + /** @var Key $key */ + $key = $user->keys()->first(); + + if (!$key) { + throw ValidationException::withMessages([ + "key" => "Użytkownik <@{$user->profile->slack_id}> nie ma żadnych kluczy", + ]); + } + + if ($key->user()->is($authUser)) { + throw ValidationException::withMessages([ + "key" => "Nie możesz zabrać sobie kluczy :dzban:", + ]); + } + + $key->user()->associate($authUser); + + $key->save(); + + $key->notify(new KeyHasBeenTakenNotification($authUser, $user)); + + return $this->respondToSlack(":white_check_mark: Klucz nr {$key->id} został zabrany użytkownikowi <@{$user->profile->slack_id}>"); + } + + protected function getRules(): array + { + return [ + "user" => ["required", new SlackUserExistsRule()], + ]; + } + + protected function getMessages(): array + { + return [ + "user.required" => "Musisz podać użytkownika, któremu chcesz zabrać klucze", + ]; + } +} diff --git a/app/Infrastructure/Slack/Rules/SlackUserExistsRule.php b/app/Infrastructure/Slack/Rules/SlackUserExistsRule.php new file mode 100644 index 0000000..500c295 --- /dev/null +++ b/app/Infrastructure/Slack/Rules/SlackUserExistsRule.php @@ -0,0 +1,24 @@ +where("slack_id", $slackId)->exists(); + } + + public function message(): string + { + return "Użytkownik :input nie istnieje w tobym"; + } +} diff --git a/app/Infrastructure/Slack/Traits/FindsUserBySlackId.php b/app/Infrastructure/Slack/Traits/FindsUserBySlackId.php new file mode 100644 index 0000000..b3f1bae --- /dev/null +++ b/app/Infrastructure/Slack/Traits/FindsUserBySlackId.php @@ -0,0 +1,43 @@ +prepareSlackIdFromString($slackId); + + /** @var User $user */ + $user = User::query() + ->whereRelation("profile", "slack_id", $id) + ->first(); + + return $user; + } + + /** + * @throws UserNotFoundException + */ + protected function findUserBySlackIdOrFail(string $slackId): ?User + { + $user = $this->findUserBySlackId($slackId); + + if (!$user) { + throw new UserNotFoundException("Użytkownik {$slackId} nie istnieje w tobym"); + } + + return $user; + } + + protected function prepareSlackIdFromString(string $slackId): string + { + return Str::between($slackId, "<@", "|"); + } +} diff --git a/app/Infrastructure/Slack/Traits/ListsHandlers.php b/app/Infrastructure/Slack/Traits/ListsHandlers.php new file mode 100644 index 0000000..0ec2d1f --- /dev/null +++ b/app/Infrastructure/Slack/Traits/ListsHandlers.php @@ -0,0 +1,46 @@ +map(fn(string $handlerClassName): BaseHandler => new $handlerClassName($this->request)) + ->filter(fn(HandlesSlashCommand $handler): bool => $handler instanceof SignatureHandler) + ->filter(function (SignatureHandler $handler) { + $signatureParts = new SignatureParts($handler->getSignature()); + + return Str::is($signatureParts->getSlashCommandName(), $this->request->command); + }); + } + + protected function mapHandlersToAttachments(Collection $handlers): array + { + return $handlers + ->sort( + fn(SignatureHandler $handlerA, SignatureHandler $handlerB): int => strcmp( + $handlerA->getFullCommand(), + $handlerB->getFullCommand(), + ), + ) + ->map( + fn(SignatureHandler $handler): AttachmentField => AttachmentField::create( + $handler->getDescription(), + "`/{$handler->getSignature()}`", + ), + ) + ->all(); + } +} diff --git a/composer.json b/composer.json index 82145dd..36d9d98 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,8 @@ "maatwebsite/excel": "^3.1", "rackbeat/laravel-ui-avatars": "^1.0", "spatie/laravel-google-calendar": "^3.5", - "spatie/laravel-model-states": "^2.1" + "spatie/laravel-model-states": "^2.1", + "spatie/laravel-slack-slash-command": "^1.11" }, "require-dev": { "blumilksoftware/codestyle": "^1.0.0", @@ -61,7 +62,8 @@ "extra": { "laravel": { "dont-discover": [ - "laravel/telescope" + "laravel/telescope", + "spatie/laravel-slack-slash-command" ] } }, diff --git a/composer.lock b/composer.lock index 8dd0196..498daa4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "414a1fc13e0731e59605248bd4e39de6", + "content-hash": "24a1b3a5dd7c4d4f50d521dda4b6654e", "packages": [ { "name": "asm89/stack-cors", @@ -815,23 +815,23 @@ }, { "name": "firebase/php-jwt", - "version": "v5.5.1", + "version": "v6.1.2", "source": { "type": "git", "url": "https://github.com/firebase/php-jwt.git", - "reference": "83b609028194aa042ea33b5af2d41a7427de80e6" + "reference": "c297139da7c6873dbd67cbd1093f09ec0bbd0c50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/83b609028194aa042ea33b5af2d41a7427de80e6", - "reference": "83b609028194aa042ea33b5af2d41a7427de80e6", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/c297139da7c6873dbd67cbd1093f09ec0bbd0c50", + "reference": "c297139da7c6873dbd67cbd1093f09ec0bbd0c50", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^7.1||^8.0" }, "require-dev": { - "phpunit/phpunit": ">=4.8 <=9" + "phpunit/phpunit": "^7.5||9.5" }, "suggest": { "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" @@ -866,9 +866,9 @@ ], "support": { "issues": "https://github.com/firebase/php-jwt/issues", - "source": "https://github.com/firebase/php-jwt/tree/v5.5.1" + "source": "https://github.com/firebase/php-jwt/tree/v6.1.2" }, - "time": "2021-11-08T20:18:51+00:00" + "time": "2022-04-21T14:37:18+00:00" }, { "name": "fruitcake/laravel-cors", @@ -1022,16 +1022,16 @@ }, { "name": "google/apiclient", - "version": "v2.12.2", + "version": "v2.12.4", "source": { "type": "git", "url": "https://github.com/googleapis/google-api-php-client.git", - "reference": "a18b0e1ef5618523c607c01a41ec137c7f9af3b1" + "reference": "702eed9ae7022ba20dc7118c8161060cb50ee9f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/google-api-php-client/zipball/a18b0e1ef5618523c607c01a41ec137c7f9af3b1", - "reference": "a18b0e1ef5618523c607c01a41ec137c7f9af3b1", + "url": "https://api.github.com/repos/googleapis/google-api-php-client/zipball/702eed9ae7022ba20dc7118c8161060cb50ee9f8", + "reference": "702eed9ae7022ba20dc7118c8161060cb50ee9f8", "shasum": "" }, "require": { @@ -1087,22 +1087,22 @@ ], "support": { "issues": "https://github.com/googleapis/google-api-php-client/issues", - "source": "https://github.com/googleapis/google-api-php-client/tree/v2.12.2" + "source": "https://github.com/googleapis/google-api-php-client/tree/v2.12.4" }, - "time": "2022-04-05T16:19:05+00:00" + "time": "2022-04-20T16:44:03+00:00" }, { "name": "google/apiclient-services", - "version": "v0.242.0", + "version": "v0.246.0", "source": { "type": "git", "url": "https://github.com/googleapis/google-api-php-client-services.git", - "reference": "73d4c0ed4b241e7396699e0ee1d1cdebabac25e8" + "reference": "33aef1ccce34799a1124c39951fed8ad0b16aced" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/73d4c0ed4b241e7396699e0ee1d1cdebabac25e8", - "reference": "73d4c0ed4b241e7396699e0ee1d1cdebabac25e8", + "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/33aef1ccce34799a1124c39951fed8ad0b16aced", + "reference": "33aef1ccce34799a1124c39951fed8ad0b16aced", "shasum": "" }, "require": { @@ -1131,30 +1131,30 @@ ], "support": { "issues": "https://github.com/googleapis/google-api-php-client-services/issues", - "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.242.0" + "source": "https://github.com/googleapis/google-api-php-client-services/tree/v0.246.0" }, - "time": "2022-04-03T01:24:10+00:00" + "time": "2022-04-24T00:58:37+00:00" }, { "name": "google/auth", - "version": "v1.19.0", + "version": "v1.21.0", "source": { "type": "git", "url": "https://github.com/googleapis/google-auth-library-php.git", - "reference": "31e5d24d5fa0eaf6adc7e596292dc4732f4b60c5" + "reference": "73392bad2eb6852eea9084b6bbdec752515cb849" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/31e5d24d5fa0eaf6adc7e596292dc4732f4b60c5", - "reference": "31e5d24d5fa0eaf6adc7e596292dc4732f4b60c5", + "url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/73392bad2eb6852eea9084b6bbdec752515cb849", + "reference": "73392bad2eb6852eea9084b6bbdec752515cb849", "shasum": "" }, "require": { - "firebase/php-jwt": "~5.0", + "firebase/php-jwt": "^5.5||^6.0", "guzzlehttp/guzzle": "^6.2.1|^7.0", "guzzlehttp/psr7": "^1.7|^2.0", - "php": ">=5.6", - "psr/cache": "^1.0|^2.0", + "php": "^7.1||^8.0", + "psr/cache": "^1.0|^2.0|^3.0", "psr/http-message": "^1.0" }, "require-dev": { @@ -1162,7 +1162,7 @@ "kelvinmo/simplejwt": "^0.2.5|^0.5.1", "phpseclib/phpseclib": "^2.0.31", "phpspec/prophecy-phpunit": "^1.1", - "phpunit/phpunit": "^5.7||^8.5.13", + "phpunit/phpunit": "^7.5||^8.5", "sebastian/comparator": ">=1.2.3", "squizlabs/php_codesniffer": "^3.5" }, @@ -1189,9 +1189,9 @@ "support": { "docs": "https://googleapis.github.io/google-auth-library-php/main/", "issues": "https://github.com/googleapis/google-auth-library-php/issues", - "source": "https://github.com/googleapis/google-auth-library-php/tree/v1.19.0" + "source": "https://github.com/googleapis/google-auth-library-php/tree/v1.21.0" }, - "time": "2022-03-24T21:22:45+00:00" + "time": "2022-04-13T20:35:52+00:00" }, { "name": "graham-campbell/result-type", @@ -1733,16 +1733,16 @@ }, { "name": "laravel/framework", - "version": "v9.7.0", + "version": "v9.9.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "54c9696ee3e558ab29317ed6e0cb16bb9db5aad4" + "reference": "4d5a07640891b772188d7737348886a0222737d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/54c9696ee3e558ab29317ed6e0cb16bb9db5aad4", - "reference": "54c9696ee3e558ab29317ed6e0cb16bb9db5aad4", + "url": "https://api.github.com/repos/laravel/framework/zipball/4d5a07640891b772188d7737348886a0222737d8", + "reference": "4d5a07640891b772188d7737348886a0222737d8", "shasum": "" }, "require": { @@ -1908,20 +1908,76 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2022-04-05T15:07:51+00:00" + "time": "2022-04-19T15:01:23+00:00" }, { - "name": "laravel/sanctum", - "version": "v2.15.0", + "name": "laravel/helpers", + "version": "v1.5.0", "source": { "type": "git", - "url": "https://github.com/laravel/sanctum.git", - "reference": "5be160413b6f37dcf8758663edeab12d0e806f56" + "url": "https://github.com/laravel/helpers.git", + "reference": "c28b0ccd799d58564c41a62395ac9511a1e72931" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sanctum/zipball/5be160413b6f37dcf8758663edeab12d0e806f56", - "reference": "5be160413b6f37dcf8758663edeab12d0e806f56", + "url": "https://api.github.com/repos/laravel/helpers/zipball/c28b0ccd799d58564c41a62395ac9511a1e72931", + "reference": "c28b0ccd799d58564c41a62395ac9511a1e72931", + "shasum": "" + }, + "require": { + "illuminate/support": "~5.8.0|^6.0|^7.0|^8.0|^9.0", + "php": "^7.1.3|^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0|^8.0|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Dries Vints", + "email": "dries@laravel.com" + } + ], + "description": "Provides backwards compatibility for helpers in the latest Laravel release.", + "keywords": [ + "helpers", + "laravel" + ], + "support": { + "source": "https://github.com/laravel/helpers/tree/v1.5.0" + }, + "time": "2022-01-12T15:58:51+00:00" + }, + { + "name": "laravel/sanctum", + "version": "v2.15.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "31fbe6f85aee080c4dc2f9b03dc6dd5d0ee72473" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/31fbe6f85aee080c4dc2f9b03dc6dd5d0ee72473", + "reference": "31fbe6f85aee080c4dc2f9b03dc6dd5d0ee72473", "shasum": "" }, "require": { @@ -1973,7 +2029,7 @@ "issues": "https://github.com/laravel/sanctum/issues", "source": "https://github.com/laravel/sanctum" }, - "time": "2022-03-28T13:53:05+00:00" + "time": "2022-04-08T13:39:49+00:00" }, { "name": "laravel/serializable-closure", @@ -2105,16 +2161,16 @@ }, { "name": "laravel/telescope", - "version": "v4.8.2", + "version": "v4.8.3", "source": { "type": "git", "url": "https://github.com/laravel/telescope.git", - "reference": "88e3ded3e1fc301a82403820b2f68ec5a9e092e3" + "reference": "bb23d58161032c8745d38348452afcbcd8adfc78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/telescope/zipball/88e3ded3e1fc301a82403820b2f68ec5a9e092e3", - "reference": "88e3ded3e1fc301a82403820b2f68ec5a9e092e3", + "url": "https://api.github.com/repos/laravel/telescope/zipball/bb23d58161032c8745d38348452afcbcd8adfc78", + "reference": "bb23d58161032c8745d38348452afcbcd8adfc78", "shasum": "" }, "require": { @@ -2167,9 +2223,9 @@ ], "support": { "issues": "https://github.com/laravel/telescope/issues", - "source": "https://github.com/laravel/telescope/tree/v4.8.2" + "source": "https://github.com/laravel/telescope/tree/v4.8.3" }, - "time": "2022-04-01T14:27:12+00:00" + "time": "2022-04-11T14:29:02+00:00" }, { "name": "laravel/tinker", @@ -2404,16 +2460,16 @@ }, { "name": "league/commonmark", - "version": "2.2.3", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "47b015bc4e50fd4438c1ffef6139a1fb65d2ab71" + "reference": "32a49eb2b38fe5e5c417ab748a45d0beaab97955" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/47b015bc4e50fd4438c1ffef6139a1fb65d2ab71", - "reference": "47b015bc4e50fd4438c1ffef6139a1fb65d2ab71", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/32a49eb2b38fe5e5c417ab748a45d0beaab97955", + "reference": "32a49eb2b38fe5e5c417ab748a45d0beaab97955", "shasum": "" }, "require": { @@ -2422,17 +2478,19 @@ "php": "^7.4 || ^8.0", "psr/event-dispatcher": "^1.0", "symfony/deprecation-contracts": "^2.1 || ^3.0", - "symfony/polyfill-php80": "^1.15" + "symfony/polyfill-php80": "^1.16" }, "require-dev": { "cebe/markdown": "^1.0", "commonmark/cmark": "0.30.0", "commonmark/commonmark.js": "0.30.0", "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", "erusev/parsedown": "^1.0", "ext-json": "*", "github/gfm": "0.29.0", "michelf/php-markdown": "^1.4", + "nyholm/psr7": "^1.5", "phpstan/phpstan": "^0.12.88 || ^1.0.0", "phpunit/phpunit": "^9.5.5", "scrutinizer/ocular": "^1.8.1", @@ -2447,7 +2505,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.3-dev" + "dev-main": "2.4-dev" } }, "autoload": { @@ -2504,7 +2562,7 @@ "type": "tidelift" } ], - "time": "2022-02-26T21:24:45+00:00" + "time": "2022-04-07T22:37:05+00:00" }, { "name": "league/config", @@ -2590,16 +2648,16 @@ }, { "name": "league/flysystem", - "version": "3.0.14", + "version": "3.0.17", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "46a5450352540e89cb8e7eb20c58b5b4aae481f6" + "reference": "29eb78cac0be0c22237c5e0f6f98234d97037d79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/46a5450352540e89cb8e7eb20c58b5b4aae481f6", - "reference": "46a5450352540e89cb8e7eb20c58b5b4aae481f6", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/29eb78cac0be0c22237c5e0f6f98234d97037d79", + "reference": "29eb78cac0be0c22237c5e0f6f98234d97037d79", "shasum": "" }, "require": { @@ -2660,7 +2718,7 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.0.14" + "source": "https://github.com/thephpleague/flysystem/tree/3.0.17" }, "funding": [ { @@ -2676,20 +2734,20 @@ "type": "tidelift" } ], - "time": "2022-04-06T18:17:37+00:00" + "time": "2022-04-14T14:57:13+00:00" }, { "name": "league/mime-type-detection", - "version": "1.9.0", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "aa70e813a6ad3d1558fc927863d47309b4c23e69" + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/aa70e813a6ad3d1558fc927863d47309b4c23e69", - "reference": "aa70e813a6ad3d1558fc927863d47309b4c23e69", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd", + "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd", "shasum": "" }, "require": { @@ -2720,7 +2778,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.9.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0" }, "funding": [ { @@ -2732,20 +2790,20 @@ "type": "tidelift" } ], - "time": "2021-11-21T11:48:40+00:00" + "time": "2022-04-17T13:12:02+00:00" }, { "name": "league/oauth1-client", - "version": "v1.10.0", + "version": "v1.10.1", "source": { "type": "git", "url": "https://github.com/thephpleague/oauth1-client.git", - "reference": "88dd16b0cff68eb9167bfc849707d2c40ad91ddc" + "reference": "d6365b901b5c287dd41f143033315e2f777e1167" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/88dd16b0cff68eb9167bfc849707d2c40ad91ddc", - "reference": "88dd16b0cff68eb9167bfc849707d2c40ad91ddc", + "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/d6365b901b5c287dd41f143033315e2f777e1167", + "reference": "d6365b901b5c287dd41f143033315e2f777e1167", "shasum": "" }, "require": { @@ -2806,22 +2864,22 @@ ], "support": { "issues": "https://github.com/thephpleague/oauth1-client/issues", - "source": "https://github.com/thephpleague/oauth1-client/tree/v1.10.0" + "source": "https://github.com/thephpleague/oauth1-client/tree/v1.10.1" }, - "time": "2021-08-15T23:05:49+00:00" + "time": "2022-04-15T14:02:14+00:00" }, { "name": "maatwebsite/excel", - "version": "3.1.38", + "version": "3.1.39", "source": { "type": "git", "url": "https://github.com/SpartnerNL/Laravel-Excel.git", - "reference": "dff132ce4d30b19863f4e84de1613fca99604992" + "reference": "5165334de44c6f7788a5818a1d019aa71a43e092" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/dff132ce4d30b19863f4e84de1613fca99604992", - "reference": "dff132ce4d30b19863f4e84de1613fca99604992", + "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/5165334de44c6f7788a5818a1d019aa71a43e092", + "reference": "5165334de44c6f7788a5818a1d019aa71a43e092", "shasum": "" }, "require": { @@ -2874,7 +2932,7 @@ ], "support": { "issues": "https://github.com/SpartnerNL/Laravel-Excel/issues", - "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.38" + "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.39" }, "funding": [ { @@ -2886,7 +2944,7 @@ "type": "github" } ], - "time": "2022-03-24T16:00:29+00:00" + "time": "2022-04-23T11:44:18+00:00" }, { "name": "maennchen/zipstream-php", @@ -3118,16 +3176,16 @@ }, { "name": "monolog/monolog", - "version": "2.4.0", + "version": "2.5.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "d7fd7450628561ba697b7097d86db72662f54aef" + "reference": "4192345e260f1d51b365536199744b987e160edc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/d7fd7450628561ba697b7097d86db72662f54aef", - "reference": "d7fd7450628561ba697b7097d86db72662f54aef", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/4192345e260f1d51b365536199744b987e160edc", + "reference": "4192345e260f1d51b365536199744b987e160edc", "shasum": "" }, "require": { @@ -3201,7 +3259,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/2.4.0" + "source": "https://github.com/Seldaek/monolog/tree/2.5.0" }, "funding": [ { @@ -3213,7 +3271,7 @@ "type": "tidelift" } ], - "time": "2022-03-14T12:44:37+00:00" + "time": "2022-04-08T15:43:54+00:00" }, { "name": "myclabs/php-enum", @@ -3856,16 +3914,16 @@ }, { "name": "phpoffice/phpspreadsheet", - "version": "1.22.0", + "version": "1.23.0", "source": { "type": "git", "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "3a9e29b4f386a08a151a33578e80ef1747037a48" + "reference": "21e4cf62699eebf007db28775f7d1554e612ed9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/3a9e29b4f386a08a151a33578e80ef1747037a48", - "reference": "3a9e29b4f386a08a151a33578e80ef1747037a48", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/21e4cf62699eebf007db28775f7d1554e612ed9e", + "reference": "21e4cf62699eebf007db28775f7d1554e612ed9e", "shasum": "" }, "require": { @@ -3889,7 +3947,7 @@ "php": "^7.3 || ^8.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0", - "psr/simple-cache": "^1.0" + "psr/simple-cache": "^1.0 || ^2.0" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "dev-master", @@ -3954,9 +4012,9 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.22.0" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.23.0" }, - "time": "2022-02-18T12:57:07+00:00" + "time": "2022-04-24T13:53:10+00:00" }, { "name": "phpoption/phpoption", @@ -4140,16 +4198,16 @@ }, { "name": "psr/cache", - "version": "2.0.0", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/cache.git", - "reference": "213f9dbc5b9bfbc4f8db86d2838dc968752ce13b" + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/213f9dbc5b9bfbc4f8db86d2838dc968752ce13b", - "reference": "213f9dbc5b9bfbc4f8db86d2838dc968752ce13b", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", "shasum": "" }, "require": { @@ -4183,9 +4241,9 @@ "psr-6" ], "support": { - "source": "https://github.com/php-fig/cache/tree/2.0.0" + "source": "https://github.com/php-fig/cache/tree/3.0.0" }, - "time": "2021-02-03T23:23:37+00:00" + "time": "2021-02-03T23:26:27+00:00" }, { "name": "psr/container", @@ -4502,25 +4560,25 @@ }, { "name": "psr/simple-cache", - "version": "1.0.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + "reference": "8707bf3cea6f710bf6ef05491234e3ab06f6432a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/8707bf3cea6f710bf6ef05491234e3ab06f6432a", + "reference": "8707bf3cea6f710bf6ef05491234e3ab06f6432a", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -4535,7 +4593,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common interfaces for simple caching", @@ -4547,9 +4605,9 @@ "simple-cache" ], "support": { - "source": "https://github.com/php-fig/simple-cache/tree/master" + "source": "https://github.com/php-fig/simple-cache/tree/2.0.0" }, - "time": "2017-10-23T01:57:42+00:00" + "time": "2021-10-29T13:22:09+00:00" }, { "name": "psy/psysh", @@ -5040,16 +5098,16 @@ }, { "name": "spatie/laravel-model-states", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-model-states.git", - "reference": "7b31a63c0bd8b33d7dc5e12e6b16d2535d9b31a8" + "reference": "bfa12486558952eca4d6c81d4dd803b83f065297" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-model-states/zipball/7b31a63c0bd8b33d7dc5e12e6b16d2535d9b31a8", - "reference": "7b31a63c0bd8b33d7dc5e12e6b16d2535d9b31a8", + "url": "https://api.github.com/repos/spatie/laravel-model-states/zipball/bfa12486558952eca4d6c81d4dd803b83f065297", + "reference": "bfa12486558952eca4d6c81d4dd803b83f065297", "shasum": "" }, "require": { @@ -5098,7 +5156,7 @@ "state" ], "support": { - "source": "https://github.com/spatie/laravel-model-states/tree/2.2.0" + "source": "https://github.com/spatie/laravel-model-states/tree/2.3.0" }, "funding": [ { @@ -5110,7 +5168,7 @@ "type": "github" } ], - "time": "2022-03-03T11:22:16+00:00" + "time": "2022-04-21T12:09:37+00:00" }, { "name": "spatie/laravel-package-tools", @@ -5171,6 +5229,71 @@ ], "time": "2022-03-15T20:01:36+00:00" }, + { + "name": "spatie/laravel-slack-slash-command", + "version": "1.11.3", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-slack-slash-command.git", + "reference": "8e507653054ff08581b28d6ddf5bb4ce8c2e2335" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-slack-slash-command/zipball/8e507653054ff08581b28d6ddf5bb4ce8c2e2335", + "reference": "8e507653054ff08581b28d6ddf5bb4ce8c2e2335", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.0", + "illuminate/config": "~5.8.0|^6.0|^7.0|^8.0|^9.0", + "illuminate/queue": "~5.8.0|^6.0|^7.0|^8.0|^9.0", + "illuminate/routing": "~5.8.0|^6.0|^7.0|^8.0|^9.0", + "illuminate/support": "~5.8.0|^6.0|^7.0|^8.0|^9.0", + "laravel/helpers": "^1.0", + "php": "^7.3|^8.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "~3.8.0|^4.0|^5.0|^6.0|^7.0", + "phpunit/phpunit": "^9.2" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\SlashCommand\\SlashCommandServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Spatie\\SlashCommand\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Make a Laravel app respond to a slash command from Slack", + "homepage": "https://github.com/spatie/laravel-slack-slash-command", + "keywords": [ + "laravel-slack", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-slack-slash-command/issues", + "source": "https://github.com/spatie/laravel-slack-slash-command/tree/1.11.3" + }, + "time": "2022-02-09T07:58:01+00:00" + }, { "name": "symfony/console", "version": "v6.0.7", @@ -7529,21 +7652,21 @@ "packages-dev": [ { "name": "blumilksoftware/codestyle", - "version": "v1.0.1", + "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/blumilksoftware/codestyle.git", - "reference": "e86ebcd5175bc435d9c8d4f83bf201a6f5a9fc60" + "reference": "3f2248859562afe7d7b2b16aa25e83dc73236fcc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/blumilksoftware/codestyle/zipball/e86ebcd5175bc435d9c8d4f83bf201a6f5a9fc60", - "reference": "e86ebcd5175bc435d9c8d4f83bf201a6f5a9fc60", + "url": "https://api.github.com/repos/blumilksoftware/codestyle/zipball/3f2248859562afe7d7b2b16aa25e83dc73236fcc", + "reference": "3f2248859562afe7d7b2b16aa25e83dc73236fcc", "shasum": "" }, "require": { "friendsofphp/php-cs-fixer": "^3.8.0", - "kubawerlos/php-cs-fixer-custom-fixers": "^3.7", + "kubawerlos/php-cs-fixer-custom-fixers": "^3.10.1", "php": "^8.0" }, "require-dev": { @@ -7551,6 +7674,9 @@ "phpunit/phpunit": "^9.5", "symfony/console": "^6.0" }, + "bin": [ + "bin/codestyle" + ], "type": "library", "autoload": { "psr-4": { @@ -7570,9 +7696,9 @@ "description": "Blumilk codestyle configurator", "support": { "issues": "https://github.com/blumilksoftware/codestyle/issues", - "source": "https://github.com/blumilksoftware/codestyle/tree/v1.0.1" + "source": "https://github.com/blumilksoftware/codestyle/tree/v1.1.0" }, - "time": "2022-04-04T06:25:21+00:00" + "time": "2022-04-25T06:04:51+00:00" }, { "name": "composer/pcre", @@ -8262,16 +8388,16 @@ }, { "name": "laravel/dusk", - "version": "v6.22.3", + "version": "v6.23.0", "source": { "type": "git", "url": "https://github.com/laravel/dusk.git", - "reference": "f42844d5a7eace45d199d276bb2bc23fec788256" + "reference": "98901d49176977c96330fd8c2ca5460eee50a246" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/dusk/zipball/f42844d5a7eace45d199d276bb2bc23fec788256", - "reference": "f42844d5a7eace45d199d276bb2bc23fec788256", + "url": "https://api.github.com/repos/laravel/dusk/zipball/98901d49176977c96330fd8c2ca5460eee50a246", + "reference": "98901d49176977c96330fd8c2ca5460eee50a246", "shasum": "" }, "require": { @@ -8329,9 +8455,9 @@ ], "support": { "issues": "https://github.com/laravel/dusk/issues", - "source": "https://github.com/laravel/dusk/tree/v6.22.3" + "source": "https://github.com/laravel/dusk/tree/v6.23.0" }, - "time": "2022-04-04T15:12:34+00:00" + "time": "2022-04-11T18:55:12+00:00" }, { "name": "mockery/mockery", @@ -10519,16 +10645,16 @@ }, { "name": "spatie/ignition", - "version": "1.2.7", + "version": "1.2.9", "source": { "type": "git", "url": "https://github.com/spatie/ignition.git", - "reference": "2f059cf42b48f7c522efbba1c05ad59fc2c1a3f2" + "reference": "db25202fab2d5c14613b8914a1bb374998bbf870" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ignition/zipball/2f059cf42b48f7c522efbba1c05ad59fc2c1a3f2", - "reference": "2f059cf42b48f7c522efbba1c05ad59fc2c1a3f2", + "url": "https://api.github.com/repos/spatie/ignition/zipball/db25202fab2d5c14613b8914a1bb374998bbf870", + "reference": "db25202fab2d5c14613b8914a1bb374998bbf870", "shasum": "" }, "require": { @@ -10585,20 +10711,20 @@ "type": "github" } ], - "time": "2022-03-29T08:48:34+00:00" + "time": "2022-04-23T20:37:21+00:00" }, { "name": "spatie/laravel-ignition", - "version": "1.2.0", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "2b54c8c66f2d280f25e15064ebe3d5e3eda19820" + "reference": "924d1ae878874ad0bb49f63b69a9af759a34ee78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/2b54c8c66f2d280f25e15064ebe3d5e3eda19820", - "reference": "2b54c8c66f2d280f25e15064ebe3d5e3eda19820", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/924d1ae878874ad0bb49f63b69a9af759a34ee78", + "reference": "924d1ae878874ad0bb49f63b69a9af759a34ee78", "shasum": "" }, "require": { @@ -10675,7 +10801,7 @@ "type": "github" } ], - "time": "2022-04-01T21:01:58+00:00" + "time": "2022-04-14T18:04:51+00:00" }, { "name": "symfony/filesystem", @@ -10931,5 +11057,5 @@ "ext-redis": "*" }, "platform-dev": [], - "plugin-api-version": "2.1.0" + "plugin-api-version": "2.2.0" } diff --git a/config/app.php b/config/app.php index a5d12a9..6ef5b69 100644 --- a/config/app.php +++ b/config/app.php @@ -37,12 +37,12 @@ return [ Illuminate\Translation\TranslationServiceProvider::class, Illuminate\Validation\ValidationServiceProvider::class, Illuminate\View\ViewServiceProvider::class, + Barryvdh\DomPDF\ServiceProvider::class, Toby\Architecture\Providers\AppServiceProvider::class, Toby\Architecture\Providers\AuthServiceProvider::class, Toby\Architecture\Providers\EventServiceProvider::class, Toby\Architecture\Providers\RouteServiceProvider::class, Toby\Architecture\Providers\TelescopeServiceProvider::class, Toby\Architecture\Providers\ObserverServiceProvider::class, - Barryvdh\DomPDF\ServiceProvider::class, ], ]; diff --git a/config/laravel-slack-slash-command.php b/config/laravel-slack-slash-command.php new file mode 100644 index 0000000..b4f8cfb --- /dev/null +++ b/config/laravel-slack-slash-command.php @@ -0,0 +1,24 @@ + env("SLACK_SIGNING_SECRET"), + "handlers" => [ + TakeKeysFrom::class, + GiveKeysTo::class, + KeyList::class, + HomeOffice::class, + DailySummary::class, + Help::class, + CatchAll::class, + ], +]; diff --git a/config/services.php b/config/services.php index e535c9e..8940ddd 100644 --- a/config/services.php +++ b/config/services.php @@ -8,4 +8,9 @@ return [ "client_secret" => env("GOOGLE_CLIENT_SECRET"), "redirect" => env("GOOGLE_REDIRECT"), ], + "slack" => [ + "url" => "https://slack.com/api", + "client_token" => env("SLACK_CLIENT_TOKEN"), + "default_channel" => env("SLACK_DEFAULT_CHANNEL"), + ], ]; diff --git a/database/factories/ProfileFactory.php b/database/factories/ProfileFactory.php index 706df10..ba96c86 100644 --- a/database/factories/ProfileFactory.php +++ b/database/factories/ProfileFactory.php @@ -23,6 +23,7 @@ class ProfileFactory extends Factory "employment_form" => $this->faker->randomElement(EmploymentForm::cases()), "position" => $this->faker->jobTitle(), "employment_date" => Carbon::createFromInterface($this->faker->dateTimeBetween("2020-10-27"))->toDateString(), + "birthday" => Carbon::createFromInterface($this->faker->dateTimeBetween("1970-01-01", "1998-01-01"))->toDateString(), ]; } } diff --git a/database/migrations/2022_04_21_101027_add_slack_id_and_birthday_columns_in_profiles_table.php b/database/migrations/2022_04_21_101027_add_slack_id_and_birthday_columns_in_profiles_table.php new file mode 100644 index 0000000..893b4ef --- /dev/null +++ b/database/migrations/2022_04_21_101027_add_slack_id_and_birthday_columns_in_profiles_table.php @@ -0,0 +1,25 @@ +string("slack_id")->nullable(); + $table->date("birthday")->nullable(); + }); + } + + public function down(): void + { + Schema::table("profiles", function (Blueprint $table): void { + $table->dropColumn("slack_id"); + $table->dropColumn("birthday"); + }); + } +}; diff --git a/resources/js/Composables/vacationTypeInfo.js b/resources/js/Composables/vacationTypeInfo.js index 3ed7591..65ec983 100644 --- a/resources/js/Composables/vacationTypeInfo.js +++ b/resources/js/Composables/vacationTypeInfo.js @@ -8,7 +8,7 @@ import HandHeartOutlineIcon from 'vue-material-design-icons/HandHeartOutline.vue import CalendarCheckIcon from 'vue-material-design-icons/CalendarCheck.vue' import MedicalBagIcon from 'vue-material-design-icons/MedicalBag.vue' import CalendarRemoveIcon from 'vue-material-design-icons/CalendarRemove.vue' -import LaptopIcon from 'vue-material-design-icons/Laptop.vue' +import HomeCityIcon from 'vue-material-design-icons/HomeCity.vue' const types = [ { @@ -43,8 +43,8 @@ const types = [ text: 'Urlop szkoleniowy', value: 'training_vacation', icon: HumanMaleBoardIcon, - color: 'text-blumilk-500', - border: 'border-blumilk-500', + color: 'text-indigo-500', + border: 'border-indigo-500', }, { text: 'Urlop bezpłatny', @@ -84,9 +84,9 @@ const types = [ { text: 'Praca zdalna', value: 'home_office', - icon: LaptopIcon, - color: 'text-fuchsia-500', - border: 'border-fuchsia-500', + icon: HomeCityIcon, + color: 'text-blumilk-500', + border: 'border-blumilk-500', }, ] diff --git a/resources/js/Pages/Users/Create.vue b/resources/js/Pages/Users/Create.vue index 4959b47..5caadf9 100644 --- a/resources/js/Pages/Users/Create.vue +++ b/resources/js/Pages/Users/Create.vue @@ -234,6 +234,52 @@
++ {{ form.errors.slackId }} +
++ {{ form.errors.birthday }} +
++ {{ form.errors.birthday }} +
++ {{ form.errors.slackId }} +
+