#20 - wip
This commit is contained in:
		
							
								
								
									
										36
									
								
								app/Enums/VacationType.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								app/Enums/VacationType.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Enums; | ||||
|  | ||||
| enum VacationType: string | ||||
| { | ||||
|     case VACATION = "vacation"; | ||||
|     case VACATION_ON_REQUEST = "vacation_on_request"; | ||||
|     case SPECIAL_VACATION = "special_vacation"; | ||||
|     case CHILDCARE_VACATION = "childcare_vacation"; | ||||
|     case TRAINING_VACATION = "training_vacation"; | ||||
|     case UNPAID_VACATION = "unpaid_vacation"; | ||||
|     case VOLUNTEERING_VACATION = "volunteering_vacation"; | ||||
|     case LOOK_FOR_WORK_VACATION = "look_for_work_vacation"; | ||||
|     case TIME_IN_LIEU = "time_in_lieu"; | ||||
|     case SICK_VACATION = "sick_vacation"; | ||||
|  | ||||
|     public function label(): string | ||||
|     { | ||||
|         return __($this->value); | ||||
|     } | ||||
|  | ||||
|     public static function casesToSelect(): array | ||||
|     { | ||||
|         $cases = collect(VacationType::cases()); | ||||
|  | ||||
|         return $cases->map( | ||||
|             fn(VacationType $enum) => [ | ||||
|                 "label" => $enum->label(), | ||||
|                 "value" => $enum->value, | ||||
|             ], | ||||
|         )->toArray(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										16
									
								
								app/Helpers/Rules/MinimumOneVacationDayRule.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/Helpers/Rules/MinimumOneVacationDayRule.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Helpers\Rules; | ||||
|  | ||||
| use Closure; | ||||
| use Toby\Models\VacationRequest; | ||||
|  | ||||
| class MinimumOneVacationDayRule | ||||
| { | ||||
|     public function check(VacationRequest $vacationRequest, Closure $next) | ||||
|     { | ||||
|         return $next($vacationRequest); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										16
									
								
								app/Helpers/Rules/PendingVacationRequestInSameRange.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/Helpers/Rules/PendingVacationRequestInSameRange.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Helpers\Rules; | ||||
|  | ||||
| use Closure; | ||||
| use Toby\Models\VacationRequest; | ||||
|  | ||||
| class PendingVacationRequestInSameRange | ||||
| { | ||||
|     public function check(VacationRequest $vacationRequest, Closure $next) | ||||
|     { | ||||
|         return $next($vacationRequest); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										16
									
								
								app/Helpers/Rules/UsedVacationDaysInSameRange.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/Helpers/Rules/UsedVacationDaysInSameRange.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Helpers\Rules; | ||||
|  | ||||
| use Closure; | ||||
| use Toby\Models\VacationRequest; | ||||
|  | ||||
| class UsedVacationDaysInSameRange | ||||
| { | ||||
|     public function check(VacationRequest $vacationRequest, Closure $next) | ||||
|     { | ||||
|         return $next($vacationRequest); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										28
									
								
								app/Helpers/VacationRequestValidator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								app/Helpers/VacationRequestValidator.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Helpers; | ||||
|  | ||||
| use Illuminate\Contracts\Pipeline\Pipeline; | ||||
| use Toby\Models\VacationRequest; | ||||
|  | ||||
| class VacationRequestValidator | ||||
| { | ||||
|     protected array $rules = [ | ||||
|  | ||||
|     ]; | ||||
|  | ||||
|     public function __construct(protected Pipeline $pipeline) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     public function validate(VacationRequest $vacationRequest): void | ||||
|     { | ||||
|         $this->pipeline | ||||
|             ->send($vacationRequest) | ||||
|             ->through($this->rules) | ||||
|             ->via("check") | ||||
|             ->then(fn(VacationRequest $vacationRequest) => $vacationRequest); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										31
									
								
								app/Http/Controllers/VacationRequestController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								app/Http/Controllers/VacationRequestController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Http\Controllers; | ||||
|  | ||||
| use Illuminate\Http\Request; | ||||
| use Inertia\Response; | ||||
| use Toby\Enums\VacationType; | ||||
| use Toby\Http\Resources\VacationRequestResource; | ||||
|  | ||||
| class VacationRequestController extends Controller | ||||
| { | ||||
|     public function index(Request $request): Response | ||||
|     { | ||||
|         $requests = $request->user() | ||||
|             ->vacationRequests() | ||||
|             ->paginate(); | ||||
|  | ||||
|         return inertia("VacationRequest/Index", [ | ||||
|             "requests" => VacationRequestResource::collection($requests), | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function create(): Response | ||||
|     { | ||||
|         return inertia("VacationRequest/Create", [ | ||||
|             "vacationTypes" => VacationType::casesToSelect(), | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
| @@ -23,6 +23,7 @@ use Illuminate\Routing\Middleware\ValidateSignature; | ||||
| use Illuminate\Session\Middleware\AuthenticateSession; | ||||
| use Illuminate\Session\Middleware\StartSession; | ||||
| use Illuminate\View\Middleware\ShareErrorsFromSession; | ||||
| use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful; | ||||
| use Toby\Http\Middleware\Authenticate; | ||||
| use Toby\Http\Middleware\HandleInertiaRequests; | ||||
| use Toby\Http\Middleware\RedirectIfAuthenticated; | ||||
| @@ -52,6 +53,7 @@ class Kernel extends HttpKernel | ||||
|             HandleInertiaRequests::class, | ||||
|         ], | ||||
|         "api" => [ | ||||
|             EnsureFrontendRequestsAreStateful::class, | ||||
|             "throttle:api", | ||||
|             SubstituteBindings::class, | ||||
|         ], | ||||
|   | ||||
							
								
								
									
										33
									
								
								app/Http/Requests/VacationRequestRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								app/Http/Requests/VacationRequestRequest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Http\Requests; | ||||
|  | ||||
| use Illuminate\Foundation\Http\FormRequest; | ||||
| use Illuminate\Validation\Rules\Enum; | ||||
| use Toby\Enums\VacationType; | ||||
| use Toby\Rules\YearPeriodExists; | ||||
|  | ||||
| class VacationRequestRequest extends FormRequest | ||||
| { | ||||
|     public function rules(): array | ||||
|     { | ||||
|         return [ | ||||
|             "type" => ["required", new Enum(VacationType::class)], | ||||
|             "from" => ["required", "date_format:Y-m-d", new YearPeriodExists()], | ||||
|             "to" => ["required", "date_format:Y-m-d", new YearPeriodExists()], | ||||
|             "comment" => ["nullable"], | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function data(): array | ||||
|     { | ||||
|         return [ | ||||
|             "type" => $this->get("type"), | ||||
|             "from" => $this->get("from"), | ||||
|             "to" => $this->get("to"), | ||||
|             "comment" => $this->get("comment"), | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @@ -15,6 +15,7 @@ class HolidayResource extends JsonResource | ||||
|         return [ | ||||
|             "id" => $this->id, | ||||
|             "name" => $this->name, | ||||
|             "date" => $this->date->toDateString(), | ||||
|             "displayDate" => $this->date->toDisplayString(), | ||||
|             "dayOfWeek" => $this->date->dayName, | ||||
|         ]; | ||||
|   | ||||
| @@ -18,7 +18,7 @@ class UserFormDataResource extends JsonResource | ||||
|             "lastName" => $this->last_name, | ||||
|             "email" => $this->email, | ||||
|             "employmentForm" => $this->employment_form, | ||||
|             "employmentDate" => $this->employment_date, | ||||
|             "employmentDate" => $this->employment_date->toDateString(), | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										24
									
								
								app/Http/Resources/VacationRequestResource.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/Http/Resources/VacationRequestResource.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Toby\Http\Resources; | ||||
|  | ||||
| use Illuminate\Http\Resources\Json\JsonResource; | ||||
|  | ||||
| class VacationRequestResource extends JsonResource | ||||
| { | ||||
|     public static $wrap = null; | ||||
|  | ||||
|     public function toArray($request): array | ||||
|     { | ||||
|         return [ | ||||
|             "id" => $this->id, | ||||
|             "user" => new UserResource($this->user), | ||||
|             "type" => $this->type->label(), | ||||
|             "from" => $this->from->toDisplayString(), | ||||
|             "to" => $this->to->toDisplayString(), | ||||
|             "commment" => $this->comment, | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @@ -23,6 +23,7 @@ use Toby\Enums\EmploymentForm; | ||||
|  * @property EmploymentForm $employment_form | ||||
|  * @property Carbon $employment_date | ||||
|  * @property Collection $vacationLimits | ||||
|  * @property Collection $vacationRequests | ||||
|  */ | ||||
| class User extends Authenticatable | ||||
| { | ||||
| @@ -46,6 +47,11 @@ class User extends Authenticatable | ||||
|         return $this->hasMany(VacationLimit::class); | ||||
|     } | ||||
|  | ||||
|     public function vacationRequests(): HasMany | ||||
|     { | ||||
|         return $this->hasMany(VacationRequest::class); | ||||
|     } | ||||
|  | ||||
|     public function scopeSearch(Builder $query, ?string $text): Builder | ||||
|     { | ||||
|         if ($text === null) { | ||||
|   | ||||
							
								
								
									
										33
									
								
								app/Models/VacationRequest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								app/Models/VacationRequest.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| <?php | ||||
|  | ||||
| namespace Toby\Models; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Factories\HasFactory; | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||||
| use Illuminate\Support\Carbon; | ||||
| use Toby\Enums\VacationType; | ||||
|  | ||||
| /** | ||||
|  * @property int $id | ||||
|  * @property VacationType $type | ||||
|  * @property Carbon $from | ||||
|  * @property Carbon $to | ||||
|  * @property string $comment | ||||
|  * @property User $user | ||||
|  */ | ||||
| class VacationRequest extends Model | ||||
| { | ||||
|     use HasFactory; | ||||
|  | ||||
|     protected $casts = [ | ||||
|         "type" => VacationType::class, | ||||
|         "from" => "date", | ||||
|         "to" => "date", | ||||
|     ]; | ||||
|  | ||||
|     public function user(): BelongsTo | ||||
|     { | ||||
|         return $this->belongsTo(User::class); | ||||
|     } | ||||
| } | ||||
| @@ -12,6 +12,7 @@ | ||||
|         "guzzlehttp/guzzle": "^7.0.1", | ||||
|         "inertiajs/inertia-laravel": "^0.5.1", | ||||
|         "laravel/framework": "^8.75", | ||||
|         "laravel/sanctum": "^2.14", | ||||
|         "laravel/socialite": "^5.2", | ||||
|         "laravel/telescope": "^4.6", | ||||
|         "laravel/tinker": "^2.5", | ||||
|   | ||||
							
								
								
									
										66
									
								
								composer.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										66
									
								
								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": "e3c6ffae4c01db02d0471c52d2370b79", | ||||
|     "content-hash": "bf5f7d8f40ecadea64ae8564a3df3110", | ||||
|     "packages": [ | ||||
|         { | ||||
|             "name": "asm89/stack-cors", | ||||
| @@ -1358,6 +1358,70 @@ | ||||
|             }, | ||||
|             "time": "2022-01-12T16:12:41+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "laravel/sanctum", | ||||
|             "version": "v2.14.0", | ||||
|             "source": { | ||||
|                 "type": "git", | ||||
|                 "url": "https://github.com/laravel/sanctum.git", | ||||
|                 "reference": "0647a87140c7522e75826cffcadb3ad6e01f71e9" | ||||
|             }, | ||||
|             "dist": { | ||||
|                 "type": "zip", | ||||
|                 "url": "https://api.github.com/repos/laravel/sanctum/zipball/0647a87140c7522e75826cffcadb3ad6e01f71e9", | ||||
|                 "reference": "0647a87140c7522e75826cffcadb3ad6e01f71e9", | ||||
|                 "shasum": "" | ||||
|             }, | ||||
|             "require": { | ||||
|                 "ext-json": "*", | ||||
|                 "illuminate/contracts": "^6.9|^7.0|^8.0|^9.0", | ||||
|                 "illuminate/database": "^6.9|^7.0|^8.0|^9.0", | ||||
|                 "illuminate/support": "^6.9|^7.0|^8.0|^9.0", | ||||
|                 "php": "^7.2|^8.0" | ||||
|             }, | ||||
|             "require-dev": { | ||||
|                 "mockery/mockery": "^1.0", | ||||
|                 "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0", | ||||
|                 "phpunit/phpunit": "^8.0|^9.3" | ||||
|             }, | ||||
|             "type": "library", | ||||
|             "extra": { | ||||
|                 "branch-alias": { | ||||
|                     "dev-master": "2.x-dev" | ||||
|                 }, | ||||
|                 "laravel": { | ||||
|                     "providers": [ | ||||
|                         "Laravel\\Sanctum\\SanctumServiceProvider" | ||||
|                     ] | ||||
|                 } | ||||
|             }, | ||||
|             "autoload": { | ||||
|                 "psr-4": { | ||||
|                     "Laravel\\Sanctum\\": "src/" | ||||
|                 } | ||||
|             }, | ||||
|             "notification-url": "https://packagist.org/downloads/", | ||||
|             "license": [ | ||||
|                 "MIT" | ||||
|             ], | ||||
|             "authors": [ | ||||
|                 { | ||||
|                     "name": "Taylor Otwell", | ||||
|                     "email": "taylor@laravel.com" | ||||
|                 } | ||||
|             ], | ||||
|             "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", | ||||
|             "keywords": [ | ||||
|                 "auth", | ||||
|                 "laravel", | ||||
|                 "sanctum" | ||||
|             ], | ||||
|             "support": { | ||||
|                 "issues": "https://github.com/laravel/sanctum/issues", | ||||
|                 "source": "https://github.com/laravel/sanctum" | ||||
|             }, | ||||
|             "time": "2022-01-12T15:07:43+00:00" | ||||
|         }, | ||||
|         { | ||||
|             "name": "laravel/serializable-closure", | ||||
|             "version": "v1.0.5", | ||||
|   | ||||
							
								
								
									
										20
									
								
								config/sanctum.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								config/sanctum.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| use Illuminate\Cookie\Middleware\EncryptCookies; | ||||
| use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; | ||||
|  | ||||
| return [ | ||||
|     'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( | ||||
|         '%s%s', | ||||
|         'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', | ||||
|         env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : '' | ||||
|     ))), | ||||
|     'guard' => ['web'], | ||||
|     'expiration' => null, | ||||
|     'middleware' => [ | ||||
|         'verify_csrf_token' => VerifyCsrfToken::class, | ||||
|         'encrypt_cookies' => EncryptCookies::class, | ||||
|     ], | ||||
| ]; | ||||
							
								
								
									
										23
									
								
								database/factories/VacationRequestFactory.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								database/factories/VacationRequestFactory.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace Database\Factories; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Factories\Factory; | ||||
| use Toby\Enums\VacationType; | ||||
| use Toby\Models\User; | ||||
|  | ||||
| class VacationRequestFactory extends Factory | ||||
| { | ||||
|     public function definition(): array | ||||
|     { | ||||
|         return [ | ||||
|             "user_id" => User::factory(), | ||||
|             "type" => $this->faker->randomElement(VacationType::cases()), | ||||
|             "from" => $this->faker->date, | ||||
|             "to" => $this->faker->date, | ||||
|             "comment" => $this->faker->boolean ? $this->faker->paragraph() : null, | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @@ -1,27 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| use Illuminate\Database\Migrations\Migration; | ||||
| use Illuminate\Database\Schema\Blueprint; | ||||
| use Illuminate\Support\Facades\Schema; | ||||
|  | ||||
| return new class() extends Migration { | ||||
|     public function up(): void | ||||
|     { | ||||
|         Schema::create("personal_access_tokens", function (Blueprint $table): void { | ||||
|             $table->id(); | ||||
|             $table->morphs("tokenable"); | ||||
|             $table->string("name"); | ||||
|             $table->string("token", 64)->unique(); | ||||
|             $table->text("abilities")->nullable(); | ||||
|             $table->timestamp("last_used_at")->nullable(); | ||||
|             $table->timestamps(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public function down(): void | ||||
|     { | ||||
|         Schema::dropIfExists("personal_access_tokens"); | ||||
|     } | ||||
| }; | ||||
| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| use Illuminate\Database\Migrations\Migration; | ||||
| use Illuminate\Database\Schema\Blueprint; | ||||
| use Illuminate\Support\Facades\Schema; | ||||
| use Toby\Models\User; | ||||
|  | ||||
| return new class() extends Migration { | ||||
|     public function up(): void | ||||
|     { | ||||
|         Schema::create('vacation_requests', function (Blueprint $table): void { | ||||
|             $table->id(); | ||||
|             $table->foreignIdFor(User::class)->constrained()->cascadeOnDelete(); | ||||
|             $table->string("type"); | ||||
|             $table->date("from"); | ||||
|             $table->date("to"); | ||||
|             $table->text("comment")->nullable(); | ||||
|             $table->timestamps(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     public function down(): void | ||||
|     { | ||||
|         Schema::dropIfExists('vacation_requests'); | ||||
|     } | ||||
| }; | ||||
| @@ -28,7 +28,9 @@ class DatabaseSeeder extends Seeder | ||||
|         User::factory(9)->create(); | ||||
|         User::factory([ | ||||
|             "email" => env("LOCAL_EMAIL_FOR_LOGIN_VIA_GOOGLE"), | ||||
|         ])->create(); | ||||
|         ]) | ||||
|             ->hasVacationRequests(5) | ||||
|             ->create(); | ||||
|  | ||||
|         $users = User::all(); | ||||
|  | ||||
|   | ||||
							
								
								
									
										132
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										132
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -15,12 +15,14 @@ | ||||
|                 "@tailwindcss/typography": "^0.5.0", | ||||
|                 "@vue/compiler-sfc": "^3.2.26", | ||||
|                 "autoprefixer": "^10.4.2", | ||||
|                 "echarts": "^5.2.2", | ||||
|                 "flatpickr": "^4.6.9", | ||||
|                 "laravel-mix": "^6.0.6", | ||||
|                 "lodash": "^4.17.21", | ||||
|                 "postcss": "^8.4.5", | ||||
|                 "tailwindcss": "^3.0.13", | ||||
|                 "vue": "^3.2.26", | ||||
|                 "vue-echarts": "^6.0.2", | ||||
|                 "vue-flatpickr-component": "^9.0.5", | ||||
|                 "vue-loader": "^17.0.0" | ||||
|             }, | ||||
| @@ -3986,6 +3988,20 @@ | ||||
|             "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", | ||||
|             "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" | ||||
|         }, | ||||
|         "node_modules/echarts": { | ||||
|             "version": "5.2.2", | ||||
|             "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.2.2.tgz", | ||||
|             "integrity": "sha512-yxuBfeIH5c+0FsoRP60w4De6omXhA06c7eUYBsC1ykB6Ys2yK5fSteIYWvkJ4xJVLQgCvAdO8C4mN6MLeJpBaw==", | ||||
|             "dependencies": { | ||||
|                 "tslib": "2.3.0", | ||||
|                 "zrender": "5.2.1" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/echarts/node_modules/tslib": { | ||||
|             "version": "2.3.0", | ||||
|             "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", | ||||
|             "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" | ||||
|         }, | ||||
|         "node_modules/ee-first": { | ||||
|             "version": "1.1.1", | ||||
|             "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", | ||||
| @@ -7797,6 +7813,11 @@ | ||||
|             "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", | ||||
|             "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" | ||||
|         }, | ||||
|         "node_modules/resize-detector": { | ||||
|             "version": "0.3.0", | ||||
|             "resolved": "https://registry.npmjs.org/resize-detector/-/resize-detector-0.3.0.tgz", | ||||
|             "integrity": "sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ==" | ||||
|         }, | ||||
|         "node_modules/resolve": { | ||||
|             "version": "1.21.0", | ||||
|             "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", | ||||
| @@ -8854,6 +8875,51 @@ | ||||
|                 "@vue/shared": "3.2.26" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/vue-echarts": { | ||||
|             "version": "6.0.2", | ||||
|             "resolved": "https://registry.npmjs.org/vue-echarts/-/vue-echarts-6.0.2.tgz", | ||||
|             "integrity": "sha512-9xDokauJtAc389MNKbwi1I0VDmp4Y6ndAJTQ8T9K7H0ffosTe1OJSJbUtkT7/fVLDFzlCcmg2TfAKaMzbpg5yQ==", | ||||
|             "hasInstallScript": true, | ||||
|             "dependencies": { | ||||
|                 "resize-detector": "^0.3.0", | ||||
|                 "vue-demi": "^0.12.1" | ||||
|             }, | ||||
|             "peerDependencies": { | ||||
|                 "@vue/composition-api": "^1.0.5", | ||||
|                 "echarts": "^5.1.2", | ||||
|                 "vue": "^2.6.12 || ^3.1.1" | ||||
|             }, | ||||
|             "peerDependenciesMeta": { | ||||
|                 "@vue/composition-api": { | ||||
|                     "optional": true | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/vue-echarts/node_modules/vue-demi": { | ||||
|             "version": "0.12.1", | ||||
|             "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.1.tgz", | ||||
|             "integrity": "sha512-QL3ny+wX8c6Xm1/EZylbgzdoDolye+VpCXRhI2hug9dJTP3OUJ3lmiKN3CsVV3mOJKwFi0nsstbgob0vG7aoIw==", | ||||
|             "hasInstallScript": true, | ||||
|             "bin": { | ||||
|                 "vue-demi-fix": "bin/vue-demi-fix.js", | ||||
|                 "vue-demi-switch": "bin/vue-demi-switch.js" | ||||
|             }, | ||||
|             "engines": { | ||||
|                 "node": ">=12" | ||||
|             }, | ||||
|             "funding": { | ||||
|                 "url": "https://github.com/sponsors/antfu" | ||||
|             }, | ||||
|             "peerDependencies": { | ||||
|                 "@vue/composition-api": "^1.0.0-rc.1", | ||||
|                 "vue": "^3.0.0-0 || ^2.6.0" | ||||
|             }, | ||||
|             "peerDependenciesMeta": { | ||||
|                 "@vue/composition-api": { | ||||
|                     "optional": true | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/vue-eslint-parser": { | ||||
|             "version": "8.0.1", | ||||
|             "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.0.1.tgz", | ||||
| @@ -9444,6 +9510,19 @@ | ||||
|             "engines": { | ||||
|                 "node": ">=12" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/zrender": { | ||||
|             "version": "5.2.1", | ||||
|             "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.2.1.tgz", | ||||
|             "integrity": "sha512-M3bPGZuyLTNBC6LiNKXJwSCtglMp8XUEqEBG+2MdICDI3d1s500Y4P0CzldQGsqpRVB7fkvf3BKQQRxsEaTlsw==", | ||||
|             "dependencies": { | ||||
|                 "tslib": "2.3.0" | ||||
|             } | ||||
|         }, | ||||
|         "node_modules/zrender/node_modules/tslib": { | ||||
|             "version": "2.3.0", | ||||
|             "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", | ||||
|             "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" | ||||
|         } | ||||
|     }, | ||||
|     "dependencies": { | ||||
| @@ -12439,6 +12518,22 @@ | ||||
|             "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", | ||||
|             "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" | ||||
|         }, | ||||
|         "echarts": { | ||||
|             "version": "5.2.2", | ||||
|             "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.2.2.tgz", | ||||
|             "integrity": "sha512-yxuBfeIH5c+0FsoRP60w4De6omXhA06c7eUYBsC1ykB6Ys2yK5fSteIYWvkJ4xJVLQgCvAdO8C4mN6MLeJpBaw==", | ||||
|             "requires": { | ||||
|                 "tslib": "2.3.0", | ||||
|                 "zrender": "5.2.1" | ||||
|             }, | ||||
|             "dependencies": { | ||||
|                 "tslib": { | ||||
|                     "version": "2.3.0", | ||||
|                     "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", | ||||
|                     "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "ee-first": { | ||||
|             "version": "1.1.1", | ||||
|             "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", | ||||
| @@ -15167,6 +15262,11 @@ | ||||
|             "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", | ||||
|             "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" | ||||
|         }, | ||||
|         "resize-detector": { | ||||
|             "version": "0.3.0", | ||||
|             "resolved": "https://registry.npmjs.org/resize-detector/-/resize-detector-0.3.0.tgz", | ||||
|             "integrity": "sha512-R/tCuvuOHQ8o2boRP6vgx8hXCCy87H1eY9V5imBYeVNyNVpuL9ciReSccLj2gDcax9+2weXy3bc8Vv+NRXeEvQ==" | ||||
|         }, | ||||
|         "resolve": { | ||||
|             "version": "1.21.0", | ||||
|             "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz", | ||||
| @@ -15964,6 +16064,23 @@ | ||||
|                 "@vue/shared": "3.2.26" | ||||
|             } | ||||
|         }, | ||||
|         "vue-echarts": { | ||||
|             "version": "6.0.2", | ||||
|             "resolved": "https://registry.npmjs.org/vue-echarts/-/vue-echarts-6.0.2.tgz", | ||||
|             "integrity": "sha512-9xDokauJtAc389MNKbwi1I0VDmp4Y6ndAJTQ8T9K7H0ffosTe1OJSJbUtkT7/fVLDFzlCcmg2TfAKaMzbpg5yQ==", | ||||
|             "requires": { | ||||
|                 "resize-detector": "^0.3.0", | ||||
|                 "vue-demi": "^0.12.1" | ||||
|             }, | ||||
|             "dependencies": { | ||||
|                 "vue-demi": { | ||||
|                     "version": "0.12.1", | ||||
|                     "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.1.tgz", | ||||
|                     "integrity": "sha512-QL3ny+wX8c6Xm1/EZylbgzdoDolye+VpCXRhI2hug9dJTP3OUJ3lmiKN3CsVV3mOJKwFi0nsstbgob0vG7aoIw==", | ||||
|                     "requires": {} | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "vue-eslint-parser": { | ||||
|             "version": "8.0.1", | ||||
|             "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.0.1.tgz", | ||||
| @@ -16364,6 +16481,21 @@ | ||||
|             "version": "21.0.0", | ||||
|             "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", | ||||
|             "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==" | ||||
|         }, | ||||
|         "zrender": { | ||||
|             "version": "5.2.1", | ||||
|             "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.2.1.tgz", | ||||
|             "integrity": "sha512-M3bPGZuyLTNBC6LiNKXJwSCtglMp8XUEqEBG+2MdICDI3d1s500Y4P0CzldQGsqpRVB7fkvf3BKQQRxsEaTlsw==", | ||||
|             "requires": { | ||||
|                 "tslib": "2.3.0" | ||||
|             }, | ||||
|             "dependencies": { | ||||
|                 "tslib": { | ||||
|                     "version": "2.3.0", | ||||
|                     "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", | ||||
|                     "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -22,12 +22,14 @@ | ||||
|         "@tailwindcss/typography": "^0.5.0", | ||||
|         "@vue/compiler-sfc": "^3.2.26", | ||||
|         "autoprefixer": "^10.4.2", | ||||
|         "echarts": "^5.2.2", | ||||
|         "flatpickr": "^4.6.9", | ||||
|         "laravel-mix": "^6.0.6", | ||||
|         "lodash": "^4.17.21", | ||||
|         "postcss": "^8.4.5", | ||||
|         "tailwindcss": "^3.0.13", | ||||
|         "vue": "^3.2.26", | ||||
|         "vue-echarts": "^6.0.2", | ||||
|         "vue-flatpickr-component": "^9.0.5", | ||||
|         "vue-loader": "^17.0.0" | ||||
|     }, | ||||
|   | ||||
| @@ -47,3 +47,24 @@ | ||||
|     -webkit-box-shadow: -5px 0 0 #527ABA, 5px 0 0 #527ABA; | ||||
|     box-shadow: -5px 0 0 #527ABA, 5px 0 0 #527ABA; | ||||
| } | ||||
|  | ||||
| ::-webkit-scrollbar { | ||||
|     width: 8px; | ||||
|     height: 8px; | ||||
| } | ||||
|  | ||||
| ::-webkit-scrollbar-track { | ||||
|     border-radius: 100vh; | ||||
|     background: transparent; | ||||
| } | ||||
|  | ||||
| ::-webkit-scrollbar-thumb { | ||||
|     border-radius: 8px; | ||||
|     background: #dadce0; | ||||
|     border: 4px solid transparent; | ||||
| } | ||||
|  | ||||
| ::-webkit-scrollbar-thumb:hover { | ||||
|     background: #dadce0; | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										238
									
								
								resources/js/Pages/VacationRequest/Create.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								resources/js/Pages/VacationRequest/Create.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,238 @@ | ||||
| <template> | ||||
|     <InertiaHead title="Składanie wniosku urlopowego" /> | ||||
|     <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"> | ||||
|                 Złóż wniosek urlopowy | ||||
|             </h2> | ||||
|         </div> | ||||
|         <form | ||||
|             class="border-t border-gray-200 px-6" | ||||
|             @submit.prevent="createForm" | ||||
|         > | ||||
|             <Listbox | ||||
|                 v-model="form.vacationType" | ||||
|                 as="div" | ||||
|                 class="sm:grid sm:grid-cols-3 py-4 items-center" | ||||
|             > | ||||
|                 <ListboxLabel class="block text-sm font-medium text-gray-700"> | ||||
|                     Rodzaj wniosku | ||||
|                 </ListboxLabel> | ||||
|                 <div class="mt-1 relative sm:mt-0 sm:col-span-2"> | ||||
|                     <ListboxButton | ||||
|                         class="bg-white relative w-full max-w-lg border rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default sm:text-sm focus:ring-1" | ||||
|                         :class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.vacationType, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.vacationType }" | ||||
|                     > | ||||
|                         <span class="block truncate">{{ form.vacationType.label }}</span> | ||||
|                         <span class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none"> | ||||
|                             <SelectorIcon class="h-5 w-5 text-gray-400" /> | ||||
|                         </span> | ||||
|                     </ListboxButton> | ||||
|  | ||||
|                     <transition | ||||
|                         leave-active-class="transition ease-in duration-100" | ||||
|                         leave-from-class="opacity-100" | ||||
|                         leave-to-class="opacity-0" | ||||
|                     > | ||||
|                         <ListboxOptions | ||||
|                             class="absolute z-10 mt-1 w-full max-w-lg bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" | ||||
|                         > | ||||
|                             <ListboxOption | ||||
|                                 v-for="vacationType in vacationTypes" | ||||
|                                 :key="vacationType.value" | ||||
|                                 v-slot="{ active, selected }" | ||||
|                                 as="template" | ||||
|                                 :value="vacationType" | ||||
|                             > | ||||
|                                 <li :class="[active ? 'text-white bg-blumilk-600' : 'text-gray-900', 'cursor-default select-none relative py-2 pl-3 pr-9']"> | ||||
|                                     <span :class="[selected ? 'font-semibold' : 'font-normal', 'block truncate']"> | ||||
|                                         {{ vacationType.label }} | ||||
|                                     </span> | ||||
|  | ||||
|                                     <span | ||||
|                                         v-if="selected" | ||||
|                                         :class="[active ? 'text-white' : 'text-blumilk-600', 'absolute inset-y-0 right-0 flex items-center pr-4']" | ||||
|                                     > | ||||
|                                         <CheckIcon class="h-5 w-5" /> | ||||
|                                     </span> | ||||
|                                 </li> | ||||
|                             </ListboxOption> | ||||
|                         </ListboxOptions> | ||||
|                     </transition> | ||||
|                     <p | ||||
|                         v-if="form.errors.vacationType" | ||||
|                         class="mt-2 text-sm text-red-600" | ||||
|                     > | ||||
|                         {{ form.errors.vacationType }} | ||||
|                     </p> | ||||
|                 </div> | ||||
|             </Listbox> | ||||
|             <div class="sm:grid sm:grid-cols-3 py-4 items-center"> | ||||
|                 <label | ||||
|                     for="date_from" | ||||
|                     class="block text-sm font-medium text-gray-700 sm:mt-px" | ||||
|                 > | ||||
|                     Planowany urlop od | ||||
|                 </label> | ||||
|                 <div class="mt-1 sm:mt-0 sm:col-span-2"> | ||||
|                     <FlatPickr | ||||
|                         id="date_from" | ||||
|                         v-model="form.dateFrom" | ||||
|                         :config="fromInputConfig" | ||||
|                         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.dateFrom, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.dateFrom }" | ||||
|                         @on-change="onFromChange" | ||||
|                     /> | ||||
|                     <p | ||||
|                         v-if="form.errors.dateFrom" | ||||
|                         class="mt-2 text-sm text-red-600" | ||||
|                     > | ||||
|                         {{ form.errors.dateFrom }} | ||||
|                     </p> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="sm:grid sm:grid-cols-3 py-4 items-center"> | ||||
|                 <label | ||||
|                     for="date_from" | ||||
|                     class="block text-sm font-medium text-gray-700 sm:mt-px" | ||||
|                 > | ||||
|                     Planowany urlop do | ||||
|                 </label> | ||||
|                 <div class="mt-1 sm:mt-0 sm:col-span-2"> | ||||
|                     <FlatPickr | ||||
|                         id="date_to" | ||||
|                         v-model="form.dateTo" | ||||
|                         :config="toInputConfig" | ||||
|                         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.dateTo, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.dateTo }" | ||||
|                         @on-change="onToChange" | ||||
|                     /> | ||||
|                     <p | ||||
|                         v-if="form.errors.dateTo" | ||||
|                         class="mt-2 text-sm text-red-600" | ||||
|                     > | ||||
|                         {{ form.errors.dateTo }} | ||||
|                     </p> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="sm:grid sm:grid-cols-3 py-4 items-center"> | ||||
|                 <span class="block text-sm font-medium text-gray-700 sm:mt-px">Liczba dni urlopu</span> | ||||
|                 <div | ||||
|                     class="mt-1 sm:mt-0 sm:col-span-2 w-full max-w-lg bg-gray-50 border border-gray-300 rounded-md px-4 py-2 inline-flex items-center text-gray-500 sm:text-sm" | ||||
|                 > | ||||
|                     1 | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="sm:grid sm:grid-cols-3 py-4 items-center"> | ||||
|                 <label | ||||
|                     for="comment" | ||||
|                     class="block text-sm font-medium text-gray-700" | ||||
|                 > | ||||
|                     Komentarz | ||||
|                 </label> | ||||
|                 <div class="mt-1 sm:mt-0 sm:col-span-2"> | ||||
|                     <textarea | ||||
|                         id="comment" | ||||
|                         rows="4" | ||||
|                         class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full max-w-lg sm:text-sm border-gray-300 rounded-md" | ||||
|                     /> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="flex justify-end py-3"> | ||||
|                 <div class="space-x-3"> | ||||
|                     <InertiaLink | ||||
|                         href="/vacation-request" | ||||
|                         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'; | ||||
| import {Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions} from '@headlessui/vue'; | ||||
| import {CheckIcon, SelectorIcon} from '@heroicons/vue/solid'; | ||||
| import {reactive} from 'vue'; | ||||
|  | ||||
| export default { | ||||
|     dateFrom: 'UserCreate', | ||||
|     components: { | ||||
|         FlatPickr, | ||||
|         Listbox, | ||||
|         ListboxButton, | ||||
|         ListboxLabel, | ||||
|         ListboxOption, | ||||
|         ListboxOptions, | ||||
|         CheckIcon, | ||||
|         SelectorIcon, | ||||
|     }, | ||||
|     props: { | ||||
|         vacationTypes: { | ||||
|             type: Object, | ||||
|             default: () => null, | ||||
|         }, | ||||
|         holidays: { | ||||
|             type: Object, | ||||
|             default: () => null, | ||||
|         }, | ||||
|     }, | ||||
|     setup(props) { | ||||
|         const form = useForm({ | ||||
|             dateFrom: null, | ||||
|             dateTo: null, | ||||
|             vacationType: props.vacationTypes[0], | ||||
|         }); | ||||
|  | ||||
|         const disableDates = [ | ||||
|             date => (date.getDay() === 0 || date.getDay() === 6), | ||||
|         ]; | ||||
|  | ||||
|         const fromInputConfig = reactive({ | ||||
|             maxDate: null, | ||||
|             disable: disableDates, | ||||
|         }); | ||||
|  | ||||
|         const toInputConfig = reactive({ | ||||
|             minDate: null, | ||||
|             disable: disableDates, | ||||
|         }); | ||||
|  | ||||
|         return { | ||||
|             form, | ||||
|             fromInputConfig, | ||||
|             toInputConfig, | ||||
|         }; | ||||
|     }, | ||||
|     methods: { | ||||
|         createForm() { | ||||
|             this.form | ||||
|                 .transform(data => ({ | ||||
|                     ...data, | ||||
|                     vacationType: data.vacationType.value, | ||||
|                 })) | ||||
|                 .post('/vacation-request'); | ||||
|         }, | ||||
|         onFromChange(selectedDates, dateStr) { | ||||
|             this.toInputConfig.minDate = dateStr; | ||||
|         }, | ||||
|         onToChange(selectedDates, dateStr) { | ||||
|             this.fromInputConfig.maxDate = dateStr; | ||||
|         }, | ||||
|     }, | ||||
|  | ||||
| }; | ||||
| </script> | ||||
							
								
								
									
										223
									
								
								resources/js/Pages/VacationRequest/Index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								resources/js/Pages/VacationRequest/Index.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,223 @@ | ||||
| <template> | ||||
|     <InertiaHead title="Twoje wnioski urlopowe" /> | ||||
|     <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"> | ||||
|                     Twoje wnioski urlopowe | ||||
|                 </h2> | ||||
|             </div> | ||||
|             <div> | ||||
|                 <InertiaLink | ||||
|                     href="vacation-requests/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 wniosek | ||||
|                 </InertiaLink> | ||||
|             </div> | ||||
|         </div> | ||||
|         <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-50"> | ||||
|                     <tr> | ||||
|                         <th | ||||
|                             scope="col" | ||||
|                             class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider" | ||||
|                         > | ||||
|                             Numer | ||||
|                         </th> | ||||
|                         <th | ||||
|                             scope="col" | ||||
|                             class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider" | ||||
|                         > | ||||
|                             Rodzaj urlopu | ||||
|                         </th> | ||||
|                         <th | ||||
|                             scope="col" | ||||
|                             class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider" | ||||
|                         > | ||||
|                             Od | ||||
|                         </th> | ||||
|                         <th | ||||
|                             scope="col" | ||||
|                             class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider" | ||||
|                         > | ||||
|                             Do | ||||
|                         </th> | ||||
|                         <th | ||||
|                             scope="col" | ||||
|                             class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider" | ||||
|                         > | ||||
|                             Dni urlopu | ||||
|                         </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="request in requests.data" | ||||
|                         :key="request.id" | ||||
|                         class="hover:bg-blumilk-25" | ||||
|                     > | ||||
|                         <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500"> | ||||
|                             {{ request.id }} | ||||
|                         </td> | ||||
|                         <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500"> | ||||
|                             {{ request.type }} | ||||
|                         </td> | ||||
|                         <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500"> | ||||
|                             {{ request.from }} | ||||
|                         </td> | ||||
|                         <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500"> | ||||
|                             {{ request.to }} | ||||
|                         </td> | ||||
|                         <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500"> | ||||
|                             X | ||||
|                         </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 | ||||
|                                                     :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" | ||||
|                                                     :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" | ||||
|                                                     /> Usuń | ||||
|                                                 </InertiaLink> | ||||
|                                             </MenuItem> | ||||
|                                         </div> | ||||
|                                     </MenuItems> | ||||
|                                 </transition> | ||||
|                             </Menu> | ||||
|                         </td> | ||||
|                     </tr> | ||||
|                     <tr | ||||
|                         v-if="! requests.data.length" | ||||
|                     > | ||||
|                         <td | ||||
|                             colspan="100%" | ||||
|                             class="text-center py-4 text-xl leading-5 text-gray-700" | ||||
|                         > | ||||
|                             Brak danych | ||||
|                         </td> | ||||
|                     </tr> | ||||
|                 </tbody> | ||||
|             </table> | ||||
|             <div | ||||
|                 v-if="requests.data.length && requests.meta.last_page !== 1" | ||||
|                 class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6 rounded-b-lg" | ||||
|             > | ||||
|                 <div class="flex-1 flex justify-between sm:hidden"> | ||||
|                     <InertiaLink | ||||
|                         :is="requests.links.prev ? 'InertiaLink': 'span'" | ||||
|                         :href="requests.links.prev" | ||||
|                         class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50" | ||||
|                     > | ||||
|                         Poprzednia | ||||
|                     </InertiaLink> | ||||
|                     <Component | ||||
|                         :is="requests.links.next ? 'InertiaLink': 'span'" | ||||
|                         :href="requests.links.next" | ||||
|                         class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50" | ||||
|                     > | ||||
|                         Następna | ||||
|                     </Component> | ||||
|                 </div> | ||||
|                 <div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between"> | ||||
|                     <div class="text-sm text-gray-700"> | ||||
|                         Wyświetlanie | ||||
|                         <span class="font-medium">{{ requests.meta.from }}</span> | ||||
|                         od | ||||
|                         <span class="font-medium">{{ requests.meta.to }}</span> | ||||
|                         do | ||||
|                         <span class="font-medium">{{ requests.meta.total }}</span> | ||||
|                         wyników | ||||
|                     </div> | ||||
|                     <nav class="relative z-0 inline-flex space-x-1"> | ||||
|                         <template | ||||
|                             v-for="(link, index) in requests.meta.links" | ||||
|                             :key="index" | ||||
|                         > | ||||
|                             <Component | ||||
|                                 :is="link.url ? 'InertiaLink' : 'span'" | ||||
|                                 :href="link.url" | ||||
|                                 :preserve-scroll="true" | ||||
|                                 class="relative inline-flex items-center px-4 py-2 border rounded-md text-sm font-medium" | ||||
|                                 :class="{ 'z-10 bg-blumilk-25 border-blumilk-500 text-blumilk-600': link.active, 'bg-white border-gray-300 text-gray-500': !link.active, 'hover:bg-blumilk-25': link.url, 'border-none': !link.url}" | ||||
|                                 v-text="link.label" | ||||
|                             /> | ||||
|                         </template> | ||||
|                     </nav> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { DotsVerticalIcon, PencilIcon, TrashIcon } from '@heroicons/vue/solid'; | ||||
| import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'; | ||||
|  | ||||
| export default { | ||||
|     name: 'UserIndex', | ||||
|     components: { | ||||
|         DotsVerticalIcon, | ||||
|         PencilIcon, | ||||
|         TrashIcon, | ||||
|         Menu, | ||||
|         MenuButton, | ||||
|         MenuItem, | ||||
|         MenuItems, | ||||
|     }, | ||||
|     props: { | ||||
|         requests: { | ||||
|             type: Object, | ||||
|             default: () => null, | ||||
|         }, | ||||
|     }, | ||||
| }; | ||||
| </script> | ||||
| @@ -323,6 +323,7 @@ export default { | ||||
|             {name: 'Użytkownicy', href: '/users', current: false}, | ||||
|             {name: 'Dostępne urlopy', href: '/vacation-limits', current: false}, | ||||
|             {name: 'Dni wolne', href: '/holidays', current: false}, | ||||
|             {name: 'Wnioski urlopowe', href: '/vacation-requests', current: false}, | ||||
|         ]; | ||||
|         const userNavigation = [ | ||||
|             {name: 'Your Profile', href: '#'}, | ||||
|   | ||||
| @@ -3,5 +3,15 @@ | ||||
|   "employment_contract": "Umowa o pracę", | ||||
|   "commission_contract": "Umowa zlecenie", | ||||
|   "b2b_contract": "Kontrakt B2B", | ||||
|   "board_member_contract": "Członek zarządu" | ||||
|   "board_member_contract": "Członek zarządu", | ||||
|   "vacation": "Urlop wypoczynkowy", | ||||
|   "vacation_on_request": "Urlop na żądanie", | ||||
|   "special_vacation": "Urlop okolicznościowy", | ||||
|   "childcare_vacation": "Opieka nad dzieckiem art 188 kp", | ||||
|   "training_vacation": "Urlop szkoleniowy", | ||||
|   "unpaid_vacation": "Urlop bezpłatny", | ||||
|   "volunteering_vacation": "Wolontariat", | ||||
|   "look_for_work_vacation": "Urlop na poszukiwanie pracy", | ||||
|   "time_in_lieu": "Odbiór za święto", | ||||
|   "sick_vacation": "Zwolnienie lekarskie" | ||||
| } | ||||
|   | ||||
| @@ -9,6 +9,7 @@ use Toby\Http\Controllers\LogoutController; | ||||
| use Toby\Http\Controllers\SelectYearPeriodController; | ||||
| use Toby\Http\Controllers\UserController; | ||||
| use Toby\Http\Controllers\VacationLimitController; | ||||
| use Toby\Http\Controllers\VacationRequestController; | ||||
|  | ||||
| Route::middleware("auth")->group(function (): void { | ||||
|     Route::get("/", fn() => inertia("Dashboard"))->name("dashboard"); | ||||
| @@ -22,6 +23,9 @@ Route::middleware("auth")->group(function (): void { | ||||
|     Route::get("/vacation-limits", [VacationLimitController::class, "edit"])->name("vacation.limits"); | ||||
|     Route::put("/vacation-limits", [VacationLimitController::class, "update"]); | ||||
|  | ||||
|     Route::get("/vacation-requests", [VacationRequestController::class, "index"]); | ||||
|     Route::get("/vacation-requests/create", [VacationRequestController::class, "create"]); | ||||
|  | ||||
|     Route::post("year-periods/{yearPeriod}/select", SelectYearPeriodController::class)->name("year-periods.select"); | ||||
| }); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user