diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..171d6b9a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,22 @@ +{ + "workbench.colorCustomizations": { + "activityBar.activeBackground": "#7c83e9", + "activityBar.background": "#7c83e9", + "activityBar.foreground": "#15202b", + "activityBar.inactiveForeground": "#15202b99", + "activityBarBadge.background": "#f6c8cb", + "activityBarBadge.foreground": "#15202b", + "commandCenter.border": "#e7e7e799", + "sash.hoverBorder": "#7c83e9", + "statusBar.background": "#505ae2", + "statusBar.foreground": "#e7e7e7", + "statusBarItem.hoverBackground": "#7c83e9", + "statusBarItem.remoteBackground": "#505ae2", + "statusBarItem.remoteForeground": "#e7e7e7", + "titleBar.activeBackground": "#505ae2", + "titleBar.activeForeground": "#e7e7e7", + "titleBar.inactiveBackground": "#505ae299", + "titleBar.inactiveForeground": "#e7e7e799" + }, + "peacock.color": "#505ae2" +} \ No newline at end of file diff --git a/app/api/.env.sample b/app/api/.env.sample index feb766ad..c39f43eb 100644 --- a/app/api/.env.sample +++ b/app/api/.env.sample @@ -18,7 +18,7 @@ DB_USERNAME=root DB_PASSWORD=password BROADCAST_DRIVER=log -CACHE_DRIVER=memcached +CACHE_DRIVER=file SESSION_DRIVER=file SESSION_LIFETIME=120 QUEUE_CONNECTION=sync @@ -27,13 +27,14 @@ REDIS_HOST=redis REDIS_PASSWORD=null REDIS_PORT=6379 -MAIL_DRIVER=smtp -MAIL_HOST=smtp.mailtrap.io -MAIL_PORT=587 -MAIL_USERNAME=14b3bc30ce304d -MAIL_PASSWORD=22b08660b859f6 -MAIL_FROM_ADDRESS="hello@example.com" -MAIL_FROM_NAME="${APP_NAME}" +MAIL_MAILER=smtp +MAIL_HOST=sandbox.smtp.mailtrap.io +MAIL_PORT=2525 +MAIL_USERNAME=USERNAME +MAIL_PASSWORD=PASSWORD +MAIL_ENCRYPTION=tls +MAIL_FROM_ADDRESS=no-reply@example.com +MAIL_FROM_NAME="Leave Management System" AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= @@ -55,3 +56,5 @@ VITE_PUSHER_HOST="${PUSHER_HOST}" VITE_PUSHER_PORT="${PUSHER_PORT}" VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" + +HOLIDAYS_YEAR="2025" diff --git a/app/api/app/Applications/Document/Controllers/DocumentController.php b/app/api/app/Applications/Document/Controllers/DocumentController.php new file mode 100644 index 00000000..dba1fecd --- /dev/null +++ b/app/api/app/Applications/Document/Controllers/DocumentController.php @@ -0,0 +1,82 @@ +documentService = $documentService; + } + + /** + * Get a JSON with all the users + * + * @return JsonResponse + */ + public function getAll(): JsonResponse + { + $leaveTypeDTOs = $this->documentService->getAll(); + return response()->json($leaveTypeDTOs); + } + + /** + * Get a JSON with a user by ID + * + * @param integer $id + * @return JsonResponse + */ + public function get(int $id): JsonResponse + { + $leaveTypeDTO = $this->documentService->get($id); + return response()->json($leaveTypeDTO); + } + + /** + * Get a paginated, filtered and sorted array of Users. + * This endpoint requires some data in the request. + * + * @param Request $request + * @return JsonResponse + */ + public function draw(Request $request): JsonResponse + { + try { + $data = $request->all(); + $leaveTypesDTO = $this->documentService->draw($data); + + return response()->json($leaveTypesDTO); + } catch (\InvalidArgumentException $e) { + // Handle specific exceptions like InvalidArgumentException + return response()->json([ + 'error' => 'Invalid Argument', + 'message' => $e->getMessage(), + ], 400); // Bad Request status code + } catch (\ValidationException $e) { + // Handle validation exceptions + return response()->json([ + 'error' => 'Validation Error', + 'message' => $e->getMessage(), + 'errors' => $e->errors(), + ], 422); // Unprocessable Entity status code + } catch (\Exception $e) { + // Handle any other general exceptions + return response()->json([ + 'error' => 'Server Error', + 'message' => $e->getMessage(), + ], 500); // Internal Server Error status code + } + } +} diff --git a/app/api/app/Applications/Document/DTO/DocumentDTO.php b/app/api/app/Applications/Document/DTO/DocumentDTO.php new file mode 100644 index 00000000..e552cb9d --- /dev/null +++ b/app/api/app/Applications/Document/DTO/DocumentDTO.php @@ -0,0 +1,86 @@ +file_name = $file_name; + $this->file_path = $file_path; + $this->id = $id; + $this->leave_request_id = $leave_request_id; + $this->user_id = $user_id; + } + + public static function fromRequest(Request $request): self + { + return new self( + $request->input('file_name'), + $request->input('file_path'), + $request->input('id', 0), + $request->input('leave_request_id', 0), + $request->input('user_id', 0), + ); + } + + public static function fromRequestForCreate(Request $request): self + { + return new self( + $request->input('file_name'), + $request->input('file_path'), + id: 0, + leave_request_id: 0, + user_id: 0, + ); + } + + public static function fromModel(Document $document): self + { + return new self( + $document->file_name, + $document->file_path, + $document->id, + $document->leave_request_id, + $document->user_id, + + ); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } + + public function toArray(): array + { + return [ + 'name' => $this->file_name, + 'slug' => $this->file_path, + 'id' => $this->id, + 'leave_request_id' => $this->leave_request_id, + 'user_id' => $this->user_id, + ]; + } + + public static function fromCollection(iterable $leaveTypes): array + { + return array_map(function (Document $leaveType) { + return self::fromModel($leaveType); + }, $leaveTypes->all()); + } +} diff --git a/app/api/app/Applications/Document/Model/Document.php b/app/api/app/Applications/Document/Model/Document.php new file mode 100644 index 00000000..cbb421f2 --- /dev/null +++ b/app/api/app/Applications/Document/Model/Document.php @@ -0,0 +1,11 @@ +app->routesAreCached()) { + // This is already done in the main RouteServiceProvider so not needed here + } else { + + $this->app->call([$this, 'map']); + + $this->app->booted(function () { + $this->app['router']->getRoutes()->refreshNameLookups(); + $this->app['router']->getRoutes()->refreshActionLookups(); + }); + } + } + + /** + * Register the application services. + * + * @return void + */ + public function register() + { + $this->app->bind(DocumentRepositoryInterface::class, DocumentRepository::class); + $this->app->bind(DocumentServiceInterface::class, DocumentService::class); + } + + public function map() + { + Route::prefix('api') + ->middleware('api') + ->namespace($this->namespace) + ->group(base_path('routes/Document/api.php')); + } + +} diff --git a/app/api/app/Applications/Document/Repositories/DocumentRepository.php b/app/api/app/Applications/Document/Repositories/DocumentRepository.php new file mode 100644 index 00000000..fd55a678 --- /dev/null +++ b/app/api/app/Applications/Document/Repositories/DocumentRepository.php @@ -0,0 +1,55 @@ +document = $document; + } + + private const COLUMNS_MAP = [ + 'file_name' => 'documents.file_name', + 'file_path' => 'documents.file_path', + ]; + + public function getAll(): array + { + $documents = $this->document::all(); + return DocumentDTO::fromCollection($documents); + } + + public function get($id): Document + { + return $this->document::findOrFail($id); + } + + public function draw($data): StarterPaginator + { + // $paginatedUsers = $this->prepareDatatableQuery($data, [User::ADMIN, User::EDITOR, User::COLLABORATOR]); + $query = $this->document->query(); + + // $query->whereIn('roles.name', $roles); + + if (array_key_exists($data['column'], self::COLUMNS_MAP)) { + $query->orderBy(self::COLUMNS_MAP[$data['column']], $data['dir']); + } + + $userId = $data['userId']; + if ($userId ) { + $query->where('documents.user_id', '=', $userId); + } + + return $query->paginate($data['length']); + } +} diff --git a/app/api/app/Applications/Document/Repositories/DocumentRepositoryInterface.php b/app/api/app/Applications/Document/Repositories/DocumentRepositoryInterface.php new file mode 100644 index 00000000..eb55cdc3 --- /dev/null +++ b/app/api/app/Applications/Document/Repositories/DocumentRepositoryInterface.php @@ -0,0 +1,33 @@ + 'required|max:255|min:2', + 'slug' => 'required|max:255|min:2', + 'color' => 'required', + ]; + + return $rules; + } + public function messages(){ + return [ + 'name.required' => 'The name field is required', + 'slug.required' => 'The slug field is required', + 'color.required' => 'The color field is required', + ]; + } +} diff --git a/app/api/app/Applications/Document/Requests/NewLeaveTypeRequest.php b/app/api/app/Applications/Document/Requests/NewLeaveTypeRequest.php new file mode 100644 index 00000000..6b75ffaa --- /dev/null +++ b/app/api/app/Applications/Document/Requests/NewLeaveTypeRequest.php @@ -0,0 +1,57 @@ + 'required|max:255|min:2', + 'last_name' => 'required|max:255|min:2', + 'email' => 'required|email|min:2|max:255|unique:users,email,'.$this->segment(3), + 'password' => 'sometimes|between:6,30|confirmed', + 'roles' => 'required|exists:roles,id', + ]; + + return $rules; + } + public function messages(){ + return [ + 'first_name.required' => 'users.first_name.required', + 'first_name.max' => 'users.first_name.max', + 'first_name.min' => 'users.first_name.min', + 'last_name.required' => 'users.last_name.required', + 'last_name.max' => 'users.last_name.max', + 'last_name.min' => 'users.last_name.min', + 'email.required' => 'users.email.required', + 'email.email' => 'users.email.invalid', + 'email.max' => 'users.email.max', + 'email.min' => 'users.email.min', + 'email.unique' => 'users.email.unique', + 'roles.required' => 'users.roles.required', + 'roles.exists' => 'users.roles.exists', + 'password.required' => 'users.password.required', + 'password.between' => 'users.password.between', + 'password.confirmed' => 'users.password.confirmed', + ]; + } +} diff --git a/app/api/app/Applications/Document/Services/DocumentService.php b/app/api/app/Applications/Document/Services/DocumentService.php new file mode 100644 index 00000000..6281da4b --- /dev/null +++ b/app/api/app/Applications/Document/Services/DocumentService.php @@ -0,0 +1,51 @@ +documentRepository = $documentRepository; + } + + public function getAll(): array + { + return $this->documentRepository->getAll(); + } + + public function get($id): DocumentDTO + { + return DocumentDTO::fromModel( + $this->documentRepository->get($id) + ); + } + + public function draw(array $data): array + { + $data['columns'] = ['users.first_name', 'users.last_name', 'email', 'roles.id', 'users.is_disabled']; + $data['length'] = $data['length'] ?? 25; + $data['column'] = $data['column'] ?? 'users.first_name'; + $data['dir'] = $data['dir'] ?? 'asc'; + $data['search'] = $data['search'] ?? ''; + $data['userId'] = $data['userId'] ?? ''; + $data['draw'] = $data['draw'] ?? 1; + + $usersCollection = $this->documentRepository->draw($data); + $usersDTOs = $usersCollection->getCollection()->map(function ($user) { + return DocumentDTO::fromModel($user); + }); + + return [ + 'data' => $usersDTOs, + 'pagination' => $usersCollection->toArray()['pagination'], + ]; + } +} diff --git a/app/api/app/Applications/Document/Services/DocumentServiceInterface.php b/app/api/app/Applications/Document/Services/DocumentServiceInterface.php new file mode 100644 index 00000000..96ad680e --- /dev/null +++ b/app/api/app/Applications/Document/Services/DocumentServiceInterface.php @@ -0,0 +1,31 @@ +leaveRequestService = $leaveRequestService; + } + + /** + * Get a JSON with all the users + * + * @return JsonResponse + */ + public function getAll(): JsonResponse + { + $leaveRequestDTOs = $this->leaveRequestService->getAll(); + return response()->json($leaveRequestDTOs); + } + + /** + * Get a JSON with all the users + * + * @return JsonResponse + */ + public function getApproved(): JsonResponse + { + $leaveRequestDTOs = $this->leaveRequestService->getApproved(); + return response()->json($leaveRequestDTOs); + } + + /** + * Get a JSON with all the users + * + * @return JsonResponse + */ + public function getPending(): JsonResponse + { + $leaveRequestDTOs = $this->leaveRequestService->getPending(); + return response()->json($leaveRequestDTOs); + } + + /** + * Get a JSON with a user by ID + * + * @param integer $id + * @return JsonResponse + */ + public function get(int $id): JsonResponse + { + $leaveRequestDTO = $this->leaveRequestService->get($id); + return response()->json($leaveRequestDTO); + } + + /** + * Store user and get JSON with a user response + * + * @param NewLeaveRequestRequest $request + * @return JsonResponse + */ + public function create(NewLeaveRequestRequest $request): JsonResponse + { + $leaveRequestDTO = LeaveRequestDTO::fromRequestForCreate($request); + $newLeaveRequestDTO = $this->leaveRequestService->create($leaveRequestDTO); + + return response()->json($newLeaveRequestDTO); + } + + /** + * Update user + * + * @param LeaveRequestRequest $request + * @return JsonResponse + */ + public function update(LeaveRequestRequest $request): JsonResponse + { + $leaveRequestId = Route::current()->parameter('id'); + $dto = LeaveRequestDTO::fromRequest($request); + $leaveRequestDTO = $this->leaveRequestService->update( + $leaveRequestId, + $dto + ); + return response()->json($leaveRequestDTO); + } + + /** + * Approve request + * + * @param Request $request + * @return JsonResponse + */ + public function approve(Request $request): JsonResponse + { + $leaveRequestId = Route::current()->parameter('id'); + $statusConfirmed = 2; + $dto = LeaveRequestDTO::fromRequest($request); + $leaveRequestDTO = $this->leaveRequestService->approve( + $leaveRequestId, + leaveRequestData: $dto, + isConfirmed: $statusConfirmed + ); + return response()->json($leaveRequestDTO); + } + + /** + * Decline Request + * + * @param Request $request + * @return JsonResponse + */ + public function decline(Request $request): JsonResponse + { + $leaveRequestId = Route::current()->parameter('id'); + $statusConfirmed = 1; + $dto = LeaveRequestDTO::fromRequest($request); + $leaveRequestDTO = $this->leaveRequestService->decline( + $leaveRequestId, + leaveRequestData: $dto, + isConfirmed: $statusConfirmed + ); + return response()->json($leaveRequestDTO); + } + /** + * Delete user + * + * @return string + */ + public function delete() + { + $leaveRequestId = Route::current()->parameter('id'); + return $this->leaveRequestService->delete($leaveRequestId); + } + + /** + * Get a paginated, filtered and sorted array of Users. + * This endpoint requires some data in the request. + * + * @param Request $request + * @return JsonResponse + */ + public function draw(Request $request): JsonResponse + { + try { + $data = $request->all(); + $leaveTypesDTO = $this->leaveRequestService->draw($data); + + return response()->json($leaveTypesDTO); + } catch (\InvalidArgumentException $e) { + // Handle specific exceptions like InvalidArgumentException + return response()->json([ + 'error' => 'Invalid Argument', + 'message' => $e->getMessage(), + ], 400); // Bad Request status code + } catch (\ValidationException $e) { + // Handle validation exceptions + return response()->json([ + 'error' => 'Validation Error', + 'message' => $e->getMessage(), + 'errors' => $e->errors(), + ], 422); // Unprocessable Entity status code + } catch (\Exception $e) { + // Handle any other general exceptions + return response()->json([ + 'error' => 'Server Error', + 'message' => $e->getMessage(), + ], 500); // Internal Server Error status code + } + } + public function downloadLeaveRequestPDF(string $file_name) + { + $filePath = storage_path( "app/public/" . $file_name); + + if (!file_exists($filePath)) { + abort(404, "PDF not found."); + } + + return response()->download($filePath, $file_name, [ + 'Content-Type' => 'application/pdf', + 'Content-Disposition' => 'attachment; filename="' . $file_name . '"' + ]); + } + +} diff --git a/app/api/app/Applications/LeaveRequest/DTO/LeaveRequestDTO.php b/app/api/app/Applications/LeaveRequest/DTO/LeaveRequestDTO.php new file mode 100644 index 00000000..2a57fa2a --- /dev/null +++ b/app/api/app/Applications/LeaveRequest/DTO/LeaveRequestDTO.php @@ -0,0 +1,134 @@ +user_id = $user_id; + $this->leave_type_id = $leave_type_id; + $this->start_date = $start_date; + $this->end_date = $end_date; + $this->reason = $reason; + $this->request_to = $request_to; + $this->confirmed_by = $confirmed_by; + $this->is_confirmed = $is_confirmed; + $this->status = $status; + $this->id = $id; + $this->user = $user; + $this->leaveType = $leaveType; + $this->requestToUser = $requestToUser; + } + + public static function fromRequest(Request $request): self + { + return new self( + user_id: $request->input('user_id'), + leave_type_id: $request->input('leave_type_id'), + start_date: $request->input('start_date'), + end_date: $request->input('end_date'), + reason: $request->input('reason', ''), + request_to: $request->input('request_to'), + confirmed_by: $request->input('confirmed_by', 0), + is_confirmed: $request->input('is_confirmed', 0), + status: $request->input('status', 0), + id: $request->input('id', 0) + ); + } + + public static function fromRequestForCreate(Request $request): self + { + return new self( + user_id: $request->input('user_id'), + leave_type_id: $request->input('leave_type_id'), + start_date: $request->input('start_date'), + end_date: $request->input('end_date'), + reason: $request->input('reason'), + request_to: $request->input('request_to'), + confirmed_by: 0, + is_confirmed: 0, + status: 0, + id: 0 + // Excluding User and LeaveType (default to null) + ); + } + + public static function fromModel(LeaveRequest $leaveRequest): self + { + return new self( + user_id: $leaveRequest->user_id, + leave_type_id: $leaveRequest->leave_type_id, + start_date: $leaveRequest->start_date, + end_date: $leaveRequest->end_date, + reason: $leaveRequest->reason, + request_to: $leaveRequest->request_to, + confirmed_by: $leaveRequest->confirmed_by, + is_confirmed: $leaveRequest->is_confirmed, + status: $leaveRequest->status, + id: $leaveRequest->id, + user: $leaveRequest->user, + leaveType: $leaveRequest->leaveType, + requestToUser: $leaveRequest->requestToUser + ); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } + + public function toArray(): array + { + return [ + 'user_id' => $this->user_id, + 'leave_type_id' => $this->leave_type_id, + 'start_date' => $this->start_date, + 'end_date' => $this->end_date, + 'reason' => $this->reason, + 'request_to' => $this->request_to, + 'confirmed_by' => $this->confirmed_by, + 'is_confirmed' => $this->is_confirmed, + 'status' => $this->status, + 'id' => $this->id, + ]; + } + + public static function fromCollection(iterable $leaveRequests): array + { + return array_map(fn (LeaveRequest $leaveRequest) => self::fromModel($leaveRequest), $leaveRequests->all()); + } +} diff --git a/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestCancelation.php b/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestCancelation.php new file mode 100644 index 00000000..ea08a172 --- /dev/null +++ b/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestCancelation.php @@ -0,0 +1,44 @@ +leaveRequest = $leaveRequest; + $this->pdfPath = $pdfPath; + } + + /** + * Build the message. + */ + public function build(): self + { + $formattedStartDate = \Carbon\Carbon::parse($this->leaveRequest->start_date)->format('d M Y'); + $formattedEndDate = $this->leaveRequest->end_date + ? \Carbon\Carbon::parse($this->leaveRequest->end_date)->format('d M Y') + : null; + $subject = $this->leaveRequest->user->first_name . ' ' . $this->leaveRequest->user->last_name . ': ' . $this->leaveRequest->leaveType->name . ': ' . $formattedStartDate . ($this->leaveRequest->end_date ? ' to ' . $formattedEndDate : ''); + $email = $this->subject($subject .' (Canceled)') + ->view('emails.leave_request_cancelation') + ->with([ + 'leaveRequest' => $this->leaveRequest, + ]); + + return $email; + } +} diff --git a/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestConfirmation.php b/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestConfirmation.php new file mode 100644 index 00000000..531bb5c9 --- /dev/null +++ b/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestConfirmation.php @@ -0,0 +1,44 @@ +leaveRequest = $leaveRequest; + } + + /** + * Build the message. + */ + public function build(): self + { + $formattedStartDate = \Carbon\Carbon::parse($this->leaveRequest->start_date)->format('d M Y'); + $formattedEndDate = $this->leaveRequest->end_date + ? \Carbon\Carbon::parse($this->leaveRequest->end_date)->format('d M Y') + : null; + + $subject =$this->leaveRequest->user->first_name . ' ' . $this->leaveRequest->user->last_name . ': ' . $this->leaveRequest->leaveType->name . ': ' . $formattedStartDate . ($this->leaveRequest->end_date ? ' to ' . $formattedEndDate : ''); + $email = $this->subject($subject .' (Approved)') + ->view('emails.leave_request_confirmation') + ->with([ + 'leaveRequest' => $this->leaveRequest, + ]); + + + return $email; + } +} diff --git a/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestConfirmationPDF.php b/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestConfirmationPDF.php new file mode 100644 index 00000000..eedc178f --- /dev/null +++ b/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestConfirmationPDF.php @@ -0,0 +1,48 @@ +leaveRequest = $leaveRequest; + $this->pdfPath = $pdfPath; + } + + /** + * Build the message. + */ + public function build(): self + { + $subject = $this->leaveRequest->user->country == 1 ? 'ESOF Starter' : 'ESOF BG'; + $email = $this->subject( $subject . ' PDF') + ->view('emails.leave_request_confirmation_pdf') + ->with([ + 'leaveRequest' => $this->leaveRequest, + ]); + + + if ($this->pdfPath && file_exists($this->pdfPath)) { + $email->attach($this->pdfPath, [ + 'as' => basename($this->pdfPath), + 'mime' => 'application/pdf', + ]); + } + + return $email; + } +} diff --git a/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestDeclining.php b/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestDeclining.php new file mode 100644 index 00000000..99a2cd71 --- /dev/null +++ b/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestDeclining.php @@ -0,0 +1,41 @@ +leaveRequest = $leaveRequest; + } + + /** + * Build the message. + */ + public function build(): self + { + $formattedStartDate = \Carbon\Carbon::parse($this->leaveRequest->start_date)->format('d M Y'); + $formattedEndDate = $this->leaveRequest->end_date + ? \Carbon\Carbon::parse($this->leaveRequest->end_date)->format('d M Y') + : null; + + $subject = $this->leaveRequest->leaveType->name . ': ' . $formattedStartDate . ($this->leaveRequest->end_date ? ' to ' . $formattedEndDate : ''); + return $this->subject($subject . ' (Declined)') + ->view('emails.leave_request_decline') + ->with([ + 'leaveRequest' => $this->leaveRequest, + ]); + } +} diff --git a/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestNotification.php b/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestNotification.php new file mode 100644 index 00000000..c756aa7f --- /dev/null +++ b/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestNotification.php @@ -0,0 +1,40 @@ +leaveRequest = $leaveRequest; + } + + /** + * Build the message. + */ + public function build(): self + { + $formattedStartDate = \Carbon\Carbon::parse($this->leaveRequest->start_date)->format('d M Y'); + $formattedEndDate = $this->leaveRequest->end_date + ? \Carbon\Carbon::parse($this->leaveRequest->end_date)->format('d M Y') + : null; + $subject =$this->leaveRequest->user->first_name . ' ' . $this->leaveRequest->user->last_name . ': ' . $this->leaveRequest->leaveType->name . ': ' . $formattedStartDate . ($this->leaveRequest->end_date ? ' to ' . $formattedEndDate : ''); + return $this->subject('Requested: ' . $subject) + ->view('emails.leave_request_notification') + ->with([ + 'leaveRequest' => $this->leaveRequest, + ]); + } +} diff --git a/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestNotificationUpdate.php b/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestNotificationUpdate.php new file mode 100644 index 00000000..23dee020 --- /dev/null +++ b/app/api/app/Applications/LeaveRequest/Mail/LeaveRequestNotificationUpdate.php @@ -0,0 +1,41 @@ +leaveRequest = $leaveRequest; + } + + /** + * Build the message. + */ + public function build(): self + { + $formattedStartDate = \Carbon\Carbon::parse($this->leaveRequest->start_date)->format('d M Y'); + $formattedEndDate = $this->leaveRequest->end_date + ? \Carbon\Carbon::parse($this->leaveRequest->end_date)->format('d M Y') + : null; + + $subject =$this->leaveRequest->user->first_name . ' ' . $this->leaveRequest->user->last_name . ': ' . $this->leaveRequest->leaveType->name . ': ' . $formattedStartDate . ($this->leaveRequest->end_date ? ' to ' . $formattedEndDate : ''); + return $this->subject($subject . ' (Update)') + ->view('emails.leave_request_notification') + ->with([ + 'leaveRequest' => $this->leaveRequest, + ]); + } +} diff --git a/app/api/app/Applications/LeaveRequest/Model/LeaveRequest.php b/app/api/app/Applications/LeaveRequest/Model/LeaveRequest.php new file mode 100644 index 00000000..00c7ef44 --- /dev/null +++ b/app/api/app/Applications/LeaveRequest/Model/LeaveRequest.php @@ -0,0 +1,78 @@ + + */ + protected $fillable = [ + 'user_id', + 'leave_type_id', + 'start_date', + 'end_date', + 'status', + 'reason', + 'request_to', + 'confirmed_by', + 'is_confirmed' + ]; + + /** + * The attributes that should be hidden for serialization. + * + * @var array + */ + protected $hidden = [ + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + + ]; + + /** + * The accessors to append to the model's array form. + * + * @var array + */ + protected $appends = [ + ]; + + /** + * Relationship with the User who requested the leave. + */ + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } + + /** + * Relationship with the LeaveType. + */ + public function leaveType() + { + return $this->belongsTo(LeaveType::class, 'leave_type_id'); + } + + public function requestToUser() + { + return $this->belongsTo(User::class, 'request_to'); + } +} diff --git a/app/api/app/Applications/LeaveRequest/Providers/LeaveRequestServiceProvider.php b/app/api/app/Applications/LeaveRequest/Providers/LeaveRequestServiceProvider.php new file mode 100644 index 00000000..1d08d7b0 --- /dev/null +++ b/app/api/app/Applications/LeaveRequest/Providers/LeaveRequestServiceProvider.php @@ -0,0 +1,58 @@ +app->routesAreCached()) { + // This is already done in the main RouteServiceProvider so not needed here + } else { + + $this->app->call([$this, 'map']); + + $this->app->booted(function () { + $this->app['router']->getRoutes()->refreshNameLookups(); + $this->app['router']->getRoutes()->refreshActionLookups(); + }); + } + } + + /** + * Register the application services. + * + * @return void + */ + public function register() + { + $this->app->bind(LeaveRequestRepositoryInterface::class, LeaveRequestRepository::class); + $this->app->bind(LeaveRequestServiceInterface::class, LeaveRequestService::class); + } + + public function map() + { + Route::prefix('api') + ->middleware('api') + ->namespace($this->namespace) + ->group(base_path('routes/LeaveRequest/api.php')); + } + +} diff --git a/app/api/app/Applications/LeaveRequest/Repositories/LeaveRequestRepository.php b/app/api/app/Applications/LeaveRequest/Repositories/LeaveRequestRepository.php new file mode 100644 index 00000000..ff684699 --- /dev/null +++ b/app/api/app/Applications/LeaveRequest/Repositories/LeaveRequestRepository.php @@ -0,0 +1,352 @@ +leaveRequest = $leaveRequest; + $this->user = $user; + } + + private const COLUMNS_MAP = [ + 'leave_type_id' => 'leave_requests.leave_type_id', + 'request_to' => 'leave_requests.request_to', + 'status' => 'leave_requests.is_confirmed', + 'start_date' => 'leave_requests.start_date', + 'end_date' => 'leave_requests.end_date' + ]; + + // Get all leaveRequests + public function getAll(): array + { + return LeaveRequestDTO::fromCollection($this->leaveRequest::all()); + } + + public function getApproved(): array + { + $query = $this->leaveRequest->with(['user', 'leaveType']); + + $query->where('is_confirmed', '=', 2); + + return $query->whereNull('deleted_at')->get()->toArray(); + } + + public function getPending(): array + { + $query = $this->leaveRequest->with(['user', 'leaveType']); + + $user = Auth::user(); + $query->where('request_to', '=', $user->id); + + $query->where('is_confirmed', '=', 0); + + return $query->whereNull('deleted_at')->get()->toArray(); + } + // Get Leave Request by Id + public function get($id): LeaveRequest + { + return $this->leaveRequest::findOrFail($id); + } + + // Create a new request + public function create(LeaveRequestDTO $leaveRequestDTO): LeaveRequest + { + $leaveRequest = $this->leaveRequest->create($leaveRequestDTO->toArray()); + if ($leaveRequest->leave_type_id == 6) { + $this->confirm($leaveRequest->id, $leaveRequestDTO, 2); + } else { + $this->sendRequestEmail($leaveRequest, false); + } + + return $leaveRequest; + } + + public function update(int $leaveRequestId, LeaveRequestDTO $leaveRequestData): LeaveRequest + { + $leaveRequest = $this->get($leaveRequestId); + $leaveRequestData->is_confirmed = 0; + $leaveRequest->update($leaveRequestData->toArray()); + $this->sendRequestEmail($leaveRequest, true); + return $leaveRequest; + } + + public function confirm(int $leaveRequestId, LeaveRequestDTO $leaveRequestData, int $isConfirmed): LeaveRequest + { + $leaveRequest = $this->get($leaveRequestId); + $user = Auth::user(); + + $leaveRequest->update([ + ...$leaveRequestData->toArray(), + 'is_confirmed' => $isConfirmed, + 'confirmed_by' => $user->id + ]); + + if ($isConfirmed == 2) { + if ($leaveRequest->leave_type_id == 3 || $leaveRequest->leave_type_id == 4) { + $this->createLeaveRequestPDF($leaveRequest->user, $leaveRequest); + $this->sendConfirmationAccountentsEmail($leaveRequest); + } + + if ($leaveRequest->user && $leaveRequestData->leave_type_id == 3) { + $this->deductPaidLeaves($leaveRequest->user, $leaveRequestData); + } + } + + $this->sendRequestConfirmationEmail($leaveRequest); + return $leaveRequest; + } + + public function delete(int $id) + { + $leaveRequest = $this->leaveRequest::findOrFail($id); + $this->sendRequestCancelationEmail($leaveRequest); + + return $leaveRequest->delete(); + } + + public function draw($data): StarterPaginator + { + $user = Auth::user(); + $query = $this->leaveRequest->with(['user', 'leaveType']); + + if (isset(self::COLUMNS_MAP[$data['column']])) { + $query->orderBy(self::COLUMNS_MAP[$data['column']], $data['dir']); + } + // Search by keyword regardless on user first name last name or email and leave type + if ($search = $data['search']) { + $query->where(function ($q) use ($search) { + $q->where('leave_requests.reason', 'like', "%$search%") + ->orWhereHas('user', fn($q) => $q->where('first_name', 'like', "%$search%") + ->orWhere('last_name', 'like', "%$search%") + ->orWhere('email', 'like', "%$search%")) + ->orWhereHas('leaveType', fn($q) => $q->where('name', 'like', "%$search%")); + }); + } + + $this->applyUserRoleFilter($query, $user, $data['userId'] ?? null); + return $query->whereNull('deleted_at')->paginate($data['length']); + } + + private function applyUserRoleFilter($query, $user, $calendarUserId) + { + $roleId = $user->getRoleAttribute(); + + if ($calendarUserId) { + $query->where('leave_requests.user_id', $calendarUserId) + ->where('leave_requests.is_confirmed', 2); + } else { + $query->where('leave_requests.user_id', $user->id); + } + } + + private function sendRequestEmail(LeaveRequest $leaveRequest, bool $isUpdate) + { + $recipients = $this->getRecipients($leaveRequest); + + $mailClass = $isUpdate ? LeaveRequestNotificationUpdate::class : LeaveRequestNotification::class; + Mail::to($recipients)->send(new $mailClass($leaveRequest)); + } + + private function sendRequestConfirmationEmail(LeaveRequest $leaveRequest) + { + $recipients = $this->getRecipients($leaveRequest); + + $mailClass = match ($leaveRequest->is_confirmed) { + 1 => LeaveRequestDeclining::class, + 2 => LeaveRequestConfirmation::class, + default => null, + }; + + if ($mailClass) { + Mail::to($recipients)->send(new $mailClass($leaveRequest)); + } + } + + private function sendRequestCancelationEmail(LeaveRequest $leaveRequest) + { + $recipients = $this->getRecipients($leaveRequest); + + Mail::to($recipients)->send(new LeaveRequestCancelation($leaveRequest)); + } + + + private function sendConfirmationAccountentsEmail(LeaveRequest $leaveRequest) { + $administrationEmails = User::role(User::COLLABORATOR) + ->where('country', '=', $leaveRequest->user->country) + ->pluck('email') + ->toArray(); + + if ($leaveRequest->is_confirmed == 2) { + $document = Document::where('leave_request_id', $leaveRequest->id)->first(); + Mail::to($administrationEmails)->send(new LeaveRequestConfirmationPDF($leaveRequest, $document ? Storage::disk('public')->path($document->file_path) : null)); + } + } + + private function getRecipients(LeaveRequest $leaveRequest) + { + $manager = $this->user->find($leaveRequest->request_to); + $requestedUser = $leaveRequest->user; + + if ($leaveRequest->is_confirmed == 2) { + return array_filter([ + $requestedUser?->email, + ...User::role(User::MANAGER)->pluck('email')->toArray(), + ...User::role(User::ADMIN)->pluck('email')->toArray() + ]); + } else if ($leaveRequest->is_confirmed == 1) { + return $requestedUser->email; + } else if ($leaveRequest->is_confirmed == 0) { + return $manager->email; + } + + + } + + private function deductPaidLeaves(User $user, LeaveRequestDTO $leaveRequestData): void + { + $startDate = new DateTime($leaveRequestData->start_date); + $endDate = $leaveRequestData->end_date ? new DateTime($leaveRequestData->end_date) : $startDate; + + $nationalHolidays = NationalHoliday::whereYear('date', $startDate->format('Y')) + ->where('country', $user->country == 1 ? 'Macedonia' : 'Bulgaria') + ->pluck('date') + ->toArray(); + + $leaveDays = iterator_count( + new DatePeriod($startDate, new DateInterval('P1D'), $endDate->modify('+1 day')) + ); + + $leaveDays -= count(array_filter( + iterator_to_array(new DatePeriod($startDate, new DateInterval('P1D'), $endDate->modify('+1 day'))), + fn($date) => in_array($date->format('N'), [6, 7]) || in_array($date->format('Y-m-d'), $nationalHolidays) + )); + + $user->update(['paid_leaves_left' => max(0, $user->paid_leaves_left - $leaveDays)]); + } + + private function createLeaveRequestPDF(User $user, LeaveRequest $leaveRequest): void + { + $isSingleDay = $leaveRequest->end_date === null; + $nowDate = $this->formatDate(now()); + // Cannot print in PDF + // Create a Cyrilyc Data for each user on create in a separate table, with foreign user_id + $fullNameCyrilic = transliterator_transliterate('Latin-Cyrillic', $user->first_name) . ' ' . transliterator_transliterate('Latin-Cyrillic', $user->last_name); + $start_date = $this->formatDate($leaveRequest->start_date); + + if ($isSingleDay) { + $leaveDays = 1; + } else { + $end_date = $this->formatDate($leaveRequest->end_date); + // Calculate valid leave days excluding weekends and national holidays + $startDate = new DateTime($leaveRequest->start_date); + $endDate = $leaveRequest->end_date ? new DateTime($leaveRequest->end_date) : $startDate; + + $nationalHolidays = NationalHoliday::whereYear('date', $startDate->format('Y')) + ->where('country', $user->country == 1 ? 'Macedonia' : 'Bulgaria') + ->pluck('date') + ->toArray(); + + $leaveDays = iterator_count( + new DatePeriod($startDate, new DateInterval('P1D'), $endDate->modify('+1 day')) + ); + + $leaveDays -= count(array_filter( + iterator_to_array(new DatePeriod($startDate, new DateInterval('P1D'), $endDate->modify('+1 day'))), + fn($date) => in_array($date->format('N'), [6, 7]) || in_array($date->format('Y-m-d'), $nationalHolidays) + )); + } + $userCountry = $leaveRequest->user->country; + + $pdf = new Fpdi(); + $pdf->AddPage(); + if ($userCountry == 1) { + if ($leaveRequest->leave_type_id == 3) { + $pdf->setSourceFile(public_path('MK_template_paid.pdf')); + } else { + $pdf->setSourceFile(public_path('MK_template_unpaid.pdf')); + } + } else { + if ($leaveRequest->leave_type_id == 3) { + $pdf->setSourceFile(public_path('BG_template_paid.pdf')); + } else { + $pdf->setSourceFile(public_path('BG_template_unpaid.pdf')); + } + } + + $tplIdx = $pdf->importPage(1); + $pdf->useTemplate($tplIdx, 0, 0, 210); + $pdf->SetFont('Arial', '', 11); + if ($userCountry !== 1) { + $pdf->SetXY(111, 79); + $pdf->Write(0, $user->first_name . " " . $user->last_name); + $pdf->SetXY($leaveRequest->leave_type_id == 3 ? 92 : 80, 135); + $pdf->Write(0, $leaveDays ?? 'N/A'); + $pdf->SetXY(44, 141); + $pdf->Write(0, $start_date ?? 'N/A'); + $pdf->SetXY(44, 147); + $pdf->Write(0, $end_date ?? ''); + $pdf->SetXY(24,215); + $pdf->Write(0, $nowDate ?? 'N/A'); + } else { + $pdf->SetXY(111, 77); + $pdf->Write(0, $user->first_name . " " . $user->last_name); + $pdf->SetXY(65, 118); + $pdf->Write(0, $leaveDays ?? 'N/A'); + $pdf->SetXY(124, 118); + $pdf->Write(0, $start_date ?? 'N/A'); + $pdf->SetXY(150, 118); + $pdf->Write(0, $end_date ?? ''); + $pdf->SetXY(24,193); + $pdf->Write(0, $nowDate ?? 'N/A'); + } + + $pdfDirectory = storage_path("app/public/"); + if (!file_exists($pdfDirectory)) { + mkdir($pdfDirectory, 0777, true); + } + + $fileName = $user->first_name . "_" . $user->last_name ."_" .str_replace('-', '_', $leaveRequest->start_date) . ".pdf"; + $pdfPath = $fileName; + $fullPath = storage_path("app/public/" . $pdfPath); + + $pdf->Output($fullPath, 'F'); + Document::create([ + 'user_id' => $leaveRequest->user_id, + 'leave_request_id' => $leaveRequest->id, + 'file_path' => $pdfPath, + 'file_name' => $fileName + ]); + } + + private function formatDate(string $date): string + { + return date('d m Y', strtotime($date)); + } +} diff --git a/app/api/app/Applications/LeaveRequest/Repositories/LeaveRequestRepositoryInterface.php b/app/api/app/Applications/LeaveRequest/Repositories/LeaveRequestRepositoryInterface.php new file mode 100644 index 00000000..771513c5 --- /dev/null +++ b/app/api/app/Applications/LeaveRequest/Repositories/LeaveRequestRepositoryInterface.php @@ -0,0 +1,70 @@ + 'required', + 'leave_type_id' => 'required', + 'start_date' => 'required|date|after_or_equal:today', + 'end_date' => 'sometimes|date|after_or_equal:start_date', + ]; + + return $rules; + } + public function messages(){ + return [ + 'first_name.required' => 'users.first_name.required', + 'first_name.max' => 'users.first_name.max', + 'first_name.min' => 'users.first_name.min', + 'last_name.required' => 'users.last_name.required', + 'last_name.max' => 'users.last_name.max', + 'last_name.min' => 'users.last_name.min', + 'email.required' => 'users.email.required', + 'email.email' => 'users.email.invalid', + 'email.max' => 'users.email.max', + 'email.min' => 'users.email.min', + 'email.unique' => 'users.email.unique', + 'roles.required' => 'users.roles.required', + 'roles.exists' => 'users.roles.exists', + 'password.required' => 'users.password.required', + 'password.between' => 'users.password.between', + 'password.confirmed' => 'users.password.confirmed', + ]; + } +} diff --git a/app/api/app/Applications/LeaveRequest/Requests/NewLeaveRequestRequest.php b/app/api/app/Applications/LeaveRequest/Requests/NewLeaveRequestRequest.php new file mode 100644 index 00000000..5038c039 --- /dev/null +++ b/app/api/app/Applications/LeaveRequest/Requests/NewLeaveRequestRequest.php @@ -0,0 +1,42 @@ + 'required', + 'leave_type_id' => 'required', + 'start_date' => 'required|date|after_or_equal:today', + 'end_date' => 'sometimes|date|after_or_equal:start_date', + ]; + + return $rules; + } + public function messages(){ + return [ + 'request_to.required' => 'The assign to field is required', + 'leave_type_id.required' => 'The leave type field is required', + ]; + } +} diff --git a/app/api/app/Applications/LeaveRequest/Services/LeaveRequestService.php b/app/api/app/Applications/LeaveRequest/Services/LeaveRequestService.php new file mode 100644 index 00000000..f2e9cfb5 --- /dev/null +++ b/app/api/app/Applications/LeaveRequest/Services/LeaveRequestService.php @@ -0,0 +1,91 @@ +leaveRequestRepository = $leaveRequestRepository; + } + + public function getAll(): array + { + return $this->leaveRequestRepository->getAll(); + } + + public function getApproved(): array + { + return $this->leaveRequestRepository->getApproved(); + } + + public function getPending(): array + { + return $this->leaveRequestRepository->getPending(); + } + + public function get($id): LeaveRequestDTO + { + return LeaveRequestDTO::fromModel( + $this->leaveRequestRepository->get($id) + ); + } + + public function create(LeaveRequestDTO $leaveRequestData): LeaveRequestDTO + { + $leaveType = $this->leaveRequestRepository->create($leaveRequestData); + return LeaveRequestDTO::fromModel($leaveType); + } + + public function update(int $leaveRequestId, LeaveRequestDTO $leaveRequestData): LeaveRequestDTO + { + $leaveType = $this->leaveRequestRepository->update($leaveRequestId, $leaveRequestData); + return LeaveRequestDTO::fromModel($leaveType); + } + + public function approve(int $leaveRequestId, LeaveRequestDTO $leaveRequestData, int $isConfirmed): LeaveRequestDTO + { + $leaveRequest = $this->leaveRequestRepository->confirm($leaveRequestId, $leaveRequestData, $isConfirmed); + return LeaveRequestDTO::fromModel($leaveRequest); + } + + public function decline(int $leaveRequestId, LeaveRequestDTO $leaveRequestData, int $isConfirmed): LeaveRequestDTO + { + $leaveRequest = $this->leaveRequestRepository->confirm($leaveRequestId, $leaveRequestData, $isConfirmed); + return LeaveRequestDTO::fromModel($leaveRequest); + } + + public function delete(int $id) + { + return $this->leaveRequestRepository->delete($id); + } + + public function draw(array $data): array + { + $data['columns'] = ['users.first_name', 'users.last_name', 'email', 'roles.id', 'users.is_disabled']; + $data['length'] = $data['length'] ?? 10; + $data['column'] = $data['column'] ?? 'users.first_name'; + $data['dir'] = $data['dir'] ?? 'asc'; + $data['search'] = $data['search'] ?? ''; + $data['userId'] = $data['userId'] ?? ''; + $data['draw'] = $data['draw'] ?? 1; + + $leaveRequestsCollection = $this->leaveRequestRepository->draw($data); + + $leaveRequestDTO = $leaveRequestsCollection->getCollection()->map(function ($leaveRequest) { + return LeaveRequestDTO::fromModel($leaveRequest); + }); + + return [ + 'data' => $leaveRequestDTO, + 'pagination' => $leaveRequestsCollection->toArray()['pagination'], + ]; + } +} diff --git a/app/api/app/Applications/LeaveRequest/Services/LeaveRequestServiceInterface.php b/app/api/app/Applications/LeaveRequest/Services/LeaveRequestServiceInterface.php new file mode 100644 index 00000000..0d580b9a --- /dev/null +++ b/app/api/app/Applications/LeaveRequest/Services/LeaveRequestServiceInterface.php @@ -0,0 +1,76 @@ +leaveTypeService = $leaveTypeService; + } + + /** + * Get a JSON with all the users + * + * @return JsonResponse + */ + public function getAll(): JsonResponse + { + $leaveTypeDTOs = $this->leaveTypeService->getAll(); + return response()->json($leaveTypeDTOs); + } + + /** + * Get a JSON with a user by ID + * + * @param integer $id + * @return JsonResponse + */ + public function get(int $id): JsonResponse + { + $leaveTypeDTO = $this->leaveTypeService->get($id); + return response()->json($leaveTypeDTO); + } + + /** + * Store user and get JSON with a user response + * + * @param Request $request + * @return JsonResponse + */ + public function create(NewLeaveTypeRequest $request): JsonResponse + { + $leaveTypeDTO = LeaveTypeDTO::fromRequestForCreate($request); + $newLeaveTypeDTO = $this->leaveTypeService->create($leaveTypeDTO); + + return response()->json($newLeaveTypeDTO); + } + + /** + * Update user + * + * @param Request $request + * @return JsonResponse + */ + public function update(LeaveTypeRequest $request): JsonResponse + { + $leaveTypeId = Route::current()->parameter('id'); + $dto = LeaveTypeDTO::fromRequest($request); + $leaveTypeDTO = $this->leaveTypeService->update( + $leaveTypeId, + $dto + ); + return response()->json($leaveTypeDTO); + } + + /** + * Delete leave Type + * + * @return string + */ + public function delete() + { + $leaveTypeId = Route::current()->parameter('id'); + return $this->leaveTypeService->delete($leaveTypeId); + } + + /** + * Get a paginated, filtered and sorted array of Users. + * This endpoint requires some data in the request. + * + * @param Request $request + * @return JsonResponse + */ + public function draw(Request $request): JsonResponse + { + try { + $data = $request->all(); + $leaveTypesDTO = $this->leaveTypeService->draw($data); + + return response()->json($leaveTypesDTO); + } catch (\InvalidArgumentException $e) { + // Handle specific exceptions like InvalidArgumentException + return response()->json([ + 'error' => 'Invalid Argument', + 'message' => $e->getMessage(), + ], 400); // Bad Request status code + } catch (\ValidationException $e) { + // Handle validation exceptions + return response()->json([ + 'error' => 'Validation Error', + 'message' => $e->getMessage(), + 'errors' => $e->errors(), + ], 422); // Unprocessable Entity status code + } catch (\Exception $e) { + // Handle any other general exceptions + return response()->json([ + 'error' => 'Server Error', + 'message' => $e->getMessage(), + ], 500); // Internal Server Error status code + } + } +} diff --git a/app/api/app/Applications/LeaveType/DTO/LeaveTypeDTO.php b/app/api/app/Applications/LeaveType/DTO/LeaveTypeDTO.php new file mode 100644 index 00000000..88db21dd --- /dev/null +++ b/app/api/app/Applications/LeaveType/DTO/LeaveTypeDTO.php @@ -0,0 +1,86 @@ +name = $name; + $this->slug = $slug; + $this->color = $color; + $this->is_paid = $is_paid; + $this->id = $id; + } + + public static function fromRequest(Request $request): self + { + return new self( + $request->input('name'), + $request->input('slug'), + $request->input('color'), + (bool) $request->input('is_paid', false), + $request->input('id', 0), + ); + } + + public static function fromRequestForCreate(Request $request): self + { + return new self( + $request->input('name'), + $request->input('slug'), + $request->input('color'), + is_paid: false, + id: 0, + ); + } + + public static function fromModel(LeaveType $leaveType): self + { + return new self( + $leaveType->name, + $leaveType->slug, + $leaveType->color, + (bool) $leaveType->is_paid, + $leaveType->id, + + ); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } + + public function toArray(): array + { + return [ + 'name' => $this->name, + 'slug' => $this->slug, + 'color' => $this->color, + 'is_paid' => $this->is_paid, + 'id' => $this->id, + ]; + } + + public static function fromCollection(iterable $leaveTypes): array + { + return array_map(function (LeaveType $leaveType) { + return self::fromModel($leaveType); + }, $leaveTypes->all()); + } +} diff --git a/app/api/app/Applications/LeaveType/Model/LeaveType.php b/app/api/app/Applications/LeaveType/Model/LeaveType.php new file mode 100644 index 00000000..c07683df --- /dev/null +++ b/app/api/app/Applications/LeaveType/Model/LeaveType.php @@ -0,0 +1,50 @@ + + */ + protected $fillable = [ + 'name', + 'slug', + 'is_paid', + 'color' + ]; + + /** + * The attributes that should be hidden for serialization. + * + * @var array + */ + protected $hidden = [ + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + + ]; + + /** + * The accessors to append to the model's array form. + * + * @var array + */ + protected $appends = [ + ]; +} diff --git a/app/api/app/Applications/LeaveType/Providers/LeaveTypeServiceProvider.php b/app/api/app/Applications/LeaveType/Providers/LeaveTypeServiceProvider.php new file mode 100644 index 00000000..6bea9683 --- /dev/null +++ b/app/api/app/Applications/LeaveType/Providers/LeaveTypeServiceProvider.php @@ -0,0 +1,58 @@ +app->routesAreCached()) { + // This is already done in the main RouteServiceProvider so not needed here + } else { + + $this->app->call([$this, 'map']); + + $this->app->booted(function () { + $this->app['router']->getRoutes()->refreshNameLookups(); + $this->app['router']->getRoutes()->refreshActionLookups(); + }); + } + } + + /** + * Register the application services. + * + * @return void + */ + public function register() + { + $this->app->bind(LeaveTypeRepositoryInterface::class, LeaveTypeRepository::class); + $this->app->bind(LeaveTypeServiceInterface::class, LeaveTypeService::class); + } + + public function map() + { + Route::prefix('api') + ->middleware('api') + ->namespace($this->namespace) + ->group(base_path('routes/LeaveType/api.php')); + } + +} diff --git a/app/api/app/Applications/LeaveType/Repositories/LeaveTypeRepository.php b/app/api/app/Applications/LeaveType/Repositories/LeaveTypeRepository.php new file mode 100644 index 00000000..26253f8e --- /dev/null +++ b/app/api/app/Applications/LeaveType/Repositories/LeaveTypeRepository.php @@ -0,0 +1,84 @@ +leaveType = $leaveType; + } + + private const COLUMNS_MAP = [ + 'slug' => 'leave_types.slug', + 'name' => 'leave_types.name', + 'color' => 'leave_types.color', + 'is_paid' => 'leave_types.is_paid' + ]; + + public function getAll(): array + { + $leaveTypes = $this->leaveType::all(); + return LeaveTypeDTO::fromCollection($leaveTypes); + } + + public function get($id): LeaveType + { + return $this->leaveType::findOrFail($id); + } + + public function create(LeaveTypeDTO $leaveTypeDTO): LeaveType + { + $attributes = $leaveTypeDTO->toArray(); + $user = new LeaveType($attributes); + $user->save(); + return $user; + } + + public function update(int $userId, LeaveTypeDTO $userData): LeaveType + { + $leaveType = $this->leaveType->findOrFail($userId); + $attributes = $userData->toArray(); + $leaveType->update($attributes); + return $leaveType; + } + + public function delete(int $id) + { + return $this->leaveType::findOrFail($id)->delete(); + } + + public function draw($data): StarterPaginator + { + // $paginatedUsers = $this->prepareDatatableQuery($data, [User::ADMIN, User::EDITOR, User::COLLABORATOR]); + + $query = $this->leaveType->query(); + + // $query->whereIn('roles.name', $roles); + + if (array_key_exists($data['column'], self::COLUMNS_MAP)) { + $query->orderBy(self::COLUMNS_MAP[$data['column']], $data['dir']); + } + + $search = $data['search']; + if ($search) { + $query->where(function ($subquery) use ($search) { + $subquery->where('leave_types.slug', 'like', '%' . $search . '%'); + $subquery->orWhere('leave_types.name', 'like', '%' . $search . '%'); + }); + } + + $query->whereNull('deleted_at'); + + return $query->paginate($data['length']); + } +} diff --git a/app/api/app/Applications/LeaveType/Repositories/LeaveTypeRepositoryInterface.php b/app/api/app/Applications/LeaveType/Repositories/LeaveTypeRepositoryInterface.php new file mode 100644 index 00000000..55e7b9ba --- /dev/null +++ b/app/api/app/Applications/LeaveType/Repositories/LeaveTypeRepositoryInterface.php @@ -0,0 +1,52 @@ + 'required|max:255|min:2', + 'slug' => 'required|max:255|min:2', + 'color' => 'required' + ]; + + return $rules; + } + public function messages(){ + return [ + 'color.required' => 'users.color.required', + 'name.required' => 'users.name.required', + 'name.max' => 'users.name.max', + 'name.min' => 'users.name.min', + 'slug.required' => 'users.slug.required', + 'slug.max' => 'users.slug.max', + 'slug.min' => 'users.slug.min', + ]; + } +} diff --git a/app/api/app/Applications/LeaveType/Requests/NewLeaveTypeRequest.php b/app/api/app/Applications/LeaveType/Requests/NewLeaveTypeRequest.php new file mode 100644 index 00000000..1031e0e9 --- /dev/null +++ b/app/api/app/Applications/LeaveType/Requests/NewLeaveTypeRequest.php @@ -0,0 +1,42 @@ + 'required|max:255|min:2', + 'slug' => 'required|max:255|min:2', + 'color' => 'required' + ]; + + return $rules; + } + public function messages(){ + return [ + 'name.required' => 'The name field is required', + 'slug.required' => 'The slug field is required', + 'color.required' => 'The color field is required', + ]; + } +} diff --git a/app/api/app/Applications/LeaveType/Services/LeaveTypeService.php b/app/api/app/Applications/LeaveType/Services/LeaveTypeService.php new file mode 100644 index 00000000..7546c89d --- /dev/null +++ b/app/api/app/Applications/LeaveType/Services/LeaveTypeService.php @@ -0,0 +1,68 @@ +leaveTypeRepository = $leaveTypeRepository; + } + + public function getAll(): array + { + return $this->leaveTypeRepository->getAll(); + } + + public function get($id): LeaveTypeDTO + { + return LeaveTypeDTO::fromModel( + $this->leaveTypeRepository->get($id) + ); + } + + public function create(LeaveTypeDTO $leaveTpyeData): LeaveTypeDTO + { + $leaveType = $this->leaveTypeRepository->create($leaveTpyeData); + return LeaveTypeDTO::fromModel($leaveType); + } + + public function update(int $leaveTypeId, LeaveTypeDTO $leaveTpyeData): LeaveTypeDTO + { + $leaveType = $this->leaveTypeRepository->update($leaveTypeId, $leaveTpyeData); + return LeaveTypeDTO::fromModel($leaveType); + } + + public function delete(int $id) + { + return $this->leaveTypeRepository->delete($id); + } + + public function draw(array $data): array + { + $data['columns'] = ['users.first_name', 'users.last_name', 'email', 'roles.id', 'users.is_disabled']; + $data['length'] = $data['length'] ?? 10; + $data['column'] = $data['column'] ?? 'users.first_name'; + $data['dir'] = $data['dir'] ?? 'asc'; + $data['search'] = $data['search'] ?? ''; + $data['draw'] = $data['draw'] ?? 1; + + $usersCollection = $this->leaveTypeRepository->draw($data); + + $usersDTOs = $usersCollection->getCollection()->map(function ($user) { + return LeaveTypeDTO::fromModel($user); + }); + + return [ + 'data' => $usersDTOs, + 'pagination' => $usersCollection->toArray()['pagination'], + ]; + } +} diff --git a/app/api/app/Applications/LeaveType/Services/LeaveTypeServiceInterface.php b/app/api/app/Applications/LeaveType/Services/LeaveTypeServiceInterface.php new file mode 100644 index 00000000..69290729 --- /dev/null +++ b/app/api/app/Applications/LeaveType/Services/LeaveTypeServiceInterface.php @@ -0,0 +1,50 @@ +leaveTypeService = $leaveTypeService; + } + + /** + * Get a JSON with all the users + * + * @return JsonResponse + */ + public function getAll(): JsonResponse + { + $leaveTypeDTOs = $this->leaveTypeService->getAll(); + return response()->json($leaveTypeDTOs); + } + + /** + * Get a JSON with a user by ID + * + * @param integer $id + * @return JsonResponse + */ + public function get(int $id): JsonResponse + { + $leaveTypeDTO = $this->leaveTypeService->get($id); + return response()->json($leaveTypeDTO); + } + + /** + * Store user and get JSON with a user response + * + * @param Request $request + * @return JsonResponse + */ + public function create(Request $request): JsonResponse + { + $leaveTypeDTO = NationalHolidayDTO::fromRequestForCreate($request); + $newLeaveTypeDTO = $this->leaveTypeService->create($leaveTypeDTO); + + return response()->json($newLeaveTypeDTO); + } + + /** + * Update user + * + * @param Request $request + * @return JsonResponse + */ + public function update(Request $request): JsonResponse + { + $leaveTypeId = Route::current()->parameter('id'); + $dto = NationalHolidayDTO::fromRequest($request); + $leaveTypeDTO = $this->leaveTypeService->update( + $leaveTypeId, + $dto + ); + return response()->json($leaveTypeDTO); + } + + /** + * Delete user + * + * @return string + */ + public function delete() + { + $leaveTypeId = Route::current()->parameter('id'); + return $this->leaveTypeService->delete($leaveTypeId); + } + + /** + * Get a paginated, filtered and sorted array of Users. + * This endpoint requires some data in the request. + * + * @param Request $request + * @return JsonResponse + */ + public function draw(Request $request): JsonResponse + { + try { + $data = $request->all(); + $leaveTypesDTO = $this->leaveTypeService->draw($data); + + return response()->json($leaveTypesDTO); + } catch (\InvalidArgumentException $e) { + // Handle specific exceptions like InvalidArgumentException + return response()->json([ + 'error' => 'Invalid Argument', + 'message' => $e->getMessage(), + ], 400); // Bad Request status code + } catch (\ValidationException $e) { + // Handle validation exceptions + return response()->json([ + 'error' => 'Validation Error', + 'message' => $e->getMessage(), + 'errors' => $e->errors(), + ], 422); // Unprocessable Entity status code + } catch (\Exception $e) { + // Handle any other general exceptions + return response()->json([ + 'error' => 'Server Error', + 'message' => $e->getMessage(), + ], 500); // Internal Server Error status code + } + } +} diff --git a/app/api/app/Applications/NationalHoliday/DTO/NationalHolidayDTO.php b/app/api/app/Applications/NationalHoliday/DTO/NationalHolidayDTO.php new file mode 100644 index 00000000..fc2f8e87 --- /dev/null +++ b/app/api/app/Applications/NationalHoliday/DTO/NationalHolidayDTO.php @@ -0,0 +1,86 @@ +date = $date; + $this->country = $country; + $this->year = $year; + $this->leave_type_id = $leave_type_id; + $this->id = $id; + } + + public static function fromRequest(Request $request): self + { + return new self( + $request->input('date'), + $request->input('country'), + $request->input('year'), + $request->input('leave_type_id'), + $request->input('id', 0), + ); + } + + public static function fromRequestForCreate(Request $request): self + { + return new self( + $request->input('date'), + $request->input('country'), + $request->input('year'), + leave_type_id: 5, + id: 0, + ); + } + + public static function fromModel(NationalHoliday $leaveType): self + { + return new self( + $leaveType->date, + $leaveType->country, + $leaveType->year, + $leaveType->leave_type_id, + $leaveType->id, + + ); + } + + public function jsonSerialize(): array + { + return $this->toArray(); + } + + public function toArray(): array + { + return [ + 'name' => $this->date, + 'slug' => $this->country, + 'color' => $this->year, + 'is_paid' => $this->leave_type_id, + 'id' => $this->id, + ]; + } + + public static function fromCollection(iterable $leaveTypes): array + { + return array_map(function (NationalHoliday $leaveType) { + return self::fromModel($leaveType); + }, $leaveTypes->all()); + } +} diff --git a/app/api/app/Applications/NationalHoliday/Model/NationalHoliday.php b/app/api/app/Applications/NationalHoliday/Model/NationalHoliday.php new file mode 100644 index 00000000..d59e767e --- /dev/null +++ b/app/api/app/Applications/NationalHoliday/Model/NationalHoliday.php @@ -0,0 +1,11 @@ +app->routesAreCached()) { + // This is already done in the main RouteServiceProvider so not needed here + } else { + + $this->app->call([$this, 'map']); + + $this->app->booted(function () { + $this->app['router']->getRoutes()->refreshNameLookups(); + $this->app['router']->getRoutes()->refreshActionLookups(); + }); + } + } + + /** + * Register the application services. + * + * @return void + */ + public function register() + { + $this->app->bind(NationalHolidayRepositoryInterface::class, NationalHolidayRepository::class); + $this->app->bind(NationalHolidayServiceInterface::class, NationalHolidayService::class); + } + + public function map() + { + Route::prefix('api') + ->middleware('api') + ->namespace($this->namespace) + ->group(base_path('routes/NationalHoliday/api.php')); + } + +} diff --git a/app/api/app/Applications/NationalHoliday/Repositories/NationalHolidayRepository.php b/app/api/app/Applications/NationalHoliday/Repositories/NationalHolidayRepository.php new file mode 100644 index 00000000..b9c3b7b6 --- /dev/null +++ b/app/api/app/Applications/NationalHoliday/Repositories/NationalHolidayRepository.php @@ -0,0 +1,88 @@ +leaveType = $leaveType; + } + + private const COLUMNS_MAP = [ + 'slug' => 'leave_types.slug', + 'name' => 'leave_types.name', + 'color' => 'leave_types.color', + 'is_paid' => 'leave_types.is_paid' + ]; + + public function getAll(): array + { + $leaveTypes = $this->leaveType::all(); + return NationalHolidayDTO::fromCollection($leaveTypes); + } + + public function get($id): NationalHoliday + { + return $this->leaveType::findOrFail($id); + } + + public function create(NationalHolidayDTO $leaveTypeDTO): NationalHoliday + { + $attributes = $leaveTypeDTO->toArray(); + $user = new NationalHoliday($attributes); + $user->save(); + return $user; + } + + public function update(int $userId, NationalHolidayDTO $userData): NationalHoliday + { + $leaveType = $this->leaveType->findOrFail($userId); + $attributes = $userData->toArray(); + $leaveType->update($attributes); + return $leaveType; + } + + public function delete(int $id) + { + return $this->leaveType::findOrFail($id)->delete(); + } + + public function draw($data): StarterPaginator + { + // $paginatedUsers = $this->prepareDatatableQuery($data, [User::ADMIN, User::EDITOR, User::COLLABORATOR]); + $query = $this->leaveType->query(); + + // $query->whereIn('roles.name', $roles); + + if (array_key_exists($data['column'], self::COLUMNS_MAP)) { + $query->orderBy(self::COLUMNS_MAP[$data['column']], $data['dir']); + } + + $search = $data['search']; + if ($search) { + $query->where(function ($subquery) use ($search) { + $subquery->where('leave_types.slug', 'like', '%' . $search . '%'); + $subquery->orWhere('leave_types.name', 'like', '%' . $search . '%'); + }); + } + + $country = $data['isCountry']; + if ($country == 1) { + $query->where('national_holidays.country', 'like', '%Macedonia%'); + } else if ($country == 2) { + $query->where('national_holidays.country', 'like', '%Bulgaria%'); + } + + return $query->paginate($data['length']); + } +} diff --git a/app/api/app/Applications/NationalHoliday/Repositories/NationalHolidayRepositoryInterface.php b/app/api/app/Applications/NationalHoliday/Repositories/NationalHolidayRepositoryInterface.php new file mode 100644 index 00000000..2003b5de --- /dev/null +++ b/app/api/app/Applications/NationalHoliday/Repositories/NationalHolidayRepositoryInterface.php @@ -0,0 +1,52 @@ + 'required|max:255|min:2', + 'slug' => 'required|max:255|min:2', + ]; + + return $rules; + } + public function messages(){ + return [ + 'name.required' => 'users.name.required', + 'name.max' => 'users.name.max', + 'name.min' => 'users.name.min', + 'slug.required' => 'users.slug.required', + 'slug.max' => 'users.slug.max', + 'slug.min' => 'users.slug.min', + ]; + } +} diff --git a/app/api/app/Applications/NationalHoliday/Requests/NewLeaveTypeRequest.php b/app/api/app/Applications/NationalHoliday/Requests/NewLeaveTypeRequest.php new file mode 100644 index 00000000..6b75ffaa --- /dev/null +++ b/app/api/app/Applications/NationalHoliday/Requests/NewLeaveTypeRequest.php @@ -0,0 +1,57 @@ + 'required|max:255|min:2', + 'last_name' => 'required|max:255|min:2', + 'email' => 'required|email|min:2|max:255|unique:users,email,'.$this->segment(3), + 'password' => 'sometimes|between:6,30|confirmed', + 'roles' => 'required|exists:roles,id', + ]; + + return $rules; + } + public function messages(){ + return [ + 'first_name.required' => 'users.first_name.required', + 'first_name.max' => 'users.first_name.max', + 'first_name.min' => 'users.first_name.min', + 'last_name.required' => 'users.last_name.required', + 'last_name.max' => 'users.last_name.max', + 'last_name.min' => 'users.last_name.min', + 'email.required' => 'users.email.required', + 'email.email' => 'users.email.invalid', + 'email.max' => 'users.email.max', + 'email.min' => 'users.email.min', + 'email.unique' => 'users.email.unique', + 'roles.required' => 'users.roles.required', + 'roles.exists' => 'users.roles.exists', + 'password.required' => 'users.password.required', + 'password.between' => 'users.password.between', + 'password.confirmed' => 'users.password.confirmed', + ]; + } +} diff --git a/app/api/app/Applications/NationalHoliday/Services/NationalHolidayService.php b/app/api/app/Applications/NationalHoliday/Services/NationalHolidayService.php new file mode 100644 index 00000000..0944379c --- /dev/null +++ b/app/api/app/Applications/NationalHoliday/Services/NationalHolidayService.php @@ -0,0 +1,68 @@ +leaveTypeRepository = $leaveTypeRepository; + } + + public function getAll(): array + { + return $this->leaveTypeRepository->getAll(); + } + + public function get($id): NationalHolidayDTO + { + return NationalHolidayDTO::fromModel( + $this->leaveTypeRepository->get($id) + ); + } + + public function create(NationalHolidayDTO $leaveTpyeData): NationalHolidayDTO + { + $leaveType = $this->leaveTypeRepository->create($leaveTpyeData); + return NationalHolidayDTO::fromModel($leaveType); + } + + public function update(int $leaveTypeId, NationalHolidayDTO $leaveTpyeData): NationalHolidayDTO + { + $leaveType = $this->leaveTypeRepository->update($leaveTypeId, $leaveTpyeData); + return NationalHolidayDTO::fromModel($leaveType); + } + + public function delete(int $id) + { + return $this->leaveTypeRepository->delete($id); + } + + public function draw(array $data): array + { + $data['columns'] = ['users.first_name', 'users.last_name', 'email', 'roles.id', 'users.is_disabled']; + $data['length'] = $data['length'] ?? 25; + $data['column'] = $data['column'] ?? 'users.first_name'; + $data['dir'] = $data['dir'] ?? 'asc'; + $data['isCountry'] = $data['isCountry'] ?? ''; + $data['search'] = $data['search'] ?? ''; + $data['draw'] = $data['draw'] ?? 1; + + $usersCollection = $this->leaveTypeRepository->draw($data); + $usersDTOs = $usersCollection->getCollection()->map(function ($user) { + return NationalHolidayDTO::fromModel($user); + }); + + return [ + 'data' => $usersDTOs, + 'pagination' => $usersCollection->toArray()['pagination'], + ]; + } +} diff --git a/app/api/app/Applications/NationalHoliday/Services/NationalHolidayServiceInterface.php b/app/api/app/Applications/NationalHoliday/Services/NationalHolidayServiceInterface.php new file mode 100644 index 00000000..6ec7e07a --- /dev/null +++ b/app/api/app/Applications/NationalHoliday/Services/NationalHolidayServiceInterface.php @@ -0,0 +1,50 @@ +userService->updatePassword($user, $request); + } + return response()->json($userDTO); } diff --git a/app/api/app/Applications/User/DTO/UserDTO.php b/app/api/app/Applications/User/DTO/UserDTO.php index 2851bbc9..d9d5b1d3 100644 --- a/app/api/app/Applications/User/DTO/UserDTO.php +++ b/app/api/app/Applications/User/DTO/UserDTO.php @@ -16,6 +16,10 @@ class UserDTO public int $id; public bool $is_disabled; public array $permissions_array; + public int $paid_leaves_max; + public int $paid_leaves_left; + public int $country; + public bool $is_office_based; public function __construct( string $first_name, @@ -26,7 +30,11 @@ public function __construct( int $role, int $id = 0, bool $is_disabled = false, - array $permissions_array = [] + array $permissions_array = [], + int $paid_leaves_max, + int $paid_leaves_left, + int $country, + bool $is_office_based = false, ) { $this->first_name = $first_name; $this->last_name = $last_name; @@ -37,6 +45,10 @@ public function __construct( $this->id = $id; $this->is_disabled = $is_disabled; $this->permissions_array = $permissions_array; + $this->paid_leaves_max = $paid_leaves_max; + $this->paid_leaves_left = $paid_leaves_left; + $this->country = $country; + $this->is_office_based = $is_office_based; } public static function fromRequest(Request $request): self @@ -50,7 +62,11 @@ public static function fromRequest(Request $request): self $request->input('role'), $request->input('id', 0), (bool) $request->input('is_disabled', false), - $request->input('permissions_array', []) + $request->input('permissions_array', []), + $request->input('paid_leaves_max'), + $request->input('paid_leaves_left'), + $request->input('country'), + (bool) $request->input('is_office_based') ); } @@ -65,7 +81,11 @@ public static function fromRequestForCreate(Request $request): self $request->input('role'), id: 0, is_disabled: false, - permissions_array: $request->input('permissions_array', []) + permissions_array: $request->input('permissions_array', []), + paid_leaves_max: 0, + paid_leaves_left: 0, + country: $request->input('country'), + is_office_based: false ); } @@ -80,7 +100,11 @@ public static function fromModel(User $user): self $user->role, $user->id, (bool) $user->is_disabled, - $user->permissions_array + $user->permissions_array, + $user->paid_leaves_max, + $user->paid_leaves_left, + $user->country, + $user->is_office_based ); } @@ -101,6 +125,10 @@ public function toArray(): array 'id' => $this->id, 'is_disabled' => $this->is_disabled, 'permissions_array' => $this->permissions_array, + 'paid_leaves_max' => $this->paid_leaves_max, + 'paid_leaves_left' => $this->paid_leaves_left, + 'country' => $this->country, + 'is_office_based' => $this->is_office_based ]; } diff --git a/app/api/app/Applications/User/Model/User.php b/app/api/app/Applications/User/Model/User.php index cbaa72a8..1d2d24b0 100644 --- a/app/api/app/Applications/User/Model/User.php +++ b/app/api/app/Applications/User/Model/User.php @@ -21,7 +21,8 @@ class User extends Authenticatable implements HasMedia use InteractsWithMedia; const ADMIN = 'admin'; - const EDITOR = 'editor'; + const MANAGER = 'manager'; + const DEVELOPER = 'developer'; const COLLABORATOR = 'collaborator'; /** @@ -36,7 +37,11 @@ class User extends Authenticatable implements HasMedia 'email_verified_at', 'password', 'is_disabled', - 'activation_code' + 'activation_code', + 'paid_leaves_max', + 'paid_leaves_left', + 'country', + 'is_office_based' ]; /** diff --git a/app/api/app/Applications/User/Repositories/UserRepository.php b/app/api/app/Applications/User/Repositories/UserRepository.php index 1cc9a602..befe734d 100644 --- a/app/api/app/Applications/User/Repositories/UserRepository.php +++ b/app/api/app/Applications/User/Repositories/UserRepository.php @@ -30,6 +30,8 @@ public function __construct( 'first_name' => 'users.first_name', 'last_name' => 'users.last_name', 'email' => 'users.email', + 'paid_leaves_left' => 'users.paid_leaves_left', + 'is_office_based' => 'is_office_based', 'roles' => 'roles.id', 'status' => 'users.is_disabled' ]; @@ -61,6 +63,9 @@ public function update(int $userId, UserDTO $userData): User { $user = $this->user->findOrFail($userId); $attributes = $userData->toArray(); + if($attributes['paid_leaves_max'] != $attributes['paid_leaves_left']) { + $attributes['paid_leaves_left'] = $attributes['paid_leaves_max']; + } $user->update($attributes); return $user; } @@ -88,7 +93,9 @@ public function draw($data): StarterPaginator $subquery->where('users.first_name', 'like', '%' . $search . '%'); $subquery->orWhere('users.last_name', 'like', '%' . $search . '%'); $subquery->orWhere('users.email', 'like', '%' . $search . '%'); - $subquery->orWhere('roles.name', 'like', '%' . $search . '%'); + $subquery->orWhereHas('roles', function ($roleQuery) use ($search) { + $roleQuery->where('roles.name', 'like', '%' . $search . '%'); + }); }); } diff --git a/app/api/app/Applications/User/Services/UserService.php b/app/api/app/Applications/User/Services/UserService.php index 181900ee..58ae5dee 100644 --- a/app/api/app/Applications/User/Services/UserService.php +++ b/app/api/app/Applications/User/Services/UserService.php @@ -90,6 +90,10 @@ public function updateMyProfile($request) $this->userRepository->setPassword($user, $request_array['password']); } + public function updatePassword($user, $request) { + $this->userRepository->setPassword($user, $request['password']); + } + public function getUserRoles(): array { $rolesCollection = $this->userRepository->getUserRoles(); diff --git a/app/api/app/Applications/User/Services/UserServiceInterface.php b/app/api/app/Applications/User/Services/UserServiceInterface.php index a9a03763..6a0fbe29 100644 --- a/app/api/app/Applications/User/Services/UserServiceInterface.php +++ b/app/api/app/Applications/User/Services/UserServiceInterface.php @@ -57,6 +57,13 @@ public function draw(array $data): array; */ public function updateMyProfile($request); + /** + * @param User $user + * @param Request $request + * @return void + */ + public function updatePassword($user, $request); + /** * @return array */ diff --git a/app/api/app/Console/Commands/PopulateNationalHolidays.php b/app/api/app/Console/Commands/PopulateNationalHolidays.php new file mode 100644 index 00000000..1c5db0db --- /dev/null +++ b/app/api/app/Console/Commands/PopulateNationalHolidays.php @@ -0,0 +1,75 @@ +option('year'); + $drop_flag = $this->option('drop'); + + if ($drop_flag) { + NationalHoliday::truncate(); + } + + if (!$year) { + return Command::FAILURE; + } + + $this->info("Fetching holidays for year: $year"); + + $countries = [ + 'Macedonia' => 'MK', + 'Bulgaria' => 'BG', + ]; + + $client = new Client(); + + foreach ($countries as $countryName => $countryCode) { + $this->info("Fetching holidays for $countryName..."); + + $response = $client->get("https://date.nager.at/api/v3/PublicHolidays/{$year}/{$countryCode}"); + + if ($response->getStatusCode() !== 200) { + $this->error("Failed to fetch holidays for $countryName."); + continue; + } + + $holidays = json_decode($response->getBody(), true); + + foreach ($holidays as $holiday) { + $holidayDate = Carbon::parse($holiday['date']); + + if ($holidayDate->isWeekend()) { + continue; + } + + NationalHoliday::updateOrCreate( + [ + 'date' => $holiday['date'], + 'country' => $countryName, + 'year' => $year, + ], + [ + 'created_at' => now(), + 'updated_at' => now(), + ] + ); + } + + $this->info("Holidays successfully populated for $countryName."); + } + + $this->info("✅ All holidays populated successfully."); + return Command::SUCCESS; + } +} diff --git a/app/api/app/Constants/UserPermissions.php b/app/api/app/Constants/UserPermissions.php index bf4ad8aa..9c5d76a9 100644 --- a/app/api/app/Constants/UserPermissions.php +++ b/app/api/app/Constants/UserPermissions.php @@ -7,4 +7,11 @@ class UserPermissions public const READ_USERS = 'read_users'; public const WRITE_USERS = 'write_users'; public const DELETE_USERS = 'delete_users'; + public const WRITE_PROFILE = 'write_profile'; + public const READ_REQUESTS = 'read_requests'; + public const WRITE_REQUESTS = 'write_requests'; + public const APPROVE_REQUESTS = 'approve_requests'; + public const DELETE_REQUESTS = 'delete_requests'; + public const DASHBOARD_VIEW = 'dashboard_view'; + } diff --git a/app/api/app/Constants/UserRoles.php b/app/api/app/Constants/UserRoles.php index 723ecb88..7b077f9d 100644 --- a/app/api/app/Constants/UserRoles.php +++ b/app/api/app/Constants/UserRoles.php @@ -5,6 +5,7 @@ class UserRoles { public const ADMIN = 'admin'; - public const EDITOR = 'editor'; + public const MANAGER = 'manager'; public const COLLABORATOR = 'collaborator'; + public const DEVELOPER = 'developer'; } diff --git a/app/api/app/Http/Controllers/HomeController.php b/app/api/app/Http/Controllers/HomeController.php index 882bc22f..a45c86bb 100644 --- a/app/api/app/Http/Controllers/HomeController.php +++ b/app/api/app/Http/Controllers/HomeController.php @@ -26,22 +26,32 @@ public function vue() 'label' => 'admin.dashboard', 'name' => 'item_dashboard', 'route' => 'dashboard', - 'permission' => UserPermissions::READ_USERS, // Change to dashboard_view + 'permission' => UserPermissions::DASHBOARD_VIEW, ], [ 'label' => 'admin.users.main', 'name' => 'item_users', 'route' => 'users', 'permission' => UserPermissions::READ_USERS, - 'submenu' => [ - [ - 'label' => 'admin.users.admin', - 'name' => 'item_users', - 'route' => 'users', - 'permission' => UserPermissions::READ_USERS, - ] - ] - ] + ], + [ + 'label' => 'admin.leave_types.main', + 'name' => 'item_types', + 'route' => 'leave_types', + 'permission' => UserPermissions::WRITE_USERS, + ], + [ + 'label' => 'admin.leave_requests.main', + 'name' => 'item_requests', + 'route' => 'leave_requests', + 'permission' => UserPermissions::READ_REQUESTS, + ], + [ + 'label' => 'Vacation Days', + 'name' => 'item_types', + 'route' => 'vacation_days', + 'permission' => UserPermissions::READ_REQUESTS, + ], ]; $pagesItems = [ diff --git a/app/api/composer.json b/app/api/composer.json index e2dde6f8..604208b0 100644 --- a/app/api/composer.json +++ b/app/api/composer.json @@ -6,11 +6,14 @@ "license": "MIT", "require": { "php": "^8.3", + "azuyalabs/yasumi": "^2.7", "fakerphp/faker": "^1.9.1", - "guzzlehttp/guzzle": "^7.2", + "guzzlehttp/guzzle": "^7.9", "laravel/framework": "^v11.0", "laravel/sanctum": "^4.0", "laravel/tinker": "^2.8", + "setasign/fpdf": "^1.8", + "setasign/fpdi": "^2.6", "spatie/laravel-medialibrary": "^11.9", "spatie/laravel-permission": "^6.0" }, diff --git a/app/api/composer.lock b/app/api/composer.lock index 92fe5fc0..3540e7d4 100644 --- a/app/api/composer.lock +++ b/app/api/composer.lock @@ -4,8 +4,81 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b3114f7b33aff77d50bb0b97a096120c", + "content-hash": "54602f11718ea3964879229bde3d0376", "packages": [ + { + "name": "azuyalabs/yasumi", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/azuyalabs/yasumi.git", + "reference": "37d1215d4f4012d3185bb9990c76ca17a4ff1c30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/azuyalabs/yasumi/zipball/37d1215d4f4012d3185bb9990c76ca17a4ff1c30", + "reference": "37d1215d4f4012d3185bb9990c76ca17a4ff1c30", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=8.0" + }, + "require-dev": { + "ext-intl": "*", + "friendsofphp/php-cs-fixer": "^2.19 || ^3.40", + "mikey179/vfsstream": "^1.6", + "phan/phan": "^5.4", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.5 || ^9.6", + "vimeo/psalm": "^5.16" + }, + "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": "2024-01-07T14:12:44+00:00" + }, { "name": "brick/math", "version": "0.12.1", @@ -461,16 +534,16 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v3.3.3", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a" + "reference": "8c784d071debd117328803d86b2097615b457500" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", - "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", + "reference": "8c784d071debd117328803d86b2097615b457500", "shasum": "" }, "require": { @@ -483,10 +556,14 @@ "require-dev": { "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.0", - "phpstan/phpstan-webmozart-assert": "^1.0", "phpunit/phpunit": "^7.0|^8.0|^9.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, "autoload": { "psr-4": { "Cron\\": "src/Cron/" @@ -510,7 +587,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" }, "funding": [ { @@ -518,20 +595,20 @@ "type": "github" } ], - "time": "2023-08-10T19:36:49+00:00" + "time": "2024-10-09T13:47:03+00:00" }, { "name": "egulias/email-validator", - "version": "4.0.2", + "version": "4.0.3", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" + "reference": "b115554301161fa21467629f1e1391c1936de517" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", - "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/b115554301161fa21467629f1e1391c1936de517", + "reference": "b115554301161fa21467629f1e1391c1936de517", "shasum": "" }, "require": { @@ -577,7 +654,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" + "source": "https://github.com/egulias/EmailValidator/tree/4.0.3" }, "funding": [ { @@ -585,20 +662,20 @@ "type": "github" } ], - "time": "2023-10-06T06:47:41+00:00" + "time": "2024-12-27T00:36:43+00:00" }, { "name": "fakerphp/faker", - "version": "v1.23.1", + "version": "v1.24.1", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b" + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/bfb4fe148adbf78eff521199619b93a52ae3554b", - "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", "shasum": "" }, "require": { @@ -646,9 +723,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.23.1" + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" }, - "time": "2024-01-02T13:46:09+00:00" + "time": "2024-11-21T13:46:39+00:00" }, { "name": "fruitcake/php-cors", @@ -911,16 +988,16 @@ }, { "name": "guzzlehttp/promises", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8" + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", - "reference": "6ea8dd08867a2a42619d65c3deb2c0fcbf81c8f8", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", "shasum": "" }, "require": { @@ -974,7 +1051,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.3" + "source": "https://github.com/guzzle/promises/tree/2.0.4" }, "funding": [ { @@ -990,7 +1067,7 @@ "type": "tidelift" } ], - "time": "2024-07-18T10:29:17+00:00" + "time": "2024-10-17T10:06:22+00:00" }, { "name": "guzzlehttp/psr7", @@ -1110,16 +1187,16 @@ }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.3", + "version": "v1.0.4", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c" + "reference": "30e286560c137526eccd4ce21b2de477ab0676d2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/ecea8feef63bd4fef1f037ecb288386999ecc11c", - "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/30e286560c137526eccd4ce21b2de477ab0676d2", + "reference": "30e286560c137526eccd4ce21b2de477ab0676d2", "shasum": "" }, "require": { @@ -1176,7 +1253,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.3" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.4" }, "funding": [ { @@ -1192,27 +1269,27 @@ "type": "tidelift" } ], - "time": "2023-12-03T19:50:20+00:00" + "time": "2025-02-03T10:55:03+00:00" }, { "name": "laravel/framework", - "version": "v11.27.1", + "version": "v11.41.3", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "2634ad4a6a71da3288943f058a8abbfec6a65ebb" + "reference": "3ef433d5865f30a19b6b1be247586068399b59cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/2634ad4a6a71da3288943f058a8abbfec6a65ebb", - "reference": "2634ad4a6a71da3288943f058a8abbfec6a65ebb", + "url": "https://api.github.com/repos/laravel/framework/zipball/3ef433d5865f30a19b6b1be247586068399b59cc", + "reference": "3ef433d5865f30a19b6b1be247586068399b59cc", "shasum": "" }, "require": { "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12", "composer-runtime-api": "^2.2", "doctrine/inflector": "^2.0.5", - "dragonmantank/cron-expression": "^3.3.2", + "dragonmantank/cron-expression": "^3.4", "egulias/email-validator": "^3.2.1|^4.0", "ext-ctype": "*", "ext-filter": "*", @@ -1222,38 +1299,39 @@ "ext-session": "*", "ext-tokenizer": "*", "fruitcake/php-cors": "^1.3", - "guzzlehttp/guzzle": "^7.8", + "guzzlehttp/guzzle": "^7.8.2", "guzzlehttp/uri-template": "^1.0", "laravel/prompts": "^0.1.18|^0.2.0|^0.3.0", - "laravel/serializable-closure": "^1.3", - "league/commonmark": "^2.2.1", - "league/flysystem": "^3.8.0", + "laravel/serializable-closure": "^1.3|^2.0", + "league/commonmark": "^2.6", + "league/flysystem": "^3.25.1", + "league/flysystem-local": "^3.25.1", + "league/uri": "^7.5.1", "monolog/monolog": "^3.0", - "nesbot/carbon": "^2.72.2|^3.0", + "nesbot/carbon": "^2.72.6|^3.8.4", "nunomaduro/termwind": "^2.0", "php": "^8.2", "psr/container": "^1.1.1|^2.0.1", "psr/log": "^1.0|^2.0|^3.0", "psr/simple-cache": "^1.0|^2.0|^3.0", "ramsey/uuid": "^4.7", - "symfony/console": "^7.0", - "symfony/error-handler": "^7.0", - "symfony/finder": "^7.0", - "symfony/http-foundation": "^7.0", - "symfony/http-kernel": "^7.0", - "symfony/mailer": "^7.0", - "symfony/mime": "^7.0", - "symfony/polyfill-php83": "^1.28", - "symfony/process": "^7.0", - "symfony/routing": "^7.0", - "symfony/uid": "^7.0", - "symfony/var-dumper": "^7.0", + "symfony/console": "^7.0.3", + "symfony/error-handler": "^7.0.3", + "symfony/finder": "^7.0.3", + "symfony/http-foundation": "^7.2.0", + "symfony/http-kernel": "^7.0.3", + "symfony/mailer": "^7.0.3", + "symfony/mime": "^7.0.3", + "symfony/polyfill-php83": "^1.31", + "symfony/process": "^7.0.3", + "symfony/routing": "^7.0.3", + "symfony/uid": "^7.0.3", + "symfony/var-dumper": "^7.0.3", "tijsverkoyen/css-to-inline-styles": "^2.2.5", - "vlucas/phpdotenv": "^5.4.1", - "voku/portable-ascii": "^2.0" + "vlucas/phpdotenv": "^5.6.1", + "voku/portable-ascii": "^2.0.2" }, "conflict": { - "mockery/mockery": "1.6.8", "tightenco/collect": "<5.5.33" }, "provide": { @@ -1300,29 +1378,33 @@ }, "require-dev": { "ably/ably-php": "^1.0", - "aws/aws-sdk-php": "^3.235.5", + "aws/aws-sdk-php": "^3.322.9", "ext-gmp": "*", - "fakerphp/faker": "^1.23", - "league/flysystem-aws-s3-v3": "^3.0", - "league/flysystem-ftp": "^3.0", - "league/flysystem-path-prefixing": "^3.3", - "league/flysystem-read-only": "^3.3", - "league/flysystem-sftp-v3": "^3.0", - "mockery/mockery": "^1.6", - "nyholm/psr7": "^1.2", - "orchestra/testbench-core": "^9.5", - "pda/pheanstalk": "^5.0", + "fakerphp/faker": "^1.24", + "guzzlehttp/promises": "^2.0.3", + "guzzlehttp/psr7": "^2.4", + "laravel/pint": "^1.18", + "league/flysystem-aws-s3-v3": "^3.25.1", + "league/flysystem-ftp": "^3.25.1", + "league/flysystem-path-prefixing": "^3.25.1", + "league/flysystem-read-only": "^3.25.1", + "league/flysystem-sftp-v3": "^3.25.1", + "mockery/mockery": "^1.6.10", + "orchestra/testbench-core": "^9.6", + "pda/pheanstalk": "^5.0.6", + "php-http/discovery": "^1.15", "phpstan/phpstan": "^1.11.5", - "phpunit/phpunit": "^10.5|^11.0", - "predis/predis": "^2.0.2", + "phpunit/phpunit": "^10.5.35|^11.3.6", + "predis/predis": "^2.3", "resend/resend-php": "^0.10.0", - "symfony/cache": "^7.0", - "symfony/http-client": "^7.0", - "symfony/psr-http-message-bridge": "^7.0" + "symfony/cache": "^7.0.3", + "symfony/http-client": "^7.0.3", + "symfony/psr-http-message-bridge": "^7.0.3", + "symfony/translation": "^7.0.3" }, "suggest": { "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", - "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.235.5).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).", "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).", "ext-apcu": "Required to use the APC cache driver.", "ext-fileinfo": "Required to use the Filesystem class.", @@ -1336,16 +1418,16 @@ "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", "filp/whoops": "Required for friendly error pages in development (^2.14.3).", "laravel/tinker": "Required to use the tinker console command (^2.0).", - "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).", - "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).", - "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.3).", - "league/flysystem-read-only": "Required to use read-only disks (^3.3)", - "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).", + "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", "mockery/mockery": "Required to use mocking (^1.6).", - "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", + "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", "phpunit/phpunit": "Required to use assertions and run tests (^10.5|^11.0).", - "predis/predis": "Required to use the predis connector (^2.0.2).", + "predis/predis": "Required to use the predis connector (^2.3).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0).", @@ -1364,6 +1446,7 @@ }, "autoload": { "files": [ + "src/Illuminate/Collections/functions.php", "src/Illuminate/Collections/helpers.php", "src/Illuminate/Events/functions.php", "src/Illuminate/Filesystem/functions.php", @@ -1401,20 +1484,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-10-08T20:25:59+00:00" + "time": "2025-01-30T13:25:22+00:00" }, { "name": "laravel/prompts", - "version": "v0.3.0", + "version": "v0.3.4", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "ea57a2261093986721d4a5f4f9524d76f21f9fa0" + "reference": "abeaa2ba4294247d5409490d1ca1bc6248087011" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/ea57a2261093986721d4a5f4f9524d76f21f9fa0", - "reference": "ea57a2261093986721d4a5f4f9524d76f21f9fa0", + "url": "https://api.github.com/repos/laravel/prompts/zipball/abeaa2ba4294247d5409490d1ca1bc6248087011", + "reference": "abeaa2ba4294247d5409490d1ca1bc6248087011", "shasum": "" }, "require": { @@ -1428,9 +1511,9 @@ "laravel/framework": ">=10.17.0 <10.25.0" }, "require-dev": { - "illuminate/collections": "^10.0|^11.0", + "illuminate/collections": "^10.0|^11.0|^12.0", "mockery/mockery": "^1.5", - "pestphp/pest": "^2.3", + "pestphp/pest": "^2.3|^3.4", "phpstan/phpstan": "^1.11", "phpstan/phpstan-mockery": "^1.1" }, @@ -1458,38 +1541,38 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.3.0" + "source": "https://github.com/laravel/prompts/tree/v0.3.4" }, - "time": "2024-09-30T14:27:51+00:00" + "time": "2025-01-24T15:41:01+00:00" }, { "name": "laravel/sanctum", - "version": "v4.0.3", + "version": "v4.0.8", "source": { "type": "git", "url": "https://github.com/laravel/sanctum.git", - "reference": "54aea9d13743ae8a6cdd3c28dbef128a17adecab" + "reference": "ec1dd9ddb2ab370f79dfe724a101856e0963f43c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sanctum/zipball/54aea9d13743ae8a6cdd3c28dbef128a17adecab", - "reference": "54aea9d13743ae8a6cdd3c28dbef128a17adecab", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/ec1dd9ddb2ab370f79dfe724a101856e0963f43c", + "reference": "ec1dd9ddb2ab370f79dfe724a101856e0963f43c", "shasum": "" }, "require": { "ext-json": "*", - "illuminate/console": "^11.0", - "illuminate/contracts": "^11.0", - "illuminate/database": "^11.0", - "illuminate/support": "^11.0", + "illuminate/console": "^11.0|^12.0", + "illuminate/contracts": "^11.0|^12.0", + "illuminate/database": "^11.0|^12.0", + "illuminate/support": "^11.0|^12.0", "php": "^8.2", "symfony/console": "^7.0" }, "require-dev": { "mockery/mockery": "^1.6", - "orchestra/testbench": "^9.0", + "orchestra/testbench": "^9.0|^10.0", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.5" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { @@ -1524,36 +1607,36 @@ "issues": "https://github.com/laravel/sanctum/issues", "source": "https://github.com/laravel/sanctum" }, - "time": "2024-09-27T14:55:41+00:00" + "time": "2025-01-26T19:34:36+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.3.5", + "version": "v2.0.2", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c" + "reference": "2e1a362527783bcab6c316aad51bf36c5513ae44" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c", - "reference": "1dc4a3dbfa2b7628a3114e43e32120cce7cdda9c", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/2e1a362527783bcab6c316aad51bf36c5513ae44", + "reference": "2e1a362527783bcab6c316aad51bf36c5513ae44", "shasum": "" }, "require": { - "php": "^7.3|^8.0" + "php": "^8.1" }, "require-dev": { - "illuminate/support": "^8.0|^9.0|^10.0|^11.0", - "nesbot/carbon": "^2.61|^3.0", - "pestphp/pest": "^1.21.3", - "phpstan/phpstan": "^1.8.2", - "symfony/var-dumper": "^5.4.11|^6.2.0|^7.0.0" + "illuminate/support": "^10.0|^11.0|^12.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -1585,26 +1668,26 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2024-09-23T13:33:08+00:00" + "time": "2025-01-24T15:42:37+00:00" }, { "name": "laravel/tinker", - "version": "v2.10.0", + "version": "v2.10.1", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "ba4d51eb56de7711b3a37d63aa0643e99a339ae5" + "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/ba4d51eb56de7711b3a37d63aa0643e99a339ae5", - "reference": "ba4d51eb56de7711b3a37d63aa0643e99a339ae5", + "url": "https://api.github.com/repos/laravel/tinker/zipball/22177cc71807d38f2810c6204d8f7183d88a57d3", + "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3", "shasum": "" }, "require": { - "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", - "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", "php": "^7.2.5|^8.0", "psy/psysh": "^0.11.1|^0.12.0", "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" @@ -1612,10 +1695,10 @@ "require-dev": { "mockery/mockery": "~1.3.3|^1.4.2", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8.5.8|^9.3.3" + "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0" }, "suggest": { - "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0)." + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)." }, "type": "library", "extra": { @@ -1649,22 +1732,22 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.10.0" + "source": "https://github.com/laravel/tinker/tree/v2.10.1" }, - "time": "2024-09-23T13:32:56+00:00" + "time": "2025-01-27T14:24:01+00:00" }, { "name": "league/commonmark", - "version": "2.5.3", + "version": "2.6.1", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "b650144166dfa7703e62a22e493b853b58d874b0" + "reference": "d990688c91cedfb69753ffc2512727ec646df2ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/b650144166dfa7703e62a22e493b853b58d874b0", - "reference": "b650144166dfa7703e62a22e493b853b58d874b0", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d990688c91cedfb69753ffc2512727ec646df2ad", + "reference": "d990688c91cedfb69753ffc2512727ec646df2ad", "shasum": "" }, "require": { @@ -1689,8 +1772,9 @@ "phpstan/phpstan": "^1.8.2", "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", "scrutinizer/ocular": "^1.8.1", - "symfony/finder": "^5.3 | ^6.0 || ^7.0", - "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 || ^7.0", + "symfony/finder": "^5.3 | ^6.0 | ^7.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", "unleashedtech/php-coding-standard": "^3.1.1", "vimeo/psalm": "^4.24.0 || ^5.0.0" }, @@ -1700,7 +1784,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.6-dev" + "dev-main": "2.7-dev" } }, "autoload": { @@ -1757,7 +1841,7 @@ "type": "tidelift" } ], - "time": "2024-08-16T11:46:16+00:00" + "time": "2024-12-29T14:10:59+00:00" }, { "name": "league/config", @@ -2029,33 +2113,208 @@ ], "time": "2024-09-21T08:32:55+00:00" }, + { + "name": "league/uri", + "version": "7.5.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "81fb5145d2644324614cc532b28efd0215bda430" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", + "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.5", + "php": "^8.1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.5.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:40:02+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.5.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-factory": "^1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common interfaces and classes for URI representation and interaction", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:18:47+00:00" + }, { "name": "maennchen/zipstream-php", - "version": "3.1.0", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/maennchen/ZipStream-PHP.git", - "reference": "b8174494eda667f7d13876b4a7bfef0f62a7c0d1" + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/b8174494eda667f7d13876b4a7bfef0f62a7c0d1", - "reference": "b8174494eda667f7d13876b4a7bfef0f62a7c0d1", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/aeadcf5c412332eb426c0f9b4485f6accba2a99f", + "reference": "aeadcf5c412332eb426c0f9b4485f6accba2a99f", "shasum": "" }, "require": { "ext-mbstring": "*", "ext-zlib": "*", - "php-64bit": "^8.1" + "php-64bit": "^8.2" }, "require-dev": { + "brianium/paratest": "^7.7", "ext-zip": "*", "friendsofphp/php-cs-fixer": "^3.16", "guzzlehttp/guzzle": "^7.5", "mikey179/vfsstream": "^1.6", "php-coveralls/php-coveralls": "^2.5", - "phpunit/phpunit": "^10.0", - "vimeo/psalm": "^5.0" + "phpunit/phpunit": "^11.0", + "vimeo/psalm": "^6.0" }, "suggest": { "guzzlehttp/psr7": "^2.4", @@ -2096,32 +2355,28 @@ ], "support": { "issues": "https://github.com/maennchen/ZipStream-PHP/issues", - "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.0" + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.2" }, "funding": [ { "url": "https://github.com/maennchen", "type": "github" - }, - { - "url": "https://opencollective.com/zipstream", - "type": "open_collective" } ], - "time": "2023-06-21T14:59:35+00:00" + "time": "2025-01-27T12:07:53+00:00" }, { "name": "monolog/monolog", - "version": "3.7.0", + "version": "3.8.1", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8" + "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f4393b648b78a5408747de94fca38beb5f7e9ef8", - "reference": "f4393b648b78a5408747de94fca38beb5f7e9ef8", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/aef6ee73a77a66e404dd6540934a9ef1b3c855b4", + "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4", "shasum": "" }, "require": { @@ -2141,12 +2396,14 @@ "guzzlehttp/psr7": "^2.2", "mongodb/mongodb": "^1.8", "php-amqplib/php-amqplib": "~2.4 || ^3", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-deprecation-rules": "^1.0", - "phpstan/phpstan-strict-rules": "^1.4", - "phpunit/phpunit": "^10.5.17", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", "predis/predis": "^1.1 || ^2", - "ruflin/elastica": "^7", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", "symfony/mailer": "^5.4 || ^6", "symfony/mime": "^5.4 || ^6" }, @@ -2197,7 +2454,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.7.0" + "source": "https://github.com/Seldaek/monolog/tree/3.8.1" }, "funding": [ { @@ -2209,24 +2466,24 @@ "type": "tidelift" } ], - "time": "2024-06-28T09:40:51+00:00" + "time": "2024-12-05T17:15:07+00:00" }, { "name": "nesbot/carbon", - "version": "3.8.0", + "version": "3.8.4", "source": { "type": "git", - "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "bbd3eef89af8ba66a3aa7952b5439168fbcc529f" + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "129700ed449b1f02d70272d2ac802357c8c30c58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bbd3eef89af8ba66a3aa7952b5439168fbcc529f", - "reference": "bbd3eef89af8ba66a3aa7952b5439168fbcc529f", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/129700ed449b1f02d70272d2ac802357c8c30c58", + "reference": "129700ed449b1f02d70272d2ac802357c8c30c58", "shasum": "" }, "require": { - "carbonphp/carbon-doctrine-types": "*", + "carbonphp/carbon-doctrine-types": "<100.0", "ext-json": "*", "php": "^8.1", "psr/clock": "^1.0", @@ -2254,10 +2511,6 @@ ], "type": "library", "extra": { - "branch-alias": { - "dev-master": "3.x-dev", - "dev-2.x": "2.x-dev" - }, "laravel": { "providers": [ "Carbon\\Laravel\\ServiceProvider" @@ -2267,6 +2520,10 @@ "includes": [ "extension.neon" ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" } }, "autoload": { @@ -2315,7 +2572,7 @@ "type": "tidelift" } ], - "time": "2024-08-19T06:22:39+00:00" + "time": "2024-12-27T09:25:35+00:00" }, { "name": "nette/schema", @@ -2467,16 +2724,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.3.1", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", "shasum": "" }, "require": { @@ -2519,38 +2776,37 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" }, - "time": "2024-10-08T18:51:32+00:00" + "time": "2024-12-30T11:07:19+00:00" }, { "name": "nunomaduro/termwind", - "version": "v2.1.0", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "e5f21eade88689536c0cdad4c3cd75f3ed26e01a" + "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/e5f21eade88689536c0cdad4c3cd75f3ed26e01a", - "reference": "e5f21eade88689536c0cdad4c3cd75f3ed26e01a", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/52915afe6a1044e8b9cee1bcff836fb63acf9cda", + "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda", "shasum": "" }, "require": { "ext-mbstring": "*", "php": "^8.2", - "symfony/console": "^7.0.4" + "symfony/console": "^7.1.8" }, "require-dev": { - "ergebnis/phpstan-rules": "^2.2.0", - "illuminate/console": "^11.1.1", - "laravel/pint": "^1.15.0", - "mockery/mockery": "^1.6.11", - "pestphp/pest": "^2.34.6", - "phpstan/phpstan": "^1.10.66", - "phpstan/phpstan-strict-rules": "^1.5.2", - "symfony/var-dumper": "^7.0.4", + "illuminate/console": "^11.33.2", + "laravel/pint": "^1.18.2", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0", + "phpstan/phpstan": "^1.12.11", + "phpstan/phpstan-strict-rules": "^1.6.1", + "symfony/var-dumper": "^7.1.8", "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -2593,7 +2849,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.1.0" + "source": "https://github.com/nunomaduro/termwind/tree/v2.3.0" }, "funding": [ { @@ -2609,7 +2865,7 @@ "type": "github" } ], - "time": "2024-09-05T15:25:50+00:00" + "time": "2024-11-21T10:39:51+00:00" }, { "name": "phpoption/phpoption", @@ -3100,16 +3356,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.4", + "version": "v0.12.7", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "2fd717afa05341b4f8152547f142cd2f130f6818" + "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/2fd717afa05341b4f8152547f142cd2f130f6818", - "reference": "2fd717afa05341b4f8152547f142cd2f130f6818", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/d73fa3c74918ef4522bb8a3bf9cab39161c4b57c", + "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c", "shasum": "" }, "require": { @@ -3136,12 +3392,12 @@ ], "type": "library", "extra": { - "branch-alias": { - "dev-main": "0.12.x-dev" - }, "bamarni-bin": { "bin-links": false, "forward-command": false + }, + "branch-alias": { + "dev-main": "0.12.x-dev" } }, "autoload": { @@ -3173,9 +3429,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.4" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.7" }, - "time": "2024-06-10T01:18:23+00:00" + "time": "2024-12-10T01:58:33+00:00" }, { "name": "ralouphie/getallheaders", @@ -3402,18 +3658,136 @@ ], "time": "2024-04-27T21:32:50+00:00" }, + { + "name": "setasign/fpdf", + "version": "1.8.6", + "source": { + "type": "git", + "url": "https://github.com/Setasign/FPDF.git", + "reference": "0838e0ee4925716fcbbc50ad9e1799b5edfae0a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Setasign/FPDF/zipball/0838e0ee4925716fcbbc50ad9e1799b5edfae0a0", + "reference": "0838e0ee4925716fcbbc50ad9e1799b5edfae0a0", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "ext-zlib": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "fpdf.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Olivier Plathey", + "email": "oliver@fpdf.org", + "homepage": "http://fpdf.org/" + } + ], + "description": "FPDF is a PHP class which allows to generate PDF files with pure PHP. F from FPDF stands for Free: you may use it for any kind of usage and modify it to suit your needs.", + "homepage": "http://www.fpdf.org", + "keywords": [ + "fpdf", + "pdf" + ], + "support": { + "source": "https://github.com/Setasign/FPDF/tree/1.8.6" + }, + "time": "2023-06-26T14:44:25+00:00" + }, + { + "name": "setasign/fpdi", + "version": "v2.6.3", + "source": { + "type": "git", + "url": "https://github.com/Setasign/FPDI.git", + "reference": "67c31f5e50c93c20579ca9e23035d8c540b51941" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Setasign/FPDI/zipball/67c31f5e50c93c20579ca9e23035d8c540b51941", + "reference": "67c31f5e50c93c20579ca9e23035d8c540b51941", + "shasum": "" + }, + "require": { + "ext-zlib": "*", + "php": "^7.1 || ^8.0" + }, + "conflict": { + "setasign/tfpdf": "<1.31" + }, + "require-dev": { + "phpunit/phpunit": "^7", + "setasign/fpdf": "~1.8.6", + "setasign/tfpdf": "~1.33", + "squizlabs/php_codesniffer": "^3.5", + "tecnickcom/tcpdf": "^6.2" + }, + "suggest": { + "setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured." + }, + "type": "library", + "autoload": { + "psr-4": { + "setasign\\Fpdi\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Slabon", + "email": "jan.slabon@setasign.com", + "homepage": "https://www.setasign.com" + }, + { + "name": "Maximilian Kresse", + "email": "maximilian.kresse@setasign.com", + "homepage": "https://www.setasign.com" + } + ], + "description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.", + "homepage": "https://www.setasign.com/fpdi", + "keywords": [ + "fpdf", + "fpdi", + "pdf" + ], + "support": { + "issues": "https://github.com/Setasign/FPDI/issues", + "source": "https://github.com/Setasign/FPDI/tree/v2.6.3" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/setasign/fpdi", + "type": "tidelift" + } + ], + "time": "2025-02-05T13:22:35+00:00" + }, { "name": "spatie/image", - "version": "3.7.4", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/spatie/image.git", - "reference": "d72d1ae07f91a3c1230e064acd4fd8c334ab237b" + "reference": "06cf293f66c833704935ba18e16c784d7e8433a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/image/zipball/d72d1ae07f91a3c1230e064acd4fd8c334ab237b", - "reference": "d72d1ae07f91a3c1230e064acd4fd8c334ab237b", + "url": "https://api.github.com/repos/spatie/image/zipball/06cf293f66c833704935ba18e16c784d7e8433a7", + "reference": "06cf293f66c833704935ba18e16c784d7e8433a7", "shasum": "" }, "require": { @@ -3461,7 +3835,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/image/tree/3.7.4" + "source": "https://github.com/spatie/image/tree/3.8.0" }, "funding": [ { @@ -3473,20 +3847,20 @@ "type": "github" } ], - "time": "2024-10-07T09:03:34+00:00" + "time": "2025-01-17T10:19:44+00:00" }, { "name": "spatie/image-optimizer", - "version": "1.7.5", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/spatie/image-optimizer.git", - "reference": "43aff6725cd87bb78ccd8532633cfa8bdc962505" + "reference": "4fd22035e81d98fffced65a8c20d9ec4daa9671c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/image-optimizer/zipball/43aff6725cd87bb78ccd8532633cfa8bdc962505", - "reference": "43aff6725cd87bb78ccd8532633cfa8bdc962505", + "url": "https://api.github.com/repos/spatie/image-optimizer/zipball/4fd22035e81d98fffced65a8c20d9ec4daa9671c", + "reference": "4fd22035e81d98fffced65a8c20d9ec4daa9671c", "shasum": "" }, "require": { @@ -3526,22 +3900,22 @@ ], "support": { "issues": "https://github.com/spatie/image-optimizer/issues", - "source": "https://github.com/spatie/image-optimizer/tree/1.7.5" + "source": "https://github.com/spatie/image-optimizer/tree/1.8.0" }, - "time": "2024-05-16T08:48:33+00:00" + "time": "2024-11-04T08:24:54+00:00" }, { "name": "spatie/laravel-medialibrary", - "version": "11.9.1", + "version": "11.12.3", "source": { "type": "git", "url": "https://github.com/spatie/laravel-medialibrary.git", - "reference": "ff589ea5532a33d84faeb64bfdfd59057b4148b8" + "reference": "f7ff95dcee59f18bd29187193b02bdb3d746f975" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/ff589ea5532a33d84faeb64bfdfd59057b4148b8", - "reference": "ff589ea5532a33d84faeb64bfdfd59057b4148b8", + "url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/f7ff95dcee59f18bd29187193b02bdb3d746f975", + "reference": "f7ff95dcee59f18bd29187193b02bdb3d746f975", "shasum": "" }, "require": { @@ -3625,7 +3999,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-medialibrary/issues", - "source": "https://github.com/spatie/laravel-medialibrary/tree/11.9.1" + "source": "https://github.com/spatie/laravel-medialibrary/tree/11.12.3" }, "funding": [ { @@ -3637,32 +4011,32 @@ "type": "github" } ], - "time": "2024-09-02T06:32:15+00:00" + "time": "2025-02-06T11:56:04+00:00" }, { "name": "spatie/laravel-package-tools", - "version": "1.16.5", + "version": "1.19.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-package-tools.git", - "reference": "c7413972cf22ffdff97b68499c22baa04eddb6a2" + "reference": "1c9c30ac6a6576b8d15c6c37b6cf23d748df2faa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/c7413972cf22ffdff97b68499c22baa04eddb6a2", - "reference": "c7413972cf22ffdff97b68499c22baa04eddb6a2", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/1c9c30ac6a6576b8d15c6c37b6cf23d748df2faa", + "reference": "1c9c30ac6a6576b8d15c6c37b6cf23d748df2faa", "shasum": "" }, "require": { - "illuminate/contracts": "^9.28|^10.0|^11.0", + "illuminate/contracts": "^9.28|^10.0|^11.0|^12.0", "php": "^8.0" }, "require-dev": { "mockery/mockery": "^1.5", - "orchestra/testbench": "^7.7|^8.0", - "pestphp/pest": "^1.22", - "phpunit/phpunit": "^9.5.24", - "spatie/pest-plugin-test-time": "^1.1" + "orchestra/testbench": "^7.7|^8.0|^9.0|^10.0", + "pestphp/pest": "^1.23|^2.1|^3.1", + "phpunit/phpunit": "^9.5.24|^10.5|^11.5", + "spatie/pest-plugin-test-time": "^1.1|^2.2" }, "type": "library", "autoload": { @@ -3689,7 +4063,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-package-tools/issues", - "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.5" + "source": "https://github.com/spatie/laravel-package-tools/tree/1.19.0" }, "funding": [ { @@ -3697,44 +4071,45 @@ "type": "github" } ], - "time": "2024-08-27T18:56:10+00:00" + "time": "2025-02-06T14:58:20+00:00" }, { "name": "spatie/laravel-permission", - "version": "6.9.0", + "version": "6.13.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-permission.git", - "reference": "fe973a58b44380d0e8620107259b7bda22f70408" + "reference": "bb3ad222d65ec804c219cc52a8b54f5af2e1636a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/fe973a58b44380d0e8620107259b7bda22f70408", - "reference": "fe973a58b44380d0e8620107259b7bda22f70408", + "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/bb3ad222d65ec804c219cc52a8b54f5af2e1636a", + "reference": "bb3ad222d65ec804c219cc52a8b54f5af2e1636a", "shasum": "" }, "require": { - "illuminate/auth": "^8.12|^9.0|^10.0|^11.0", - "illuminate/container": "^8.12|^9.0|^10.0|^11.0", - "illuminate/contracts": "^8.12|^9.0|^10.0|^11.0", - "illuminate/database": "^8.12|^9.0|^10.0|^11.0", + "illuminate/auth": "^8.12|^9.0|^10.0|^11.0|^12.0", + "illuminate/container": "^8.12|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^8.12|^9.0|^10.0|^11.0|^12.0", + "illuminate/database": "^8.12|^9.0|^10.0|^11.0|^12.0", "php": "^8.0" }, "require-dev": { "laravel/passport": "^11.0|^12.0", - "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0", - "phpunit/phpunit": "^9.4|^10.1" + "laravel/pint": "^1.0", + "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0|^10.0", + "phpunit/phpunit": "^9.4|^10.1|^11.5" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "6.x-dev", - "dev-master": "6.x-dev" - }, "laravel": { "providers": [ "Spatie\\Permission\\PermissionServiceProvider" ] + }, + "branch-alias": { + "dev-main": "6.x-dev", + "dev-master": "6.x-dev" } }, "autoload": { @@ -3771,7 +4146,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-permission/issues", - "source": "https://github.com/spatie/laravel-permission/tree/6.9.0" + "source": "https://github.com/spatie/laravel-permission/tree/6.13.0" }, "funding": [ { @@ -3779,20 +4154,20 @@ "type": "github" } ], - "time": "2024-06-22T23:04:52+00:00" + "time": "2025-02-05T15:42:48+00:00" }, { "name": "spatie/temporary-directory", - "version": "2.2.1", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/spatie/temporary-directory.git", - "reference": "76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a" + "reference": "580eddfe9a0a41a902cac6eeb8f066b42e65a32b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a", - "reference": "76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a", + "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/580eddfe9a0a41a902cac6eeb8f066b42e65a32b", + "reference": "580eddfe9a0a41a902cac6eeb8f066b42e65a32b", "shasum": "" }, "require": { @@ -3828,7 +4203,7 @@ ], "support": { "issues": "https://github.com/spatie/temporary-directory/issues", - "source": "https://github.com/spatie/temporary-directory/tree/2.2.1" + "source": "https://github.com/spatie/temporary-directory/tree/2.3.0" }, "funding": [ { @@ -3840,20 +4215,20 @@ "type": "github" } ], - "time": "2023-12-25T11:46:58+00:00" + "time": "2025-01-13T13:04:43+00:00" }, { "name": "symfony/clock", - "version": "v7.1.1", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7" + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/3dfc8b084853586de51dd1441c6242c76a28cbe7", - "reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7", + "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", "shasum": "" }, "require": { @@ -3898,7 +4273,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.1.1" + "source": "https://github.com/symfony/clock/tree/v7.2.0" }, "funding": [ { @@ -3914,20 +4289,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/console", - "version": "v7.1.5", + "version": "v7.2.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee" + "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0fa539d12b3ccf068a722bbbffa07ca7079af9ee", - "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee", + "url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3", + "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3", "shasum": "" }, "require": { @@ -3991,7 +4366,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.5" + "source": "https://github.com/symfony/console/tree/v7.2.1" }, "funding": [ { @@ -4007,20 +4382,20 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-12-11T03:49:26+00:00" }, { "name": "symfony/css-selector", - "version": "v7.1.1", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4" + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/1c7cee86c6f812896af54434f8ce29c8d94f9ff4", - "reference": "1c7cee86c6f812896af54434f8ce29c8d94f9ff4", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", "shasum": "" }, "require": { @@ -4056,7 +4431,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.1.1" + "source": "https://github.com/symfony/css-selector/tree/v7.2.0" }, "funding": [ { @@ -4072,20 +4447,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { @@ -4093,12 +4468,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -4123,7 +4498,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" }, "funding": [ { @@ -4139,20 +4514,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/error-handler", - "version": "v7.1.3", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "432bb369952795c61ca1def65e078c4a80dad13c" + "reference": "959a74d044a6db21f4caa6d695648dcb5584cb49" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/432bb369952795c61ca1def65e078c4a80dad13c", - "reference": "432bb369952795c61ca1def65e078c4a80dad13c", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/959a74d044a6db21f4caa6d695648dcb5584cb49", + "reference": "959a74d044a6db21f4caa6d695648dcb5584cb49", "shasum": "" }, "require": { @@ -4198,7 +4573,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.1.3" + "source": "https://github.com/symfony/error-handler/tree/v7.2.3" }, "funding": [ { @@ -4214,20 +4589,20 @@ "type": "tidelift" } ], - "time": "2024-07-26T13:02:51+00:00" + "time": "2025-01-07T09:39:55+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.1.1", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7" + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", - "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1", + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1", "shasum": "" }, "require": { @@ -4278,7 +4653,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.1" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.2.0" }, "funding": [ { @@ -4294,20 +4669,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50" + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/8f93aec25d41b72493c6ddff14e916177c9efc50", - "reference": "8f93aec25d41b72493c6ddff14e916177c9efc50", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", "shasum": "" }, "require": { @@ -4316,12 +4691,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -4354,7 +4729,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" }, "funding": [ { @@ -4370,20 +4745,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/finder", - "version": "v7.1.4", + "version": "v7.2.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "d95bbf319f7d052082fb7af147e0f835a695e823" + "reference": "87a71856f2f56e4100373e92529eed3171695cfb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/d95bbf319f7d052082fb7af147e0f835a695e823", - "reference": "d95bbf319f7d052082fb7af147e0f835a695e823", + "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb", + "reference": "87a71856f2f56e4100373e92529eed3171695cfb", "shasum": "" }, "require": { @@ -4418,7 +4793,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.4" + "source": "https://github.com/symfony/finder/tree/v7.2.2" }, "funding": [ { @@ -4434,35 +4809,36 @@ "type": "tidelift" } ], - "time": "2024-08-13T14:28:19+00:00" + "time": "2024-12-30T19:00:17+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.1.5", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "e30ef73b1e44eea7eb37ba69600a354e553f694b" + "reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e30ef73b1e44eea7eb37ba69600a354e553f694b", - "reference": "e30ef73b1e44eea7eb37ba69600a354e553f694b", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ee1b504b8926198be89d05e5b6fc4c3810c090f0", + "reference": "ee1b504b8926198be89d05e5b6fc4c3810c090f0", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/polyfill-mbstring": "~1.1", "symfony/polyfill-php83": "^1.27" }, "conflict": { "doctrine/dbal": "<3.6", - "symfony/cache": "<6.4" + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" }, "require-dev": { "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4|^7.0", + "symfony/cache": "^6.4.12|^7.1.5", "symfony/dependency-injection": "^6.4|^7.0", "symfony/expression-language": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", @@ -4495,7 +4871,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.1.5" + "source": "https://github.com/symfony/http-foundation/tree/v7.2.3" }, "funding": [ { @@ -4511,20 +4887,20 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2025-01-17T10:56:55+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.1.5", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "44204d96150a9df1fc57601ec933d23fefc2d65b" + "reference": "caae9807f8e25a9b43ce8cc6fafab6cf91f0cc9b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/44204d96150a9df1fc57601ec933d23fefc2d65b", - "reference": "44204d96150a9df1fc57601ec933d23fefc2d65b", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/caae9807f8e25a9b43ce8cc6fafab6cf91f0cc9b", + "reference": "caae9807f8e25a9b43ce8cc6fafab6cf91f0cc9b", "shasum": "" }, "require": { @@ -4553,7 +4929,7 @@ "symfony/twig-bridge": "<6.4", "symfony/validator": "<6.4", "symfony/var-dumper": "<6.4", - "twig/twig": "<3.0.4" + "twig/twig": "<3.12" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" @@ -4581,7 +4957,7 @@ "symfony/validator": "^6.4|^7.0", "symfony/var-dumper": "^6.4|^7.0", "symfony/var-exporter": "^6.4|^7.0", - "twig/twig": "^3.0.4" + "twig/twig": "^3.12" }, "type": "library", "autoload": { @@ -4609,7 +4985,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.1.5" + "source": "https://github.com/symfony/http-kernel/tree/v7.2.3" }, "funding": [ { @@ -4625,20 +5001,20 @@ "type": "tidelift" } ], - "time": "2024-09-21T06:09:21+00:00" + "time": "2025-01-29T07:40:13+00:00" }, { "name": "symfony/mailer", - "version": "v7.1.5", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "bbf21460c56f29810da3df3e206e38dfbb01e80b" + "reference": "f3871b182c44997cf039f3b462af4a48fb85f9d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/bbf21460c56f29810da3df3e206e38dfbb01e80b", - "reference": "bbf21460c56f29810da3df3e206e38dfbb01e80b", + "url": "https://api.github.com/repos/symfony/mailer/zipball/f3871b182c44997cf039f3b462af4a48fb85f9d3", + "reference": "f3871b182c44997cf039f3b462af4a48fb85f9d3", "shasum": "" }, "require": { @@ -4647,7 +5023,7 @@ "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", + "symfony/mime": "^7.2", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -4689,7 +5065,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.1.5" + "source": "https://github.com/symfony/mailer/tree/v7.2.3" }, "funding": [ { @@ -4705,20 +5081,20 @@ "type": "tidelift" } ], - "time": "2024-09-08T12:32:26+00:00" + "time": "2025-01-27T11:08:17+00:00" }, { "name": "symfony/mime", - "version": "v7.1.5", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "711d2e167e8ce65b05aea6b258c449671cdd38ff" + "reference": "2fc3b4bd67e4747e45195bc4c98bea4628476204" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/711d2e167e8ce65b05aea6b258c449671cdd38ff", - "reference": "711d2e167e8ce65b05aea6b258c449671cdd38ff", + "url": "https://api.github.com/repos/symfony/mime/zipball/2fc3b4bd67e4747e45195bc4c98bea4628476204", + "reference": "2fc3b4bd67e4747e45195bc4c98bea4628476204", "shasum": "" }, "require": { @@ -4773,7 +5149,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.1.5" + "source": "https://github.com/symfony/mime/tree/v7.2.3" }, "funding": [ { @@ -4789,7 +5165,7 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2025-01-27T11:08:17+00:00" }, { "name": "symfony/polyfill-ctype", @@ -4817,8 +5193,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4893,8 +5269,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4972,8 +5348,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -5054,8 +5430,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -5138,8 +5514,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -5212,8 +5588,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -5292,8 +5668,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -5374,8 +5750,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -5429,16 +5805,16 @@ }, { "name": "symfony/process", - "version": "v7.1.5", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "5c03ee6369281177f07f7c68252a280beccba847" + "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/5c03ee6369281177f07f7c68252a280beccba847", - "reference": "5c03ee6369281177f07f7c68252a280beccba847", + "url": "https://api.github.com/repos/symfony/process/zipball/d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", + "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", "shasum": "" }, "require": { @@ -5470,7 +5846,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.5" + "source": "https://github.com/symfony/process/tree/v7.2.0" }, "funding": [ { @@ -5486,20 +5862,20 @@ "type": "tidelift" } ], - "time": "2024-09-19T21:48:23+00:00" + "time": "2024-11-06T14:24:19+00:00" }, { "name": "symfony/routing", - "version": "v7.1.4", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "1500aee0094a3ce1c92626ed8cf3c2037e86f5a7" + "reference": "ee9a67edc6baa33e5fae662f94f91fd262930996" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/1500aee0094a3ce1c92626ed8cf3c2037e86f5a7", - "reference": "1500aee0094a3ce1c92626ed8cf3c2037e86f5a7", + "url": "https://api.github.com/repos/symfony/routing/zipball/ee9a67edc6baa33e5fae662f94f91fd262930996", + "reference": "ee9a67edc6baa33e5fae662f94f91fd262930996", "shasum": "" }, "require": { @@ -5551,7 +5927,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.1.4" + "source": "https://github.com/symfony/routing/tree/v7.2.3" }, "funding": [ { @@ -5567,20 +5943,20 @@ "type": "tidelift" } ], - "time": "2024-08-29T08:16:25+00:00" + "time": "2025-01-17T10:56:55+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", "shasum": "" }, "require": { @@ -5593,12 +5969,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -5634,7 +6010,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" }, "funding": [ { @@ -5650,20 +6026,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/string", - "version": "v7.1.5", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306" + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d66f9c343fa894ec2037cc928381df90a7ad4306", - "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306", + "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", "shasum": "" }, "require": { @@ -5721,7 +6097,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.5" + "source": "https://github.com/symfony/string/tree/v7.2.0" }, "funding": [ { @@ -5737,24 +6113,25 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-11-13T13:31:26+00:00" }, { "name": "symfony/translation", - "version": "v7.1.5", + "version": "v7.2.2", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "235535e3f84f3dfbdbde0208ede6ca75c3a489ea" + "reference": "e2674a30132b7cc4d74540d6c2573aa363f05923" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/235535e3f84f3dfbdbde0208ede6ca75c3a489ea", - "reference": "235535e3f84f3dfbdbde0208ede6ca75c3a489ea", + "url": "https://api.github.com/repos/symfony/translation/zipball/e2674a30132b7cc4d74540d6c2573aa363f05923", + "reference": "e2674a30132b7cc4d74540d6c2573aa363f05923", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/translation-contracts": "^2.5|^3.0" }, @@ -5815,7 +6192,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.1.5" + "source": "https://github.com/symfony/translation/tree/v7.2.2" }, "funding": [ { @@ -5831,20 +6208,20 @@ "type": "tidelift" } ], - "time": "2024-09-16T06:30:38+00:00" + "time": "2024-12-07T08:18:10+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a" + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", - "reference": "b9d2189887bb6b2e0367a9fc7136c5239ab9b05a", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", "shasum": "" }, "require": { @@ -5852,12 +6229,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -5893,7 +6270,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" }, "funding": [ { @@ -5909,20 +6286,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/uid", - "version": "v7.1.5", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "8c7bb8acb933964055215d89f9a9871df0239317" + "reference": "2d294d0c48df244c71c105a169d0190bfb080426" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/8c7bb8acb933964055215d89f9a9871df0239317", - "reference": "8c7bb8acb933964055215d89f9a9871df0239317", + "url": "https://api.github.com/repos/symfony/uid/zipball/2d294d0c48df244c71c105a169d0190bfb080426", + "reference": "2d294d0c48df244c71c105a169d0190bfb080426", "shasum": "" }, "require": { @@ -5967,7 +6344,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.1.5" + "source": "https://github.com/symfony/uid/tree/v7.2.0" }, "funding": [ { @@ -5983,20 +6360,20 @@ "type": "tidelift" } ], - "time": "2024-09-17T09:16:35+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.1.5", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "e20e03889539fd4e4211e14d2179226c513c010d" + "reference": "82b478c69745d8878eb60f9a049a4d584996f73a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e20e03889539fd4e4211e14d2179226c513c010d", - "reference": "e20e03889539fd4e4211e14d2179226c513c010d", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/82b478c69745d8878eb60f9a049a4d584996f73a", + "reference": "82b478c69745d8878eb60f9a049a4d584996f73a", "shasum": "" }, "require": { @@ -6012,7 +6389,7 @@ "symfony/http-kernel": "^6.4|^7.0", "symfony/process": "^6.4|^7.0", "symfony/uid": "^6.4|^7.0", - "twig/twig": "^3.0.4" + "twig/twig": "^3.12" }, "bin": [ "Resources/bin/var-dump-server" @@ -6050,7 +6427,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.1.5" + "source": "https://github.com/symfony/var-dumper/tree/v7.2.3" }, "funding": [ { @@ -6066,35 +6443,37 @@ "type": "tidelift" } ], - "time": "2024-09-16T10:07:02+00:00" + "time": "2025-01-17T11:39:41+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", - "version": "v2.2.7", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb" + "reference": "0d72ac1c00084279c1816675284073c5a337c20d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/83ee6f38df0a63106a9e4536e3060458b74ccedb", - "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0d72ac1c00084279c1816675284073c5a337c20d", + "reference": "0d72ac1c00084279c1816675284073c5a337c20d", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", - "php": "^5.5 || ^7.0 || ^8.0", - "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" + "php": "^7.4 || ^8.0", + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10" + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -6117,9 +6496,9 @@ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "support": { "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.2.7" + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.3.0" }, - "time": "2023-12-08T13:03:43+00:00" + "time": "2024-12-21T16:25:41+00:00" }, { "name": "vlucas/phpdotenv", @@ -6207,16 +6586,16 @@ }, { "name": "voku/portable-ascii", - "version": "2.0.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "b56450eed252f6801410d810c8e1727224ae0743" + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", - "reference": "b56450eed252f6801410d810c8e1727224ae0743", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", "shasum": "" }, "require": { @@ -6241,7 +6620,7 @@ "authors": [ { "name": "Lars Moelleken", - "homepage": "http://www.moelleken.org/" + "homepage": "https://www.moelleken.org/" } ], "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", @@ -6253,7 +6632,7 @@ ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/2.0.1" + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" }, "funding": [ { @@ -6277,7 +6656,7 @@ "type": "tidelift" } ], - "time": "2022-03-08T17:03:00+00:00" + "time": "2024-11-21T01:49:47+00:00" }, { "name": "webmozart/assert", @@ -6341,16 +6720,16 @@ "packages-dev": [ { "name": "filp/whoops", - "version": "2.16.0", + "version": "2.17.0", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "befcdc0e5dce67252aa6322d82424be928214fa2" + "reference": "075bc0c26631110584175de6523ab3f1652eb28e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/befcdc0e5dce67252aa6322d82424be928214fa2", - "reference": "befcdc0e5dce67252aa6322d82424be928214fa2", + "url": "https://api.github.com/repos/filp/whoops/zipball/075bc0c26631110584175de6523ab3f1652eb28e", + "reference": "075bc0c26631110584175de6523ab3f1652eb28e", "shasum": "" }, "require": { @@ -6400,7 +6779,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.16.0" + "source": "https://github.com/filp/whoops/tree/2.17.0" }, "funding": [ { @@ -6408,7 +6787,7 @@ "type": "github" } ], - "time": "2024-09-25T12:00:00+00:00" + "time": "2025-01-25T12:00:00+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -6463,16 +6842,16 @@ }, { "name": "laravel/pint", - "version": "v1.18.1", + "version": "v1.20.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "35c00c05ec43e6b46d295efc0f4386ceb30d50d9" + "reference": "53072e8ea22213a7ed168a8a15b96fbb8b82d44b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/35c00c05ec43e6b46d295efc0f4386ceb30d50d9", - "reference": "35c00c05ec43e6b46d295efc0f4386ceb30d50d9", + "url": "https://api.github.com/repos/laravel/pint/zipball/53072e8ea22213a7ed168a8a15b96fbb8b82d44b", + "reference": "53072e8ea22213a7ed168a8a15b96fbb8b82d44b", "shasum": "" }, "require": { @@ -6483,13 +6862,13 @@ "php": "^8.1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.64.0", - "illuminate/view": "^10.48.20", - "larastan/larastan": "^2.9.8", - "laravel-zero/framework": "^10.4.0", + "friendsofphp/php-cs-fixer": "^3.66.0", + "illuminate/view": "^10.48.25", + "larastan/larastan": "^2.9.12", + "laravel-zero/framework": "^10.48.25", "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.35.1" + "nunomaduro/termwind": "^1.17.0", + "pestphp/pest": "^2.36.0" }, "bin": [ "builds/pint" @@ -6525,32 +6904,32 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-09-24T17:22:50+00:00" + "time": "2025-01-14T16:20:53+00:00" }, { "name": "laravel/sail", - "version": "v1.35.0", + "version": "v1.41.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "992bc2d9e52174c79515967f30849d21daa334d8" + "reference": "fe1a4ada0abb5e4bd99eb4e4b0d87906c00cdeec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/992bc2d9e52174c79515967f30849d21daa334d8", - "reference": "992bc2d9e52174c79515967f30849d21daa334d8", + "url": "https://api.github.com/repos/laravel/sail/zipball/fe1a4ada0abb5e4bd99eb4e4b0d87906c00cdeec", + "reference": "fe1a4ada0abb5e4bd99eb4e4b0d87906c00cdeec", "shasum": "" }, "require": { - "illuminate/console": "^9.52.16|^10.0|^11.0", - "illuminate/contracts": "^9.52.16|^10.0|^11.0", - "illuminate/support": "^9.52.16|^10.0|^11.0", + "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0", + "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0", "php": "^8.0", "symfony/console": "^6.0|^7.0", "symfony/yaml": "^6.0|^7.0" }, "require-dev": { - "orchestra/testbench": "^7.0|^8.0|^9.0", + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0", "phpstan/phpstan": "^1.10" }, "bin": [ @@ -6588,7 +6967,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2024-10-08T14:45:26+00:00" + "time": "2025-01-24T15:45:36+00:00" }, { "name": "mockery/mockery", @@ -6675,16 +7054,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -6723,7 +7102,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" }, "funding": [ { @@ -6731,27 +7110,27 @@ "type": "tidelift" } ], - "time": "2024-06-12T14:39:25+00:00" + "time": "2024-11-08T17:47:46+00:00" }, { "name": "nunomaduro/collision", - "version": "v8.4.0", + "version": "v8.5.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "e7d1aa8ed753f63fa816932bbc89678238843b4a" + "reference": "f5c101b929c958e849a633283adff296ed5f38f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/e7d1aa8ed753f63fa816932bbc89678238843b4a", - "reference": "e7d1aa8ed753f63fa816932bbc89678238843b4a", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/f5c101b929c958e849a633283adff296ed5f38f5", + "reference": "f5c101b929c958e849a633283adff296ed5f38f5", "shasum": "" }, "require": { - "filp/whoops": "^2.15.4", - "nunomaduro/termwind": "^2.0.1", + "filp/whoops": "^2.16.0", + "nunomaduro/termwind": "^2.1.0", "php": "^8.2.0", - "symfony/console": "^7.1.3" + "symfony/console": "^7.1.5" }, "conflict": { "laravel/framework": "<11.0.0 || >=12.0.0", @@ -6759,14 +7138,14 @@ }, "require-dev": { "larastan/larastan": "^2.9.8", - "laravel/framework": "^11.19.0", - "laravel/pint": "^1.17.1", - "laravel/sail": "^1.31.0", - "laravel/sanctum": "^4.0.2", - "laravel/tinker": "^2.9.0", - "orchestra/testbench-core": "^9.2.3", - "pestphp/pest": "^2.35.0 || ^3.0.0", - "sebastian/environment": "^6.1.0 || ^7.0.0" + "laravel/framework": "^11.28.0", + "laravel/pint": "^1.18.1", + "laravel/sail": "^1.36.0", + "laravel/sanctum": "^4.0.3", + "laravel/tinker": "^2.10.0", + "orchestra/testbench-core": "^9.5.3", + "pestphp/pest": "^2.36.0 || ^3.4.0", + "sebastian/environment": "^6.1.0 || ^7.2.0" }, "type": "library", "extra": { @@ -6828,7 +7207,7 @@ "type": "patreon" } ], - "time": "2024-08-03T15:32:23+00:00" + "time": "2024-10-15T16:06:32+00:00" }, { "name": "phar-io/manifest", @@ -7271,16 +7650,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.5.36", + "version": "10.5.45", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870" + "reference": "bd68a781d8e30348bc297449f5234b3458267ae8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870", - "reference": "aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/bd68a781d8e30348bc297449f5234b3458267ae8", + "reference": "bd68a781d8e30348bc297449f5234b3458267ae8", "shasum": "" }, "require": { @@ -7290,7 +7669,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.0", + "myclabs/deep-copy": "^1.12.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.1", @@ -7301,7 +7680,7 @@ "phpunit/php-timer": "^6.0.0", "sebastian/cli-parser": "^2.0.1", "sebastian/code-unit": "^2.0.0", - "sebastian/comparator": "^5.0.2", + "sebastian/comparator": "^5.0.3", "sebastian/diff": "^5.1.1", "sebastian/environment": "^6.1.0", "sebastian/exporter": "^5.1.2", @@ -7352,7 +7731,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.36" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.45" }, "funding": [ { @@ -7368,7 +7747,7 @@ "type": "tidelift" } ], - "time": "2024-10-08T15:36:51+00:00" + "time": "2025-02-06T16:08:12+00:00" }, { "name": "sebastian/cli-parser", @@ -7540,16 +7919,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.2", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53" + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53", - "reference": "2d3e04c3b4c1e84a5e7382221ad8883c8fbc4f53", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", "shasum": "" }, "require": { @@ -7560,7 +7939,7 @@ "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.4" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { @@ -7605,7 +7984,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.2" + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" }, "funding": [ { @@ -7613,7 +7992,7 @@ "type": "github" } ], - "time": "2024-08-12T06:03:08+00:00" + "time": "2024-10-18T14:56:07+00:00" }, { "name": "sebastian/complexity", @@ -8288,27 +8667,27 @@ }, { "name": "spatie/backtrace", - "version": "1.6.2", + "version": "1.7.1", "source": { "type": "git", "url": "https://github.com/spatie/backtrace.git", - "reference": "1a9a145b044677ae3424693f7b06479fc8c137a9" + "reference": "0f2477c520e3729de58e061b8192f161c99f770b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/backtrace/zipball/1a9a145b044677ae3424693f7b06479fc8c137a9", - "reference": "1a9a145b044677ae3424693f7b06479fc8c137a9", + "url": "https://api.github.com/repos/spatie/backtrace/zipball/0f2477c520e3729de58e061b8192f161c99f770b", + "reference": "0f2477c520e3729de58e061b8192f161c99f770b", "shasum": "" }, "require": { - "php": "^7.3|^8.0" + "php": "^7.3 || ^8.0" }, "require-dev": { "ext-json": "*", - "laravel/serializable-closure": "^1.3", - "phpunit/phpunit": "^9.3", - "spatie/phpunit-snapshot-assertions": "^4.2", - "symfony/var-dumper": "^5.1" + "laravel/serializable-closure": "^1.3 || ^2.0", + "phpunit/phpunit": "^9.3 || ^11.4.3", + "spatie/phpunit-snapshot-assertions": "^4.2 || ^5.1.6", + "symfony/var-dumper": "^5.1 || ^6.0 || ^7.0" }, "type": "library", "autoload": { @@ -8335,7 +8714,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/backtrace/tree/1.6.2" + "source": "https://github.com/spatie/backtrace/tree/1.7.1" }, "funding": [ { @@ -8347,20 +8726,20 @@ "type": "other" } ], - "time": "2024-07-22T08:21:24+00:00" + "time": "2024-12-02T13:28:15+00:00" }, { "name": "spatie/error-solutions", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/spatie/error-solutions.git", - "reference": "ae7393122eda72eed7cc4f176d1e96ea444f2d67" + "reference": "d239a65235a1eb128dfa0a4e4c4ef032ea11b541" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/error-solutions/zipball/ae7393122eda72eed7cc4f176d1e96ea444f2d67", - "reference": "ae7393122eda72eed7cc4f176d1e96ea444f2d67", + "url": "https://api.github.com/repos/spatie/error-solutions/zipball/d239a65235a1eb128dfa0a4e4c4ef032ea11b541", + "reference": "d239a65235a1eb128dfa0a4e4c4ef032ea11b541", "shasum": "" }, "require": { @@ -8413,7 +8792,7 @@ ], "support": { "issues": "https://github.com/spatie/error-solutions/issues", - "source": "https://github.com/spatie/error-solutions/tree/1.1.1" + "source": "https://github.com/spatie/error-solutions/tree/1.1.2" }, "funding": [ { @@ -8421,20 +8800,20 @@ "type": "github" } ], - "time": "2024-07-25T11:06:04+00:00" + "time": "2024-12-11T09:51:56+00:00" }, { "name": "spatie/flare-client-php", - "version": "1.8.0", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/spatie/flare-client-php.git", - "reference": "180f8ca4c0d0d6fc51477bd8c53ce37ab5a96122" + "reference": "140a42b2c5d59ac4ecf8f5b493386a4f2eb28272" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/180f8ca4c0d0d6fc51477bd8c53ce37ab5a96122", - "reference": "180f8ca4c0d0d6fc51477bd8c53ce37ab5a96122", + "url": "https://api.github.com/repos/spatie/flare-client-php/zipball/140a42b2c5d59ac4ecf8f5b493386a4f2eb28272", + "reference": "140a42b2c5d59ac4ecf8f5b493386a4f2eb28272", "shasum": "" }, "require": { @@ -8482,7 +8861,7 @@ ], "support": { "issues": "https://github.com/spatie/flare-client-php/issues", - "source": "https://github.com/spatie/flare-client-php/tree/1.8.0" + "source": "https://github.com/spatie/flare-client-php/tree/1.10.0" }, "funding": [ { @@ -8490,7 +8869,7 @@ "type": "github" } ], - "time": "2024-08-01T08:27:26+00:00" + "time": "2024-12-02T14:30:06+00:00" }, { "name": "spatie/ignition", @@ -8577,16 +8956,16 @@ }, { "name": "spatie/laravel-ignition", - "version": "2.8.0", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "3c067b75bfb50574db8f7e2c3978c65eed71126c" + "reference": "62042df15314b829d0f26e02108f559018e2aad0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/3c067b75bfb50574db8f7e2c3978c65eed71126c", - "reference": "3c067b75bfb50574db8f7e2c3978c65eed71126c", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/62042df15314b829d0f26e02108f559018e2aad0", + "reference": "62042df15314b829d0f26e02108f559018e2aad0", "shasum": "" }, "require": { @@ -8617,12 +8996,12 @@ "type": "library", "extra": { "laravel": { - "providers": [ - "Spatie\\LaravelIgnition\\IgnitionServiceProvider" - ], "aliases": { "Flare": "Spatie\\LaravelIgnition\\Facades\\Flare" - } + }, + "providers": [ + "Spatie\\LaravelIgnition\\IgnitionServiceProvider" + ] } }, "autoload": { @@ -8664,24 +9043,25 @@ "type": "github" } ], - "time": "2024-06-12T15:01:18+00:00" + "time": "2024-12-02T08:43:31+00:00" }, { "name": "symfony/yaml", - "version": "v7.1.5", + "version": "v7.2.3", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "4e561c316e135e053bd758bf3b3eb291d9919de4" + "reference": "ac238f173df0c9c1120f862d0f599e17535a87ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/4e561c316e135e053bd758bf3b3eb291d9919de4", - "reference": "4e561c316e135e053bd758bf3b3eb291d9919de4", + "url": "https://api.github.com/repos/symfony/yaml/zipball/ac238f173df0c9c1120f862d0f599e17535a87ec", + "reference": "ac238f173df0c9c1120f862d0f599e17535a87ec", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -8719,7 +9099,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.1.5" + "source": "https://github.com/symfony/yaml/tree/v7.2.3" }, "funding": [ { @@ -8735,7 +9115,7 @@ "type": "tidelift" } ], - "time": "2024-09-17T12:49:58+00:00" + "time": "2025-01-07T12:55:42+00:00" }, { "name": "theseer/tokenizer", diff --git a/app/api/config/app.php b/app/api/config/app.php index e87499f0..e7d68595 100644 --- a/app/api/config/app.php +++ b/app/api/config/app.php @@ -171,7 +171,11 @@ // Load module service providers before the RouteServiceProvider App\Applications\User\Providers\UserServiceProvider::class, + App\Applications\LeaveType\Providers\LeaveTypeServiceProvider::class, + App\Applications\LeaveRequest\Providers\LeaveRequestServiceProvider::class, App\Applications\Navigation\Providers\NavigationServiceProvider::class, + App\Applications\NationalHoliday\Providers\NationalHolidayServiceProvider::class, + App\Applications\Document\Providers\DocumentServiceProvider::class, // Load the RouteServiceProvider last because it has the any route redirect App\Providers\RouteServiceProvider::class, diff --git a/app/api/database/migrations/2014_10_12_000000_create_documents_table.php b/app/api/database/migrations/2014_10_12_000000_create_documents_table.php new file mode 100644 index 00000000..cb59c762 --- /dev/null +++ b/app/api/database/migrations/2014_10_12_000000_create_documents_table.php @@ -0,0 +1,32 @@ +id(); + $table->integer('leave_request_id'); + $table->integer('user_id'); + $table->string('file_name'); + $table->string('file_path'); + $table->softDeletes(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('documents'); + } +}; diff --git a/app/api/database/migrations/2014_10_12_000000_create_leave_types_table.php b/app/api/database/migrations/2014_10_12_000000_create_leave_types_table.php new file mode 100644 index 00000000..f6a5c0b5 --- /dev/null +++ b/app/api/database/migrations/2014_10_12_000000_create_leave_types_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('name'); + $table->string('slug'); + $table->boolean('is_paid')->default(0); + $table->string('color')->default('#FFFFFF'); + $table->softDeletes(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('leave_types'); + } +}; diff --git a/app/api/database/migrations/2014_10_12_000000_create_national_holidays_table.php b/app/api/database/migrations/2014_10_12_000000_create_national_holidays_table.php new file mode 100644 index 00000000..b9675148 --- /dev/null +++ b/app/api/database/migrations/2014_10_12_000000_create_national_holidays_table.php @@ -0,0 +1,25 @@ +id(); + $table->date('date'); + $table->string('country'); // 'Macedonia' or 'Bulgaria' + $table->year('year'); + $table->integer('leave_type_id')->default(5); + $table->timestamps(); + + }); + } + + public function down(): void + { + Schema::dropIfExists('national_holidays'); + } +}; diff --git a/app/api/database/migrations/2014_10_12_000000_create_user_days_table.php b/app/api/database/migrations/2014_10_12_000000_create_user_days_table.php new file mode 100644 index 00000000..48353602 --- /dev/null +++ b/app/api/database/migrations/2014_10_12_000000_create_user_days_table.php @@ -0,0 +1,33 @@ +id(); + $table->string('user_id'); + $table->string('days_to_use')->nullable(); + $table->string('days_used')->nullable(); + $table->string('year')->unique(); + $table->boolean('if_filled')->nullable(); + $table->softDeletes(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('user_days'); + } +}; diff --git a/app/api/database/migrations/2014_10_12_000000_create_users_table.php b/app/api/database/migrations/2014_10_12_000000_create_users_table.php index 3bf74538..ccb4d853 100644 --- a/app/api/database/migrations/2014_10_12_000000_create_users_table.php +++ b/app/api/database/migrations/2014_10_12_000000_create_users_table.php @@ -19,7 +19,11 @@ public function up(): void $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->string('activation_code')->nullable(); + $table->integer('country')->nullable()->default(1); $table->boolean('is_disabled')->nullable()->default(0); + $table->integer('paid_leaves_max')->nullable()->default(20); + $table->integer('paid_leaves_left')->nullable()->default(20); + $table->boolean('is_office_based')->nullable()->default(0); $table->rememberToken(); $table->softDeletes(); $table->timestamps(); diff --git a/app/api/database/migrations/2014_10_13_000000_create_leave_requests_table.php b/app/api/database/migrations/2014_10_13_000000_create_leave_requests_table.php new file mode 100644 index 00000000..d66dd174 --- /dev/null +++ b/app/api/database/migrations/2014_10_13_000000_create_leave_requests_table.php @@ -0,0 +1,55 @@ +id(); + $table->unsignedBigInteger('user_id'); + $table->unsignedBigInteger('leave_type_id'); + $table->unsignedBigInteger('request_to'); + $table->string('start_date'); + $table->string('end_date')->nullable(); + $table->integer('status')->nullable()->default(0); + $table->string('reason')->nullable()->default("-"); + $table->integer('confirmed_by')->nullable(); + $table->integer('is_confirmed')->nullable(); + $table->softDeletes(); + $table->timestamps(); + + $table->foreign('user_id') + ->references('id')->on('users') + ->onDelete('cascade'); + + $table->foreign('leave_type_id') + ->references('id')->on('leave_types') + ->onDelete('cascade'); + + $table->foreign('request_to') + ->references('id')->on('users') + ->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('leave_requests', function (Blueprint $table) { + $table->dropForeign(['user_id']); + $table->dropForeign(['leave_type_id']); + $table->dropForeign(['request_to']); + }); + + Schema::dropIfExists('leave_requests'); + } +}; diff --git a/app/api/database/seeders/DatabaseSeeder.php b/app/api/database/seeders/DatabaseSeeder.php index 74eee7f5..981252b0 100644 --- a/app/api/database/seeders/DatabaseSeeder.php +++ b/app/api/database/seeders/DatabaseSeeder.php @@ -2,7 +2,6 @@ namespace Database\Seeders; -// use Illuminate\Database\Console\Seeds\WithoutModelEvents; use App\Applications\User\Model\User; use App\Constants\UserPermissions; use App\Constants\UserRoles; @@ -10,7 +9,8 @@ use Illuminate\Support\Facades\Hash; use Spatie\Permission\Models\Permission; use Spatie\Permission\Models\Role; -use Faker\Factory as Faker; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Str; class DatabaseSeeder extends Seeder { @@ -19,6 +19,7 @@ class DatabaseSeeder extends Seeder */ public function run(): void { + // Create Users $admin = User::create([ 'first_name' => 'Admin', 'last_name' => 'Userot', @@ -26,54 +27,92 @@ public function run(): void 'password' => Hash::make('password') ]); - $editor = User::create([ - 'first_name' => 'Editor', + $manager = User::create([ + 'first_name' => 'Manager', 'last_name' => 'Userot', - 'email' => 'editor@example.com', + 'email' => 'manager@example.com', + 'password' => Hash::make('password') + ]); + + $developer = User::create([ + 'first_name' => 'Developer', + 'last_name' => 'Userot', + 'email' => 'developer@example.com', 'password' => Hash::make('password') ]); $collaborator = User::create([ 'first_name' => 'Collaborator', - 'last_name' => 'Userot', + 'last_name' => 'Macedonia', 'email' => 'collaborator@example.com', 'password' => Hash::make('password') ]); - // Create permissions - Permission::create(['name' => UserPermissions::READ_USERS]); - Permission::create(['name' => UserPermissions::WRITE_USERS]); - Permission::create(['name' => UserPermissions::DELETE_USERS]); - - // Create three roles and assign created permissions - $roleAdmin = Role::create(['name' => UserRoles::ADMIN])->givePermissionTo(Permission::all()); - $roleEditor = Role::create(['name' => UserRoles::EDITOR])->givePermissionTo([UserPermissions::READ_USERS, UserPermissions::WRITE_USERS]); - $roleCollaborator = Role::create(['name' => UserRoles::COLLABORATOR])->givePermissionTo(UserPermissions::READ_USERS); + // Create Permissions + $permissions = [ + UserPermissions::READ_USERS, + UserPermissions::WRITE_USERS, + UserPermissions::DELETE_USERS, + UserPermissions::WRITE_PROFILE, + UserPermissions::READ_REQUESTS, + UserPermissions::APPROVE_REQUESTS, + UserPermissions::WRITE_REQUESTS, + UserPermissions::DELETE_REQUESTS, + UserPermissions::DASHBOARD_VIEW, + ]; - $roles = [$roleAdmin->id, $roleEditor->id, $roleCollaborator->id]; + foreach ($permissions as $permission) { + Permission::create(['name' => $permission]); + } - // Adding permissions via a role - $admin->assignRole(UserRoles::ADMIN); - $editor->assignRole(UserRoles::EDITOR); - $collaborator->assignRole(UserRoles::COLLABORATOR); + // Create Roles and Assign Permissions + $roleAdmin = Role::create(['name' => UserRoles::ADMIN]) + ->givePermissionTo(Permission::all()); - $faker = Faker::create(); + $roleManager = Role::create(['name' => UserRoles::MANAGER]) + ->givePermissionTo([ + UserPermissions::DASHBOARD_VIEW, + UserPermissions::READ_USERS, + UserPermissions::WRITE_USERS, + UserPermissions::WRITE_PROFILE, + UserPermissions::READ_REQUESTS, + UserPermissions::WRITE_REQUESTS, + UserPermissions::DELETE_REQUESTS, + UserPermissions::APPROVE_REQUESTS, + ]); - // Common password for all users, hashed - $password = Hash::make('password'); + $roleDeveloper = Role::create(['name' => UserRoles::DEVELOPER]) + ->givePermissionTo([ + UserPermissions::DASHBOARD_VIEW, + UserPermissions::READ_REQUESTS, + UserPermissions::WRITE_PROFILE, + UserPermissions::WRITE_REQUESTS, + UserPermissions::DELETE_REQUESTS, + ]); - for ($i = 0; $i < 100; $i++) { - // Create a new user with random data - $user = User::create([ - 'first_name' => $faker->name, - 'last_name' => $faker->name, - 'email' => $faker->unique()->safeEmail, - 'password' => $password, - // Other fields like 'first_name', 'last_name', etc., can be added here + $roleCollaborator = Role::create(['name' => UserRoles::COLLABORATOR]) + ->givePermissionTo([ + UserPermissions::DASHBOARD_VIEW, + UserPermissions::READ_USERS, + UserPermissions::READ_REQUESTS, ]); - // Assign a random role to the user - $user->roles()->attach($faker->randomElement($roles)); - } + // Assign Roles to Users + $admin->assignRole(UserRoles::ADMIN); + $manager->assignRole(UserRoles::MANAGER); + $developer->assignRole(UserRoles::DEVELOPER); + $collaborator->assignRole(UserRoles::COLLABORATOR); + + // Seed Leave Types + $leaveTypes = [ + ['name' => 'Sick (unpaid)', 'slug' => Str::slug('Sick unpaid'), 'is_paid' => false, 'color' => '#FF6B6B'], + ['name' => 'Sick (paid)', 'slug' => Str::slug('Sick paid'), 'is_paid' => true, 'color' => '#FFA726'], + ['name' => 'Vacation Day (paid)', 'slug' => Str::slug('Vacation Day paid'), 'is_paid' => true, 'color' => '#4CAF50'], + ['name' => 'Vacation Day (unpaid)', 'slug' => Str::slug('Vacation Day unpaid'), 'is_paid' => false, 'color' => '#90A4AE'], + ['name' => 'National Holiday (paid)', 'slug' => Str::slug('National Holiday paid'), 'is_paid' => true, 'color' => '#6326F2'], + ['name' => 'Work From Home', 'slug' => Str::slug('Work From Home'), 'is_paid' => true, 'color' => '#ECCBCB'], + ]; + + DB::table('leave_types')->insert($leaveTypes); } } diff --git a/app/api/public/BG_template_paid.pdf b/app/api/public/BG_template_paid.pdf new file mode 100644 index 00000000..46761399 Binary files /dev/null and b/app/api/public/BG_template_paid.pdf differ diff --git a/app/api/public/BG_template_unpaid.pdf b/app/api/public/BG_template_unpaid.pdf new file mode 100644 index 00000000..59288410 Binary files /dev/null and b/app/api/public/BG_template_unpaid.pdf differ diff --git a/app/api/public/MK_template_paid.pdf b/app/api/public/MK_template_paid.pdf new file mode 100644 index 00000000..5a7df042 Binary files /dev/null and b/app/api/public/MK_template_paid.pdf differ diff --git a/app/api/public/MK_template_unpaid.pdf b/app/api/public/MK_template_unpaid.pdf new file mode 100644 index 00000000..f7682fac Binary files /dev/null and b/app/api/public/MK_template_unpaid.pdf differ diff --git a/app/api/resources/views/emails/leave_request_cancelation.blade.php b/app/api/resources/views/emails/leave_request_cancelation.blade.php new file mode 100644 index 00000000..58f1b4a6 --- /dev/null +++ b/app/api/resources/views/emails/leave_request_cancelation.blade.php @@ -0,0 +1,18 @@ + + + + CANCELED + + +@php + use Carbon\Carbon; + $formattedStartDate = Carbon::parse($leaveRequest->start_date)->format('F j, Y'); + $formattedEndDate = $leaveRequest->end_date ? Carbon::parse($leaveRequest->end_date)->format('F j, Y') : null; +@endphp + @if($leaveRequest->end_date == null) +

