first commit

This commit is contained in:
Sampanna Rimal
2024-08-27 17:48:06 +05:45
commit 53c0140f58
10839 changed files with 1125847 additions and 0 deletions

View File

@ -0,0 +1,149 @@
<?php
namespace Modules\Leave\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Modules\Employee\Repositories\EmployeeInterface;
use Modules\Employee\Repositories\EmployeeRepository;
use Modules\Leave\Models\Leave;
use Modules\Leave\Repositories\LeaveInterface;
use Modules\Leave\Repositories\LeaveRepository;
use Modules\Leave\Repositories\LeaveTypeInterface;
use Modules\Leave\Repositories\LeaveTypeRepository;
use Yoeunes\Toastr\Facades\Toastr;
class LeaveController extends Controller
{
private $leaveRepository;
private $employeeRepository;
private $leaveTypeRepository;
public function __construct(LeaveInterface $leaveRepository, EmployeeInterface $employeeRepository, LeaveTypeInterface $leaveTypeRepository)
{
$this->leaveRepository = $leaveRepository;
$this->employeeRepository = $employeeRepository;
$this->leaveTypeRepository = $leaveTypeRepository;
}
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$filters = $request->all();
$data['leaves'] = $this->leaveRepository->findAll($filters);
$data['employeeList'] = $this->employeeRepository->pluck();
$data['status'] = Leave::PROGRESS_STATUS;
return view('leave::leave.index', $data);
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$data['title'] = 'Create Leave';
$data['editable'] = false;
$data['employeeList'] = $this->employeeRepository->pluck();
$data['leaveTypeList'] = $this->leaveTypeRepository->pluck();
$data['status'] = Leave::PROGRESS_STATUS;
$data['duration'] = Leave::DURATION;
return view('leave::leave.create', $data);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): RedirectResponse
{
$inputData = $request->all();
try {
$this->leaveRepository->create($inputData);
$employeemodel = $this->employeeRepository->getEmployeeById($inputData['employee_id']);
sendNotification($employeemodel->user, [
'msg' => 'Leave Created']);
toastr()->success('Leave Created Succesfully');
} catch (\Throwable $th) {
toastr()->error($th->getMessage());
}
return redirect()->route('leave.index');
}
/**
* Show the specified resource.
*/
public function show($id)
{
$data['title'] = 'View Leave';
$data['leave'] = $this->leaveRepository->getLeaveById($id);
return view('leave::leave.show', $data);
}
/**
* Show the form for editing the specified resource.
*/
public function edit($id)
{
$data['title'] = 'Edit Leave';
$data['editable'] = true;
$data['leave'] = $this->leaveRepository->getLeaveById($id);
$data['employeeList'] = $this->employeeRepository->pluck();
$data['leaveTypeList'] = $this->leaveTypeRepository->pluck();
$data['status'] = Leave::PROGRESS_STATUS;
$data['duration'] = Leave::DURATION;
return view('leave::leave.edit', $data);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, $id): RedirectResponse
{
$inputData = $request->except(['_method', "_token"]);
try {
$this->leaveRepository->update($id, $inputData);
toastr()->success('Leave Updated Succesfully');
} catch (\Throwable $th) {
toastr()->error($th->getMessage());
}
return redirect()->route('leave.index');
}
/**
* Remove the specified resource from storage.
*/
public function destroy($id)
{
try {
$this->leaveRepository->delete($id);
toastr()->success('Leave Deleted Succesfully');
} catch (\Throwable $th) {
//throw $th;
toastr()->error($th->getMessage());
}
}
/**
* Update the status of the specified resource in storage.
*/
public function updateStatus(Request $request)
{
try {
$this->leaveRepository->update($request->leave_id, $request->except(['_method', "_token"]));
$employeemodel = $this->employeeRepository->getEmployeeById($request->employee_id);
sendNotification($employeemodel->user, [
'msg' => 'Leave Status Changed']);
toastr()->success('Leave Status Updated Successfully');
} catch (\Throwable $th) {
toastr()->error($th->getMessage());
}
return redirect()->route('leave.index');
}
}

View File

