json(['available' => false, 'items' => []]); } app(PartnershipSources::class)->resolve($type, $id, $request->user()); return response()->json(['available' => true, 'items' => DB::table('portal_partnerships as p')->join('partnership_origins as origin', 'origin.partnership_id', '=', 'p.id') ->where('origin.type', $type)->where('origin.target_id', $id)->where('p.status', 'published')->orderByDesc('p.id')->distinct()->get(['p.id', 'p.title'])])->header('Cache-Control', 'private, no-store'); } public function index(Request $request): Response|JsonResponse { $filters = $request->validate(['q' => 'nullable|string|max:120', 'sector_id' => 'nullable|integer', 'partnership_type' => 'nullable|string|max:40', 'kind' => 'nullable|in:offering,seeking', 'territory_id' => 'nullable|uuid', 'origin_type' => 'nullable|string|max:40', 'origin_id' => 'nullable|string|max:100']); $query = DB::table('portal_partnerships as p')->where('p.status', 'published'); foreach (['sector_id', 'partnership_type', 'kind'] as $field) { if (! empty($filters[$field])) { if ($field === 'sector_id') { $sector = Sector::whereKey($filters[$field])->first(); $query->whereIn('p.sector_id', $sector ? Sector::whereIn('slug', SectorOrganization::slugs($sector->slug))->pluck('id') : []); } else { $query->where('p.'.$field, $filters[$field]); } } } if (! empty($filters['q'])) { $term = '%'.$filters['q'].'%'; $query->where(fn ($q) => $q->where('p.title', 'ilike', $term)->orWhere('p.sector', 'ilike', $term) ->orWhereRaw("p.payload->>'summary' ILIKE ?", [$term])->orWhere(fn ($details) => $details->whereRaw("COALESCE(p.payload->>'confidentiality','public')='public'")->where(fn ($body) => $body->where('p.offer', 'ilike', $term)->orWhere('p.seeking', 'ilike', $term)))); } if (! empty($filters['origin_type']) && ! empty($filters['origin_id'])) { // Authenticate access to a private master before exposing a backlink query. app(PartnershipSources::class)->resolve($filters['origin_type'], $filters['origin_id'], $request->user()); $query->whereExists(fn ($q) => $q->selectRaw('1')->from('partnership_origins')->whereColumn('partnership_id', 'p.id')->where('type', $filters['origin_type'])->where('target_id', $filters['origin_id'])); } if (! empty($filters['territory_id'])) { abort_unless(app(InvestmentGeography::class)->publicReferences()->where('id', $filters['territory_id'])->exists(), 422); $ids = app(InvestmentGeography::class)->descendants($filters['territory_id']); $investmentIds = app(InvestmentCatalogue::class)->query(['geo_location_id' => $filters['territory_id']])->pluck('id')->map(fn ($id) => (string) $id)->all(); $query->where(fn ($q) => $q->whereExists(fn ($places) => $places->selectRaw('1')->from('partnership_locations')->whereColumn('partnership_id', 'p.id')->whereIn('geo_location_id', $ids)) ->orWhereExists(fn ($origins) => $origins->selectRaw('1')->from('partnership_origins as origin')->join('project_locations as pl', DB::raw('pl.project_id::text'), '=', 'origin.target_id') ->whereColumn('origin.partnership_id', 'p.id')->where('origin.type', 'project')->whereNull('pl.deleted_at')->where('pl.data_classification', 'public')->whereIn('pl.geo_location_id', $ids)) ->orWhereExists(fn ($origins) => $origins->selectRaw('1')->from('partnership_origins')->whereColumn('partnership_id', 'p.id')->where('type', 'investment')->whereIn('target_id', $investmentIds)) ->orWhereExists(fn ($members) => $members->selectRaw('1')->from('partnership_origins as origin') ->joinSub(app(InvestmentRelations::class)->memberships()->select(['cp.cluster_id', 'cp.project_id']), 'member', DB::raw('member.cluster_id::text'), '=', 'origin.target_id') ->join('project_locations as pl', 'pl.project_id', '=', 'member.project_id')->whereColumn('origin.partnership_id', 'p.id')->where('origin.type', 'cluster') ->whereNull('pl.deleted_at')->where('pl.data_classification', 'public')->whereIn('pl.geo_location_id', $ids)) ->orWhereExists(fn ($zones) => $zones->selectRaw('1')->from('partnership_origins as origin')->join('investment_zone_locations as zl', DB::raw('zl.zone_id::text'), '=', 'origin.target_id') ->join('investment_zones as zone', 'zone.id', '=', 'zl.zone_id')->whereColumn('origin.partnership_id', 'p.id')->where('origin.type', 'zone')->where('zone.publication_status', 'published') ->whereNull('zone.deleted_at')->where('zl.validation_status', 'validated')->whereIn('zl.geo_location_id', $ids))); } $results = $query->orderByDesc('p.id')->select(['p.id', 'p.title', 'p.sector', 'p.province', 'p.kind', 'p.partnership_type', 'p.sector_id', 'p.deadline', 'p.published_at']) ->selectRaw("p.payload->>'summary' as summary")->paginate(12)->withQueryString(); app(PublicSearchMetrics::class)->candidate($request, 'partnerships', $results->total(), $filters); if ($request->expectsJson()) { return response()->json($results); } $mapPoints = []; foreach ($results as $partnership) { $locations = app(InvestmentRelations::class)->locations(array_values(DB::table('partnership_locations')->where('partnership_id', $partnership->id)->get() ->map(fn ($location) => ['id' => $location->geo_location_id, 'role' => $location->role, 'source_reference' => $location->source_reference])->all())); foreach ($locations as $location) { if ($location['location_type'] !== 'site') { continue; } foreach ($location['coordinates'] as $coordinate) { $mapPoints[] = ['geometry' => $coordinate['geometry'], 'properties' => ['id' => count($mapPoints), 'title' => $partnership->title, 'location' => $location['name'].' · '.$location['role'].' · '.$location['source_reference'], 'url' => '/parcerias/'.$partnership->id]]; } } foreach (app(PartnershipSources::class)->forPartnership($partnership->id) as $source) { $context = $source['geography']['locations'] ?? []; foreach ($source['geography']['members'] ?? [] as $member) { $context = [...$context, ...$member['locations']]; } foreach ($context as $location) { foreach ($location['coordinates'] as $coordinate) { $mapPoints[] = ['geometry' => $coordinate['geometry'], 'properties' => ['id' => count($mapPoints), 'title' => $partnership->title, 'location' => 'Contexto da origem: '.$location['name'].'; implantação da parceria não confirmada', 'url' => '/parcerias/'.$partnership->id]]; } } } } return Inertia::render('partnerships/catalogue', ['results' => $results, 'filters' => $filters, 'mapPoints' => $mapPoints, ...$this->catalogues($request)]); } public function dashboard(Request $request): Response { $actor = $request->user(); $exchange = app(PartnershipExchange::class); $organizations = $exchange->readableOrganizations($actor); $mine = DB::table('portal_partnerships')->where(fn ($q) => $q->where(fn ($own) => $own->where('user_id', $actor->id)->whereNull('organization_id'))->orWhereIn('organization_id', $organizations))->latest()->limit(100)->get(); $interests = DB::table('partnership_interests as i')->join('portal_partnerships as p', 'p.id', '=', 'i.partnership_id') ->leftJoin('partnership_rooms as room', 'room.interest_id', '=', 'i.id') ->where(fn ($q) => $q->where(fn ($own) => $own->where('i.user_id', $actor->id)->whereNull('i.organization_id'))->orWhereIn('i.organization_id', $organizations)) ->select(['i.id', 'i.partnership_id', 'i.status', 'i.decision_reason', 'p.title', 'p.status as partnership_status', 'p.user_id', 'p.organization_id', 'room.id as room_id'])->orderByDesc('i.id')->limit(100)->get() ->map(function ($interest) use ($actor, $exchange) { $canRead = $interest->partnership_status === 'published' || $exchange->owns($actor, $interest, false) || Gate::forUser($actor)->allows('manage-portal'); return ['id' => $interest->id, 'partnership_id' => $interest->partnership_id, 'status' => $interest->status, 'decision_reason' => $interest->decision_reason, 'title' => $canRead ? $interest->title : null, 'partnership_href' => $canRead ? '/parcerias/'.$interest->partnership_id : null, 'room_id' => $interest->room_id]; }); $moderation = Gate::forUser($actor)->allows('manage-portal') ? DB::table('portal_partnerships')->whereIn('status', ['submitted', 'pending'])->orderBy('updated_at')->get(['id', 'title', 'status', 'version']) : []; return Inertia::render('partnerships/dashboard', ['mine' => $mine->map(fn ($p) => ['id' => $p->id, 'title' => $p->title, 'status' => $p->status, 'version' => $p->version]), 'interests' => $interests, 'moderation' => $moderation, 'notifications' => app(PartnershipNotifications::class)->forUser($actor)]); } public function edit(Request $request, ?int $id = null): Response { $record = $id ? app(PartnershipExchange::class)->find($id) : null; abort_if($record && ! app(PartnershipExchange::class)->owns($request->user(), $record), 403); $origins = $record ? DB::table('partnership_origins')->where('partnership_id', $id)->get() : []; if (! $record && $request->filled('origin_type') && $request->filled('origin_id')) { $source = app(PartnershipSources::class)->resolve($request->string('origin_type')->toString(), $request->string('origin_id')->toString(), $request->user()); $origins = [['type' => $source['type'], 'target_id' => $source['target_id']]]; } $payload = $record ? json_decode($record->payload, true) : []; unset($payload['_creation_hash']); return Inertia::render('partnerships/editor', ['editorActorId' => $request->user()->id, 'saveCommands' => $record ? app(PartnershipDraftSaves::class)->commands($request->user(), $record) : [], 'record' => $record ? [...(array) $record, 'payload' => $payload] : null, 'origins' => $origins, 'locations' => $record ? DB::table('partnership_locations')->where('partnership_id', $id)->get() : [], ...$this->catalogues($request)]); } public function save(Request $request, ?int $id = null): RedirectResponse|JsonResponse { $record = app(PartnershipExchange::class)->save($request->user(), $request->all(), $id); return $request->expectsJson() ? response()->json(['id' => $record->id, 'version' => $record->version, 'status' => $record->status, 'replayed' => (bool) ($record->command_replayed ?? false), 'saved_version' => $record->command_saved_version ?? null]) : redirect('/parcerias/'.$record->id.'/editar')->with('success', 'Rascunho guardado.'); } public function show(Request $request, int $id): Response|JsonResponse { $record = app(PartnershipExchange::class)->find($id); $owner = $request->user() && app(PartnershipExchange::class)->owns($request->user(), $record, false); $moderator = $request->user() && Gate::forUser($request->user())->allows('manage-portal'); abort_unless($record->status === 'published' || $owner || $moderator, 404); $payload = json_decode($record->payload, true); unset($payload['_creation_hash']); $privateDetails = ($payload['confidentiality'] ?? 'public') === 'private' && ! $owner && ! $moderator; $mine = $request->user() ? DB::table('partnership_interests')->where('partnership_id', $id)->get()->filter(fn ($i) => app(PartnershipExchange::class)->owns($request->user(), $i, false))->values() : collect(); $interests = $owner ? DB::table('partnership_interests')->where('partnership_id', $id)->orderBy('id')->get() : $mine; $commands = $request->user() ? app(PartnershipCandidacies::class)->commands($request->user(), $id) : []; $interestData = $interests->map(function ($interest) use ($record, $request, $commands) { return [...(array) $interest, 'payload' => json_decode($interest->payload, true), 'assessment' => json_decode($interest->assessment, true), 'can_edit' => $request->user() && app(PartnershipExchange::class)->owns($request->user(), $interest), 'commands' => $commands[(int) $interest->id] ?? [], 'match' => app(PartnershipMatching::class)->explain($record, $interest), 'room_id' => DB::table('partnership_rooms')->where('interest_id', $interest->id)->value('id')]; }); $props = ['record' => ['id' => $record->id, 'title' => $record->title, 'status' => $record->status, 'version' => $record->version, 'kind' => $record->kind, 'partnership_type' => $record->partnership_type, 'sector' => $record->sector, 'province' => $record->province, 'sector_id' => $record->sector_id, 'deadline' => $record->deadline, 'offer' => $privateDetails ? null : $record->offer, 'seeking' => $privateDetails ? null : $record->seeking, 'payload' => $privateDetails ? ['summary' => $payload['summary'] ?? '', 'confidentiality' => 'private'] : $payload], 'owner' => (bool) $owner, 'canEdit' => $request->user() && app(PartnershipExchange::class)->owns($request->user(), $record), 'moderator' => (bool) $moderator, 'acceptingInterests' => app(PartnershipCandidacies::class)->accepting($record), 'canStartInterest' => $request->user() && ! $owner && ! DB::table('partnership_interests')->where('partnership_id', $id)->where('user_id', $request->user()->id)->exists(), 'origins' => app(PartnershipSources::class)->forPartnership($id, $owner ? $request->user() : null), 'interests' => $interestData, 'locations' => app(InvestmentRelations::class)->locations(array_values(DB::table('partnership_locations')->where('partnership_id', $id)->get()->map(fn ($l) => ['id' => $l->geo_location_id, 'role' => $l->role, 'source_reference' => $l->source_reference])->all())), 'historySummary' => ($owner || $moderator) ? app(PartnershipPublicationHistory::class)->summary($request->user(), $id) : null, 'publicationActions' => app(PartnershipPublication::class)->actions($request->user(), $record), 'publicationCommands' => app(PartnershipPublication::class)->commands($request->user(), $record), ...$this->catalogues($request)]; if ($request->user()) { Inertia::encryptHistory(); } return $request->expectsJson() ? response()->json($props)->header('Cache-Control', 'private, no-store') : Inertia::render('partnerships/detail', $props); } public function publicationHistory(Request $request, int $id): Response|JsonResponse { $data = $request->validate(['before' => 'nullable|string|max:4000']); $history = app(PartnershipPublicationHistory::class)->page($request->user(), $id, $data['before'] ?? null); return $request->expectsJson() ? response()->json($history) : Inertia::render('partnerships/publication-history', $history); } public function transition(Request $request, int $id): RedirectResponse|JsonResponse { $data = $request->validate(['client_key' => 'nullable|uuid', 'version' => 'required|integer|min:1', 'action' => 'required|string', 'reason' => 'required|string|max:2000']); $record = app(PartnershipExchange::class)->transition($request->user(), $id, $data['version'], $data['action'], $data['reason'], $data['client_key'] ?? null); return $request->expectsJson() ? response()->json(['id' => $record->id, 'status' => $record->status, 'version' => $record->version, 'replayed' => (bool) ($record->command_replayed ?? false)]) : back()->with('success', 'Decisão registada.'); } public function interest(Request $request, int $id): RedirectResponse|JsonResponse { $interest = app(PartnershipExchange::class)->interest($request->user(), $id, $request->all()); return $request->expectsJson() ? response()->json(['id' => $interest->id, 'version' => $interest->version, 'replayed' => (bool) ($interest->command_replayed ?? false)]) : back()->with('success', 'Interesse guardado.'); } public function decide(Request $request, int $id): RedirectResponse|JsonResponse { $data = $request->validate(['client_key' => 'nullable|uuid', 'partnership_version' => 'nullable|integer|min:1', 'weights_hash' => 'nullable|string|regex:/^[a-f0-9]{64}$/', 'version' => 'required|integer|min:1', 'action' => 'required|string', 'reason' => 'required|string|max:2000', 'assessment' => 'array|max:7']); $interest = app(PartnershipExchange::class)->decideInterest($request->user(), $id, $data['version'], $data['action'], $data['reason'], $data['assessment'] ?? [], $data['partnership_version'] ?? null, $data['weights_hash'] ?? null, $data['client_key'] ?? null); return $request->expectsJson() ? response()->json(['status' => $interest->status, 'version' => $interest->version, 'replayed' => (bool) ($interest->command_replayed ?? false)]) : back()->with('success', 'Candidatura atualizada.'); } public function candidacyHistory(Request $request, int $id): Response|JsonResponse { $data = $request->validate(['before' => 'nullable|string|max:4000']); $history = app(PartnershipCandidacyHistory::class)->page($request->user(), $id, $data['before'] ?? null); return $request->expectsJson() ? response()->json($history) : Inertia::render('partnerships/interest-history', $history); } public function agreementHistory(Request $request, int $id): Response|JsonResponse { $data = $request->validate(['before' => 'nullable|string|max:4000']); $history = app(PartnershipAgreementHistory::class)->page($request->user(), $id, $data['before'] ?? null); return $request->expectsJson() ? response()->json($history) : Inertia::render('partnerships/agreement-history', $history); } public function room(Request $request, int $id): Response|JsonResponse { $context = app(PartnershipExchange::class)->room($request->user(), $id); $participants = app(PartnershipExchange::class)->participants($context['partnership'], $context['interest']); $participantIds = array_column($participants, 'id'); $canWrite = app(PartnershipExchange::class)->owns($request->user(), $context['partnership']) || app(PartnershipExchange::class)->owns($request->user(), $context['interest']); $canAssign = $canWrite && in_array($context['room']->status, ['agreement_recorded', 'agreed', 'execution'], true); $creationKeys = app(PartnershipMilestones::class)->creationKeys($request->user(), (int) $context['partnership']->id, $id); $partnership = (array) $context['partnership']; $payload = json_decode($partnership['payload'], true, 512, JSON_THROW_ON_ERROR); unset($payload['_creation_hash']); $partnership['payload'] = json_encode($payload, JSON_THROW_ON_ERROR); $props = [...$context, 'partnership' => $partnership, 'room' => [...(array) $context['room'], 'agreement' => app(PartnershipAgreementProjection::class)->current($context['room']->agreement)], 'messages' => DB::table('partnership_messages as m')->join('users as u', 'u.id', '=', 'm.author_id')->where('room_id', $id)->orderBy('m.id')->get(['m.id', 'm.body', 'm.created_at', 'm.author_id', 'm.client_key', 'u.name']) ->map(fn ($message) => [...(array) $message, 'client_key' => (int) $message->author_id === $request->user()->id ? $message->client_key : null]), 'documents' => DB::table('partnership_documents as link')->join('documents as d', 'd.id', '=', 'link.document_id')->where('room_id', $id) ->where(fn ($q) => $q->where('link.shared', true)->orWhere('link.owner_id', $request->user()->id)) ->get(['link.id', 'link.shared', 'link.owner_id', 'd.file_name', 'd.status', 'd.malware_scan_status', 'd.id as document_id']), 'milestones' => DB::table('partnership_milestones as m')->leftJoin('users as responsible', 'responsible.id', '=', 'm.responsible_id')->where('m.room_id', $id)->orderBy('m.id') ->get(['m.*', 'responsible.name as responsible_name'])->map(fn ($milestone) => [...(array) $milestone, 'responsible_is_participant' => in_array((int) $milestone->responsible_id, $participantIds, true), 'client_key' => $creationKeys[(int) $milestone->id] ?? null]), 'agreementActorId' => $request->user()->id, 'agreementParty' => app(PartnershipAgreements::class)->party($request->user(), $context), 'agreementCommands' => app(PartnershipAgreements::class)->commands($request->user(), $context), 'participants' => $canAssign ? $participants : [], 'canWrite' => $canWrite]; return $request->expectsJson() ? response()->json($props) : Inertia::render('partnerships/room', $props); } public function message(Request $request, int $id): RedirectResponse|JsonResponse { $data = $request->validate(['client_key' => 'required|uuid', 'body' => 'required|string|max:15000']); DB::transaction(function () use ($request, $id, $data) { $context = app(PartnershipExchange::class)->room($request->user(), $id); abort_unless(app(PartnershipExchange::class)->owns($request->user(), $context['partnership']) || app(PartnershipExchange::class)->owns($request->user(), $context['interest']), 403); $locked = DB::table('partnership_rooms')->where('id', $id)->lockForUpdate()->first(); abort_unless($locked !== null, 404); $key = ['room_id' => $id, 'author_id' => $request->user()->id, 'client_key' => $data['client_key']]; $existing = DB::table('partnership_messages')->where($key)->first(); if ($existing) { abort_unless($existing->body === $data['body'], 409, 'Chave já utilizada com outro conteúdo.'); return; } abort_if(in_array($locked->status, ['closed', 'cancelled'], true), 409, 'Sala encerrada.'); DB::table('partnership_messages')->insert([...$key, 'body' => $data['body']]); app(PartnershipExchange::class)->event($context['partnership'], $request->user(), 'partnership.message_created.v1', ['room_id' => $id], 'Nova mensagem privada', [$context['partnership']->user_id, $context['interest']->user_id]); }); return $request->expectsJson() ? response()->json(['sent' => true]) : back()->with('success', 'Mensagem registada.'); } public function agreement(Request $request, int $id): RedirectResponse|JsonResponse { $result = app(PartnershipAgreements::class)->act($request->user(), $id, $request->all()); return $request->expectsJson() ? response()->json($result) : back()->with('success', 'Acordo atualizado; assinatura jurídica não verificada.'); } public function milestone(Request $request, int $id, ?int $milestone = null): RedirectResponse|JsonResponse { $data = $request->validate(['client_key' => 'nullable|uuid', 'title' => 'required|string|max:200', 'responsible_id' => 'required|integer', 'due_date' => 'nullable|date_format:Y-m-d', 'status' => 'required|in:pending,progress,completed', 'evidence' => 'required_if:status,completed|nullable|string|max:10000', 'version' => ($milestone ? 'required' : 'nullable').'|integer|min:1']); if (isset($data['client_key'])) { $data['client_key'] = strtolower($data['client_key']); } $result = app(PartnershipMilestones::class)->save($request->user(), $id, $data, $milestone); return $request->expectsJson() ? response()->json($result) : back()->with('success', 'Marco guardado.'); } public function upload(Request $request, int $id): RedirectResponse|JsonResponse { $data = $request->validate(['client_key' => 'nullable|uuid', 'file' => 'required|file|mimes:pdf,jpg,jpeg,png|max:10240', 'shared' => 'required|boolean']); $file = $request->file('file'); abort_unless($file instanceof UploadedFile, 422); $result = app(PartnershipRoomUploads::class)->save($request->user(), $id, $file, (bool) $data['shared'], isset($data['client_key']) ? strtolower($data['client_key']) : null); return $request->expectsJson() ? response()->json($result) : back()->with('success', 'Envio reconciliado com o documento privado. Consulte o estado da análise na lista.')->with('uploaded_document_id', $result['document_id']); } public function download(Request $request, int $id, int $attachment): \Illuminate\Http\Response|BinaryFileResponse { $context = app(PartnershipExchange::class)->room($request->user(), $id); $document = DB::table('partnership_documents as link')->join('documents as d', 'd.id', '=', 'link.document_id')->where('link.room_id', $id)->where('link.id', $attachment) ->where(fn ($q) => $q->where('link.shared', true)->orWhere('link.owner_id', $request->user()->id))->where('d.status', 'active')->whereNull('d.deleted_at') ->where('d.malware_scan_status', 'clean')->whereColumn('d.malware_scanned_sha256', 'd.sha256')->first(['d.*']); abort_unless($document !== null, 404); $response = app(VerifiedDocumentBinary::class)->respond($document); app(PartnershipExchange::class)->event($context['partnership'], $request->user(), 'partnership.document_accessed.v1', ['room_id' => $id, 'attachment_id' => $attachment], 'Documento consultado'); return $response; } public function share(Request $request, int $id, int $attachment): RedirectResponse|JsonResponse { $data = $request->validate(['shared' => 'required|boolean']); DB::transaction(function () use ($request, $id, $attachment, $data) { $context = app(PartnershipExchange::class)->room($request->user(), $id, (bool) $data['shared']); $link = DB::table('partnership_documents')->where('room_id', $id)->where('id', $attachment)->lockForUpdate()->first(); abort_unless($link !== null && (int) $link->owner_id === $request->user()->id, 403, 'Só o autor pode autorizar ou retirar a partilha.'); DB::table('partnership_documents')->where('id', $attachment)->update(['shared' => $data['shared']]); app(PartnershipExchange::class)->event($context['partnership'], $request->user(), 'partnership.document_sharing_changed.v1', ['room_id' => $id, 'attachment_id' => $attachment, 'shared' => $data['shared']], 'Autorização documental atualizada'); }); return $request->expectsJson() ? response()->json(['saved' => true]) : back()->with('success', 'Autorização de partilha atualizada.'); } public function notification(Request $request, int $id): RedirectResponse { app(PartnershipNotifications::class)->markRead($request->user(), $id); return back(); } /** @return array */ private function catalogues(Request $request): array { $sources = []; foreach (InvestmentPortfolioController::visible()->where(fn ($q) => $q->whereNotNull('project_id')->orWhereNotNull('cluster_id'))->get(['id', 'title', 'project_id', 'cluster_id']) as $dossier) { $sources[] = ['type' => $dossier->project_id ? 'project' : 'cluster', 'target_id' => $dossier->project_id ?? $dossier->cluster_id, 'title' => $dossier->title, 'href' => '/carteira/'.$dossier->id]; } foreach (DB::table('opportunities')->where('status', 'published')->whereNotNull('published_at')->get(['id', 'title', 'slug']) as $investment) { $sources[] = ['type' => 'investment', 'target_id' => (string) $investment->id, 'title' => $investment->title, 'href' => '/oportunidades/'.$investment->slug]; } foreach (DB::table('investment_zones')->where('publication_status', 'published')->whereNull('deleted_at')->get(['id', 'name']) as $zone) { $sources[] = ['type' => 'zone', 'target_id' => $zone->id, 'title' => $zone->name, 'href' => '/zonas/'.$zone->id]; } foreach (ExistingBusiness::query()->published()->get(['id', 'public_title', 'public_slug']) as $business) { $sources[] = ['type' => 'business', 'target_id' => $business->id, 'title' => $business->public_title, 'href' => '/negocios/'.$business->public_slug]; } if ($request->user()) { foreach (DB::table('investment_company_declarations')->where('user_id', $request->user()->id)->get(['id']) as $company) { $sources[] = ['type' => 'company', 'target_id' => $company->id, 'title' => 'Declaração empresarial existente', 'href' => '/investidor/profile']; } foreach (DB::table('simulation_form_drafts')->where('owner_user_id', $request->user()->id)->whereNull('deleted_at')->get(['id', 'created_at']) as $draft) { $sources[] = ['type' => 'simulation', 'target_id' => $draft->id, 'title' => 'Ficha de projeto · '.$draft->created_at, 'href' => '/simulador-fiscal?draft='.$draft->id]; } } return ['types' => config('partnerships.types'), 'criteria' => config('partnerships.criteria'), 'sources' => $sources, 'sectors' => Sector::choices()->orderBy('name')->get(['id', 'name', 'slug']), 'territories' => app(InvestmentGeography::class)->publicReferences()->orderBy('name')->get(['id', 'name', 'location_type', 'parent_id']), 'organizations' => $request->user() ? DB::table('memberships as m')->join('organizations as o', 'o.id', '=', 'm.organization_id')->where('m.user_id', $request->user()->id) ->whereNull('m.deleted_at')->whereNull('o.deleted_at')->whereIn('m.role', ['admin', 'manager', 'officer'])->get(['o.id', 'o.name']) : []]; } }