user()->id)->latest()->get()->map(function ($journey) { $dossier = InvestmentPortfolioController::visible()->find($journey->dossier_id); $interest = $journey->interest_request_id ? InterestRequest::find($journey->interest_request_id) : null; return ['id' => $journey->id, 'dossier_id' => $dossier?->id, 'title' => $dossier->title ?? 'Ficha temporariamente indisponível', 'status' => $journey->status, 'reference' => $interest?->reference, 'request_status' => $interest?->status]; }); return Inertia::render('portfolio/workspace', ['journeys' => $journeys]); } public function start(Request $request, string $id): RedirectResponse { $dossier = InvestmentPortfolioController::visible()->findOrFail($id); $data = $request->validate(['origin_id' => ['nullable', 'uuid']]); $origin = ! empty($data['origin_id']) ? InvestmentPortfolioController::visible()->whereKey($data['origin_id'])->firstOrFail() : null; abort_if($origin && ! InvestmentPortfolioController::related($dossier, $origin), 422); DB::transaction(function () use ($request, $dossier, $origin) { DB::table('users')->where('id', $request->user()->id)->lockForUpdate()->first(); $account = DB::table('investor_accounts')->where('user_id', $request->user()->id)->first(); $investorId = $account?->investor_id; if (! $account) { $investor = Investor::create(['legal_name' => $request->user()->name, 'investor_type' => 'individual', 'status' => 'prospect', 'source_system' => 'portal-account', 'data_classification' => 'confidential']); DB::table('investor_accounts')->insert(['user_id' => $request->user()->id, 'investor_id' => $investor->id, 'created_at' => now(), 'updated_at' => now()]); $investorId = $investor->id; } $journey = InvestorJourney::firstOrNew(['user_id' => $request->user()->id, 'dossier_id' => $dossier->id]); if (! $journey->exists) { PortalAudit::save($journey, ['investor_id' => $investorId, 'status' => 'started', 'origin_dossier_id' => $origin?->id, 'source_system' => 'portal-account', 'data_classification' => 'confidential'], $request->user()->id); } }); return redirect('/investidor')->with('success', 'Guardado no seu painel.'); } public function submit(Request $request, string $id, InterestSubmission $submission): RedirectResponse { $data = $request->validate(['message' => ['required', 'string', 'min:20', 'max:5000'], 'country' => ['required', 'string', 'max:100'], 'privacy_consent' => ['accepted'], 'role' => ['required', 'in:investir,financiar,fornecer,operar,parceria'], 'horizon' => ['required', 'string', 'max:100']]); DB::transaction(function () use ($request, $id, $data, $submission) { $journey = InvestorJourney::where('user_id', $request->user()->id)->whereKey($id)->lockForUpdate()->firstOrFail(); if ($journey->interest_request_id) { return; // One expression per saved journey; retry returns its original protocol. } $dossier = InvestmentPortfolioController::visible()->whereKey($journey->dossier_id)->lockForUpdate()->firstOrFail(); $origin = $journey->origin_dossier_id ? InvestmentPortfolioController::visible()->find($journey->origin_dossier_id) : null; $interest = $submission->receive(['email' => $request->user()->email, 'fullName' => $request->user()->name, 'message' => $data['message']], null, fn () => InterestRequest::create([ 'reference' => 'IR-'.now()->year.'-'.Str::ulid(), 'full_name' => $request->user()->name, 'email' => $request->user()->email, 'investor_type' => 'individual', 'country' => $data['country'], 'sector_interest' => Str::limit($dossier->content['sector'] ?? $dossier->title, 150, ''), 'message' => $data['message'], 'privacy_consent' => true, 'consented_at' => now(), 'status' => 'received', 'source_page' => '/carteira/'.$dossier->id, 'metadata' => ['journey_id' => $journey->id, 'dossier_id' => $dossier->id, 'origin_dossier_id' => $journey->origin_dossier_id, 'project_id' => $dossier->project_id ?? $origin?->project_id, 'cluster_id' => $dossier->cluster_id ?? $origin?->cluster_id, 'corridor_id' => $dossier->corridor_id ?? $origin?->corridor_id, 'role' => $data['role'], 'horizon' => $data['horizon'], 'purpose' => 'Acompanhamento da manifestação de interesse'], ])); PortalAudit::save($journey, ['interest_request_id' => $interest->id, 'status' => 'active'], $request->user()->id); }); return back()->with('success', 'Manifestação recebida. Consulte o protocolo no painel.'); } }