Listado de cierres

This commit is contained in:
Juan Pablo Vial
2025-08-05 18:18:05 -04:00
parent 28b272bc55
commit 62aa6a08a0
3 changed files with 316 additions and 71 deletions

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
use Phinx\Migration\AbstractMigration; use Phinx\Migration\AbstractMigration;
final class CreateReservation extends AbstractMigration final class CreateReservations extends AbstractMigration
{ {
/** /**
* Change Method. * Change Method.

View File

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
use Phinx\Migration\AbstractMigration;
final class AddCommentsToEstadoCierre extends AbstractMigration
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* https://book.cakephp.org/phinx/0/en/migrations.html#the-change-method
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change(): void
{
$this->table('estado_cierre_comentarios')
->addColumn('estado_cierre_id', 'integer', ['signed' => false])
->addColumn('fecha', 'datetime', ['default' => 'CURRENT_TIMESTAMP'])
->addColumn('comments', 'text')
->addForeignKey('estado_cierre_id', 'estado_cierre', 'id', ['delete' => 'cascade', 'update' => 'cascade'])
->create();
}
}

View File

@ -24,7 +24,7 @@
</div> </div>
@endif @endif
</div> </div>
<h4 class="ui header" id="project"></h4> <h3 class="ui header" id="project"></h3>
<div class="ui right aligned top attached basic segment" id="controls"> <div class="ui right aligned top attached basic segment" id="controls">
<div class="ui tiny icon buttons"> <div class="ui tiny icon buttons">
<button class="ui button" id="up_button"> <button class="ui button" id="up_button">
@ -33,10 +33,19 @@
<button class="ui button" id="refresh_button"> <button class="ui button" id="refresh_button">
<i class="refresh icon"></i> <i class="refresh icon"></i>
</button> </button>
<button class="ui green icon button" id="add_button">
<i class="plus icon"></i>
</button>
</div> </div>
</div> </div>
<table class="ui striped table" id="reservations"> <div class="ui top attached tabular menu" id="tabs">
<thead> <div class="yellow active item" data-tab="pending">Pendientes</div>
<div class="green item" data-tab="active">Activas</div>
<div class="red item" data-tab="rejected">Rechazadas</div>
</div>
<div class="ui bottom attached tab fitted segment active" data-tab="pending">
<table class="ui yellow striped table" id="pending_reservations">
<thead>
<tr> <tr>
<th>Unidades</th> <th>Unidades</th>
<th>Cliente</th> <th>Cliente</th>
@ -44,15 +53,43 @@
<th>Oferta</th> <th>Oferta</th>
<th>¿Valida?</th> <th>¿Valida?</th>
<th>Operador</th> <th>Operador</th>
<th class="right aligned"> <th></th>
<button class="ui small tertiary green icon button" id="add_button">
<i class="plus icon"></i>
</button>
</th>
</tr> </tr>
</thead> </thead>
<tbody></tbody> <tbody></tbody>
</table> </table>
</div>
<div class="ui bottom attached tab fitted segment" data-tab="active">
<table class="ui green striped table" id="active_reservations">
<thead>
<tr>
<th>Unidades</th>
<th>Cliente</th>
<th>Fecha</th>
<th>Oferta</th>
<th>Operador</th>
<th></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="ui bottom attached tab fitted segment" data-tab="rejected">
<table class="ui red striped table" id="rejected_reservations">
<thead>
<tr>
<th>Unidades</th>
<th>Cliente</th>
<th>Fecha</th>
<th>Oferta</th>
<th>Estado</th>
<th>Operador</th>
<th>Comentarios</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div> </div>
@endsection @endsection
@ -94,7 +131,7 @@
event.preventDefault() event.preventDefault()
const project_id = event.currentTarget.dataset.id const project_id = event.currentTarget.dataset.id
if (project_id === this.curent_project) { if (project_id === this.current_project) {
this.hide() this.hide()
this.title_component.innerHTML = event.currentTarget.innerHTML this.title_component.innerHTML = event.currentTarget.innerHTML
@ -102,7 +139,7 @@
return return
} }
this.curent_project = project_id this.current_project = project_id
reservations.get().reservations(project_id) reservations.get().reservations(project_id)
this.hide() this.hide()
@ -122,41 +159,73 @@
this.title_component.style.display = this.display.project this.title_component.style.display = this.display.project
} }
} }
class Reservations { class Controls {
display = { display = {
reservations: '', up: '',
controls: '' reset: '',
} }
component_id = '' component_id = ''
component = null component = null
controls_id = ''
controls = null buttons = {
formatters = { up: null,
date: null, reset: null,
ufs: null add: null
} }
reservations = [] constructor({component_id}) {
constructor({component_id, controls_id, formatters = {date, ufs}}) {
this.component_id = component_id this.component_id = component_id
this.component = document.getElementById(this.component_id) this.component = document.getElementById(this.component_id)
this.display.reservations = this.component.style.display const buttons = this.component.querySelectorAll('button')
this.controls_id = controls_id this.buttons.up = buttons[0]
this.controls = document.getElementById(this.controls_id) this.buttons.reset = buttons[1]
this.display.controls = this.controls.style.display this.buttons.add = buttons[2]
this.formatters = formatters this.display.up = buttons[0].style.display
this.display.reset = buttons[1].style.display
this.watch() this.watch()
this.hide() this.hide()
} }
watch() { watch() {
const buttons = this.controls.querySelectorAll('button') Object.entries(this.buttons).forEach(([key, value]) => {
const up_button = buttons[0] const name = key.replace('_button', '')
const reset_button = buttons[1] value.addEventListener('click', reservations.action()[name].bind(reservations))
up_button.addEventListener('click', reservations.action().up.bind(reservations)) })
reset_button.addEventListener('click', reservations.action().reset.bind(reservations)) }
hide() {
this.buttons.up.style.display = this.display.up
this.buttons.reset.style.display = this.display.reset
}
show() {
this.buttons.up.style.display = 'none'
this.buttons.reset.style.display = 'none'
}
}
class Reservations {
display = {
reservations: '',
}
component_id = ''
component = null
formatters = {
date: null,
ufs: null
}
columnNames = []
reservations = []
constructor({component_id, formatters = {date, ufs}}) {
this.component_id = component_id
this.component = document.getElementById(this.component_id)
this.display.reservations = this.component.style.display
this.formatters = formatters
this.set().columnNames()
this.hide()
} }
set() { set() {
@ -164,53 +233,135 @@
reservations: reservations => { reservations: reservations => {
this.reservations = reservations this.reservations = reservations
return this return this
},
columnNames: () => {
const tds = this.component.querySelector('thead tr').querySelectorAll('th')
this.columnNames = []
tds.forEach(td => {
let name = td.innerHTML.toLowerCase()
if (name === '') {
return
}
if (name.includes('?')) {
name = name.replaceAll(/[¿?]/g, '')
}
this.columnNames.push(name)
})
return this
} }
} }
} }
columnsData() {
return this.reservations.map(reservation => {
const date = new Date(Date.parse(reservation.fecha) + 24 * 60 * 60 * 1000)
return {
id: reservation.id,
unidades: reservation.summary,
cliente: reservation.buyer.nombreCompleto,
fecha: this.formatters.date.format(date),
oferta: `${this.formatters.ufs.format(reservation.offer)} UF`,
valida: reservation.valid ? '<span class="ui green text">Si</span>' : '<span class="ui red text">No</span>',
operador: reservation.broker?.name ?? '',
}
})
}
draw() { draw() {
const tbody = this.component.querySelector('tbody') const tbody = this.component.querySelector('tbody')
tbody.innerHTML = '' tbody.innerHTML = ''
this.reservations.forEach(reservation => { this.columnsData().forEach(column => {
const tr = document.createElement('tr') const tr = document.createElement('tr')
const date = new Date(Date.parse(reservation.fecha) + 24 * 60 * 60 * 1000) const contents = []
tr.innerHTML = ` const id = column.id
<td>${reservation.summary}</td> this.columnNames.forEach(name => {
<td>${reservation.buyer.nombreCompleto}</td> contents.push(`<td>${column[name]}</td>`)
<td>${this.formatters.date.format(date)}</td> })
<td>${this.formatters.ufs.format(reservation.offer)} UF</td> const actions = this.drawActions(id)
<td>${reservation.valid ? 'Si' : 'No'}</td> if (actions !== '') {
<td>${reservation.broker?.name ?? ''}</td> contents.push(actions)
<td class="right aligned"> }
<button class="ui small icon button" data-id="${reservation.id}"> tr.innerHTML = contents.join("\n")
<i class="edit icon"></i>
</button>
<button class="ui small icon button" data-id="${reservation.id}">
<i class="trash icon"></i>
</button>
</td>
`
tbody.appendChild(tr) tbody.appendChild(tr)
}) })
this.show() this.show()
} }
drawActions(id) {
return `
<td class="right aligned">
<button class="ui green mini icon button approve" data-id="${id}" title="Aprobar">
<i class="check icon"></i>
</button>
<button class="ui red mini icon button reject" data-id="${id}" title="Rechazar">
<i class="trash icon"></i>
</button>
</td>`
}
show() { show() {
this.component.style.display = this.display.reservations this.component.style.display = this.display.reservations
this.controls.style.display = this.display.controls
} }
hide() { hide() {
this.component.style.display = 'none' this.component.style.display = 'none'
this.controls.style.display = 'none' }
}
class ActiveReservations extends Reservations {
constructor({component_id, formatters = {date, ufs}}) {
super({component_id, formatters})
}
columnsData() {
const data = super.columnsData();
return data.map(row => {
delete(row['valida'])
return row
})
}
drawActions(id) {
return `
<td class="right aligned">
<button class="ui green mini icon button edit" data-id="${id}" title="Promesar">
<i class="right chevron icon"></i>
</button>
<button class="ui red mini icon button remove" data-id="${id}" title="Abandonar">
<i class="trash icon"></i>
</button>
</td>`
}
}
class PendingReservations extends Reservations {
constructor({component_id, formatters = {date, ufs}}) {
super({component_id, formatters})
}
}
class RejectedReservations extends Reservations {
constructor({component_id, formatters = {date, ufs}}) {
super({component_id, formatters})
}
columnsData() {
const data = super.columnsData()
return this.reservations.map((reservation, idx) => {
data[idx]['estado'] = reservation.state.charAt(0).toUpperCase() + reservation.state.slice(1)
data[idx]['comentarios'] = reservation.comments?.join('<br />\n') ?? ''
return data[idx]
})
}
drawActions(id) {
return ''
} }
} }
const reservations = { const reservations = {
components: { components: {
projects: null, projects: null,
reservations: null controls: null,
reservations: {
active: null,
pending: null,
rejected: null
}
}, },
get() { get() {
return { return {
reservations: project_id => { active: project_id => {
const reservations = [ const reservations = [
{ {
id: 1, id: 1,
@ -220,26 +371,74 @@
nombreCompleto: 'Juan Perez' nombreCompleto: 'Juan Perez'
}, },
fecha: '2021-01-01', fecha: '2021-01-01',
offer: 1000, offer: 3000,
valid: false, valid: true,
broker: { broker: {
name: 'Operador 1' name: 'Operador 1'
} }
}, }
]
this.components.reservations.active.set().reservations(reservations).draw()
},
pending: project_id => {
const reservations = [
{ {
id: 2, id: 2,
summary: 'D2', summary: 'D2',
buyer: { buyer: {
rut: '12345678-9', rut: '12345678-9',
nombreCompleto: 'Pedro Gonzalez' nombreCompleto: 'Pedro Jimenez'
}, },
fecha: '2021-10-01', fecha: '2021-10-01',
offer: 1200, offer: 1000,
valid: true, valid: false,
broker: null broker: {
name: 'Operador 2'
}
} }
] ]
this.components.reservations.set().reservations(reservations).draw() this.components.reservations.pending.set().reservations(reservations).draw()
},
rejected: project_id => {
const reservations = [
{
id: 3,
summary: 'D3',
buyer: {
rut: '12345678-9',
nombreCompleto: 'Fernando Dominguez'
},
fecha: '2021-01-10',
offer: 1000,
valid: false,
state: 'rechazado',
broker: {
name: 'Operador 3'
},
comments: ['Comentarios']
},
{
id: 4,
summary: 'D4',
buyer: {
rut: '12345678-9',
nombreCompleto: 'Domingo Gomez'
},
fecha: '2021-02-01',
offer: 1000,
valid: false,
state: 'abandonado',
broker: {
name: 'Operador 4'
}
}
]
this.components.reservations.rejected.set().reservations(reservations).draw()
},
reservations: project_id => {
this.get().active(project_id)
this.get().pending(project_id)
this.get().rejected(project_id)
} }
} }
}, },
@ -248,19 +447,28 @@
reset: event => { reset: event => {
event.preventDefault() event.preventDefault()
this.components.projects.current_project = null this.components.projects.current_project = null
this.components.reservations.reservations = [] Object.entries(this.components.reservations).forEach(([key, value]) => {
this.components.reservations.hide() this.components.reservations[key].reservations = []
this.components.reservations[key].hide()
})
this.components.projects.show() this.components.projects.show()
return false return false
}, },
up: event => { up: event => {
event.preventDefault() event.preventDefault()
this.components.reservations.hide() Object.values(this.components.reservations).forEach(reservations => reservations.hide())
this.components.projects.show() this.components.projects.show()
return false return false
}, },
add: event => {
event.preventDefault()
console.debug('Add')
return false
},
reservations: () => { reservations: () => {
this.components.reservations.draw() Object.values(this.components.reservations).forEach(reservations => {
reservations.draw()
})
} }
} }
}, },
@ -270,7 +478,12 @@
ufs: new Intl.NumberFormat('es-CL', {minimumFractionDigits: 2, maximumFractionDigits: 2}) ufs: new Intl.NumberFormat('es-CL', {minimumFractionDigits: 2, maximumFractionDigits: 2})
} }
this.components.projects = new Projects({component_id: configuration.ids.projects, title_id: configuration.ids.project}) this.components.projects = new Projects({component_id: configuration.ids.projects, title_id: configuration.ids.project})
this.components.reservations = new Reservations({component_id: configuration.ids.reservations, controls_id: configuration.ids.controls, formatters}) this.components.controls = new Controls({component_id: configuration.ids.controls})
this.components.reservations.active = new ActiveReservations({component_id: configuration.ids.active, formatters})
this.components.reservations.pending = new PendingReservations({component_id: configuration.ids.pending, formatters})
this.components.reservations.rejected = new RejectedReservations({component_id: configuration.ids.rejected, formatters})
$(`#${configuration.ids.tabs} .item`).tab()
} }
} }
$(document).ready(() => { $(document).ready(() => {
@ -279,7 +492,10 @@
projects: 'projects', projects: 'projects',
project: 'project', project: 'project',
controls: 'controls', controls: 'controls',
reservations: 'reservations' tabs: 'tabs',
active: 'active_reservations',
pending: 'pending_reservations',
rejected: 'rejected_reservations',
} }
}) })
}) })