@ -0,0 +1,100 @@
<?php
namespace Modules\Leave\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Modules\Leave\Repositories\LeaveTypeInterface;
use Modules\Leave\Repositories\LeaveTypeRepository;
class LeaveTypeController extends Controller
{
private $leaveTypeRepository;
public function __construct(LeaveTypeInterface $leaveTypeRepository)
{
$this->leaveTypeRepository = $leaveTypeRepository;
}
/**
* Display a listing of the resource.
*/
public function index()
{
$data['title'] = 'LeaveType List';
$data['leaveTypeLists'] = $this->leaveTypeRepository->findAll();
return view('leave::leave-type.index', $data);
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$data['title'] = 'Create LeaveType';
$data['editable'] = false;
return view('leave::leave-type.create', $data);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): RedirectResponse
{
try {
$this->leaveTypeRepository->create($request->all());
toastr()->success('LeaveType created successfully');
} catch (\Throwable $th) {
toastr()->error($th->getMessage());
}
return redirect()->route('leaveType.index');
}
/**
* Show the specified resource.
*/
public function show($id)
{
$data['title'] = 'View Leave';
$data['leave'] = $this->leaveTypeRepository->getLeaveTypeById($id);
return view('leave::leave-type.show', $data);
}
/**
* Show the form for editing the specified resource.
*/
public function edit($id)
{
$data['editable'] = true;
$data['title'] = 'Edit LeaveType';
$data['leaveType'] = $this->leaveTypeRepository->getLeaveTypeById($id);
return view('leave::leave-type.edit', $data);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, $id): RedirectResponse
{
try {
$this->leaveTypeRepository->update($id, $request->except(['_token', '_method']));
toastr()->success('LeaveType updated successfully');
} catch (\Throwable $th) {
toastr()->error($th->getMessage());
}
return redirect()->route('leaveType.index');
}
/**
* Remove the specified resource from storage.
*/
public function destroy($id)
{
$this->leaveTypeRepository->delete($id);
toastr()->success('LeaveType deleted successfully');
return redirect()->route('leaveType.index');
}
}

View File

View File

@ -0,0 +1,26 @@
<?php
namespace Modules\Leave\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class LeaveTypeRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
//
];
}
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
}

View File

View File