{{ $leaveRequest->user->first_name }} {{ $leaveRequest->user->last_name }} is on {{ $leaveRequest->leaveType->name }} leave on {{ $formattedStartDate }}

+ @else +

{{ $leaveRequest->user->first_name }} {{ $leaveRequest->user->last_name }} is on {{ $leaveRequest->leaveType->name }} leave from {{ $formattedStartDate }} to {{ $formattedEndDate }}

+ @endif + + \ No newline at end of file diff --git a/app/api/resources/views/emails/leave_request_confirmation.blade.php b/app/api/resources/views/emails/leave_request_confirmation.blade.php new file mode 100644 index 00000000..61af12bf --- /dev/null +++ b/app/api/resources/views/emails/leave_request_confirmation.blade.php @@ -0,0 +1,21 @@ + + + + APPROVED + + +@php + use Carbon\Carbon; + $formattedStartDate = Carbon::parse($leaveRequest->start_date)->format('F j, Y'); + $formattedEndDate = $leaveRequest->end_date ? Carbon::parse($leaveRequest->end_date)->format('F j, Y') : null; +@endphp + @if($leaveRequest->end_date == null) +

{{ $leaveRequest->user->first_name }} {{ $leaveRequest->user->last_name }} is on {{ $leaveRequest->leaveType->name }} leave on {{ $formattedStartDate }}

