feat(mobile): add mobile app with Expo/React Native and Laravel API endpoints
This commit is contained in:
181
app/Http/Controllers/Api/LeaveApiController.php
Normal file
181
app/Http/Controllers/Api/LeaveApiController.php
Normal file
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\LeaveApplication;
|
||||
use App\Models\User;
|
||||
use App\Services\EmployeeService;
|
||||
use App\Services\LeaveService;
|
||||
use Illuminate\Http\Request;
|
||||
use Exception;
|
||||
|
||||
class LeaveApiController extends Controller
|
||||
{
|
||||
protected EmployeeService $employeeService;
|
||||
protected LeaveService $leaveService;
|
||||
|
||||
public function __construct(EmployeeService $employeeService, LeaveService $leaveService)
|
||||
{
|
||||
$this->employeeService = $employeeService;
|
||||
$this->leaveService = $leaveService;
|
||||
}
|
||||
|
||||
public function balances(Request $request)
|
||||
{
|
||||
try {
|
||||
$employee = $this->employeeService->resolveEmployee($request->user());
|
||||
} catch (Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'data' => [],
|
||||
'message' => 'Employee profile not found',
|
||||
'errors' => null,
|
||||
], 403);
|
||||
}
|
||||
|
||||
$balances = $this->leaveService->getBalances($employee);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $balances,
|
||||
'message' => null,
|
||||
'errors' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
public function applications(Request $request)
|
||||
{
|
||||
try {
|
||||
$employee = $this->employeeService->resolveEmployee($request->user());
|
||||
} catch (Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'data' => [],
|
||||
'message' => 'Employee profile not found',
|
||||
'errors' => null,
|
||||
], 403);
|
||||
}
|
||||
|
||||
$applications = LeaveApplication::where('employee_id', $request->user()->id)
|
||||
->orderBy('created_at', 'desc')
|
||||
->get();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $applications,
|
||||
'message' => null,
|
||||
'errors' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
public function apply(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'leave_type' => 'nullable|string',
|
||||
'leave_type_id' => 'nullable|integer',
|
||||
'start_date' => 'required|date',
|
||||
'end_date' => 'required|date|after_or_equal:start_date',
|
||||
'reason' => 'required|string',
|
||||
]);
|
||||
|
||||
try {
|
||||
$employee = $this->employeeService->resolveEmployee($request->user());
|
||||
} catch (Exception $e) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'data' => null,
|
||||
'message' => 'Employee profile not found',
|
||||
'errors' => null,
|
||||
], 403);
|
||||
}
|
||||
|
||||
$application = $this->leaveService->submitApplication($request->user(), $employee, $request->all());
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Leave application submitted successfully',
|
||||
'data' => $application,
|
||||
'errors' => null,
|
||||
], 201);
|
||||
}
|
||||
|
||||
public function updateStatus(Request $request, $id)
|
||||
{
|
||||
$request->validate([
|
||||
'status' => 'required|string|in:approved,rejected,cancelled,pending',
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
$application = LeaveApplication::find($id);
|
||||
|
||||
if (!$application) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'data' => null,
|
||||
'message' => 'Leave application not found',
|
||||
'errors' => null,
|
||||
], 404);
|
||||
}
|
||||
|
||||
$employee = $this->employeeService->resolveEmployee($user);
|
||||
|
||||
// Scoping & authorization checks
|
||||
if ($request->status === 'cancelled') {
|
||||
$isOwner = in_array($application->employee_id, [$employee->id, $user->id]) || $application->created_by === $user->id;
|
||||
if (!$isOwner && !in_array($user->type, ['company', 'hr', 'superadmin', 'manager'])) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'data' => null,
|
||||
'message' => 'Unauthorized to cancel this leave application',
|
||||
'errors' => null,
|
||||
], 403);
|
||||
}
|
||||
} else {
|
||||
// Approval or Rejection requires manager / HR role within same company tenant
|
||||
$userType = strtolower($user->type ?? '');
|
||||
$isElevatedRole = in_array($userType, ['company', 'hr', 'superadmin', 'manager']);
|
||||
|
||||
if (!$isElevatedRole) {
|
||||
try {
|
||||
$isElevatedRole = $user->hasAnyRole(['company', 'hr', 'superadmin', 'manager']);
|
||||
} catch (\Throwable $e) {
|
||||
$isElevatedRole = false;
|
||||
}
|
||||
}
|
||||
|
||||
$empUser = User::find($application->employee_id)
|
||||
?? Employee::find($application->employee_id)?->user
|
||||
?? User::find($application->created_by);
|
||||
|
||||
$appCompanyId = $empUser ? ($empUser->type === 'company' ? $empUser->id : ($empUser->created_by ?: $empUser->id)) : null;
|
||||
$approverCompanyId = $user->type === 'company' ? $user->id : ($user->created_by ?: $user->id);
|
||||
|
||||
$isSuperAdmin = in_array($userType, ['superadmin']);
|
||||
$isSameCompany = ($appCompanyId !== null && $approverCompanyId !== null && (int)$appCompanyId === (int)$approverCompanyId);
|
||||
|
||||
if (!$isElevatedRole || (!$isSameCompany && !$isSuperAdmin)) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'data' => null,
|
||||
'message' => 'Unauthorized to review this leave application. Approval requires authorized manager or HR within the same organization.',
|
||||
'errors' => null,
|
||||
], 403);
|
||||
}
|
||||
}
|
||||
|
||||
$application->update(['status' => $request->status]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Leave application status updated to ' . $request->status,
|
||||
'data' => $application,
|
||||
'errors' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
public function cancel(Request $request, $id)
|
||||
{
|
||||
return $this->updateStatus($request->merge(['status' => 'cancelled']), $id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user