@ -0,0 +1,76 @@
<?php
namespace Modules\Leave\Models;
use App\Models\Scopes\CreatedByScope;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Modules\Employee\Models\Employee;
class Leave extends Model
{
protected $table = 'tbl_leaves';
protected $primaryKey = 'leave_id';
protected $guarded = [];
protected $appends = ['status_name'];
const PROGRESS_STATUS = [
1 => 'Pending',
2 => 'Approved',
3 => 'Rejected',
];
const DURATION = [
1 => 'Full Day',
2 => 'Half Day',
3 => 'Multiple',
];
protected static function booted(): void
{
static::addGlobalScope(new CreatedByScope);
}
protected function statusName(): Attribute
{
return Attribute::make(
get: function (mixed $value, array $attributes) {
switch ($attributes['status']) {
case '1':
$color = 'dark';
break;
case '2':
$color = 'success';
break;
case '3':
$color = 'danger';
break;
default:
$color = 'light';
break;
}
return collect([
'status' => self::PROGRESS_STATUS[$attributes['status']],
'color' => $color]);
},
set: fn($value) => $value,
);
}
public function getDuration()
{
return self::DURATION[$this->duration] ?? null;
}
public function employee()
{
return $this->belongsTo(Employee::class, 'employee_id');
}
public function leaveType()
{
return $this->belongsTo(LeaveType::class, 'leave_type_id');
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Modules\Leave\Models;
use App\Traits\StatusTrait;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class LeaveType extends Model
{
use HasFactory, StatusTrait;
protected $table = 'tbl_leave_types';
protected $primaryKey = 'leave_type_id';
protected $fillable = [
'name',
'total_days',
'max_accumulated_days',
'is_accumulated',
'is_proportionate',
'status',
'description',
'remarks',
'createdBy',
'updatedBy',
];
protected $casts = [
'is_accumulated' => 'boolean',
'is_proportionate' => 'boolean',
];
}

View File

View File

@ -0,0 +1,121 @@
<?php
namespace Modules\Leave\Providers;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
use Modules\Leave\Repositories\LeaveInterface;
use Modules\Leave\Repositories\LeaveRepository;
use Modules\Leave\Repositories\LeaveTypeInterface;
use Modules\Leave\Repositories\LeaveTypeRepository;
class LeaveServiceProvider extends ServiceProvider
{
protected string $moduleName = 'Leave';
protected string $moduleNameLower = 'leave';
/**
* Boot the application events.
*/
public function boot(): void
{
$this->registerCommands();
$this->registerCommandSchedules();
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
$this->loadMigrationsFrom(module_path($this->moduleName, 'database/migrations'));
}
/**
* Register the service provider.
*/
public function register(): void
{
$this->app->bind(LeaveInterface::class, LeaveRepository::class);
$this->app->bind(LeaveTypeInterface::class, LeaveTypeRepository::class);
$this->app->register(RouteServiceProvider::class);
}
/**
* Register commands in the format of Command::class
*/
protected function registerCommands(): void
{
// $this->commands([]);
}
/**
* Register command Schedules.
*/
protected function registerCommandSchedules(): void
{
// $this->app->booted(function () {
// $schedule = $this->app->make(Schedule::class);
// $schedule->command('inspire')->hourly();
// });
}
/**
* Register translations.
*/
public function registerTranslations(): void
{
$langPath = resource_path('lang/modules/' . $this->moduleNameLower);
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, $this->moduleNameLower);
$this->loadJsonTranslationsFrom($langPath);
} else {
$this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower);
$this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang'));
}
}
/**
* Register config.
*/
protected function registerConfig(): void
{
$this->publishes([module_path($this->moduleName, 'config/config.php') => config_path($this->moduleNameLower . '.php')], 'config');
$this->mergeConfigFrom(module_path($this->moduleName, 'config/config.php'), $this->moduleNameLower);
}
/**
* Register views.
*/
public function registerViews(): void
{
$viewPath = resource_path('views/modules/' . $this->moduleNameLower);
$sourcePath = module_path($this->moduleName, 'resources/views');
$this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower . '-module-views']);
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);
$componentNamespace = str_replace('/', '\\', config('modules.namespace') . '\\' . $this->moduleName . '\\' . ltrim(config('modules.paths.generator.component-class.path'), config('modules.paths.app_folder', '')));
Blade::componentNamespace($componentNamespace, $this->moduleNameLower);
}
/**
* Get the services provided by the provider.
*/
public function provides(): array
{
return [];
}
private function getPublishableViewPaths(): array
{
$paths = [];
foreach (config('view.paths') as $path) {
if (is_dir($path . '/modules/' . $this->moduleNameLower)) {
$paths[] = $path . '/modules/' . $this->moduleNameLower;
}
}
return $paths;
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace Modules\Leave\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
{
/**
* Called before routes are registered.
*
* Register any model bindings or pattern based filters.
*/
public function boot(): void
{
parent::boot();
}
/**
* Define the routes for the application.
*/
public function map(): void
{
$this->mapApiRoutes();
$this->mapWebRoutes();
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*/
protected function mapWebRoutes(): void
{
Route::middleware('web')->group(module_path('Leave', '/routes/web.php'));
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*/
protected function mapApiRoutes(): void
{
Route::middleware('api')->prefix('api')->name('api.')->group(module_path('Leave', '/routes/api.php'));
}
}

View File

View File

@ -0,0 +1,12 @@
<?php
namespace Modules\Leave\Repositories;
interface LeaveInterface
{
public function findAll($filters = [], $limit = null, $offset = null);
public function getLeaveById($leaveId);
public function delete($leaveId);
public function create(array $LeaveDetails);
public function update($leaveId, array $newDetails);
}

View File

@ -0,0 +1,52 @@
<?php
namespace Modules\Leave\Repositories;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Modules\Leave\Models\Leave;
class LeaveRepository implements LeaveInterface
{
public function findAll($filters = [], $limit = null, $offset = null)
{
return Leave::with(['leaveType', 'employee'])->when($filters, function ($query) use ($filters) {
if (isset($filters["employee_id"])) {
$query->whereHas('employee', function (Builder $query) use ($filters) {
$query->where('id', '=', $filters["employee_id"]);
});
}
if (isset($filters["status"])) {
$query->where("status", $filters["status"]);
}
if (isset($filters["date"])) {
$explodeDate = explode("to", $filters['date']);
$query->whereBetween("start_date", [$explodeDate[0], preg_replace('/\s+/', '', $explodeDate[1])]);
}
})->get();
}
public function getLeaveById($leaveId)
{
return Leave::findOrFail($leaveId);
}
public function delete($leaveId)
{
Leave::destroy($leaveId);
}
public function create(array $leaveDetails)
{
return Leave::create($leaveDetails);
}
public function update($leaveId, array $newDetails)
{
return Leave::where('leave_id', $leaveId)->update($newDetails);
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Modules\Leave\Repositories;
interface LeaveTypeInterface
{
public function pluck();
public function findAll();
public function getLeaveTypeById($leaveTypeId);
public function delete($leaveTypeId);
public function create(array $LeaveTypeDetails);
public function update($leaveTypeId, array $newDetails);
}

View File

@ -0,0 +1,38 @@
<?php
namespace Modules\Leave\Repositories;
use Modules\Leave\Models\LeaveType;
class LeaveTypeRepository implements LeaveTypeInterface
{
public function pluck()
{
return LeaveType::pluck('name', 'leave_type_id');
}
public function findAll()
{
return LeaveType::get();
}
public function getLeaveTypeById($leaveTypeId)
{
return LeaveType::findOrFail($leaveTypeId);
}
public function create(array $leaveTypeDetails)
{
return LeaveType::create($leaveTypeDetails);
}
public function update($leaveTypeId, array $newDetails)
{
return LeaveType::where('leave_type_id', $leaveTypeId)->update($newDetails);
}
public function delete($leaveTypeId)
{
LeaveType::destroy($leaveTypeId);
}
}

View File

@ -0,0 +1,30 @@
{
"name": "nwidart/leave",
"description": "",
"authors": [
{
"name": "Nicolas Widart",
"email": "n.widart@gmail.com"
}
],
"extra": {
"laravel": {
"providers": [],
"aliases": {
}
}
},
"autoload": {
"psr-4": {
"Modules\\Leave\\": "app/",
"Modules\\Leave\\Database\\Factories\\": "database/factories/",
"Modules\\Leave\\Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Modules\\Leave\\Tests\\": "tests/"
}
}
}

View File

View File

@ -0,0 +1,5 @@
<?php
return [
'name' => 'Leave',
];

View File

@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('tbl_leaves', function (Blueprint $table) {
$table->tinyInteger('leave_id')->unsigned()->autoIncrement();
$table->unsignedBigInteger('employee_id');
$table->unsignedBigInteger('leave_type_id');
$table->date('start_date')->nullable();
$table->date('end_date')->nullable();
$table->date('leave_approved_date')->nullable();
$table->Integer('total_days')->nullable();
$table->unsignedBigInteger('leave_approved_by')->nullable();
$table->integer('duration')->nullable();
$table->integer('status')->default(11);
$table->longtext('description')->nullable();
$table->text('remarks')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('tbl_leaves');
}
};

View File

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('tbl_leave_types', function (Blueprint $table) {
$table->tinyInteger('leave_type_id')->unsigned()->autoIncrement();
$table->string('name');
$table->integer('total_days');
$table->integer('max_accumulated_days')->nullable();
$table->boolean('is_accumulated')->default(false);
$table->boolean('is_proportionate')->default(false);
$table->integer('status')->default(11);
$table->mediumText('description')->nullable();
$table->mediumText('remarks')->nullable();
$table->integer('createdBy')->nullable();
$table->integer('updatedBy')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('tbl_leave_types');
}
};

View File

View File

@ -0,0 +1,16 @@
<?php
namespace Modules\Leave\database\seeders;
use Illuminate\Database\Seeder;
class LeaveDatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// $this->call([]);
}
}

11
Modules/Leave/module.json Normal file
View File

@ -0,0 +1,11 @@
{
"name": "Leave",
"alias": "leave",
"description": "",
"keywords": [],
"priority": 0,
"providers": [
"Modules\\Leave\\Providers\\LeaveServiceProvider"
],
"files": []
}

View File

@ -0,0 +1,15 @@
{
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"axios": "^1.1.2",
"laravel-vite-plugin": "^0.7.5",
"sass": "^1.69.5",
"postcss": "^8.3.7",
"vite": "^4.0.0"
}
}

View File

View File

View File

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Leave Module - {{ config('app.name', 'Laravel') }}</title>
<meta name="description" content="{{ $description ?? '' }}">
<meta name="keywords" content="{{ $keywords ?? '' }}">
<meta name="author" content="{{ $author ?? '' }}">
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
{{-- Vite CSS --}}
{{-- {{ module_vite('build-leave', 'resources/assets/sass/app.scss') }} --}}
</head>
<body>
@yield('content')
{{-- Vite JS --}}
{{-- {{ module_vite('build-leave', 'resources/assets/js/app.js') }} --}}
</body>

View File

@ -0,0 +1,30 @@
@extends('layouts.app')
@section('content')
<div class="page-content">
<div class="container-fluid">
<!-- start page title -->
@include('layouts.partials.breadcrumb', ['title' => $title])
<!-- end page title -->
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
<form action="{{ route('leaveType.store') }}" class="needs-validation" novalidate method="post">
@csrf
@include('leave::leave-type.partials.action')
</form>
</div>
</div>
</div>
</div>
<!--end row-->
</div>
<!-- container-fluid -->
</div>
@endsection
@push('js')
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
@endpush

View File

@ -0,0 +1,49 @@
@extends('layouts.app')
@section('content')
<div class="page-content">
<div class="container-fluid">
<!-- start page title -->
<div class="row">
<div class="col-12">
<div class="page-title-box d-sm-flex align-items-center justify-content-between">
<h4 class="mb-sm-0">{{ $title }}</h4>
<div class="page-title-right">
<ol class="breadcrumb m-0">
<li class="breadcrumb-item"><a href="javascript: void(0);">Dashboards</a></li>
<li class="breadcrumb-item active">{{ $title }}</li>
</ol>
</div>
</div>
</div>
</div>
<!-- end page title -->
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
{{-- @dd($leaveType) --}}
{{ html()->modelForm($leaveType, 'PUT')->route('leaveType.update', $leaveType->leave_type_id)->class(['needs-validation'])->attributes(['novalidate'])->open() }}
@include('leave::leave-type.partials.action')
{{ html()->closeModelForm() }}
</div>
</div>
</div>
</div>
<!--end row-->
</div>
<!-- container-fluid -->
</div>
@endsection
@push('js')
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
@endpush

View File

@ -0,0 +1,82 @@
@extends('layouts.app')
@section('content')
<div class="page-content">
<div class="container-fluid">
<!-- start page title -->
@include('layouts.partials.breadcrumb', ['title' => $title])
<!-- end page title -->
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header align-items-center d-flex">
<h5 class="card-title flex-grow-1 mb-0">LeaveType Lists</h5>
<div class="flex-shrink-0">
@role('admin')
<button type="button" class="btn btn-primary waves-effect waves-light"><i class=ri-user-shared-line me-1
align-bottom"></i> Assign</button>
@endrole
<a href="{{ route('leaveType.create') }}" class="btn btn-success waves-effect waves-light"><i
class="ri-add-fill me-1 align-bottom"></i> Add LeaveType</a>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table id="buttons-datatables" class="display table-sm table-bordered table" style="width:100%">
<thead>
<tr>
<th>S.N</th>
<th>Name</th>
<th>Total Days</th>
<th>Accumulated</th>
<th>Proportionate</th>
{{-- <th>Created By</th> --}}
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@forelse ($leaveTypeLists as $key => $leaveType)
<tr>
<td>{{ $key + 1 }}</td>
<td>{{ $leaveType->name }}</td>
<td>{{ $leaveType->total_days }}</td>
<td>{{ $leaveType->is_accumulated ? 'Checked' : 'Not Checked' }}</td>
<td>{{ $leaveType->is_proportionate ? 'Checked' : 'Not Checked' }}</td>
{{-- <td>{{ $leaveType->createdBy }}</td> --}}
<td>{!! $leaveType->status_name !!}</td>
<td>
<div class="hstack flex-wrap gap-3">
@can('leaveType.show')
<a href="{{ route('leaveType.show', $leaveType->leave_type_id) }}" class="link-info fs-15">
<i class="ri-eye-line"></i>
</a>
@endcan
</a>
<a href="{{ route('leaveType.edit', $leaveType->leave_type_id) }}"
class="link-success fs-15 edit-item-btn"><i class="ri-edit-2-line"></i></a>
<a href="javascript:void(0);"
data-link="{{ route('leaveType.destroy', $leaveType->leave_type_id) }}"
data-id="{{ $leaveType->leave_type_id }}" class="link-danger fs-15 remove-item-btn"><i
class="ri-delete-bin-line"></i></a>
</div>
</td>
</tr>
@empty
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!--end row-->
</div>
</div>
@endsection

View File

@ -0,0 +1,49 @@
<div class="row gy-3">
<div class="col-md-6">
{{ html()->label('Name')->class('form-label') }}
{{ html()->text('name')->class('form-control')->placeholder('LeaveType Name')->required() }}
{{ html()->div('Please Fill the Name')->class('invalid-feedback') }}
</div>
<div class="col-md-3">
{{ html()->label('Total Days')->class('form-label') }}
{{ html()->number('total_days')->class('form-control')->placeholder('Total Leave Days')->required() }}
{{ html()->div('Please Fill the total Leave Days')->class('invalid-feedback') }}
</div>
<div class="col-md-3">
{{ html()->label('Maximum Accumulated Days')->class('form-label') }}
{{ html()->number('max_accumulated_days')->class('form-control')->placeholder('Maximum Acummulated Days')->required() }}
{{ html()->div('Please Fill this field')->class('invalid-feedback') }}
</div>
<div class="col-md-12">
{{ html()->label('Description')->class('form-label') }}
{{ html()->textarea('description')->class('form-control')->placeholder('LeaveType Description')->attributes(['rows' => 3])->required() }}
{{ html()->div('Please Fill the Description')->class('invalid-feedback') }}
</div>
<div class="col-md-2">
{{ html()->label('Accumulated')->class('form-check-label me-1') }}
{{ html()->checkbox('is_accumulated',false,'1')->class('form-check-input')->checked($editable && $leaveType->is_accumulated == 1)}}
</div>
<div class="col-md-2">
{{ html()->label('Proportionate')->class('form-check-label me-1') }}
{{ html()->checkbox('is_proportionate',false,'1')->class('form-check-input')->checked($editable && $leaveType->is_proportionate == 1) }}
</div>
</div>
<div class="text-end">
<a href="javascript::void(0)" class="btn btn-light btn-md">Cancel</a>
<button type="submit" class="btn btn-primary btn-md">{{ $editable ? 'Update' : 'Add LeaveType' }}</button>
</div>
@push('js')
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
@endpush

View File

@ -0,0 +1,16 @@
<div class="modal fade" id="viewModal" tabindex="-1" aria-labelledby="viewModalLabel" aria-modal="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalgridLabel">View Leave</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form action="{{ route('leave-t.store') }}" class="needs-validation" novalidate method="post">
@csrf
@include('leave::leave.partials.action')
</form>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,50 @@
@extends('layouts.app')
@section('content')
<div class="page-content">
<div class="container-fluid">
@include('layouts.partials.breadcrumb', ['title' => $title])
<div class="row">
<div class="col-md-8">
<div class="card card-body p-4">
<div>
<div class="table-responsive">
<table class="table-borderless mb-0 table">
<tbody>
<tr>
<th><span class="fw-medium">Leave Name</span></th>
<td>{{ $leave->name}}</td>
</tr>
<tr>
<th><span class="fw-medium">Total Days</span></th>
<td>{{ $leave->total_days}}</td>
</tr>
<tr>
<th><span class="fw-medium">Accumulated</span></th>
<td>{{ $leave->max_accumulated_days}}</td>
</tr>
<tr>
<th><span class="fw-medium">Status</span></th>
<td>{{ $leave->status}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="mb-3 text-end">
<a href="{{ route('leave.index') }}" class="btn btn-secondary w-sm">Back</a>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('js')
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
@endpush

View File

@ -0,0 +1,17 @@
@extends('layouts.app')
@section('content')
<div class="page-content">
<div class="container-fluid">
@include('layouts.partials.breadcrumb', ['title' => $title])
{{ html()->form('POST')->route('leave.store')->class(['needs-validation'])->attributes(['novalidate', 'enctype' => 'multipart/form-data'])->open() }}
@include('leave::leave.partials.action')
{{ html()->form()->close() }}
</div>
</div>
@endsection
@push('js')
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
@endpush

View File

@ -0,0 +1,35 @@
@extends('layouts.app')
@section('content')
<div class="page-content">
<div class="container-fluid">
<!-- start page title -->
<div class="row">
<div class="col-12">
<div class="page-title-box d-sm-flex align-items-center justify-content-between">
<h4 class="mb-sm-0">{{ $title }}</h4>
<div class="page-title-right">
<ol class="breadcrumb m-0">
<li class="breadcrumb-item"><a href="javascript: void(0);">Dashboards</a></li>
<li class="breadcrumb-item active">{{ $title }}</li>
</ol>
</div>
</div>
</div>
</div>
<!-- end page title -->
{{ html()->modelForm($leave, 'PUT')->route('leave.update', $leave->leave_id)->class(['needs-validation'])->attributes(['novalidate'])->open() }}
@include('leave::leave.partials.action')
{{ html()->closeModelForm() }}
<!--end row-->
</div>
<!-- container-fluid -->
</div>
@endsection
@push('js')
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
@endpush

View File

@ -0,0 +1,211 @@
@extends('layouts.app')
@section('content')
<div class="page-content">
<div class="container-fluid">
<div class="card">
<div class="card-body text-align-center">
<form action="{{ route('leave.index') }}" method="GET">
<div class="row g-2">
<div class="col-xl-3">
<div class="search-box">
{{ html()->select('employee_id', $employeeList)->class('form-control select2')->value(request('employee_id'))->placeholder('By Employee') }}
</div>
</div>
<div class="col-sm-3">
<div class="">
{{ html()->text('date')->class('form-control')->value(request('date'))->placeholder('Date Range')->attributes([
'id' => 'datepicker-range',
'data-provider' => 'flatpickr',
'data-date-format' => 'Y-m-d',
'data-range-date' => 'true',
]) }}
</div>
</div>
<div class="col-xl-3">
<div class="search-box">
{{ html()->select('status', $status)->class('form-control select2')->value(request('status'))->placeholder('By Status') }}
</div>
</div>
</div>
<div class="g-2 text-end">
<div>
<button type="submit" class="btn btn-primary">Filter</button>
<a href="{{ route('leave.index') }}" class="btn btn-danger">Reset</a>
</div>
</div>
</form>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="card">
<div class="card-header align-items-center d-flex">
<h5 class="card-title flex-grow-1 mb-0">Leave Lists</h5>
<div class="flex-shrink-0">
<a href="{{ route('leave.create') }}" class="btn btn-success waves-effect waves-light"><i
class="ri-add-fill me-1 align-bottom"></i> Apply Leave</a>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table id="buttons-datatables" class="display table-sm table-bordered table" style="width:100%">
<thead>
<tr>
<th>S.N</th>
<th>Employee Name</th>
<th>Leave Type</th>
<th>Start Date</th>
<th>End Date</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@forelse ($leaves as $key => $leave)
<tr>
<td>{{ $key + 1 }}</td>
<td>{{ optional($leave->employee)->full_name }}</td>
<td>
<div class="d-flex align-items-center">
<div class="">
<h5 class="fs-12 fw-medium my-1">
{{ optional($leave->leaveType)->name }}
</h5>
<span class="fs-10 text-danger text-bold">{{ $leave->getDuration() }}</span>
</div>
</div>
</td>
<td>{{ $leave->start_date }}</td>
<td>{{ $leave->end_date }}</td>
<td>
<span class="badge bg-{{ $leave->status_name['color'] }}">
{!! $leave->status_name['status'] !!} </span>
</td>
<td>
<div class="hstack flex-wrap gap-3">
{{-- <a href="javascript:void(0);" class="link-info fs-15 view-item-btn" data-bs-toggle="modal"
data-bs-target="#viewModal">
<i class="ri-eye-line"></i>
</a> --}}
@can('leave.show')
<a href="{{ route('leave.show', $leave->leave_id) }}" class="link-info fs-15">
<i class="ri-eye-line"></i>
</a>
@endcan
@can('leave.edit')
<a href="{{ route('leave.edit', $leave->leave_id) }}"
class="link-success fs-15 edit-item-btn"><i class="ri-edit-2-line"></i></a>
@endcan
@can('leave.updateStatus')
<a href="javascript:void(0);" class="link-dark fs-15 approve-item-btn" data-bs-toggle="modal"
data-bs-target="#statusModal" data-leave-id="{{ $leave->leave_id }}"
data-employee-id="{{ $leave->employee_id }}">
<i class="ri-check-double-line"></i>
</a>
@endcan
@can('leave.destroy')
<a href="javascript:void(0);" data-link="{{ route('leave.destroy', $leave->leave_id) }}"
data-id="{{ $leave->leave_id }}" class="link-danger fs-15 remove-item-btn"><i
class="ri-delete-bin-line"></i></a>
@endcan
</div>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="text-center"> No Leave Found</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!--end row-->
</div>
</div>
<!-- container-fluid -->
@can('leave.updateStatus')
<div class="modal fade" id="statusModal" tabindex="-1" aria-labelledby="statusModalLabel" aria-modal="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalgridLabel">Update Status</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form action="{{ route('leave.updateStatus') }}" id="statusForm" class="needs-validation" novalidate
method="post">
@csrf
<input type="hidden" id="leave_id" name="leave_id" value="">
<input type="hidden" id="employeeId" name="employee_id" value="">
<div class="row g-2">
<div class="col-md-12">
{{ html()->label('Status')->class('form-label') }}
{{ html()->select('status', $status)->class('form-select select2')->placeholder('Select Status')->required() }}
{{ html()->div('Please Select Status')->class('invalid-feedback') }}
</div>
<div class="col-md-12">
{{ html()->label('Remark')->class('form-label') }}
{{ html()->textarea('remarks')->class('form-control')->placeholder('Write remarks...') }}
</div>
</div>
<div class="mt-3 text-end">
{{ html()->button('Save')->class('btn btn-info') }}
</div>
</form>
</div>
</div>
</div>
</div>
@endcan
@endsection
@push('js')
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
<script>
// $(document).ready(function(){
// $('#statusForm').on('submit',function(e){
// e.preventDefault();
// var formData = $(this).serialize();
// $.ajax({
// url:'{{ route('leave.updateStatus') }}',
// type: 'POST',
// // headers: {
// // 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
// // }
// data: formData,
// success: function(response){
// console.log(response);
// location.reload();
// },
// error: function(xhr, status, error){
// console.error(error)
// }
// });
// });
// });
$(document).ready(function() {
$('.approve-item-btn').on('click', function() {
let leaveId = $(this).data('leave-id');
$('#leave_id').val(leaveId);
let employeeId = $(this).data('employee-id');
$('#employeeId').val(employeeId);
})
})
</script>
@endpush

View File

@ -0,0 +1,99 @@
<div class="row">
<div class="col-lg-8">
<div class="card">
<div class="card-body">
<div class="row g-2">
<div class="col-md-6">
{{ html()->label('Employee')->class('form-label') }}
{{ html()->select('employee_id', $employeeList)->class('form-select select2')->placeholder('Select Employee')->required() }}
{{ html()->div('Please Select Employee')->class('invalid-feedback') }}
</div>
<div class="col-md-6">
{{ html()->label('Leave Type')->class('form-label') }}
{{ html()->select('leave_type_id', $leaveTypeList)->class('form-select select2')->placeholder('Select Leave Type')->required() }}
{{ html()->div('Please Select Leave Type')->class('invalid-feedback') }}
</div>
<div class="col-md-12">
{{ html()->label('Selet Duration')->class('form-label') }}
<div>
@foreach ($duration as $durationKey => $durationItem)
<div class="form-check form-check-inline">
{{ html()->radio('duration')->class('form-check-input duration')->checked(old('duration', isset($leave) && $leave->duration === $durationKey))->attributes(['id' => $durationKey])->value($durationKey)->required() }}
{{ html()->label($durationItem)->class('form-check-label')->for($durationKey) }}
</div>
@endforeach
</div>
</div>
<div class="col-md-6">
{{ html()->label('Start Date')->class('form-label') }}
{{ html()->date('start_date')->class('form-control')->placeholder('Select Start Date')->required() }}
{{ html()->div('Please Select Start Date')->class('invalid-feedback') }}
</div>
<div class="col-md-6 end-date d-none">
{{ html()->label('End Date')->class('form-label') }}
{{ html()->date('end_date')->class('form-control')->placeholder('Select End Date') }}
{{ html()->div('Please Select End Date')->class('invalid-feedback') }}
</div>
<div class="col-md-12">
{{ html()->label('Description')->class('form-label') }}
{{ html()->textarea('description')->class('form-control')->placeholder('Write Reason for Leave') }}
</div>
</div>
</div>
</div>
<div class="mb-3 text-end">
<a href="{{ route('leave.index') }}" class="btn btn-danger w-sm">Cancel</a>
{{ html()->button($editable ? 'Update' : 'Add Leave', 'submit')->class('btn btn-success') }}
</div>
</div>
<div class="col-lg-4">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Leave Status</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
{{ html()->label('Status')->class('form-label') }}
{{ html()->select('status', $status)->class('form-control select2')->required() }}
</div>
</div>
</div>
<!-- end card body -->
</div>
<!-- end card -->
</div>
</div>
@push('js')
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
<script>
$('input[name=duration]').on('click', function() {
if ($(this).is(":checked")) {
var checked = $(this).prop("checked", true).val();
if (checked == 3) {
$('.end-date').removeClass('d-none')
} else {
$('.end-date').addClass('d-none')
$('input[name=end_date]').val('');
}
}
})
</script>
@isset($editable)
<script>
$(document).ready(function() {
$("input[name=duration]:checked").trigger("click");
})
</script>
@endisset
@endpush

View File

@ -0,0 +1,16 @@
<div class="modal fade" id="viewModal" tabindex="-1" aria-labelledby="viewModalLabel" aria-modal="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalgridLabel">View Leave</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form action="{{ route('leave.store') }}" class="needs-validation" novalidate method="post">
@csrf
@include('leave::leave.partials.action')
</form>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,81 @@
@extends('layouts.app')
@section('content')
<div class="page-content">
<div class="container-fluid">
@include('layouts.partials.breadcrumb', ['title' => $title])
<div class="row">
<div class="col-md-8">
<div class="card card-body p-4">
<div>
<div class="table-responsive">
<table class="table-borderless mb-0 table">
<tbody>
<tr>
<th><span class="fw-medium">Employee Name</span></th>
<td>{{ ($leave->employee)->first_name }} {{ ($leave->employee)->middle_name }} {{ ($leave->employee)->last_name }}</td>
</tr>
<tr>
<th><span class="fw-medium">Leave Type</span></th>
<td> {{ optional($leave->leaveType)->name }} </td>
</tr>
<tr>
<th><span class="fw-medium">Start Date</span></th>
<td>{{ $leave->start_date }}</td>
</tr>
<tr>
<th><span class="fw-medium">End Date</span></th>
<td>{{ $leave->end_date }}</td>
</tr>
<tr>
<th><span class="fw-medium">Leave Approved Date</span></th>
<td>{{ $leave->leave_approved_date }}</td>
</tr>
<tr>
<th><span class="fw-medium">Total Days</span></th>
<td>{{ $leave->total_days}}</td>
</tr>
<tr>
<th><span class="fw-medium">Leave Approved By</span></th>
<td>{{ $leave->leave_approved_by}}</td>
</tr>
<tr>
<th><span class="fw-medium">Duration</span></th>
<td>{{ $leave->getDuration() }}</td>
</tr>
<tr>
<th><span class="fw-medium">Leave Approved Date</span></th>
<td>{{ $leave->employee_id }}</td>
</tr>
<tr>
<th><span class="fw-medium">Status</span></th>
<td>{{ $leave->status_name['status'] }}</td>
</tr>
<tr>
<th><span class="fw-medium">Description</span></th>
<td>{{ $leave->description }}</td>
</tr>
<tr>
<th><span class="fw-medium">Remark</span></th>
<td>{{ $leave->remarks }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="mb-3 text-end">
<a href="{{ route('leave.index') }}" class="btn btn-secondary w-sm">Back</a>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('js')
<script src="{{ asset('assets/js/pages/form-validation.init.js') }}"></script>
@endpush

View File

View File

@ -0,0 +1,19 @@
<?php
use Illuminate\Support\Facades\Route;
use Modules\Leave\Http\Controllers\LeaveController;
/*
*--------------------------------------------------------------------------
* API Routes
*--------------------------------------------------------------------------
*
* Here is where you can register API routes for your application. These
* routes are loaded by the RouteServiceProvider within a group which
* is assigned the "api" middleware group. Enjoy building your API!
*
*/
Route::middleware(['auth:sanctum'])->prefix('v1')->group(function () {
Route::apiResource('leave', LeaveController::class)->names('leave');
});

View File

@ -0,0 +1,22 @@
<?php
use Illuminate\Support\Facades\Route;
use Modules\Leave\Http\Controllers\LeaveController;
use Modules\Leave\Http\Controllers\LeaveTypeController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::group([], function () {
Route::post('status-update',[LeaveController::class,'updateStatus'])->name('leave.updateStatus');
Route::resource('leave', LeaveController::class)->names('leave');
Route::resource('leave-type', LeaveTypeController::class)->names('leaveType');
});

View File

@ -0,0 +1,26 @@
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
build: {
outDir: '../../public/build-leave',
emptyOutDir: true,
manifest: true,
},
plugins: [
laravel({
publicDirectory: '../../public',
buildDirectory: 'build-leave',
input: [
__dirname + '/resources/assets/sass/app.scss',
__dirname + '/resources/assets/js/app.js'
],
refresh: true,
}),
],
});
//export const paths = [
// 'Modules/Leave/resources/assets/sass/app.scss',
// 'Modules/Leave/resources/assets/js/app.js',
//];