468 lines
18 KiB
PHP
468 lines
18 KiB
PHP
<?php
|
|
|
|
namespace Modules\ManagementTools\Http\Controllers;
|
|
|
|
use App\User;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Routing\Controller;
|
|
use Modules\ManagementTools\Entities\MtUserTask;
|
|
use Modules\ManagementTools\Utils\ManagementToolsUtil;
|
|
use Modules\ManagementTools\Utils\ProfessionalProjectBridge;
|
|
|
|
class EmployeeTaskController extends Controller
|
|
{
|
|
public function __construct(
|
|
protected ManagementToolsUtil $mtUtil,
|
|
protected ProfessionalProjectBridge $ppBridge
|
|
) {
|
|
}
|
|
|
|
protected function authorizeAccess(): void
|
|
{
|
|
if (! auth()->user()->can('managementtools.access') || ! auth()->user()->can('managementtools.manage_employee_tasks')) {
|
|
abort(403, 'Unauthorized action.');
|
|
}
|
|
}
|
|
|
|
protected function authorizeUserTasksPage(): void
|
|
{
|
|
if (! auth()->user()->can('managementtools.access')
|
|
|| (! auth()->user()->can('managementtools.manage_employee_tasks')
|
|
&& ! auth()->user()->can('managementtools.daily_report_all'))) {
|
|
abort(403, 'Unauthorized action.');
|
|
}
|
|
}
|
|
|
|
protected function authorizeOwnOrManage(): void
|
|
{
|
|
if (! auth()->user()->can('managementtools.daily_report_own')
|
|
&& ! auth()->user()->can('managementtools.manage_employee_tasks')) {
|
|
abort(403, 'Unauthorized action.');
|
|
}
|
|
}
|
|
|
|
protected const TABS = ['defined', 'pending', 'unreported', 'inability', 'incomplete', 'approved', 'all'];
|
|
|
|
public function index(Request $request)
|
|
{
|
|
$this->authorizeUserTasksPage();
|
|
$business_id = (int) $request->session()->get('user.business_id');
|
|
$active_tab = $request->query('tab', 'defined');
|
|
if (! in_array($active_tab, self::TABS, true)) {
|
|
$active_tab = 'defined';
|
|
}
|
|
|
|
return view('managementtools::employee_tasks.index', [
|
|
'tab_counts' => $this->buildTabCounts($business_id),
|
|
'active_tab' => $active_tab,
|
|
'can_manage' => auth()->user()->can('managementtools.manage_employee_tasks'),
|
|
]);
|
|
}
|
|
|
|
public function tabCounts(Request $request)
|
|
{
|
|
$this->authorizeUserTasksPage();
|
|
$business_id = (int) $request->session()->get('user.business_id');
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'counts' => $this->buildTabCounts($business_id),
|
|
]);
|
|
}
|
|
|
|
public function tabContent(Request $request, string $tab)
|
|
{
|
|
$this->authorizeUserTasksPage();
|
|
if (! in_array($tab, self::TABS, true)) {
|
|
abort(404);
|
|
}
|
|
|
|
$business_id = (int) $request->session()->get('user.business_id');
|
|
$tasks = $this->tasksForTab($business_id, $tab, $request);
|
|
$count = $tasks instanceof \Illuminate\Pagination\AbstractPaginator ? $tasks->total() : $tasks->count();
|
|
|
|
$html = view('managementtools::employee_tasks.partials.tab_pane_content', [
|
|
'tab' => $tab,
|
|
'tasks' => $tasks,
|
|
'users' => $tab === 'approved' ? User::forDropdown($business_id, false) : [],
|
|
'filter_user_id' => $request->input('filter_user_id'),
|
|
'filter_category_id' => $request->input('filter_category_id'),
|
|
'filter_start' => $this->mtUtil->formatForInput($this->mtUtil->parseInputDate($request->filter_start)),
|
|
'filter_end' => $this->mtUtil->formatForInput($this->mtUtil->parseInputDate($request->filter_end)),
|
|
'can_manage' => auth()->user()->can('managementtools.manage_employee_tasks'),
|
|
'can_urgent_message' => auth()->user()->can('managementtools.urgent_messages'),
|
|
'can_task_report' => auth()->user()->can('managementtools.task_report'),
|
|
'has_pp_projects' => $this->ppBridge->isAvailable(),
|
|
'project_filter_options' => $this->ppBridge->isAvailable()
|
|
? $this->ppBridge->categoriesDropdown($business_id)
|
|
: [],
|
|
])->render();
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'html' => $html,
|
|
'count' => $count,
|
|
'tab' => $tab,
|
|
]);
|
|
}
|
|
|
|
protected function buildTabCounts(int $business_id): array
|
|
{
|
|
$three_days_ago = Carbon::today()->subDays(3)->format('Y-m-d');
|
|
|
|
return [
|
|
'defined' => MtUserTask::forBusiness($business_id)->where('task_status', 0)->count(),
|
|
'pending' => MtUserTask::forBusiness($business_id)->pendingApproval()->count(),
|
|
'unreported' => MtUserTask::forBusiness($business_id)
|
|
->where('task_status', 0)
|
|
->whereDoesntHave('dailyReports', fn ($q) => $q->where('report_date', '>=', $three_days_ago))
|
|
->count(),
|
|
'inability' => MtUserTask::forBusiness($business_id)
|
|
->whereHas('dailyReports', fn ($q) => $q->where('task_status', 4))
|
|
->count(),
|
|
'incomplete' => MtUserTask::forBusiness($business_id)
|
|
->where('task_status', 0)
|
|
->whereNotNull('due_date')
|
|
->where('due_date', '<', Carbon::today())
|
|
->count(),
|
|
'approved' => MtUserTask::forBusiness($business_id)->where('task_status', 1)->count(),
|
|
'all' => MtUserTask::forBusiness($business_id)->count(),
|
|
];
|
|
}
|
|
|
|
protected function applyProjectFilter($query, Request $request): void
|
|
{
|
|
if (! $request->filled('filter_category_id') || ! $this->ppBridge->isAvailable()) {
|
|
return;
|
|
}
|
|
|
|
$filter = $request->input('filter_category_id');
|
|
if ($filter === 'uncategorized') {
|
|
$query->whereNotNull('project_id')->whereHas('ppProject', fn ($q) => $q->uncategorized());
|
|
} elseif (is_numeric($filter)) {
|
|
$query->whereHas('ppProject.categories', fn ($q) => $q->where('pp_project_categories.id', (int) $filter));
|
|
}
|
|
}
|
|
|
|
protected function tasksForTab(int $business_id, string $tab, Request $request)
|
|
{
|
|
$three_days_ago = Carbon::today()->subDays(3)->format('Y-m-d');
|
|
$per_page = 50;
|
|
$with = ['user', 'ppProject.categories'];
|
|
|
|
switch ($tab) {
|
|
case 'defined':
|
|
$query = MtUserTask::forBusiness($business_id)
|
|
->where('task_status', 0)
|
|
->with($with)
|
|
->orderByDesc('created_at');
|
|
$this->applyProjectFilter($query, $request);
|
|
|
|
return $query->paginate($per_page);
|
|
|
|
case 'pending':
|
|
$query = MtUserTask::forBusiness($business_id)
|
|
->pendingApproval()
|
|
->with($with)
|
|
->orderByDesc('created_at');
|
|
$this->applyProjectFilter($query, $request);
|
|
|
|
return $query->paginate($per_page);
|
|
|
|
case 'unreported':
|
|
$query = MtUserTask::forBusiness($business_id)
|
|
->where('task_status', 0)
|
|
->whereDoesntHave('dailyReports', fn ($q) => $q->where('report_date', '>=', $three_days_ago))
|
|
->with($with)
|
|
->orderByDesc('created_at');
|
|
$this->applyProjectFilter($query, $request);
|
|
|
|
return $query->paginate($per_page);
|
|
|
|
case 'inability':
|
|
$query = MtUserTask::forBusiness($business_id)
|
|
->whereHas('dailyReports', fn ($q) => $q->where('task_status', 4))
|
|
->with($with)
|
|
->orderByDesc('updated_at');
|
|
$this->applyProjectFilter($query, $request);
|
|
|
|
return $query->paginate($per_page);
|
|
|
|
case 'incomplete':
|
|
$query = MtUserTask::forBusiness($business_id)
|
|
->where('task_status', 0)
|
|
->whereNotNull('due_date')
|
|
->where('due_date', '<', Carbon::today())
|
|
->with($with)
|
|
->orderBy('due_date');
|
|
$this->applyProjectFilter($query, $request);
|
|
|
|
return $query->paginate(40);
|
|
|
|
case 'approved':
|
|
$query = MtUserTask::forBusiness($business_id)->where('task_status', 1)->with($with);
|
|
if ($request->filled('filter_user_id')) {
|
|
$query->where('user_id', (int) $request->filter_user_id);
|
|
}
|
|
if ($request->filled('filter_start')) {
|
|
$start = $this->mtUtil->parseInputDate($request->filter_start);
|
|
if ($start) {
|
|
$query->whereDate('updated_at', '>=', $start);
|
|
}
|
|
}
|
|
if ($request->filled('filter_end')) {
|
|
$end = $this->mtUtil->parseInputDate($request->filter_end);
|
|
if ($end) {
|
|
$query->whereDate('updated_at', '<=', $end);
|
|
}
|
|
}
|
|
$this->applyProjectFilter($query, $request);
|
|
|
|
return $query->orderByDesc('updated_at')->paginate($per_page);
|
|
|
|
case 'all':
|
|
$query = MtUserTask::forBusiness($business_id)
|
|
->with($with)
|
|
->orderByDesc('created_at');
|
|
$this->applyProjectFilter($query, $request);
|
|
|
|
return $query->paginate($per_page);
|
|
|
|
default:
|
|
abort(404);
|
|
}
|
|
}
|
|
|
|
public function create()
|
|
{
|
|
$this->authorizeAccess();
|
|
$business_id = (int) request()->session()->get('user.business_id');
|
|
$users = User::forDropdown($business_id, true);
|
|
$projects_grouped = $this->ppBridge->getProjectsGroupedByCategory($business_id, (int) request()->input('user_id', auth()->id()));
|
|
|
|
return view('managementtools::employee_tasks.create', [
|
|
'users' => $users,
|
|
'statuses' => MtUserTask::statusLabels(),
|
|
'has_pp_projects' => $this->ppBridge->isAvailable(),
|
|
'projects_grouped' => $projects_grouped,
|
|
]);
|
|
}
|
|
|
|
/** Admin creates a system task (pre-approved). */
|
|
public function store(Request $request)
|
|
{
|
|
$this->authorizeAccess();
|
|
$business_id = (int) $request->session()->get('user.business_id');
|
|
|
|
$request->validate([
|
|
'user_id' => 'required|integer|exists:users,id',
|
|
'title' => 'required|string|max:255',
|
|
'description' => 'nullable|string|max:2000',
|
|
'due_date' => 'nullable|string|max:20',
|
|
'task_status' => 'nullable|integer|in:0,1,2',
|
|
'project_id' => 'nullable|integer',
|
|
]);
|
|
|
|
if ($request->filled('project_id') && ! $this->ppBridge->validateProjectForUser($business_id, (int) $request->user_id, (int) $request->project_id)) {
|
|
return response()->json(['success' => false, 'msg' => __('managementtools::lang.invalid_project')]);
|
|
}
|
|
|
|
MtUserTask::create([
|
|
'business_id' => $business_id,
|
|
'user_id' => $request->user_id,
|
|
'project_id' => $request->input('project_id'),
|
|
'title' => $request->title,
|
|
'description' => $request->description,
|
|
'due_date' => $this->mtUtil->parseInputDate($request->due_date),
|
|
'task_status' => $request->input('task_status', 0),
|
|
'task_source' => MtUserTask::SOURCE_SYSTEM,
|
|
'approval_status' => MtUserTask::APPROVAL_APPROVED,
|
|
'approved_by' => auth()->id(),
|
|
'approved_at' => now(),
|
|
'created_by' => auth()->id(),
|
|
]);
|
|
|
|
return response()->json(['success' => true, 'msg' => __('managementtools::lang.task_added')]);
|
|
}
|
|
|
|
/** Modal form for employee task submission (CRM-style). */
|
|
public function createSubmitForm()
|
|
{
|
|
$this->authorizeOwnOrManage();
|
|
$business_id = (int) request()->session()->get('user.business_id');
|
|
$projects_grouped = $this->ppBridge->getProjectsGroupedByCategory($business_id, auth()->id());
|
|
|
|
return view('managementtools::employee_tasks.submit_task_form', [
|
|
'projects_grouped' => $projects_grouped,
|
|
'has_pp_projects' => $this->ppBridge->isAvailable(),
|
|
]);
|
|
}
|
|
|
|
/** Employee submits own task — pending manager approval. */
|
|
public function submitOwn(Request $request)
|
|
{
|
|
$this->authorizeOwnOrManage();
|
|
$business_id = (int) $request->session()->get('user.business_id');
|
|
$user_id = auth()->id();
|
|
$projects_grouped = $this->ppBridge->getProjectsGroupedByCategory($business_id, $user_id);
|
|
$has_projects = $this->ppBridge->isAvailable() && ! empty($projects_grouped);
|
|
|
|
$rules = [
|
|
'title' => 'required|string|max:255',
|
|
'description' => 'nullable|string|max:2000',
|
|
'due_date' => 'nullable|string|max:20',
|
|
'estimated_hours' => 'required|integer|min:0|max:24',
|
|
'estimated_minutes' => 'required|integer|min:0|max:59',
|
|
];
|
|
if ($has_projects) {
|
|
$rules['project_id'] = 'required|integer';
|
|
}
|
|
|
|
$request->validate($rules);
|
|
|
|
if ($has_projects && ! $this->ppBridge->validateProjectForUser($business_id, $user_id, (int) $request->project_id)) {
|
|
return response()->json(['success' => false, 'msg' => __('managementtools::lang.invalid_project')]);
|
|
}
|
|
|
|
$minutes = ((int) $request->estimated_hours * 60) + (int) $request->estimated_minutes;
|
|
|
|
MtUserTask::create([
|
|
'business_id' => $business_id,
|
|
'user_id' => $user_id,
|
|
'project_id' => $request->input('project_id'),
|
|
'title' => $request->title,
|
|
'description' => $request->description,
|
|
'due_date' => $this->mtUtil->parseInputDate($request->due_date),
|
|
'estimated_minutes' => $minutes,
|
|
'task_status' => 0,
|
|
'task_source' => MtUserTask::SOURCE_USER,
|
|
'approval_status' => MtUserTask::APPROVAL_PENDING,
|
|
'created_by' => $user_id,
|
|
]);
|
|
|
|
return response()->json(['success' => true, 'msg' => __('managementtools::lang.user_task_submitted')]);
|
|
}
|
|
|
|
/** Employee deletes own pending submission. */
|
|
public function deleteOwn($id)
|
|
{
|
|
$this->authorizeOwnOrManage();
|
|
$business_id = (int) request()->session()->get('user.business_id');
|
|
$task = MtUserTask::forBusiness($business_id)
|
|
->where('user_id', auth()->id())
|
|
->userSubmitted()
|
|
->findOrFail($id);
|
|
|
|
if (! $task->isPendingApproval()) {
|
|
return response()->json(['success' => false, 'msg' => __('managementtools::lang.something_went_wrong')]);
|
|
}
|
|
|
|
$task->delete();
|
|
|
|
return response()->json(['success' => true, 'msg' => __('managementtools::lang.task_deleted')]);
|
|
}
|
|
|
|
protected function getProjectsForUser(int $business_id, int $user_id): array
|
|
{
|
|
return $this->ppBridge->getProjectsForUser($business_id, $user_id);
|
|
}
|
|
|
|
public function approve($id)
|
|
{
|
|
$this->authorizeAccess();
|
|
$business_id = (int) request()->session()->get('user.business_id');
|
|
$task = MtUserTask::forBusiness($business_id)->findOrFail($id);
|
|
|
|
if (! $task->isPendingApproval()) {
|
|
return response()->json(['success' => false, 'msg' => __('managementtools::lang.something_went_wrong')]);
|
|
}
|
|
|
|
$task->update([
|
|
'approval_status' => MtUserTask::APPROVAL_APPROVED,
|
|
'approved_by' => auth()->id(),
|
|
'approved_at' => now(),
|
|
]);
|
|
|
|
return response()->json(['success' => true, 'msg' => __('managementtools::lang.task_approved')]);
|
|
}
|
|
|
|
public function reject($id)
|
|
{
|
|
$this->authorizeAccess();
|
|
$business_id = (int) request()->session()->get('user.business_id');
|
|
$task = MtUserTask::forBusiness($business_id)->findOrFail($id);
|
|
|
|
if (! $task->isPendingApproval()) {
|
|
return response()->json(['success' => false, 'msg' => __('managementtools::lang.something_went_wrong')]);
|
|
}
|
|
|
|
$task->update([
|
|
'approval_status' => MtUserTask::APPROVAL_REJECTED,
|
|
'approved_by' => auth()->id(),
|
|
'approved_at' => now(),
|
|
'task_status' => 2,
|
|
]);
|
|
|
|
return response()->json(['success' => true, 'msg' => __('managementtools::lang.task_rejected')]);
|
|
}
|
|
|
|
public function edit($id)
|
|
{
|
|
$this->authorizeAccess();
|
|
$business_id = (int) request()->session()->get('user.business_id');
|
|
$task = MtUserTask::forBusiness($business_id)->findOrFail($id);
|
|
$users = User::forDropdown($business_id, true);
|
|
$projects_grouped = $this->ppBridge->getProjectsGroupedByCategory($business_id, (int) $task->user_id);
|
|
|
|
return view('managementtools::employee_tasks.edit', [
|
|
'task' => $task,
|
|
'users' => $users,
|
|
'statuses' => MtUserTask::statusLabels(),
|
|
'has_pp_projects' => $this->ppBridge->isAvailable(),
|
|
'projects_grouped' => $projects_grouped,
|
|
]);
|
|
}
|
|
|
|
public function update(Request $request, $id)
|
|
{
|
|
$this->authorizeAccess();
|
|
$business_id = (int) $request->session()->get('user.business_id');
|
|
$task = MtUserTask::forBusiness($business_id)->findOrFail($id);
|
|
|
|
$request->validate([
|
|
'user_id' => 'required|integer|exists:users,id',
|
|
'title' => 'required|string|max:255',
|
|
'description' => 'nullable|string|max:2000',
|
|
'due_date' => 'nullable|string|max:20',
|
|
'task_status' => 'nullable|integer|in:0,1,2',
|
|
'project_id' => 'nullable|integer',
|
|
]);
|
|
|
|
if ($request->filled('project_id') && ! $this->ppBridge->validateProjectForUser($business_id, (int) $request->user_id, (int) $request->project_id)) {
|
|
return response()->json(['success' => false, 'msg' => __('managementtools::lang.invalid_project')]);
|
|
}
|
|
|
|
$task->update([
|
|
'user_id' => $request->user_id,
|
|
'project_id' => $request->input('project_id'),
|
|
'title' => $request->title,
|
|
'description' => $request->description,
|
|
'due_date' => $this->mtUtil->parseInputDate($request->due_date),
|
|
'task_status' => $request->input('task_status', 0),
|
|
]);
|
|
|
|
return response()->json(['success' => true, 'msg' => __('managementtools::lang.task_updated')]);
|
|
}
|
|
|
|
public function destroy($id)
|
|
{
|
|
$this->authorizeAccess();
|
|
$business_id = (int) request()->session()->get('user.business_id');
|
|
$task = MtUserTask::forBusiness($business_id)->findOrFail($id);
|
|
$task->delete();
|
|
|
|
return response()->json(['success' => true, 'msg' => __('managementtools::lang.task_deleted')]);
|
|
}
|
|
}
|