import { ClassificationEditor, type ClassificationOptions, type ClassificationRelation, } from './opportunity-classification'; import { valueText } from '@/lib/value-text'; import OpportunityParcelGeometry from './opportunity-parcel-geometry'; import TerritoryGeometryPreview from './territory-geometry-preview'; import { control } from '@/pages/management/shared'; import OpportunityFillFlow from './opportunity-fill-flow'; import { opportunityFillGroups } from '@/lib/opportunity-fill-flow'; import { modalityLabels } from './opportunity-search-filters'; type Data = Record; type Schema = { $ref?: string; type?: string | string[]; properties?: Record; items?: Schema; enum?: unknown[]; const?: unknown; format?: string; maxLength?: number; required?: string[]; }; type Resource = { id: string; payload: { title?: string; role?: string; [key: string]: unknown }; file?: { file_name: string }; }; function resourceTerritory(resource?: Resource) { const p = resource?.payload; const id = p?.locality_id ?? p?.admin_post_id ?? p?.district_id ?? p?.province_id; return typeof id === 'string' ? id : undefined; } export type CanonicalOptions = { classification?: ClassificationOptions; version: string; modularProfiles?: Record; historyVersion?: string | null; schema: Schema; submissionTextLimits?: Record< string, { minLength?: number; maxLength?: number } >; definitions: Record; contacts: { id: string; full_name: string }[]; contactInvestors?: { id: string; legal_name: string }[]; activities: { id: string; parent_id: string | null; sector_id: number | null; code?: string; level?: number | null; path?: { id: string; code: string; label: string }[]; display_label?: string; historical_only?: boolean; source_version?: string | null; edition?: string | null; label_pt_mz: string; }[]; resources: Record; gates: string[]; countries: { code: string; name: string }[]; authorities: { id: string; label_pt_mz: string }[]; }; const labels: Record = { official_title: 'Título oficial', promoter_type: 'Tipo de Promotor', internal_contact_id: 'Contacto interno', sector_id: 'Sector', subsector_id: 'Subsector', economic_activity_id: 'Atividade económica', location_mode: 'Uma ou várias localizações', geographic_scope: 'Âmbito geográfico', multi_district_rationale: 'Justificação territorial', locations: 'Onde se localiza — locais documentados', executive_summary: 'Resumo executivo', demand_statement: 'Necessidade e proposta', visibility: 'Visibilidade após publicação', truth_declaration: 'Declaração de veracidade', publication_languages: 'Versões por idioma', language_code: 'Idioma', title: 'Título', summary: 'Resumo', documents: 'Documentos guardados', evidences: 'Evidências', document_id: 'Documento associado', evidence_type: 'Tipo de evidência', description: 'Descrição', images: 'Imagens guardadas', review_due_date: 'Data de revisão editorial', real_estate: 'Imobiliário', land_rights: 'DUAT e direitos fundiários', business_transaction: 'Negócio', module_profile_version: 'Versão do contrato QA (seleção explícita)', module_details: 'Condições do módulo', mode: 'Percurso: procura, oferta ou modalidade', conditions_status: 'Condições propostas ou compromisso documentado', object: 'Objeto', representation_document_id: 'Documento de representação', object_document_id: 'Documento do objeto/procedimento', disclosure_authorization_document_id: 'Autorização de divulgação da intenção/chamada', external_decision_document_id: 'Ato competente do direito atribuído', granting_entity: 'Entidade concedente declarada', procedure_regime: 'Regime/procedimento declarado', scope: 'Âmbito territorial ou digital', term_value: 'Duração ou prazo', term_unit: 'Unidade da duração', remuneration_obligations: 'Remuneração e obrigações', participation_conditions: 'Condições de participação', land_required: 'Precisa de terra?', construction_required: 'Inclui construção?', applicable_authorizations: 'Autorizações aplicáveis e situação', instrument: 'Instrumento de financiamento', offered_capacity: 'Capacidade oferecida pelo financiador', eligibility_criteria: 'Critérios declarados do financiador', capacity_document_id: 'Evidência da capacidade declarada', financial_plan_document_id: 'Plano financeiro do projeto', secured_evidence_document_id: 'Evidência do financiamento assegurado', use_of_funds: 'Finalidade do financiamento procurado', disbursement_calendar: 'Calendário proposto de desembolso', repayment_periodicity: 'Periodicidade de reembolso', proposed_rights: 'Direitos propostos', equity_percentage: 'Percentagem de capital proposta', terms: 'Condições', scenario: 'Cenário identificado', source: 'Fonte identificada', terms_date: 'Data da fonte/condições', commitment_document_id: 'Documento do compromisso assumido', acceptance_criteria: 'Critérios de aceitação do fornecimento', quotation_method: 'Método de cotação', total_price: 'Preço total (impostos identificados separadamente)', declared_taxes: 'Impostos declarados, sem cálculo implícito', advance_or_guarantee: 'Há adiantamento ou garantia?', subtype: 'Subtipo para classificação funcional', recipients: 'Destinatários', expected_output: 'Resultado esperado', history_status: 'Situação do histórico financeiro', history_declaration: 'Declaração de ausência de exercícios completos', history_declaration_document_id: 'Documento da declaração', projections: 'Projeções identificadas — não são contas realizadas', transaction_type: 'Modalidade', property_type: 'Tipo de imóvel', construction_status: 'Estado construtivo', property_condition: 'Condição do imóvel', land_area: 'Área do terreno', built_area: 'Área construída', area_unit: 'Unidade da área', units: 'Unidades', rooms: 'Compartimentos', floors: 'Pisos', amenities: 'Comodidades', utilities: 'Serviços e acessos', occupancy_status: 'Ocupação', title_type: 'Tipo de título', registration_number: 'Número de registo', encumbrances: 'Ónus e restrições', price: 'Preço do imóvel', rent: 'Renda', rent_periodicity: 'Periodicidade', deposit: 'Caução', valuation: 'Avaliação', viewing: 'Visitas', planning_permit_document_id: 'Licença de construção', use_license_document_id: 'Licença de utilização', operation_type: 'Operação fundiária admissível', duat_status: 'Situação do DUAT', duat_number: 'Número do DUAT', holder_id: 'Titular cadastrado', holder_name: 'Nome do titular', issuing_authority_id: 'Entidade emissora', issue_date: 'Data de emissão', expiry_date: 'Data de validade', authorized_purpose: 'Finalidade autorizada', exploitation_plan_document_id: 'Plano de exploração', parcels: 'Parcelas', location_id: 'Localização associada', area: 'Área', cadastral_number: 'Número cadastral', overlap_status: 'Sobreposição', improvements: 'Benfeitorias', communities: 'Comunidades', community_consultation: 'Consulta comunitária', overlaps_conflicts: 'Sobreposições e conflitos', required_authorizations: 'Autorizações necessárias', consideration: 'Contraprestação', legal_entity_id: 'Entidade representada', legal_form: 'Forma jurídica', percentage_offered: 'Percentagem oferecida', asking_price: 'Valor solicitado', financial_periods: 'Exercícios financeiros', assets_liabilities: 'Ativos e passivos', employees: 'Empregos e obrigações', contracts_licenses: 'Contratos e licenças', litigation_contingencies: 'Litígios e contingências', reason_for_transaction: 'Fundamentação da operação', partner_contribution: 'Contribuição do parceiro', governance_rights: 'Direitos de governação', franchise_terms: 'Condições da franquia', confidentiality_level: 'Confidencialidade', due_diligence_status: 'Preparação da análise', opportunity_type: 'Tipo de investimento', maturity_stage: 'Etapa de maturidade', investment_modalities: 'Modalidades de investimento', investor_profiles: 'Perfis de investidor', products: 'Produtos', assets: 'Ativos', inputs: 'Insumos', markets: 'Mercados', competitive_advantage: 'Vantagem competitiva', employment: 'Emprego', impact_indicators: 'Impactos', total_investment: 'Investimento total', currency: 'Moeda', investment_breakdown: 'Rubricas de investimento', secured_financing: 'Financiamento assegurado', funding_sought: 'Financiamento procurado', financial_indicators: 'Indicadores financeiros', milestones: 'Marcos', licenses: 'Licenças', environmental_social: 'Aspetos ambientais e sociais', risks: 'Riscos', relations: 'Relações', enterprise_size: 'Dimensão empresarial declarada (separada do setor)', amount: 'Montante', name: 'Nome', label: 'Designação', status: 'Estado', date: 'Data', method: 'Método', appraiser: 'Avaliador', year: 'Exercício', revenue: 'Receita', profit: 'Resultado', type: 'Tipo', value: 'Valor', details: 'Detalhes', mitigation: 'Mitigação', due_date: 'Prazo', authority: 'Entidade competente', assessment_status: 'Estado da avaliação', environmental_category: 'Categoria ambiental', direct_jobs: 'Empregos diretos', indirect_jobs: 'Empregos indirectos', female_percentage: 'Participação de mulheres (%)', youth_percentage: 'Participação de jovens (%)', social_risks: 'Riscos sociais', mitigation_plan: 'Plano de mitigação', employment_type: 'Tipo de emprego', geometry: 'Geometria da parcela', start_date: 'Data de início', end_date: 'Data de fim', responsible_name: 'Responsável declarado', dependencies: 'Marcos dos quais depende', npv: 'Valor atual líquido', irr: 'Taxa interna de retorno (%)', payback_years: 'Prazo de retorno (anos)', horizon_years: 'Horizonte (anos)', discount_rate: 'Taxa de desconto (%)', model_document_id: 'Modelo financeiro associado', title_holder_name: 'Titular do título', title_document_id: 'Documento do título', territorial_right_document_id: 'Documento do direito territorial aplicável', construction_license_document_id: 'Documento da licença de construção aplicável', holder_document_id: 'Documento do titular', expected_completion_date: 'Conclusão prevista', occupancy_details: 'Condições de ocupação', occupancy_document_id: 'Contrato de ocupação', urban_limit_document_id: 'Fonte do limite urbanístico', max_built_area: 'Área edificável máxima declarada', amenities_other: 'Outra comodidade', communities_affected: 'Existem comunidades afetadas?', exploitation_plan_required: 'Plano de exploração aplicável?', entity_registration: 'Registo e beneficiário efetivo', tax_identifier: 'Identificação fiscal', legal_document_id: 'Documento de constituição/registo', beneficial_owner_document_id: 'Documento do beneficiário efetivo', available_financial_years: 'Exercícios financeiros existentes', liabilities: 'Passivos', cash_flow: 'Fluxo de caixa', excluded_assets: 'Ativos excluídos', guarantees: 'Garantias', reconciliation_document_id: 'Documento de reconciliação', obligations: 'Obrigações', labor_liabilities: 'Passivos laborais', transferability: 'Transmissibilidade', consent_document_id: 'Consentimento de transmissão', nda_document_id: 'Acordo de confidencialidade', teaser: 'Resumo autorizado para publicação', board: 'Órgãos de gestão', reserved_matters: 'Matérias reservadas', dividends: 'Dividendos', exit: 'Saída', deadlock: 'Resolução de impasse', initial_fee: 'Encargo inicial', royalties: 'Royalties e encargos', support: 'Apoio', exclusivity: 'Exclusividade', renewal: 'Renovação', termination: 'Cessação', brand_license_document_id: 'Licença da marca', publish_contact: 'Autoriza publicar o contacto?', public_contact_id: 'Contacto para divulgação', }; function FieldErrors({ path, errors, }: { path: string; errors: Record; }) { const message = errors[path]; return message ? ( ) : null; } function Editor({ name, path, schema, value, onChange, options, errors, }: { name: string; path: string; schema: Schema; value: unknown; onChange: (v: unknown) => void; options: CanonicalOptions; errors: Record; }) { if (schema.$ref) schema = options.definitions[schema.$ref.split('/').pop()!] ?? schema; const types = Array.isArray(schema.type) ? schema.type : [schema.type]; const label = labels[name] ?? name.replaceAll('_', ' '); const parcelUnit = name === 'area_unit' && path.includes('land_rights.parcels.'); const textLimits = path === 'canonical.demand_statement' ? options.submissionTextLimits?.demand_statement : undefined; const descriptions = [ errors[path] ? path + '-error' : '', parcelUnit ? path + '-help' : '', textLimits ? path + '-length-help' : '', ] .filter(Boolean) .join(' '); const props = { id: path, 'aria-invalid': !!errors[path], 'aria-describedby': descriptions || undefined, }; let input; if (name === 'geometry') input = ; else if ( name.endsWith('document_id') || name === 'location_id' || name === 'image_id' ) { const kind = name === 'location_id' ? 'locations' : name === 'image_id' ? 'images' : 'documents'; const rows = options.resources[kind] ?? []; input = ( <> {name === 'location_id' && !!value && ( r.id === value), )} endpoint="/minhas-oportunidades/territory-geometries" label={`Limite administrativo associado à parcela ${Number(path.match(/parcels\.(\d+)\./)?.[1] ?? 0) + 1}`} /> )} ); } else if (name === 'country_code') input = ( ); else if (name === 'issuing_authority_id' || name === 'authority_id') input = ( ); else if (schema.enum) input = ( ); else if (types.includes('boolean') || schema.const === true) input = ( ); else if (types.includes('array')) { const rows = Array.isArray(value) ? value : []; let item = schema.items ?? { type: 'string' }; if (item.$ref) item = options.definitions[item.$ref.split('/').pop()!] ?? item; const itemTypes = Array.isArray(item.type) ? item.type : [item.type]; return (
{label} {rows.map((v, i) => (
onChange( rows.map((r, j) => (j === i ? next : r)), ) } />
))}
); } else if (types.includes('object') || schema.properties) { const fields = schema.properties ?? {}; const data = value && typeof value === 'object' && !Array.isArray(value) ? (value as Data) : {}; return (
{label} {Object.entries(fields).map(([key, s]) => ( onChange({ ...data, [key]: v })} /> ))} {!Object.keys(fields).length && (

Este detalhe exige definição contratual antes da submissão.

)}
); } else { const numeric = types.includes('number') || types.includes('integer'); input = numeric || schema.format === 'date' ? ( onChange( e.target.value === '' ? null : numeric ? Number(e.target.value) : e.target.value, ) } /> ) : (