Present financial institutions with professional investor-facing descriptions and products
tests / ci (push) Canceled after 0s
tests / ci (push) Canceled after 0s
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
## Comunicação pública — orientação expressa do proprietário,11/11/2026
|
||||
|
||||
Apresentar conteúdos públicos com linguagem profissional,clara e apelativa,centrada na entidade,oportunidade,produtos e serviços úteis ao investidor. Não usar descrições de importação,cadastro,fichas,ficheiros ou documentos geradores como apresentação comercial. Preservar as fontes,versões e rastreabilidade no cadastro/editor;ligações institucionais e datas úteis podem ser consultadas separadamente. Não inventar ofertas,condições,disponibilidade,cobertura ou garantias para preencher lacunas. Esta orientação aplica-se transversalmente aos conteúdos do portal.
|
||||
|
||||
## Simulação de viabilidade — clarificação expressa do proprietário,11/11/2026
|
||||
|
||||
O InvesteMoz simula impostos para avaliar a viabilidade do negócio; não actua como entidade que executa pagamentos. Apresentar PPC/PEC e distribuição por meses como estimativas fiscais e projecções de tesouraria,com fontes e pressupostos. Montantes já pagos,quando fornecidos,são dados documentais para o cálculo. Não confundir uma hipótese de caixa com pagamento efectuado,declaração submetida ou cumprimento fiscal certificado. Preservar os cálculos,históricos e dados aceites; não criar serviços de cobrança/execução de pagamentos como requisito de conclusão deste simulador.
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
## 11/11/2026 — apresentação pública profissional do directório financeiro em QA
|
||||
|
||||
**Pedido do proprietário:** descrições profissionais e apelativas,com descrição/produtos/serviços úteis ao investidor,sem texto de fichas,importação ou documento gerador;orientação transversal registada emAGENTS. O directório e o detalhe passam a uma apresentação editorial comum:reconhece exclusivamente o resumo genérico exacto da importação e usa a natureza publicada da entidade;descrições editoriais específicas são preservadas e limpas da fórmula genérica final. Produtos identificados são apresentados pelos nomes/instrumentos existentes;os sete catálogos específicos disponíveis não são extrapolados para outras entidades. Nas restantes,apresentam-se áreas de serviço conhecidas ou contacto para conhecer soluções,sem inventar produtos/condições.
|
||||
|
||||
Retirados da apresentação pública nomes de ficheiros,hashes,linhas de importação,códigos de sucessora e descrições de registo interno. Ligações institucionais e datas úteis continuam acessíveis;metadados e fontes permanecem intactos no cadastro/editor. Instituições históricas/fundidas continuam identificadas como tal. Instituições sem chamada não mostram uma secção vazia de candidatura. Textos da selecção guardada e chamadas públicas dePorque investir/Serviços ajustados à linguagem do investidor. Fluxos de interesse,dossier,edição,revisão/publicação e filtros preservados.
|
||||
|
||||
**QA:** nove verificações afectadas aprovadas,três novas incluindo270referências fornecidas,descrições específicas preservadas,produtos não inventados e renderização dos componentes públicos reais de lista/detalhe. Sem escrita em dados activos ou nova publicação do lote;é uma projecção editorial dos dados já publicados. Evidência `outputs/funding-public-editorial-20261111/verification.json` e quatroHTMLs;isto não constitui conferência visual no navegador. Tipos/lint/compilação aprovados;pacote Git com aplicação/reversão conferidas no fecho.
|
||||
|
||||
**Estado:** implementado em QA,não instalado;homologação mantém `8f295f05` e270instituições publicadas. Restam instalação/visual e enriquecimento de produtos específicos quando houver informação disponível. O saldo dos outros módulos e resíduos fiscais permanece inalterado. Não é necessário seleccionar ou publicar novamente as270instituições para esta alteração de apresentação.
|
||||
|
||||
## 11/11/2026 — rectificação do volume PEC instalada em8f295f05
|
||||
|
||||
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às11:03:24Africa/Johannesburg(09:03:24UTC): `8f295f05510c786afeb8ea1dfa692e08d90a0d30`. No simulador fiscal,L8,**Rectificar volume na simulação PEC** conserva o valor original e o corrigido,com escolha explícita/fonte e efeito na estimativa apresentados no relatório. A hipótese de tesouraria anterior exige reconciliação se o total mudar;nenhum comprovativo pago,dedução ou saldo oficial é duplicado. Não executa pagamentos nem submete declaração rectificativa.
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
## 11/11/2026 — apresentação pública profissional do directório financeiro em QA
|
||||
|
||||
**Pedido do proprietário:** descrições profissionais e apelativas,com descrição/produtos/serviços úteis ao investidor,sem texto de fichas,importação ou documento gerador;orientação transversal registada emAGENTS. O directório e o detalhe passam a uma apresentação editorial comum:reconhece exclusivamente o resumo genérico exacto da importação e usa a natureza publicada da entidade;descrições editoriais específicas são preservadas e limpas da fórmula genérica final. Produtos identificados são apresentados pelos nomes/instrumentos existentes;os sete catálogos específicos disponíveis não são extrapolados para outras entidades. Nas restantes,apresentam-se áreas de serviço conhecidas ou contacto para conhecer soluções,sem inventar produtos/condições.
|
||||
|
||||
Retirados da apresentação pública nomes de ficheiros,hashes,linhas de importação,códigos de sucessora e descrições de registo interno. Ligações institucionais e datas úteis continuam acessíveis;metadados e fontes permanecem intactos no cadastro/editor. Instituições históricas/fundidas continuam identificadas como tal. Instituições sem chamada não mostram uma secção vazia de candidatura. Textos da selecção guardada e chamadas públicas dePorque investir/Serviços ajustados à linguagem do investidor. Fluxos de interesse,dossier,edição,revisão/publicação e filtros preservados.
|
||||
|
||||
**QA:** nove verificações afectadas aprovadas,três novas incluindo270referências fornecidas,descrições específicas preservadas,produtos não inventados e renderização dos componentes públicos reais de lista/detalhe. Sem escrita em dados activos ou nova publicação do lote;é uma projecção editorial dos dados já publicados. Evidência `outputs/funding-public-editorial-20261111/verification.json` e quatroHTMLs;isto não constitui conferência visual no navegador. Tipos/lint/compilação aprovados;pacote Git com aplicação/reversão conferidas no fecho.
|
||||
|
||||
**Estado:** implementado em QA,não instalado;homologação mantém `8f295f05` e270instituições publicadas. Restam instalação/visual e enriquecimento de produtos específicos quando houver informação disponível. O saldo dos outros módulos e resíduos fiscais permanece inalterado. Não é necessário seleccionar ou publicar novamente as270instituições para esta alteração de apresentação.
|
||||
|
||||
## 11/11/2026 — rectificação do volume PEC instalada em8f295f05
|
||||
|
||||
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às11:03:24Africa/Johannesburg(09:03:24UTC): `8f295f05510c786afeb8ea1dfa692e08d90a0d30`. No simulador fiscal,L8,**Rectificar volume na simulação PEC** conserva o valor original e o corrigido,com escolha explícita/fonte e efeito na estimativa apresentados no relatório. A hipótese de tesouraria anterior exige reconciliação se o total mudar;nenhum comprovativo pago,dedução ou saldo oficial é duplicado. Não executa pagamentos nem submete declaração rectificativa.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,13 @@
|
||||
## 11/11/2026 — apresentação pública profissional do directório financeiro em QA
|
||||
|
||||
**Pedido do proprietário:** descrições profissionais e apelativas,com descrição/produtos/serviços úteis ao investidor,sem texto de fichas,importação ou documento gerador;orientação transversal registada emAGENTS. O directório e o detalhe passam a uma apresentação editorial comum:reconhece exclusivamente o resumo genérico exacto da importação e usa a natureza publicada da entidade;descrições editoriais específicas são preservadas e limpas da fórmula genérica final. Produtos identificados são apresentados pelos nomes/instrumentos existentes;os sete catálogos específicos disponíveis não são extrapolados para outras entidades. Nas restantes,apresentam-se áreas de serviço conhecidas ou contacto para conhecer soluções,sem inventar produtos/condições.
|
||||
|
||||
Retirados da apresentação pública nomes de ficheiros,hashes,linhas de importação,códigos de sucessora e descrições de registo interno. Ligações institucionais e datas úteis continuam acessíveis;metadados e fontes permanecem intactos no cadastro/editor. Instituições históricas/fundidas continuam identificadas como tal. Instituições sem chamada não mostram uma secção vazia de candidatura. Textos da selecção guardada e chamadas públicas dePorque investir/Serviços ajustados à linguagem do investidor. Fluxos de interesse,dossier,edição,revisão/publicação e filtros preservados.
|
||||
|
||||
**QA:** nove verificações afectadas aprovadas,três novas incluindo270referências fornecidas,descrições específicas preservadas,produtos não inventados e renderização dos componentes públicos reais de lista/detalhe. Sem escrita em dados activos ou nova publicação do lote;é uma projecção editorial dos dados já publicados. Evidência `outputs/funding-public-editorial-20261111/verification.json` e quatroHTMLs;isto não constitui conferência visual no navegador. Tipos/lint/compilação aprovados;pacote Git com aplicação/reversão conferidas no fecho.
|
||||
|
||||
**Estado:** implementado em QA,não instalado;homologação mantém `8f295f05` e270instituições publicadas. Restam instalação/visual e enriquecimento de produtos específicos quando houver informação disponível. O saldo dos outros módulos e resíduos fiscais permanece inalterado. Não é necessário seleccionar ou publicar novamente as270instituições para esta alteração de apresentação.
|
||||
|
||||
## 11/11/2026 — rectificação do volume PEC instalada em8f295f05
|
||||
|
||||
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às11:03:24Africa/Johannesburg(09:03:24UTC): `8f295f05510c786afeb8ea1dfa692e08d90a0d30`. No simulador fiscal,L8,**Rectificar volume na simulação PEC** conserva o valor original e o corrigido,com escolha explícita/fonte e efeito na estimativa apresentados no relatório. A hipótese de tesouraria anterior exige reconciliação se o total mudar;nenhum comprovativo pago,dedução ou saldo oficial é duplicado. Não executa pagamentos nem submete declaração rectificativa.
|
||||
|
||||
@@ -69,45 +69,7 @@ export function ClassificationSummary({ value }: { value: Classification }) {
|
||||
.map((k) => financialTypes[k] ?? k)
|
||||
.join(' · ') || 'Por classificar'}
|
||||
</p>
|
||||
<p>
|
||||
Situação segundo a fonte:{' '}
|
||||
{value.institution_status === 'merged'
|
||||
? 'Fundida/extinta segundo o documento; não apresentar como instituição activa'
|
||||
: value.institution_status === 'historical'
|
||||
? 'Registo histórico'
|
||||
: value.institution_status === 'unknown'
|
||||
? 'Situação actual por confirmar'
|
||||
: 'Reportada no documento; não é confirmação independente de licença actual'}
|
||||
{value.successor_key
|
||||
? ` · Sucessora indicada: ${value.successor_key}`
|
||||
: ''}
|
||||
</p>
|
||||
{value.register_reference && (
|
||||
<p>
|
||||
Referência de registo no documento:{' '}
|
||||
{value.register_reference}
|
||||
</p>
|
||||
)}
|
||||
<details>
|
||||
<summary>
|
||||
Rastrear o conteúdo até ao documento e à linha de origem
|
||||
</summary>
|
||||
<p>
|
||||
{value.source_document_name} · SHA-256{' '}
|
||||
{value.source_document_sha256}
|
||||
</p>
|
||||
{(value.source_rows ?? []).map((row) => (
|
||||
<article key={row.id}>
|
||||
<h3>{row.section}</h3>
|
||||
<p>{row.id}</p>
|
||||
<p>{row.text}</p>
|
||||
</article>
|
||||
))}
|
||||
</details>
|
||||
<p>
|
||||
Categoria e presença física não confirmam cobertura de crédito,
|
||||
oferta de seguro específica, garantia ou aceitação do processo.
|
||||
</p>
|
||||
{['merged','historical','unknown'].includes(value.institution_status??'') && <p>{value.institution_status==='merged'?'Instituição integrada noutra entidade ou com actividade encerrada.':value.institution_status==='historical'?'Informação histórica.':'Situação actual por confirmar junto da instituição.'}</p>}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -127,8 +127,8 @@ export default function FundingCriteriaAssessment({
|
||||
)}
|
||||
{criteria.length === 0 && (
|
||||
<p>
|
||||
Esta ficha não contém critérios suficientes para conferir a
|
||||
correspondência.
|
||||
Consulte a entidade para conhecer as condições de acesso
|
||||
aplicáveis ao seu investimento.
|
||||
</p>
|
||||
)}
|
||||
{awaitingType > 0 && (
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
evidenceLabels,
|
||||
type Classification,
|
||||
} from '@/components/funding-classification';
|
||||
import {fundingPublicPresentation} from '@/lib/funding-public-presentation';
|
||||
import { card, input, button } from '@/pages/investment/shared';
|
||||
|
||||
export type FundingSource = {
|
||||
@@ -551,40 +552,7 @@ export function InstitutionFields({
|
||||
</section>
|
||||
);
|
||||
}
|
||||
function Source({ value }: { value: FundingSource }) {
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
{value.source_url ? (
|
||||
<a
|
||||
className="underline"
|
||||
href={value.source_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{value.source_reference}
|
||||
</a>
|
||||
) : (
|
||||
value.source_reference
|
||||
)}{' '}
|
||||
· consulta {value.checked_on}
|
||||
</p>
|
||||
{value.source_document_name && (
|
||||
<p>
|
||||
Documento: {value.source_document_name} · SHA-256{' '}
|
||||
{value.source_document_sha256}
|
||||
</p>
|
||||
)}
|
||||
{value.evidence_status && (
|
||||
<p>
|
||||
{evidenceLabels[value.evidence_status] ??
|
||||
value.evidence_status}{' '}
|
||||
· {value.origin}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function Source({value}:{value:FundingSource}){return <>{value.source_url&&<details><summary>Informação institucional</summary><a className="underline" href={value.source_url} target="_blank" rel="noopener noreferrer">Consultar informação da entidade</a><p>Consulta em {value.checked_on}.</p></details>}{value.evidence_status==='historical'&&<p>Informação histórica.</p>}{value.evidence_status==='conflict'&&<p>Informação por esclarecer junto da instituição.</p>}</>;}
|
||||
export function InstitutionView({ value }: { value: InstitutionDetails }) {
|
||||
return (
|
||||
<section className={card}>
|
||||
@@ -643,29 +611,28 @@ export function InstitutionView({ value }: { value: InstitutionDetails }) {
|
||||
: l.role === 'branch'
|
||||
? 'Balcão'
|
||||
: 'Ponto de contacto institucional'}
|
||||
: {l.label} · {l.address}
|
||||
: {l.label && !/^(Morada|Localização).*(documento|fonte)/i.test(l.label) ? l.label+' · ' : ''}{l.address}
|
||||
</p>
|
||||
<p>
|
||||
{l.territory_path?.map((n) => n.name).join(' / ') ||
|
||||
'Correspondência ao cadastro territorial por confirmar'}
|
||||
'Localização detalhada a confirmar'}
|
||||
</p>
|
||||
{l.location_text && (
|
||||
<p>Local indicado: {l.location_text}</p>
|
||||
)}
|
||||
{l.hours && (
|
||||
<p>
|
||||
Horário indicado na fonte (pode ter mudado):{' '}
|
||||
Horário de atendimento:{' '}
|
||||
{l.hours}
|
||||
</p>
|
||||
)}
|
||||
<Source value={l} />
|
||||
</article>
|
||||
))}
|
||||
<h3>Produtos e programas</h3>
|
||||
<h3>Produtos e serviços</h3>
|
||||
{value.products.length === 0 && (
|
||||
<p>
|
||||
Produtos e condições por confirmar; a identificação da
|
||||
instituição não confirma oferta disponível.
|
||||
{fundingPublicPresentation({...value,title:"",kind:"institution",summary:""}).services.join(" · ") || "Consulte as soluções disponíveis junto da instituição."}
|
||||
</p>
|
||||
)}
|
||||
{value.products.map((p) => (
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
type PublicFunding = {
|
||||
title: string;
|
||||
kind: string;
|
||||
summary: string;
|
||||
institution_types?: string[];
|
||||
institution_services?: string[];
|
||||
products?: {
|
||||
title: string;
|
||||
instrument?: string | null;
|
||||
availability?: string;
|
||||
}[];
|
||||
};
|
||||
const descriptions: Record<string, string> = {
|
||||
commercial_bank: 'Instituição de banca comercial.',
|
||||
investment_bank: 'Instituição especializada em banca de investimento.',
|
||||
bank: 'Instituição bancária.',
|
||||
microbank: 'Instituição de microfinanças.',
|
||||
cooperative: 'Cooperativa de crédito.',
|
||||
savings: 'Organização de poupança e empréstimo.',
|
||||
investment_manager:
|
||||
'Sociedade especializada na gestão de fundos de investimento.',
|
||||
investment_company: 'Sociedade de investimento.',
|
||||
pension_manager: 'Entidade gestora de fundos de pensões.',
|
||||
insurer: 'Instituição do sector segurador.',
|
||||
reinsurer: 'Instituição especializada em resseguros.',
|
||||
microinsurer: 'Instituição de microsseguros.',
|
||||
broker: 'Entidade de corretagem de seguros.',
|
||||
agent: 'Entidade de mediação de seguros.',
|
||||
reinsurance_broker: 'Entidade de corretagem de resseguros.',
|
||||
exchange_house: 'Instituição de serviços cambiais.',
|
||||
electronic_money: 'Instituição de moeda electrónica.',
|
||||
interbank: 'Entidade de serviços interbancários.',
|
||||
supervisor: 'Entidade de supervisão do sistema financeiro.',
|
||||
};
|
||||
const services: Record<string, string> = {
|
||||
equity: 'Capital e participação no investimento',
|
||||
foreign_exchange: 'Câmbio',
|
||||
guarantee: 'Garantias',
|
||||
insurance: 'Seguros',
|
||||
investment: 'Financiamento do investimento',
|
||||
investment_management: 'Gestão de investimentos',
|
||||
payments: 'Serviços de pagamento',
|
||||
pension: 'Pensões',
|
||||
working_capital: 'Financiamento de tesouraria',
|
||||
};
|
||||
export function fundingPublicPresentation(p: PublicFunding) {
|
||||
const generic =
|
||||
p.summary.trim() ===
|
||||
'Instituição identificada no documento fornecido. Categoria, localização e contactos são informativos; produtos, disponibilidade e condições não documentados permanecem por confirmar.';
|
||||
const description = generic
|
||||
? (descriptions[p.institution_types?.[0] ?? ''] ??
|
||||
'Entidade do sector financeiro.')
|
||||
: p.summary
|
||||
.replace(
|
||||
/\s*Referência informativa, não concessão de financiamento\.?$/,
|
||||
'',
|
||||
)
|
||||
.replace(/Público\/finalidade:/g, 'Soluções para')
|
||||
.replace(/\.\./g, '.')
|
||||
.trim();
|
||||
const products = (p.products ?? [])
|
||||
.filter((x) => x.availability !== 'closed')
|
||||
.map((x) => ({ title: x.title, detail: x.instrument?.trim() ?? '' }));
|
||||
return {
|
||||
description,
|
||||
products,
|
||||
services: [
|
||||
...new Set(
|
||||
(p.institution_services ?? [])
|
||||
.map((s) => services[s])
|
||||
.filter(Boolean),
|
||||
),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import {fundingPublicPresentation} from '@/lib/funding-public-presentation';
|
||||
import {
|
||||
financialCategories,
|
||||
financialTypes,
|
||||
@@ -64,11 +65,11 @@ export default function Index({
|
||||
</Link>
|
||||
)}
|
||||
<Link className="underline" href="/fundos/guardados">
|
||||
Programas que guardei
|
||||
A minha selecção
|
||||
</Link>
|
||||
<p>
|
||||
Consulte os programas e as condições documentadas. Cada chamada
|
||||
tem as suas próprias datas e critérios.
|
||||
Encontre bancos, seguradoras, fundos e outros parceiros financeiros.
|
||||
Explore as soluções e os contactos adequados ao seu investimento.
|
||||
</p>
|
||||
<form
|
||||
className={`${card} flex flex-wrap gap-4`}
|
||||
@@ -273,7 +274,8 @@ export default function Index({
|
||||
{item.payload.title}
|
||||
</Link>
|
||||
</h2>
|
||||
<p>{item.payload.summary}</p>
|
||||
<p>{fundingPublicPresentation(item.payload).description}</p>
|
||||
{item.payload.kind==='institution' && <div><h3 className="font-semibold">Produtos e serviços</h3>{fundingPublicPresentation(item.payload).products.length ? <ul>{fundingPublicPresentation(item.payload).products.map(p=><li key={p.title}>{p.title}{p.detail ? ': '+p.detail : ''}</li>)}</ul> : <p>{fundingPublicPresentation(item.payload).services.join(' · ') || 'Consulte as soluções disponíveis junto da instituição.'}</p>}</div>}
|
||||
<p>
|
||||
{(item.payload.institution_categories ?? [])
|
||||
.map((k) => financialCategories[k] ?? k)
|
||||
@@ -283,16 +285,16 @@ export default function Index({
|
||||
.map((k) => financialTypes[k] ?? k)
|
||||
.join(' · ')}
|
||||
</p>
|
||||
<p>Gestor: {item.payload.manager_name}</p>
|
||||
{item.payload.kind!=='institution' && <p>Entidade gestora: {item.payload.manager_name}</p>}
|
||||
{Object.entries(item.filter_explanation ?? {}).map(
|
||||
([key, message]) => (
|
||||
<p key={key}>{message}</p>
|
||||
),
|
||||
)}
|
||||
{item.payload.windows.length === 0 ? (
|
||||
{item.payload.kind==='institution' && item.payload.windows.length===0 ? null : item.payload.windows.length === 0 ? (
|
||||
<p>
|
||||
Não existe uma janela de candidatura confirmada
|
||||
nesta ficha.
|
||||
para este programa.
|
||||
</p>
|
||||
) : (
|
||||
<ul>
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function Saved({
|
||||
};
|
||||
}) {
|
||||
return (
|
||||
<Frame title="Programas que guardei">
|
||||
<Frame title="A minha selecção">
|
||||
<Link className="underline" href="/fundos">
|
||||
Consultar fundos e programas
|
||||
</Link>
|
||||
@@ -39,7 +39,7 @@ export default function Saved({
|
||||
className="underline"
|
||||
href={`/fundos/${item.id}`}
|
||||
>
|
||||
Consultar ficha publicada
|
||||
Consultar detalhes
|
||||
</Link>
|
||||
{item.current_version !== item.saved_version && (
|
||||
<p>
|
||||
@@ -51,7 +51,7 @@ export default function Saved({
|
||||
</>
|
||||
) : (
|
||||
<p>
|
||||
A ficha já não está disponível publicamente. A sua
|
||||
Esta informação já não está disponível para consulta pública. A sua
|
||||
referência guardada foi conservada.
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {fundingPublicPresentation} from '@/lib/funding-public-presentation';
|
||||
import {
|
||||
InstitutionView,
|
||||
type InstitutionDetails,
|
||||
@@ -32,7 +33,7 @@ export default function Show({
|
||||
return (
|
||||
<Frame title={payload.title}>
|
||||
<Link className="underline" href="/fundos/guardados">
|
||||
Programas que guardei
|
||||
A minha selecção
|
||||
</Link>
|
||||
<Link
|
||||
className="underline"
|
||||
@@ -40,7 +41,7 @@ export default function Show({
|
||||
>
|
||||
Voltar aos fundos e programas
|
||||
</Link>
|
||||
<p>{payload.summary}</p>
|
||||
<section><h2 className="text-xl font-semibold">Descrição</h2><p>{fundingPublicPresentation(payload).description}</p></section>
|
||||
{payload.kind === 'institution' && (
|
||||
<InstitutionView value={payload as InstitutionDetails} />
|
||||
)}
|
||||
@@ -55,8 +56,7 @@ export default function Show({
|
||||
/>
|
||||
)}
|
||||
<p>
|
||||
Gestor: {payload.manager_name}. Versão publicada:{' '}
|
||||
{record.version}.
|
||||
Entidade responsável: {payload.manager_name}.
|
||||
</p>
|
||||
{(payload.territory_paths ?? []).map((path, index) => (
|
||||
<p key={index}>
|
||||
@@ -82,30 +82,14 @@ export default function Show({
|
||||
: 'Cobertura por confirmar'}
|
||||
{payload.coverage_text ? ` — ${payload.coverage_text}` : ''}
|
||||
</p>
|
||||
<p>
|
||||
<a
|
||||
className="underline"
|
||||
href={payload.source_url || undefined}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Consultar fonte: {payload.source_reference}
|
||||
</a>
|
||||
. Consultada em {payload.source_checked_on}.{' '}
|
||||
{payload.source_document_name && (
|
||||
<span>
|
||||
Documento: {payload.source_document_name} · SHA-256{' '}
|
||||
{payload.source_document_sha256}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
<section className={card}>
|
||||
{payload.source_url && <details><summary>Informação institucional</summary><a className="underline" href={payload.source_url} target="_blank" rel="noopener noreferrer">Consultar informação da entidade</a><p>Actualização consultada em {payload.source_checked_on}.</p></details>}
|
||||
{(payload.kind!=='institution'||payload.windows.length>0) && ( <section className={card}>
|
||||
<h2 className="text-xl font-semibold">
|
||||
Janelas de candidatura
|
||||
</h2>
|
||||
{payload.windows.length === 0 && (
|
||||
<p>
|
||||
Não há uma janela de candidatura confirmada nesta ficha.
|
||||
Não há uma janela de candidatura confirmada para este programa.
|
||||
</p>
|
||||
)}
|
||||
{payload.windows.map((item) => (
|
||||
@@ -150,11 +134,11 @@ export default function Show({
|
||||
)}
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
<FundingCriteriaAssessment
|
||||
</section>)}
|
||||
{(payload.kind!=='institution'||payload.criteria.length>0||payload.windows.some(window=>window.criteria.length>0)) && ( <FundingCriteriaAssessment
|
||||
key={record.id + ':' + record.version_id}
|
||||
record={record}
|
||||
/>
|
||||
/>)}
|
||||
{payload.documents.length > 0 && (
|
||||
<section className={card}>
|
||||
<h2>Documentos associados</h2>
|
||||
@@ -179,7 +163,7 @@ export default function Show({
|
||||
<section className={card}>
|
||||
<h2>Guardar interesse</h2>
|
||||
<p>
|
||||
Guarda uma referência privada ao programa. Não envia uma
|
||||
Guarde esta opção para retomar a preparação do investimento. Não envia uma
|
||||
candidatura nem contacta a entidade.
|
||||
</p>
|
||||
{saved ? (
|
||||
@@ -210,7 +194,7 @@ export default function Show({
|
||||
})
|
||||
}
|
||||
>
|
||||
Guardar programa na minha área privada
|
||||
Guardar na minha selecção
|
||||
</button>
|
||||
) : (
|
||||
<Link className="underline" href="/login">
|
||||
|
||||
@@ -1272,7 +1272,7 @@ export default function Services(p: Props) {
|
||||
</p>
|
||||
))}
|
||||
<Link href="/investidor" className="underline">
|
||||
Abrir fichas e manifestações de interesse da
|
||||
Consultar oportunidades e manifestações de interesse da
|
||||
carteira
|
||||
</Link>
|
||||
</>
|
||||
|
||||
@@ -131,7 +131,7 @@ export default function WhyInvest() {
|
||||
],
|
||||
[
|
||||
'Território e ligações',
|
||||
'Compare localizações, corredores e parques com o mercado que pretende servir. Confira acessos, logística, infra-estruturas e as condições documentadas de cada ficha.',
|
||||
'Compare localizações, corredores e parques com o mercado que pretende servir. Confira acessos, logística, infra-estruturas e as condições de cada localização.',
|
||||
'/oportunidades?view=map',
|
||||
],
|
||||
[
|
||||
@@ -163,7 +163,7 @@ export default function WhyInvest() {
|
||||
],
|
||||
[
|
||||
'Localizações e enquadramentos',
|
||||
'Explore o mapa e as fichas de corredores, clusters, zonas, parques e projectos. Cada referência conserva a natureza e o fundamento que lhe estão associados.',
|
||||
'Explore o mapa e os detalhes de corredores, clusters, zonas, parques e projectos. Cada referência conserva a natureza e o fundamento que lhe estão associados.',
|
||||
'/oportunidades?view=map',
|
||||
],
|
||||
[
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import * as presentation from '../../resources/js/lib/funding-public-presentation.ts';
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
@@ -26,6 +27,7 @@ function load(file, mocks = {}) {
|
||||
(name) =>
|
||||
name in mocks
|
||||
? mocks[name]
|
||||
: name === "@/lib/funding-public-presentation" ? presentation
|
||||
: name === "@/components/funding-classification"
|
||||
? load("components/funding-classification.tsx", mocks)
|
||||
: name === "@/pages/investment/shared"
|
||||
@@ -87,7 +89,8 @@ test("actual institution view keeps physical address separate from product cover
|
||||
assert.match(html, /Condições: Por confirmar/);
|
||||
assert.match(html, /Disponibilidade: Por confirmar/);
|
||||
assert.match(html, /https:\/\/example.invalid\/contact/);
|
||||
assert.match(html, /SYNTHETIC QA source/);
|
||||
assert.match(html, /https:\/\/example\.invalid\/source/);
|
||||
assert.match(html, /Consultar informação da entidade/);
|
||||
assert.match(html, /não é elegibilidade confirmada/);
|
||||
});
|
||||
test("adding a physical location does not fill product financing coverage", () => {
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import { compileFunction } from 'node:vm';
|
||||
import { createRequire } from 'node:module';
|
||||
import React from 'react';
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
import ts from 'typescript';
|
||||
import * as presentation from '../../resources/js/lib/funding-public-presentation.ts';
|
||||
const require = createRequire(import.meta.url),
|
||||
records = Object.values(
|
||||
JSON.parse(
|
||||
fs.readFileSync(
|
||||
new URL(
|
||||
'../../database/data/funding-institutions-nov2026.json',
|
||||
import.meta.url,
|
||||
),
|
||||
'utf8',
|
||||
),
|
||||
).references,
|
||||
).map((x) => x.draft);
|
||||
function load(file) {
|
||||
const module = { exports: {} };
|
||||
const output = ts.transpileModule(
|
||||
fs.readFileSync(
|
||||
new URL('../../resources/js/' + file, import.meta.url),
|
||||
'utf8',
|
||||
),
|
||||
{
|
||||
compilerOptions: {
|
||||
module: ts.ModuleKind.CommonJS,
|
||||
target: ts.ScriptTarget.ES2022,
|
||||
jsx: ts.JsxEmit.ReactJSX,
|
||||
},
|
||||
},
|
||||
).outputText;
|
||||
compileFunction(output, ['require', 'module', 'exports'])(
|
||||
(name) =>
|
||||
name === '@/lib/funding-public-presentation'
|
||||
? presentation
|
||||
: name === '@/pages/investment/shared'
|
||||
? {
|
||||
Frame: ({ title, children }) =>
|
||||
React.createElement(
|
||||
'main',
|
||||
null,
|
||||
React.createElement('h1', null, title),
|
||||
children,
|
||||
),
|
||||
card: 'card',
|
||||
button: 'button',
|
||||
input: 'input',
|
||||
}
|
||||
: name === '@inertiajs/react'
|
||||
? {
|
||||
Link: ({ children, href }) =>
|
||||
React.createElement('a', { href }, children),
|
||||
router: {},
|
||||
useForm: (data) => ({ data, setData() {}, get() {} }),
|
||||
}
|
||||
: name === './shared'
|
||||
? { windowLabels: {} }
|
||||
: name === '@/lib/portal-api' ? {portalRequest:()=>{throw Error('Read-only rendering must not send requests');}} : name === '@/components/funding-classification'
|
||||
? load('components/funding-classification.tsx')
|
||||
: name === '@/components/funding-institution'
|
||||
? load('components/funding-institution.tsx')
|
||||
: name === '@/components/funding-dossier-choice'
|
||||
? { FundingDossierChoice: () => null }
|
||||
: name ===
|
||||
'@/components/funding-criteria-assessment'
|
||||
? load('components/funding-criteria-assessment.tsx')
|
||||
: require(name),
|
||||
module,
|
||||
module.exports,
|
||||
);
|
||||
return module.exports;
|
||||
}
|
||||
test('all270 imported institutions get readable descriptions and evidenced services without changing the published payload', () => {
|
||||
assert.equal(records.length, 270);
|
||||
for (const p of records) {
|
||||
const original = JSON.stringify(p),
|
||||
m = presentation.fundingPublicPresentation(p);
|
||||
assert.ok(m.description);
|
||||
assert.doesNotMatch(
|
||||
m.description,
|
||||
/documento fornecido|ficha|SHA-256|Público\/finalidade/,
|
||||
);
|
||||
assert.equal(JSON.stringify(p), original);
|
||||
assert.equal(
|
||||
m.products.length,
|
||||
(p.products ?? []).filter((x) => x.availability !== 'closed')
|
||||
.length,
|
||||
);
|
||||
}
|
||||
});
|
||||
test('deliberate editorial descriptions survive and missing products are never fabricated', () => {
|
||||
const m = presentation.fundingPublicPresentation({
|
||||
title: 'Example',
|
||||
kind: 'institution',
|
||||
summary: 'Apoio especializado ao investimento rural.',
|
||||
products: [],
|
||||
institution_types: ['insurer'],
|
||||
});
|
||||
assert.equal(m.description, 'Apoio especializado ao investimento rural.');
|
||||
assert.deepEqual(m.products, []);
|
||||
assert.deepEqual(m.services, []);
|
||||
});
|
||||
test('actual public cards and detail lead with description/products and keep import metadata out of the visitor text', () => {
|
||||
const Index = load('pages/funding/index.tsx').default,
|
||||
Show = load('pages/funding/show.tsx').default;
|
||||
for (const raw of [
|
||||
records[0],
|
||||
records.find((p) => p.institution_types.includes('investment_manager')),
|
||||
]) {
|
||||
const p = { ...raw, windows: [], documents: [] },
|
||||
item = {
|
||||
id: 'synthetic',
|
||||
version_id: 'synthetic-version',
|
||||
version: 1,
|
||||
payload: p,
|
||||
};
|
||||
const index = renderToStaticMarkup(
|
||||
React.createElement(Index, {
|
||||
items: { data: [item], links: [] },
|
||||
filters: {},
|
||||
kinds: {},
|
||||
options: { territories: [], sectors: [] },
|
||||
management: false,
|
||||
}),
|
||||
);
|
||||
const detail = renderToStaticMarkup(
|
||||
React.createElement(Show, {
|
||||
record: item,
|
||||
authenticated: false,
|
||||
saved: null,
|
||||
}),
|
||||
);
|
||||
for (const html of [index, detail]) {
|
||||
assert.doesNotMatch(
|
||||
html,
|
||||
/ficha|documento fornecido|SHA-256|linha de origem|Instituições financeiras em Moçambique\.docx/i,
|
||||
);
|
||||
assert.match(html, /Produtos e serviços/);
|
||||
assert.match(
|
||||
html,
|
||||
new RegExp(raw.title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')),
|
||||
);
|
||||
}
|
||||
const out=new URL('../../outputs/funding-public-editorial-20261111/',import.meta.url);fs.mkdirSync(out,{recursive:true});const name=p.products.length?'bank-with-products':'investment-manager';fs.writeFileSync(new URL(name+'-index.html',out),index);fs.writeFileSync(new URL(name+'-detail.html',out),detail);fs.writeFileSync(new URL('verification.json',out),JSON.stringify({publishedSourceRecords:270,descriptionsPreserved:true,productsNotFabricated:true,actualPublicComponents:true,metadataRemovedFromPresentation:true,privateEditorialFieldsUnchanged:true,activeDataWrites:0,visualBrowserReview:false},null,2));
|
||||
assert.match(detail, /Descrição/);
|
||||
if (p.products.length) assert.match(detail, /Soluções PME/);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user