From 3395424d824851496ca5c97a7f814f6ae30d5209 Mon Sep 17 00:00:00 2001 From: nala Date: Wed, 30 Sep 2026 12:59:28 +0200 Subject: [PATCH] resolucao dos inputs das provincias, distritos, localidades --- .github/workflows/tests.yml | 5 + app/Http/Controllers/EducationController.php | 5 +- .../EducationInvestmentController.php | 11 +++ .../InvestmentDiscoveryController.php | 2 +- .../remediation-execution-2026-09-30.md | 96 +++++++++++++++++++ docs/investmoz/requirements-audit.md | 22 ++++- .../js/components/investment-territory.tsx | 95 +++++++++++++++--- resources/js/pages/PortalInvestidor.tsx | 30 +----- resources/js/pages/ai/index.tsx | 9 +- resources/js/pages/education/analysis.tsx | 47 ++++++++- resources/js/pages/education/index.tsx | 61 +++++++----- resources/js/pages/investment/catalogue.tsx | 14 +-- resources/js/pages/portal/services.tsx | 50 +++++++--- 13 files changed, 355 insertions(+), 92 deletions(-) create mode 100644 docs/investmoz/remediation-execution-2026-09-30.md diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c879b56a..972a4762 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -66,5 +66,10 @@ jobs: - name: Run CI Checks run: composer ci:check + - name: Validate PostgreSQL/PostGIS schema lifecycle + env: + INVESTMOZ_SCHEMA_TESTS: '1' + run: php vendor/bin/phpunit --configuration=phpunit.investmoz.xml --do-not-cache-result + - name: Test frontend API contracts run: node --test tests/frontend/portal-api.test.mjs diff --git a/app/Http/Controllers/EducationController.php b/app/Http/Controllers/EducationController.php index 49626c10..a335c577 100644 --- a/app/Http/Controllers/EducationController.php +++ b/app/Http/Controllers/EducationController.php @@ -62,7 +62,9 @@ class EducationController extends Controller 'providers' => TrainingProvider::where('status', '!=', 'inactive')->orderBy('name')->get(['id', 'name']), 'courses' => Course::where('status', '!=', 'retired')->whereIn('provider_id', TrainingProvider::where('status', '!=', 'inactive')->select('id'))->orderBy('title')->get(['id', 'title', 'provider_id']), 'projects' => Project::orderBy('name')->get(['id', 'code', 'name']), - 'territories' => DB::table('geo_locations')->whereNull('deleted_at')->orderBy('name')->get(['id', 'name', 'code']), + 'territories' => DB::table('geo_locations')->whereNull('deleted_at') + ->whereIn('location_type', ['province', 'district', 'admin_post', 'locality']) + ->orderBy('name')->get(['id', 'name', 'parent_id', 'location_type']), 'today' => now()->toDateString(), ]); } @@ -148,6 +150,7 @@ class EducationController extends Controller return Inertia::render('education/analysis', ['demand' => $demand, 'candidates' => $candidates, 'proposal' => EducationInvestmentProposal::where('labour_demand_id', $id)->first(), 'sectors' => Sector::orderBy('name')->get(['id', 'name']), + 'territoryPath' => app(InvestmentGeography::class)->path($demand->getAttribute('geo_location_id'), true), 'eligible' => $eligible, 'gap' => $calculator->observed((int) $demand->getAttribute('quantity'), null, $eligible, $current)]); } } diff --git a/app/Http/Controllers/EducationInvestmentController.php b/app/Http/Controllers/EducationInvestmentController.php index 001f85a8..bf326b90 100644 --- a/app/Http/Controllers/EducationInvestmentController.php +++ b/app/Http/Controllers/EducationInvestmentController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers; use App\Models\EducationInvestmentProposal; use App\Models\LabourDemand; use App\Models\Opportunity; +use App\Services\InvestmentGeography; use App\Services\PortalAudit; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -27,6 +28,10 @@ class EducationInvestmentController extends Controller 'risks' => ['required', 'string', 'min:10', 'max:5000'], 'source' => ['required', 'string', 'min:10', 'max:5000'], 'currency' => ['required', Rule::in(['MZN', 'USD', 'EUR', 'ZAR'])], + 'province_id' => ['required', 'uuid'], + 'district_id' => ['nullable', 'uuid'], + 'admin_post_id' => ['nullable', 'uuid'], + 'locality_id' => ['nullable', 'uuid'], ]); DB::transaction(function () use ($request, $id, $data): void { @@ -38,6 +43,10 @@ class EducationInvestmentController extends Controller if (EducationInvestmentProposal::withTrashed()->where('labour_demand_id', $id)->exists()) { return; } + $territory = app(InvestmentGeography::class)->selection($data, true); + $province = $territory[0]; + $district = $territory[1] ?? null; + $primaryTerritory = $territory[array_key_last($territory)]; $reference = 'EDU-'.Str::uuid(); $opportunity = new Opportunity; PortalAudit::save($opportunity, [ @@ -45,6 +54,8 @@ class EducationInvestmentController extends Controller 'slug' => strtolower($reference), 'title' => $data['title'], 'summary' => 'Proposta educativa em preparação. Lacuna e viabilidade pendentes de validação institucional.', 'description' => $data['intervention'], 'estimated_investment_currency' => $data['currency'], + 'province' => $province->name, 'district' => $district?->name, + 'primary_territory_id' => $primaryTerritory->id, 'territory_version' => 1, 'status' => 'draft', 'is_featured' => false, ], $request->user()->id); PortalAudit::save(new EducationInvestmentProposal, [ diff --git a/app/Http/Controllers/InvestmentDiscoveryController.php b/app/Http/Controllers/InvestmentDiscoveryController.php index b8c87903..c528e2c1 100644 --- a/app/Http/Controllers/InvestmentDiscoveryController.php +++ b/app/Http/Controllers/InvestmentDiscoveryController.php @@ -24,7 +24,7 @@ class InvestmentDiscoveryController extends Controller $filters['education'] = true; } - return Inertia::render('investment/catalogue', ['filters' => $filters, 'references' => $catalogue->references(), + return Inertia::render('investment/catalogue', ['filters' => $filters, 'catalogueUrl' => $request->routeIs('investment.education') ? '/educacao' : '/oportunidades', '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') : [], diff --git a/docs/investmoz/remediation-execution-2026-09-30.md b/docs/investmoz/remediation-execution-2026-09-30.md new file mode 100644 index 00000000..52372998 --- /dev/null +++ b/docs/investmoz/remediation-execution-2026-09-30.md @@ -0,0 +1,96 @@ +# Execução do plano de remediação — 30/09/2026 + +## Resumo + +Linha de base: branch `main`, commit `3dc7c5c649d433527e2e261e33b8d45eb9e71bd8`, +árvore limpa antes desta remediação. A auditoria de 25/09 tinha uma conclusão de +CI desactualizada: o workflow actual já usa PHP 8.5 e disponibiliza PostgreSQL / +PostGIS 17-3.5. A lacuna reproduzível era o teste protegido de ciclo de esquema +não ser invocado pelo workflow. + +Foi adicionado ao CI um passo dedicado para executar +`tests/InvestMoz/SchemaLifecycleTest.php` contra o serviço PostgreSQL efémero do +job. O teste exige explicitamente `INVESTMOZ_SCHEMA_TESTS=1`, valida que o nome +da base termina em `_test` e recusa configuração em cache antes de executar +`migrate:fresh`. Não houve ligação a produção nem alteração de bases de dados +nesta sessão. + +**Decisão global: No-Go.** O reforço do CI não fecha as lacunas funcionais e +operacionais da matriz de requisitos. + +## Salvaguardas e ambiente + +- Git confirmou `main` e a árvore limpa no início. Não criei commit. +- Versões locais observadas: PHP 8.5.0, Laravel 13.30.1, Node 24.20.0 e npm 11.19.0. +- O workflow executa num job Ubuntu e num serviço PostgreSQL/PostGIS efémero, + com base `investmoz_test` e credenciais exclusivas do job. +- Não há Docker, `psql` ou `pg_isready` instalados localmente. Não foi possível + confirmar ou criar uma instância local descartável. +- Nenhum segredo foi lido ou incluído neste documento. Nenhuma base de dados, + credencial ou serviço externo foi alterado. + +## Matriz de remediação + +| ID | Domínio | Severidade / causa-raiz | Acção correctiva | Responsável sugerido / dependências | Esforço inicial* | Evidência e critério de aceitação | Estado | +| ---------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------- | +| REM-CI-01 | CI / esquema | Alta: o teste de ciclo do esquema era ignorado sem `INVESTMOZ_SCHEMA_TESTS=1`. | Adicionado passo explícito ao workflow, dirigido ao serviço efémero PostGIS. | DevOps; depende de execução GitHub Actions. | Pequeno, 1–2 horas | Job GitHub Actions verde; teste dedicado sem skip e com resultado aprovado. | Implementado; execução remota Não verificada. | +| REM-DOC-01 | Rastreabilidade | Baixa: auditoria de 25/09 afirmava PHP 8.3 e ausência de PostgreSQL no workflow actual. | Corrigida a conclusão e identificada a data histórica das outras classificações. | Manutenção técnica; sem dependência externa. | Pequeno, menos de 1 hora | Documento corresponde ao YAML actual; diferenças funcionais continuam explicitamente abertas. | Corrigido por inspecção estática. | +| REM-GEO-01 | GIS / dados oficiais | Crítica: testes de esquema não provam importação oficial, desempenho representativo ou permissões no ambiente-alvo. | Validar por domínio sem tocar dados até existir fonte e ambiente aprovados. | Responsáveis GIS/dados + backend; depende de dados licenciados e staging descartável. | Grande, 1–2 semanas (estimativa) | Origem/licença, SRID, reconciliação, testes espaciais, perfis e tempos reproduzíveis. | Pendente; Não verificado. | +| REM-SEC-01 | Segurança / privacidade | Crítica: sem evidência nesta sessão de pentest, revisão completa de perfis, retenção e base legal. | Revisão de segurança e validação jurídica com responsáveis competentes. | AppSec, privacidade e assessoria jurídica; depende de requisitos e perfis aprovados. | Grande, 1–2 semanas (estimativa) | Matriz de permissões aprovada, achados tratados e requisitos legais referenciados. | Pendente; Não verificado. | +| REM-OPS-01 | Operação / recuperação | Alta: deploy descrito, mas backup/restauro, monitorização e resposta não demonstrados no alvo. | Ensaio de restauro isolado e validação operacional. | SRE/infraestrutura; depende do ambiente-alvo e política de backup. | Médio/grande, 3–5 dias (estimativa) | Restore reproduzível, health checks, alertas e runbook exercitado. | Pendente; Não verificado. | +| REM-BIZ-01 | Âmbito funcional | Crítica: matriz de 25/09 identifica requisitos de CRM, negócios, jornadas, integrações e outros domínios incompletos. | Priorizar com proprietário funcional; implementar por domínio com critérios aprovados. | Proprietário do produto + líderes de domínio; depende de MVP aprovado. | Extra grande, por fases (estimativa não aprovada) | Fluxos críticos aprovados ponta a ponta e rastreados a requisitos e testes. | Pendente; Não conforme para aceite integral. | + +\* Estimativas de planeamento preliminares, não aprovadas; devem ser revistas pelos responsáveis após decomposição do trabalho. + +## Alterações realizadas + +- `.github/workflows/tests.yml`: acrescentado o passo “Validate PostgreSQL/PostGIS + schema lifecycle”, com `INVESTMOZ_SCHEMA_TESTS=1` e a configuração PHPUnit + dedicada. O serviço já existente no workflow é efémero e tem nome de base + terminado em `_test`. +- `docs/investmoz/requirements-audit.md`: corrigida a afirmação obsoleta sobre + versão PHP e ausência de serviço PostGIS; registado que o resultado remoto + continua por verificar e que a decisão funcional global não muda. +- `docs/investmoz/remediation-execution-2026-09-30.md`: criado este registo, + incluindo matriz, salvaguardas e evidência pendente. + +Não foram removidos ficheiros, requisitos, testes ou controlos. Não foram +alteradas migrations, dados, dependências, contratos de API ou credenciais. + +## Verificações desta sessão + +| Verificação | Comando / método | Resultado | +| ----------------------------------- | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Branch, commit e estado inicial | `git status --short --branch`; `git rev-parse HEAD` | `main`, commit `3dc7c5c…`, limpo antes das alterações. | +| Versões locais | `php -v`; `php artisan --version`; `node.exe --version`; `npm.cmd --version` | PHP 8.5.0, Laravel 13.30.1, Node 24.20.0, npm 11.19.0. | +| Workflow actual | Inspecção de `.github/workflows/tests.yml` | PHP 8.5, serviço `postgis/postgis:17-3.5`, credenciais limitadas ao job. | +| Protecção do teste | Inspecção de `SchemaLifecycleTest.php` e `phpunit.investmoz.xml` | Teste exige opt-in, ambiente `testing`, driver pgsql, nome `_test` e ausência de config cache antes de `migrate:fresh`. | +| Testes API frontend | `node.exe --test tests/frontend/portal-api.test.mjs` | Aprovado: 13 testes, 0 falhas. | +| TypeScript | `npm.cmd run types:check` | Aprovado (`tsc --noEmit`). | +| PHPStan | `php vendor/bin/phpstan analyse --no-progress --debug --memory-limit=512M` | Aprovado: 0 erros. A primeira execução falhou no limite local de 128 MB; repetida sem workers paralelos com 512 MB. | +| Composer | `composer validate --no-check-publish` | Aprovado. | +| Formatação dos ficheiros alterados | `vp check --no-lint .github/workflows/tests.yml docs/investmoz/remediation-execution-2026-09-30.md` | Aprovada para o documento; o verificador não reporta YAML como ficheiro formatável. | +| Formatação PHP global | `php vendor/bin/pint --test` | Falhou em 2 ficheiros PHP preexistentes não alterados: migration `2026_09_28_160000_investment_primary_territory.php` e `tools/test-mne-integrity.php`. | +| Check frontend global | `npm.cmd run check` | Falhou por 103 ficheiros fora do padrão no primeiro scan; não apliquei autofix em massa. TypeScript passou separadamente. | +| Integridade do diff | `git diff --check` | Aprovado; sem whitespace errors. | +| Protecção local do teste de esquema | `php vendor/bin/phpunit --configuration=phpunit.investmoz.xml --do-not-cache-result` sem opt-in | PHPUnit terminou com 1 teste ignorado pela guarda, 0 asserções; não ligou à base de dados. | +| Ciclo de esquema PostgreSQL/PostGIS | Não executado localmente | Sem servidor/cliente PostgreSQL local; ligação do PHPUnit não pôde ser comprovada como descartável. A execução real foi delegada ao serviço efémero no CI. | +| Sintaxe YAML do workflow | Não verificada por parser dedicado | Python, Ruby, Docker e actionlint não estão disponíveis; a inspecção estática não substitui validação pelo GitHub Actions. | +| Resultado GitHub Actions | Não disponível nesta sessão | Não afirmar aprovação do pipeline até execução do workflow alterado. | + +## Pendências, reversão e aceitação + +Para reverter apenas esta remediação, remover o passo dedicado do workflow e +restaurar o texto histórico em `requirements-audit.md`; manter o registo desta +execução como evidência histórica, assinalando a reversão. A alteração do CI só +afecta o job efémero e não requer reversão de dados. + +O critério de encerramento de REM-CI-01 é um run verde do workflow no commit que +contém a alteração, com o teste dedicado efectivamente executado. O critério de +Go global continua a exigir os gates funcionais, geográficos, de segurança, +integridade, desempenho, operação, legalidade e recuperação; esses gates não +foram satisfeitos nem reavaliados nesta sessão. + +Nenhuma evidência foi fabricada. Os resultados históricos permanecem atribuídos +às respectivas datas e ambientes; tudo o que não foi reproduzido está marcado +como Não verificado ou pendente. diff --git a/docs/investmoz/requirements-audit.md b/docs/investmoz/requirements-audit.md index d790828c..a65f2af4 100644 --- a/docs/investmoz/requirements-audit.md +++ b/docs/investmoz/requirements-audit.md @@ -98,11 +98,14 @@ O próprio DOCX contém uma frase auto-contraditória na precedência de A.31 (parágrafo 6932); conservar a reconciliação aprovada e resolver a documentação, sem executar cegamente os blocos históricos. -**Falha concreta de CI:** `.github/workflows/tests.yml` configura PHP 8.3, enquanto -vendor/composer/platform_check.php exige >=8.4.1. Também não provisiona serviço -PostgreSQL/PostGIS, embora os testes e migrations precisem dele. Existe workflow, -mas a sua execução bem sucedida com o projecto actual não está comprovada. Não foi -executada nem alterada nesta auditoria. +**Actualização em 30/09/2026:** a observação de CI acima, registada na auditoria de +25/09, ficou desactualizada. O workflow actual configura PHP 8.5 e provisiona um +serviço `postgis/postgis:17-3.5` para o job. A lacuna reproduzível era outra: o +teste `SchemaLifecycleTest` exige `INVESTMOZ_SCHEMA_TESTS=1`, mas o workflow não +activava esse teste destrutivo protegido. O workflow foi actualizado para o +executar explicitamente contra a base efémera do serviço PostgreSQL do job. A +alteração ainda precisa de uma execução aprovada do GitHub Actions; o resultado +do pipeline permanece Não verificado até essa execução. `decisions.md` continua útil, mas não está totalmente actualizado: P-14 recebeu implementação parcial para o histórico da gestão. Isso não conclui a auditoria @@ -126,3 +129,12 @@ transversal nem resolve automaticamente as restantes decisões. staging e validação operacional antes da publicação integral. Esta auditoria não implementou funcionalidades adicionais nem alterou a base de dados. + +## Estado dos requisitos nesta revisão — 30/09/2026 + +As classificações funcionais e de domínio acima foram recolhidas em 25/09 e são +uma fotografia histórica, não uma revalidação de todos os módulos no commit +`3dc7c5c649d433527e2e261e33b8d45eb9e71bd8`. A execução autorizada e as evidências +mais recentes estão em [remediation-execution-2026-09-30.md](remediation-execution-2026-09-30.md). +As lacunas de requisitos de negócio continuam abertas; a correcção do workflow +não altera a decisão No-Go para o aceite global. diff --git a/resources/js/components/investment-territory.tsx b/resources/js/components/investment-territory.tsx index d5717cca..3061cfac 100644 --- a/resources/js/components/investment-territory.tsx +++ b/resources/js/components/investment-territory.tsx @@ -13,9 +13,86 @@ type Option = TerritoryOption; const levels = ["province", "district", "admin_post", "locality"]; const labels = ["Província", "Distrito", "Posto administrativo", "Localidade"]; const control = "w-full rounded border bg-background p-2 text-foreground"; +const emptyTerritoryPath: TerritoryOption[] = []; + +export function ProvinceSelect({ + value, + onChange, + endpoint = "/api/v1/investment/territory-options", + placeholder = "Seleccione uma província", + className = control, + label = "Província", + required = false, +}: { + value: string; + onChange: (value: string) => void; + endpoint?: string; + placeholder?: string; + className?: string; + label?: string; + required?: boolean; +}) { + const id = useId(); + const [options, setOptions] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(""); + const [retry, setRetry] = useState(0); + + useEffect(() => { + const controller = new AbortController(); + setLoading(true); + setError(""); + fetch(`${endpoint}?type=province&page=1`, { + signal: controller.signal, + headers: { Accept: "application/json" }, + }) + .then(async (response) => { + if (!response.ok) throw new Error("Não foi possível carregar as províncias."); + return response.json(); + }) + .then((data) => setOptions(data.data)) + .catch((cause) => { + if (!controller.signal.aborted) + setError(cause instanceof Error ? cause.message : "Erro de ligação."); + }) + .finally(() => { + if (!controller.signal.aborted) setLoading(false); + }); + return () => controller.abort(); + }, [endpoint, retry]); + + return ( + <> + + {error && ( + + {error}{" "} + + + )} + + ); +} export function TerritoryFields({ - initial = [], + initial = emptyTerritoryPath, onChange, endpoint = "/api/v1/investment/territory-options", }: { @@ -24,6 +101,7 @@ export function TerritoryFields({ endpoint?: string; }) { const [path, setPath] = useState(initial); + useEffect(() => setPath(initial), [initial]); return (
{(["province_id", "district_id", "admin_post_id", "locality_id"] as const).map( @@ -64,7 +142,6 @@ export function TerritoryLevel({ fieldError?: string; }) { const id = useId(); - const [q, setQ] = useState(""); const [page, setPage] = useState(1); const [options, setOptions] = useState([]); const [more, setMore] = useState(false); @@ -85,7 +162,6 @@ export function TerritoryLevel({ const params = new URLSearchParams({ type: levels[index], parent_id: parent, - q, page: String(page), }); const response = await fetch(`${endpoint}?${params}`, { @@ -108,23 +184,12 @@ export function TerritoryLevel({ clearTimeout(timer); controller.abort(); }; - }, [index, parent, q, page, enabled, endpoint, retry]); + }, [index, parent, page, enabled, endpoint, retry]); return (
- { - setQ(e.target.value); - setPage(1); - }} - placeholder="Pesquisar por código ou nome" - /> - + /> query.setData("province", e.target.value)} + onChange={(province) => query.setData("province", province)} + className={control} + placeholder={t("Seleccione uma província", "Select a province")} + label={t("Província declarada", "Declared province")} /> diff --git a/resources/js/pages/education/analysis.tsx b/resources/js/pages/education/analysis.tsx index 7859a352..2b9151f7 100644 --- a/resources/js/pages/education/analysis.tsx +++ b/resources/js/pages/education/analysis.tsx @@ -1,5 +1,7 @@ import { Link, useForm } from "@inertiajs/react"; +import { useState } from "react"; import { Management, Field, control, button } from "../management/shared"; +import { TerritoryFields, type TerritoryOption } from "@/components/investment-territory"; export default function Analysis({ demand, candidates, @@ -7,8 +9,15 @@ export default function Analysis({ gap, proposal, sectors, + territoryPath, }: { - demand: { id: string; version_no: number; quantity: number; qualification: string }; + demand: { + id: string; + version_no: number; + quantity: number; + qualification: string; + geo_location_id: string; + }; candidates: { id: string; quantity: number; @@ -29,7 +38,9 @@ export default function Analysis({ source: string; } | null; sectors: { id: number; name: string }[]; + territoryPath: TerritoryOption[]; }) { + const [selectedTerritory, setSelectedTerritory] = useState(territoryPath); const form = useForm({ demand_version: demand.version_no, sector_id: "", @@ -41,12 +52,19 @@ export default function Analysis({ risks: "", source: "", currency: "MZN", + province_id: territoryPath[0]?.id ?? "", + district_id: territoryPath[1]?.id ?? "", + admin_post_id: territoryPath[2]?.id ?? "", + locality_id: territoryPath[3]?.id ?? "", }); return (

Procura: {demand.quantity} trabalhadores · {demand.qualification}

+

+ Localização da procura: {territoryPath.map((territory) => territory.name).join(" · ") || "Não indicada"} +

{gap.state === "expired" ? "Fonte expirada. Actualize a declaração antes de analisar." @@ -120,6 +138,33 @@ export default function Analysis({ form.post(`/management/education/demands/${demand.id}/investment`); }} > +

+

Localização da oportunidade educativa

+

+ Inicialmente preenchida com a localização da procura. Confirme ou ajuste cada nível territorial. +

+ { + setSelectedTerritory(path); + form.setData((data) => ({ + ...data, + province_id: path[0]?.id ?? "", + district_id: path[1]?.id ?? "", + admin_post_id: path[2]?.id ?? "", + locality_id: path[3]?.id ?? "", + })); + }} + /> + {Object.entries(form.errors) + .filter(([key]) => key.endsWith("_id")) + .map(([key, error]) => ( +

+ {error} +

+ ))} +
form.setData("geo_location_id", v)} - /> +
+ + form.setData("geo_location_id", path[path.length - 1]?.id ?? "") + } + /> +
{demand && ( Fonte expirada - - - +
+ + search.setData("geo_location_id", path[path.length - 1]?.id ?? "") + } + /> +
- + Limpar filtros
@@ -475,7 +477,7 @@ export default function Catalogue({ onClick={() => { const params = new URLSearchParams(query()); params.set("view", value); - router.get(`/oportunidades?${params}`, {}, { preserveScroll: true }); + router.get(`${catalogueUrl}?${params}`, {}, { preserveScroll: true }); }} > {["Grelha", "Lista", "Mapa", "Lista e mapa"][i]} diff --git a/resources/js/pages/portal/services.tsx b/resources/js/pages/portal/services.tsx index 4634e22b..4180ec53 100644 --- a/resources/js/pages/portal/services.tsx +++ b/resources/js/pages/portal/services.tsx @@ -2,6 +2,7 @@ import { Head, Link, useForm, usePage } from "@inertiajs/react"; import { useState } from "react"; import { PortalMenu } from "@/layouts/portal-navigation"; import { newSubmissionKey } from "@/lib/portal-api"; +import { ProvinceSelect } from "@/components/investment-territory"; const input = "w-full rounded-lg border border-slate-300 bg-white p-3 text-slate-900"; const button = @@ -249,14 +250,25 @@ function PartnershipForm() { seeking: "Parceiro ou capacidade que procura", }[key] } -