Files
oficial/app/resources/views/ventas/reservations.blade.php

555 lines
21 KiB
PHP
Raw Normal View History

2025-07-22 18:21:05 -04:00
@extends('layout.base')
@section('page_title')
Cierres -Reservas
@endsection
@section('page_content')
<div class="ui container">
<h2 class="ui header">Cierres - Reservas</h2>
<div class="ui compact segment" id="projects">
<div class="ui header">Proyectos</div>
@if (count($projects) == 0)
<div class="ui message">
No hay proyectos en venta.
</div>
@else
<div class="ui link list">
@foreach ($projects as $project)
<div class="item link" data-id="{{ $project->id }}">
{{ $project->descripcion }}
</div>
@endforeach
</div>
@endif
</div>
2025-08-12 19:17:32 -04:00
<div class="ui two column grid">
<div class="column">
<h3 class="ui header" id="project"></h3>
</div>
<div class="column">
<div class="ui active inline loader" id="loader"></div>
</div>
</div>
2025-08-08 17:06:16 -04:00
<div id="results">
<div class="ui right aligned top attached basic segment" id="controls">
<div class="ui tiny icon buttons">
<button class="ui button" id="up_button">
<i class="up arrow icon"></i>
</button>
<button class="ui button" id="refresh_button">
<i class="refresh icon"></i>
</button>
<button class="ui green icon button" id="add_button">
<i class="plus icon"></i>
</button>
</div>
</div>
<div class="ui top attached tabular menu" id="tabs">
<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>
<th>Unidades</th>
<th>Cliente</th>
<th>Fecha</th>
<th>Oferta</th>
<th>¿Valida?</th>
<th>Operador</th>
<th></th>
</tr>
</thead>
<tbody></tbody>
</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>
2025-07-22 18:21:05 -04:00
</div>
2025-08-05 18:18:05 -04:00
</div>
2025-07-22 18:21:05 -04:00
</div>
2025-08-12 19:17:32 -04:00
@include('ventas.reservations.add_modal')
2025-07-22 18:21:05 -04:00
@endsection
@push('page_styles')
<style>
.item.link {
cursor: pointer;
text-decoration: underline;
}
</style>
@endpush
@push('page_scripts')
<script>
class Projects {
display = {
projects: '',
project: ''
}
component_id = ''
component = null
current_project = null;
title_id = ''
title_component = null
constructor({component_id, title_id}) {
this.component_id = component_id
this.component = document.getElementById(this.component_id)
this.display.projects = this.component.style.display
this.title_id = title_id
this.title_component = document.getElementById(this.title_id)
this.display.project = this.title_component.style.display
this.show()
this.watch()
}
select(event) {
event.preventDefault()
const project_id = event.currentTarget.dataset.id
2025-08-12 19:17:32 -04:00
reservations.show.results()
2025-08-08 17:06:16 -04:00
2025-08-05 18:18:05 -04:00
if (project_id === this.current_project) {
2025-07-22 18:21:05 -04:00
this.hide()
this.title_component.innerHTML = event.currentTarget.innerHTML
2025-08-12 19:17:32 -04:00
reservations.action.reservations()
2025-07-22 18:21:05 -04:00
return
}
2025-08-05 18:18:05 -04:00
this.current_project = project_id
2025-07-22 18:21:05 -04:00
2025-08-12 19:17:32 -04:00
reservations.get.reservations(project_id)
reservations.components.modals.add.load(project_id)
2025-07-22 18:21:05 -04:00
this.hide()
2025-08-08 17:06:16 -04:00
2025-07-22 18:21:05 -04:00
this.title_component.innerHTML = event.currentTarget.innerHTML
}
watch() {
this.component.querySelectorAll('.item.link').forEach(item => {
item.addEventListener('click', this.select.bind(this))
})
}
show() {
this.component.style.display = this.display.projects
this.title_component.style.display = 'none'
}
hide() {
this.component.style.display = 'none'
this.title_component.style.display = this.display.project
}
}
2025-08-05 18:18:05 -04:00
class Controls {
display = {
up: '',
reset: '',
}
component_id = ''
component = null
buttons = {
up: null,
reset: null,
add: null
}
constructor({component_id}) {
this.component_id = component_id
this.component = document.getElementById(this.component_id)
const buttons = this.component.querySelectorAll('button')
this.buttons.up = buttons[0]
this.buttons.reset = buttons[1]
this.buttons.add = buttons[2]
this.display.up = buttons[0].style.display
this.display.reset = buttons[1].style.display
this.watch()
this.hide()
}
watch() {
Object.entries(this.buttons).forEach(([key, value]) => {
const name = key.replace('_button', '')
2025-08-12 19:17:32 -04:00
value.addEventListener('click', this.action[name].bind(this))
2025-08-05 18:18:05 -04:00
})
}
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'
}
2025-08-12 19:17:32 -04:00
action = {
reset: event => {
event.preventDefault()
reservations.action.reset(event)
return false
},
up: event => {
event.preventDefault()
reservations.action.up(event)
return false
},
add: event => {
event.preventDefault()
reservations.action.add(event)
return false
}
}
2025-08-05 18:18:05 -04:00
}
2025-07-22 18:21:05 -04:00
class Reservations {
display = {
reservations: '',
}
component_id = ''
component = null
formatters = {
date: null,
ufs: null
}
2025-08-05 18:18:05 -04:00
columnNames = []
2025-07-22 18:21:05 -04:00
reservations = []
2025-08-05 18:18:05 -04:00
constructor({component_id, formatters = {date, ufs}}) {
2025-07-22 18:21:05 -04:00
this.component_id = component_id
this.component = document.getElementById(this.component_id)
this.display.reservations = this.component.style.display
2025-08-05 18:18:05 -04:00
2025-07-22 18:21:05 -04:00
this.formatters = formatters
2025-08-05 18:18:05 -04:00
this.set().columnNames()
2025-07-22 18:21:05 -04:00
2025-08-05 18:18:05 -04:00
this.hide()
2025-07-22 18:21:05 -04:00
}
set() {
return {
reservations: reservations => {
this.reservations = reservations
return this
2025-08-05 18:18:05 -04:00
},
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
2025-07-22 18:21:05 -04:00
}
}
}
2025-08-05 18:18:05 -04:00
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 ?? '',
}
})
}
2025-07-22 18:21:05 -04:00
draw() {
const tbody = this.component.querySelector('tbody')
tbody.innerHTML = ''
2025-08-05 18:18:05 -04:00
this.columnsData().forEach(column => {
2025-07-22 18:21:05 -04:00
const tr = document.createElement('tr')
2025-08-05 18:18:05 -04:00
const contents = []
const id = column.id
this.columnNames.forEach(name => {
contents.push(`<td>${column[name]}</td>`)
})
const actions = this.drawActions(id)
if (actions !== '') {
contents.push(actions)
}
tr.innerHTML = contents.join("\n")
2025-07-22 18:21:05 -04:00
tbody.appendChild(tr)
})
this.show()
}
2025-08-05 18:18:05 -04:00
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>`
}
2025-08-08 17:06:16 -04:00
empty() {
const tbody = this.component.querySelector('tbody')
tbody.innerHTML = ''
const col_span = this.columnNames.length + 1
const tr = document.createElement('tr')
tr.innerHTML = `<td colspan="${col_span}">No hay cierres</td>`
tbody.appendChild(tr)
this.show()
}
2025-07-22 18:21:05 -04:00
show() {
this.component.style.display = this.display.reservations
}
hide() {
this.component.style.display = 'none'
2025-08-05 18:18:05 -04:00
}
}
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 ''
2025-07-22 18:21:05 -04:00
}
}
const reservations = {
components: {
projects: null,
2025-08-08 17:06:16 -04:00
loader: null,
results: null,
2025-08-05 18:18:05 -04:00
controls: null,
reservations: {
active: null,
pending: null,
rejected: null
2025-08-12 19:17:32 -04:00
},
modals: {
add: null
2025-08-05 18:18:05 -04:00
}
2025-07-22 18:21:05 -04:00
},
2025-08-08 17:06:16 -04:00
display: {
loader: '',
results: '',
},
2025-08-12 19:17:32 -04:00
get: {
send: (project_id, url_segment, component) => {
const url = `/api/ventas/reservations/project/${project_id}/${url_segment}`
return APIClient.fetch(url).then(response => response.json()).then(json => {
if (json.reservations.length === 0) {
component.empty()
return
}
component.set().reservations(json.reservations).draw()
})
},
active: project_id => {
return reservations.get.send(project_id, 'active', reservations.components.reservations.active)
/*const url = `/ventas/reservations/project/${project_id}/active`
return APIClient.fetch(url).then(json => {
if (json.reservations.length === 0) {
return
}
this.components.reservations.active.set().reservations(json.reservations).draw()
})*/
},
pending: project_id => {
return reservations.get.send(project_id, 'pending', reservations.components.reservations.pending)
/*const url = `/ventas/reservations/project/${project_id}/pending`
return APIClient.fetch(url).then(json => {
if (json.reservations.length === 0) {
return
}
this.components.reservations.pending.set().reservations(json.reservations).draw()
})*/
},
rejected: project_id => {
return reservations.get.send(project_id, 'rejected', reservations.components.reservations.rejected)
/*const url = `/ventas/reservations/project/${project_id}/rejected`
return APIClient.fetch(url).then(json => {
if (json.reservations.length === 0) {
return
}
this.components.reservations.rejected.set().reservations(json.reservations).draw()
})*/
},
reservations: project_id => {
reservations.loading.show()
const promises = []
promises.push(reservations.get.active(project_id))
promises.push(reservations.get.pending(project_id))
promises.push(reservations.get.rejected(project_id))
return Promise.any(promises).then(() => {
reservations.loading.hide()
})
2025-08-08 17:06:16 -04:00
}
},
2025-08-12 19:17:32 -04:00
loading: {
show: () => {
reservations.components.loader.style.display = reservations.display.loader
},
hide: () => {
reservations.components.loader.style.display = 'none'
2025-07-22 18:21:05 -04:00
}
},
2025-08-12 19:17:32 -04:00
action: {
reset: event => {
event.preventDefault()
reservations.components.projects.current_project = null
Object.entries(reservations.components.reservations).forEach(([key, value]) => {
reservations.components.reservations[key].reservations = []
reservations.components.reservations[key].hide()
})
reservations.show.projects()
return false
},
up: event => {
event.preventDefault()
Object.values(reservations.components.reservations).forEach(reservations => reservations.hide())
reservations.show.projects()
return false
},
add: event => {
event.preventDefault()
reservations.components.modals.add.show()
return false
},
reservations: () => {
Object.values(reservations.components.reservations).forEach(reservations => {
reservations.draw()
})
2025-07-22 18:21:05 -04:00
}
},
2025-08-12 19:17:32 -04:00
show: {
projects: () => {
reservations.components.projects.show()
reservations.components.results.style.display = 'none'
},
results: () => {
reservations.components.projects.hide()
reservations.components.results.style.display = reservations.display.results
2025-08-08 17:06:16 -04:00
}
},
2025-07-22 18:21:05 -04:00
setup(configuration) {
const formatters = {
date: new Intl.DateTimeFormat('es-CL', {year: 'numeric', month: 'long', day: 'numeric'}),
ufs: new Intl.NumberFormat('es-CL', {minimumFractionDigits: 2, maximumFractionDigits: 2})
}
2025-08-08 17:06:16 -04:00
this.components.loader = document.getElementById(configuration.ids.loader)
2025-07-22 18:21:05 -04:00
this.components.projects = new Projects({component_id: configuration.ids.projects, title_id: configuration.ids.project})
2025-08-05 18:18:05 -04:00
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})
2025-08-08 17:06:16 -04:00
this.display.loader = this.components.loader.style.display
2025-08-12 19:17:32 -04:00
this.loading.hide()
2025-08-08 17:06:16 -04:00
2025-08-05 18:18:05 -04:00
$(`#${configuration.ids.tabs} .item`).tab()
2025-08-08 17:06:16 -04:00
this.components.results = document.getElementById(configuration.ids.results)
this.display.results = this.components.results.style.display
2025-08-12 19:17:32 -04:00
this.show.projects()
this.components.modals.add = new AddReservationModal()
2025-07-22 18:21:05 -04:00
}
}
$(document).ready(() => {
reservations.setup({
ids: {
projects: 'projects',
project: 'project',
2025-08-08 17:06:16 -04:00
results: 'results',
loader: 'loader',
2025-07-22 18:21:05 -04:00
controls: 'controls',
2025-08-05 18:18:05 -04:00
tabs: 'tabs',
active: 'active_reservations',
pending: 'pending_reservations',
rejected: 'rejected_reservations',
2025-07-22 18:21:05 -04:00
}
})
})
</script>
@endpush