Agregar, editar y borrar comentarios

This commit is contained in:
Juan Pablo Vial
2024-07-04 18:09:25 -04:00
parent fc543729d2
commit a428aeebe1
7 changed files with 358 additions and 24 deletions

View File

@ -27,7 +27,10 @@ $app->group('/ventas', function($app) {
});
$app->group('/venta/{venta_id}', function($app) {
$app->get('/unidades[/]', [Ventas::class, 'unidades']);
$app->get('/comentarios[/]', [Ventas::class, 'comentarios']);
$app->group('/comentarios', function($app) {
$app->post('/add[/]', [Ventas::class, 'addComentario']);
$app->get('[/]', [Ventas::class, 'comentarios']);
});
$app->group('/escritura', function($app) {
$app->post('/add[/]', [Ventas\Escrituras::class, 'add']);
});

View File

@ -0,0 +1,8 @@
<?php
use Incoviba\Controller\API\Ventas\Comentarios;
$app->group('/comentario/{comentario_id}', function($app) {
$app->post('/edit[/]', [Comentarios::class, 'edit']);
$app->delete('/remove[/]', [Comentarios::class, 'remove']);
//$app->get('[/]', [Comentarios::class, 'get']);
});

View File

@ -3,9 +3,9 @@
COMENTARIOS
</div>
<div class="right aligned column">
<a href="javascript: addComment()" style="color: inherit;">
<button class="ui icon button" style="background: none; color: inherit; padding: 0;" id="add_comentario_button">
<i class="plus icon"></i>
</a>
</button>
</div>
</div>
<div class="ui segment">
@ -13,37 +13,199 @@
<tbody id="comentarios"></tbody>
</table>
</div>
<div class="ui modal" id="addComment">
<div class="header">
Agregar Comentario
</div>
<div class="content">
<form class="ui form">
<div class="three wide field">
<label>Fecha</label>
<div class="ui calendar" id="fecha">
<div class="ui icon input">
<input type="text" placeholder="Fecha">
<i class="calendar icon"></i>
</div>
</div>
</div>
<div class="field">
<label>Comentario</label>
<textarea id="comentario" rows="2"></textarea>
</div>
</form>
</div>
<div class="actions">
<div class="ui black deny button">
Cancelar
</div>
<div class="ui positive right labeled icon button">
Agregar
<i class="checkmark icon"></i>
</div>
</div>
</div>
<div class="ui modal" id="editComment">
<div class="header">
Edit Comentario
</div>
<div class="content">
<form class="ui form">
<input type="hidden" name="id" />
<div class="three wide field">
<label>Fecha</label>
<div class="ui calendar" id="fechaEdit">
<div class="ui icon input">
<input type="text" placeholder="Fecha">
<i class="calendar icon"></i>
</div>
</div>
</div>
<div class="field">
<label>Comentario</label>
<textarea id="comentarioEdit" rows="2"></textarea>
</div>
</form>
</div>
<div class="actions">
<div class="ui black deny button">
Cancelar
</div>
<div class="ui positive right labeled icon button">
Editar
<i class="checkmark icon"></i>
</div>
</div>
</div>
@push('page_scripts')
<script type="text/javascript">
class Comentario
{
class Comentario {
id
fecha
texto
constructor({fecha, texto})
constructor({id, fecha, texto})
{
this.id = id
this.fecha = new Date(fecha + 'T00:00:00')
this.texto = texto
}
draw(dateFormatter)
{
return $('<tr></tr>').append(
$('<td></td>').html(dateFormatter.format(this.fecha))
).append(
$('<td></td>').html(this.texto)
).append(
$('<td></td>').addClass('right aligned').append(
$('<a></a>').attr('href', 'javascript: removeComment();').append(
$('<i></i>').addClass('minus icon')
)
)
)
return [
'<tr>',
`<td class="collapsing">${dateFormatter.format(this.fecha)}</td>`,
`<td>${this.texto}</td>`,
'<td class="right aligned">',
`<button class="ui tiny tertiary icon button editComentario" data-id="${this.id}">`,
'<i class="edit icon"></i>',
'</button>',
`<button class="ui tiny tertiary red icon button removeComentario" data-id="${this.id}">`,
'<i class="minus icon"></i>',
'</button>',
'</td>',
'</tr>'
].join("\n")
}
}
class AddModal {
props
constructor({id}) {
this.props = {
id
}
$(this.props.id).modal({
onApprove: () => {
this.approve()
}
})
const cdo = structuredClone(calendar_date_options)
$(this.props.id).find('.ui.calendar').calendar(cdo)
this.hide()
}
approve() {
const fecha = $(this.props.id).find('#fecha').calendar('get date')
const fechaString = [fecha.getFullYear(), fecha.getMonth()+1, fecha.getDate()].join('-')
const comentario = $(this.props.id).find('#comentario').val()
const uri = '{{$urls->api}}/venta/{{$venta->id}}/comentarios/add'
const body = new FormData()
body.append('fecha', fechaString)
body.append('texto', comentario)
fetchAPI(uri, {method: 'post', body}).then(response => {
if (!response) {
return
}
return response.json().then(data => {
if (data.added) {
comentarios.comentarios.push(new Comentario(data.comentario))
comentarios.draw().comentarios()
}
})
})
}
show() {
const modal = $(this.props.id)
modal.find('form').trigger('reset')
modal.modal('show')
}
hide() {
const modal = $(this.props.id)
modal.modal('hide')
}
}
class EditModal {
props
constructor({id}) {
this.props = {
id
}
$(this.props.id).modal({
onApprove: () => {
this.approve()
}
})
const cdo = structuredClone(calendar_date_options)
$(this.props.id).find('.ui.calendar').calendar(cdo)
this.hide()
}
approve() {
const id = $(this.props.id).find("[name='id']").val()
const fecha = $(this.props.id).find('#fechaEdit').calendar('get date')
const fechaString = [fecha.getFullYear(), fecha.getMonth()+1, fecha.getDate()].join('-')
const comentario = $(this.props.id).find('#comentarioEdit').val()
const uri = `{{$urls->api}}/ventas/comentario/${id}/edit`
const body = new FormData()
body.append('fecha', fechaString)
body.append('texto', comentario)
fetchAPI(uri, {method: 'post', body}).then(response => {
if (!response) {
return
}
return response.json().then(data => {
if (data.edited) {
const idx = comentarios.comentarios.findIndex(comentario => comentario.id === data.comentario_id)
comentarios.comentarios[idx] = new Comentario(data.comentario)
comentarios.draw().comentarios()
}
})
})
}
show() {
const modal = $(this.props.id)
modal.modal('show')
}
hide() {
const modal = $(this.props.id)
modal.modal('hide')
}
}
const comentarios = {
comentarios: [],
id: '',
modals: {
add: null,
edit: null
},
fetch: function() {
return {
comentarios: () => {
@ -60,6 +222,7 @@
this.comentarios.push(new Comentario(settings))
})
this.draw().comentarios()
})
}
}
@ -73,11 +236,41 @@
this.comentarios.forEach(comentario => {
body.append(comentario.draw(dateFormatter))
})
$('.editComentario').on('click', event => {
const id = $(event.currentTarget).data('id')
const comentario = this.comentarios.find(comentario => comentario.id === id)
const modal = this.modals.edit
const fecha = new Date(comentario.fecha)
$(modal.props.id).find("[name='id']").val(id)
$(modal.props.id).find('#fechaEdit').calendar('set date', fecha)
$(modal.props.id).find('#comentarioEdit').val(comentario.texto)
modal.show()
})
$('.removeComentario').click(event => {
const id = $(event.currentTarget).data('id')
const uri = `{{$urls->api}}/ventas/comentario/${id}/remove`
fetchAPI(uri, {method: 'delete'}).then(response => {
if (!response) {
return
}
return response.json().then(data => {
if (data.removed) {
this.comentarios = this.comentarios.filter(comentario => comentario.id !== id)
this.draw().comentarios()
}
})
})
})
}
}
},
setup: function(id) {
this.id = id
this.modals.add = new AddModal({id: '#addComment'})
this.modals.edit = new EditModal({id: '#editComment'})
$('#add_comentario_button').click(() => {
this.modals.add.show()
})
this.fetch().comentarios()
}
}

