feat: integrate private offers with existing editorial workflow

This commit is contained in:
2026-11-14 16:20:30 +02:00
parent 6e302db62e
commit 91c1857368
19 changed files with 1353 additions and 30 deletions
@@ -105,7 +105,9 @@ final class PortalEditorialWorkflowController extends Controller
}
$items = $query->orderByDesc('e.updated_at')->paginate(25, ['r.id', DB::raw("e.payload->'resource'->>'title' AS title"), 'r.document_type', 'r.published', 'e.id as edition_id', 'e.revision_no', 'e.state', 'e.legal_required', 'u.name as responsible'])->withQueryString();
$destinations = app(ContentManagement::class)->access($actor);
$data = ['items' => $items, 'filters' => $filters, 'notices' => $workflow->notices($actor), 'resourceAccess' => $access->full($actor) || $access->editorial($actor), 'opportunitiesUrl' => $destinations['opportunities']['index'] ?? null, 'opportunityPending' => $workflow->opportunityPending($actor), 'clustersUrl' => $destinations['clusters']['index'] ?? null,
$data = ['items' => $items, 'filters' => $filters, 'notices' => $workflow->notices($actor), 'resourceAccess' => $access->full($actor) || $access->editorial($actor), 'opportunitiesUrl' => $destinations['opportunities']['index'] ?? null,
'offersUrl' => isset($destinations['opportunities']['index']) && app(PortalProfileAccess::class)->available() ? '/minhas-oportunidades/ofertas' : null,
'opportunityPending' => $workflow->opportunityPending($actor), 'clustersUrl' => $destinations['clusters']['index'] ?? null,
'portfolioUrl' => $destinations['portfolio']['index'] ?? null, 'portfolioPending' => $workflow->portfolioPending($actor), 'demographyUrl' => $destinations['demography']['index'] ?? null, 'demographyPending' => $workflow->demographyPending($actor),
'territorialUrl' => app(TerritorialRegistry::class)->editorialAvailable() && $actor->can('manage-portal') ? '/management/territorial/editorial' : null,
'nativePending' => app(PortalEditorialNativeNotices::class)->pending($actor), 'zonesUrl' => app(PortalEditorialNativeNotices::class)->access($actor, 'investment-zones') ? '/management/investment/zones' : null,
@@ -4,6 +4,8 @@ namespace App\Http\Controllers;
use App\Models\Opportunity;
use App\Services\OpportunityCanonicalContract;
use App\Services\OpportunityManagement;
use App\Services\OpportunityProfileWorkflow;
use App\Services\PortalProfileAccess;
use App\Services\PrivateOfferPreparation;
use Illuminate\Http\JsonResponse;
@@ -15,13 +17,15 @@ final class PrivateOfferPreparationController extends Controller
{
public function index(Request $request, PortalProfileAccess $access): Response
{
$filters = $request->validate(['state' => 'nullable|in:draft,submitted,in_review,needs_information,rejected,approved']);
$records = $access->scope(Opportunity::query(), $request->user())->whereNotNull('workflow_context->private_offer_preparation')
->orderByDesc('id')->paginate(20);
->when($filters['state'] ?? null, fn ($q, $state) => $q->where('workflow_context->private_offer_preparation->state', $state))
->orderByDesc('id')->paginate(20)->withQueryString();
return Inertia::render('offers/preparation-index', ['records' => $records->through(fn ($r) => [
'id' => $r->id, 'title' => $r->title, 'reference' => $r->reference,
'state' => data_get($r->workflow_context, 'private_offer_preparation.state'),
]), 'canCreate' => count($access->navigation($request->user())['contexts']) > 0]);
'id' => $r->id, 'title' => app(OpportunityManagement::class)->values($r)['title'] ?? null, 'reference' => $r->reference,
'state' => $access->state($r),
]), 'filters' => $filters, 'canCreate' => count($access->navigation($request->user())['contexts']) > 0]);
}
public function edit(Request $request, PrivateOfferPreparation $service, ?int $id = null): Response|JsonResponse
@@ -56,4 +60,15 @@ final class PrivateOfferPreparationController extends Controller
return response()->json(['record' => $service->presentation($request->user(), $record),
'message' => 'Submissão privada registada. A oferta não foi publicada.']);
}
public function transition(Request $request, PrivateOfferPreparation $service, OpportunityProfileWorkflow $workflow, int $id): JsonResponse
{
$service->record($request->user(), $id);
$data = $request->validate(['etag' => 'required|string|size:64', 'action' => 'required|in:start_review,assign_review,request_correction,reject,approve,request_revision',
'assignee_user_id' => 'required_if:action,assign_review|integer|min:1',
'reason' => 'required|string|min:20|max:1000', 'correction_items' => 'sometimes|array', 'correction_items.*' => 'string']);
$record = $workflow->transition($request->user(), $id, $data);
return response()->json(['record' => $service->presentation($request->user(), $record), 'message' => 'Decisão privada registada. A oferta não foi publicada.']);
}
}