filters($request); if ($request->routeIs('investment.education')) { $filters['education'] = true; } return Inertia::render('investment/catalogue', ['filters' => $filters, 'references' => $catalogue->references(), 'territorySelection' => app(InvestmentGeography::class)->selection($filters), 'results' => $catalogue->query($filters)->paginate(12)->withQueryString(), 'favourites' => $request->user() ? DB::table('opportunity_favourites')->where('user_id', $request->user()->id)->pluck('opportunity_id') : [], 'territorial' => $this->territorial($filters)]); } /** * @param array $filters * @return Collection */ private function territorial(array $filters): Collection { $query = InvestmentPortfolioController::visible(); $path = app(InvestmentGeography::class)->selection($filters); if ($path !== []) { $ids = app(InvestmentGeography::class)->descendants($path[array_key_last($path)]->id); $query->where(function ($query) use ($ids) { foreach (['project_id' => 'projects', 'cluster_id' => 'economic_clusters', 'corridor_id' => 'development_corridors'] as $foreign => $table) { $query->orWhereIn($foreign, DB::table($table)->whereNull('deleted_at')->whereIn('primary_territory_id', $ids)->select('id')); } }); } if (in_array($filters['dimension'] ?? null, ['zee', 'zfi'], true)) { return $query->whereRaw('false')->get(); } if (in_array($filters['dimension'] ?? null, ['cluster', 'corridor'], true)) { $query->whereNotNull($filters['dimension'].'_id'); } foreach (['zee_id', 'zfi_id', 'park_id', 'education', 'facets', 'sector', 'province', 'district', 'geo_location_id', 'min_amount', 'max_amount', 'currency', 'before', 'maturity_level'] as $field) { if (isset($filters[$field]) && $filters[$field] !== '' && $filters[$field] !== false && $filters[$field] !== []) { return $query->whereRaw('false')->get(); } } if (! empty($filters['q'])) { $query->where('title', 'ilike', '%'.$filters['q'].'%'); } foreach (['cluster_id', 'corridor_id', 'project_id'] as $field) { if (! empty($filters[$field])) { $query->where($field, $filters[$field]); } } return $query->orderBy('title')->limit(30)->get(); } public function search(Request $request, InvestmentCatalogue $catalogue): JsonResponse { return response()->json($catalogue->query($catalogue->filters($request))->paginate($request->integer('per_page', 12))->withQueryString()); } public function references(InvestmentCatalogue $catalogue): JsonResponse { return response()->json($catalogue->references()); } public function detail(Request $request, string $slug, InvestmentCatalogue $catalogue): Response { $record = Opportunity::published()->with('sector:id,name,slug')->where('slug', $slug)->firstOrFail(); $data = $request->validate(['zone_id' => ['nullable', 'uuid']]); $origin = null; if (! empty($data['zone_id'])) { $origin = app(InvestmentZones::class)->links()->join('investment_item_opportunities as m', 'm.item_id', '=', 'zl.item_id') ->join('investment_zones as z', 'z.id', '=', 'zl.zone_id')->where('zl.zone_id', $data['zone_id'])->where('m.opportunity_id', $record->id)->first(['z.id', 'z.name']); abort_unless($origin !== null, 404); } return Inertia::render('investment/detail', ['record' => $record, 'context' => $catalogue->context($record->id), 'zone_origin' => $origin]); } public function detailApi(string $slug, InvestmentCatalogue $catalogue): JsonResponse { $record = Opportunity::published()->with('sector:id,name,slug')->where('slug', $slug)->firstOrFail(); return response()->json(['record' => $record, 'context' => $catalogue->context($record->id)]); } public function suggest(Request $request, InvestmentCatalogue $catalogue): JsonResponse { $filters = $catalogue->filters($request); if (mb_strlen(trim($filters['q'] ?? '')) < 2) { return response()->json(['opportunities' => [], 'territorial' => []]); } return response()->json(['opportunities' => $catalogue->query($filters)->limit(5)->get(['id', 'title', 'slug']), 'territorial' => $this->territorial($filters)->take(5)->values()]); } public function map(Request $request, InvestmentCatalogue $catalogue): JsonResponse { return response()->json($catalogue->map($catalogue->filters($request))); } public function compare(Request $request, InvestmentCatalogue $catalogue): JsonResponse { $data = $request->validate(['ids' => ['required', 'array', 'min:1', 'max:4'], 'ids.*' => ['required', 'integer', 'distinct']]); $records = Opportunity::published()->with('sector:id,name,slug')->whereIn('id', $data['ids'])->get(); abort_unless($records->count() === count($data['ids']), 404); return response()->json(['records' => $records->map(fn ($r) => ['record' => $r, 'context' => $catalogue->context($r->id)])]); } public function favourite(Request $request, int $id): JsonResponse { Opportunity::published()->findOrFail($id); $data = $request->validate(['saved' => ['required', 'boolean']]); $key = ['user_id' => $request->user()->id, 'opportunity_id' => $id]; if ($data['saved']) { DB::table('opportunity_favourites')->insertOrIgnore([...$key, 'created_at' => now(), 'updated_at' => now()]); } else { DB::table('opportunity_favourites')->where($key)->delete(); } return response()->json(['saved' => $data['saved']]); } public function export(Request $request, InvestmentCatalogue $catalogue): StreamedResponse { $records = $catalogue->query($catalogue->filters($request))->limit(1000)->get(); return response()->streamDownload(function () use ($records) { $stream = fopen('php://output', 'w'); if ($stream === false) { return; } fwrite($stream, "\xEF\xBB\xBF"); fputcsv($stream, ['Referência', 'Título', 'Sector', 'Província', 'Montante', 'Moeda', 'Maturidade', 'URL'], ';', '"', ''); foreach ($records as $r) { $fields = [$r->reference, $r->title, $r->sector?->name, $r->province, $r->estimated_investment_amount, $r->estimated_investment_currency, $r->maturity_label, url('/oportunidades/'.$r->slug)]; fputcsv($stream, array_map(fn ($v) => preg_match('/^[\s]*[=+@-]/u', (string) $v) ? "'".$v : $v, $fields), ';', '"', ''); } fclose($stream); }, 'oportunidades.csv', ['Content-Type' => 'text/csv; charset=UTF-8', 'X-Export-Limit' => '1000']); } }