From bb24330430e0e5b633cab0b49f56a62d869e8df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20Magalh=C3=A3es?= Date: Fri, 23 Aug 2024 11:12:52 -0300 Subject: [PATCH] Feature/modelos (#427) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add action para entidade * feat: add template do componente modal salvar modelo * feat: add regra front gerar modelo * feat: add redme como usar o componente * feat: aplica regra apenas para entidade oportunidade * feat: add requisição componente success * feat: add trait generate model * feat: add geração de modelo com especificação se é modelo * feat: define identificação se é modelo e se é oficial * feat: define padrão é modelo * feat: adiciona filtro retornar oportunidades diferente de modelos * feat: aplica regra no componente apenas para entidade oportunidade * feat: redireciona para meus modelos * feat: se modelo habilita ação de somente remover * feat: muda regra status quando modelo e gera fields e files para os forms * feat: add testes de validação da geração e sucesso do modelo --- src/conf/opportunity-types.php | 10 ++ src/core/Controllers/Opportunity.php | 1 + src/core/Traits/EntityGenerateModel.php | 163 ++++++++++++++++++ src/cypress/e2e/opportunity/index.cy.js | 51 +++++- .../components/entity-actions/template.php | 9 +- .../opportunity-create-model/README.md | 11 ++ .../opportunity-create-model/script.js | 62 +++++++ .../opportunity-create-model/template.php | 39 +++++ .../opportunity-create-model/texts.php | 10 ++ .../components/panel--entity-card/script.js | 4 + .../components/panel--entity-tabs/script.js | 22 ++- .../panel--entity-tabs/template.php | 2 +- 12 files changed, 376 insertions(+), 8 deletions(-) create mode 100644 src/core/Traits/EntityGenerateModel.php create mode 100644 src/modules/Entities/components/opportunity-create-model/README.md create mode 100644 src/modules/Entities/components/opportunity-create-model/script.js create mode 100644 src/modules/Entities/components/opportunity-create-model/template.php create mode 100644 src/modules/Entities/components/opportunity-create-model/texts.php diff --git a/src/conf/opportunity-types.php b/src/conf/opportunity-types.php index 75084b47cb..3b98391392 100644 --- a/src/conf/opportunity-types.php +++ b/src/conf/opportunity-types.php @@ -257,6 +257,16 @@ function compareNamesOpportunity ($item1, $item2) { return ($value == 0 || $value == "") ? false : true; } ), + 'isModel' => array( + 'type' => 'integer', + 'label' => \MapasCulturais\i::__('É modelo?'), + 'default_value' => 0 + ), + 'isModelOfficial' => array( + 'type' => 'integer', + 'label' => \MapasCulturais\i::__('É modelo oficial?'), + ), + ), 'items' => $items, diff --git a/src/core/Controllers/Opportunity.php b/src/core/Controllers/Opportunity.php index d2f62ff72b..74a05ed05d 100644 --- a/src/core/Controllers/Opportunity.php +++ b/src/core/Controllers/Opportunity.php @@ -37,6 +37,7 @@ class Opportunity extends EntityController { Traits\ControllerAPINested, Traits\ControllerLock, Traits\EntityOpportunityDuplicator, + Traits\EntityGenerateModel, Traits\ControllerEntityActions { Traits\ControllerEntityActions::PATCH_single as _PATCH_single; } diff --git a/src/core/Traits/EntityGenerateModel.php b/src/core/Traits/EntityGenerateModel.php new file mode 100644 index 0000000000..45630c7812 --- /dev/null +++ b/src/core/Traits/EntityGenerateModel.php @@ -0,0 +1,163 @@ +requireAuthentication(); + $this->opportunity = $this->requestedEntity; + $this->opportunityModel = $this->generateModel(); + + $this->generateEvaluationMethods(); + $this->generatePhases(); + $this->generateMetadata(); + $this->generateRegistrationFieldsAndFiles(); + + $this->opportunityModel->save(true); + + if($this->isAjax()){ + $this->json($this->opportunity); + }else{ + $app->redirect($app->request->getReferer()); + } + } + + private function generateModel() : ProjectOpportunity + { + $app = App::i(); + + $postData = $this->postData; + + $name = $postData['name']; + $description = $postData['description']; + + $this->opportunityModel = clone $this->opportunity; + + $this->opportunityModel->setName($name); + $this->opportunityModel->setStatus(-1); + $this->opportunityModel->setShortDescription($description); + $app->em->persist($this->opportunityModel); + $app->em->flush(); + + return $this->opportunityModel; + } + + private function generateEvaluationMethods() : void + { + $app = App::i(); + + // duplica o método de avaliação para a oportunidade primária + $evaluationMethodConfigurations = $app->repo('EvaluationMethodConfiguration')->findBy([ + 'opportunity' => $this->opportunity + ]); + foreach ($evaluationMethodConfigurations as $evaluationMethodConfiguration) { + $newMethodConfiguration = clone $evaluationMethodConfiguration; + $newMethodConfiguration->setOpportunity($this->opportunityModel); + $newMethodConfiguration->save(true); + + // duplica os metadados das configurações do modelo de avaliação + foreach ($evaluationMethodConfiguration->getMetadata() as $metadataKey => $metadataValue) { + $newMethodConfiguration->setMetadata($metadataKey, $metadataValue); + $newMethodConfiguration->save(true); + } + } + } + + private function generatePhases() : void + { + $app = App::i(); + + $phases = $app->repo('Opportunity')->findBy([ + 'parent' => $this->opportunity + ]); + foreach ($phases as $phase) { + if (!$phase->getMetadata('isLastPhase')) { + $newPhase = clone $phase; + $newPhase->setParent($this->opportunityModel); + + foreach ($phase->getMetadata() as $metadataKey => $metadataValue) { + if (!is_null($metadataValue) && $metadataValue != '') { + $newPhase->setMetadata($metadataKey, $metadataValue); + $newPhase->save(true); + } + } + + $newPhase->save(true); + + $evaluationMethodConfigurations = $app->repo('EvaluationMethodConfiguration')->findBy([ + 'opportunity' => $phase + ]); + + foreach ($evaluationMethodConfigurations as $evaluationMethodConfiguration) { + $newMethodConfiguration = clone $evaluationMethodConfiguration; + $newMethodConfiguration->setOpportunity($newPhase); + $newMethodConfiguration->save(true); + + // duplica os metadados das configurações do modelo de avaliação para a fase + foreach ($evaluationMethodConfiguration->getMetadata() as $metadataKey => $metadataValue) { + $newMethodConfiguration->setMetadata($metadataKey, $metadataValue); + $newMethodConfiguration->save(true); + } + } + } + + if ($phase->getMetadata('isLastPhase')) { + $publishDate = $phase->getPublishTimestamp(); + } + } + + if (isset($publishDate)) { + $phases = $app->repo('Opportunity')->findBy([ + 'parent' => $this->opportunityModel + ]); + + foreach ($phases as $phase) { + if ($phase->getMetadata('isLastPhase')) { + $phase->setPublishTimestamp($publishDate); + $phase->save(true); + } + } + } + } + + + private function generateMetadata() : void + { + foreach ($this->opportunity->getMetadata() as $metadataKey => $metadataValue) { + if (!is_null($metadataValue) && $metadataValue != '') { + $this->opportunityModel->setMetadata($metadataKey, $metadataValue); + } + } + + $this->opportunityModel->setMetadata('isModel', 1); + $this->opportunityModel->setMetadata('isModelOfficial', 0); + + $this->opportunityModel->saveTerms(); + } + + private function generateRegistrationFieldsAndFiles() : void + { + foreach ($this->opportunity->getRegistrationFieldConfigurations() as $registrationFieldConfiguration) { + $fieldConfiguration = clone $registrationFieldConfiguration; + $fieldConfiguration->setOwnerId($this->opportunityModel->getId()); + $fieldConfiguration->save(true); + } + + foreach ($this->opportunity->getRegistrationFileConfigurations() as $registrationFileConfiguration) { + $fileConfiguration = clone $registrationFileConfiguration; + $fileConfiguration->setOwnerId($this->opportunityModel->getId()); + $fileConfiguration->save(true); + } + + } +} diff --git a/src/cypress/e2e/opportunity/index.cy.js b/src/cypress/e2e/opportunity/index.cy.js index 21a911c9cb..9f44040373 100644 --- a/src/cypress/e2e/opportunity/index.cy.js +++ b/src/cypress/e2e/opportunity/index.cy.js @@ -152,7 +152,7 @@ describe("Opportunity Page", () => { cy.wait(1000); }); - it("Garante geração de modelo da oportunidade", () => { + it("Garante duplicação da oportunidade", () => { cy.visit("/autenticacao/"); loginWith("Admin@local", "mapas123"); @@ -160,7 +160,7 @@ describe("Opportunity Page", () => { cy.get('.rowBtn > :nth-child(6)').click(); - cy.contains("Salvar modelo"); + cy.contains("Duplicar modelo"); cy.contains("Todas as configurações atuais da oportunidade, incluindo o vínculo com a entidade associada e os campos de formulário criados, serão duplicadas."); cy.get('.modal__action > .button--primary').click(); @@ -170,4 +170,51 @@ describe("Opportunity Page", () => { cy.get('.panel-entity-card__header > .left > .panel-entity-card__header--info > .panel-entity-card__header--info-link > .mc-title').contains("[Cópia]"); }); + + it("Garante preenchimento obrigatório na geração de modelo baseado em uma oportunidade", () => { + cy.visit("/autenticacao/"); + loginWith("Admin@local", "mapas123"); + cy.get(':nth-child(4) > :nth-child(1) > a').click(); + cy.get('.right > .button--primary').click(); + + cy.wait(1000); + + cy.get('.col-12 > .button').click(); + + cy.get('.modal__content > :nth-child(3) > :nth-child(1) > input').should('be.visible').clear(); + + cy.get(':nth-child(3) > textarea').should('be.visible').clear(); + + cy.get('.modal__action > .button--primary').click(); + + cy.contains('Todos os campos são obrigatorio'); + }); + + it("Garante geração de modelo baseado em uma oportunidade", () => { + cy.visit("/autenticacao/"); + loginWith("Admin@local", "mapas123"); + cy.get(':nth-child(4) > :nth-child(1) > a').click(); + cy.get('.right > .button--primary').click(); + + cy.wait(1000); + + cy.get('.col-12 > .button').click(); + + cy.contains("Salvar modelo"); + cy.contains("Para salvar um modelo, preencha os campos abaixo."); + cy.contains("Nome do modelo"); + cy.contains("Breve descrição do modelo"); + cy.contains("Salvar modelo"); + + cy.get('.modal__content > :nth-child(3) > :nth-child(1) > input').should('be.visible').clear().type('Nome do modelo'); + + cy.get(':nth-child(3) > textarea').should('be.visible').type('Descrição do modelo'); + + cy.get('.modal__action > .button--primary').click(); + cy.wait(3000); + + cy.visit("/minhas-oportunidades/#mymodels"); + cy.wait(1000); + cy.contains("Nome do modelo"); + }); }); diff --git a/src/modules/Entities/components/entity-actions/template.php b/src/modules/Entities/components/entity-actions/template.php index 1b8869ac6b..895db4cbd6 100644 --- a/src/modules/Entities/components/entity-actions/template.php +++ b/src/modules/Entities/components/entity-actions/template.php @@ -9,6 +9,7 @@ $this->import(' mc-confirm-button mc-loading + opportunity-create-model '); ?>
@@ -47,16 +48,20 @@ +
+ +
+ applyTemplateHook('entity-actions--primary', 'end') ?>
applyTemplateHook('entity-actions--leftGroupBtn', 'after'); ?> diff --git a/src/modules/Entities/components/opportunity-create-model/README.md b/src/modules/Entities/components/opportunity-create-model/README.md new file mode 100644 index 0000000000..6e50a8aae7 --- /dev/null +++ b/src/modules/Entities/components/opportunity-create-model/README.md @@ -0,0 +1,11 @@ +# Componente `` + +### Importando componente +```PHP +import('opportunity-create-model'); +?> +``` +### Exemplos de uso +```HTML + \ No newline at end of file diff --git a/src/modules/Entities/components/opportunity-create-model/script.js b/src/modules/Entities/components/opportunity-create-model/script.js new file mode 100644 index 0000000000..48aa662fd8 --- /dev/null +++ b/src/modules/Entities/components/opportunity-create-model/script.js @@ -0,0 +1,62 @@ +app.component('opportunity-create-model', { + template: $TEMPLATES['opportunity-create-model'], + setup() { + const messages = useMessages(); + const text = Utils.getTexts('opportunity-create-model') + return { text, messages } + }, + props: { + entity: { + type: Entity, + required: true, + }, + }, + + data() { + let sendSuccess = false; + let formData = { + name: this.entity.name, + description: '', + } + + return { sendSuccess, formData } + }, + + methods: { + async save() { + this.__processing = this.text('Gerando modelo...'); + + const api = new API(this.entity.__objectType); + + let objt = this.formData; + objt.entityId = this.entity.id; + + let error = null; + + if (error = this.validade(objt)) { + let mess = ""; + mess = this.text('Todos os campos são obrigatorio'); + this.messages.error(mess); + return; + } + + await api.POST(`/opportunity/generatemodel/${objt.entityId}`, objt).then(res => { + this.messages.success(this.text('Modelo gerado com sucesso')); + this.sendSuccess = true; + window.location.href = '/minhas-oportunidades/#mymodels'; + }); + }, + validade(objt) { + let result = null; + let ignore = []; + + Object.keys(objt).forEach(function (item) { + if (!objt[item] && !ignore.includes(item)) { + result = item; + return; + } + }); + return result; + }, + }, +}); diff --git a/src/modules/Entities/components/opportunity-create-model/template.php b/src/modules/Entities/components/opportunity-create-model/template.php new file mode 100644 index 0000000000..4c0837446a --- /dev/null +++ b/src/modules/Entities/components/opportunity-create-model/template.php @@ -0,0 +1,39 @@ +import(" + mc-modal +"); +?> +
+ + + + + + + +
\ No newline at end of file diff --git a/src/modules/Entities/components/opportunity-create-model/texts.php b/src/modules/Entities/components/opportunity-create-model/texts.php new file mode 100644 index 0000000000..453df2b0a0 --- /dev/null +++ b/src/modules/Entities/components/opportunity-create-model/texts.php @@ -0,0 +1,10 @@ + i::__('Recaptcha inválida'), + 'Todos os campos são obrigatorio' => i::__('Todos os campos são obrigatorio'), + 'Modelo gerado com sucesso' => i::__('Modelo gerado com sucesso'), + 'Gerando modelo...' => i::__('Gerando modelo...'), +]; diff --git a/src/modules/Panel/components/panel--entity-card/script.js b/src/modules/Panel/components/panel--entity-card/script.js index 9dd18c6d72..ca55355f84 100644 --- a/src/modules/Panel/components/panel--entity-card/script.js +++ b/src/modules/Panel/components/panel--entity-card/script.js @@ -23,6 +23,10 @@ app.component('panel--entity-card', { return this.class; }, leftButtons() { + if (this.entity.isModel) { + return 'delete'; + } + let buttons = 'archive,delete,destroy'; if(this.entity.status === 0) { diff --git a/src/modules/Panel/components/panel--entity-tabs/script.js b/src/modules/Panel/components/panel--entity-tabs/script.js index 53cdb2c67f..b36f6bc5fb 100644 --- a/src/modules/Panel/components/panel--entity-tabs/script.js +++ b/src/modules/Panel/components/panel--entity-tabs/script.js @@ -11,10 +11,23 @@ app.component('panel--entity-tabs', { }, data() { - let query = { + let query = {}; + + if (this.type == 'opportunity') { + query.isModel = 'OR(NULL(), EQ(0))'; + } + + query = { + ...query, '@order': 'updateTimestamp DESC', '@permissions': 'view' }; + + let queryGetModel = { + '@order': 'updateTimestamp DESC', + '@permissions': 'view' + }; + if (this.user) { query.user = `EQ(${this.user})` } @@ -25,6 +38,7 @@ app.component('panel--entity-tabs', { publish: { status: 'GTE(1)', ...query }, draft: { status: 'EQ(0)', ...query }, granted: { ...query, '@permissions': '@control', status: 'GTE(0)', user: '!EQ(@me)' }, + mymodels: { status: 'EQ(-1)', isModel: 'EQ(1)', ...queryGetModel }, trash: { status: 'EQ(-10)', ...query }, archived: { status: 'EQ(-2)', ...query }, }, @@ -44,11 +58,11 @@ app.component('panel--entity-tabs', { }, select: { type: String, - default: 'id,status,name,type,createTimestamp,terms,files.avatar,currentUserPermissions' + default: 'id,status,name,type,createTimestamp,terms,files.avatar,currentUserPermissions,isModel' }, tabs: { type: String, - default: "publish,draft,granted,trash,archived" + default: "publish,draft,granted,mymodels,trash,archived" }, }, @@ -64,6 +78,8 @@ app.component('panel--entity-tabs', { if (status == 'publish') { return true; + } else if (status == 'mymodels' && this.type == 'opportunity') { + return true; } else if (typeof this.description?.status == 'undefined') { return false; } else if (typeof this.description?.status?.options[status] != 'undefined') { diff --git a/src/modules/Panel/components/panel--entity-tabs/template.php b/src/modules/Panel/components/panel--entity-tabs/template.php index abddb6c250..9e67392fd2 100644 --- a/src/modules/Panel/components/panel--entity-tabs/template.php +++ b/src/modules/Panel/components/panel--entity-tabs/template.php @@ -18,6 +18,7 @@ 'publish' => i::esc_attr__('Publicados'), 'draft' => i::esc_attr__('Em rascunho'), 'granted' => i::esc_attr__('Com permissão'), + 'mymodels' => i::esc_attr__('Meus modelos'), 'archived' => i::esc_attr__('Arquivados'), 'trash' => i::esc_attr__('Lixeira'), ]; @@ -70,7 +71,6 @@ -