Skip to content

Commit

Permalink
Merge pull request #19 from secultce/develop
Browse files Browse the repository at this point in the history
Release v1.1.1
ronnyjohnti authored May 27, 2024
2 parents 3916a23 + bb8d7c7 commit be01841
Showing 6 changed files with 193 additions and 96 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/vendor/
/.idea/
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -3,12 +3,20 @@ Todas as mudanças relevantes serão documentadas nesse arquivo.

O formato é baseado no [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0), e esse projeto adere ao [Semantic Versionning](https://semver.org/spec/v2.0.0.html).

## [1.1.1] - 2024-05-27
### Adicionado
- Adicionada notificação no painel de controle do proponente quando o parecer é publicado.
- Adicionada publicação de parecer em avaliações técnicas.
### Correções
- Retirado link fantasma para o perfil do parecerista na visão do proponente;
- Corrigido botão "Visualizar Pareceres" para aparecer ao proponente, somente quando pareceres estiverem publicados.

## [1.1.0] - 2024-03-13
### Adicionado
- Agora o administrador pode selecionar se quer fazer publicação dos pareceres de forma automática ou manual;
- Adicionando botão para administrador publicar os pareceres (somente quando estiver configurado como publicação manual).

## [1.0.0] - 2024-02-27
### Adicionado
- O administrador da oportunidade consegue ver a lista de pareceres na aba de inscrições da oportunidade.
- O administrador da oportunidade consegue ver a lista de pareceres na aba de inscrições da oportunidade;
- O proponente consegue visualizar o parecer documental desde que a oportunidade esteja com o resultado publicado.
46 changes: 43 additions & 3 deletions Controllers/OpinionManagement.php
Original file line number Diff line number Diff line change
@@ -2,8 +2,10 @@

namespace OpinionManagement\Controllers;

use Doctrine\Common\Collections\Criteria;
use MapasCulturais\Controller,
MapasCulturais\App;
use MapasCulturais\Entities\Notification;
use OpinionManagement\Helpers\EvaluationList;

class OpinionManagement extends Controller
@@ -37,11 +39,14 @@ public function GET_opinions(): void
$registration = $app->repo('Registration')->find($this->getData['id']);
if($registration->canUser('view')) {
$opinions = new EvaluationList($registration);
$this->json($opinions);
$this->json([
'evaluationMethod' => (string) $registration->opportunity->evaluationMethodConfiguration->type,
'opinions' => $opinions,
]);
return;
}

$this->errorJson(['permission-denied'], 403);
$this->json(['permission-denied'], 403);
}

public function POST_publishOpinions(): void
@@ -58,13 +63,48 @@ public function POST_publishOpinions(): void
}


$opportunity->setMetadata('publishedOpinions', 'true');
$opportunity->setMetadata('publishedOpinions', true);
$error = $opportunity->save(true);
if($error) {
$this->errorJson(['error' => new \PDOException('Cannot save this data')], 500);
return;
}

$this->notificateUsers($opportunity->id);

$this->json(['success' => true]);
}

public static function notificateUsers(int $opportunityId, bool $verifyPublishingOpinions = true): bool
{
$app = App::i();
$opportunity = $app->repo('Opportunity')->find($opportunityId);
if($verifyPublishingOpinions && $opportunity->publishedOpinions === false) {
return false;
}

set_time_limit(500);

$criteria = new Criteria();
$criteria->where($criteria->expr()->eq('opportunity', $opportunity));
$criteria->andWhere($criteria->expr()->gt('status', '0'));

$registrations = $app->repo('Registration')->matching($criteria);
$count = count($registrations);
foreach ($registrations as $i => $registration) {
$notification = new Notification();
$notification->user = $registration->owner->user;
$notification->message =
sprintf(
"Sua inscrição <a style='font-weight:bold;' href='/inscricao/{$registration->id}'>%s</a>" .
" da oportunidade <a style='font-weight:bold;' href='/oportunidade/{$opportunity->id}'/>%s</a>está com os pareceres publicados.",
$registration->number,
$opportunity->name
);
$notification->save(true);
$app->log->debug("Notificação ".($i+1)."/$count enviada para o usuário {$registration->owner->user->id} ({$registration->owner->name})");
}

return true;
}
}
106 changes: 46 additions & 60 deletions Plugin.php
Original file line number Diff line number Diff line change
@@ -4,6 +4,8 @@

