{% extends "@UVDeskSupportCenter/Templates/layout.html.twig" %}
{# WebkulUVDeskSupportCenterBundle/Resources/views/Front/ticketList.html.twig #}
{% block title %}{% trans %}Tickets{% endtrans %}{% endblock %}
{% block ogtitle %}{% trans %}Tickets{% endtrans %}{% endblock %}
{% block twtitle %}{% trans %}Tickets{% endtrans %}{% endblock %}
{% block metaDescription %}{% trans %}ticketlist.metaDescription{% endtrans %}{% endblock %}
{% block metaKeywords %}{% trans %}ticketlist.metaKeywords{% endtrans %}{% endblock %}
{% block tabHeader %}
<div class="uv-nav-bar uv-nav-tab">
<div class="uv-container">
<div class="uv-nav-bar-lt">
<ul class="uv-nav-tab-label">
<li><a class="uv-nav-tab-active" href="{{ path('helpdesk_customer_ticket_collection') }}">{{ 'Ticket Requests'|trans }}</a></li>
{% if websiteConfiguration.ticketCreateOption is defined and websiteConfiguration.ticketCreateOption %}
<li><a href="{{ path('helpdesk_customer_create_ticket') }}">{{ 'New Ticket Request'|trans }}</a></li>
<li> <a href="{{path('helpdesk_knowledgebase_folder')}}">{{"Home"|trans}}</a></li>
{% endif %}
</ul>
</div>
<div class="uv-nav-bar-rt">
<form method="get" action="{{path('helpdesk_customer_front_article_search')}}">
<input name="s" class="uv-nav-search" type="text" placeholder="{{ 'Search'|trans }}">
</form>
</div>
</div>
</div>
{% endblock %}
{% block body %}
<style>
.uv-container{
width: 1500px !important
}
/* Estilo para opciones desactivadas */
.uv-report-brick.disabled {
opacity: 0.2; /* Reduce la opacidad para mostrar que está desactivado */
pointer-events: none; /* Desactiva los clics en las opciones */
}
#comment {
width: 100%; /* Establece el ancho del textarea al 100% del contenedor padre */
height: 100px; /* Establece la altura del textarea en píxeles o cualquier otra unidad deseada */
padding: 10px; /* Agrega relleno (padding) para espacio adicional dentro del textarea */
border: 1px solid #ccc; /* Agrega un borde alrededor del textarea */
border-radius: 5px; /* Agrega bordes redondeados al textarea */
resize: none; /* Evita que el usuario pueda cambiar el tamaño del textarea */
font-size: 16px; /* Establece el tamaño de fuente del texto en el textarea */
line-height: 1.5; /* Establece la altura de línea del texto para mejorar la legibilidad */
/* Puedes agregar más estilos aquí, como colores de fondo, colores de texto, etc. */
}
.uv-icon-kudos {
background-image: url("{{ asset('bundles/uvdeskcoreframework/images/uvdesk-kudo-sprite.svg') }}");
display: inline-block;
width: 40px;
height: 40px;
}
.uv-icon-kudos.kudos-1 {
background-position: 0px 40px;
}
.uv-icon-kudos.kudos-2 {
background-position: -40px 40px;
}
.uv-icon-kudos.kudos-3 {
background-position: -80px 40px;
}
.uv-icon-kudos.kudos-4 {
background-position: -120px 40px;
}
.uv-icon-kudos.kudos-5 {
background-position: -160px 40px;
}
.kudos-text-1 {
color: #FC6E46;
}
.kudos-text-2 {
color: #FC6E46;
}
.kudos-text-3 {
color: #FCDA32;
}
.kudos-text-4 {
color: #01D101;
}
.kudos-text-5 {
color: #01D101;
}
.uv-table td a {
color: #333;
}
.tooltip {
position: relative;
}
.tooltip .tooltip-text {
visibility: hidden;
background-color: #fff;
color: #000;
text-align: center;
border: 1px solid #000;
border-radius: 4px;
padding: 5px;
position: absolute;
z-index: 1;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
opacity: 0;
transition: opacity 0.3s;
font-size: 14px;
max-width: 100%; /* Limita el ancho máximo del tooltip al 100% */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis; /* Agrega puntos suspensivos si el contenido desborda */
}
.tooltip:before {
content: "";
top: 100%;
left: 50%;
transform: translateX(-50%);
border-width: 6px;
border-color: transparent transparent #fff transparent;
}
.tooltip:hover .tooltip-text {
visibility: visible;
opacity: 1;
}
</style>
<div class="uv-nav-tab-view">
<h1>{{ 'Ticket Requests'|trans }}</h1>
<div class="uv-action-view">
<div class="uv-action-view-lt">
<!--Sort by-->
<div class="uv-dropdown sort">
<div class="uv-dropdown-btn uv-vertical-align uv-margin-right-5">
{{ 'Sort By:'|trans }} {{ 'Ticket Id'|trans }}
</div>
<div class="uv-dropdown-list uv-bottom-left">
<div class="uv-dropdown-container">
<label>{{ 'Sort By'|trans }}</label>
<ul>
</ul>
</div>
</div>
</div>
<!--Sort by-->
<!--Filter By Status-->
<div class="uv-dropdown filter-by-status">
<div class="uv-dropdown-btn">
{{ 'Status:'|trans }} {{ 'All'|trans }}
</div>
<div class="uv-dropdown-list uv-bottom-left">
<div class="uv-dropdown-container">
<label>{{ 'Status'|trans }}</label>
<ul>
<li class="uv-drop-list-active"><a href="#" data-id="">{{ 'All'|trans }}</a></li>
{% for status in ticket_service.getStatus() %}
<li><a href="#" data-id="{{status.id}}">{{ status.description|trans }}</a></li>
{% endfor %}
</ul>
</div>
</div>
</div>
<!--//Filter By Status-->
</div>
<div class="uv-action-view-rt">
<input type="text" class="uv-search-inline uv-search-inline-full" placeholder="{{ 'Search Tickets'|trans }}">
</div>
</div>
</div>
<!--//Action Bar-->
<div class="uv-table uv-list-view">
<table>
<thead>
<tr>
<th>{{ "Ticket Id"|trans }}</th>
<th>{{ "Priority"|trans }}</th>
<th>{{ "Reference"|trans }}</th>
<th>{{ "Subject"|trans }}</th>
<th>{{ "Status"|trans }}</th>
<th>{{ "Timestamp"|trans }}</th>
<th>{{ "Agent"|trans }}</th>
<th class="uv-last">{{ "Kudos"|trans }} </th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="navigation"></div>
</div>
{% endblock %}
{% block footer %}
{{ parent() }}
<!-- Sorting Template -->
<script id="ticket_list_sorting_tmp" type="text/template">
<li class="<% if(sort == 't.id') { %>uv-drop-list-active<% } %>">
<a href="#<% if(queryString != '') { %><%- queryString %>/<% } %><% if(page) { %>page/<%- page %><% } else { %>page/1<% } %>/sort/t.id/<% if(sort == 't.id') { %><% if(direction) { %>direction/<%- direction %><% } else { %>direction/desc<% } %><% } else { %>direction/asc<% } %>" data-field="t.id">
{{ 'Ticket Id'|trans }}
</a>
<% if(sort == 't.id') { %>
<span class="uv-sorting <% if(direction == 'asc') { %> descend <% } else { %> ascend <% } %>"></span>
<% } %>
</li>
<li class="<% if(sort == 'name') { %>uv-drop-list-active<% } %>">
<a href="#<% if(queryString != '') { %><%- queryString %>/<% } %><% if(page) { %>page/<%- page %><% } else { %>page/1<% } %>/sort/name/<% if(sort == 'name') { %><% if(direction) { %>direction/<%- direction %><% } else { %>direction/desc<% } %><% } else { %>direction/asc<% } %>" data-field="name">
{{ 'Agent Name'|trans }}
</a>
<% if(sort == 'name') { %>
<span class="uv-sorting <% if(direction == 'asc') { %> descend <% } else { %> ascend <% } %>"></span>
<% } %>
</li>
</script>
<!-- //Sorting Template -->
<script id="ticket_list_item_tmp" type="text/template">
<td data-value="{{ 'Ticket Id'|trans }}">
<a class="not-shiny" href="<%- path %>">
#<%- id %>
</a>
</td>
<td style="<% if(priority.colorCode) { %>background-color:<%= priority.colorCode %><% } %>" data-value="{{ 'Priority'|trans }}">
<a class="not-shiny" href="<%- path %>">
<%- priority.description %>
</a>
</td>
<td data-value="{{ 'Reference'|trans }}" class="tooltip">
<a class="not-shiny" href="<%- path %>">
<%- reference %>
</a>
<span class="tooltip-text">
<%- lastThread.threadMessage && lastThread.threadMessage.length <= 100 ? lastThread.threadMessage : lastThread.threadMessage.substr(0, 100) + '...' %>
</span>
</td>
<td data-value="{{ 'Subject'|trans }}" class="tooltip">
<a class="not-shiny" href="<%- path %>">
<%- subject %>
</a>
<span class="tooltip-text">
<%- lastThread.threadMessage && lastThread.threadMessage.length <= 100 ? lastThread.threadMessage : lastThread.threadMessage.substr(0, 100) + '...' %>
</span>
</td>
<td style="<% if(status.colorCode) { %>background-color:<%= status.colorCode %><% } %>" data-value="{{ 'Status'|trans }}">
<a class="not-shiny" href="<%- path %>">
<%- status.code %>
</a>
</td>
<td data-value="{{ 'Timestamp'|trans }}">
<a class="not-shiny" href="<%- path %>">
<%= formatedCreatedAt %>
</a>
</td>
<td data-value="{{ 'Agent'|trans }}">
<a class="not-shiny" href="<%- path %>">
<% if(agent) { %>
<% if(agent.smallThumbnail != null) { %>
<img class="uv-table-agent" src="{{ app.request.scheme ~'://' ~ app.request.httpHost ~ asset('') }}<%- agent.smallThumbnail %>" alt=""/>
<% } else { %>
<img class="uv-table-agent" src="{{ asset(default_agent_image_path) }}" alt=""/>
<% } %>
<%- agent.firstName + ' ' + (agent.lastName == null ? '' : agent.lastName) %>
<% } else { %>
{{ 'Unassigned'|trans }}
<% } %>
</a>
</td>
<td>
<div class="uv-report-brick-wrapper" <% if (status.id === 5 || status.id === 9) { %>style="display: block;"<% } else { %>style="visibility: hidden;"<% } %>>
<!-- Primer elemento -->
<span class="uv-report-brick <% if (vote !== null) { %>selected<% } %> <% if (vote !== null && vote !== 2) { %>disabled<% } %>" data-ticket-id="<%- id %>" data-vote="2" data-voted="<%- vote === 2 %>" title="{{ 'Sad'|trans }}" onclick="mostrarValor(this)">
<span class="uv-icon-kudos kudos-2"></span>
</span>
<!-- Segundo elemento -->
<span class="uv-report-brick <% if (vote !== null) { %>selected<% } %> <% if (vote !== null && vote !== 3) { %>disabled<% } %>" data-ticket-id="<%- id %>" data-vote="3" data-voted="<%- vote === 3 %>" title="{{ 'Neutral'|trans }}" onclick="mostrarValor(this)">
<span class="uv-icon-kudos kudos-3"></span>
</span>
<!-- Tercer elemento -->
<span class="uv-report-brick <% if (vote !== null) { %>selected<% } %> <% if (vote !== null && vote !== 4) { %>disabled<% } %>" data-ticket-id="<%- id %>" data-vote="4" data-voted="<%- vote === 4 %>" title="{{ 'Happy'|trans }}" onclick="mostrarValor(this)">
<span class="uv-icon-kudos kudos-4"></span>
</span>
</div>
</td>
</script>
<div class="uv-pop-up-overlay" id="confirm-modal" style="display: none;">
<div class="uv-pop-up-box uv-pop-up-slim">
<span class="uv-pop-up-close"></span>
<h2>{{ 'Confirm Action'|trans }}</h2>
<p>{{ 'Are you sure? You want to perform this action.'|trans }}</p>
<!-- Agrega el campo de comentario -->
<textarea id="comment" placeholder="Add a comment"></textarea>
<div class="uv-pop-up-actions">
<a href="#" class="uv-btn uv-btn-error confirm">{{ 'Confirm'|trans }}</a>
<a href="#" class="uv-btn cancel">{{ 'Cancel'|trans }}</a>
</div>
</div>
</div>
<script type="text/javascript">
function mostrarValor(element) {
// Obtener el valor del voto desde el atributo "data-vote"
var voteValue = element.getAttribute("data-vote");
// Obtener el ID del ticket desde el atributo "data-ticket-id"
var ticketId = element.getAttribute("data-ticket-id");
// Obtener el primer número antes del guión "-"
var ticketIdParts = ticketId.split("-");
var firstNumber = ticketIdParts[0];
// Verificar si la carita ya tiene un valor asignado (clase "selected")
if (!element.classList.contains("selected")) {
// Mostrar el mensaje de confirmación
var confirmModal = document.getElementById("confirm-modal");
confirmModal.style.display = "block";
// Asignar acciones a los botones del mensaje de confirmación
var confirmButton = confirmModal.querySelector(".confirm");
var cancelButton = confirmModal.querySelector(".cancel");
// Construir la URL utilizando plantillas literales
var url = "{{ path('helpdesk_customer_create_vote', {'id': 'ReplaceMeWithFirstNumber', 'vote': 'ReplaceMeWithVote', 'comment': 'ReplaceMeWithComment'}) }}";
url = url.replace("ReplaceMeWithFirstNumber", firstNumber);
url = url.replace("ReplaceMeWithVote", voteValue);
confirmButton.onclick = function () {
let comment = document.getElementById("comment").value;
console.log('comment', comment);
if (comment === '') {
comment = 'NoCommentProvided'; // O proporciona un valor predeterminado adecuado
}
url = url.replace("ReplaceMeWithComment", comment);
console.log('url',url);
// Realizar la solicitud AJAX
$.ajax({
url: url,
type: 'POST',
dataType: 'json',
success: function (response) {
// Desactivar todas las caritas en el mismo ticket
var caritasEnMismoTicket = document.querySelectorAll(`.uv-report-brick[data-ticket-id="${ticketId}"]`);
caritasEnMismoTicket.forEach(function(carita) {
carita.classList.remove("selected");
carita.classList.add("disabled");
});
// Activar la carita clickeada
element.classList.remove("disabled");
element.classList.add("selected");
// Ocultar el mensaje de confirmación
confirmModal.style.display = "none";
app.appView.renderResponseAlert(response)
},
error: function (xhr) {
// Manejar errores si es necesario
console.error("Error en la solicitud AJAX: " + xhr.statusText);
// Ocultar el mensaje de confirmación
confirmModal.style.display = "none";
app.appView.renderResponseAlert(response)
}
});
}
// Acción al hacer clic en "Cancelar"
cancelButton.onclick = function () {
// Ocultar el mensaje de confirmación
confirmModal.style.display = "none";
};
}
}
$(function() {
var ticketPath = "{{ path('helpdesk_customer_ticket', {'id': 'replaceId' }) }}";
var TicketModel = Backbone.Model.extend({
idAttribute : "id",
defaults : {
path : "",
}
});
var TicketCollection = AppCollection.extend({
model : TicketModel,
url : "{{ path('helpdesk_customer_ticket_collection_xhr') }}",
filterParameters : {
"status" : "",
"search" : "",
},
parseRecords: function (resp, options) {
return resp.tickets;
},
syncData : function() {
app.appView.showLoader();
this.fetch({
data : this.getValidParameters(),
reset: true,
success: function(model, response) {
app.appView.hideLoader();
var ticketListView = new TicketList();
app.pager.paginationData = response.pagination;
app.pager.url = response.pagination.url;
if(ticketCollection.length == 0 && app.pager.paginationData.current != "0")
router.navigate(url.replace('replacePage', app.pager.paginationData.last),{trigger: true});
else {
app.pager.render();
$(".uv-pagination a").on("mouseover", function() {
$(this).attr('style', 'color: white !important')
}).on("mouseout", function() {
$(this).removeAttr("style");
});
}
},
error: function (model, xhr, options) {
if(url = xhr.getResponseHeader('Location'))
window.location = url;
}
});
}
});
var TicketItem = Backbone.View.extend({
tagName : "tr",
template : _.template($("#ticket_list_item_tmp").html()),
render : function () {
this.model.set({path:ticketPath.replace('replaceId', this.model.attributes.id + '-' + this.model.attributes.numeracion)})
if(this.model.attributes.numeracion){
this.model.set('id', this.model.attributes.id + '-' + this.model.attributes.numeracion)
}
this.$el.html(this.template(this.model.toJSON()));
var text= this.$el.html()
if(this.model.attributes.isCustomerView != true){
this.$el.html(text);
this.$el.addClass('unread')
}
if(this.model.attributes.status.id==8 || this.model.attributes.status.id==7){
var result4= text.split('class="not-shiny"').join('class="not-shiny" style="color: aliceblue;"')
this.$el.html(result4);
this.el.bgColor="#a70000"
}
//console.log(this)
return this;
}
});
var TicketList = Backbone.View.extend({
el : $(".uv-list-view table tbody"),
initialize : function() {
this.render();
},
render : function () {
this.$el.html('');
if(ticketCollection.length) {
_.each(ticketCollection.models, function (item) {
this.renderTicket(item);
}, this);
} else {
this.$el.append("<tr style='text-align: center;float: none;'><td colspan='5'>{% trans %}No Record Found{% endtrans %}</td></tr>")
}
},
renderTicket : function (item) {
var ticketItem = new TicketItem({
model: item
});
this.$el.append(ticketItem.render().el);
}
});
var Filter = app.Filter.extend({
defaultSortIndex: 't.id',
sortText: "{% trans %}Sort By:{% endtrans %} ",
defaultSortText: "{% trans %}Sort By:{% endtrans %} {% trans %}Ticket Id{% endtrans %}",
template : _.template($("#ticket_list_sorting_tmp").html()),
filterByStatus : function(e) {
e.preventDefault()
this.collection.reset();
this.collection.state.currentPage = null;
this.collection.setSorting(null, null, {full: false});
this.collection.filterParameters.status = Backbone.$(e.currentTarget).find('a').attr('data-id');
var queryString = app.appView.buildQuery($.param(this.collection.getValidParameters()));
router.navigate(queryString, {trigger: true});
},
})
var ticketCollection = new TicketCollection();
var filter = new Filter({
el: $('.uv-action-view'),
collection : ticketCollection
});
Router = Backbone.Router.extend({
routes: {
'page/:number(/sort/:sortField)(/direction/:order)' : 'paginate',
'search/:query(/page/:number)(/sort/:sortField)(/direction/:order)' : 'filterByQuery',
'status/:status(/search/:query)(/page/:number)(/sort/:sortField)(/direction/:order)' : 'filterByStatus',
'' : 'initializeList',
'_=_' : 'initializeList',
},
initializeList : function() {
this.resetParams('','');
this.fetch(null, null, null);
},
paginate : function(number, sortField, order) {
this.resetParams('', '');
this.fetch(number, sortField, order);
},
filterByQuery : function(query, number, sortField, order) {
if(query != null)
query = query.replace(/\+/g,' ');
this.resetParams(query, '');
this.fetch(number, sortField, order);
},
filterByStatus : function(status, query, number, sortField, order) {
if(query != null)
query = query.replace(/\+/g,' ');
this.resetParams(query, status);
this.fetch(number, sortField, order);
},
fetch : function(number, sortField, order) {
ticketCollection.state.currentPage = number;
filter.sortCollection(sortField, order);
ticketCollection.syncData();
},
resetParams : function(query, status) {
ticketCollection.filterParameters.search = query;
$(".uv-search-inline ").val(query);
ticketCollection.filterParameters.status = status;
var statusText = status ? $(".filter-by-status a[data-id='" + status + "']").text() : "{% trans %}All{% endtrans %}";
$(".filter-by-status .uv-dropdown-btn").text("{% trans %}Status:{% endtrans %} " + statusText);
}
});
router = new Router();
Backbone.history.start({push_state:true});
});
</script>
{% endblock %}