.
This commit is contained in:
@@ -26,13 +26,27 @@ class TranslationApiController extends Controller
|
||||
public function translate(Request $request): JsonResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
'text' => 'required|string|max:10000',
|
||||
'text' => is_array($request->input('text'))
|
||||
? 'required|array|min:1|max:8'
|
||||
: 'required|string|max:10000',
|
||||
'text.*' => 'required|string|max:10000',
|
||||
'source_lang' => 'required|string|max:20',
|
||||
'target_lang' => 'required|string|max:20',
|
||||
'is_html' => 'boolean',
|
||||
]);
|
||||
|
||||
$isBatch = is_array($data['text']);
|
||||
if ($isBatch && mb_strlen(implode('', $data['text'])) > 10000) {
|
||||
return response()->json(['message' => 'O lote de tradução excede o limite permitido.'], 422);
|
||||
}
|
||||
|
||||
if ($data['source_lang'] === $data['target_lang']) {
|
||||
return response()->json(['original_text' => $data['text'], 'translated_text' => $data['text'], 'source_lang' => $data['source_lang'], 'target_lang' => $data['target_lang']])->header('Cache-Control', 'no-store');
|
||||
return response()->json([
|
||||
'original_text' => $data['text'],
|
||||
'translated_text' => $data['text'],
|
||||
'source_lang' => $data['source_lang'],
|
||||
'target_lang' => $data['target_lang'],
|
||||
])->header('Cache-Control', 'no-store');
|
||||
}
|
||||
|
||||
$payload = ['q' => $data['text'], 'source' => $data['source_lang'], 'target' => $data['target_lang'], 'format' => ($data['is_html'] ?? false) ? 'html' : 'text'];
|
||||
@@ -42,8 +56,19 @@ class TranslationApiController extends Controller
|
||||
try {
|
||||
$response = Http::connectTimeout(2)->timeout(config('services.translation.timeout'))->post($this->url().'/translate', $payload);
|
||||
$translated = $response->json('translatedText');
|
||||
if ($response->successful() && is_string($translated) && $translated !== '') {
|
||||
return response()->json(['original_text' => $data['text'], 'translated_text' => $translated, 'source_lang' => $data['source_lang'], 'target_lang' => $data['target_lang']])->header('Cache-Control', 'no-store');
|
||||
$validTranslation = $isBatch
|
||||
? is_array($translated)
|
||||
&& count($translated) === count($data['text'])
|
||||
&& ! array_filter($translated, fn ($text) => ! is_string($text) || $text === '')
|
||||
: is_string($translated) && $translated !== '';
|
||||
|
||||
if ($response->successful() && $validTranslation) {
|
||||
return response()->json([
|
||||
'original_text' => $data['text'],
|
||||
'translated_text' => $translated,
|
||||
'source_lang' => $data['source_lang'],
|
||||
'target_lang' => $data['target_lang'],
|
||||
])->header('Cache-Control', 'no-store');
|
||||
}
|
||||
} catch (Throwable) {
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Opportunity;
|
||||
use App\Services\InvestmentGeography;
|
||||
use App\Services\SectorOrganization;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -20,8 +21,9 @@ class HomeController extends Controller
|
||||
|
||||
$locale = $validated['locale'] ?? 'pt';
|
||||
|
||||
$data = Cache::remember("portal_home_sector_organization_v1_{$locale}", now()->addMinutes(10), function () {
|
||||
$data = Cache::remember("portal_home_territorial_v2_{$locale}", now()->addMinutes(10), function () {
|
||||
$sectors = SectorOrganization::catalog();
|
||||
$statistics = app(InvestmentGeography::class)->opportunityStatistics();
|
||||
|
||||
$featured = Opportunity::with('sector:id,name,slug')
|
||||
->published()->where('is_featured', true)
|
||||
@@ -48,9 +50,9 @@ class HomeController extends Controller
|
||||
'url' => "/oportunidades/{$o->slug}",
|
||||
])->values()->all(),
|
||||
'stats' => [
|
||||
'opportunities' => Opportunity::published()->count(),
|
||||
'opportunities' => $statistics['opportunities'],
|
||||
'sectors' => $sectors->count(),
|
||||
'provinces' => Opportunity::published()->distinct('province')->count('province'),
|
||||
'provinces' => $statistics['provinces'],
|
||||
],
|
||||
];
|
||||
});
|
||||
|
||||
@@ -21,11 +21,13 @@ use App\Services\EducationWorkQueue;
|
||||
use App\Services\InvestmentGeography;
|
||||
use App\Services\PortalAudit;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
@@ -166,6 +168,7 @@ class EducationController extends Controller
|
||||
Inertia::encryptHistory();
|
||||
|
||||
return tap(Inertia::render('education/declaration', ['declaration' => $row, 'kind' => $kind, 'actorId' => $request->user()->id, 'today' => now()->toDateString(),
|
||||
'preparation' => app(EducationReviews::class)->preparation($request->user(), $record),
|
||||
'history' => app(EducationReviews::class)->history($request->user(), $record),
|
||||
'backUrl' => route('management.education.index', $filters, false), 'references' => $references])->toResponse($request), static fn (\Symfony\Component\HttpFoundation\Response $response) => $response->headers->set('Cache-Control', 'private, no-store'));
|
||||
}
|
||||
@@ -203,6 +206,29 @@ class EducationController extends Controller
|
||||
return back()->with('success', 'Declaração registada para revisão independente.');
|
||||
}
|
||||
|
||||
public function reviewPreparation(Request $request, string $kind, string $id): JsonResponse
|
||||
{
|
||||
$service = app(EducationReviews::class);
|
||||
app(EducationAccess::class)->authorize($request->user());
|
||||
if ($request->isMethod('POST')) {
|
||||
// Like CRM preparations, retain partial text before business validation.
|
||||
$input = json_decode($request->getContent(), true);
|
||||
if (! is_array($input)) {
|
||||
throw ValidationException::withMessages(['snapshot' => 'A preparação deve ser enviada em JSON.']);
|
||||
}
|
||||
$view = $service->savePreparation($request->user(), $kind, $id, $input);
|
||||
} else {
|
||||
$expected = $request->validate(['actor_id' => 'sometimes|required|integer|min:1']);
|
||||
abort_if(isset($expected['actor_id']) && (int) $expected['actor_id'] !== $request->user()->id, 409, 'A conta mudou. Actualize a entrada antes de recuperar.');
|
||||
$record = match ($kind) {
|
||||
'demands' => LabourDemand::findOrFail($id), 'capacities' => TrainingCapacity::findOrFail($id), default => abort(404)
|
||||
};
|
||||
$view = $service->preparation($request->user(), $record);
|
||||
}
|
||||
|
||||
return response()->json($view)->header('Cache-Control', 'private, no-store');
|
||||
}
|
||||
|
||||
public function review(Request $request, string $kind, string $id): RedirectResponse
|
||||
{
|
||||
$result = app(EducationReviews::class)->decide($request->user(), $kind, $id, $request->all());
|
||||
|
||||
@@ -4,10 +4,16 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Sector;
|
||||
use App\Services\DocumentManagementAccess;
|
||||
use App\Services\FundingDossierPreparation;
|
||||
use App\Services\FundingInstitutionClassification;
|
||||
use App\Services\FundingInstitutionDetails;
|
||||
use App\Services\FundingProgrammeReferences;
|
||||
use App\Services\FundingProgrammes;
|
||||
use App\Services\InvestmentGeography;
|
||||
use App\Services\InvestorContext;
|
||||
use App\Services\InvestorPath;
|
||||
use App\Services\Procurement;
|
||||
use App\Services\VirtualDesk;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -22,20 +28,38 @@ final class FundingProgrammeController extends Controller
|
||||
{
|
||||
public function reference(Request $request, string $code, FundingProgrammeReferences $references): \Symfony\Component\HttpFoundation\Response
|
||||
{
|
||||
return tap(Inertia::render('funding/reference', ['reference' => $references->preview($request->user(), $code)])->toResponse($request),
|
||||
$plan = $references->preview($request->user(), $code);
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json($plan)->header('Cache-Control', 'private, no-store');
|
||||
}
|
||||
|
||||
return tap(Inertia::render('funding/reference', ['reference' => $plan])->toResponse($request),
|
||||
static fn (\Symfony\Component\HttpFoundation\Response $response) => $response->headers->set('Cache-Control', 'private, no-store'));
|
||||
}
|
||||
|
||||
public function prepareReference(Request $request, string $code, FundingProgrammeReferences $references): RedirectResponse
|
||||
public function prepareReference(Request $request, string $code, FundingProgrammeReferences $references): RedirectResponse|JsonResponse
|
||||
{
|
||||
$data = $request->validate(['hash' => 'required|string|size:64']);
|
||||
$result = $references->prepare($request->user(), $code, $data['hash']);
|
||||
if ($request->expectsJson()) {
|
||||
$reference = $references->preview($request->user(), $code);
|
||||
$service = app(FundingProgrammes::class);
|
||||
$matches = $service->matchesReference($request->user(), $result['id'], $reference['draft']);
|
||||
$publication = $matches ? $service->preview($request->user(), $result['id']) : null;
|
||||
|
||||
return response()->json([...$result, 'matches_reference' => $matches, 'publication' => $publication ? ['version' => $publication['version'], 'hash' => $publication['hash']] : null])->header('Cache-Control', 'private, no-store');
|
||||
}
|
||||
|
||||
return redirect('/management/fundos/'.$result['id'])->with('success', $result['created']
|
||||
? 'Rascunho preparado com as fontes indicadas. Ainda não está publicado.'
|
||||
: 'Esta referência já tem ficha. Aberta a versão existente, sem substituir alterações ou acrescentar chamadas.');
|
||||
}
|
||||
|
||||
public function assisted(Request $request, FundingProgrammeReferences $references): \Symfony\Component\HttpFoundation\Response
|
||||
{
|
||||
return tap(Inertia::render('funding/assisted', ['batch' => $references->assisted($request->user())])->toResponse($request), static fn ($response) => $response->headers->set('Cache-Control', 'private, no-store'));
|
||||
}
|
||||
|
||||
public function manage(Request $request, FundingProgrammes $service): \Symfony\Component\HttpFoundation\Response
|
||||
{
|
||||
$service->guard($request->user());
|
||||
@@ -43,7 +67,7 @@ final class FundingProgrammeController extends Controller
|
||||
$items->through(fn ($row) => ['id' => $row->id, 'title' => json_decode($row->draft, true)['title'] ?? 'Rascunho sem título',
|
||||
'version' => (int) $row->version, 'published' => $row->published_version_id !== null]);
|
||||
|
||||
return tap(Inertia::render('funding/manage', ['items' => $items])->toResponse($request),
|
||||
return tap(Inertia::render('funding/manage', ['items' => $items, 'references' => app(FundingProgrammeReferences::class)->choices($request->user())])->toResponse($request),
|
||||
static fn (\Symfony\Component\HttpFoundation\Response $response) => $response->headers->set('Cache-Control', 'private, no-store'));
|
||||
}
|
||||
|
||||
@@ -63,6 +87,7 @@ final class FundingProgrammeController extends Controller
|
||||
return tap(Inertia::render('funding/edit', ['record' => $row ? ['id' => $row->id, 'code' => $row->code, 'version' => (int) $row->version,
|
||||
'draft' => json_decode($row->draft, true), 'published' => $row->published_version_id !== null] : null,
|
||||
'references' => Arr::only($references, ['organizations', 'sectors', 'territories', 'documents']),
|
||||
'categories' => FundingInstitutionClassification::CATEGORIES, 'institutionTypes' => FundingInstitutionClassification::TYPES, 'institutionNatures' => FundingInstitutionDetails::NATURES, 'needs' => FundingInstitutionDetails::NEEDS,
|
||||
'kinds' => FundingProgrammes::KINDS, 'windowModes' => FundingProgrammes::WINDOWS,
|
||||
'history' => $id ? DB::table('funding_programme_versions')->where('programme_id', $id)->orderByDesc('version')->get(['version', 'reason', 'published_at']) : []])->toResponse($request),
|
||||
static fn (\Symfony\Component\HttpFoundation\Response $response) => $response->headers->set('Cache-Control', 'private, no-store'));
|
||||
@@ -80,9 +105,12 @@ final class FundingProgrammeController extends Controller
|
||||
return response()->json($service->preview($request->user(), $id))->header('Cache-Control', 'private, no-store');
|
||||
}
|
||||
|
||||
public function publish(Request $request, string $id, FundingProgrammes $service): RedirectResponse
|
||||
public function publish(Request $request, string $id, FundingProgrammes $service): RedirectResponse|JsonResponse
|
||||
{
|
||||
$service->publish($request->user(), $id, $request->all());
|
||||
$versionId = $service->publish($request->user(), $id, $request->all());
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json(['id' => $id, 'version_id' => $versionId, 'published' => true])->header('Cache-Control', 'private, no-store');
|
||||
}
|
||||
|
||||
return back()->with('success', 'Nova versão publicada. Os interesses anteriores conservam a versão guardada.');
|
||||
}
|
||||
@@ -118,15 +146,22 @@ final class FundingProgrammeController extends Controller
|
||||
|
||||
public function index(Request $request, FundingProgrammes $service): \Symfony\Component\HttpFoundation\Response
|
||||
{
|
||||
$filters = $request->validate(['q' => 'nullable|string|max:150', 'kind' => 'nullable|in:fund,programme,incentive',
|
||||
$filters = $request->validate(['q' => 'nullable|string|max:150', 'kind' => 'nullable|in:institution,fund,programme,incentive',
|
||||
'category' => ['nullable', Rule::in(array_keys(FundingInstitutionClassification::CATEGORIES))], 'institution_type' => ['nullable', Rule::in(array_keys(FundingInstitutionClassification::TYPES))], 'location_mode' => 'nullable|in:physical,coverage', 'include_historical' => 'nullable|boolean',
|
||||
'need' => ['nullable', Rule::in(array_keys(FundingInstitutionDetails::NEEDS))],
|
||||
'context' => 'nullable|uuid', 'opportunity' => 'nullable|string|max:150',
|
||||
'territory' => ['nullable', 'uuid', Rule::in(app(InvestmentGeography::class)->publicReferences()->pluck('id')->all())],
|
||||
'sector' => ['nullable', 'integer', Rule::in(Sector::choices()->whereNotNull('published_at')->pluck('id')->all())],
|
||||
'certainty' => 'nullable|in:include_unknown,documented', 'state' => 'nullable|in:open,closed,upcoming,unknown']);
|
||||
$filters = array_filter($filters, fn ($value) => $value !== null && $value !== '');
|
||||
$dossier = $request->user() && ! empty($filters['context']) ? app(FundingDossierPreparation::class)->context($request->user(), $filters['context'], $filters['opportunity'] ?? null) : null;
|
||||
if ($dossier && empty($filters['territory']) && $dossier['territory']) {
|
||||
$filters['territory'] = $dossier['territory'];
|
||||
}
|
||||
$query = $service->catalogueQuery($filters);
|
||||
if (! empty($filters['q'])) {
|
||||
$term = '%'.str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $filters['q']).'%';
|
||||
$query->where(fn ($q) => $q->whereRaw("v.payload->>'title' ilike ?", [$term])->orWhereRaw("v.payload->>'summary' ilike ?", [$term]));
|
||||
$query->where(fn ($q) => $q->whereRaw("v.payload->>'title' ilike ?", [$term])->orWhereRaw("v.payload->>'summary' ilike ?", [$term])->orWhereRaw("(v.payload->'aliases')::text ilike ?", [$term])->orWhereRaw("EXISTS (SELECT 1 FROM jsonb_array_elements(COALESCE(v.payload->'locations','[]'::jsonb)) l WHERE l->>'label' ilike ? OR l->>'address' ilike ? OR l->>'location_text' ilike ?)", [$term, $term, $term]));
|
||||
}
|
||||
if (! empty($filters['kind'])) {
|
||||
$query->whereRaw("v.payload->>'kind' = ?", [$filters['kind']]);
|
||||
@@ -138,7 +173,7 @@ final class FundingProgrammeController extends Controller
|
||||
return [...$record, 'filter_explanation' => $service->filterExplanation($record, $filters)];
|
||||
});
|
||||
|
||||
return tap(Inertia::render('funding/index', ['items' => $items, 'filters' => $filters, 'kinds' => FundingProgrammes::KINDS,
|
||||
return tap(Inertia::render('funding/index', ['items' => $items, 'filters' => $filters, 'categories' => FundingInstitutionClassification::CATEGORIES, 'institutionTypes' => FundingInstitutionClassification::TYPES, 'kinds' => FundingProgrammes::KINDS, 'needs' => FundingInstitutionDetails::NEEDS, 'dossier' => $dossier,
|
||||
'options' => ['territories' => app(InvestmentGeography::class)->publicReferences()->orderBy('name')->get(['id', 'name']),
|
||||
'sectors' => Sector::choices()->whereNotNull('published_at')->orderBy('name')->get(['id', 'name'])], 'management' => $service->access($request->user())])
|
||||
->toResponse($request), static fn (\Symfony\Component\HttpFoundation\Response $response) => $response->headers->set('Cache-Control', 'private, no-store'));
|
||||
@@ -146,14 +181,38 @@ final class FundingProgrammeController extends Controller
|
||||
|
||||
public function show(Request $request, string $id, FundingProgrammes $service): \Symfony\Component\HttpFoundation\Response
|
||||
{
|
||||
$request->validate(['context' => 'nullable|uuid', 'opportunity' => 'nullable|string|max:150', 'need' => ['nullable', Rule::in(array_keys(FundingInstitutionDetails::NEEDS))], 'territory' => 'nullable|uuid', 'category' => 'nullable|string|max:80', 'institution_type' => 'nullable|string|max:80', 'location_mode' => 'nullable|in:physical,coverage']);
|
||||
$dossier = $request->user() && $request->filled('context') ? app(FundingDossierPreparation::class)->context($request->user(), $request->string('context')->toString(), $request->query('opportunity')) : null;
|
||||
if ($dossier && ! $request->filled('territory') && $dossier['territory']) {
|
||||
$request->merge(['territory' => $dossier['territory']]);
|
||||
}
|
||||
$record = $service->published($id);
|
||||
$destinations = [];
|
||||
if ($request->user()) {
|
||||
foreach (app(InvestorPath::class)->contexts($request->user()) as $item) {
|
||||
$case = app(VirtualDesk::class)->case($request->user(), $item['id']);
|
||||
$state = app(InvestorContext::class)->state($case);
|
||||
$choices = $state['opportunity_choices'] ?? [];
|
||||
if ($choices === []) {
|
||||
$destinations[] = ['id' => $item['id'], 'opportunity' => null, 'title' => $item['title']];
|
||||
}
|
||||
foreach ($choices as $choice) {
|
||||
$destinations[] = ['id' => $item['id'], 'opportunity' => $choice['opportunity']['key'], 'title' => $item['title'].' — '.$choice['opportunity']['title']];
|
||||
}
|
||||
}
|
||||
}
|
||||
$saved = $request->user() ? DB::table('funding_programme_interests as i')->join('funding_programme_versions as v', 'v.id', '=', 'i.version_id')
|
||||
->where('i.programme_id', $id)->where('i.user_id', $request->user()->id)->whereNull('i.removed_at')->first(['v.version', 'i.saved_at']) : null;
|
||||
|
||||
return tap(Inertia::render('funding/show', ['record' => $record, 'saved' => $saved, 'authenticated' => $request->user() !== null])
|
||||
return tap(Inertia::render('funding/show', ['record' => $record, 'saved' => $saved, 'authenticated' => $request->user() !== null, 'dossier' => $dossier, 'selection' => [...$request->only(['need', 'territory', 'context', 'opportunity', 'category', 'institution_type', 'location_mode']), 'territory_label' => app(InvestmentGeography::class)->publicReferences()->where('id', $request->query('territory'))->value('name') ?? 'Local por seleccionar'], 'needs' => FundingInstitutionDetails::NEEDS, 'destinations' => $destinations])
|
||||
->toResponse($request), static fn (\Symfony\Component\HttpFoundation\Response $response) => $response->headers->set('Cache-Control', 'private, no-store'));
|
||||
}
|
||||
|
||||
public function dossier(Request $request, string $id, FundingDossierPreparation $service): JsonResponse
|
||||
{
|
||||
return response()->json($service->save($request->user(), $id, $request->all()))->header('Cache-Control', 'private, no-store');
|
||||
}
|
||||
|
||||
public function assess(Request $request, string $id, FundingProgrammes $service): JsonResponse
|
||||
{
|
||||
return response()->json($service->assess($id, $request->all()))->header('Cache-Control', 'private, no-store');
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\InvestmentPreparationWorkspace;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
final class InvestmentPreparationController extends Controller
|
||||
{
|
||||
public function show(Request $request, string $id, InvestmentPreparationWorkspace $workspace): JsonResponse|Response
|
||||
{
|
||||
$request->validate(['opportunity' => 'nullable|string|max:150', 'history_scope' => 'nullable|in:dossier', 'history_before' => 'nullable|integer|min:1']);
|
||||
$data = $workspace->view($request->user(), $id, $request->query('opportunity'), $request->filled('history_before') ? $request->integer('history_before') : null, $request->query('history_scope') === 'dossier');
|
||||
|
||||
return $request->expectsJson() ? response()->json($data) : Inertia::render('investor-path/preparation', ['workspace' => $data]);
|
||||
}
|
||||
|
||||
public function change(Request $request, string $id, InvestmentPreparationWorkspace $workspace): JsonResponse
|
||||
{
|
||||
return response()->json($workspace->change($request->user(), $id, $request->all()));
|
||||
}
|
||||
|
||||
public function preview(Request $request, string $id, InvestmentPreparationWorkspace $workspace): \Illuminate\Http\Response
|
||||
{
|
||||
$request->validate(['opportunity' => 'nullable|string|max:150', 'history_scope' => 'nullable|in:dossier']);
|
||||
$data = $workspace->view($request->user(), $id, $request->query('opportunity'), null, $request->query('history_scope') === 'dossier');
|
||||
|
||||
return response()->view('investments.preparation-index', ['workspace' => $data, 'manifest' => null])->header('Content-Security-Policy', "sandbox; default-src 'none'; style-src 'unsafe-inline'")->header('X-Content-Type-Options', 'nosniff');
|
||||
}
|
||||
|
||||
public function download(Request $request, string $id, string $exportId, InvestmentPreparationWorkspace $workspace): \Illuminate\Http\Response
|
||||
{
|
||||
$request->validate(['opportunity' => 'nullable|string|max:150', 'history_scope' => 'nullable|in:dossier']);
|
||||
$bundle = $workspace->bundle($request->user(), $id, $request->query('opportunity'), $exportId, $request->query('history_scope') === 'dossier');
|
||||
|
||||
return response($bundle['bytes'], 200, ['Content-Type' => 'application/zip', 'Content-Disposition' => 'attachment; filename="preparacao-apiex-'.$id.'.zip"', 'X-Export-Id' => $exportId, 'X-Content-Type-Options' => 'nosniff']);
|
||||
}
|
||||
|
||||
public function export(Request $request, string $id, InvestmentPreparationWorkspace $workspace): \Illuminate\Http\Response
|
||||
{
|
||||
$body = [...$request->all(), 'action' => 'apiex_export'];
|
||||
$state = $workspace->change($request->user(), $id, $body);
|
||||
$bundle = $workspace->bundle($request->user(), $id, $state['opportunity'], $body['operation_key']);
|
||||
|
||||
return response($bundle['bytes'], 200, ['Content-Type' => 'application/zip', 'Content-Disposition' => 'attachment; filename="preparacao-apiex-'.$id.'.zip"', 'X-Export-Id' => $bundle['export_id'], 'X-Content-Type-Options' => 'nosniff']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Services\OpportunityCuration;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class OpportunityCurationController extends Controller
|
||||
{
|
||||
public function show(Request $request, int $id, OpportunityCuration $service): Response
|
||||
{
|
||||
$base = str_starts_with($request->path(), 'minhas-oportunidades') ? '/minhas-oportunidades' : '/management/opportunities';
|
||||
$filters = $request->validate(['page' => 'nullable|integer|min:1|max:1000000']);
|
||||
|
||||
return Inertia::render('management/opportunity-curation', [...$service->state($request->user(), $id, (int) ($filters['page'] ?? 1)), 'basePath' => $base]);
|
||||
}
|
||||
|
||||
public function preview(Request $request, int $id, OpportunityCuration $service): JsonResponse
|
||||
{
|
||||
return response()->json($service->preview($request->user(), $id, $request->all()))->header('Cache-Control', 'private, no-store');
|
||||
}
|
||||
|
||||
public function confirm(Request $request, int $id, OpportunityCuration $service): JsonResponse
|
||||
{
|
||||
return response()->json($service->confirm($request->user(), $id, $request->all()))->header('Cache-Control', 'private, no-store');
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ use App\Models\Sector;
|
||||
use App\Services\InvestmentGeography;
|
||||
use App\Services\OpportunityCanonicalContract;
|
||||
use App\Services\OpportunityCanonicalResponse;
|
||||
use App\Services\OpportunityCuration;
|
||||
use App\Services\OpportunityManagement;
|
||||
use App\Services\OpportunityManagementInstallation;
|
||||
use App\Services\OpportunityMaturity;
|
||||
@@ -130,6 +131,8 @@ class OpportunityManagementController extends Controller
|
||||
$access->authorize($request->user(), 'view', $record);
|
||||
}
|
||||
Inertia::share(['basePath' => str_starts_with($request->path(), 'minhas-oportunidades') ? '/minhas-oportunidades' : '/management/opportunities',
|
||||
'curationHref' => $record && app(OpportunityCuration::class)->allowed($request->user(), $record)
|
||||
? (str_starts_with($request->path(), 'minhas-oportunidades') ? '/minhas-oportunidades/' : '/management/opportunities/').$record->id.'/destaque' : null,
|
||||
'canonicalOptions' => ($record?->owner_user_id !== null || str_starts_with($request->path(), 'minhas-oportunidades')) ? app(OpportunityCanonicalContract::class)->options($request->user(), $record) : null,
|
||||
'workflowState' => $record ? $access->state($record) : null,
|
||||
'profileReauthenticationRequired' => $record && $record->owner_user_id !== null
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Opportunity;
|
||||
use App\Support\PublicSitemapXml;
|
||||
use Generator;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
class PublicSitemapController extends Controller
|
||||
{
|
||||
private const PAGE_SIZE = 1000;
|
||||
|
||||
public function index(PublicSitemapXml $xml): StreamedResponse
|
||||
{
|
||||
$base = $this->base();
|
||||
$pages = (int) ceil(Opportunity::published()->count() / self::PAGE_SIZE);
|
||||
$locations = (static function () use ($base, $pages): Generator {
|
||||
yield $base.'/sitemap/pages.xml';
|
||||
for ($page = 1; $page <= $pages; $page++) {
|
||||
yield $base.'/sitemap/opportunities/'.$page.'.xml';
|
||||
}
|
||||
})();
|
||||
|
||||
return $this->response($xml, $locations, true);
|
||||
}
|
||||
|
||||
public function pages(PublicSitemapXml $xml): StreamedResponse
|
||||
{
|
||||
$base = $this->base();
|
||||
$paths = ['/', '/oportunidades', '/sectores', '/como-investir', '/documentos', '/apoio', '/simulador-fiscal', '/politica-de-privacidade', '/termos-de-uso'];
|
||||
|
||||
return $this->response($xml, array_map(fn ($path) => $base.$path, $paths));
|
||||
}
|
||||
|
||||
public function opportunities(string $page, PublicSitemapXml $xml): StreamedResponse
|
||||
{
|
||||
$number = filter_var($page, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 1000000]]);
|
||||
abort_if($number === false, 404);
|
||||
$base = $this->base();
|
||||
$records = Opportunity::published()->orderBy('id')->offset(($number - 1) * self::PAGE_SIZE)->limit(self::PAGE_SIZE)->get(['slug']);
|
||||
abort_if($records->isEmpty(), 404);
|
||||
|
||||
return $this->response($xml, $records->map(fn ($record) => $base.'/oportunidades/'.rawurlencode($record->slug)));
|
||||
}
|
||||
|
||||
private function base(): string
|
||||
{
|
||||
$url = rtrim((string) config('app.url'), '/');
|
||||
$parts = parse_url($url);
|
||||
abort_unless(filter_var($url, FILTER_VALIDATE_URL) && is_array($parts)
|
||||
&& in_array($parts['scheme'] ?? null, ['http', 'https'], true)
|
||||
&& ! isset($parts['query']) && ! isset($parts['fragment']) && ! isset($parts['user']) && ! isset($parts['pass']), 503);
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/** @param iterable<string> $locations */
|
||||
private function response(PublicSitemapXml $xml, iterable $locations, bool $index = false): StreamedResponse
|
||||
{
|
||||
return response()->stream(static function () use ($xml, $locations, $index): void {
|
||||
foreach ($xml->chunks($locations, $index) as $chunk) {
|
||||
echo $chunk;
|
||||
}
|
||||
}, 200, ['Content-Type' => 'application/xml; charset=UTF-8', 'Cache-Control' => 'no-store', 'X-Content-Type-Options' => 'nosniff']);
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,23 @@ class VirtualDeskController extends Controller
|
||||
}
|
||||
|
||||
public function showCase(Request $request, string $id, VirtualDesk $desk): JsonResponse|Response
|
||||
{
|
||||
$data = $this->caseViewData($request, $id, $desk);
|
||||
|
||||
return $request->expectsJson() ? response()->json($data) : Inertia::render('desk/case', $data);
|
||||
}
|
||||
|
||||
public function preDossier(Request $request, string $id, VirtualDesk $desk): \Illuminate\Http\Response
|
||||
{
|
||||
return response()->view('desk.pre-dossier', $this->caseViewData($request, $id, $desk), 200, [
|
||||
'Cache-Control' => 'private, no-store', 'X-Content-Type-Options' => 'nosniff',
|
||||
]);
|
||||
}
|
||||
|
||||
/** Same authorised projection for the workspace and its preparation document.
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function caseViewData(Request $request, string $id, VirtualDesk $desk): array
|
||||
{
|
||||
$case = $desk->case($request->user(), $id);
|
||||
$owner = $desk->owns($request->user(), $case, false);
|
||||
@@ -98,7 +115,7 @@ class VirtualDeskController extends Controller
|
||||
'links' => DB::table('desk_institutional_links')->get()->map(fn ($link) => [...(array) $link, 'monitor' => app(InstitutionalLinkMonitor::class)->status($link)]), 'source' => config('virtual-desk.source'), 'support' => config('portal.support'),
|
||||
'guidance' => app(InvestorGuidance::class)->installed() ? app(InvestorGuidance::class)->view($request->user(), $id) : null];
|
||||
|
||||
return $request->expectsJson() ? response()->json($data) : Inertia::render('desk/case', $data);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/** @return Collection<int,\stdClass> */
|
||||
|
||||
Reference in New Issue
Block a user