use MapasCulturais\App,
OpinionManagement\Controllers\OpinionManagement;
use MapasCulturais\Entities\Opportunity as OpportunityEntity;
use MapasCulturais\Entities\Registration;

/**
* @method part(string $string, array $args = [])
@@ -38,27 +40,20 @@ public function _init(): void
);
});

$app->hook('template(opportunity.edit.registration-config):after', function () use ($app) {
$plugin = $this;

$app->hook('template(opportunity.edit.evaluations-config):begin', function () use ($app, $plugin) {
$opportunity = $this->controller->requestedEntity;
/**
* @todo: Refatorar quando for mudar para publicar pareceres técnicos
*/
if($opportunity->evaluationMethodConfiguration->type != 'documentary') {
return;

if($plugin::isEvaluationMethodValid($opportunity)) {
$this->part('OpinionManagement/selection-autopublish', ['opportunity' => $opportunity]);
}
$this->part('OpinionManagement/selection-autopublish', ['opportunity' => $opportunity]);
});

$app->hook('template(opportunity.single.registration-list-header):end', function() use($app) {
$app->hook('template(opportunity.single.registration-list-header):end', function() use($app, $plugin) {
$opportunity = $this->controller->requestedEntity;
/**
* @todo: Refatorar quando for mudar para publicar pareceres técnicos
*/
if($opportunity->evaluationMethodConfiguration->type != 'documentary') {
return;
}

if($opportunity->canUser('@control')) {
if($plugin::isEvaluationMethodValid($opportunity) && $opportunity->canUser('@control')) {
$this->part('OpinionManagement/admin-registrations-table-column.php');
$app->view->enqueueScript(
'app',
@@ -73,68 +68,53 @@ public function _init(): void
});

$app->hook('template(opportunity.single.user-registration-table--registration--status):end', function ($registration, $opportunity) use ($app) {
/**
* @todo: Refatorar quando for mudar para publicar pareceres técnicos
*/
if($opportunity->publishedOpinions != 'true') {
return;
}

if($registration->canUser('@control')) {
if($opportunity->publishedOpinions === true && $registration->canUser('@control')) {
$this->part('OpinionManagement/user-btn-show-opinion.php', ['registration' => $registration]);
}
});

$app->hook('template(opportunity.single.opportunity-registrations--tables):begin', function () use ($app) {
$app->hook('template(opportunity.single.opportunity-registrations--tables):begin', function () use ($app, $plugin) {
$opportunity = $this->controller->requestedEntity;
/**
* @todo: Refatorar quando for mudar para publicar pareceres técnicos
*/
if($opportunity->evaluationMethodConfiguration->type != 'documentary'
|| $opportunity->autopublishOpinions !== 'Não'
|| $opportunity->publishedOpinions == 'true'
if($plugin::isEvaluationMethodValid($opportunity)
&& $opportunity->autopublishOpinions !== 'Sim'
&& !$opportunity->publishedOpinions
) {
return;
$this->part('OpinionManagement/admin-btn-publish-opinions.php', ['opportunity' => $opportunity]);
}

$this->part('OpinionManagement/admin-btn-publish-opinions.php', ['opportunity' => $opportunity]);
});

$app->hook('template(registration.view.header-fieldset):after', function() use($app) {
$app->hook('template(registration.view.header-fieldset):after', function() use($app, $plugin) {
$registration = $this->controller->requestedEntity;
$opportunity = $registration->opportunity;

/**
* @todo: Refatorar quando for mudar para publicar pareceres técnicos
*/
if($opportunity->evaluationMethodConfiguration->type != 'documentary' || (!$opportunity->publishedRegistrations && !$opportunity->canUser('@control'))) {
return;
}

if($registration->canUser('@control')) {
if($plugin::isEvaluationMethodValid($opportunity)
&& $opportunity->publishedOpinions
&& $opportunity->canUser('@control')
) {
$this->part('OpinionManagement/user-btn-show-opinion.php');
}
});

$app->hook('template(panel.<<registrations|index>>.panel-registration):end', function ($registration) use ($app) {
/**
* @todo: Refatorar quando for mudar para publicar pareceres técnicos
*/
if(!$registration->opportunity->publishedRegistrations
|| $registration->opportunity->evaluationMethodConfiguration->type != 'documentary'
) return;
$this->part('OpinionManagement/user-btn-show-opinion.php', ['registration' => $registration]);
$app->view->enqueueScript(
'app',
'opinion-management',
'OpinionManagement/js/opinionManagement.js'
);
$app->hook('template(panel.<<registrations|index>>.panel-registration):end', function (Registration $registration) use ($app,$plugin) {
if($registration->opportunity->publishedOpinions
&& $plugin::isEvaluationMethodValid($registration->opportunity)
) {
$this->part('OpinionManagement/user-btn-show-opinion.php', ['registration' => $registration]);
$app->view->enqueueScript(
'app',
'opinion-management',
'OpinionManagement/js/opinionManagement.js'
);
}
});

$app->hook('entity(Opportunity).publishRegistrations:before', function () {
if($this->autopublishOpinions == 'Sim')
$this->setMetadata('publishedOpinions', 'true');
if($this->autopublishOpinions === 'Sim') {
$this->setMetadata('publishedOpinions', true);
OpinionManagement::notificateUsers($this->id);
}
});

}

/**
@@ -155,11 +135,17 @@ public function register(): void
'required' => true,
]);
$this->registerOpportunityMetadata('publishedOpinions', [
'type' => 'select',
'type' => 'boolean',
'label' => \MapasCulturais\i::__('Pareceres publicados'),
'default_value' => 'false',
'options' => ['true', 'false'],
'default_value' => false,
'options' => [true, false],
'required' => true,
]);
}

public static function isEvaluationMethodValid(OpportunityEntity $opportunity): bool
{
return $opportunity->evaluationMethodConfiguration->type == 'documentary'
|| $opportunity->evaluationMethodConfiguration->type == 'technical';
}
}
117 changes: 92 additions & 25 deletions assets/OpinionManagement/js/opinionManagement.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,66 @@
const handleChkCollapseChange = target => {
const handleChkCollapseChange = (target, evaluationMethod) => {
if(evaluationMethod === 'technical') {
return
}
const chkCollapses = $('.chk-collapse')
for(let i= 0;i < chkCollapses.length; i++) {
if (chkCollapses[i] === target) continue
chkCollapses[i].checked = false
}
}

const opinionHtml = opinion => {
let htmlParsed = '<div class="opinion">'
htmlParsed += `<div class="evaluation-title">
<h3>Parecerista <a href="${opinion.agent.singleUrl}" target="_blank">${opinion.agent.name}</a></h3>
<label for="chk-collapse-${opinion.id}"><div class="collapsible"></div></label>
<p>Resultado da avaliação documental:<a href="${opinion.singleUrl}" class="criteria-status-${opinion.result < 0 ? 'invalid' : 'valid'}"></a></p>
</div>
<input type="checkbox" id="chk-collapse-${opinion.id}" class="chk-collapse" name="chk-collapse" onchange="handleChkCollapseChange(this)">`
for(const criteriaId in opinion.evaluationData) {
const criteria = opinion.evaluationData[criteriaId]
criteria.obs_items = criteria.obs_items?.replace('\n','<br>')
criteria.obs = criteria.obs?.replace('\n','<br>')

htmlParsed += `<div class="criteria-fields">`
htmlParsed += `<h5>${criteria.label}</h5>`
htmlParsed += `<p class="criteria-status-${criteria.evaluation === '' ? 'pending' : criteria.evaluation}"></p>`
// htmlParsed += criteria.evaluation === 'invalid' ? `<p class="opinion-evaluation-obs">${criteria['obs_items']}</p>` : ''
htmlParsed += `<p class="opinion-evaluation-obs">${criteria['obs_items']}</p>`
htmlParsed += `<p class="opinion-evaluation-obs">${criteria['obs']}</p>`
htmlParsed += `</div>`
const opinionHtml = (opinion, evaluationMethod) => {
const resultHtml = (opinionUrl, result, evaluationMethod) => {
if (evaluationMethod === 'documentary')
return `<p>
Resultado da avaliação documental:
<a href="${opinionUrl}"
class="criteria-status-${result < 0 ? 'invalid' : 'valid'}"></a>
</p>`;
if (evaluationMethod === 'technical')
return `<p>Nota da avaliação técnica:<a href="${opinionUrl}">${result}</a></p>`
}
htmlParsed += '</div>'

const evaluationToHtml = (opinion, evaluationMethod) => {
let evaluationHtml = ''
if(evaluationMethod === 'documentary') {
for(const criteriaId in opinion.evaluationData) {
const criteria = opinion.evaluationData[criteriaId]
criteria.obs_items = criteria.obs_items?.replace('\n','<br>')
criteria.obs = criteria.obs?.replace('\n','<br>')

evaluationHtml += `<div class="criteria-fields">
<h5>${criteria.label}</h5>
<p class="criteria-status-${criteria.evaluation === '' ? 'pending' : criteria.evaluation}"></p>
${criteria.evaluation === 'invalid' ? `<p class="opinion-evaluation-obs">${criteria['obs_items']}</p>` : ''}
<p class="opinion-evaluation-obs">${criteria['obs']}</p>
</div>`
}
}
if(evaluationMethod === 'technical') {
evaluationHtml += `<div class="criteria-fields">
<p class="opinion-evaluation-obs">${opinion.evaluationData.obs}</p>
</div>`
}

return evaluationHtml
}

let htmlParsed = `<div class="opinion">
<div class="evaluation-title">
<h3>
Parecerista
${opinion.agent.singleUrl ?
'<a href="'+opinion.agent.singleUrl+'" target="_blank">'+opinion.agent.name+'</a>' :
opinion.agent.name
}
</h3>
<label for="chk-collapse-${opinion.id}"><div class="collapsible"></div></label>
${resultHtml(opinion.singleUrl, opinion.result, evaluationMethod)}
</div>
<input type="checkbox" id="chk-collapse-${opinion.id}" class="chk-collapse" name="chk-collapse" onchange="handleChkCollapseChange(this, '${evaluationMethod}')">
${evaluationToHtml(opinion, evaluationMethod)}
</div>`
return htmlParsed
}

@@ -44,15 +77,16 @@ const showOpinions = registrationId => {
if (!response.ok) throw new Error(response.statusText)
return response.json()
})
.then(opinions => {
.then(({opinions, evaluationMethod}) => {
// @todo: Em versões futuras fazer alteração para mostrar quem são os pareceristas com pendências na avaliação
if(opinions.length === 0)
return Swal.fire({
title: "Não há avaliações!",
text: "As avaliações desta inscrição ainda não foram iniciadas."
})

const html = `<div>${opinions.map(opinion => opinionHtml(opinion)).join('')}</div>`;

const html = `<div>${opinions.map(opinion => opinionHtml(opinion, evaluationMethod)).join('')}</div>`;

Swal.fire({
html,
@@ -99,7 +133,6 @@ const publishOpinions = target => {
return response.json()
})
.then(response => {
console.log(response)
Swal.fire({
title: "Pareceres publicados com sucesso!",
text: "Os pareceres desta inscrição agora encontram-se publicados.",
@@ -126,3 +159,37 @@ const errorAlert = message => {
showCloseButton: true,
})
}

alertPublish = id => {
Swal.fire({
title: "Tem certeza?",
html: "<strong>ATENÇÃO</strong>, essa ação é uma ação irreversível. Caso a próxima fase seja uma prestação de contas, primeiro crie a fase de prestação de contas para só depois fazer a publicação.",
showConfirmButton: true,
showCloseButton: false,
showCancelButton: true,
confirmButtonText: 'Publicar',
cancelButtonText: 'Cancelar',
})
.then(function (result) {
if(!result.isConfirmed) {
MapasCulturais.Messages.alert("Ação cancelada!")
return
}

let loading = Swal.fire({
title: "Verificando se há notificações a serem enviadas.",
allowOutsideClick: false,
showConfirmButton: false,
didOpen: () => {
Swal.showLoading();
},
})

var url = MapasCulturais.createUrl('opportunity', 'publishRegistrations', [id]);
$.get(url, function() {
loading.close()
MapasCulturais.Messages.success('Resultado publicado');
})
location.reload();
});
}
8 changes: 1 addition & 7 deletions layouts/parts/OpinionManagement/selection-autopublish.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
<style>
#opinions-config:has(~ #evaluations-config[style="display: none;"]) {
display: none;
}
</style>

<div id="opinions-config" class="registration-fieldset">
<h4>Publicação de Pareceres</h4>
<p>Deseja que os pareceres desta fase/oportunidade sejam publicados para os proponentes automaticamente ao publicar os resultados?</p>
<span
class="js-editable editable editable-click editable-unsaved"
class="js-editable editable editable-click"
data-edit="autopublishOpinions"
data-original-title="Publicar pareceres automaticamente"
data-value="<?= /** @var \MapasCulturais\Entities\Opportunity $opportunity */

0 comments on commit be01841

Please sign in to comment.