fix(portal): identify operator and institutional support contact
This commit is contained in:
+1
-1
@@ -21,7 +21,7 @@ return [
|
||||
// Experimental proposal only; functional approval and operational SLA remain pending.
|
||||
'opportunity_modular_profiles_qa_enabled' => env('PORTAL_OPPORTUNITY_MODULAR_PROFILES_QA_ENABLED', false),
|
||||
'opportunity_other_qa_subtypes' => [],
|
||||
'operator_name' => env('PORTAL_OPERATOR_NAME'),
|
||||
'operator_name' => env('PORTAL_OPERATOR_NAME') ?: 'Tempo, Lda.',
|
||||
// Tempo, 19/10/2026: institutional contact; commercial department remains separate.
|
||||
'institutional_contact_email' => env('PORTAL_INSTITUTIONAL_CONTACT_EMAIL', 'comercial@tempo.co.mz'),
|
||||
'support' => [
|
||||
|
||||
@@ -10,8 +10,9 @@ const navigation = [
|
||||
|
||||
export function TempoFooter() {
|
||||
const { t, language } = useTempoUi();
|
||||
const { portalInstitutionalContactEmail } = usePage<{
|
||||
const { portalInstitutionalContactEmail, portalOperator } = usePage<{
|
||||
portalInstitutionalContactEmail?: string | null;
|
||||
portalOperator?: string | null;
|
||||
}>().props;
|
||||
return (
|
||||
<footer
|
||||
@@ -27,6 +28,9 @@ export function TempoFooter() {
|
||||
>
|
||||
<PortalProductIdentity />
|
||||
</Link>
|
||||
<p className="text-sm text-slate-300">
|
||||
{t('Operado por')} {portalOperator || 'Tempo, Lda.'}
|
||||
</p>
|
||||
<div className="flex items-start gap-4">
|
||||
<MapPin
|
||||
className="mt-1 size-[22px] shrink-0 text-[#ed1c24]"
|
||||
|
||||
@@ -432,9 +432,10 @@ function Simulator() {
|
||||
}
|
||||
|
||||
export default function Services(p: Props) {
|
||||
const { flash, auth } = usePage<{
|
||||
const { flash, auth, portalInstitutionalContactEmail } = usePage<{
|
||||
flash?: { success?: string };
|
||||
auth: { user: unknown };
|
||||
portalInstitutionalContactEmail?: string | null;
|
||||
}>().props;
|
||||
const currentUrl = usePage<{ url: string }>().url;
|
||||
const filter = useForm({
|
||||
@@ -1053,6 +1054,19 @@ export default function Services(p: Props) {
|
||||
<>
|
||||
<section className="space-y-3">
|
||||
<h2 className="text-xl font-bold">Contactos</h2>
|
||||
{portalInstitutionalContactEmail &&
|
||||
portalInstitutionalContactEmail !==
|
||||
p.contacts?.email && (
|
||||
<p>
|
||||
Contacto institucional:{' '}
|
||||
<a
|
||||
className="underline"
|
||||
href={`mailto:${portalInstitutionalContactEmail}`}
|
||||
>
|
||||
{portalInstitutionalContactEmail}
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
{p.contacts?.email && (
|
||||
<p>
|
||||
Email:{' '}
|
||||
@@ -1073,9 +1087,8 @@ export default function Services(p: Props) {
|
||||
)}
|
||||
{!p.contacts?.email && !p.contacts?.phone && (
|
||||
<p>
|
||||
Utilize o formulário para contactar a
|
||||
equipa. Os contactos directos ainda não
|
||||
estão publicados.
|
||||
Para pedidos de apoio ao investimento,
|
||||
utilize o formulário desta página.
|
||||
</p>
|
||||
)}
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
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';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const Empty = () => null;
|
||||
const passthrough = ({ children }) => React.createElement('div', null, children);
|
||||
|
||||
function load(file, page) {
|
||||
const code = ts.transpileModule(fs.readFileSync(new URL(file, import.meta.url), 'utf8'), {
|
||||
compilerOptions: { module: ts.ModuleKind.CommonJS, jsx: ts.JsxEmit.ReactJSX, target: ts.ScriptTarget.ES2022 },
|
||||
}).outputText;
|
||||
const module = { exports: {} };
|
||||
const aliases = new Proxy({ default: Empty }, { get: (target, name) => name in target ? target[name] : Empty });
|
||||
compileFunction(code, ['require', 'module', 'exports'])((name) => {
|
||||
if (name === 'lucide-react') return aliases;
|
||||
if (name === '@inertiajs/react') return {
|
||||
Link: 'a', Head: Empty, usePage: () => page,
|
||||
useForm: (data) => ({ data, errors: {}, processing: false, setData() {}, post() {}, reset() {} }),
|
||||
};
|
||||
if (name === '@/components/tempo-brand') return { useTempoUi: () => ({ t: (text) => text, language: 'pt' }), TempoSocialLinks: Empty };
|
||||
if (name === '@/pages/investment/shared') return { Frame: passthrough };
|
||||
if (name.startsWith('@/')) return aliases;
|
||||
return require(name);
|
||||
}, module, module.exports);
|
||||
return module.exports;
|
||||
}
|
||||
|
||||
test('footer identifies the decided operator without implying a public partnership', () => {
|
||||
const { TempoFooter } = load('../../resources/js/components/tempo-footer.tsx', {
|
||||
props: { portalOperator: 'Tempo, Lda.', portalInstitutionalContactEmail: 'institution@example.invalid' },
|
||||
});
|
||||
const html = renderToStaticMarkup(React.createElement(TempoFooter));
|
||||
assert.match(html, /Operado por.*Tempo, Lda\./s);
|
||||
assert.match(html, /mailto:institution@example.invalid/);
|
||||
assert.doesNotMatch(html, /parceria com a APIEX/);
|
||||
});
|
||||
|
||||
test('support distinguishes the existing institutional contact from dedicated support', () => {
|
||||
const { default: Services } = load('../../resources/js/pages/portal/services.tsx', {
|
||||
url: '/apoio', props: { auth: { user: null }, portalInstitutionalContactEmail: 'institution@example.invalid' },
|
||||
});
|
||||
const html = renderToStaticMarkup(React.createElement(Services, { section: 'support', contacts: {}, countries: [] }));
|
||||
assert.match(html, /Contacto institucional/);
|
||||
assert.match(html, /mailto:institution@example.invalid/);
|
||||
assert.doesNotMatch(html, /Os contactos directos ainda não/);
|
||||
assert.match(html, /utilize o formulário desta página/);
|
||||
});
|
||||
Reference in New Issue
Block a user