From d0738c3b8ec97bebb09ec46f821d3d2fc9aaf4e6 Mon Sep 17 00:00:00 2001 From: Victor Ferreira Date: Thu, 12 Feb 2026 22:55:57 -0300 Subject: [PATCH 01/60] feat: adiciona 23 novos campos de entrega para planejamento e monitoramento (backend) --- .../OpportunityWorkplan/Entities/Delivery.php | 25 + src/modules/OpportunityWorkplan/Module.php | 428 ++++++++++++++++++ src/modules/ProjectMonitoring/Module.php | 115 ++++- 3 files changed, 567 insertions(+), 1 deletion(-) diff --git a/src/modules/OpportunityWorkplan/Entities/Delivery.php b/src/modules/OpportunityWorkplan/Entities/Delivery.php index 2bab303734..907aba850a 100644 --- a/src/modules/OpportunityWorkplan/Entities/Delivery.php +++ b/src/modules/OpportunityWorkplan/Entities/Delivery.php @@ -122,6 +122,31 @@ public function isMetadataRequired(string $metadata_key):bool { 'availabilityType' => 'workplan_monitoringInformTheFormOfAvailability', 'numberOfParticipants' => 'workplan_registrationReportTheNumberOfParticipants', 'participantProfile' => 'workplan_monitoringProvideTheProfileOfParticipants', + + // Novos campos de planejamento + 'artChainLink' => 'workplan_deliveryInformArtChainLink', + 'totalBudget' => 'workplan_deliveryInformTotalBudget', + 'numberOfCities' => 'workplan_deliveryInformNumberOfCities', + 'numberOfNeighborhoods' => 'workplan_deliveryInformNumberOfNeighborhoods', + 'mediationActions' => 'workplan_deliveryInformMediationActions', + 'paidStaffByRole' => 'workplan_deliveryInformPaidStaffByRole', + 'teamCompositionGender' => 'workplan_deliveryInformTeamComposition', + 'teamCompositionRace' => 'workplan_deliveryInformTeamComposition', + 'revenueType' => 'workplan_deliveryInformRevenueType', + 'commercialUnits' => 'workplan_deliveryInformCommercialUnits', + 'unitPrice' => 'workplan_deliveryInformCommercialUnits', + 'hasCommunityCoauthors' => 'workplan_deliveryInformCommunityCoauthors', + 'hasTransInclusionStrategy' => 'workplan_deliveryInformTransInclusion', + 'transInclusionActions' => 'workplan_deliveryInformTransInclusion', + 'hasAccessibilityPlan' => 'workplan_deliveryInformAccessibilityPlan', + 'expectedAccessibilityMeasures' => 'workplan_deliveryInformAccessibilityPlan', + 'hasEnvironmentalPractices' => 'workplan_deliveryInformEnvironmentalPractices', + 'environmentalPracticesDescription' => 'workplan_deliveryInformEnvironmentalPractices', + 'hasPressStrategy' => 'workplan_deliveryInformPressStrategy', + 'communicationChannels' => 'workplan_deliveryInformCommunicationChannels', + 'hasInnovationAction' => 'workplan_deliveryInformInnovation', + 'innovationTypes' => 'workplan_deliveryInformInnovation', + 'documentationTypes' => 'workplan_deliveryInformDocumentationTypes', ]; if ($this->$metadata_key) { diff --git a/src/modules/OpportunityWorkplan/Module.php b/src/modules/OpportunityWorkplan/Module.php index a96aa05ba5..43b5fd5184 100644 --- a/src/modules/OpportunityWorkplan/Module.php +++ b/src/modules/OpportunityWorkplan/Module.php @@ -217,6 +217,112 @@ function register() 'default_value' => false ]); + // ============================================ + // CONFIGURAÇÕES PARA NOVOS CAMPOS DE ENTREGA + // ============================================ + + $this->registerOpportunityMetadata('workplan_deliveryInformArtChainLink', [ + 'label' => i::__('Informar principal elo das artes acionado'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformTotalBudget', [ + 'label' => i::__('Informar orçamento total da atividade'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformNumberOfCities', [ + 'label' => i::__('Informar número de municípios'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformNumberOfNeighborhoods', [ + 'label' => i::__('Informar número de bairros'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformMediationActions', [ + 'label' => i::__('Informar número de ações de mediação/formação de público'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformPaidStaffByRole', [ + 'label' => i::__('Informar pessoas remuneradas por função'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformTeamComposition', [ + 'label' => i::__('Informar composição da equipe (gênero e raça/cor)'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformRevenueType', [ + 'label' => i::__('Informar tipo de receita previsto'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformCommercialUnits', [ + 'label' => i::__('Informar unidades para comercialização'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformCommunityCoauthors', [ + 'label' => i::__('Informar envolvimento de comunidades como coautores'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformTransInclusion', [ + 'label' => i::__('Informar estratégias de inclusão Trans e Travestis'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformAccessibilityPlan', [ + 'label' => i::__('Informar medidas de acessibilidade previstas'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformEnvironmentalPractices', [ + 'label' => i::__('Informar práticas socioambientais'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformPressStrategy', [ + 'label' => i::__('Informar estratégia de relacionamento com imprensa'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformCommunicationChannels', [ + 'label' => i::__('Informar canais de comunicação'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformInnovation', [ + 'label' => i::__('Informar ações de experimentação/inovação'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformDocumentationTypes', [ + 'label' => i::__('Informar tipo de documentação'), + 'type' => 'boolean', + 'default_value' => false + ]); + $app->registerFileGroup('delivery', new \MapasCulturais\Definitions\FileGroup('evidences')); // metadados workplan @@ -523,5 +629,327 @@ function register() $totalValueForecast = new Metadata('totalValueForecast', ['label' => \MapasCulturais\i::__('Previsão de valor total')]); $app->registerMetadata($totalValueForecast, Delivery::class); + + // ============================================ + // NOVOS CAMPOS DE PLANEJAMENTO DA ENTREGA + // ============================================ + + // Principal elo das artes acionado pela atividade + $artChainLink = new Metadata('artChainLink', [ + 'label' => \MapasCulturais\i::__('Principal elo das artes acionado pela atividade'), + 'type' => 'select', + 'options' => array( + \MapasCulturais\i::__('Criação'), + \MapasCulturais\i::__('Produção'), + \MapasCulturais\i::__('Difusão'), + \MapasCulturais\i::__('Circulação'), + \MapasCulturais\i::__('Formação'), + \MapasCulturais\i::__('Fruição'), + \MapasCulturais\i::__('Memória/Preservação'), + \MapasCulturais\i::__('Pesquisa'), + \MapasCulturais\i::__('Gestão Cultural'), + ), + ]); + $app->registerMetadata($artChainLink, Delivery::class); + + // Orçamento total da atividade + $totalBudget = new Metadata('totalBudget', [ + 'label' => \MapasCulturais\i::__('Qual o orçamento total da atividade?'), + 'type' => 'currency' + ]); + $app->registerMetadata($totalBudget, Delivery::class); + + // Em quantos municípios + $numberOfCities = new Metadata('numberOfCities', [ + 'label' => \MapasCulturais\i::__('Em quantos municípios a atividade vai ser realizada?'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => \MapasCulturais\i::__('Deve ser um número maior ou igual a zero') + ] + ]); + $app->registerMetadata($numberOfCities, Delivery::class); + + // Em quantos bairros + $numberOfNeighborhoods = new Metadata('numberOfNeighborhoods', [ + 'label' => \MapasCulturais\i::__('Em quantos bairros a atividade vai ser realizada?'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => \MapasCulturais\i::__('Deve ser um número maior ou igual a zero') + ] + ]); + $app->registerMetadata($numberOfNeighborhoods, Delivery::class); + + // Quantas ações de mediação/formação de público + $mediationActions = new Metadata('mediationActions', [ + 'label' => \MapasCulturais\i::__('Quantas ações de mediação/formação de público estão previstas?'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => \MapasCulturais\i::__('Deve ser um número maior ou igual a zero') + ] + ]); + $app->registerMetadata($mediationActions, Delivery::class); + + // Pessoas remuneradas por função (estrutura JSON) + $paidStaffByRole = new Metadata('paidStaffByRole', [ + 'label' => \MapasCulturais\i::__('Quantas pessoas serão remuneradas, por função?'), + 'type' => 'json', + 'serialize' => function ($val) { + return json_encode($val); + }, + 'unserialize' => function($val) { + return json_decode((string) $val, true); + } + ]); + $app->registerMetadata($paidStaffByRole, Delivery::class); + + // Composição da equipe por gênero + $teamCompositionGender = new Metadata('teamCompositionGender', [ + 'label' => \MapasCulturais\i::__('Composição prevista da equipe por gênero'), + 'type' => 'json', + 'serialize' => function ($val) { + return json_encode($val); + }, + 'unserialize' => function($val) { + return json_decode((string) $val, true); + } + ]); + $app->registerMetadata($teamCompositionGender, Delivery::class); + + // Composição da equipe por raça/cor + $teamCompositionRace = new Metadata('teamCompositionRace', [ + 'label' => \MapasCulturais\i::__('Composição prevista da equipe por raça/cor'), + 'type' => 'json', + 'serialize' => function ($val) { + return json_encode($val); + }, + 'unserialize' => function($val) { + return json_decode((string) $val, true); + } + ]); + $app->registerMetadata($teamCompositionRace, Delivery::class); + + // Tipo de receita previsto + $revenueType = new Metadata('revenueType', [ + 'label' => \MapasCulturais\i::__('Qual o tipo de receita previsto?'), + 'type' => 'multiselect', + 'options' => array( + \MapasCulturais\i::__('Venda de ingressos'), + \MapasCulturais\i::__('Venda de produtos'), + \MapasCulturais\i::__('Patrocínio privado'), + \MapasCulturais\i::__('Apoio cultural'), + \MapasCulturais\i::__('Doações'), + \MapasCulturais\i::__('Cachê'), + \MapasCulturais\i::__('Prestação de serviços'), + \MapasCulturais\i::__('Direitos autorais'), + \MapasCulturais\i::__('Licenciamento'), + \MapasCulturais\i::__('Não haverá receita'), + \MapasCulturais\i::__('Outros'), + ), + ]); + $app->registerMetadata($revenueType, Delivery::class); + + // Quantidade de unidades para comercialização + $commercialUnits = new Metadata('commercialUnits', [ + 'label' => \MapasCulturais\i::__('Quantidade de unidades previstas para comercialização'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => \MapasCulturais\i::__('Deve ser um número maior ou igual a zero') + ] + ]); + $app->registerMetadata($commercialUnits, Delivery::class); + + // Valor unitário previsto + $unitPrice = new Metadata('unitPrice', [ + 'label' => \MapasCulturais\i::__('Valor unitário previsto (R$)'), + 'type' => 'currency' + ]); + $app->registerMetadata($unitPrice, Delivery::class); + + // Envolvimento de comunidades como coautores + $hasCommunityCoauthors = new Metadata('hasCommunityCoauthors', [ + 'label' => \MapasCulturais\i::__('A atividade prevê envolvimento de comunidades/coletivos como coautores/coexecutores?'), + 'type' => 'select', + 'options' => array( + 'true' => \MapasCulturais\i::__('Sim'), + 'false' => \MapasCulturais\i::__('Não'), + ), + ]); + $app->registerMetadata($hasCommunityCoauthors, Delivery::class); + + // Estratégias Trans e Travestis (boolean) + $hasTransInclusionStrategy = new Metadata('hasTransInclusionStrategy', [ + 'label' => \MapasCulturais\i::__('A atividade prevê estratégias voltadas à promoção do acesso de pessoas Trans e Travestis?'), + 'type' => 'select', + 'options' => array( + 'true' => \MapasCulturais\i::__('Sim'), + 'false' => \MapasCulturais\i::__('Não'), + ), + ]); + $app->registerMetadata($hasTransInclusionStrategy, Delivery::class); + + // Quais ações Trans e Travestis (condicional) + $transInclusionActions = new Metadata('transInclusionActions', [ + 'label' => \MapasCulturais\i::__('Quais ações foram previstas para incorporar estratégias voltadas à promoção do acesso de pessoas Trans e Travestis?'), + 'type' => 'text' + ]); + $app->registerMetadata($transInclusionActions, Delivery::class); + + // Medidas de acessibilidade (boolean) + $hasAccessibilityPlan = new Metadata('hasAccessibilityPlan', [ + 'label' => \MapasCulturais\i::__('A atividade prevê medidas de acessibilidade?'), + 'type' => 'select', + 'options' => array( + 'true' => \MapasCulturais\i::__('Sim'), + 'false' => \MapasCulturais\i::__('Não'), + ), + ]); + $app->registerMetadata($hasAccessibilityPlan, Delivery::class); + + // Quais medidas de acessibilidade previstas (condicional) + $expectedAccessibilityMeasures = new Metadata('expectedAccessibilityMeasures', [ + 'label' => \MapasCulturais\i::__('Quais medidas de acessibilidade estão previstas na atividade?'), + 'type' => 'multiselect', + 'options' => array( + \MapasCulturais\i::__('Rotas acessíveis, com espaço de manobra para cadeira de rodas'), + \MapasCulturais\i::__('Palco acessível'), + \MapasCulturais\i::__('Camarim acessível'), + \MapasCulturais\i::__('Piso tátil'), + \MapasCulturais\i::__('Rampas'), + \MapasCulturais\i::__("Elevadores adequados para PCD's"), + \MapasCulturais\i::__('Corrimãos e guarda-corpos'), + \MapasCulturais\i::__("Banheiros adaptados para PCD's"), + \MapasCulturais\i::__('Área de alimentação preferencial identificada'), + \MapasCulturais\i::__("Vagas de estacionamento para PCD's reservadas"), + \MapasCulturais\i::__("Assentos para pessoas obesas, pessoas com mobilidade reduzida, PCD's e pessoas idosas reservadas"), + \MapasCulturais\i::__('Filas preferenciais identificadas'), + \MapasCulturais\i::__('Iluminação adequada'), + \MapasCulturais\i::__('Livro e/ou similares em braile'), + \MapasCulturais\i::__('Audiolivro'), + \MapasCulturais\i::__('Uso Língua Brasileira de Sinais - Libras'), + \MapasCulturais\i::__('Sistema Braille em materiais impressos'), + \MapasCulturais\i::__('Sistema de sinalização ou comunicação tátil'), + \MapasCulturais\i::__('Audiodescrição'), + \MapasCulturais\i::__('Legendas para surdos e ensurdecidos'), + \MapasCulturais\i::__('Linguagem simples'), + \MapasCulturais\i::__('Textos adaptados para software de leitor de tela'), + \MapasCulturais\i::__('Capacitação em acessibilidade para equipes atuantes nos projetos culturais'), + \MapasCulturais\i::__('Contratação de profissionais especializados em acessibilidade cultural'), + \MapasCulturais\i::__('Contratação de profissionais com deficiência'), + \MapasCulturais\i::__('Formação e sensibilização de agentes culturais sobre acessibilidade'), + \MapasCulturais\i::__('Formação e sensibilização de públicos da cadeia produtiva cultural sobre acessibilidade'), + \MapasCulturais\i::__("Envolvimento de PCD's na concepção do projeto"), + \MapasCulturais\i::__('Outras'), + ), + ]); + $app->registerMetadata($expectedAccessibilityMeasures, Delivery::class); + + // Práticas socioambientais (boolean) + $hasEnvironmentalPractices = new Metadata('hasEnvironmentalPractices', [ + 'label' => \MapasCulturais\i::__('A atividade prevê medidas ou práticas socioambientais?'), + 'type' => 'select', + 'options' => array( + 'true' => \MapasCulturais\i::__('Sim'), + 'false' => \MapasCulturais\i::__('Não'), + ), + ]); + $app->registerMetadata($hasEnvironmentalPractices, Delivery::class); + + // Quais práticas socioambientais (condicional) + $environmentalPracticesDescription = new Metadata('environmentalPracticesDescription', [ + 'label' => \MapasCulturais\i::__('Quais medidas e práticas socioambientais estão previstas na atividade?'), + 'type' => 'text' + ]); + $app->registerMetadata($environmentalPracticesDescription, Delivery::class); + + // Estratégia de relacionamento com imprensa + $hasPressStrategy = new Metadata('hasPressStrategy', [ + 'label' => \MapasCulturais\i::__('A atividade contará com uma estratégia de relacionamento com a imprensa?'), + 'type' => 'select', + 'options' => array( + 'true' => \MapasCulturais\i::__('Sim'), + 'false' => \MapasCulturais\i::__('Não'), + ), + ]); + $app->registerMetadata($hasPressStrategy, Delivery::class); + + // Canais de comunicação + $communicationChannels = new Metadata('communicationChannels', [ + 'label' => \MapasCulturais\i::__('Quais canais de comunicação estão previstos para difusão da atividade?'), + 'type' => 'multiselect', + 'options' => array( + \MapasCulturais\i::__('Redes sociais (Instagram, Facebook, Twitter/X)'), + \MapasCulturais\i::__('Site próprio'), + \MapasCulturais\i::__('Blog'), + \MapasCulturais\i::__('YouTube'), + \MapasCulturais\i::__('Podcast'), + \MapasCulturais\i::__('Newsletter/E-mail marketing'), + \MapasCulturais\i::__('WhatsApp/Telegram'), + \MapasCulturais\i::__('Assessoria de imprensa'), + \MapasCulturais\i::__('Rádio'), + \MapasCulturais\i::__('TV'), + \MapasCulturais\i::__('Jornal impresso'), + \MapasCulturais\i::__('Revista'), + \MapasCulturais\i::__('Cartazes/Flyers'), + \MapasCulturais\i::__('Outdoor/Mídia externa'), + \MapasCulturais\i::__('Plataformas de streaming'), + \MapasCulturais\i::__('Comunicação direta (corpo a corpo)'), + \MapasCulturais\i::__('Outros'), + ), + ]); + $app->registerMetadata($communicationChannels, Delivery::class); + + // Experimentação/inovação (boolean) + $hasInnovationAction = new Metadata('hasInnovationAction', [ + 'label' => \MapasCulturais\i::__('A atividade prevê ao menos uma ação de experimentação/inovação?'), + 'type' => 'select', + 'options' => array( + 'true' => \MapasCulturais\i::__('Sim'), + 'false' => \MapasCulturais\i::__('Não'), + ), + ]); + $app->registerMetadata($hasInnovationAction, Delivery::class); + + // Tipos de experimentação/inovação (condicional) + $innovationTypes = new Metadata('innovationTypes', [ + 'label' => \MapasCulturais\i::__('Quais tipos de experimentação/inovação previstos na atividade?'), + 'type' => 'multiselect', + 'options' => array( + \MapasCulturais\i::__('Uso de novas tecnologias (AR, VR, IA, etc.)'), + \MapasCulturais\i::__('Novas linguagens artísticas'), + \MapasCulturais\i::__('Fusão de linguagens'), + \MapasCulturais\i::__('Metodologias participativas inovadoras'), + \MapasCulturais\i::__('Novos modelos de gestão cultural'), + \MapasCulturais\i::__('Economia criativa e novos modelos de negócio'), + \MapasCulturais\i::__('Sustentabilidade e práticas ambientais inovadoras'), + \MapasCulturais\i::__('Inclusão e acessibilidade de forma inovadora'), + \MapasCulturais\i::__('Experimentação em espaços não convencionais'), + \MapasCulturais\i::__('Coprodução/cocriação com públicos'), + \MapasCulturais\i::__('Outros'), + ), + ]); + $app->registerMetadata($innovationTypes, Delivery::class); + + // Tipo de documentação + $documentationTypes = new Metadata('documentationTypes', [ + 'label' => \MapasCulturais\i::__('Tipo de documentação que será produzida'), + 'type' => 'multiselect', + 'options' => array( + \MapasCulturais\i::__('Fotografia'), + \MapasCulturais\i::__('Vídeo'), + \MapasCulturais\i::__('Áudio'), + \MapasCulturais\i::__('Relatório textual'), + \MapasCulturais\i::__('Caderno de processo'), + \MapasCulturais\i::__('Publicação impressa'), + \MapasCulturais\i::__('Publicação digital'), + \MapasCulturais\i::__('Website/Plataforma online'), + \MapasCulturais\i::__('Redes sociais'), + \MapasCulturais\i::__('Depoimentos'), + \MapasCulturais\i::__('Registros de processo'), + \MapasCulturais\i::__('Acervo digitalizado'), + \MapasCulturais\i::__('Não haverá documentação específica'), + \MapasCulturais\i::__('Outros'), + ), + ]); + $app->registerMetadata($documentationTypes, Delivery::class); } } \ No newline at end of file diff --git a/src/modules/ProjectMonitoring/Module.php b/src/modules/ProjectMonitoring/Module.php index 2379526807..1a903573ac 100644 --- a/src/modules/ProjectMonitoring/Module.php +++ b/src/modules/ProjectMonitoring/Module.php @@ -358,6 +358,99 @@ public function register() { ]); $app->registerMetadata($evidenceLinks, Delivery::class); + // ============================================ + // NOVOS CAMPOS DE MONITORAMENTO (EXECUTADOS) + // ============================================ + + // Municípios executados + $executedNumberOfCities = new Metadata('executedNumberOfCities', [ + 'label' => i::__('Em quantos municípios a atividade foi realizada?'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => i::__('Deve ser um número maior ou igual a zero') + ] + ]); + $app->registerMetadata($executedNumberOfCities, Delivery::class); + + // Bairros executados + $executedNumberOfNeighborhoods = new Metadata('executedNumberOfNeighborhoods', [ + 'label' => i::__('Em quantos bairros a atividade foi realizada?'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => i::__('Deve ser um número maior ou igual a zero') + ] + ]); + $app->registerMetadata($executedNumberOfNeighborhoods, Delivery::class); + + // Ações de mediação executadas + $executedMediationActions = new Metadata('executedMediationActions', [ + 'label' => i::__('Quantas ações de mediação/formação de público foram realizadas?'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => i::__('Deve ser um número maior ou igual a zero') + ] + ]); + $app->registerMetadata($executedMediationActions, Delivery::class); + + // Unidades comercializadas (executado) + $executedCommercialUnits = new Metadata('executedCommercialUnits', [ + 'label' => i::__('Quantidade de unidades efetivamente comercializadas'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => i::__('Deve ser um número maior ou igual a zero') + ] + ]); + $app->registerMetadata($executedCommercialUnits, Delivery::class); + + // Valor unitário executado + $executedUnitPrice = new Metadata('executedUnitPrice', [ + 'label' => i::__('Valor unitário praticado (R$)'), + 'type' => 'currency' + ]); + $app->registerMetadata($executedUnitPrice, Delivery::class); + + // Pessoas remuneradas por função (executado) + $executedPaidStaffByRole = new Metadata('executedPaidStaffByRole', [ + 'label' => i::__('Quantas pessoas foram remuneradas, por função?'), + 'type' => 'json', + 'serialize' => function ($val) { + return json_encode($val); + }, + 'unserialize' => function($val) { + return json_decode((string) $val, true); + } + ]); + $app->registerMetadata($executedPaidStaffByRole, Delivery::class); + + // Composição da equipe por gênero (executado) + $executedTeamCompositionGender = new Metadata('executedTeamCompositionGender', [ + 'label' => i::__('Composição efetiva da equipe por gênero'), + 'type' => 'json', + 'serialize' => function ($val) { + return json_encode($val); + }, + 'unserialize' => function($val) { + return json_decode((string) $val, true); + } + ]); + $app->registerMetadata($executedTeamCompositionGender, Delivery::class); + + // Composição da equipe por raça/cor (executado) + $executedTeamCompositionRace = new Metadata('executedTeamCompositionRace', [ + 'label' => i::__('Composição efetiva da equipe por raça/cor'), + 'type' => 'json', + 'serialize' => function ($val) { + return json_encode($val); + }, + 'unserialize' => function($val) { + return json_decode((string) $val, true); + } + ]); + $app->registerMetadata($executedTeamCompositionRace, Delivery::class); + + // Medidas de acessibilidade executadas (já existe accessibilityMeasures) + // Este campo já existe e será usado para os dados executados + // Metadados para Registration (Inscrição) $this->registerRegistrationMetadata('workplanSnapshot', [ 'label' => i::__('Snapshot do plano de trabalho'), @@ -420,6 +513,16 @@ public function register() { $delivery->numberOfParticipants = $data['numberOfParticipants']; $delivery->participantProfile = $data['participantProfile']; $delivery->priorityAudience = $data['priorityAudience']; + + // Novos campos executados + $delivery->executedNumberOfCities = $data['executedNumberOfCities'] ?? null; + $delivery->executedNumberOfNeighborhoods = $data['executedNumberOfNeighborhoods'] ?? null; + $delivery->executedMediationActions = $data['executedMediationActions'] ?? null; + $delivery->executedCommercialUnits = $data['executedCommercialUnits'] ?? null; + $delivery->executedUnitPrice = $data['executedUnitPrice'] ?? null; + $delivery->executedPaidStaffByRole = $data['executedPaidStaffByRole'] ?? null; + $delivery->executedTeamCompositionGender = $data['executedTeamCompositionGender'] ?? null; + $delivery->executedTeamCompositionRace = $data['executedTeamCompositionRace'] ?? null; } $app->hook('entity(Registration).save:finish', function() use ($goals, $deliveries, $first_phase, $app) { @@ -486,7 +589,17 @@ public function register() { 'numberOfParticipants' => $delivery->numberOfParticipants, 'participantProfile' => $delivery->participantProfile, 'priorityAudience' => $delivery->priorityAudience, - 'status' => $delivery->status + 'status' => $delivery->status, + + // Novos campos executados + 'executedNumberOfCities' => $delivery->executedNumberOfCities, + 'executedNumberOfNeighborhoods' => $delivery->executedNumberOfNeighborhoods, + 'executedMediationActions' => $delivery->executedMediationActions, + 'executedCommercialUnits' => $delivery->executedCommercialUnits, + 'executedUnitPrice' => $delivery->executedUnitPrice, + 'executedPaidStaffByRole' => $delivery->executedPaidStaffByRole, + 'executedTeamCompositionGender' => $delivery->executedTeamCompositionGender, + 'executedTeamCompositionRace' => $delivery->executedTeamCompositionRace, ]; } From 54ce129aad234f7554beb5185b9f756b817acfc0 Mon Sep 17 00:00:00 2001 From: Victor Ferreira Date: Thu, 12 Feb 2026 23:04:54 -0300 Subject: [PATCH 02/60] =?UTF-8?q?feat:=20implementa=2023=20novos=20campos?= =?UTF-8?q?=20configur=C3=A1veis=20para=20entregas=20(planejamento=20e=20m?= =?UTF-8?q?onitoramento)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/WorkplanService.php | 26 +++ .../opportunity-enable-workplan/template.php | 104 +++++++++ .../template.php | 47 ++++ .../registration-workplan/script.js | 74 ++++++ .../registration-workplan/template.php | 215 ++++++++++++++++++ 5 files changed, 466 insertions(+) diff --git a/src/modules/OpportunityWorkplan/Services/WorkplanService.php b/src/modules/OpportunityWorkplan/Services/WorkplanService.php index 1d74162eec..a23bb5645a 100644 --- a/src/modules/OpportunityWorkplan/Services/WorkplanService.php +++ b/src/modules/OpportunityWorkplan/Services/WorkplanService.php @@ -63,6 +63,32 @@ public function save($registration, $workplan, $data) $delivery->renevueQtd = $d['renevueQtd'] ?? null; $delivery->unitValueForecast = $d['unitValueForecast'] ?? null; $delivery->totalValueForecast = $d['totalValueForecast'] ?? null; + + // Novos campos de planejamento + $delivery->artChainLink = $d['artChainLink'] ?? null; + $delivery->totalBudget = $d['totalBudget'] ?? null; + $delivery->numberOfCities = $d['numberOfCities'] ?? null; + $delivery->numberOfNeighborhoods = $d['numberOfNeighborhoods'] ?? null; + $delivery->mediationActions = $d['mediationActions'] ?? null; + $delivery->paidStaffByRole = $d['paidStaffByRole'] ?? null; + $delivery->teamCompositionGender = $d['teamCompositionGender'] ?? null; + $delivery->teamCompositionRace = $d['teamCompositionRace'] ?? null; + $delivery->revenueType = $d['revenueType'] ?? null; + $delivery->commercialUnits = $d['commercialUnits'] ?? null; + $delivery->unitPrice = $d['unitPrice'] ?? null; + $delivery->hasCommunityCoauthors = $d['hasCommunityCoauthors'] ?? null; + $delivery->hasTransInclusionStrategy = $d['hasTransInclusionStrategy'] ?? null; + $delivery->transInclusionActions = $d['transInclusionActions'] ?? null; + $delivery->hasAccessibilityPlan = $d['hasAccessibilityPlan'] ?? null; + $delivery->expectedAccessibilityMeasures = $d['expectedAccessibilityMeasures'] ?? null; + $delivery->hasEnvironmentalPractices = $d['hasEnvironmentalPractices'] ?? null; + $delivery->environmentalPracticesDescription = $d['environmentalPracticesDescription'] ?? null; + $delivery->hasPressStrategy = $d['hasPressStrategy'] ?? null; + $delivery->communicationChannels = $d['communicationChannels'] ?? null; + $delivery->hasInnovationAction = $d['hasInnovationAction'] ?? null; + $delivery->innovationTypes = $d['innovationTypes'] ?? null; + $delivery->documentationTypes = $d['documentationTypes'] ?? null; + $delivery->goal = $goal; $delivery->save(true); } diff --git a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php index 8b05a74074..bfd689b085 100644 --- a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php +++ b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php @@ -235,6 +235,110 @@ + +

+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php b/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php index 5b3a6ddc1d..147bab3894 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php +++ b/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php @@ -122,6 +122,53 @@ {{ convertToCurrency(executedRevenue) }} {{ validationErrors.executedRevenue.join('; ') }} + + + +
+ +
+ {{ delivery.numberOfCities }} +
+ + {{ proxy.executedNumberOfCities }} +
+ +
+ +
+ {{ delivery.numberOfNeighborhoods }} +
+ + {{ proxy.executedNumberOfNeighborhoods }} +
+ +
+ +
+ {{ delivery.mediationActions }} +
+ + {{ proxy.executedMediationActions }} +
+ +
+ +
+ {{ delivery.commercialUnits }} +
+ + {{ proxy.executedCommercialUnits }} +
+ +
+ +
+ {{ convertToCurrency(delivery.unitPrice) }} +
+ + {{ convertToCurrency(proxy.executedUnitPrice) }} +
diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan/script.js b/src/modules/OpportunityWorkplan/components/registration-workplan/script.js index 5bc6f424ce..d0e8a4594d 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan/script.js +++ b/src/modules/OpportunityWorkplan/components/registration-workplan/script.js @@ -142,6 +142,43 @@ app.component('registration-workplan', { entityDelivery.renevueQtd = null; entityDelivery.unitValueForecast = null; entityDelivery.totalValueForecast = null; + + // Novos campos de planejamento + entityDelivery.artChainLink = null; + entityDelivery.totalBudget = null; + entityDelivery.numberOfCities = null; + entityDelivery.numberOfNeighborhoods = null; + entityDelivery.mediationActions = null; + entityDelivery.paidStaffByRole = []; + entityDelivery.teamCompositionGender = { + masculine: 0, + feminine: 0, + nonBinary: 0, + notDeclared: 0 + }; + entityDelivery.teamCompositionRace = { + white: 0, + black: 0, + brown: 0, + indigenous: 0, + asian: 0, + notDeclared: 0 + }; + entityDelivery.revenueType = []; + entityDelivery.commercialUnits = null; + entityDelivery.unitPrice = null; + entityDelivery.hasCommunityCoauthors = null; + entityDelivery.hasTransInclusionStrategy = null; + entityDelivery.transInclusionActions = null; + entityDelivery.hasAccessibilityPlan = null; + entityDelivery.expectedAccessibilityMeasures = []; + entityDelivery.hasEnvironmentalPractices = null; + entityDelivery.environmentalPracticesDescription = null; + entityDelivery.hasPressStrategy = null; + entityDelivery.communicationChannels = []; + entityDelivery.hasInnovationAction = null; + entityDelivery.innovationTypes = []; + entityDelivery.documentationTypes = []; goal.deliveries.push(entityDelivery); }, @@ -558,5 +595,42 @@ app.component('registration-workplan', { enableTutorial() { localStorage.setItem('tutorialDisabled', 'false'); }, + + // ============================================ + // MÉTODOS PARA NOVOS CAMPOS ESTRUTURADOS + // ============================================ + + // Pessoas remuneradas por função + addPaidStaffRole(delivery) { + if (!delivery.paidStaffByRole) { + delivery.paidStaffByRole = []; + } + delivery.paidStaffByRole.push({ role: '', count: 0 }); + }, + removePaidStaffRole(delivery, index) { + delivery.paidStaffByRole.splice(index, 1); + }, + + // Calcular total de composição por gênero + calculateGenderTotal(composition) { + if (!composition) return 0; + const total = (composition.masculine || 0) + + (composition.feminine || 0) + + (composition.nonBinary || 0) + + (composition.notDeclared || 0); + return total; + }, + + // Calcular total de composição por raça/cor + calculateRaceTotal(composition) { + if (!composition) return 0; + const total = (composition.white || 0) + + (composition.black || 0) + + (composition.brown || 0) + + (composition.indigenous || 0) + + (composition.asian || 0) + + (composition.notDeclared || 0); + return total; + }, }, }) \ No newline at end of file diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan/template.php b/src/modules/OpportunityWorkplan/components/registration-workplan/template.php index ed4f9c7a5e..a161e709dd 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan/template.php +++ b/src/modules/OpportunityWorkplan/components/registration-workplan/template.php @@ -192,6 +192,94 @@
+ +
+ +
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ {{ calculateGenderTotal(delivery.teamCompositionGender) }} +
+
+ + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ {{ calculateRaceTotal(delivery.teamCompositionRace) }} +
+
+
+ + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
From 9dc445351677f377f456ab1179dba3db929fc679 Mon Sep 17 00:00:00 2001 From: Victor Ferreira Date: Thu, 12 Feb 2026 23:42:53 -0300 Subject: [PATCH 03/60] =?UTF-8?q?fix:=20move=20checkboxes=20de=20novos=20c?= =?UTF-8?q?ampos=20para=20se=C3=A7=C3=A3o=20correta=20e=20adiciona=20confi?= =?UTF-8?q?gs=20de=20monitoramento?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/OpportunityWorkplan/Module.php | 40 +++++++ .../opportunity-enable-workplan/template.php | 104 ++++++++++++------ 2 files changed, 111 insertions(+), 33 deletions(-) diff --git a/src/modules/OpportunityWorkplan/Module.php b/src/modules/OpportunityWorkplan/Module.php index 43b5fd5184..1a3af7459d 100644 --- a/src/modules/OpportunityWorkplan/Module.php +++ b/src/modules/OpportunityWorkplan/Module.php @@ -323,6 +323,46 @@ function register() 'default_value' => false ]); + // ============================================ + // CONFIGURAÇÕES PARA MONITORAMENTO (CAMPOS EXECUTADOS) + // ============================================ + + $this->registerOpportunityMetadata('workplan_monitoringInformNumberOfCities', [ + 'label' => i::__('Informar número de municípios executados'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformNumberOfNeighborhoods', [ + 'label' => i::__('Informar número de bairros executados'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformMediationActions', [ + 'label' => i::__('Informar ações de mediação executadas'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformCommercialUnits', [ + 'label' => i::__('Informar unidades comercializadas executadas'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformPaidStaffByRole', [ + 'label' => i::__('Informar pessoas remuneradas executadas por função'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformTeamComposition', [ + 'label' => i::__('Informar composição da equipe executada (gênero e raça/cor)'), + 'type' => 'boolean', + 'default_value' => false + ]); + $app->registerFileGroup('delivery', new \MapasCulturais\Definitions\FileGroup('evidences')); // metadados workplan diff --git a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php index bfd689b085..26474a1e51 100644 --- a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php +++ b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php @@ -200,143 +200,181 @@ - - -
-

-
text('header-description', i::__('As informações marcadas abaixo serão obrigatórias no monitoramento da oportunidade.')) ?>
-
+ +

+
- +
- +
- +
- +
-

+
+ +
+
+
+
+
+

+
text('header-description', i::__('As informações marcadas abaixo serão obrigatórias no monitoramento da oportunidade.')) ?>
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+

+
From d3bbcaeb1815d426199bdd10c82d570a0342e7c9 Mon Sep 17 00:00:00 2001 From: Victor Ferreira Date: Wed, 25 Feb 2026 22:36:46 -0300 Subject: [PATCH 04/60] =?UTF-8?q?feat:=20torna=20segmento=20art=C3=ADstico?= =?UTF-8?q?-cultural=20configur=C3=A1vel=20no=20plano=20de=20trabalho?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/OpportunityWorkplan/Module.php | 8 +++++++- .../components/opportunity-enable-workplan/script.js | 1 + .../components/opportunity-enable-workplan/template.php | 6 ++++++ .../components/registration-workplan/script.js | 5 ++++- .../components/registration-workplan/template.php | 2 +- 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/modules/OpportunityWorkplan/Module.php b/src/modules/OpportunityWorkplan/Module.php index 1a3af7459d..718d210639 100644 --- a/src/modules/OpportunityWorkplan/Module.php +++ b/src/modules/OpportunityWorkplan/Module.php @@ -51,7 +51,7 @@ function _init(){ $errors['projectDuration'] = [i::__('Plano de metas - Duração do projeto (meses) obrigatório.')]; } - if (!$workplan?->culturalArtisticSegment) { + if ($registration->opportunity->workplan_dataProjectInformCulturalArtisticSegment && !$workplan?->culturalArtisticSegment) { $errors['culturalArtisticSegment'] = [i::__('Plano de metas - Segmento artistico-cultural obrigatório.')]; } @@ -115,6 +115,12 @@ function register() ]); + $this->registerOpportunityMetadata('workplan_dataProjectInformCulturalArtisticSegment', [ + 'label' => i::__('Informar segmento artístico-cultural'), + 'type' => 'boolean', + 'default_value' => false + ]); + $this->registerOpportunityMetadata('workplan_dataProjectlimitMaximumDurationOfProjects', [ 'label' => i::__('Limitar duração máxima dos projetos'), 'type' => 'boolean', diff --git a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/script.js b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/script.js index 2942db51e4..5bbaa44c12 100644 --- a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/script.js +++ b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/script.js @@ -82,6 +82,7 @@ app.component('opportunity-enable-workplan', { disabledWorkPlan() { this.entity.workplan_dataProjectlimitMaximumDurationOfProjects = false; this.entity.workplan_dataProjectmaximumDurationInMonths = 0; + this.entity.workplan_dataProjectInformCulturalArtisticSegment = false; this.entity.workplan_metaInformTheStageOfCulturalMaking = false; this.entity.workplan_metaLimitNumberOfGoals = false; diff --git a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php index 26474a1e51..0ca92c920e 100644 --- a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php +++ b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php @@ -66,6 +66,12 @@

+
+ +
+
-
+
+
@@ -114,6 +117,24 @@ {{ `As ${getGoalLabelDefault} são constituídas por uma ou mais ${getDeliveryLabelDefault}` }}
+
+ + +
+ +
+ + +
+
+
+ + +
+
-
- - +
+
+ + +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
@@ -280,65 +336,6 @@
-
-
- - -
- -
-
- - -
- -
- - -
- -
- - -
-
-
- - - -
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- - -
-
From 7e8e6b735314e1f118c4913ae01a75c784d15cd4 Mon Sep 17 00:00:00 2001 From: Victor Ferreira Date: Thu, 26 Feb 2026 17:25:26 -0300 Subject: [PATCH 10/60] fix(workplan): valida novos campos conforme flags de obrigatoriedade configurados --- .../registration-workplan/script.js | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan/script.js b/src/modules/OpportunityWorkplan/components/registration-workplan/script.js index 5d31f07bea..07aa9f21c1 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan/script.js +++ b/src/modules/OpportunityWorkplan/components/registration-workplan/script.js @@ -317,12 +317,34 @@ app.component('registration-workplan', { if ('name' in delivery && !delivery.name) emptyFields.push(`Nome da ${this.getDeliveryLabelDefault}`); if ('description' in delivery && !delivery.description) emptyFields.push("Descrição"); if ('typeDelivery' in delivery && !delivery.typeDelivery) emptyFields.push(`Tipo de ${this.getDeliveryLabelDefault}`); - if (this.opportunity.workplan_registrationInformCulturalArtisticSegment && 'segmentDelivery' in delivery && !delivery.segmentDelivery) emptyFields.push(`Segmento artístico-cultural da ${this.getDeliveryLabelDefault}`); - if (this.opportunity.workplan_registrationReportTheNumberOfParticipants && 'expectedNumberPeople' in delivery && !delivery.expectedNumberPeople) emptyFields.push("Número previsto de pessoas"); + + // Campos configuráveis: só valida se habilitado E obrigatório + if (this.opportunity.workplan_registrationInformCulturalArtisticSegment && this.opportunity.workplan_deliveryRequireSegment && !delivery.segmentDelivery) emptyFields.push(`Segmento artístico-cultural da ${this.getDeliveryLabelDefault}`); + if (this.opportunity.workplan_registrationReportTheNumberOfParticipants && this.opportunity.workplan_deliveryRequireExpectedNumberPeople && !delivery.expectedNumberPeople) emptyFields.push("Número previsto de pessoas"); if (this.opportunity.workplan_registrationReportExpectedRenevue && 'generaterRevenue' in delivery && !delivery.generaterRevenue) emptyFields.push(`A ${this.getDeliveryLabelDefault} irá gerar receita?`); if (delivery.generaterRevenue == 'true' && 'renevueQtd' in delivery && !delivery.renevueQtd) emptyFields.push("Quantidade"); if (delivery.generaterRevenue == 'true' && 'unitValueForecast' in delivery && !delivery.unitValueForecast) emptyFields.push("Previsão de valor unitário"); if (delivery.generaterRevenue == 'true' && 'totalValueForecast' in delivery && !delivery.totalValueForecast) emptyFields.push("Previsão de valor total"); + + // Novos campos configuráveis + if (this.opportunity.workplan_deliveryInformArtChainLink && this.opportunity.workplan_deliveryRequireArtChainLink && !delivery.artChainLink) emptyFields.push("Principal elo das artes acionado"); + if (this.opportunity.workplan_deliveryInformTotalBudget && this.opportunity.workplan_deliveryRequireTotalBudget && !delivery.totalBudget) emptyFields.push("Orçamento total da atividade"); + if (this.opportunity.workplan_deliveryInformNumberOfCities && this.opportunity.workplan_deliveryRequireNumberOfCities && (delivery.numberOfCities === null || delivery.numberOfCities === '')) emptyFields.push("Número de municípios"); + if (this.opportunity.workplan_deliveryInformNumberOfNeighborhoods && this.opportunity.workplan_deliveryRequireNumberOfNeighborhoods && (delivery.numberOfNeighborhoods === null || delivery.numberOfNeighborhoods === '')) emptyFields.push("Número de bairros"); + if (this.opportunity.workplan_deliveryInformMediationActions && this.opportunity.workplan_deliveryRequireMediationActions && (delivery.mediationActions === null || delivery.mediationActions === '')) emptyFields.push("Ações de mediação/formação de público"); + if (this.opportunity.workplan_deliveryInformPaidStaffByRole && this.opportunity.workplan_deliveryRequirePaidStaffByRole && (!Array.isArray(delivery.paidStaffByRole) || !delivery.paidStaffByRole.length)) emptyFields.push("Pessoas remuneradas por função"); + if (this.opportunity.workplan_deliveryInformTeamComposition && this.opportunity.workplan_deliveryRequireTeamCompositionGender && (!delivery.teamCompositionGender || !this.calculateGenderTotal(delivery.teamCompositionGender))) emptyFields.push("Composição da equipe por gênero"); + if (this.opportunity.workplan_deliveryInformTeamComposition && this.opportunity.workplan_deliveryRequireTeamCompositionRace && (!delivery.teamCompositionRace || !this.calculateRaceTotal(delivery.teamCompositionRace))) emptyFields.push("Composição da equipe por raça/cor"); + if (this.opportunity.workplan_deliveryInformRevenueType && this.opportunity.workplan_deliveryRequireRevenueType && (!Array.isArray(delivery.revenueType) || !delivery.revenueType.length)) emptyFields.push("Tipo de receita previsto"); + if (this.opportunity.workplan_deliveryInformCommercialUnits && this.opportunity.workplan_deliveryRequireCommercialUnits && (delivery.commercialUnits === null || delivery.commercialUnits === '')) emptyFields.push("Quantidade de unidades para comercialização"); + if (this.opportunity.workplan_deliveryInformCommercialUnits && this.opportunity.workplan_deliveryRequireUnitPrice && !delivery.unitPrice) emptyFields.push("Valor unitário previsto"); + if (this.opportunity.workplan_deliveryInformCommunityCoauthors && this.opportunity.workplan_deliveryRequireCommunityCoauthorsDetail && !delivery.hasCommunityCoauthors) emptyFields.push("Envolvimento de comunidades como coautores"); + if (this.opportunity.workplan_deliveryInformTransInclusion && delivery.hasTransInclusionStrategy === 'true' && this.opportunity.workplan_deliveryRequireTransInclusionActions && !delivery.transInclusionActions) emptyFields.push("Ações de inclusão Trans e Travestis"); + if (this.opportunity.workplan_deliveryInformAccessibilityPlan && delivery.hasAccessibilityPlan === 'true' && this.opportunity.workplan_deliveryRequireExpectedAccessibilityMeasures && (!Array.isArray(delivery.expectedAccessibilityMeasures) || !delivery.expectedAccessibilityMeasures.length)) emptyFields.push("Medidas de acessibilidade previstas"); + if (this.opportunity.workplan_deliveryInformEnvironmentalPractices && delivery.hasEnvironmentalPractices === 'true' && this.opportunity.workplan_deliveryRequireEnvironmentalPracticesDescription && !delivery.environmentalPracticesDescription) emptyFields.push("Descrição de práticas socioambientais"); + if (this.opportunity.workplan_deliveryInformCommunicationChannels && this.opportunity.workplan_deliveryRequireCommunicationChannels && (!Array.isArray(delivery.communicationChannels) || !delivery.communicationChannels.length)) emptyFields.push("Canais de comunicação"); + if (this.opportunity.workplan_deliveryInformInnovation && delivery.hasInnovationAction === 'true' && this.opportunity.workplan_deliveryRequireInnovationTypes && (!Array.isArray(delivery.innovationTypes) || !delivery.innovationTypes.length)) emptyFields.push("Tipos de experimentação/inovação"); + if (this.opportunity.workplan_deliveryInformDocumentationTypes && this.opportunity.workplan_deliveryRequireDocumentationTypes && (!Array.isArray(delivery.documentationTypes) || !delivery.documentationTypes.length)) emptyFields.push("Tipo de documentação"); if (emptyFields.length > 0) { const emptyFieldsList = `
    ${emptyFields.map(item => `
  • ${item}
  • `).join('')}
`; From 5ca152d32ec16fa646c10059c6ad9b5bae7e8c57 Mon Sep 17 00:00:00 2001 From: Lucas Pirola Date: Thu, 26 Feb 2026 18:15:59 -0300 Subject: [PATCH 11/60] fix(workplan): aplica decode do retorno da api --- .../registration-workplan/script.js | 106 ++++++++++++++---- 1 file changed, 87 insertions(+), 19 deletions(-) diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan/script.js b/src/modules/OpportunityWorkplan/components/registration-workplan/script.js index 07aa9f21c1..5194487d22 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan/script.js +++ b/src/modules/OpportunityWorkplan/components/registration-workplan/script.js @@ -77,39 +77,99 @@ app.component('registration-workplan', { const response = api.GET(`${this.registration.id}`); response.then((res) => res.json().then((data) => { if (data.workplan != null) { + this.ensureDeliveryFieldsInitialized(data.workplan); this.workplan = data.workplan; - this.ensureDeliveryFieldsInitialized(); this.updateEnableButtonNewGoal(); } })); }, - ensureDeliveryFieldsInitialized() { + ensureDeliveryFieldsInitialized(workplan = null) { // Ensure all goals and their deliveries have the new fields initialized - if (!this.workplan.goals) return; + const wp = workplan || this.workplan; + if (!wp.goals) return; - this.workplan.goals.forEach(goal => { + wp.goals.forEach(goal => { if (!goal.deliveries) return; goal.deliveries.forEach(delivery => { // Initialize array fields (required for mc-multiselect) + // Parse JSON strings if needed + if (typeof delivery.revenueType === 'string') { + delivery.revenueType = JSON.parse(delivery.revenueType || '[]'); + } if (!Array.isArray(delivery.revenueType)) delivery.revenueType = []; + + if (typeof delivery.expectedAccessibilityMeasures === 'string') { + delivery.expectedAccessibilityMeasures = JSON.parse(delivery.expectedAccessibilityMeasures || '[]'); + } if (!Array.isArray(delivery.expectedAccessibilityMeasures)) delivery.expectedAccessibilityMeasures = []; + + if (typeof delivery.communicationChannels === 'string') { + delivery.communicationChannels = JSON.parse(delivery.communicationChannels || '[]'); + } if (!Array.isArray(delivery.communicationChannels)) delivery.communicationChannels = []; + + if (typeof delivery.innovationTypes === 'string') { + delivery.innovationTypes = JSON.parse(delivery.innovationTypes || '[]'); + } if (!Array.isArray(delivery.innovationTypes)) delivery.innovationTypes = []; + + if (typeof delivery.documentationTypes === 'string') { + delivery.documentationTypes = JSON.parse(delivery.documentationTypes || '[]'); + } if (!Array.isArray(delivery.documentationTypes)) delivery.documentationTypes = []; - // Initialize object fields + // Initialize paidStaffByRole + if (typeof delivery.paidStaffByRole === 'string') { + delivery.paidStaffByRole = JSON.parse(delivery.paidStaffByRole || '[]'); + } if (!Array.isArray(delivery.paidStaffByRole)) delivery.paidStaffByRole = []; - if (!delivery.teamCompositionGender) { + + // Initialize teamCompositionGender - PARSE JSON STRING FROM API + if (typeof delivery.teamCompositionGender === 'string') { + try { + delivery.teamCompositionGender = JSON.parse(delivery.teamCompositionGender); + } catch (e) { + delivery.teamCompositionGender = { + masculine: 0, + feminine: 0, + nonBinary: 0, + notDeclared: 0 + }; + } + } + if (!delivery.teamCompositionGender || typeof delivery.teamCompositionGender !== 'object') { delivery.teamCompositionGender = { masculine: 0, feminine: 0, nonBinary: 0, notDeclared: 0 }; + } else { + // Ensure all properties exist and convert to numbers + delivery.teamCompositionGender.masculine = Number(delivery.teamCompositionGender.masculine) || 0; + delivery.teamCompositionGender.feminine = Number(delivery.teamCompositionGender.feminine) || 0; + delivery.teamCompositionGender.nonBinary = Number(delivery.teamCompositionGender.nonBinary) || 0; + delivery.teamCompositionGender.notDeclared = Number(delivery.teamCompositionGender.notDeclared) || 0; } - if (!delivery.teamCompositionRace) { + + // Initialize teamCompositionRace - PARSE JSON STRING FROM API + if (typeof delivery.teamCompositionRace === 'string') { + try { + delivery.teamCompositionRace = JSON.parse(delivery.teamCompositionRace); + } catch (e) { + delivery.teamCompositionRace = { + white: 0, + black: 0, + brown: 0, + indigenous: 0, + asian: 0, + notDeclared: 0 + }; + } + } + if (!delivery.teamCompositionRace || typeof delivery.teamCompositionRace !== 'object') { delivery.teamCompositionRace = { white: 0, black: 0, @@ -118,6 +178,14 @@ app.component('registration-workplan', { asian: 0, notDeclared: 0 }; + } else { + // Ensure all properties exist and convert to numbers + delivery.teamCompositionRace.white = Number(delivery.teamCompositionRace.white) || 0; + delivery.teamCompositionRace.black = Number(delivery.teamCompositionRace.black) || 0; + delivery.teamCompositionRace.brown = Number(delivery.teamCompositionRace.brown) || 0; + delivery.teamCompositionRace.indigenous = Number(delivery.teamCompositionRace.indigenous) || 0; + delivery.teamCompositionRace.asian = Number(delivery.teamCompositionRace.asian) || 0; + delivery.teamCompositionRace.notDeclared = Number(delivery.teamCompositionRace.notDeclared) || 0; } // Initialize simple fields if they don't exist @@ -371,8 +439,8 @@ app.component('registration-workplan', { const response = api.POST(`save`, data); response.then((res) => res.json().then((data) => { + this.ensureDeliveryFieldsInitialized(data.workplan); this.workplan = data.workplan; - this.ensureDeliveryFieldsInitialized(); this.updateEnableButtonNewGoal(); messages.success(this.text('Modificações salvas')); })); @@ -697,22 +765,22 @@ app.component('registration-workplan', { // Calcular total de composição por gênero calculateGenderTotal(composition) { if (!composition) return 0; - const total = (composition.masculine || 0) + - (composition.feminine || 0) + - (composition.nonBinary || 0) + - (composition.notDeclared || 0); + const total = (Number(composition.masculine) || 0) + + (Number(composition.feminine) || 0) + + (Number(composition.nonBinary) || 0) + + (Number(composition.notDeclared) || 0); return total; }, - + // Calcular total de composição por raça/cor calculateRaceTotal(composition) { if (!composition) return 0; - const total = (composition.white || 0) + - (composition.black || 0) + - (composition.brown || 0) + - (composition.indigenous || 0) + - (composition.asian || 0) + - (composition.notDeclared || 0); + const total = (Number(composition.white) || 0) + + (Number(composition.black) || 0) + + (Number(composition.brown) || 0) + + (Number(composition.indigenous) || 0) + + (Number(composition.asian) || 0) + + (Number(composition.notDeclared) || 0); return total; }, }, From 54d1a7c69fb3c3f1f6d6fb2f249eaf2aea6bd1d5 Mon Sep 17 00:00:00 2001 From: Lucas Pirola Date: Thu, 26 Feb 2026 18:20:34 -0300 Subject: [PATCH 12/60] =?UTF-8?q?fix(workplan):=20ado=C3=A7=C3=A3o=20do=20?= =?UTF-8?q?componente=20mc-tag-list=20para=20melhor=20visualizar=20as=20op?= =?UTF-8?q?=C3=A7=C3=B5es=20selecionadas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../registration-workplan/script.js | 29 +++++++++++++++++++ .../registration-workplan/template.php | 16 ++++++---- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan/script.js b/src/modules/OpportunityWorkplan/components/registration-workplan/script.js index 5194487d22..79315590e5 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan/script.js +++ b/src/modules/OpportunityWorkplan/components/registration-workplan/script.js @@ -783,5 +783,34 @@ app.component('registration-workplan', { (Number(composition.notDeclared) || 0); return total; }, + + // ============================================ + // MÉTODOS HELPER PARA MULTISELECT + // ============================================ + + // Remove item de um array multiselect + removeMultiselectItem(array, key) { + const index = array.indexOf(key); + if (index > -1) { + array.splice(index, 1); + } + }, + + // Toggle item em array multiselect (usado pelos mc-tag-list) + toggleRevenueType(delivery, key) { + this.removeMultiselectItem(delivery.revenueType, key); + }, + toggleAccessibilityMeasures(delivery, key) { + this.removeMultiselectItem(delivery.expectedAccessibilityMeasures, key); + }, + toggleCommunicationChannels(delivery, key) { + this.removeMultiselectItem(delivery.communicationChannels, key); + }, + toggleInnovationTypes(delivery, key) { + this.removeMultiselectItem(delivery.innovationTypes, key); + }, + toggleDocumentationTypes(delivery, key) { + this.removeMultiselectItem(delivery.documentationTypes, key); + }, }, }) \ No newline at end of file diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan/template.php b/src/modules/OpportunityWorkplan/components/registration-workplan/template.php index dea9b7bb6c..d8498b34a0 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan/template.php +++ b/src/modules/OpportunityWorkplan/components/registration-workplan/template.php @@ -13,6 +13,7 @@ mc-icon mc-confirm-button mc-currency-input + mc-tag-list '); ?> @@ -338,7 +339,8 @@
- + +
@@ -384,7 +386,8 @@
- + +
@@ -412,7 +415,8 @@
- + +
@@ -425,13 +429,15 @@
- + +
- + +
From 58088ad59dc6bb9e48aad1f61363f27bd719726d Mon Sep 17 00:00:00 2001 From: Victor Ferreira Date: Thu, 26 Feb 2026 17:44:10 -0300 Subject: [PATCH 13/60] =?UTF-8?q?fix(workplan):=20renomeia=20styles.css=20?= =?UTF-8?q?para=20style.css=20e=20ajusta=20layout=20inline=20do=20check=20?= =?UTF-8?q?obrigat=C3=B3rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{styles.css => style.css} | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) rename src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/{styles.css => style.css} (57%) diff --git a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/styles.css b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/style.css similarity index 57% rename from src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/styles.css rename to src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/style.css index 2d9ac55106..210b450b12 100644 --- a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/styles.css +++ b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/style.css @@ -1,9 +1,10 @@ /* Estilos para checkboxes de obrigatoriedade (sub-checkboxes) */ .field__checkbox--sub { - margin-left: 30px; + margin-left: 16px; font-size: 0.9em; color: #666; font-weight: normal; + white-space: nowrap; } .field__checkbox--sub input[type="checkbox"] { @@ -12,23 +13,32 @@ /* Animação suave para aparecer/desaparecer */ .field__checkbox--sub { - animation: fadeIn 0.3s ease-in; + animation: fadeIn 0.2s ease-in; } @keyframes fadeIn { from { opacity: 0; - transform: translateY(-5px); + transform: translateX(-4px); } to { opacity: 1; - transform: translateY(0); + transform: translateX(0); } } -/* Estilo para indicar agrupamento visual */ +/* Agrupamento inline: campo principal + obrigatório na mesma linha */ .field__group { + display: flex; + flex-direction: row; + align-items: center; + flex-wrap: wrap; margin-bottom: 12px; + gap: 0; +} + +.field__group .field__checkbox { + flex-shrink: 0; } .field__group .field__checkbox--sub:last-child { From 973657b4f62877b41f673d8e99d976100ea81588 Mon Sep 17 00:00:00 2001 From: Lucas Pirola Date: Thu, 26 Feb 2026 18:33:46 -0300 Subject: [PATCH 14/60] fix(workplan): adota mc-tag-list em mais campos --- .../registration-workplan-form-delivery/template.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php b/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php index 147bab3894..a790466238 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php +++ b/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php @@ -90,8 +90,8 @@
- - + + {{ validationErrors.accessibilityMeasures.join('; ') }}
@@ -104,8 +104,8 @@
- - + + {{ validationErrors.priorityAudience.join('; ') }}
From b4f159f1de77a487c242d89ea571910fad9c057f Mon Sep 17 00:00:00 2001 From: Lucas Pirola Date: Thu, 26 Feb 2026 18:51:45 -0300 Subject: [PATCH 15/60] =?UTF-8?q?feat(workplan):=20Refinamento=20no=20comp?= =?UTF-8?q?onente=20de=20fun=C3=A7=C3=A3o=20remenuderada?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/OpportunityWorkplan/Module.php | 102 ++++++++++++++++++ .../template.php | 11 +- .../registration-workplan/script.js | 33 +++++- .../registration-workplan/template.php | 44 +++++--- 4 files changed, 172 insertions(+), 18 deletions(-) diff --git a/src/modules/OpportunityWorkplan/Module.php b/src/modules/OpportunityWorkplan/Module.php index 0243baaa05..48daa78a39 100644 --- a/src/modules/OpportunityWorkplan/Module.php +++ b/src/modules/OpportunityWorkplan/Module.php @@ -1079,6 +1079,108 @@ function register() $paidStaffByRole = new Metadata('paidStaffByRole', [ 'label' => \MapasCulturais\i::__('Quantas pessoas serão remuneradas, por função?'), 'type' => 'json', + 'options' => [ + // Direção e Gestão + \MapasCulturais\i::__('Diretor Artístico'), + \MapasCulturais\i::__('Diretor de Arte'), + \MapasCulturais\i::__('Diretor Musical'), + \MapasCulturais\i::__('Produtor Cultural'), + \MapasCulturais\i::__('Produtor Musical'), + \MapasCulturais\i::__('Produtor Audiovisual'), + \MapasCulturais\i::__('Gestor Cultural'), + \MapasCulturais\i::__('Curador'), + \MapasCulturais\i::__('Assistente de produção'), + \MapasCulturais\i::__('Assistente de direção'), + + // Artes Cênicas + \MapasCulturais\i::__('Ator/Atriz'), + \MapasCulturais\i::__('Bailarino'), + \MapasCulturais\i::__('Dançarino'), + \MapasCulturais\i::__('Coreógrafo'), + \MapasCulturais\i::__('Dramaturgo'), + \MapasCulturais\i::__('Iluminador'), + \MapasCulturais\i::__('Cenotécnico'), + \MapasCulturais\i::__('Figurinista'), + \MapasCulturais\i::__('Maquiador'), + \MapasCulturais\i::__('Contra-regra'), + \MapasCulturais\i::__('Assistente de palco'), + + // Música + \MapasCulturais\i::__('Músico/Musicista'), + \MapasCulturais\i::__('Cantor'), + \MapasCulturais\i::__('Compositor'), + \MapasCulturais\i::__('Arranjador'), + \MapasCulturais\i::__('Maestro'), + \MapasCulturais\i::__('Instrumentista'), + \MapasCulturais\i::__('DJ'), + + // Artes Visuais + \MapasCulturais\i::__('Artista Visual'), + \MapasCulturais\i::__('Pintor'), + \MapasCulturais\i::__('Escultor'), + \MapasCulturais\i::__('Fotógrafo'), + \MapasCulturais\i::__('Designer Gráfico'), + \MapasCulturais\i::__('Ilustrador'), + \MapasCulturais\i::__('Grafiteiro'), + \MapasCulturais\i::__('Muralista'), + + // Audiovisual + \MapasCulturais\i::__('Roteirista'), + \MapasCulturais\i::__('Operador de Câmera'), + \MapasCulturais\i::__('Editor de Vídeo'), + \MapasCulturais\i::__('Operador de Som'), + \MapasCulturais\i::__('Técnico de Iluminação'), + \MapasCulturais\i::__('Finalizador'), + + // Literatura e Comunicação + \MapasCulturais\i::__('Escritor'), + \MapasCulturais\i::__('Poeta'), + \MapasCulturais\i::__('Contador de Histórias'), + \MapasCulturais\i::__('Jornalista'), + \MapasCulturais\i::__('Redator'), + \MapasCulturais\i::__('Editor de Livros'), + \MapasCulturais\i::__('Revisor'), + \MapasCulturais\i::__('Tradutor'), + + // Educação e Mediação + \MapasCulturais\i::__('Educador Cultural'), + \MapasCulturais\i::__('Mediador'), + \MapasCulturais\i::__('Oficineiro'), + \MapasCulturais\i::__('Professor'), + \MapasCulturais\i::__('Instrutor'), + + // Técnicos e Suporte + \MapasCulturais\i::__('Técnico de Som'), + \MapasCulturais\i::__('Técnico de Iluminação'), + \MapasCulturais\i::__('Montador de Palco'), + \MapasCulturais\i::__('Maquinista'), + \MapasCulturais\i::__('Eletricista'), + \MapasCulturais\i::__('Engenheiro de Som'), + + // Cultura Popular + \MapasCulturais\i::__('Mestre de Cultura Popular'), + \MapasCulturais\i::__('Brincante'), + \MapasCulturais\i::__('Artesão'), + \MapasCulturais\i::__('Capoeirista'), + + // Digital + \MapasCulturais\i::__('Desenvolvedor de Software'), + \MapasCulturais\i::__('Web Designer'), + \MapasCulturais\i::__('Designer de Som'), + \MapasCulturais\i::__('Gestor de Redes Sociais'), + + // Administração + \MapasCulturais\i::__('Coordenador'), + \MapasCulturais\i::__('Secretário'), + \MapasCulturais\i::__('Assistente Administrativo'), + \MapasCulturais\i::__('Contador'), + + // Outros + \MapasCulturais\i::__('Pesquisador'), + \MapasCulturais\i::__('Consultor Cultural'), + \MapasCulturais\i::__('Assessor de Imprensa'), + \MapasCulturais\i::__('Outra'), + ], 'serialize' => function ($val) { return json_encode($val); }, diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php b/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php index a790466238..08372a3a9d 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php +++ b/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php @@ -169,7 +169,16 @@ {{ convertToCurrency(proxy.executedUnitPrice) }}
- + +
+ +
    +
  • + {{ staff.role === 'Outra' && staff.customRole ? staff.customRole : staff.role }}: {{ staff.count }} +
  • +
+
+
diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan/script.js b/src/modules/OpportunityWorkplan/components/registration-workplan/script.js index 79315590e5..61e75adba8 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan/script.js +++ b/src/modules/OpportunityWorkplan/components/registration-workplan/script.js @@ -124,7 +124,14 @@ app.component('registration-workplan', { if (typeof delivery.paidStaffByRole === 'string') { delivery.paidStaffByRole = JSON.parse(delivery.paidStaffByRole || '[]'); } - if (!Array.isArray(delivery.paidStaffByRole)) delivery.paidStaffByRole = []; + if (!Array.isArray(delivery.paidStaffByRole)) { + delivery.paidStaffByRole = []; + } else { + // Ensure each staff object has customRole property + delivery.paidStaffByRole.forEach(staff => { + if (!('customRole' in staff)) staff.customRole = ''; + }); + } // Initialize teamCompositionGender - PARSE JSON STRING FROM API if (typeof delivery.teamCompositionGender === 'string') { @@ -400,7 +407,27 @@ app.component('registration-workplan', { if (this.opportunity.workplan_deliveryInformNumberOfCities && this.opportunity.workplan_deliveryRequireNumberOfCities && (delivery.numberOfCities === null || delivery.numberOfCities === '')) emptyFields.push("Número de municípios"); if (this.opportunity.workplan_deliveryInformNumberOfNeighborhoods && this.opportunity.workplan_deliveryRequireNumberOfNeighborhoods && (delivery.numberOfNeighborhoods === null || delivery.numberOfNeighborhoods === '')) emptyFields.push("Número de bairros"); if (this.opportunity.workplan_deliveryInformMediationActions && this.opportunity.workplan_deliveryRequireMediationActions && (delivery.mediationActions === null || delivery.mediationActions === '')) emptyFields.push("Ações de mediação/formação de público"); - if (this.opportunity.workplan_deliveryInformPaidStaffByRole && this.opportunity.workplan_deliveryRequirePaidStaffByRole && (!Array.isArray(delivery.paidStaffByRole) || !delivery.paidStaffByRole.length)) emptyFields.push("Pessoas remuneradas por função"); + // Validação de pessoas remuneradas por função + if (this.opportunity.workplan_deliveryInformPaidStaffByRole && this.opportunity.workplan_deliveryRequirePaidStaffByRole) { + if (!Array.isArray(delivery.paidStaffByRole) || !delivery.paidStaffByRole.length) { + emptyFields.push("Pessoas remuneradas por função"); + } else { + // Validar se todos os itens têm função e quantidade preenchidos + let hasInvalidStaff = false; + delivery.paidStaffByRole.forEach((staff, idx) => { + if (!staff.role || staff.count === null || staff.count === '' || staff.count === 0) { + hasInvalidStaff = true; + } + // Se a função é "Outra", verificar se customRole está preenchido + if (staff.role === 'Outra' && !staff.customRole) { + hasInvalidStaff = true; + } + }); + if (hasInvalidStaff) { + emptyFields.push("Pessoas remuneradas por função - todos os campos devem estar preenchidos"); + } + } + } if (this.opportunity.workplan_deliveryInformTeamComposition && this.opportunity.workplan_deliveryRequireTeamCompositionGender && (!delivery.teamCompositionGender || !this.calculateGenderTotal(delivery.teamCompositionGender))) emptyFields.push("Composição da equipe por gênero"); if (this.opportunity.workplan_deliveryInformTeamComposition && this.opportunity.workplan_deliveryRequireTeamCompositionRace && (!delivery.teamCompositionRace || !this.calculateRaceTotal(delivery.teamCompositionRace))) emptyFields.push("Composição da equipe por raça/cor"); if (this.opportunity.workplan_deliveryInformRevenueType && this.opportunity.workplan_deliveryRequireRevenueType && (!Array.isArray(delivery.revenueType) || !delivery.revenueType.length)) emptyFields.push("Tipo de receita previsto"); @@ -756,7 +783,7 @@ app.component('registration-workplan', { if (!Array.isArray(delivery.paidStaffByRole)) { delivery.paidStaffByRole = []; } - delivery.paidStaffByRole.push({ role: '', count: 0 }); + delivery.paidStaffByRole.push({ role: '', count: 0, customRole: '' }); }, removePaidStaffRole(delivery, index) { delivery.paidStaffByRole.splice(index, 1); diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan/template.php b/src/modules/OpportunityWorkplan/components/registration-workplan/template.php index d8498b34a0..6aa312e4ce 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan/template.php +++ b/src/modules/OpportunityWorkplan/components/registration-workplan/template.php @@ -257,23 +257,39 @@
-
-
-
- -
-
- -
-
-
+ +
+ -
From 161a56f3e06e5248da2a145a2216c9155433e076 Mon Sep 17 00:00:00 2001 From: Lucas Pirola Date: Thu, 26 Feb 2026 19:06:55 -0300 Subject: [PATCH 16/60] fix(workplan): Add missing style --- .../2.components/_registration-workplan.scss | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/themes/BaseV2/assets-src/sass/2.components/_registration-workplan.scss b/src/themes/BaseV2/assets-src/sass/2.components/_registration-workplan.scss index 6590b4e49d..858abe3029 100644 --- a/src/themes/BaseV2/assets-src/sass/2.components/_registration-workplan.scss +++ b/src/themes/BaseV2/assets-src/sass/2.components/_registration-workplan.scss @@ -88,4 +88,50 @@ padding: .5rem; z-index: 2; } +} + +// Paid Staff by Role - Lista de funções remuneradas +.paid-staff-list { + margin-top: size(12); +} + +.paid-staff-item { + background-color: #ffffff; + border: 1px solid #ddd; + border-radius: 4px; + padding: size(16); + margin-bottom: size(12); + + &__header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: size(12); + padding-bottom: size(8); + border-bottom: 1px solid #eee; + } + + &__title { + color: var(--mc-primary-500); + font-size: 0.95rem; + margin: 0; + } + + &__fields { + .field { + margin-bottom: size(8); + + label { + display: block; + font-weight: 500; + margin-bottom: size(4); + font-size: 0.9rem; + color: #666; + } + } + } +} + +.paid-staff-add { + margin-top: size(12); } \ No newline at end of file From d3cbe4335db43230d7d0addc72f687b15616c1bc Mon Sep 17 00:00:00 2001 From: Lucas Pirola Date: Thu, 26 Feb 2026 22:50:26 -0300 Subject: [PATCH 17/60] feat(workplan): Separa em blocos melhor organizados --- .../registration-workplan/template.php | 634 +++++++++--------- .../2.components/_registration-workplan.scss | 36 + 2 files changed, 369 insertions(+), 301 deletions(-) diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan/template.php b/src/modules/OpportunityWorkplan/components/registration-workplan/template.php index 6aa312e4ce..26971a0dec 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan/template.php +++ b/src/modules/OpportunityWorkplan/components/registration-workplan/template.php @@ -18,7 +18,7 @@ ?>
- + + + +
From 9d150ce69cbb0a3c7cfe9880da8ccd6802a8eada Mon Sep 17 00:00:00 2001 From: Lucas Pirola Date: Wed, 11 Mar 2026 23:21:19 -0300 Subject: [PATCH 55/60] =?UTF-8?q?feat(workplan):=20Equipara=20com=20campos?= =?UTF-8?q?=20de=20inscri=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OpportunityWorkplan/Entities/Delivery.php | 41 +- src/modules/OpportunityWorkplan/Module.php | 180 ++++++++ .../opportunity-enable-workplan/script.js | 48 ++- .../opportunity-enable-workplan/template.php | 87 ++++ .../script.js | 105 +++++ .../template.php | 222 +++++++++- src/modules/ProjectMonitoring/Module.php | 398 +++++++++++++++++- 7 files changed, 1055 insertions(+), 26 deletions(-) diff --git a/src/modules/OpportunityWorkplan/Entities/Delivery.php b/src/modules/OpportunityWorkplan/Entities/Delivery.php index d81090a993..5bf63c23a0 100644 --- a/src/modules/OpportunityWorkplan/Entities/Delivery.php +++ b/src/modules/OpportunityWorkplan/Entities/Delivery.php @@ -147,7 +147,7 @@ public function isMetadataRequired(string $metadata_key):bool { 'require' => 'workplan_monitoringRequireAvailabilityType' ], 'numberOfParticipants' => [ - 'inform' => 'workplan_registrationReportTheNumberOfParticipants', + 'inform' => 'workplan_monitoringInformNumberOfParticipants', 'require' => 'workplan_monitoringRequireNumberOfParticipants' ], 'participantProfile' => [ @@ -289,6 +289,43 @@ public function isMetadataRequired(string $metadata_key):bool { 'inform' => 'workplan_monitoringInformCommunicationChannels', 'require' => 'workplan_monitoringRequireCommunicationChannels' ], + 'executedRevenueType' => [ + 'inform' => 'workplan_monitoringInformRevenueType', + 'require' => 'workplan_monitoringRequireRevenueType' + ], + 'executedSegmentDelivery' => [ + 'inform' => 'workplan_monitoringInformSegmentDelivery', + 'require' => 'workplan_monitoringRequireSegmentDelivery' + ], + 'executedCommunityCoauthorsDetail' => [ + 'inform' => 'workplan_monitoringInformCommunityCoauthors', + 'require' => 'workplan_monitoringRequireCommunityCoauthorsDetail', + 'gate' => 'executedHasCommunityCoauthors' + ], + 'executedTransInclusionActions' => [ + 'inform' => 'workplan_monitoringInformTransInclusion', + 'require' => 'workplan_monitoringRequireTransInclusionActions', + 'gate' => 'executedHasTransInclusionStrategy' + ], + 'executedExpectedAccessibilityMeasures' => [ + 'inform' => 'workplan_monitoringInformAccessibilityPlan', + 'require' => 'workplan_monitoringRequireExpectedAccessibilityMeasures', + 'gate' => 'executedHasAccessibilityPlan' + ], + 'executedEnvironmentalPracticesDescription' => [ + 'inform' => 'workplan_monitoringInformEnvironmentalPractices', + 'require' => 'workplan_monitoringRequireEnvironmentalPracticesDescription', + 'gate' => 'executedHasEnvironmentalPractices' + ], + 'executedInnovationTypes' => [ + 'inform' => 'workplan_monitoringInformInnovation', + 'require' => 'workplan_monitoringRequireInnovationTypes', + 'gate' => 'executedHasInnovationAction' + ], + 'executedDocumentationTypes' => [ + 'inform' => 'workplan_monitoringInformDocumentationTypes', + 'require' => 'workplan_monitoringRequireDocumentationTypes' + ], ]; // Campo não está no mapa → não é obrigatório @@ -381,4 +418,4 @@ protected function validateSelectField(string $field): bool { $value = $this->$field; return !is_null($value) && $value !== ''; } -} \ No newline at end of file +} diff --git a/src/modules/OpportunityWorkplan/Module.php b/src/modules/OpportunityWorkplan/Module.php index f32a1053fc..8533677478 100644 --- a/src/modules/OpportunityWorkplan/Module.php +++ b/src/modules/OpportunityWorkplan/Module.php @@ -194,6 +194,64 @@ function _init(){ } } + $monitoring_simple_fields = [ + 'availabilityType', + 'participantProfile', + 'numberOfParticipants', + 'executedRevenue', + 'executedNumberOfCities', + 'executedNumberOfNeighborhoods', + 'executedMediationActions', + 'executedCommercialUnits', + 'executedUnitPrice', + 'executedArtChainLink', + 'executedSegmentDelivery', + 'executedCommunityCoauthorsDetail', + 'executedTransInclusionActions', + 'executedEnvironmentalPracticesDescription', + ]; + foreach ($monitoring_simple_fields as $field) { + if ($delivery->isMetadataRequired($field) && !self::validateSelectField($delivery, $field)) { + $label = self::getFieldLabel($field); + $errors['delivery'][] = i::__("Campo '{$label}' obrigatório na entrega '{$delivery->name}'"); + } + } + + $monitoring_json_array_fields = ['executedPaidStaffByRole']; + foreach ($monitoring_json_array_fields as $field) { + if ($delivery->isMetadataRequired($field) && !self::validateJsonArrayField($delivery, $field)) { + $label = self::getFieldLabel($field); + $errors['delivery'][] = i::__("Campo '{$label}' obrigatório na entrega '{$delivery->name}'"); + } + } + + $monitoring_json_object_fields = [ + 'executedTeamCompositionGender', + 'executedTeamCompositionRace', + ]; + foreach ($monitoring_json_object_fields as $field) { + if ($delivery->isMetadataRequired($field) && !self::validateJsonObjectField($delivery, $field)) { + $label = self::getFieldLabel($field); + $errors['delivery'][] = i::__("Campo '{$label}' obrigatório na entrega '{$delivery->name}'"); + } + } + + $monitoring_multiselect_fields = [ + 'accessibilityMeasures', + 'priorityAudience', + 'executedCommunicationChannels', + 'executedRevenueType', + 'executedExpectedAccessibilityMeasures', + 'executedInnovationTypes', + 'executedDocumentationTypes', + ]; + foreach ($monitoring_multiselect_fields as $field) { + if ($delivery->isMetadataRequired($field) && !self::validateMultiselectField($delivery, $field)) { + $label = self::getFieldLabel($field); + $errors['delivery'][] = i::__("Campo '{$label}' obrigatório na entrega '{$delivery->name}'"); + } + } + } } } @@ -383,6 +441,12 @@ function register() 'default_value' => false ]); + $this->registerOpportunityMetadata('workplan_monitoringInformNumberOfParticipants', [ + 'label' => i::__('Informar número de participantes executado'), + 'type' => 'boolean', + 'default_value' => false + ]); + $this->registerOpportunityMetadata('workplan_monitoringReportExecutedRevenue', [ 'label' => i::__('Informar receita executada'), 'type' => 'boolean', @@ -535,6 +599,60 @@ function register() 'default_value' => false ]); + $this->registerOpportunityMetadata('workplan_monitoringInformRevenueType', [ + 'label' => i::__('Informar tipo de receita executada'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformCommunityCoauthors', [ + 'label' => i::__('Informar envolvimento executado de comunidades/coletivos como coautores/coexecutores'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformTransInclusion', [ + 'label' => i::__('Informar estratégias executadas de inclusão Trans e Travestis'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformAccessibilityPlan', [ + 'label' => i::__('Informar plano de acessibilidade executado'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformEnvironmentalPractices', [ + 'label' => i::__('Informar práticas socioambientais executadas'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformPressStrategy', [ + 'label' => i::__('Informar estratégia executada de relacionamento com imprensa'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformInnovation', [ + 'label' => i::__('Informar ações executadas de experimentação/inovação'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformDocumentationTypes', [ + 'label' => i::__('Informar tipos de documentação produzida (executado)'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformSegmentDelivery', [ + 'label' => i::__('Informar segmento artístico-cultural executado da entrega'), + 'type' => 'boolean', + 'default_value' => false + ]); + // ============================================ // METADADOS DE OBRIGATORIEDADE (REQUIRE) // ============================================ @@ -754,6 +872,54 @@ function register() 'default_value' => false ]); + $this->registerOpportunityMetadata('workplan_monitoringRequireRevenueType', [ + 'label' => i::__('Tipo de receita executada é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringRequireCommunityCoauthorsDetail', [ + 'label' => i::__('Detalhamento de coautoria/coexecução executada é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringRequireTransInclusionActions', [ + 'label' => i::__('Ações executadas de inclusão Trans e Travestis são obrigatórias'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringRequireExpectedAccessibilityMeasures', [ + 'label' => i::__('Medidas de acessibilidade executadas são obrigatórias'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringRequireEnvironmentalPracticesDescription', [ + 'label' => i::__('Práticas socioambientais executadas são obrigatórias'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringRequireInnovationTypes', [ + 'label' => i::__('Tipos de experimentação/inovação executados são obrigatórios'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringRequireDocumentationTypes', [ + 'label' => i::__('Tipos de documentação produzida são obrigatórios'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringRequireSegmentDelivery', [ + 'label' => i::__('Segmento artístico-cultural executado é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + $this->registerOpportunityMetadata('workplan_monitoringInformArtChainLink', [ 'label' => i::__('Informar principal elo das artes acionado (executado)'), 'type' => 'boolean', @@ -1601,6 +1767,20 @@ public static function getFieldLabel(string $field): string { 'participantProfile' => 'Perfil do público', 'priorityAudience' => 'Territórios prioritários', 'executedRevenue' => 'Receita executada', + 'executedRevenueType' => 'Tipo de receita executada', + 'executedSegmentDelivery' => 'Segmento artístico-cultural executado', + 'executedHasCommunityCoauthors' => 'Envolvimento executado de comunidades', + 'executedCommunityCoauthorsDetail' => 'Detalhamento executado de coautoria', + 'executedHasTransInclusionStrategy' => 'Estratégia executada de inclusão Trans/Travestis', + 'executedTransInclusionActions' => 'Ações executadas de inclusão Trans/Travestis', + 'executedHasAccessibilityPlan' => 'Plano de acessibilidade executado', + 'executedExpectedAccessibilityMeasures' => 'Medidas de acessibilidade executadas', + 'executedHasEnvironmentalPractices' => 'Práticas socioambientais executadas', + 'executedEnvironmentalPracticesDescription' => 'Descrição de práticas socioambientais executadas', + 'executedHasPressStrategy' => 'Estratégia executada de relacionamento com imprensa', + 'executedHasInnovationAction' => 'Ação executada de experimentação/inovação', + 'executedInnovationTypes' => 'Tipos de experimentação/inovação executados', + 'executedDocumentationTypes' => 'Tipos de documentação produzida', ]; return $labels[$field] ?? $field; diff --git a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/script.js b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/script.js index a5c095a782..a648974e13 100644 --- a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/script.js +++ b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/script.js @@ -135,6 +135,9 @@ app.component('opportunity-enable-workplan', { 'entity.workplan_monitoringProvideTheProfileOfParticipants'(_new) { if (!_new) this.entity.workplan_monitoringRequireParticipantProfile = false; }, + 'entity.workplan_monitoringInformNumberOfParticipants'(_new) { + if (!_new) this.entity.workplan_monitoringRequireNumberOfParticipants = false; + }, 'entity.workplan_monitoringInformThePriorityAudience'(_new) { if (!_new) this.entity.workplan_monitoringRequirePriorityAudience = false; }, @@ -172,6 +175,30 @@ app.component('opportunity-enable-workplan', { 'entity.workplan_monitoringInformCommunicationChannels'(_new) { if (!_new) this.entity.workplan_monitoringRequireCommunicationChannels = false; }, + 'entity.workplan_monitoringInformRevenueType'(_new) { + if (!_new) this.entity.workplan_monitoringRequireRevenueType = false; + }, + 'entity.workplan_monitoringInformCommunityCoauthors'(_new) { + if (!_new) this.entity.workplan_monitoringRequireCommunityCoauthorsDetail = false; + }, + 'entity.workplan_monitoringInformTransInclusion'(_new) { + if (!_new) this.entity.workplan_monitoringRequireTransInclusionActions = false; + }, + 'entity.workplan_monitoringInformAccessibilityPlan'(_new) { + if (!_new) this.entity.workplan_monitoringRequireExpectedAccessibilityMeasures = false; + }, + 'entity.workplan_monitoringInformEnvironmentalPractices'(_new) { + if (!_new) this.entity.workplan_monitoringRequireEnvironmentalPracticesDescription = false; + }, + 'entity.workplan_monitoringInformInnovation'(_new) { + if (!_new) this.entity.workplan_monitoringRequireInnovationTypes = false; + }, + 'entity.workplan_monitoringInformDocumentationTypes'(_new) { + if (!_new) this.entity.workplan_monitoringRequireDocumentationTypes = false; + }, + 'entity.workplan_monitoringInformSegmentDelivery'(_new) { + if (!_new) this.entity.workplan_monitoringRequireSegmentDelivery = false; + }, }, computed: { getWorkplanLabelDefault() { @@ -277,6 +304,8 @@ app.component('opportunity-enable-workplan', { this.entity.workplan_monitoringRequireAccessibilityMeasures = false; this.entity.workplan_monitoringProvideTheProfileOfParticipants = false; this.entity.workplan_monitoringRequireParticipantProfile = false; + this.entity.workplan_monitoringInformNumberOfParticipants = false; + this.entity.workplan_monitoringRequireNumberOfParticipants = false; this.entity.workplan_monitoringInformThePriorityAudience = false; this.entity.workplan_monitoringRequirePriorityAudience = false; this.entity.workplan_monitoringReportExecutedRevenue = false; @@ -301,6 +330,23 @@ app.component('opportunity-enable-workplan', { this.entity.workplan_monitoringRequireArtChainLink = false; this.entity.workplan_monitoringInformCommunicationChannels = false; this.entity.workplan_monitoringRequireCommunicationChannels = false; + this.entity.workplan_monitoringInformRevenueType = false; + this.entity.workplan_monitoringRequireRevenueType = false; + this.entity.workplan_monitoringInformCommunityCoauthors = false; + this.entity.workplan_monitoringRequireCommunityCoauthorsDetail = false; + this.entity.workplan_monitoringInformTransInclusion = false; + this.entity.workplan_monitoringRequireTransInclusionActions = false; + this.entity.workplan_monitoringInformAccessibilityPlan = false; + this.entity.workplan_monitoringRequireExpectedAccessibilityMeasures = false; + this.entity.workplan_monitoringInformEnvironmentalPractices = false; + this.entity.workplan_monitoringRequireEnvironmentalPracticesDescription = false; + this.entity.workplan_monitoringInformPressStrategy = false; + this.entity.workplan_monitoringInformInnovation = false; + this.entity.workplan_monitoringRequireInnovationTypes = false; + this.entity.workplan_monitoringInformDocumentationTypes = false; + this.entity.workplan_monitoringRequireDocumentationTypes = false; + this.entity.workplan_monitoringInformSegmentDelivery = false; + this.entity.workplan_monitoringRequireSegmentDelivery = false; }, pluralParaSingular(texto) { const palavras = texto.split(' '); @@ -332,4 +378,4 @@ app.component('opportunity-enable-workplan', { return palavrasNoSingular.join(' '); } }, -}) \ No newline at end of file +}) diff --git a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php index a4fc4fb490..08bbebef6d 100644 --- a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php +++ b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php @@ -446,6 +446,15 @@ +
+ + +
+
+
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ + +
+ +
+ + +
diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/script.js b/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/script.js index d35371add8..8b93bc37e1 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/script.js +++ b/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/script.js @@ -111,9 +111,18 @@ app.component('registration-workplan-form-delivery', { artChainLinkOptions () { return Vue.markRaw($DESCRIPTIONS.delivery.artChainLink?.options ?? []); }, + booleanOptions () { + return Vue.markRaw($DESCRIPTIONS.delivery.hasCommunityCoauthors?.options ?? { + true: 'Sim', + false: 'Não', + }); + }, communicationChannelsOptions () { return Vue.markRaw($DESCRIPTIONS.delivery.communicationChannels?.options ?? {}); }, + documentationTypeOptions () { + return Vue.markRaw($DESCRIPTIONS.delivery.documentationTypes?.options ?? {}); + }, executedCommunicationChannels: { get () { const val = this.proxy.executedCommunicationChannels; @@ -125,6 +134,50 @@ app.component('registration-workplan-form-delivery', { this.proxy.executedCommunicationChannels = value; }, }, + executedDocumentationTypes: { + get () { + const val = this.proxy.executedDocumentationTypes; + if (!val) return []; + if (typeof val === 'string') return JSON.parse(val) ?? []; + return val; + }, + set (value) { + this.proxy.executedDocumentationTypes = value; + }, + }, + executedExpectedAccessibilityMeasures: { + get () { + const val = this.proxy.executedExpectedAccessibilityMeasures; + if (!val) return []; + if (typeof val === 'string') return JSON.parse(val) ?? []; + return val; + }, + set (value) { + this.proxy.executedExpectedAccessibilityMeasures = value; + }, + }, + executedInnovationTypes: { + get () { + const val = this.proxy.executedInnovationTypes; + if (!val) return []; + if (typeof val === 'string') return JSON.parse(val) ?? []; + return val; + }, + set (value) { + this.proxy.executedInnovationTypes = value; + }, + }, + executedRevenueType: { + get () { + const val = this.proxy.executedRevenueType; + if (!val) return []; + if (typeof val === 'string') return JSON.parse(val) ?? []; + return val; + }, + set (value) { + this.proxy.executedRevenueType = value; + }, + }, executedTeamCompositionGender: { get () { let val = this.proxy.executedTeamCompositionGender; @@ -160,10 +213,22 @@ app.component('registration-workplan-form-delivery', { const g = this.executedTeamCompositionGender; return Object.values(g).some(v => Number(v) > 0); }, + hasExecutedDocumentationTypes () { + return this.executedDocumentationTypes.length > 0; + }, + hasExecutedExpectedAccessibilityMeasures () { + return this.executedExpectedAccessibilityMeasures.length > 0; + }, + hasExecutedInnovationTypes () { + return this.executedInnovationTypes.length > 0; + }, hasExecutedRaceData () { const r = this.executedTeamCompositionRace; return Object.values(r).some(v => Number(v) > 0); }, + hasExecutedRevenueType () { + return this.executedRevenueType.length > 0; + }, executedPaidStaffByRole: { get () { const val = this.proxy.executedPaidStaffByRole; @@ -180,6 +245,18 @@ app.component('registration-workplan-form-delivery', { paidStaffRoleOptions () { return Vue.markRaw($DESCRIPTIONS.delivery.paidStaffByRole?.options ?? []); }, + revenueTypeOptions () { + return Vue.markRaw($DESCRIPTIONS.delivery.revenueType?.options ?? {}); + }, + segmentDeliveryOptions () { + return Vue.markRaw($DESCRIPTIONS.delivery.segmentDelivery?.options ?? {}); + }, + accessibilityPlanOptions () { + return Vue.markRaw($DESCRIPTIONS.delivery.expectedAccessibilityMeasures?.options ?? {}); + }, + innovationTypeOptions () { + return Vue.markRaw($DESCRIPTIONS.delivery.innovationTypes?.options ?? {}); + }, }, methods: { convertToCurrency(field) { @@ -192,6 +269,34 @@ app.component('registration-workplan-form-delivery', { else arr.push(item); this.executedCommunicationChannels = arr; }, + toggleExecutedDocumentationType (item) { + const idx = this.executedDocumentationTypes.indexOf(item); + const arr = [...this.executedDocumentationTypes]; + if (idx >= 0) arr.splice(idx, 1); + else arr.push(item); + this.executedDocumentationTypes = arr; + }, + toggleExecutedExpectedAccessibilityMeasure (item) { + const idx = this.executedExpectedAccessibilityMeasures.indexOf(item); + const arr = [...this.executedExpectedAccessibilityMeasures]; + if (idx >= 0) arr.splice(idx, 1); + else arr.push(item); + this.executedExpectedAccessibilityMeasures = arr; + }, + toggleExecutedInnovationType (item) { + const idx = this.executedInnovationTypes.indexOf(item); + const arr = [...this.executedInnovationTypes]; + if (idx >= 0) arr.splice(idx, 1); + else arr.push(item); + this.executedInnovationTypes = arr; + }, + toggleExecutedRevenueType (item) { + const idx = this.executedRevenueType.indexOf(item); + const arr = [...this.executedRevenueType]; + if (idx >= 0) arr.splice(idx, 1); + else arr.push(item); + this.executedRevenueType = arr; + }, calculateGenderTotal (composition) { if (!composition) return 0; return ['cisgenderWoman','cisgenderMan','transgenderWoman','transgenderMan','nonBinary','otherGenderIdentity','preferNotToSay'] diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php b/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php index 2139b1fa23..80bb72c0c6 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php +++ b/src/modules/OpportunityWorkplan/components/registration-workplan-form-delivery/template.php @@ -79,7 +79,7 @@
- + {{ proxy.participantProfile }} {{ validationErrors.participantProfile.join('; ') }}
- + {{ validationErrors.priorityAudience.join('; ') }}
-
- +
+ {{ proxy.numberOfParticipants }} {{ validationErrors.numberOfParticipants.join('; ') }}
- + {{ convertToCurrency(executedRevenue) }} {{ validationErrors.executedRevenue.join('; ') }}
+
+ +
+ + +
+ + + {{ validationErrors.executedRevenueType.join('; ') }} +
+
- +
{{ delivery.numberOfCities }}
{{ proxy.executedNumberOfCities }} + {{ validationErrors.executedNumberOfCities.join('; ') }}
- +
{{ delivery.numberOfNeighborhoods }}
{{ proxy.executedNumberOfNeighborhoods }} + {{ validationErrors.executedNumberOfNeighborhoods.join('; ') }}
- +
{{ delivery.mediationActions }}
{{ proxy.executedMediationActions }} + {{ validationErrors.executedMediationActions.join('; ') }}
- +
{{ delivery.commercialUnits }}
{{ proxy.executedCommercialUnits }} + {{ validationErrors.executedCommercialUnits.join('; ') }}
- +
{{ convertToCurrency(delivery.unitPrice) }}
{{ convertToCurrency(proxy.executedUnitPrice) }} + {{ validationErrors.executedUnitPrice.join('; ') }}
@@ -180,7 +199,7 @@
- +
    @@ -242,7 +261,7 @@
    - +
    : {{ delivery.teamCompositionGender.cisgenderWoman || 0 }}, @@ -299,11 +318,12 @@ : {{ proxy.executedTeamCompositionGender?.preferNotToSay || 0 }}
    + {{ validationErrors.executedTeamCompositionGender.join('; ') }}
    - +
    : {{ delivery.teamCompositionRace.white || 0 }}, @@ -354,11 +374,12 @@ : {{ proxy.executedTeamCompositionRace?.notDeclared || 0 }}
    + {{ validationErrors.executedTeamCompositionRace.join('; ') }}
    - +
    {{ delivery.artChainLink }}
    @@ -367,11 +388,12 @@ {{ proxy.executedArtChainLink }} + {{ validationErrors.executedArtChainLink.join('; ') }}
    - +
    @@ -381,6 +403,170 @@ + {{ validationErrors.executedCommunicationChannels.join('; ') }} +
    + +
    + +
    + {{ delivery.segmentDelivery }} +
    + + {{ proxy.executedSegmentDelivery }} + {{ validationErrors.executedSegmentDelivery.join('; ') }} +
    + +
    + +
    + {{ booleanOptions[delivery.hasCommunityCoauthors] ?? delivery.hasCommunityCoauthors }} +
    + + {{ booleanOptions[proxy.executedHasCommunityCoauthors] ?? proxy.executedHasCommunityCoauthors }} + {{ validationErrors.executedHasCommunityCoauthors.join('; ') }} +
    + +
    + +
    + {{ delivery.communityCoauthorsDetail }} +
    + + {{ proxy.executedCommunityCoauthorsDetail }} + {{ validationErrors.executedCommunityCoauthorsDetail.join('; ') }} +
    + +
    + +
    + {{ booleanOptions[delivery.hasTransInclusionStrategy] ?? delivery.hasTransInclusionStrategy }} +
    + + {{ booleanOptions[proxy.executedHasTransInclusionStrategy] ?? proxy.executedHasTransInclusionStrategy }} + {{ validationErrors.executedHasTransInclusionStrategy.join('; ') }} +
    + +
    + +
    + {{ delivery.transInclusionActions }} +
    + + {{ proxy.executedTransInclusionActions }} + {{ validationErrors.executedTransInclusionActions.join('; ') }} +
    + +
    + +
    + {{ booleanOptions[delivery.hasAccessibilityPlan] ?? delivery.hasAccessibilityPlan }} +
    + + {{ booleanOptions[proxy.executedHasAccessibilityPlan] ?? proxy.executedHasAccessibilityPlan }} + {{ validationErrors.executedHasAccessibilityPlan.join('; ') }} +
    + +
    + +
    + + +
    + + + {{ validationErrors.executedExpectedAccessibilityMeasures.join('; ') }} +
    + +
    + +
    + {{ booleanOptions[delivery.hasEnvironmentalPractices] ?? delivery.hasEnvironmentalPractices }} +
    + + {{ booleanOptions[proxy.executedHasEnvironmentalPractices] ?? proxy.executedHasEnvironmentalPractices }} + {{ validationErrors.executedHasEnvironmentalPractices.join('; ') }} +
    + +
    + +
    + {{ delivery.environmentalPracticesDescription }} +
    + + {{ proxy.executedEnvironmentalPracticesDescription }} + {{ validationErrors.executedEnvironmentalPracticesDescription.join('; ') }} +
    + +
    + +
    + {{ booleanOptions[delivery.hasPressStrategy] ?? delivery.hasPressStrategy }} +
    + + {{ booleanOptions[proxy.executedHasPressStrategy] ?? proxy.executedHasPressStrategy }} + {{ validationErrors.executedHasPressStrategy.join('; ') }} +
    + +
    + +
    + {{ booleanOptions[delivery.hasInnovationAction] ?? delivery.hasInnovationAction }} +
    + + {{ booleanOptions[proxy.executedHasInnovationAction] ?? proxy.executedHasInnovationAction }} + {{ validationErrors.executedHasInnovationAction.join('; ') }} +
    + +
    + +
    + + +
    + + + {{ validationErrors.executedInnovationTypes.join('; ') }} +
    + +
    + +
    + + +
    + + + {{ validationErrors.executedDocumentationTypes.join('; ') }}
    @@ -404,4 +590,4 @@ {{ validationErrors.evidenceLinks.join('; ') }}
    -
    \ No newline at end of file +
diff --git a/src/modules/ProjectMonitoring/Module.php b/src/modules/ProjectMonitoring/Module.php index 1a903573ac..a56930d889 100644 --- a/src/modules/ProjectMonitoring/Module.php +++ b/src/modules/ProjectMonitoring/Module.php @@ -368,7 +368,13 @@ public function register() { 'type' => 'integer', 'validations' => [ 'v::intVal()->min(0)' => i::__('Deve ser um número maior ou igual a zero') - ] + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedNumberOfCities')) { + return i::__('Campo obrigatório'); + } + return false; + } ]); $app->registerMetadata($executedNumberOfCities, Delivery::class); @@ -378,7 +384,13 @@ public function register() { 'type' => 'integer', 'validations' => [ 'v::intVal()->min(0)' => i::__('Deve ser um número maior ou igual a zero') - ] + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedNumberOfNeighborhoods')) { + return i::__('Campo obrigatório'); + } + return false; + } ]); $app->registerMetadata($executedNumberOfNeighborhoods, Delivery::class); @@ -388,7 +400,13 @@ public function register() { 'type' => 'integer', 'validations' => [ 'v::intVal()->min(0)' => i::__('Deve ser um número maior ou igual a zero') - ] + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedMediationActions')) { + return i::__('Campo obrigatório'); + } + return false; + } ]); $app->registerMetadata($executedMediationActions, Delivery::class); @@ -398,14 +416,26 @@ public function register() { 'type' => 'integer', 'validations' => [ 'v::intVal()->min(0)' => i::__('Deve ser um número maior ou igual a zero') - ] + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedCommercialUnits')) { + return i::__('Campo obrigatório'); + } + return false; + } ]); $app->registerMetadata($executedCommercialUnits, Delivery::class); // Valor unitário executado $executedUnitPrice = new Metadata('executedUnitPrice', [ 'label' => i::__('Valor unitário praticado (R$)'), - 'type' => 'currency' + 'type' => 'currency', + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedUnitPrice')) { + return i::__('Campo obrigatório'); + } + return false; + } ]); $app->registerMetadata($executedUnitPrice, Delivery::class); @@ -418,6 +448,12 @@ public function register() { }, 'unserialize' => function($val) { return json_decode((string) $val, true); + }, + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedPaidStaffByRole')) { + return i::__('Campo obrigatório'); + } + return false; } ]); $app->registerMetadata($executedPaidStaffByRole, Delivery::class); @@ -431,6 +467,12 @@ public function register() { }, 'unserialize' => function($val) { return json_decode((string) $val, true); + }, + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedTeamCompositionGender')) { + return i::__('Campo obrigatório'); + } + return false; } ]); $app->registerMetadata($executedTeamCompositionGender, Delivery::class); @@ -444,10 +486,324 @@ public function register() { }, 'unserialize' => function($val) { return json_decode((string) $val, true); + }, + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedTeamCompositionRace')) { + return i::__('Campo obrigatório'); + } + return false; } ]); $app->registerMetadata($executedTeamCompositionRace, Delivery::class); + $executedArtChainLink = new Metadata('executedArtChainLink', [ + 'label' => i::__('Principal elo das artes acionado (executado)'), + 'type' => 'select', + 'options' => [ + i::__('Acesso'), + i::__('Criação'), + i::__('Produção'), + i::__('Difusão'), + i::__('Circulação'), + i::__('Internacionalização'), + i::__('Formação'), + i::__('Fruição'), + i::__('Memória/Preservação'), + i::__('Pesquisa'), + i::__('Reflexão'), + i::__('Gestão Cultural'), + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedArtChainLink')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedArtChainLink, Delivery::class); + + $executedCommunicationChannels = new Metadata('executedCommunicationChannels', [ + 'label' => i::__('Canais de comunicação utilizados (executado)'), + 'type' => 'multiselect', + 'options' => [ + i::__('Instagram'), + i::__('Facebook'), + i::__('TikTok'), + i::__('YouTube'), + i::__('X/Twitter'), + i::__('WhatsApp (listas/grupos)'), + i::__('Telegram (canais/grupos)'), + i::__('Site/página oficial do projeto'), + i::__('E-mail marketing/newsletter'), + i::__('Plataformas de eventos/inscrição (ex.: Sympla/Shotgun/Eventbrite)'), + i::__('Portais, blogs e influenciadores/as locais'), + i::__('Rádio comunitária'), + i::__('Rádio comercial'), + i::__('TV local'), + i::__('Mídia impressa'), + i::__('Cartazes e materiais impressos'), + i::__('Carro de som'), + i::__('Outros'), + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedCommunicationChannels')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedCommunicationChannels, Delivery::class); + + $executedRevenueType = new Metadata('executedRevenueType', [ + 'label' => i::__('Qual o tipo de receita executada?'), + 'type' => 'multiselect', + 'options' => [ + i::__('Venda de ingressos'), + i::__('Venda de produtos'), + i::__('Patrocínio privado'), + i::__('Apoio cultural'), + i::__('Doações'), + i::__('Cachê'), + i::__('Prestação de serviços'), + i::__('Direitos autorais'), + i::__('Licenciamento'), + i::__('Não haverá receita'), + i::__('Outros'), + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedRevenueType')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedRevenueType, Delivery::class); + + $executedSegmentDelivery = new Metadata('executedSegmentDelivery', [ + 'label' => i::__('Segmento artístico-cultural executado da entrega'), + 'type' => 'select', + 'options' => [ + i::__('Artes Visuais'), + i::__('Artesanato'), + i::__('Audiovisual e Mídias Interativas'), + i::__('Circo'), + i::__('Culturas Tradicionais e Populares'), + i::__('Culturas dos Povos Originários'), + i::__('Dança'), + i::__('Design e Serviços Criativos'), + i::__('Economia, Produção e Áreas Técnicas da Cultura'), + i::__('Festas Populares'), + i::__('Humanidades'), + i::__('Livro, Leitura e Literatura'), + i::__('Música'), + i::__('Patrimônio Cultural Imaterial'), + i::__('Patrimônio Cultural Material'), + i::__('Performance'), + i::__('Produção e Áreas Técnicas da Cultura'), + i::__('Teatro'), + i::__('Transversalidades') + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedSegmentDelivery')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedSegmentDelivery, Delivery::class); + + $executedHasCommunityCoauthors = new Metadata('executedHasCommunityCoauthors', [ + 'label' => i::__('A atividade executada contou com envolvimento de comunidades/coletivos como coautores/coexecutores?'), + 'type' => 'select', + 'options' => [ + 'true' => i::__('Sim'), + 'false' => i::__('Não'), + ], + ]); + $app->registerMetadata($executedHasCommunityCoauthors, Delivery::class); + + $executedCommunityCoauthorsDetail = new Metadata('executedCommunityCoauthorsDetail', [ + 'label' => i::__('Descreva o envolvimento executado das comunidades/coletivos como coautores/coexecutores'), + 'type' => 'text', + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedCommunityCoauthorsDetail')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedCommunityCoauthorsDetail, Delivery::class); + + $executedHasTransInclusionStrategy = new Metadata('executedHasTransInclusionStrategy', [ + 'label' => i::__('A atividade executada contou com estratégias voltadas à promoção do acesso de pessoas Trans e Travestis?'), + 'type' => 'select', + 'options' => [ + 'true' => i::__('Sim'), + 'false' => i::__('Não'), + ], + ]); + $app->registerMetadata($executedHasTransInclusionStrategy, Delivery::class); + + $executedTransInclusionActions = new Metadata('executedTransInclusionActions', [ + 'label' => i::__('Quais ações executadas promoveram o acesso de pessoas Trans e Travestis?'), + 'type' => 'text', + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedTransInclusionActions')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedTransInclusionActions, Delivery::class); + + $executedHasAccessibilityPlan = new Metadata('executedHasAccessibilityPlan', [ + 'label' => i::__('A atividade executada contou com medidas de acessibilidade?'), + 'type' => 'select', + 'options' => [ + 'true' => i::__('Sim'), + 'false' => i::__('Não'), + ], + ]); + $app->registerMetadata($executedHasAccessibilityPlan, Delivery::class); + + $executedExpectedAccessibilityMeasures = new Metadata('executedExpectedAccessibilityMeasures', [ + 'label' => i::__('Quais medidas de acessibilidade foram executadas na atividade?'), + 'type' => 'multiselect', + 'options' => [ + i::__('Rotas acessíveis, com espaço de manobra para cadeira de rodas'), + i::__('Palco acessível'), + i::__('Camarim acessível'), + i::__('Piso tátil'), + i::__('Rampas'), + i::__("Elevadores adequados para PCD's"), + i::__('Corrimãos e guarda-corpos'), + i::__("Banheiros adaptados para PCD's"), + i::__('Área de alimentação preferencial identificada'), + i::__("Vagas de estacionamento para PCD's reservadas"), + i::__("Assentos para pessoas obesas, pessoas com mobilidade reduzida, PCD's e pessoas idosas reservadas"), + i::__('Filas preferenciais identificadas'), + i::__('Iluminação adequada'), + i::__('Livro e/ou similares em braile'), + i::__('Audiolivro'), + i::__('Uso Língua Brasileira de Sinais - Libras'), + i::__('Sistema Braille em materiais impressos'), + i::__('Sistema de sinalização ou comunicação tátil'), + i::__('Audiodescrição'), + i::__('Legendas para surdos e ensurdecidos'), + i::__('Linguagem simples'), + i::__('Textos adaptados para software de leitor de tela'), + i::__('Capacitação em acessibilidade para equipes atuantes nos projetos culturais'), + i::__('Contratação de profissionais especializados em acessibilidade cultural'), + i::__('Contratação de profissionais com deficiência'), + i::__('Formação e sensibilização de agentes culturais sobre acessibilidade'), + i::__('Formação e sensibilização de públicos da cadeia produtiva cultural sobre acessibilidade'), + i::__("Envolvimento de PCD's na concepção do projeto"), + i::__('Outras'), + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedExpectedAccessibilityMeasures')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedExpectedAccessibilityMeasures, Delivery::class); + + $executedHasEnvironmentalPractices = new Metadata('executedHasEnvironmentalPractices', [ + 'label' => i::__('A atividade executada contou com medidas ou práticas socioambientais?'), + 'type' => 'select', + 'options' => [ + 'true' => i::__('Sim'), + 'false' => i::__('Não'), + ], + ]); + $app->registerMetadata($executedHasEnvironmentalPractices, Delivery::class); + + $executedEnvironmentalPracticesDescription = new Metadata('executedEnvironmentalPracticesDescription', [ + 'label' => i::__('Quais medidas e práticas socioambientais foram executadas na atividade?'), + 'type' => 'text', + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedEnvironmentalPracticesDescription')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedEnvironmentalPracticesDescription, Delivery::class); + + $executedHasPressStrategy = new Metadata('executedHasPressStrategy', [ + 'label' => i::__('A atividade executada contou com estratégia de relacionamento com a imprensa?'), + 'type' => 'select', + 'options' => [ + 'true' => i::__('Sim'), + 'false' => i::__('Não'), + ], + ]); + $app->registerMetadata($executedHasPressStrategy, Delivery::class); + + $executedHasInnovationAction = new Metadata('executedHasInnovationAction', [ + 'label' => i::__('A atividade executada contou com ação de experimentação/inovação?'), + 'type' => 'select', + 'options' => [ + 'true' => i::__('Sim'), + 'false' => i::__('Não'), + ], + ]); + $app->registerMetadata($executedHasInnovationAction, Delivery::class); + + $executedInnovationTypes = new Metadata('executedInnovationTypes', [ + 'label' => i::__('Quais tipos de experimentação/inovação foram executados?'), + 'type' => 'multiselect', + 'options' => [ + i::__('Uso de novas tecnologias (AR, VR, IA, etc.)'), + i::__('Novas linguagens artísticas'), + i::__('Fusão de linguagens'), + i::__('Metodologias participativas inovadoras'), + i::__('Novos modelos de gestão cultural'), + i::__('Economia criativa e novos modelos de negócio'), + i::__('Sustentabilidade e práticas ambientais inovadoras'), + i::__('Inclusão e acessibilidade de forma inovadora'), + i::__('Experimentação em espaços não convencionais'), + i::__('Coprodução/cocriação com públicos'), + i::__('Outros'), + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedInnovationTypes')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedInnovationTypes, Delivery::class); + + $executedDocumentationTypes = new Metadata('executedDocumentationTypes', [ + 'label' => i::__('Tipo de documentação produzida (executado)'), + 'type' => 'multiselect', + 'options' => [ + i::__('Fotografia'), + i::__('Vídeo'), + i::__('Áudio'), + i::__('Relatório textual'), + i::__('Caderno de processo'), + i::__('Publicação impressa'), + i::__('Publicação digital'), + i::__('Website/Plataforma online'), + i::__('Redes sociais'), + i::__('Depoimentos'), + i::__('Registros de processo'), + i::__('Acervo digitalizado'), + i::__('Não haverá documentação específica'), + i::__('Outros'), + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedDocumentationTypes')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedDocumentationTypes, Delivery::class); + // Medidas de acessibilidade executadas (já existe accessibilityMeasures) // Este campo já existe e será usado para os dados executados @@ -523,6 +879,22 @@ public function register() { $delivery->executedPaidStaffByRole = $data['executedPaidStaffByRole'] ?? null; $delivery->executedTeamCompositionGender = $data['executedTeamCompositionGender'] ?? null; $delivery->executedTeamCompositionRace = $data['executedTeamCompositionRace'] ?? null; + $delivery->executedArtChainLink = $data['executedArtChainLink'] ?? null; + $delivery->executedCommunicationChannels = $data['executedCommunicationChannels'] ?? null; + $delivery->executedRevenueType = $data['executedRevenueType'] ?? null; + $delivery->executedSegmentDelivery = $data['executedSegmentDelivery'] ?? null; + $delivery->executedHasCommunityCoauthors = $data['executedHasCommunityCoauthors'] ?? null; + $delivery->executedCommunityCoauthorsDetail = $data['executedCommunityCoauthorsDetail'] ?? null; + $delivery->executedHasTransInclusionStrategy = $data['executedHasTransInclusionStrategy'] ?? null; + $delivery->executedTransInclusionActions = $data['executedTransInclusionActions'] ?? null; + $delivery->executedHasAccessibilityPlan = $data['executedHasAccessibilityPlan'] ?? null; + $delivery->executedExpectedAccessibilityMeasures = $data['executedExpectedAccessibilityMeasures'] ?? null; + $delivery->executedHasEnvironmentalPractices = $data['executedHasEnvironmentalPractices'] ?? null; + $delivery->executedEnvironmentalPracticesDescription = $data['executedEnvironmentalPracticesDescription'] ?? null; + $delivery->executedHasPressStrategy = $data['executedHasPressStrategy'] ?? null; + $delivery->executedHasInnovationAction = $data['executedHasInnovationAction'] ?? null; + $delivery->executedInnovationTypes = $data['executedInnovationTypes'] ?? null; + $delivery->executedDocumentationTypes = $data['executedDocumentationTypes'] ?? null; } $app->hook('entity(Registration).save:finish', function() use ($goals, $deliveries, $first_phase, $app) { @@ -600,6 +972,22 @@ public function register() { 'executedPaidStaffByRole' => $delivery->executedPaidStaffByRole, 'executedTeamCompositionGender' => $delivery->executedTeamCompositionGender, 'executedTeamCompositionRace' => $delivery->executedTeamCompositionRace, + 'executedArtChainLink' => $delivery->executedArtChainLink, + 'executedCommunicationChannels' => $delivery->executedCommunicationChannels, + 'executedRevenueType' => $delivery->executedRevenueType, + 'executedSegmentDelivery' => $delivery->executedSegmentDelivery, + 'executedHasCommunityCoauthors' => $delivery->executedHasCommunityCoauthors, + 'executedCommunityCoauthorsDetail' => $delivery->executedCommunityCoauthorsDetail, + 'executedHasTransInclusionStrategy' => $delivery->executedHasTransInclusionStrategy, + 'executedTransInclusionActions' => $delivery->executedTransInclusionActions, + 'executedHasAccessibilityPlan' => $delivery->executedHasAccessibilityPlan, + 'executedExpectedAccessibilityMeasures' => $delivery->executedExpectedAccessibilityMeasures, + 'executedHasEnvironmentalPractices' => $delivery->executedHasEnvironmentalPractices, + 'executedEnvironmentalPracticesDescription' => $delivery->executedEnvironmentalPracticesDescription, + 'executedHasPressStrategy' => $delivery->executedHasPressStrategy, + 'executedHasInnovationAction' => $delivery->executedHasInnovationAction, + 'executedInnovationTypes' => $delivery->executedInnovationTypes, + 'executedDocumentationTypes' => $delivery->executedDocumentationTypes, ]; } From 395aed9a94ae39b3fa16f312544f67184c14cee5 Mon Sep 17 00:00:00 2001 From: Victor Ferreira Date: Mon, 16 Mar 2026 23:20:13 -0300 Subject: [PATCH 56/60] Convert opportunity-enable-workplan style.css to style.scss --- .../opportunity-enable-workplan/{style.css => style.scss} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/{style.css => style.scss} (100%) diff --git a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/style.css b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/style.scss similarity index 100% rename from src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/style.css rename to src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/style.scss From 0992de413bb6f6e871099eb6fc6607fe4fc6c365 Mon Sep 17 00:00:00 2001 From: Victor Ferreira Date: Mon, 16 Mar 2026 23:21:17 -0300 Subject: [PATCH 57/60] Make mc-multiselect model prop required instead of defaulting to empty array --- src/modules/Components/components/mc-multiselect/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/Components/components/mc-multiselect/script.js b/src/modules/Components/components/mc-multiselect/script.js index 80de93f93c..61e45ced4f 100644 --- a/src/modules/Components/components/mc-multiselect/script.js +++ b/src/modules/Components/components/mc-multiselect/script.js @@ -21,7 +21,7 @@ app.component('mc-multiselect', { model: { type: Array, - default: () => [], + required: true, }, title: { From 99fb182edeae9c41163755ca22ac269f744814f7 Mon Sep 17 00:00:00 2001 From: Victor Ferreira Date: Tue, 17 Mar 2026 11:00:36 -0300 Subject: [PATCH 58/60] Move opportunity-enable-workplan styles to BaseV2 theme SCSS component file --- .../opportunity-enable-workplan/style.scss | 79 ------------------- .../_opportunity-enable-workplan.scss | 77 +++++++++++++++++- 2 files changed, 74 insertions(+), 82 deletions(-) delete mode 100644 src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/style.scss diff --git a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/style.scss b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/style.scss deleted file mode 100644 index 39f64ccc9f..0000000000 --- a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/style.scss +++ /dev/null @@ -1,79 +0,0 @@ -/* Estilos para checkboxes de obrigatoriedade (sub-checkboxes) */ -.field__checkbox--sub { - margin-left: 16px; - font-size: 0.9em; - color: #666; - font-weight: normal; - white-space: nowrap; -} - -.field__checkbox--sub input[type="checkbox"] { - margin-right: 8px; -} - -/* Animação suave para aparecer/desaparecer */ -.field__checkbox--sub { - animation: fadeIn 0.2s ease-in; -} - -@keyframes fadeIn { - from { - opacity: 0; - transform: translateX(-4px); - } - to { - opacity: 1; - transform: translateX(0); - } -} - -/* Agrupamento inline: campo principal + obrigatório na mesma linha */ -.field__group { - display: flex; - flex-direction: row; - align-items: center; - flex-wrap: wrap; - margin-bottom: 12px; - gap: 0; -} - -.field__group .field__checkbox { - flex-shrink: 0; -} - -.field__group .field__checkbox--sub:last-child { - margin-bottom: 0; -} - -/* Headers de seção para agrupamento temático */ -.config-section-header { - font-size: 1.1rem; - font-weight: 600; - color: var(--mc-primary-500, #4a90e2); - margin-top: 24px; - margin-bottom: 8px; - padding-bottom: 6px; - border-bottom: 2px solid var(--mc-primary-500, #4a90e2); -} - -.config-section-header:first-child { - margin-top: 0; -} - -.config-section-description { - font-size: 0.85rem; - color: var(--mc-gray-700, #666); - margin-bottom: 16px; - font-style: italic; - line-height: 1.4; -} - -@media (max-width: 768px) { - .config-section-header { - font-size: 1rem; - } - - .config-section-description { - font-size: 0.8rem; - } -} diff --git a/src/themes/BaseV2/assets-src/sass/2.components/_opportunity-enable-workplan.scss b/src/themes/BaseV2/assets-src/sass/2.components/_opportunity-enable-workplan.scss index fb7f71e12b..f0345b1c34 100644 --- a/src/themes/BaseV2/assets-src/sass/2.components/_opportunity-enable-workplan.scss +++ b/src/themes/BaseV2/assets-src/sass/2.components/_opportunity-enable-workplan.scss @@ -1,5 +1,16 @@ @use '../0.settings/mixins' as *; +@keyframes fadeIn { + from { + opacity: 0; + transform: translateX(-4px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + .opportunity-enable-workplan { display: flex; flex-direction: column; @@ -39,7 +50,7 @@ margin-top: size(16); padding: size(20); width: 100%; - } + } .field__limits { max-width: 6.25rem; @@ -48,8 +59,8 @@ } .disabled-workplan { - display:flex; - align-items:right; + display: flex; + align-items: center; justify-content: flex-end; } @@ -60,4 +71,64 @@ .mt { margin-top: size(8); } + + .field__checkbox--sub { + margin-left: 16px; + font-size: 0.9em; + color: #666; + font-weight: normal; + white-space: nowrap; + animation: fadeIn 0.2s ease-in; + + input[type="checkbox"] { + margin-right: 8px; + } + } + + .field__group { + display: flex; + flex-direction: row; + align-items: center; + flex-wrap: wrap; + margin-bottom: 12px; + gap: 0; + + .field__checkbox { + flex-shrink: 0; + } + + .field__checkbox--sub:last-child { + margin-bottom: 0; + } + } + + .config-section-header { + font-size: 1.1rem; + font-weight: 600; + color: var(--mc-primary-500, #4a90e2); + margin-top: 24px; + margin-bottom: 8px; + padding-bottom: 6px; + border-bottom: 2px solid var(--mc-primary-500, #4a90e2); + + &:first-child { + margin-top: 0; + } + + @media (max-width: 768px) { + font-size: 1rem; + } + } + + .config-section-description { + font-size: 0.85rem; + color: var(--mc-gray-700, #666); + margin-bottom: 16px; + font-style: italic; + line-height: 1.4; + + @media (max-width: 768px) { + font-size: 0.8rem; + } + } } \ No newline at end of file From 0ac3e519b97b1b5235f93fa252fdcc001069d2eb Mon Sep 17 00:00:00 2001 From: Victor Ferreira Date: Tue, 17 Mar 2026 21:42:22 -0300 Subject: [PATCH 59/60] =?UTF-8?q?fix:=20exibe=20tag=20obrigat=C3=B3rio=20n?= =?UTF-8?q?os=20campos=20pai=20de=20comunidades,=20Trans,=20acessibilidade?= =?UTF-8?q?,=20socioambiental=20e=20estrat=C3=A9gias=20de=20comunica=C3=A7?= =?UTF-8?q?=C3=A3o=20no=20plano=20de=20trabalho?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/OpportunityWorkplan/Module.php | 6 ++++++ .../opportunity-enable-workplan/template.php | 3 +++ .../components/registration-workplan/template.php | 10 +++++----- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/modules/OpportunityWorkplan/Module.php b/src/modules/OpportunityWorkplan/Module.php index 8533677478..9811890204 100644 --- a/src/modules/OpportunityWorkplan/Module.php +++ b/src/modules/OpportunityWorkplan/Module.php @@ -541,6 +541,12 @@ function register() 'default_value' => false ]); + $this->registerOpportunityMetadata('workplan_deliveryRequireHasPressStrategy', [ + 'label' => i::__('Estratégias de comunicação são obrigatórias'), + 'type' => 'boolean', + 'default_value' => false + ]); + $this->registerOpportunityMetadata('workplan_deliveryInformCommunicationChannels', [ 'label' => i::__('Informar canais de comunicação'), 'type' => 'boolean', diff --git a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php index 08bbebef6d..ca410d9503 100644 --- a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php +++ b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php @@ -381,6 +381,9 @@ +
diff --git a/src/modules/OpportunityWorkplan/components/registration-workplan/template.php b/src/modules/OpportunityWorkplan/components/registration-workplan/template.php index 4671f695c7..28ff7256ed 100644 --- a/src/modules/OpportunityWorkplan/components/registration-workplan/template.php +++ b/src/modules/OpportunityWorkplan/components/registration-workplan/template.php @@ -439,7 +439,7 @@
- + @@ -470,7 +470,7 @@
- + @@ -504,7 +504,7 @@

- + @@ -456,7 +456,7 @@
- + @@ -485,7 +485,7 @@
- +