270 lines
8.0 KiB
PHP
270 lines
8.0 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Http\Requests\StoreProjectRequest;
|
|
use App\Models\Project;
|
|
use App\Models\User;
|
|
use App\Models\Currency;
|
|
use App\Helpers\CurrencyHelper;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Barryvdh\DomPDF\Facade\Pdf;
|
|
|
|
class ProjectController extends Controller
|
|
{
|
|
/**
|
|
* Display a listing of projects with filters and pagination.
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$this->authorize('viewAny', Project::class);
|
|
|
|
$query = Project::with(['manager', 'defaultCurrency', 'tasks']);
|
|
|
|
// Filter by status
|
|
if ($request->filled('status')) {
|
|
$query->byStatus($request->status);
|
|
}
|
|
|
|
// Filter by search
|
|
if ($request->filled('search')) {
|
|
$search = $request->search;
|
|
$query->where(function ($q) use ($search) {
|
|
$q->where('name', 'like', "%{$search}%")
|
|
->orWhere('code', 'like', "%{$search}%")
|
|
->orWhere('description', 'like', "%{$search}%");
|
|
});
|
|
}
|
|
|
|
// Sort
|
|
$sortField = $request->get('sort', 'created_at');
|
|
$sortDirection = $request->get('direction', 'desc');
|
|
$allowedSorts = ['name', 'code', 'status', 'progress', 'budget', 'start_date', 'created_at'];
|
|
if (!in_array($sortField, $allowedSorts)) {
|
|
$sortField = 'created_at';
|
|
}
|
|
if (!in_array($sortDirection, ['asc', 'desc'])) {
|
|
$sortDirection = 'desc';
|
|
}
|
|
$query->orderBy($sortField, $sortDirection);
|
|
|
|
$projects = $query->paginate(12)->appends($request->query());
|
|
|
|
return view('projects.index', compact('projects'));
|
|
}
|
|
|
|
/**
|
|
* Show the form for creating a new project.
|
|
*/
|
|
public function create()
|
|
{
|
|
$this->authorize('create', Project::class);
|
|
|
|
$managers = User::where('tenant_id', $this->getTenantId())->where('is_active', true)->get();
|
|
$currencies = Currency::where('is_active', true)->get();
|
|
$statuses = ['planning', 'in_progress', 'on_hold', 'completed', 'cancelled'];
|
|
$project = null;
|
|
|
|
return view('projects.form', compact('project', 'managers', 'currencies', 'statuses'));
|
|
}
|
|
|
|
/**
|
|
* Store a newly created project in storage.
|
|
*/
|
|
public function store(StoreProjectRequest $request)
|
|
{
|
|
$this->authorize('create', Project::class);
|
|
|
|
$data = $request->validated();
|
|
$data['tenant_id'] = $this->getTenantId();
|
|
|
|
$project = Project::create($data);
|
|
|
|
// Attach manager as project member
|
|
if (!empty($data['manager_id'])) {
|
|
$project->users()->attach($data['manager_id'], ['role' => 'manager']);
|
|
}
|
|
|
|
return redirect()
|
|
->route('projects.show', $project)
|
|
->with('success', __('project.created_successfully'));
|
|
}
|
|
|
|
/**
|
|
* Display the specified project.
|
|
*/
|
|
public function show(Project $project)
|
|
{
|
|
$this->authorize('view', $project);
|
|
|
|
$project->load([
|
|
'manager',
|
|
'defaultCurrency',
|
|
'tasks.assignee',
|
|
'tasks' => fn($q) => $q->orderBy('id')->orderBy('due_date'),
|
|
//'employees',
|
|
'expenses.currency',
|
|
'users',
|
|
]);
|
|
|
|
$taskStats = [
|
|
'total' => $project->tasks->count(),
|
|
'pending' => $project->tasks->where('status', 'pending')->count() + $project->tasks->where('status', 'todo')->count(),
|
|
'in_progress' => $project->tasks->where('status', 'in_progress')->count(),
|
|
'review' => $project->tasks->where('status', 'review')->count(),
|
|
'done' => $project->tasks->where('status', 'done')->count(),
|
|
];
|
|
|
|
return view('projects.show', compact('project', 'taskStats'));
|
|
}
|
|
|
|
/**
|
|
* Show the form for editing the specified project.
|
|
*/
|
|
public function edit(Project $project)
|
|
{
|
|
$this->authorize('update', $project);
|
|
|
|
$managers = User::where('tenant_id', $this->getTenantId())->where('is_active', true)->get();
|
|
$currencies = Currency::where('is_active', true)->get();
|
|
$statuses = ['planning', 'in_progress', 'on_hold', 'completed', 'cancelled'];
|
|
|
|
return view('projects.form', compact('project', 'managers', 'currencies', 'statuses'));
|
|
}
|
|
|
|
/**
|
|
* Update the specified project in storage.
|
|
*/
|
|
public function update(StoreProjectRequest $request, Project $project)
|
|
{
|
|
$this->authorize('update', $project);
|
|
|
|
$project->update($request->validated());
|
|
|
|
return redirect()
|
|
->route('projects.show', $project)
|
|
->with('success', __('project.updated_successfully'));
|
|
}
|
|
|
|
/**
|
|
* Soft-delete the specified project.
|
|
*/
|
|
public function destroy(Project $project)
|
|
{
|
|
$this->authorize('delete', $project);
|
|
|
|
$project->delete();
|
|
|
|
return redirect()
|
|
->route('projects.index')
|
|
->with('success', __('project.deleted_successfully'));
|
|
}
|
|
|
|
/**
|
|
* Update project status via AJAX.
|
|
*/
|
|
public function updateStatus(Request $request, Project $project)
|
|
{
|
|
$this->authorize('update', $project);
|
|
|
|
$request->validate([
|
|
'status' => 'required|in:planning,in_progress,on_hold,completed,cancelled',
|
|
]);
|
|
|
|
$oldStatus = $project->status;
|
|
$project->status = $request->status;
|
|
|
|
if ($request->status === 'completed') {
|
|
$project->progress = 100;
|
|
}
|
|
|
|
$project->save();
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => __('project.status_updated'),
|
|
'status' => $project->status,
|
|
'status_label' => $project->status_label,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Export projects to Excel.
|
|
*/
|
|
public function exportExcel(Request $request)
|
|
{
|
|
$this->authorize('viewAny', Project::class);
|
|
|
|
$query = Project::with(['manager', 'defaultCurrency']);
|
|
|
|
if ($request->filled('status')) {
|
|
$query->byStatus($request->status);
|
|
}
|
|
|
|
$projects = $query->get();
|
|
|
|
// Simple CSV export
|
|
$filename = 'projects_' . now()->format('Y-m-d_His') . '.csv';
|
|
$headers = [
|
|
'Content-Type' => 'text/csv; charset=UTF-8',
|
|
'Content-Disposition' => "attachment; filename=\"{$filename}\"",
|
|
];
|
|
|
|
$callback = function () use ($projects) {
|
|
$file = fopen('php://output', 'w');
|
|
// BOM for UTF-8
|
|
fprintf($file, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
|
|
|
fputcsv($file, [
|
|
__('project.code'),
|
|
__('project.name'),
|
|
__('project.status'),
|
|
__('project.progress'),
|
|
__('project.budget'),
|
|
__('project.manager'),
|
|
__('project.start_date'),
|
|
__('project.end_date'),
|
|
]);
|
|
|
|
foreach ($projects as $project) {
|
|
fputcsv($file, [
|
|
$project->code,
|
|
$project->name,
|
|
$project->status_label,
|
|
$project->progress . '%',
|
|
$project->budget,
|
|
$project->manager?->name,
|
|
$project->start_date?->format('Y-m-d'),
|
|
$project->end_date?->format('Y-m-d'),
|
|
]);
|
|
}
|
|
|
|
fclose($file);
|
|
};
|
|
|
|
return response()->stream($callback, 200, $headers);
|
|
}
|
|
|
|
/**
|
|
* Export projects to PDF.
|
|
*/
|
|
public function exportPdf(Request $request)
|
|
{
|
|
$this->authorize('viewAny', Project::class);
|
|
|
|
$query = Project::with(['manager', 'defaultCurrency', 'tasks']);
|
|
|
|
if ($request->filled('status')) {
|
|
$query->byStatus($request->status);
|
|
}
|
|
|
|
$projects = $query->get();
|
|
|
|
$pdf = Pdf::loadView('projects.pdf', compact('projects'));
|
|
$filename = 'projects_' . now()->format('Y-m-d_His') . '.pdf';
|
|
|
|
return $pdf->download($filename);
|
|
}
|
|
}
|