diff --git a/resources/js/lib/opportunity-information.ts b/resources/js/lib/opportunity-information.ts new file mode 100644 index 00000000..2be7b96d --- /dev/null +++ b/resources/js/lib/opportunity-information.ts @@ -0,0 +1,41 @@ +export function opportunityDescription(record: { + title: string; + summary?: string | null; +}): string { + const summary = record.summary + ?.replace(/<[^>]*>/g, ' ') + .replace(/\s+/g, ' ') + .trim(); + const text = + summary || `Consulte a ficha de ${record.title} no InvesteMoz.`; + return Array.from( + text + .replace(/<[^>]*>/g, ' ') + .replace(/\s+/g, ' ') + .trim(), + ) + .slice(0, 160) + .join(''); +} + +export function recordedVerificationDate(value?: string | null): string { + if (!value?.trim()) return 'Não registada'; + const parts = /^(\d{4})-(\d{2})-(\d{2})/.exec(value); + if (!parts) return 'Data não interpretável'; + const [, year, month, day] = parts.map(Number); + const calendar = new Date(Date.UTC(year, month - 1, day)); + if ( + calendar.getUTCFullYear() !== year || + calendar.getUTCMonth() !== month - 1 || + calendar.getUTCDate() !== day + ) + return 'Data não interpretável'; + if (value.length > 10 && !/(Z|[+-]\d{2}:\d{2})$/.test(value)) + return 'Fuso horário não registado'; + const date = new Date(value); + if (Number.isNaN(date.getTime())) return 'Data não interpretável'; + return new Intl.DateTimeFormat('pt-MZ', { + dateStyle: 'medium', + timeZone: 'Africa/Maputo', + }).format(date); +} diff --git a/resources/js/pages/investment/detail.tsx b/resources/js/pages/investment/detail.tsx index 520fc4e7..e5a3b94a 100644 --- a/resources/js/pages/investment/detail.tsx +++ b/resources/js/pages/investment/detail.tsx @@ -9,7 +9,11 @@ import { } from '@/lib/search-territory'; import type { EducationalArticle } from '@/components/educational-article-card'; import { ClassificationSummary } from '@/components/opportunity-classification'; -import { Link, router, usePage } from '@inertiajs/react'; +import { Head, Link, router, usePage } from '@inertiajs/react'; +import { + opportunityDescription, + recordedVerificationDate, +} from '@/lib/opportunity-information'; import { useState } from 'react'; import { downloadPublicOpportunityOffline } from '@/lib/public-opportunity-offline'; import InvestmentMapPanel from '@/components/investment-map-panel'; @@ -46,6 +50,23 @@ export default function Detail({ const [offlineMessage, setOfflineMessage] = useState(''); return ( +
+ + + + {r.maturity_label === 'Em preparação' && (Em preparação. Consulte a oportunidade e @@ -273,6 +294,10 @@ export default function Detail({