View File

@ -12,6 +12,7 @@ use Incoviba\Controller\withRedis;
use Incoviba\Model;
use Incoviba\Repository;
use Incoviba\Service;
use Psr\Log\LoggerInterface;
class Ventas extends Controller
{
@ -190,7 +191,7 @@ class Ventas extends Controller
$output['total'] = count($output['comentarios']);
} catch (EmptyRedis) {
try {
$comentarios = $comentarioRepository->fetchByVenta($venta->id);
$comentarios = $comentarioRepository->fetchActiveByVenta($venta->id);
$output['total'] = count($comentarios);
$output['comentarios'] = $comentarios;
$this->saveRedis($redisService, $redisKey, $output['comentarios']);
@ -337,4 +338,36 @@ class Ventas extends Controller
} catch (EmptyResult) {}
return $this->withJson($response, $output);
}
public function addComentario(ServerRequestInterface $request, ResponseInterface $response,
Repository\Venta $ventaRepository, Repository\Venta\Comentario $comentarioRepository,
Service\Redis $redisService,
int $venta_id): ResponseInterface
{
$body = $request->getParsedBody();
$output = [
'venta_id' => $venta_id,
'input' => $body,
'comentario' => null,
'added' => false
];
try {
$venta = $ventaRepository->fetchById($venta_id);
$body['venta'] = $venta->id;
$body['estado'] = true;
$filteredData = $comentarioRepository->filterData($body);
try {
$comentarioRepository->fetchByVentaAndFechaAndTexto($venta_id, new DateTimeImmutable($filteredData['fecha']), $filteredData['texto']);
} catch (EmptyResult) {
$redisKey = "comentarios:venta:{$venta_id}";
$comentarios = $this->fetchRedis($redisService, $redisKey);
$comentario = $comentarioRepository->create($filteredData);
$output['comentario'] = $comentarioRepository->save($comentario);
$comentarios []= $comentario;
$this->saveRedis($redisService, $redisKey, $comentarios);
}
$output['added'] = true;
} catch (EmptyResult) {}
return $this->withJson($response, $output);
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace Incoviba\Controller\API\Ventas;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Incoviba\Common\Implement\Exception\EmptyResult;
use Incoviba\Controller\API\withJson;
use Incoviba\Controller\withRedis;
use Incoviba\Repository;
use Incoviba\Service;
class Comentarios
{
use withJson, withRedis;
public function edit(ServerRequestInterface $request, ResponseInterface $response,
Repository\Venta\Comentario $comentarioRepository,
Service\Redis $redisService, int $comentario_id): ResponseInterface
{
$output = [
'comentario_id' => $comentario_id,
'comentario' => null,
'edited' => false
];
$data = $request->getParsedBody();
try {
$comentario = $comentarioRepository->fetchById($comentario_id);
$filteredData = $comentarioRepository->filterData($data);
$comentario = $comentarioRepository->edit($comentario, $filteredData);
$redisKey = "comentarios:venta:{$comentario->venta->id}";
$comentarios = $this->fetchRedis($redisService, $redisKey);
$index = array_column(json_decode(json_encode($comentarios), true), 'id');
$idx = array_search($comentario->id, $index);
$comentarios[$idx] = $comentario;
$this->saveRedis($redisService, $redisKey, $comentarios);
$output['comentario'] = $comentario;
$output['edited'] = true;
} catch (EmptyResult) {}
return $this->withJson($response, $output);
}
public function remove(ServerRequestInterface $request, ResponseInterface $response,
Repository\Venta\Comentario $comentarioRepository, Service\Redis $redisService,
int $comentario_id): ResponseInterface
{
$output = [
'comentario_id' => $comentario_id,
'comentario' => null,
'removed' => false
];
try {
$comentario = $comentarioRepository->fetchById($comentario_id);
$output['comentario'] = $comentario;
$comentarioRepository->remove($comentario);
$redisKey = "comentarios:venta:{$comentario->venta->id}";
$comentarios = $this->fetchRedis($redisService, $redisKey);
$index = array_column(json_decode(json_encode($comentarios), true), 'id');
$idx = array_search($comentario->id, $index);
unset($comentarios[$idx]);
$this->saveRedis($redisService, $redisKey, $comentarios);
$output['removed'] = true;
} catch (EmptyResult) {}
return $this->withJson($response, $output);
}
}

View File

@ -3,9 +3,11 @@ namespace Incoviba\Model\Venta;
use DateTimeInterface;
use Incoviba\Common\Ideal;
use Incoviba\Model\Venta;
class Comentario extends Ideal\Model
{
public Venta $venta;
public DateTimeInterface $fecha;
public string $texto;
public bool $activo;
@ -13,6 +15,7 @@ class Comentario extends Ideal\Model
public function jsonSerialize(): mixed
{
return array_merge(parent::jsonSerialize(), [
'venta_id' => $this->venta->id,
'fecha' => $this->fecha->format('Y-m-d'),
'texto' => $this->texto,
'activo' => $this->activo

View File

@ -6,10 +6,11 @@ use Incoviba\Common\Ideal;
use Incoviba\Common\Define;
use Incoviba\Common\Implement;
use Incoviba\Model;
use Incoviba\Repository;
class Comentario extends Ideal\Repository
{
public function __construct(Define\Connection $connection)
public function __construct(Define\Connection $connection, protected Repository\Venta $ventaRepsitory)
{
parent::__construct($connection);
$this->setTable('comentario');
@ -18,6 +19,10 @@ class Comentario extends Ideal\Repository
public function create(?array $data = null): Define\Model
{
$map = (new Implement\Repository\MapperParser(['texto']))
->register('venta', (new Implement\Repository\Mapper())
->setFunction(function($data) {
return $this->ventaRepsitory->fetchById($data['venta']);
}))
->register('fecha', new Implement\Repository\Mapper\DateTime('fecha'))
->register('estado', new Implement\Repository\Mapper\Boolean('estado', 'activo'));
return $this->parseData(new Model\Venta\Comentario(), $data, $map);
@ -25,19 +30,40 @@ class Comentario extends Ideal\Repository
public function save(Define\Model $model): Define\Model
{
$model->id = $this->saveNew(
['fecha', 'texto', 'estado'],
[$model->fecha->format('Y-m-d'), $model->texto, $model->activo ? 1 : 0]
['venta', 'fecha', 'texto', 'estado'],
[$model->venta->id, $model->fecha->format('Y-m-d'), $model->texto, $model->activo ? 1 : 0]
);
return $model;
}
public function edit(Define\Model $model, array $new_data): Define\Model
{
return $this->update($model, ['fecha', 'texto', 'estado'], $new_data);
return $this->update($model, ['venta', 'fecha', 'texto', 'estado'], $new_data);
}
public function fetchByVenta(int $venta_id): array
public function fetchActiveByVenta(int $venta_id): array
{
$query = "SELECT * FROM `{$this->getTable()}` WHERE `venta` = ? AND `estado` = 1 ORDER BY `fecha` DESC";
$query = $this->connection->getQueryBuilder()
->select()
->from($this->getTable())
->where('venta = ? AND estado = 1')
->order('fecha DESC');
return $this->fetchMany($query, [$venta_id]);
}
public function fetchByVenta(int $venta_id): array
{
$query = $this->connection->getQueryBuilder()
->select()
->from($this->getTable())
->where('venta = ?')
->order('fecha DESC');
return $this->fetchMany($query, [$venta_id]);
}
public function fetchByVentaAndFechaAndTexto(int $venta_id, DateTimeImmutable $fecha, string $texto): Model\Venta\Comentario
{
$query = $this->connection->getQueryBuilder()
->select()
->from($this->getTable())
->where('venta = ? AND fecha = ? AND texto = ?');
return $this->fetchOne($query, [$venta_id, $fecha->format('Y-m-d'), $texto]);
}
}