#28 - holidays management
This commit is contained in:
		
							
								
								
									
										34
									
								
								app/Helpers/PolishHolidaysRetriever.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								app/Helpers/PolishHolidaysRetriever.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Helpers; | ||||
|  | ||||
| use Illuminate\Support\Carbon; | ||||
| use Illuminate\Support\Collection; | ||||
| use Toby\Models\YearPeriod; | ||||
| use Yasumi\Holiday; | ||||
| use Yasumi\Yasumi; | ||||
|  | ||||
| class PolishHolidaysRetriever | ||||
| { | ||||
|     protected const PROVIDER_KEY = "Poland"; | ||||
|     protected const LANG_KEY = "pl"; | ||||
|  | ||||
|     public function getForYearPeriod(YearPeriod $yearPeriod): Collection | ||||
|     { | ||||
|         $polishProvider = Yasumi::create(static::PROVIDER_KEY, $yearPeriod->year); | ||||
|  | ||||
|         $holidays = $polishProvider->getHolidays(); | ||||
|  | ||||
|         return $this->prepareHolidays($holidays); | ||||
|     } | ||||
|  | ||||
|     protected function prepareHolidays(array $holidays): Collection | ||||
|     { | ||||
|         return collect($holidays)->map(fn(Holiday $holiday) => [ | ||||
|             "name" => $holiday->getName([static::LANG_KEY]), | ||||
|             "date" => Carbon::createFromTimestamp($holiday->getTimestamp()), | ||||
|         ])->values(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										65
									
								
								app/Http/Controllers/HolidayController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								app/Http/Controllers/HolidayController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\RedirectResponse; | ||||
| use Inertia\Response; | ||||
| use Toby\Http\Requests\HolidayRequest; | ||||
| use Toby\Http\Resources\HolidayFormDataResource; | ||||
| use Toby\Http\Resources\HolidayResource; | ||||
| use Toby\Models\Holiday; | ||||
|  | ||||
| class HolidayController extends Controller | ||||
| { | ||||
|     public function index(): Response | ||||
|     { | ||||
|         $holidays = Holiday::query() | ||||
|             ->orderBy("date") | ||||
|             ->get(); | ||||
|  | ||||
|         return inertia("Holidays/Index", [ | ||||
|             "holidays" => HolidayResource::collection($holidays), | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function create(): Response | ||||
|     { | ||||
|         return inertia("Holidays/Create"); | ||||
|     } | ||||
|  | ||||
|     public function store(HolidayRequest $request): RedirectResponse | ||||
|     { | ||||
|         Holiday::query()->create($request->data()); | ||||
|  | ||||
|         return redirect() | ||||
|             ->route("holidays.index") | ||||
|             ->with("success", __("Holiday has been created")); | ||||
|     } | ||||
|  | ||||
|     public function edit(Holiday $holiday): Response | ||||
|     { | ||||
|         return inertia("Holidays/Edit", [ | ||||
|             "holiday" => new HolidayFormDataResource($holiday), | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function update(HolidayRequest $request, Holiday $holiday): RedirectResponse | ||||
|     { | ||||
|         $holiday->update($request->data()); | ||||
|  | ||||
|         return redirect() | ||||
|             ->route("holidays.index") | ||||
|             ->with("success", __("Holiday has been updated")); | ||||
|     } | ||||
|  | ||||
|     public function destroy(Holiday $holiday): RedirectResponse | ||||
|     { | ||||
|         $holiday->delete(); | ||||
|  | ||||
|         return redirect() | ||||
|             ->route("holidays.index") | ||||
|             ->with("success", __("Holiday has been deleted")); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										32
									
								
								app/Http/Requests/HolidayRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								app/Http/Requests/HolidayRequest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Http\Requests; | ||||
|  | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Support\Carbon; | ||||
| use Toby\Models\YearPeriod; | ||||
| use Toby\Rules\YearPeriodExists; | ||||
|  | ||||
| class HolidayRequest extends FormRequest | ||||
| { | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             "name" => ["required", "min:3", "max:150"], | ||||
|             "date" => ["required", "date", new YearPeriodExists()], | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function data(): array | ||||
|     { | ||||
|         $date = $this->get("date"); | ||||
|  | ||||
|         return [ | ||||
|             "name" => $this->get("name"), | ||||
|             "date" => $date, | ||||
|             "year_period_id" => YearPeriod::findByYear(Carbon::create($date)->year)->id, | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										21
									
								
								app/Http/Resources/HolidayFormDataResource.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								app/Http/Resources/HolidayFormDataResource.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Http\Resources; | ||||
|  | ||||
| use Illuminate\Http\Resources\Json\JsonResource; | ||||
|  | ||||
| class HolidayFormDataResource extends JsonResource | ||||
| { | ||||
|     public static $wrap = null; | ||||
|  | ||||
|     public function toArray($request): array | ||||
|     { | ||||
|         return [ | ||||
|             "id" => $this->id, | ||||
|             "name" => $this->name, | ||||
|             "date" => $this->date->toDateString(), | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										22
									
								
								app/Http/Resources/HolidayResource.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								app/Http/Resources/HolidayResource.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Http\Resources; | ||||
|  | ||||
| use Illuminate\Http\Resources\Json\JsonResource; | ||||
|  | ||||
| class HolidayResource extends JsonResource | ||||
| { | ||||
|     public static $wrap = null; | ||||
|  | ||||
|     public function toArray($request): array | ||||
|     { | ||||
|         return [ | ||||
|             "id" => $this->id, | ||||
|             "name" => $this->name, | ||||
|             "displayDate" => $this->date->toDisplayString(), | ||||
|             "dayOfWeek" => $this->date->dayName, | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										32
									
								
								app/Models/Holiday.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								app/Models/Holiday.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Models; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||||
| use Illuminate\Support\Carbon; | ||||
|  | ||||
| /** | ||||
|  * @property int $id | ||||
|  * @property string $name | ||||
|  * @property Carbon $date | ||||
|  * @property YearPeriod $yearPeriod | ||||
|  */ | ||||
| class Holiday extends Model | ||||
| { | ||||
|     use HasFactory; | ||||
|  | ||||
|     protected $guarded = []; | ||||
|  | ||||
|     protected $casts = [ | ||||
|         "date" => "date", | ||||
|     ]; | ||||
|  | ||||
|     public function yearPeriod(): BelongsTo | ||||
|     { | ||||
|         return $this->belongsTo(YearPeriod::class); | ||||
|     } | ||||
| } | ||||
| @@ -4,29 +4,33 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Models; | ||||
|  | ||||
| use Carbon\Carbon; | ||||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Database\Eloquent\Relations\HasMany; | ||||
| use Illuminate\Support\Carbon; | ||||
| use Illuminate\Support\Collection; | ||||
|  | ||||
| /** | ||||
|  * @property int $id | ||||
|  * @property int $year | ||||
|  * @property Collection $vacationLimits | ||||
|  * @property Collection $holidays | ||||
|  */ | ||||
| class YearPeriod extends Model | ||||
| { | ||||
|     use HasFactory; | ||||
|  | ||||
|     protected $fillable = [ | ||||
|         "year", | ||||
|     ]; | ||||
|     protected $guarded = []; | ||||
|  | ||||
|     public static function current(): ?static | ||||
|     { | ||||
|         static::findByYear(Carbon::now()->year); | ||||
|     } | ||||
|  | ||||
|     public static function findByYear(int $year): ?static | ||||
|     { | ||||
|         /** @var YearPeriod $year */ | ||||
|         $year = static::query()->where("year", Carbon::now()->year)->first(); | ||||
|         $year = static::query()->where("year", $year)->first(); | ||||
|  | ||||
|         return $year; | ||||
|     } | ||||
| @@ -35,4 +39,9 @@ class YearPeriod extends Model | ||||
|     { | ||||
|         return $this->hasMany(VacationLimit::class); | ||||
|     } | ||||
|  | ||||
|     public function holidays(): HasMany | ||||
|     { | ||||
|         return $this->hasMany(Holiday::class); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Observers; | ||||
|  | ||||
| use Toby\Helpers\PolishHolidaysRetriever; | ||||
| use Toby\Helpers\UserAvatarGenerator; | ||||
| use Toby\Models\User; | ||||
| use Toby\Models\YearPeriod; | ||||
| @@ -12,10 +13,17 @@ class YearPeriodObserver | ||||
| { | ||||
|     public function __construct( | ||||
|         protected UserAvatarGenerator $generator, | ||||
|         protected PolishHolidaysRetriever $polishHolidaysRetriever, | ||||
|     ) { | ||||
|     } | ||||
|  | ||||
|     public function created(YearPeriod $yearPeriod): void | ||||
|     { | ||||
|         $this->createVacationLimitsFor($yearPeriod); | ||||
|         $this->createHolidaysFor($yearPeriod); | ||||
|     } | ||||
|  | ||||
|     protected function createVacationLimitsFor(YearPeriod $yearPeriod): void | ||||
|     { | ||||
|         $users = User::all(); | ||||
|  | ||||
| @@ -25,4 +33,16 @@ class YearPeriodObserver | ||||
|             ]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     protected function createHolidaysFor(YearPeriod $yearPeriod): void | ||||
|     { | ||||
|         $holidays = $this->polishHolidaysRetriever->getForYearPeriod($yearPeriod); | ||||
|  | ||||
|         foreach ($holidays as $holiday) { | ||||
|             $yearPeriod->holidays()->create([ | ||||
|                 "name" => $holiday["name"], | ||||
|                 "date" => $holiday["date"], | ||||
|             ]); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -6,6 +6,7 @@ namespace Toby\Providers; | ||||
|  | ||||
| use Illuminate\Support\Carbon; | ||||
| use Illuminate\Support\ServiceProvider; | ||||
| use Toby\Models\Holiday; | ||||
| use Toby\Models\VacationLimit; | ||||
| use Toby\Scopes\SelectedYearPeriodScope; | ||||
|  | ||||
| @@ -15,6 +16,9 @@ class AppServiceProvider extends ServiceProvider | ||||
|     { | ||||
|         Carbon::macro("toDisplayString", fn() => $this->translatedFormat("j F Y")); | ||||
|  | ||||
|         VacationLimit::addGlobalScope($this->app->make(SelectedYearPeriodScope::class)); | ||||
|         $selectedYearPeriodScope = $this->app->make(SelectedYearPeriodScope::class); | ||||
|  | ||||
|         VacationLimit::addGlobalScope($selectedYearPeriodScope); | ||||
|         Holiday::addGlobalScope($selectedYearPeriodScope); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										24
									
								
								app/Rules/YearPeriodExists.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/Rules/YearPeriodExists.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Rules; | ||||
|  | ||||
| use Illuminate\Contracts\Validation\Rule; | ||||
| use Illuminate\Support\Carbon; | ||||
| use Toby\Models\YearPeriod; | ||||
|  | ||||
| class YearPeriodExists implements Rule | ||||
| { | ||||
|     public function passes($attribute, $value): bool | ||||
|     { | ||||
|         $yearPeriod = YearPeriod::findByYear(Carbon::create($value)->year); | ||||
|  | ||||
|         return $yearPeriod !== null; | ||||
|     } | ||||
|  | ||||
|     public function message(): string | ||||
|     { | ||||
|         return "The year period for given year doesn't exist"; | ||||
|     } | ||||
| } | ||||
| @@ -7,6 +7,7 @@ | ||||
|     "require": { | ||||
|         "php": "^8.1", | ||||
|         "ext-pdo": "*", | ||||
|         "azuyalabs/yasumi": "^2.4", | ||||
|         "fruitcake/laravel-cors": "^2.0", | ||||
|         "guzzlehttp/guzzle": "^7.0.1", | ||||
|         "inertiajs/inertia-laravel": "^0.5.1", | ||||
|   | ||||
							
								
								
									
										75
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										75
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							| @@ -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": "3412dd2a403927b829237ae4db36351a", | ||||
|     "content-hash": "e3c6ffae4c01db02d0471c52d2370b79", | ||||
|     "packages": [ | ||||
|         { | ||||
|             "name": "asm89/stack-cors", | ||||
| @@ -62,6 +62,79 @@ | ||||
|             }, | ||||
|             "time": "2022-01-18T09:12:03+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "azuyalabs/yasumi", | ||||
|             "version": "2.4.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/azuyalabs/yasumi.git", | ||||
|                 "reference": "083a0d0579fee17e68d688d463bc01098ac2691f" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/azuyalabs/yasumi/zipball/083a0d0579fee17e68d688d463bc01098ac2691f", | ||||
|                 "reference": "083a0d0579fee17e68d688d463bc01098ac2691f", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
|                 "ext-json": "*", | ||||
|                 "php": ">=7.3" | ||||
|             }, | ||||
|             "require-dev": { | ||||
|                 "friendsofphp/php-cs-fixer": "^2.16", | ||||
|                 "infection/infection": "^0.17 | ^0.22", | ||||
|                 "mikey179/vfsstream": "^1.6", | ||||
|                 "phan/phan": "^4.0", | ||||
|                 "phpstan/phpstan": "^0.12.66", | ||||
|                 "phpunit/phpunit": "^8.5 | ^9.4", | ||||
|                 "vimeo/psalm": "^4" | ||||
|             }, | ||||
|             "suggest": { | ||||
|                 "ext-calendar": "For calculating the date of Easter" | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "autoload": { | ||||
|                 "psr-4": { | ||||
|                     "Yasumi\\": "src/Yasumi/" | ||||
|                 } | ||||
|             }, | ||||
|             "notification-url": "https://packagist.org/downloads/", | ||||
|             "license": [ | ||||
|                 "MIT" | ||||
|             ], | ||||
|             "authors": [ | ||||
|                 { | ||||
|                     "name": "Sacha Telgenhof", | ||||
|                     "email": "me@sachatelgenhof.com", | ||||
|                     "role": "Maintainer" | ||||
|                 } | ||||
|             ], | ||||
|             "description": "The easy PHP Library for calculating holidays.", | ||||
|             "homepage": "https://www.yasumi.dev", | ||||
|             "keywords": [ | ||||
|                 "Bank", | ||||
|                 "calculation", | ||||
|                 "calendar", | ||||
|                 "celebration", | ||||
|                 "date", | ||||
|                 "holiday", | ||||
|                 "holidays", | ||||
|                 "national", | ||||
|                 "time" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "docs": "https://www.yasumi.dev", | ||||
|                 "issues": "https://github.com/azuyalabs/yasumi/issues", | ||||
|                 "source": "https://github.com/azuyalabs/yasumi" | ||||
|             }, | ||||
|             "funding": [ | ||||
|                 { | ||||
|                     "url": "https://www.buymeacoffee.com/sachatelgenhof", | ||||
|                     "type": "other" | ||||
|                 } | ||||
|             ], | ||||
|             "time": "2021-05-09T09:03:34+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "brick/math", | ||||
|             "version": "0.9.3", | ||||
|   | ||||
							
								
								
									
										21
									
								
								database/factories/HolidayFactory.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								database/factories/HolidayFactory.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Database\Factories; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Factories\Factory; | ||||
| use Illuminate\Support\Carbon; | ||||
|  | ||||
| class HolidayFactory extends Factory | ||||
| { | ||||
|     public function definition(): array | ||||
|     { | ||||
|         $now = Carbon::now(); | ||||
|  | ||||
|         return [ | ||||
|             "name" => $this->faker->word, | ||||
|             "date" => $this->faker->dateTimeBetween($now->startOfYear(), $now->endOfYear()), | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,26 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| use Illuminate\Database\Migrations\Migration; | ||||
| use Illuminate\Database\Schema\Blueprint; | ||||
| use Illuminate\Support\Facades\Schema; | ||||
| use Toby\Models\YearPeriod; | ||||
|  | ||||
| return new class() extends Migration { | ||||
|     public function up(): void | ||||
|     { | ||||
|         Schema::create("holidays", function (Blueprint $table): void { | ||||
|             $table->id(); | ||||
|             $table->foreignIdFor(YearPeriod::class)->constrained()->cascadeOnDelete(); | ||||
|             $table->string("name"); | ||||
|             $table->date("date")->unique(); | ||||
|             $table->timestamps(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public function down(): void | ||||
|     { | ||||
|         Schema::dropIfExists("holidays"); | ||||
|     } | ||||
| }; | ||||
| @@ -7,6 +7,7 @@ namespace Database\Seeders; | ||||
| use Illuminate\Database\Seeder; | ||||
| use Illuminate\Support\Carbon; | ||||
| use Illuminate\Support\Collection; | ||||
| use Toby\Helpers\PolishHolidaysRetriever; | ||||
| use Toby\Helpers\UserAvatarGenerator; | ||||
| use Toby\Models\User; | ||||
| use Toby\Models\VacationLimit; | ||||
| @@ -54,6 +55,16 @@ class DatabaseSeeder extends Seeder | ||||
|                         ->create(); | ||||
|                 } | ||||
|             }) | ||||
|             ->afterCreating(function (YearPeriod $yearPeriod): void { | ||||
|                 $polishHolidaysRetriever = new PolishHolidaysRetriever(); | ||||
|  | ||||
|                 foreach ($polishHolidaysRetriever->getForYearPeriod($yearPeriod) as $holiday) { | ||||
|                     $yearPeriod->holidays()->create([ | ||||
|                         "name" => $holiday["name"], | ||||
|                         "date" => $holiday["date"], | ||||
|                     ]); | ||||
|                 } | ||||
|             }) | ||||
|             ->create(); | ||||
|     } | ||||
|  | ||||
|   | ||||
							
								
								
									
										106
									
								
								resources/js/Pages/Holidays/Create.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								resources/js/Pages/Holidays/Create.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| <template> | ||||
|     <InertiaHead title="Dodaj dzień wolny" /> | ||||
|     <div class="bg-white sm:rounded-lg shadow-md"> | ||||
|         <div class="p-4 sm:px-6"> | ||||
|             <h2 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 Dodaj dzień wolny | ||||
|             </h2> | ||||
|             <p class="mt-1 text-sm text-gray-500"> | ||||
|                 Użytkownik nie będzie miał możliwości wzięcia urlopu w dzień wolny. | ||||
|             </p> | ||||
|         </div> | ||||
|         <form | ||||
|             class="border-t border-gray-200 px-6" | ||||
|             @submit.prevent="createHoliday" | ||||
|         > | ||||
|             <div class="sm:grid sm:grid-cols-3 py-4 items-center"> | ||||
|                 <label | ||||
|                     for="name" | ||||
|                     class="block text-sm font-medium text-gray-700 sm:mt-px" | ||||
|                 > | ||||
|                     Nazwa | ||||
|                 </label> | ||||
|                 <div class="mt-1 sm:mt-0 sm:col-span-2"> | ||||
|                     <input | ||||
|                         id="name" | ||||
|                         v-model="form.name" | ||||
|                         type="text" | ||||
|                         class="block w-full max-w-lg shadow-sm rounded-md sm:text-sm" | ||||
|                         :class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.name, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.name }" | ||||
|                     > | ||||
|                     <p | ||||
|                         v-if="form.errors.name" | ||||
|                         class="mt-2 text-sm text-red-600" | ||||
|                     > | ||||
|                         {{ form.errors.name }} | ||||
|                     </p> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="sm:grid sm:grid-cols-3 py-4 items-center"> | ||||
|                 <label | ||||
|                     for="date" | ||||
|                     class="block text-sm font-medium text-gray-700 sm:mt-px" | ||||
|                 > | ||||
|                     Data | ||||
|                 </label> | ||||
|                 <div class="mt-1 sm:mt-0 sm:col-span-2"> | ||||
|                     <FlatPickr | ||||
|                         id="date" | ||||
|                         v-model="form.date" | ||||
|                         placeholder="Wybierz datę" | ||||
|                         class="block w-full max-w-lg shadow-sm rounded-md sm:text-sm" | ||||
|                         :class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.date, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.date }" | ||||
|                     /> | ||||
|                     <p | ||||
|                         v-if="form.errors.date" | ||||
|                         class="mt-2 text-sm text-red-600" | ||||
|                     > | ||||
|                         {{ form.errors.date }} | ||||
|                     </p> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="flex justify-end py-3"> | ||||
|                 <div class="space-x-3"> | ||||
|                     <InertiaLink | ||||
|                         href="/holidays" | ||||
|                         class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500" | ||||
|                     > | ||||
|                         Anuluj | ||||
|                     </InertiaLink> | ||||
|                     <button | ||||
|                         type="submit" | ||||
|                         :disabled="form.processing" | ||||
|                         class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blumilk-600 hover:bg-blumilk-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500" | ||||
|                     > | ||||
|                         Zapisz | ||||
|                     </button> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </form> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { useForm } from '@inertiajs/inertia-vue3'; | ||||
| import FlatPickr from 'vue-flatpickr-component'; | ||||
|  | ||||
| export default { | ||||
|     name: 'HolidayCreate', | ||||
|     components: { | ||||
|         FlatPickr, | ||||
|     }, | ||||
|     setup() { | ||||
|         const form = useForm({ | ||||
|             name: null, | ||||
|             date: new Date(), | ||||
|         }); | ||||
|  | ||||
|         return { form }; | ||||
|     }, | ||||
|     methods: { | ||||
|         createHoliday() { | ||||
|             this.form.post('/holidays'); | ||||
|         }, | ||||
|     }, | ||||
| }; | ||||
| </script> | ||||
							
								
								
									
										112
									
								
								resources/js/Pages/Holidays/Edit.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								resources/js/Pages/Holidays/Edit.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,112 @@ | ||||
| <template> | ||||
|     <InertiaHead title="Edytuj dzień wolny" /> | ||||
|     <div class="bg-white sm:rounded-lg shadow-md"> | ||||
|         <div class="p-4 sm:px-6"> | ||||
|             <h2 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                 Edytuj dzień wolny | ||||
|             </h2> | ||||
|             <p class="mt-1 text-sm text-gray-500"> | ||||
|                 Użytkownik nie będzie miał możliwości wzięcia urlopu w dzień wolny. | ||||
|             </p> | ||||
|         </div> | ||||
|         <form | ||||
|             class="border-t border-gray-200 px-6" | ||||
|             @submit.prevent="editHoliday" | ||||
|         > | ||||
|             <div class="sm:grid sm:grid-cols-3 py-4 items-center"> | ||||
|                 <label | ||||
|                     for="name" | ||||
|                     class="block text-sm font-medium text-gray-700 sm:mt-px" | ||||
|                 > | ||||
|                     Nazwa | ||||
|                 </label> | ||||
|                 <div class="mt-1 sm:mt-0 sm:col-span-2"> | ||||
|                     <input | ||||
|                         id="name" | ||||
|                         v-model="form.name" | ||||
|                         type="text" | ||||
|                         class="block w-full max-w-lg shadow-sm rounded-md sm:text-sm" | ||||
|                         :class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.name, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.name }" | ||||
|                     > | ||||
|                     <p | ||||
|                         v-if="form.errors.name" | ||||
|                         class="mt-2 text-sm text-red-600" | ||||
|                     > | ||||
|                         {{ form.errors.name }} | ||||
|                     </p> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="sm:grid sm:grid-cols-3 py-4 items-center"> | ||||
|                 <label | ||||
|                     for="date" | ||||
|                     class="block text-sm font-medium text-gray-700 sm:mt-px" | ||||
|                 > | ||||
|                     Data | ||||
|                 </label> | ||||
|                 <div class="mt-1 sm:mt-0 sm:col-span-2"> | ||||
|                     <FlatPickr | ||||
|                         id="date" | ||||
|                         v-model="form.date" | ||||
|                         placeholder="Wybierz datę" | ||||
|                         class="block w-full max-w-lg shadow-sm rounded-md sm:text-sm" | ||||
|                         :class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.date, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.date }" | ||||
|                     /> | ||||
|                     <p | ||||
|                         v-if="form.errors.date" | ||||
|                         class="mt-2 text-sm text-red-600" | ||||
|                     > | ||||
|                         {{ form.errors.date }} | ||||
|                     </p> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="flex justify-end py-3"> | ||||
|                 <div class="space-x-3"> | ||||
|                     <InertiaLink | ||||
|                         href="/holidays" | ||||
|                         class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500" | ||||
|                     > | ||||
|                         Anuluj | ||||
|                     </InertiaLink> | ||||
|                     <button | ||||
|                         type="submit" | ||||
|                         :disabled="form.processing" | ||||
|                         class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blumilk-600 hover:bg-blumilk-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500" | ||||
|                     > | ||||
|                         Zapisz | ||||
|                     </button> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </form> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { useForm } from '@inertiajs/inertia-vue3'; | ||||
| import FlatPickr from 'vue-flatpickr-component'; | ||||
|  | ||||
| export default { | ||||
|     name: 'HolidayCreate', | ||||
|     components: { | ||||
|         FlatPickr, | ||||
|     }, | ||||
|     props: { | ||||
|         holiday: { | ||||
|             type: Object, | ||||
|             default: () => null, | ||||
|         }, | ||||
|     }, | ||||
|     setup(props) { | ||||
|         const form = useForm({ | ||||
|             name: props.holiday.name, | ||||
|             date: new Date(props.holiday.date), | ||||
|         }); | ||||
|  | ||||
|         return { form }; | ||||
|     }, | ||||
|     methods: { | ||||
|         editHoliday() { | ||||
|             this.form.put(`/holidays/${this.holiday.id}`); | ||||
|         }, | ||||
|     }, | ||||
| }; | ||||
| </script> | ||||
							
								
								
									
										167
									
								
								resources/js/Pages/Holidays/Index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								resources/js/Pages/Holidays/Index.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,167 @@ | ||||
| <template> | ||||
|     <InertiaHead title="Dni wolne od pracy" /> | ||||
|     <div class="bg-white sm:rounded-lg shadow-md"> | ||||
|         <div class="flex justify-between items-center p-4 sm:px-6"> | ||||
|             <div> | ||||
|                 <h2 class="text-lg leading-6 font-medium text-gray-900"> | ||||
|                     Dni wolne od pracy | ||||
|                 </h2> | ||||
|                 <p class="mt-1 text-sm text-gray-500"> | ||||
|                     Lista dni wolnych od pracy w danym roku | ||||
|                 </p> | ||||
|             </div> | ||||
|             <div> | ||||
|                 <InertiaLink | ||||
|                     href="holidays/create" | ||||
|                     class="inline-flex items-center px-4 py-3 border border-transparent text-sm leading-4 font-medium rounded-md shadow-sm text-white bg-blumilk-600 hover:bg-blumilk-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500" | ||||
|                 > | ||||
|                     Dodaj dzień wolny | ||||
|                 </InertiaLink> | ||||
|             </div> | ||||
|         </div> | ||||
|         <div class="border-t border-gray-200"> | ||||
|             <div class="overflow-x-auto xl:overflow-x-visible overflow-y-auto xl:overflow-y-visible"> | ||||
|                 <table class="min-w-full divide-y divide-gray-200"> | ||||
|                     <thead class="bg-gray-100"> | ||||
|                         <tr> | ||||
|                             <th | ||||
|                                 scope="col" | ||||
|                                 class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider" | ||||
|                             > | ||||
|                                 Nazwa | ||||
|                             </th> | ||||
|                             <th | ||||
|                                 scope="col" | ||||
|                                 class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider" | ||||
|                             > | ||||
|                                 Data | ||||
|                             </th> | ||||
|                             <th | ||||
|                                 scope="col" | ||||
|                                 class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider" | ||||
|                             > | ||||
|                                 Dzień tygodnia | ||||
|                             </th> | ||||
|                             <th | ||||
|                                 scope="col" | ||||
|                                 class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider" | ||||
|                             /> | ||||
|                         </tr> | ||||
|                     </thead> | ||||
|                     <tbody class="bg-white divide-y divide-gray-100"> | ||||
|                         <tr | ||||
|                             v-for="holiday in holidays.data" | ||||
|                             :key="holiday.id" | ||||
|                             class="hover:bg-blumilk-25" | ||||
|                         > | ||||
|                             <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500 font-semibold capitalize"> | ||||
|                                 {{ holiday.name }} | ||||
|                             </td> | ||||
|                             <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500"> | ||||
|                                 {{ holiday.displayDate }} | ||||
|                             </td> | ||||
|                             <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500"> | ||||
|                                 {{ holiday.dayOfWeek }} | ||||
|                             </td> | ||||
|                             <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500 text-right"> | ||||
|                                 <Menu | ||||
|                                     as="div" | ||||
|                                     class="relative inline-block text-left" | ||||
|                                 > | ||||
|                                     <MenuButton class="rounded-full flex items-center text-gray-400 hover:text-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-blumilk-500"> | ||||
|                                         <DotsVerticalIcon | ||||
|                                             class="h-5 w-5" | ||||
|                                             aria-hidden="true" | ||||
|                                         /> | ||||
|                                     </MenuButton> | ||||
|  | ||||
|                                     <transition | ||||
|                                         enter-active-class="transition ease-out duration-100" | ||||
|                                         enter-from-class="transform opacity-0 scale-95" | ||||
|                                         enter-to-class="transform opacity-100 scale-100" | ||||
|                                         leave-active-class="transition ease-in duration-75" | ||||
|                                         leave-from-class="transform opacity-100 scale-100" | ||||
|                                         leave-to-class="transform opacity-0 scale-95" | ||||
|                                     > | ||||
|                                         <MenuItems class="origin-top-right absolute right-0 mt-2 w-56 z-10 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none"> | ||||
|                                             <div class="py-1"> | ||||
|                                                 <MenuItem | ||||
|                                                     v-slot="{ active }" | ||||
|                                                     class="flex" | ||||
|                                                 > | ||||
|                                                     <InertiaLink | ||||
|                                                         :href="`/holidays/${holiday.id}/edit`" | ||||
|                                                         :class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'font-medium block px-4 py-2 text-sm']" | ||||
|                                                     > | ||||
|                                                         <PencilIcon | ||||
|                                                             class="mr-2 h-5 w-5 text-blue-500" | ||||
|                                                             aria-hidden="true" | ||||
|                                                         /> Edytuj | ||||
|                                                     </InertiaLink> | ||||
|                                                 </MenuItem> | ||||
|                                                 <MenuItem | ||||
|                                                     v-slot="{ active }" | ||||
|                                                     class="flex" | ||||
|                                                 > | ||||
|                                                     <InertiaLink | ||||
|                                                         as="button" | ||||
|                                                         method="delete" | ||||
|                                                         :preserve-scroll="true" | ||||
|                                                         :href="`/holidays/${holiday.id}`" | ||||
|                                                         :class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'block w-full text-left font-medium px-4 py-2 text-sm']" | ||||
|                                                     > | ||||
|                                                         <TrashIcon | ||||
|                                                             class="mr-2 h-5 w-5 text-red-500" | ||||
|                                                             aria-hidden="true" | ||||
|                                                         /> Usuń | ||||
|                                                     </InertiaLink> | ||||
|                                                 </MenuItem> | ||||
|                                             </div> | ||||
|                                         </MenuItems> | ||||
|                                     </transition> | ||||
|                                 </Menu> | ||||
|                             </td> | ||||
|                         </tr> | ||||
|                         <tr | ||||
|                             v-if="!holidays.data.length" | ||||
|                         > | ||||
|                             <td | ||||
|                                 colspan="100%" | ||||
|                                 class="text-center py-4 text-xl leading-5 text-gray-700" | ||||
|                             > | ||||
|                                 Brak danych | ||||
|                             </td> | ||||
|                         </tr> | ||||
|                     </tbody> | ||||
|                 </table> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { DotsVerticalIcon, PencilIcon, TrashIcon } from '@heroicons/vue/solid'; | ||||
| import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'; | ||||
|  | ||||
| export default { | ||||
|     name: 'HolidayINdex', | ||||
|     components: { | ||||
|         DotsVerticalIcon, | ||||
|         PencilIcon, | ||||
|         TrashIcon, | ||||
|         Menu, | ||||
|         MenuButton, | ||||
|         MenuItem, | ||||
|         MenuItems, | ||||
|     }, | ||||
|     props: { | ||||
|         holidays: { | ||||
|             type: Object, | ||||
|             default: () => null, | ||||
|         }, | ||||
|     }, | ||||
|     setup() { | ||||
|         return {}; | ||||
|     }, | ||||
| }; | ||||
| </script> | ||||
| @@ -322,7 +322,7 @@ export default { | ||||
|             {name: 'Strona główna', href: '/', current: true}, | ||||
|             {name: 'Użytkownicy', href: '/users', current: false}, | ||||
|             {name: 'Dostępne urlopy', href: '/vacation-limits', current: false}, | ||||
|             {name: 'Company Directory', href: '#', current: false}, | ||||
|             {name: 'Dni wolne', href: '/holidays', current: false}, | ||||
|             {name: 'Openings', href: '#', current: false}, | ||||
|         ]; | ||||
|         const userNavigation = [ | ||||
|   | ||||
| @@ -4,6 +4,7 @@ declare(strict_types=1); | ||||
|  | ||||
| use Illuminate\Support\Facades\Route; | ||||
| use Toby\Http\Controllers\GoogleController; | ||||
| use Toby\Http\Controllers\HolidayController; | ||||
| use Toby\Http\Controllers\LogoutController; | ||||
| use Toby\Http\Controllers\SelectYearPeriodController; | ||||
| use Toby\Http\Controllers\UserController; | ||||
| @@ -14,7 +15,9 @@ Route::middleware("auth")->group(function (): void { | ||||
|     Route::post("/logout", LogoutController::class); | ||||
|  | ||||
|     Route::resource("users", UserController::class); | ||||
|     Route::post("users/{user}/restore", [UserController::class, "restore"])->withTrashed(); | ||||
|     Route::post("/users/{user}/restore", [UserController::class, "restore"])->withTrashed(); | ||||
|  | ||||
|     Route::resource("holidays", HolidayController::class); | ||||
|  | ||||
|     Route::get("/vacation-limits", [VacationLimitController::class, "edit"])->name("vacation.limits"); | ||||
|     Route::put("/vacation-limits", [VacationLimitController::class, "update"]); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user