96 lines
6.1 KiB
PHP
96 lines
6.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Domain\Opportunities\Enums\OpportunityStatus;
|
|
use App\Models\Opportunity;
|
|
use App\Models\Sector;
|
|
use App\Services\InvestmentGeography;
|
|
use App\Services\OpportunityManagement;
|
|
use App\Services\OpportunityManagementInstallation;
|
|
use App\Services\OpportunityResources;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Validation\Rule;
|
|
use Inertia\Inertia;
|
|
use Inertia\Response;
|
|
|
|
class OpportunityManagementController extends Controller
|
|
{
|
|
public function installation(Request $request, OpportunityManagementInstallation $service): JsonResponse|Response
|
|
{
|
|
return $request->expectsJson() ? response()->json($service->preview()) : Inertia::render('management/opportunity-install', $service->preview());
|
|
}
|
|
|
|
public function applyInstallation(Request $request, OpportunityManagementInstallation $service): RedirectResponse
|
|
{
|
|
$data = $request->validate(['hash' => 'required|string|size:64', 'confirm' => 'required|accepted']);
|
|
$service->apply($request->user(), $data['hash']);
|
|
|
|
return back()->with('success', 'Gestão instalada; nenhuma oportunidade foi submetida ou publicada.');
|
|
}
|
|
|
|
public function index(Request $request, OpportunityManagement $service): Response
|
|
{
|
|
if (! $service->installed()) {
|
|
return app(PortalManagementController::class)->catalog($request, 'opportunities');
|
|
}
|
|
$filters = $request->validate(['q' => 'nullable|string|max:150', 'status' => ['nullable', Rule::in([...array_column(OpportunityStatus::cases(), 'value'), 'returned', 'auto_validation_failed', 'technical_review', 'legal_territorial_review', 'approval_pending', 'publication_pending'])], 'sector_id' => ['nullable', 'integer', Rule::exists('sectors', 'id')->whereNull('deleted_at')], 'geography_scope' => ['nullable', Rule::in(['unspecified', 'specific', 'multiple', 'national', 'digital'])], 'category' => ['nullable', Rule::in(OpportunityManagement::CATEGORIES)], 'deleted' => 'nullable|boolean', 'page' => 'nullable|integer|min:1']);
|
|
$query = Opportunity::query();
|
|
if ($filters['deleted'] ?? false) {
|
|
$query->onlyTrashed();
|
|
}
|
|
if ($filters['q'] ?? null) {
|
|
$query->where(fn ($q) => $q->whereLike('title', '%'.$filters['q'].'%')->orWhereLike('reference', '%'.$filters['q'].'%'));
|
|
}
|
|
if ($filters['status'] ?? null) {
|
|
$query->where('status', $filters['status']);
|
|
}
|
|
if ($filters['category'] ?? null) {
|
|
$query->where('opportunity_category', $filters['category']);
|
|
}
|
|
if ($filters['sector_id'] ?? null) {
|
|
$query->where('sector_id', $filters['sector_id']);
|
|
}
|
|
if ($filters['geography_scope'] ?? null) {
|
|
$query->where('geography_scope', $filters['geography_scope']);
|
|
}
|
|
|
|
return Inertia::render('management/opportunities', ['records' => $query->orderByDesc('id')->paginate(20)->withQueryString(), 'filters' => $filters, 'counts' => Opportunity::selectRaw('status,count(*) AS total')->groupBy('status')->pluck('total', 'status'), 'categories' => OpportunityManagement::CATEGORIES, 'geographyReviewCount' => Opportunity::where('geography_scope', 'unspecified')->count(), 'sectors' => Sector::orderBy('name')->get(['id', 'name'])]);
|
|
}
|
|
|
|
public function edit(Request $request, OpportunityManagement $service, ?int $id = null): Response
|
|
{
|
|
if (! $service->installed()) {
|
|
return app(PortalManagementController::class)->edit('opportunities', $id);
|
|
}
|
|
$record = $id ? Opportunity::withTrashed()->findOrFail($id) : null;
|
|
$values = $record ? ($record->management_draft ? json_decode($record->management_draft, true) : $record->only(OpportunityManagement::FIELDS)) : [];
|
|
$classification = $id ? DB::table('audit_events')->where('source_system', 'opportunity-origin-review')->where('after_data->opportunity_id', $id)->orderByDesc('occurred_at')->orderByDesc('id')->value('after_data') : null;
|
|
|
|
return Inertia::render('management/opportunity-draft', ['resourcesInstalled' => app(OpportunityResources::class)->installed(), 'record' => $record, 'values' => $values, 'etag' => $record ? $service->etag($record) : null, 'readOnly' => str_ends_with($request->path(), '/view'), 'closedVersion' => $record ? $service->closed($record) : false, 'classification' => $classification ? json_decode($classification, true)['classification'] : 'undetermined', 'sectors' => Sector::where('is_active', true)->orderBy('name')->get(['id', 'name']), 'categories' => OpportunityManagement::CATEGORIES,
|
|
'territoryPath' => app(InvestmentGeography::class)->path($values['primary_territory_id'] ?? null, true),
|
|
'history' => $id ? DB::table('audit_events')->where('source_system', 'opportunity-management')->where('after_data->record_id', $id)->orderByDesc('occurred_at')->get(['id', 'action', 'actor_user_id', 'occurred_at']) : []]);
|
|
}
|
|
|
|
public function save(Request $request, OpportunityManagement $service, ?int $id = null): JsonResponse
|
|
{
|
|
$record = $service->save($request->user(), $request->all(), $id);
|
|
if (! $id) {
|
|
$request->session()->flash('success', 'Rascunho guardado no servidor. Pode continuar o preenchimento; não foi submetido.');
|
|
}
|
|
|
|
return response()->json(['id' => $record->id, 'etag' => $service->etag($record), 'message' => 'Rascunho guardado no servidor. Não foi submetido, aprovado ou publicado.'], $id ? 200 : 201)->header('ETag', '"'.$service->etag($record).'"');
|
|
}
|
|
|
|
public function lifecycle(Request $request, int $id, OpportunityManagement $service): JsonResponse
|
|
{
|
|
abort_unless($service->installed(), 503);
|
|
$record = $service->lifecycle($request->user(), $id, $request->all());
|
|
|
|
return response()->json(['id' => $record->id, 'message' => $request->input('action') === 'restore' ? 'Restaurado como rascunho; nova revisão necessária antes de publicar.' : 'Exclusão lógica/arquivo registado; histórico e relações preservados.']);
|
|
}
|
|
}
|