+ @else +

{{ $leaveRequest->user->first_name }} {{ $leaveRequest->user->last_name }} is on {{ $leaveRequest->leaveType->name }} leave from {{ $formattedStartDate }} to {{ $formattedEndDate }}

+ @endif + + View Leave Request + + + \ No newline at end of file diff --git a/app/api/resources/views/emails/leave_request_confirmation_pdf.blade.php b/app/api/resources/views/emails/leave_request_confirmation_pdf.blade.php new file mode 100644 index 00000000..c8b17447 --- /dev/null +++ b/app/api/resources/views/emails/leave_request_confirmation_pdf.blade.php @@ -0,0 +1,12 @@ + + + + ESOF BG + + + @php + + @endphp +

ESOF BG

+ + \ No newline at end of file diff --git a/app/api/resources/views/emails/leave_request_decline.blade.php b/app/api/resources/views/emails/leave_request_decline.blade.php new file mode 100644 index 00000000..ad8e7044 --- /dev/null +++ b/app/api/resources/views/emails/leave_request_decline.blade.php @@ -0,0 +1,12 @@ + + + + DECLINED + + +

Your Leave Request Has Been Declined

+ + Open Leave Request + + + \ No newline at end of file diff --git a/app/api/resources/views/emails/leave_request_notification.blade.php b/app/api/resources/views/emails/leave_request_notification.blade.php new file mode 100644 index 00000000..fc12b227 --- /dev/null +++ b/app/api/resources/views/emails/leave_request_notification.blade.php @@ -0,0 +1,31 @@ + + + + New Leave Request + + +@php + use Carbon\Carbon; + $formattedStartDate = Carbon::parse($leaveRequest->start_date)->format('F j, Y'); + $formattedEndDate = $leaveRequest->end_date ? Carbon::parse($leaveRequest->end_date)->format('F j, Y') : null; +@endphp +

New Leave Request Assigned to You

+

{{ $leaveRequest->user->first_name }} {{ $leaveRequest->user->last_name }}

+ @if ($leaveRequest->end_date == null) +

{{ $formattedStartDate }}

+ @else +

{{ $formattedStartDate }} to {{ $formattedEndDate }}

+ @endif + + @if ($leaveRequest->reason && $leaveRequest->reason !== null) +

+ Reason: {{ $leaveRequest->reason ? $leaveRequest->reason : "" }} +

+ @endif + +

{{ $leaveRequest->leaveType->name }}

+ + Open Leave Request Confirmation Page + + + \ No newline at end of file diff --git a/app/api/routes/Document/api.php b/app/api/routes/Document/api.php new file mode 100644 index 00000000..81a6d432 --- /dev/null +++ b/app/api/routes/Document/api.php @@ -0,0 +1,27 @@ + 'auth:sanctum' +], function () { + Route::group([ + 'prefix' => 'document', + ], function () { + Route::get('all', [DocumentController::class, 'getAll']); + Route::get('draw', [DocumentController::class, 'draw']); + Route::get('{id}', [DocumentController::class, 'get']); + }); +}); diff --git a/app/api/routes/LeaveRequest/api.php b/app/api/routes/LeaveRequest/api.php new file mode 100644 index 00000000..2240729e --- /dev/null +++ b/app/api/routes/LeaveRequest/api.php @@ -0,0 +1,37 @@ + 'auth:sanctum' +], function () { + Route::group([ + 'prefix' => 'leave_request', + ], function () { + Route::get('all', [LeaveRequestController::class, 'getAll']); + Route::get('draw', [LeaveRequestController::class, 'draw']); + Route::get('approved', [LeaveRequestController::class, 'getApproved']); + Route::get('pending', [LeaveRequestController::class, 'getPending']); + + // CRUD ROUTES + Route::get('{id}/download', [LeaveRequestController::class, 'downloadLeaveRequestPDF']); + Route::post('create', [LeaveRequestController::class, 'create']); + Route::post('{id}/approve', [LeaveRequestController::class, 'approve']); + Route::post('{id}/decline', [LeaveRequestController::class, 'decline']); + Route::get('{id}', [LeaveRequestController::class, 'get']); + Route::patch('{id}', [LeaveRequestController::class, 'update']); + Route::post('{id}/delete', [LeaveRequestController::class, 'delete']); + }); +}); diff --git a/app/api/routes/LeaveType/api.php b/app/api/routes/LeaveType/api.php new file mode 100644 index 00000000..79e90b94 --- /dev/null +++ b/app/api/routes/LeaveType/api.php @@ -0,0 +1,33 @@ + 'auth:sanctum' +], function () { + Route::group([ + 'prefix' => 'leave_type', + ], function () { + Route::get('all', [LeaveTypeController::class, 'getAll']); + Route::get('draw', [LeaveTypeController::class, 'draw']); + + // CRUD ROUTES + Route::post('create', [LeaveTypeController::class, 'create']); + Route::get('{id}', [LeaveTypeController::class, 'get']); + Route::patch('{id}', [LeaveTypeController::class, 'update']); + Route::delete('{id}', [LeaveTypeController::class, 'delete']); + Route::post('{id}/delete', [LeaveTypeController::class, 'delete']); + }); +}); diff --git a/app/api/routes/NationalHoliday/api.php b/app/api/routes/NationalHoliday/api.php new file mode 100644 index 00000000..58b14afc --- /dev/null +++ b/app/api/routes/NationalHoliday/api.php @@ -0,0 +1,32 @@ + 'auth:sanctum' +], function () { + Route::group([ + 'prefix' => 'national_holiday', + ], function () { + Route::get('all', [NationalHolidayController::class, 'getAll']); + Route::get('draw', [NationalHolidayController::class, 'draw']); + + // CRUD ROUTES + Route::post('create', [NationalHolidayController::class, 'create']); + Route::get('{id}', [NationalHolidayController::class, 'get']); + Route::patch('{id}', [NationalHolidayController::class, 'update']); + Route::delete('{id}', [NationalHolidayController::class, 'delete']); + }); +}); diff --git a/app/api/routes/User/api.php b/app/api/routes/User/api.php index cf29307c..030e5f34 100644 --- a/app/api/routes/User/api.php +++ b/app/api/routes/User/api.php @@ -32,7 +32,7 @@ Route::post('create', [UserController::class, 'create']); Route::get('{id}', [UserController::class, 'get']); Route::patch('{id}', [UserController::class, 'update']); - Route::delete('{id}', [UserController::class, 'delete']); + Route::post('{id}/delete', [UserController::class, 'delete']); // User avatars Route::post('avatar/{id}', [UserController::class, 'uploadAvatar']); diff --git a/app/api/storage/fonts/DejaVuSans.ttf b/app/api/storage/fonts/DejaVuSans.ttf new file mode 100644 index 00000000..e5f7eecc Binary files /dev/null and b/app/api/storage/fonts/DejaVuSans.ttf differ diff --git a/app/client/admin/.gitignore b/app/client/admin/.gitignore index 30bc1627..7af7f047 100644 --- a/app/client/admin/.gitignore +++ b/app/client/admin/.gitignore @@ -1 +1,2 @@ -/node_modules \ No newline at end of file +/node_modules +.env \ No newline at end of file diff --git a/app/client/admin/package-lock.json b/app/client/admin/package-lock.json index 2dd08fa7..be34925d 100644 --- a/app/client/admin/package-lock.json +++ b/app/client/admin/package-lock.json @@ -10,9 +10,14 @@ "./starter-core/icons" ], "dependencies": { + "@fullcalendar/core": "^6.1.15", + "@fullcalendar/daygrid": "^6.1.15", + "@fullcalendar/vue3": "^6.1.15", + "@popperjs/core": "^2.11.8", "@starter-core/dash-ui": "1.0.2", "@starter-core/icons": "^1.0.1", "@tanstack/vue-query": "^5.24.1", + "@vueuse/core": "^12.5.0", "@websanova/vue-auth": "^4.2.1", "axios": "^1.6.7", "eslint-config-prettier": "^9.1.0", @@ -248,9 +253,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "engines": { "node": ">=6.9.0" } @@ -264,11 +269,11 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", - "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", "dependencies": { - "@babel/types": "^7.25.6" + "@babel/types": "^7.26.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -278,13 +283,12 @@ } }, "node_modules/@babel/types": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", - "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -874,6 +878,44 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fullcalendar/core": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.15.tgz", + "integrity": "sha512-BuX7o6ALpLb84cMw1FCB9/cSgF4JbVO894cjJZ6kP74jzbUZNjtwffwRdA+Id8rrLjT30d/7TrkW90k4zbXB5Q==", + "license": "MIT", + "dependencies": { + "preact": "~10.12.1" + } + }, + "node_modules/@fullcalendar/core/node_modules/preact": { + "version": "10.12.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz", + "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/@fullcalendar/daygrid": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.15.tgz", + "integrity": "sha512-j8tL0HhfiVsdtOCLfzK2J0RtSkiad3BYYemwQKq512cx6btz6ZZ2RNc/hVnIxluuWFyvx5sXZwoeTJsFSFTEFA==", + "license": "MIT", + "peerDependencies": { + "@fullcalendar/core": "~6.1.15" + } + }, + "node_modules/@fullcalendar/vue3": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/@fullcalendar/vue3/-/vue3-6.1.15.tgz", + "integrity": "sha512-ctfTICGrNEIj7gmLHQcUYe0WzDTSW5Vd9hyOnVChxPU75AZU9WqdDMkHwJYnfNxNhT6QQuiMHq/qsRRd5zQwOw==", + "license": "MIT", + "peerDependencies": { + "@fullcalendar/core": "~6.1.15", + "vue": "^3.0.11" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -1134,7 +1176,7 @@ "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "peer": true, + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -1556,8 +1598,7 @@ "node_modules/@types/web-bluetooth": { "version": "0.0.20", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", - "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==", - "dev": true + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==" }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.5.0", @@ -1812,49 +1853,49 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.4.tgz", - "integrity": "sha512-oNwn+BAt3n9dK9uAYvI+XGlutwuTq/wfj4xCBaZCqwwVIGtD7D6ViihEbyYZrDHIHTDE3Q6oL3/hqmAyFEy9DQ==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", + "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", "dependencies": { "@babel/parser": "^7.25.3", - "@vue/shared": "3.5.4", + "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.4.tgz", - "integrity": "sha512-yP9RRs4BDLOLfldn6ah+AGCNovGjMbL9uHvhDHf5wan4dAHLnFGOkqtfE7PPe4HTXIqE7l/NILdYw53bo1C8jw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", + "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", "dependencies": { - "@vue/compiler-core": "3.5.4", - "@vue/shared": "3.5.4" + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.4.tgz", - "integrity": "sha512-P+yiPhL+NYH7m0ZgCq7AQR2q7OIE+mpAEgtkqEeH9oHSdIRvUO+4X6MPvblJIWcoe4YC5a2Gdf/RsoyP8FFiPQ==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", + "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", "dependencies": { "@babel/parser": "^7.25.3", - "@vue/compiler-core": "3.5.4", - "@vue/compiler-dom": "3.5.4", - "@vue/compiler-ssr": "3.5.4", - "@vue/shared": "3.5.4", + "@vue/compiler-core": "3.5.13", + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13", "estree-walker": "^2.0.2", "magic-string": "^0.30.11", - "postcss": "^8.4.44", + "postcss": "^8.4.48", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.4.tgz", - "integrity": "sha512-acESdTXsxPnYr2C4Blv0ggx5zIFMgOzZmYU2UgvIff9POdRGbRNBHRyzHAnizcItvpgerSKQbllUc9USp3V7eg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", + "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", "dependencies": { - "@vue/compiler-dom": "3.5.4", - "@vue/shared": "3.5.4" + "@vue/compiler-dom": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/devtools-api": { @@ -2189,49 +2230,49 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.4.tgz", - "integrity": "sha512-HKKbEuP7tYSGCq4e4nK6ZW6l5hyG66OUetefBp4budUyjvAYsnQDf+bgFzg2RAgnH0CInyqXwD9y47jwJEHrQw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", + "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", "dependencies": { - "@vue/shared": "3.5.4" + "@vue/shared": "3.5.13" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.4.tgz", - "integrity": "sha512-f3ek2sTA0AFu0n+w+kCtz567Euqqa3eHewvo4klwS7mWfSj/A+UmYTwsnUFo35KeyAFY60JgrCGvEBsu1n/3LA==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", + "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", "dependencies": { - "@vue/reactivity": "3.5.4", - "@vue/shared": "3.5.4" + "@vue/reactivity": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.4.tgz", - "integrity": "sha512-ofyc0w6rbD5KtjhP1i9hGOKdxGpvmuB1jprP7Djlj0X7R5J/oLwuNuE98GJ8WW31Hu2VxQHtk/LYTAlW8xrJdw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", + "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", "dependencies": { - "@vue/reactivity": "3.5.4", - "@vue/runtime-core": "3.5.4", - "@vue/shared": "3.5.4", + "@vue/reactivity": "3.5.13", + "@vue/runtime-core": "3.5.13", + "@vue/shared": "3.5.13", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.4.tgz", - "integrity": "sha512-FbjV6DJLgKRetMYFBA1UXCroCiED/Ckr53/ba9wivyd7D/Xw9fpo0T6zXzCnxQwyvkyrL7y6plgYhWhNjGxY5g==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz", + "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", "dependencies": { - "@vue/compiler-ssr": "3.5.4", - "@vue/shared": "3.5.4" + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13" }, "peerDependencies": { - "vue": "3.5.4" + "vue": "3.5.13" } }, "node_modules/@vue/shared": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.4.tgz", - "integrity": "sha512-L2MCDD8l7yC62Te5UUyPVpmexhL9ipVnYRw9CsWfm/BGRL5FwDX4a25bcJ/OJSD3+Hx+k/a8LDKcG2AFdJV3BA==" + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", + "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==" }, "node_modules/@vue/tsconfig": { "version": "0.5.1", @@ -2240,44 +2281,28 @@ "dev": true }, "node_modules/@vueuse/core": { - "version": "10.7.2", - "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.7.2.tgz", - "integrity": "sha512-AOyAL2rK0By62Hm+iqQn6Rbu8bfmbgaIMXcE3TSr7BdQ42wnSFlwIdPjInO62onYsEMK/yDMU8C6oGfDAtZ2qQ==", - "dev": true, + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.5.0.tgz", + "integrity": "sha512-GVyH1iYqNANwcahAx8JBm6awaNgvR/SwZ1fjr10b8l1HIgDp82ngNbfzJUgOgWEoxjL+URAggnlilAEXwCOZtg==", "dependencies": { "@types/web-bluetooth": "^0.0.20", - "@vueuse/metadata": "10.7.2", - "@vueuse/shared": "10.7.2", - "vue-demi": ">=0.14.6" + "@vueuse/metadata": "12.5.0", + "@vueuse/shared": "12.5.0", + "vue": "^3.5.13" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, - "node_modules/@vueuse/core/node_modules/vue-demi": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", - "integrity": "sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" + "node_modules/@vueuse/core/node_modules/@vueuse/shared": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.5.0.tgz", + "integrity": "sha512-vMpcL1lStUU6O+kdj6YdHDixh0odjPAUM15uJ9f7MY781jcYkIwFA4iv2EfoIPO6vBmvutI1HxxAwmf0cx5ISQ==", + "dependencies": { + "vue": "^3.5.13" }, "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/@vueuse/integrations": { @@ -2346,6 +2371,30 @@ } } }, + "node_modules/@vueuse/integrations/node_modules/@vueuse/core": { + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.7.2.tgz", + "integrity": "sha512-AOyAL2rK0By62Hm+iqQn6Rbu8bfmbgaIMXcE3TSr7BdQ42wnSFlwIdPjInO62onYsEMK/yDMU8C6oGfDAtZ2qQ==", + "dev": true, + "dependencies": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "10.7.2", + "@vueuse/shared": "10.7.2", + "vue-demi": ">=0.14.6" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/integrations/node_modules/@vueuse/metadata": { + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.7.2.tgz", + "integrity": "sha512-kCWPb4J2KGrwLtn1eJwaJD742u1k5h6v/St5wFe8Quih90+k2a0JP8BS4Zp34XUuJqS2AxFYMb1wjUL8HfhWsQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@vueuse/integrations/node_modules/vue-demi": { "version": "0.14.7", "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.7.tgz", @@ -2373,10 +2422,9 @@ } }, "node_modules/@vueuse/metadata": { - "version": "10.7.2", - "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.7.2.tgz", - "integrity": "sha512-kCWPb4J2KGrwLtn1eJwaJD742u1k5h6v/St5wFe8Quih90+k2a0JP8BS4Zp34XUuJqS2AxFYMb1wjUL8HfhWsQ==", - "dev": true, + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.5.0.tgz", + "integrity": "sha512-Ui7Lo2a7AxrMAXRF+fAp9QsXuwTeeZ8fIB9wsLHqzq9MQk+2gMYE2IGJW48VMJ8ecvCB3z3GsGLKLbSasQ5Qlg==", "funding": { "url": "https://github.com/sponsors/antfu" } @@ -5175,9 +5223,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } @@ -5344,9 +5392,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", @@ -5698,9 +5746,9 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -5786,9 +5834,9 @@ } }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "funding": [ { "type": "opencollective", @@ -5804,8 +5852,8 @@ } ], "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -7227,14 +7275,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -7696,16 +7736,104 @@ "@vue/devtools-kit": "^7.0.15" } }, + "node_modules/vitepress/node_modules/@vueuse/core": { + "version": "10.11.1", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.1.tgz", + "integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==", + "dev": true, + "dependencies": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "10.11.1", + "@vueuse/shared": "10.11.1", + "vue-demi": ">=0.14.8" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/vitepress/node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "dev": true, + "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/vitepress/node_modules/@vueuse/metadata": { + "version": "10.11.1", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.1.tgz", + "integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/vitepress/node_modules/@vueuse/shared": { + "version": "10.11.1", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.1.tgz", + "integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==", + "dev": true, + "dependencies": { + "vue-demi": ">=0.14.8" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/vitepress/node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "dev": true, + "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": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.4.tgz", - "integrity": "sha512-3yAj2gkmiY+i7+22A1PWM+kjOVXjU74UPINcTiN7grIVPyFFI0lpGwHlV/4xydDmobaBn7/xmi+YG8HeSlCTcg==", - "dependencies": { - "@vue/compiler-dom": "3.5.4", - "@vue/compiler-sfc": "3.5.4", - "@vue/runtime-dom": "3.5.4", - "@vue/server-renderer": "3.5.4", - "@vue/shared": "3.5.4" + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", + "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", + "dependencies": { + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-sfc": "3.5.13", + "@vue/runtime-dom": "3.5.13", + "@vue/server-renderer": "3.5.13", + "@vue/shared": "3.5.13" }, "peerDependencies": { "typescript": "*" diff --git a/app/client/admin/package.json b/app/client/admin/package.json index 5abaee59..ba6ed477 100644 --- a/app/client/admin/package.json +++ b/app/client/admin/package.json @@ -15,9 +15,14 @@ "stylelint:fix": "stylelint \"./src/**/*.scss\" --fix" }, "dependencies": { + "@fullcalendar/core": "^6.1.15", + "@fullcalendar/daygrid": "^6.1.15", + "@fullcalendar/vue3": "^6.1.15", + "@popperjs/core": "^2.11.8", "@starter-core/dash-ui": "1.0.2", "@starter-core/icons": "^1.0.1", "@tanstack/vue-query": "^5.24.1", + "@vueuse/core": "^12.5.0", "@websanova/vue-auth": "^4.2.1", "axios": "^1.6.7", "eslint-config-prettier": "^9.1.0", diff --git a/app/client/admin/src/components/AdminLayout/AdminLayout.vue b/app/client/admin/src/components/AdminLayout/AdminLayout.vue index 30120071..919d108b 100644 --- a/app/client/admin/src/components/AdminLayout/AdminLayout.vue +++ b/app/client/admin/src/components/AdminLayout/AdminLayout.vue @@ -19,9 +19,6 @@ const [block, element] = useBEMBuilder( "admin-layout", - ref({ - "aside-minimized": isSidebarMinimized, - }), ); provide(layoutConfigKey, layoutConfig); @@ -40,7 +37,6 @@ 'has-subheader': layoutConfig.hasSubHeader, 'fixed-subheader': layoutConfig.hasSubHeader && layoutConfig.hasSubHeaderFixed, - 'aside-minimized': isSidebarMinimized, }), ).value " diff --git a/app/client/admin/src/components/AdminSidebar/AdminSidebar.vue b/app/client/admin/src/components/AdminSidebar/AdminSidebar.vue index aa83b780..37507af2 100644 --- a/app/client/admin/src/components/AdminSidebar/AdminSidebar.vue +++ b/app/client/admin/src/components/AdminSidebar/AdminSidebar.vue @@ -43,10 +43,6 @@
diff --git a/app/client/admin/src/components/AsideBrand/AsideBrand.scss b/app/client/admin/src/components/AsideBrand/AsideBrand.scss index 4d9e538f..ff669b2d 100644 --- a/app/client/admin/src/components/AsideBrand/AsideBrand.scss +++ b/app/client/admin/src/components/AsideBrand/AsideBrand.scss @@ -14,6 +14,11 @@ max-width: 13rem; max-height: 3rem; } + + a { + font-size: 26px; + font-weight: bold; + } } .kt-aside__brand-tools { diff --git a/app/client/admin/src/components/AsideBrand/AsideBrand.vue b/app/client/admin/src/components/AsideBrand/AsideBrand.vue index b0699112..98d2ca00 100644 --- a/app/client/admin/src/components/AsideBrand/AsideBrand.vue +++ b/app/client/admin/src/components/AsideBrand/AsideBrand.vue @@ -22,27 +22,9 @@
-
- - -
+
diff --git a/app/client/admin/src/components/AuthBase/AuthBase.scss b/app/client/admin/src/components/AuthBase/AuthBase.scss index fe801935..a2c81bb3 100644 --- a/app/client/admin/src/components/AuthBase/AuthBase.scss +++ b/app/client/admin/src/components/AuthBase/AuthBase.scss @@ -18,7 +18,7 @@ &__logo { text-align: center; - margin: 0 auto 4rem; + margin: 10% auto 0 auto ; img { max-width: 100%; diff --git a/app/client/admin/src/components/AuthBase/AuthBase.vue b/app/client/admin/src/components/AuthBase/AuthBase.vue index 1ce9453b..344db7b2 100644 --- a/app/client/admin/src/components/AuthBase/AuthBase.vue +++ b/app/client/admin/src/components/AuthBase/AuthBase.vue @@ -8,16 +8,10 @@
-
diff --git a/app/client/admin/src/components/ConfirmDialog/ConfirmDialog.scss b/app/client/admin/src/components/ConfirmDialog/ConfirmDialog.scss new file mode 100644 index 00000000..958f0527 --- /dev/null +++ b/app/client/admin/src/components/ConfirmDialog/ConfirmDialog.scss @@ -0,0 +1,87 @@ +.confirm-modal-backdrop { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); // Darker overlay + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + animation: fadeIn 0.3s ease-in-out; + } + + .confirm-modal { + background: white; + padding: 20px; + border-radius: 10px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); + width: 350px; + text-align: center; + animation: scaleIn 0.3s ease-in-out; + } + + .confirm-modal p { + font-size: 16px; + font-weight: 500; + margin-bottom: 15px; + } + + .confirm-modal-actions { + display: flex; + justify-content: center; + gap: 10px; + margin-top: 15px; + } + + // Button styles + button { + padding: 10px 16px; + border: none; + cursor: pointer; + border-radius: 5px; + font-size: 14px; + font-weight: bold; + transition: all 0.2s ease-in-out; + } + + .confirm-confirm { + background: #d9534f; // Bootstrap Danger Red + color: white; + + &:hover { + background: #c9302c; + } + } + + .confirm-cancel { + background: #6c757d; // Bootstrap Gray + color: white; + + &:hover { + background: #5a6268; + } + } + + // Animations + @keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + + @keyframes scaleIn { + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } + } + \ No newline at end of file diff --git a/app/client/admin/src/components/ConfirmDialog/ConfirmDialog.vue b/app/client/admin/src/components/ConfirmDialog/ConfirmDialog.vue new file mode 100644 index 00000000..58aae9dc --- /dev/null +++ b/app/client/admin/src/components/ConfirmDialog/ConfirmDialog.vue @@ -0,0 +1,23 @@ + + + diff --git a/app/client/admin/src/components/ConfirmDialog/index.ts b/app/client/admin/src/components/ConfirmDialog/index.ts new file mode 100644 index 00000000..30fcd69d --- /dev/null +++ b/app/client/admin/src/components/ConfirmDialog/index.ts @@ -0,0 +1,2 @@ + +export {default as ConfirmDialog} from "./ConfirmDialog.vue"; \ No newline at end of file diff --git a/app/client/admin/src/components/HeaderUserBar/HeaderUserBar.vue b/app/client/admin/src/components/HeaderUserBar/HeaderUserBar.vue index e4013f97..49f04a2a 100644 --- a/app/client/admin/src/components/HeaderUserBar/HeaderUserBar.vue +++ b/app/client/admin/src/components/HeaderUserBar/HeaderUserBar.vue @@ -47,7 +47,7 @@ {{ user.first_name }} - avatar + avatar {{ userFirstLetter }} diff --git a/app/client/admin/src/components/PageWrapper/PageWrapper.vue b/app/client/admin/src/components/PageWrapper/PageWrapper.vue index 15e3026f..1ce36f95 100644 --- a/app/client/admin/src/components/PageWrapper/PageWrapper.vue +++ b/app/client/admin/src/components/PageWrapper/PageWrapper.vue @@ -15,9 +15,6 @@ const [block, element] = useBEMBuilder( "page-wrapper", - ref({ - "sidebar-minimized": true, - }), );