{% extends "@UVDeskCoreFramework//Templates//layout.html.twig" %}
{% block title %}
{{ 'Horarios Laborales'|trans }}
{% endblock %}
{% block pageContent %}
<style>
.uv-action-bar {
border-bottom: 2px solid #e9ecef;
padding-bottom: 20px;
margin-bottom: 30px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
margin: -20px -20px 30px -20px;
padding: 30px 20px 20px 20px;
border-radius: 0 0 15px 15px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}
.uv-action-bar h1 {
color: white;
margin: 0;
font-size: 28px;
font-weight: 600;
text-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.uv-working-hours-container {
padding: 20px;
background: #f8f9fa;
min-height: calc(100vh - 200px);
}
/* Nuevo layout con dashboard y sidebar */
.uv-countries-dashboard {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 30px;
margin-bottom: 30px;
}
.uv-countries-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.uv-country-card-expanded {
background: white;
border-radius: 15px;
padding: 20px;
box-shadow: 0 6px 20px rgba(0,0,0,0.08);
border-top: 4px solid #28a745;
position: relative;
transition: all 0.3s ease;
}
.uv-country-card-expanded.closed {
border-top-color: #dc3545;
}
.uv-country-card-expanded:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0,0,0,0.12);
}
/* Sidebar de estadísticas */
.uv-sidebar-stats {
background: white;
border-radius: 15px;
padding: 20px;
box-shadow: 0 4px 15px rgba(0,0,0,0.08);
height: fit-content;
position: sticky;
top: 20px;
}
.uv-sidebar-title {
font-size: 18px;
font-weight: 700;
color: #2c3e50;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #e9ecef;
display: flex;
align-items: center;
gap: 8px;
}
.uv-stat-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 0;
border-bottom: 1px solid #f1f3f4;
}
.uv-stat-item:last-child {
border-bottom: none;
}
.uv-stat-label {
font-size: 13px;
color: #6c757d;
font-weight: 500;
display: flex;
align-items: center;
gap: 6px;
}
.uv-stat-value {
font-size: 16px;
font-weight: 700;
color: #2c3e50;
}
.uv-stat-icon {
font-size: 16px;
}
/* Información principal del país */
.uv-country-main-info {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #e9ecef;
}
.uv-country-name {
font-size: 18px;
font-weight: 700;
color: #2c3e50;
display: flex;
align-items: center;
gap: 10px;
}
.uv-country-flag {
font-size: 24px;
}
.uv-country-status {
padding: 6px 12px;
border-radius: 15px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.uv-country-status.open {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
border: 1px solid rgba(40, 167, 69, 0.3);
}
.uv-country-status.closed {
background: rgba(220, 53, 69, 0.1);
color: #dc3545;
border: 1px solid rgba(220, 53, 69, 0.3);
}
/* Detalles del país */
.uv-country-details {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-bottom: 15px;
}
.uv-detail-section {
background: #f8f9fa;
padding: 12px;
border-radius: 8px;
border-left: 3px solid #667eea;
}
.uv-detail-title {
font-size: 11px;
font-weight: 600;
color: #495057;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 6px;
}
.uv-detail-content {
font-size: 13px;
color: #2c3e50;
font-weight: 500;
}
.uv-time-display {
font-family: 'Courier New', monospace;
font-size: 14px;
font-weight: 700;
color: #667eea;
}
/* Agentes del país */
.uv-agents-section {
background: linear-gradient(135deg, #fff3e0, #ffe0b2);
padding: 12px;
border-radius: 8px;
border: 1px solid #ffcc02;
margin-top: 10px;
}
.uv-agents-title {
font-size: 12px;
font-weight: 600;
color: #e65100;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 6px;
}
.uv-agents-preview {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: flex-start;
}
.uv-agent-chip {
display: flex;
align-items: center;
gap: 6px;
background: rgba(255, 255, 255, 0.9);
padding: 6px 10px;
border-radius: 15px;
font-size: 11px;
color: #2c3e50;
font-weight: 500;
border: 1px solid rgba(102, 126, 234, 0.2);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
min-width: fit-content;
max-width: 200px;
}
.uv-agent-avatar-small {
width: 18px;
height: 18px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 9px;
font-weight: bold;
flex-shrink: 0;
}
.uv-no-agents {
text-align: center;
padding: 10px;
color: #6c757d;
font-size: 11px;
font-style: italic;
background: rgba(255, 255, 255, 0.5);
border-radius: 6px;
}
/* Botón de edición */
.uv-edit-hours-btn {
background: linear-gradient(135deg, #ffc107, #ff9800);
color: #212529;
border: none;
padding: 8px 12px;
border-radius: 8px;
font-size: 11px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 0.5px;
display: flex;
align-items: center;
gap: 4px;
}
.uv-edit-hours-btn:hover {
background: linear-gradient(135deg, #e0a800, #f57c00);
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(255, 193, 7, 0.3);
}
/* Responsive para el nuevo layout */
@media (max-width: 768px) {
.uv-countries-dashboard {
grid-template-columns: 1fr;
gap: 20px;
}
.uv-countries-grid {
grid-template-columns: 1fr;
gap: 15px;
}
.uv-country-details {
grid-template-columns: 1fr;
gap: 10px;
}
.uv-sidebar-stats {
position: static;
order: -1;
}
}
/* Estilos eliminados - ya no necesarios con el nuevo diseño */
@keyframes pulse-green {
0% { box-shadow: 0 0 12px rgba(40, 167, 69, 0.4); }
50% { box-shadow: 0 0 20px rgba(40, 167, 69, 0.6); }
100% { box-shadow: 0 0 12px rgba(40, 167, 69, 0.4); }
}
@keyframes pulse-red {
0% { box-shadow: 0 0 12px rgba(220, 53, 69, 0.4); }
50% { box-shadow: 0 0 20px rgba(220, 53, 69, 0.6); }
100% { box-shadow: 0 0 12px rgba(220, 53, 69, 0.4); }
}
.uv-refresh-button {
background: linear-gradient(135deg, #007bff, #0056b3);
color: white;
border: none;
padding: 12px 24px;
border-radius: 25px;
cursor: pointer;
margin-left: 15px;
font-weight: 600;
font-size: 14px;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0, 123, 255, 0.3);
}
.uv-refresh-button:hover {
background: linear-gradient(135deg, #0056b3, #004085);
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 123, 255, 0.4);
}
.uv-refresh-button i {
margin-right: 8px;
}
.uv-loading {
text-align: center;
padding: 40px 20px;
color: #6c757d;
font-size: 16px;
}
.uv-loading i {
font-size: 24px;
margin-bottom: 15px;
display: block;
color: #667eea;
}
.uv-error {
background: linear-gradient(135deg, #f8d7da, #f5c6cb);
color: #721c24;
padding: 20px;
border-radius: 15px;
margin: 20px 0;
border: 1px solid #f5c6cb;
text-align: center;
font-weight: 500;
}
.uv-status-badge {
display: inline-block;
padding: 2px 5px;
border-radius: 10px;
font-size: 8px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-left: 5px;
}
.uv-status-badge.open {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
border: 1px solid rgba(40, 167, 69, 0.3);
}
.uv-status-badge.closed {
background: rgba(220, 53, 69, 0.1);
color: #dc3545;
border: 1px solid rgba(220, 53, 69, 0.3);
}
/* Estilos para la sección de tickets */
.uv-tickets-section {
background: white;
border-radius: 20px;
padding: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.08);
margin-bottom: 30px;
}
.uv-tickets-section h2 {
color: #2c3e50;
font-size: 20px;
font-weight: 700;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 2px solid #e9ecef;
display: flex;
align-items: center;
gap: 8px;
}
.uv-tickets-table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
}
.uv-tickets-table th {
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
color: #495057;
font-weight: 600;
padding: 10px 8px;
text-align: left;
border-bottom: 2px solid #dee2e6;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.uv-tickets-table td {
padding: 10px 8px;
border-bottom: 1px solid #f1f3f4;
color: #6c757d;
font-size: 12px;
}
.uv-tickets-table tr:hover {
background-color: #f8f9fa;
}
.uv-ticket-id {
font-weight: 600;
color: #667eea;
font-family: 'Courier New', monospace;
font-size: 11px;
}
.uv-ticket-id-link {
color: #667eea;
text-decoration: none;
font-weight: 600;
font-family: 'Courier New', monospace;
font-size: 11px;
transition: all 0.3s ease;
padding: 2px 6px;
border-radius: 4px;
background: rgba(102, 126, 234, 0.1);
border: 1px solid rgba(102, 126, 234, 0.2);
}
.uv-ticket-id-link:hover {
text-decoration: none;
background: rgba(102, 126, 234, 0.2);
border-color: rgba(102, 126, 234, 0.4);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
}
.uv-ticket-id-link:active {
transform: translateY(0);
}
.uv-ticket-subject {
font-weight: 500;
color: #2c3e50;
max-width: 180px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 11px;
}
.uv-customer-info {
display: flex;
flex-direction: column;
gap: 2px;
}
.uv-customer-name {
font-weight: 600;
color: #2c3e50;
font-size: 11px;
}
.uv-customer-email {
font-size: 10px;
color: #6c757d;
}
.uv-region-info {
display: flex;
flex-direction: column;
gap: 2px;
}
.uv-region-name {
font-weight: 600;
color: #28a745;
font-size: 11px;
}
.uv-region-description {
font-size: 9px;
color: #6c757d;
font-style: italic;
}
.uv-agent-assigned {
display: flex;
flex-direction: column;
gap: 2px;
}
.uv-agent-assigned-name {
font-weight: 600;
color: #667eea;
font-size: 11px;
}
.uv-agent-assigned-email {
font-size: 9px;
color: #6c757d;
}
.uv-agent-assigned-none {
color: #6c757d;
font-style: italic;
font-size: 10px;
}
.uv-priority-badge {
padding: 2px 5px;
border-radius: 8px;
font-size: 9px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.uv-priority-badge.low {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
border: 1px solid rgba(40, 167, 69, 0.3);
}
.uv-priority-badge.medium {
background: rgba(255, 193, 7, 0.1);
color: #ffc107;
border: 1px solid rgba(255, 193, 7, 0.3);
}
.uv-priority-badge.high {
background: rgba(220, 53, 69, 0.1);
color: #dc3545;
border: 1px solid rgba(220, 53, 69, 0.3);
}
.uv-status-badge-ticket {
padding: 2px 5px;
border-radius: 8px;
font-size: 9px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.uv-status-badge-ticket.open {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
border: 1px solid rgba(40, 167, 69, 0.3);
}
.uv-status-badge-ticket.pending {
background: rgba(255, 193, 7, 0.1);
color: #ffc107;
border: 1px solid rgba(255, 193, 7, 0.3);
}
.uv-status-badge-ticket.closed {
background: rgba(108, 117, 125, 0.1);
color: #6c757d;
border: 1px solid rgba(108, 117, 125, 0.3);
}
.uv-card-header-icon {
width: 25px;
height: 25px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
font-weight: bold;
}
/* Estilos para la sección de agentes */
.uv-agents-section {
background: white;
border-radius: 20px;
padding: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.08);
}
.uv-agents-section h2 {
color: #2c3e50;
font-size: 20px;
font-weight: 700;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 2px solid #e9ecef;
display: flex;
align-items: center;
gap: 8px;
}
.uv-agents-table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
}
.uv-agents-table th {
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
color: #495057;
font-weight: 600;
padding: 10px 8px;
text-align: left;
border-bottom: 2px solid #dee2e6;
font-size: 12px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.uv-agents-table td {
padding: 10px 8px;
border-bottom: 1px solid #f1f3f4;
color: #6c757d;
font-size: 12px;
}
.uv-agents-table tr:hover {
background-color: #f8f9fa;
}
.uv-agent-id {
font-weight: 600;
color: #667eea;
font-family: 'Courier New', monospace;
font-size: 11px;
}
.uv-agent-name {
font-weight: 600;
color: #2c3e50;
font-size: 12px;
}
.uv-agent-email {
font-size: 10px;
color: #6c757d;
}
.uv-agent-idor {
font-size: 10px;
color: #6c757d;
font-family: 'Courier New', monospace;
}
.uv-agent-role {
font-size: 10px;
color: #6c757d;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.uv-agent-region {
font-weight: 600;
color: #28a745;
font-size: 11px;
}
.uv-agent-region-description {
font-size: 9px;
color: #6c757d;
font-style: italic;
}
.uv-agent-status {
padding: 3px 6px;
border-radius: 10px;
font-size: 9px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.uv-agent-status.active {
background: rgba(40, 167, 69, 0.1);
color: #28a745;
border: 1px solid rgba(40, 167, 69, 0.3);
}
.uv-agent-status.inactive {
background: rgba(108, 117, 125, 0.1);
color: #6c757d;
border: 1px solid rgba(108, 117, 125, 0.3);
}
.uv-agent-last-activity {
font-size: 10px;
color: #6c757d;
font-style: italic;
font-family: 'Courier New', monospace;
}
/* Estilos para el modal de asignación de agentes */
.uv-modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 10000;
display: none;
align-items: center;
justify-content: center;
}
.uv-modal-overlay.show {
display: flex;
}
.uv-modal {
background: white;
border-radius: 20px;
padding: 30px;
max-width: 700px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 25px 80px rgba(0, 0, 0, 0.3);
position: relative;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.uv-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 25px;
padding-bottom: 20px;
border-bottom: 3px solid #e9ecef;
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
margin: -30px -30px 25px -30px;
padding: 25px 30px 20px 30px;
border-radius: 20px 20px 0 0;
}
.uv-modal-title {
font-size: 20px;
font-weight: 700;
color: #2c3e50;
margin: 0;
}
.uv-modal-close {
background: none;
border: none;
font-size: 24px;
color: #6c757d;
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: all 0.3s ease;
}
.uv-modal-close:hover {
background: #f8f9fa;
color: #495057;
}
.uv-modal-content {
margin-bottom: 20px;
}
.uv-modal-description {
color: #6c757d;
margin-bottom: 20px;
line-height: 1.5;
}
.uv-agents-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 12px;
margin-bottom: 20px;
}
.uv-agent-option {
background: #f8f9fa;
border: 2px solid #e9ecef;
border-radius: 10px;
padding: 15px;
cursor: pointer;
transition: all 0.3s ease;
text-align: center;
}
.uv-agent-option:hover {
border-color: #667eea;
background: #f0f2ff;
transform: translateY(-2px);
}
.uv-agent-option.selected {
border-color: #28a745;
background: #f8fff9;
}
.uv-agent-option-avatar {
width: 50px;
height: 50px;
background: linear-gradient(135deg, #667eea, #764ba2);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 18px;
font-weight: bold;
margin: 0 auto 10px auto;
}
.uv-agent-option-name {
font-weight: 600;
color: #2c3e50;
margin-bottom: 5px;
font-size: 14px;
}
.uv-agent-option-email {
color: #6c757d;
font-size: 12px;
margin-bottom: 8px;
}
.uv-agent-option-status {
display: inline-block;
padding: 4px 8px;
border-radius: 15px;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
background: rgba(40, 167, 69, 0.1);
color: #28a745;
border: 1px solid rgba(40, 167, 69, 0.3);
}
.uv-modal-footer {
display: flex;
gap: 10px;
justify-content: flex-end;
}
.uv-btn {
padding: 10px 20px;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
font-size: 14px;
}
.uv-btn-secondary {
background: #6c757d;
color: white;
border: 2px solid #6c757d;
}
.uv-btn-secondary:hover {
background: #5a6268;
border-color: #5a6268;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(108, 117, 125, 0.3);
}
.uv-btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: 2px solid transparent;
}
.uv-btn-primary:hover {
background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%);
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}
.uv-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.uv-confirmation-message {
background: linear-gradient(135deg, #fff3cd, #ffeaa7);
border: 1px solid #ffc107;
border-radius: 10px;
padding: 15px;
margin-bottom: 20px;
text-align: center;
color: #856404;
font-weight: 500;
}
.uv-ticket-assign-btn {
background: linear-gradient(135deg, #007bff, #0056b3);
color: white;
border: none;
padding: 6px 12px;
border-radius: 6px;
font-size: 10px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.uv-ticket-assign-btn:hover {
background: linear-gradient(135deg, #0056b3, #004085);
transform: translateY(-1px);
}
.uv-ticket-assign-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
background: #6c757d;
}
@media (max-width: 768px) {
.uv-working-hours-grid {
grid-template-columns: 1fr;
gap: 15px;
}
.uv-action-bar {
padding: 20px 15px 15px 15px;
margin: -20px -15px 20px -15px;
}
.uv-action-bar h1 {
font-size: 24px;
}
.uv-working-hours-container {
padding: 15px;
}
.uv-card-content {
left: -15px;
right: -15px;
border-radius: 8px;
}
.uv-card-content.expanded {
padding: 12px;
}
.uv-card-content.expanded::before,
.uv-card-content.expanded::after {
left: 35px;
}
.uv-modal {
margin: 20px;
padding: 20px;
max-height: 90vh;
}
.uv-agents-grid {
grid-template-columns: 1fr;
gap: 10px;
}
.uv-agent-option {
padding: 12px;
}
.uv-modal-footer {
flex-direction: column;
}
.uv-btn {
width: 100%;
text-align: center;
}
.uv-time-info {
flex-direction: column;
align-items: flex-start;
}
.uv-time-label {
margin-bottom: 3px;
min-width: auto;
}
.uv-agents-count {
padding: 3px 6px;
gap: 3px;
}
.uv-agents-count-number {
font-size: 11px;
min-width: 14px;
}
.uv-agents-count-total {
font-size: 9px;
}
.uv-agents-section-card {
padding: 8px;
}
.uv-agent-item {
padding: 5px;
gap: 6px;
}
.uv-agent-avatar {
width: 20px;
height: 20px;
font-size: 8px;
}
.uv-tickets-table {
font-size: 11px;
}
.uv-tickets-table th,
.uv-tickets-table td {
padding: 8px 6px;
}
.uv-agent-assigned {
gap: 1px;
}
.uv-agent-assigned-name {
font-size: 10px;
}
.uv-agent-assigned-email {
font-size: 8px;
}
.uv-agent-assigned-none {
font-size: 9px;
}
.uv-agents-table {
font-size: 11px;
}
.uv-agents-table th,
.uv-agents-table td {
padding: 8px 6px;
}
.uv-ticket-id-link {
font-size: 10px;
padding: 1px 4px;
}
.uv-ticket-id-link:hover {
transform: none;
box-shadow: 0 1px 4px rgba(102, 126, 234, 0.3);
}
/* Estilos para el botón de edición de horarios */
.uv-edit-hours-btn {
background: linear-gradient(135deg, #ffc107, #ff9800);
color: #212529;
border: none;
padding: 6px 8px;
border-radius: 6px;
font-size: 10px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-left: 8px;
display: flex;
align-items: center;
justify-content: center;
min-width: 28px;
height: 28px;
}
.uv-edit-hours-btn:hover {
background: linear-gradient(135deg, #e0a800, #f57c00);
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(255, 193, 7, 0.3);
}
.uv-edit-icon {
font-size: 12px;
}
/* Estilos para el modal de edición de horarios */
.uv-modal-large {
max-width: 800px;
width: 95%;
padding: 35px;
}
.uv-edit-hours-form {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-radius: 15px;
padding: 25px;
margin-top: 20px;
border: 1px solid #dee2e6;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
}
.uv-form-group {
margin-bottom: 25px;
}
.uv-form-group label {
display: block;
font-weight: 600;
color: #2c3e50;
margin-bottom: 8px;
font-size: 14px;
}
.uv-form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #2c3e50;
font-size: 14px;
}
.uv-form-control {
width: 100%;
padding: 12px 16px;
border: 2px solid #e9ecef;
border-radius: 10px;
font-size: 14px;
transition: all 0.3s ease;
background: white;
font-weight: 500;
}
.uv-form-control:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.1);
transform: translateY(-1px);
}
.uv-form-control:hover {
border-color: #ced4da;
}
.uv-form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}
.uv-working-days-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
gap: 12px;
margin-top: 12px;
}
.uv-day-checkbox {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 16px;
background: white;
border: 2px solid #e9ecef;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 14px;
font-weight: 500;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.uv-day-checkbox:hover {
border-color: #667eea;
background: #f8f9fa;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.uv-day-checkbox input[type="checkbox"] {
margin: 0;
width: 18px;
height: 18px;
accent-color: #667eea;
cursor: pointer;
}
.uv-day-checkbox input[type="checkbox"]:checked + span {
color: #667eea;
font-weight: 700;
}
.uv-day-checkbox:has(input[type="checkbox"]:checked) {
border-color: #667eea;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.uv-day-checkbox:has(input[type="checkbox"]:checked) span {
color: white;
}
/* Responsive para el modal de edición */
@media (max-width: 768px) {
.uv-form-row {
grid-template-columns: 1fr;
gap: 10px;
}
.uv-working-days-grid {
grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
gap: 8px;
}
.uv-day-checkbox {
padding: 6px 8px;
font-size: 12px;
}
.uv-edit-hours-btn {
margin-left: 4px;
padding: 4px 6px;
min-width: 24px;
height: 24px;
}
.uv-edit-icon {
font-size: 10px;
}
}
}
</style>
<div class="uv-working-hours-container">
<div class="uv-action-bar">
<h1>{{ 'Horarios Laborales'|trans }}</h1>
<button class="uv-refresh-button" onclick="refreshAll()">
<i class="fa fa-refresh"></i> Actualizar Todo
</button>
</div>
<!-- Sección de Horarios Laborales -->
<div id="working-hours-content">
<div class="uv-loading">
<i class="fa fa-spinner fa-spin"></i>
Cargando horarios laborales...
</div>
</div>
<!-- Sección de Tickets Recientes -->
<div class="uv-tickets-section">
<h2>📋 Últimos 5 Tickets Ingresados</h2>
<div id="tickets-content">
<div class="uv-loading">
<i class="fa fa-spinner fa-spin"></i>
Cargando tickets recientes...
</div>
</div>
</div>
<!-- Sección de Agentes -->
<div class="uv-agents-section">
<h2>👥 Agentes </h2>
<div id="agents-content">
<div class="uv-loading">
<i class="fa fa-spinner fa-spin"></i>
Cargando agentes...
</div>
</div>
</div>
</div>
<!-- Modal para asignar agentes -->
<div id="assignAgentModal" class="uv-modal-overlay">
<div class="uv-modal">
<div class="uv-modal-header">
<h3 class="uv-modal-title">🔄 Asignar Agente al Ticket</h3>
<button class="uv-modal-close" onclick="closeAssignAgentModal()">×</button>
</div>
<div class="uv-modal-content">
<div class="uv-modal-description">
Selecciona un agente de una oficina abierta para asignar al ticket <strong id="modalTicketId"></strong>
<div style="margin-top: 8px; padding: 8px; background: rgba(102, 126, 234, 0.1); border-radius: 6px; font-size: 12px; color: #667eea;">
<strong>ℹ️ Solo se muestran agentes de oficinas que están abiertas actualmente</strong>
</div>
</div>
<div id="confirmationMessage" class="uv-confirmation-message" style="display: none;">
Se le va a asignar el ticket actual a este agente, ¿desea continuar?
</div>
<div id="agentsGrid" class="uv-agents-grid">
<!-- Los agentes se cargarán dinámicamente aquí -->
</div>
</div>
<div class="uv-modal-footer">
<button class="uv-btn uv-btn-secondary" onclick="closeAssignAgentModal()">Cancelar</button>
<button id="confirmAssignBtn" class="uv-btn uv-btn-primary" onclick="confirmAssignAgent()" disabled>
Confirmar Asignación
</button>
</div>
</div>
</div>
<!-- Modal para editar horarios laborales -->
<div id="editHoursModal" class="uv-modal-overlay">
<div class="uv-modal uv-modal-large">
<div class="uv-modal-header">
<h3 class="uv-modal-title">🕐 Editar Horarios Laborales</h3>
<button class="uv-modal-close" onclick="closeEditHoursModal()">×</button>
</div>
<div class="uv-modal-content">
<div class="uv-modal-description">
Configura los horarios laborales para <strong id="editCountryName"></strong>
</div>
<div class="uv-edit-hours-form">
<div class="uv-form-group">
<label for="editTimezone">🌍 Zona Horaria:</label>
<select id="editTimezone" class="uv-form-control">
<option value="America/Argentina/Buenos_Aires">Argentina (Buenos Aires)</option>
<option value="America/Bogota">Colombia (Bogotá)</option>
<option value="America/Mexico_City">México (Ciudad de México)</option>
<option value="America/Sao_Paulo">Brasil (São Paulo)</option>
<option value="Europe/Madrid">España (Madrid)</option>
<option value="America/Lima">Perú (Lima)</option>
<option value="America/Santiago">Chile (Santiago)</option>
<option value="America/Caracas">Venezuela (Caracas)</option>
<option value="America/Costa_Rica">Costa Rica (San José)</option>
<option value="America/New_York">Estados Unidos (Nueva York)</option>
<option value="Europe/London">Reino Unido (Londres)</option>
<option value="Asia/Tokyo">Japón (Tokio)</option>
<option value="Australia/Sydney">Australia (Sídney)</option>
</select>
</div>
<div class="uv-form-row">
<div class="uv-form-group">
<label for="editStartTime">🕐 Hora de Inicio:</label>
<input type="time" id="editStartTime" class="uv-form-control" value="09:00">
</div>
<div class="uv-form-group">
<label for="editEndTime">🕐 Hora de Fin:</label>
<input type="time" id="editEndTime" class="uv-form-control" value="18:00">
</div>
</div>
<div class="uv-form-group">
<label>📅 Días Laborales:</label>
<div class="uv-working-days-grid">
<label class="uv-day-checkbox">
<input type="checkbox" id="editMonday" checked>
<span>Lunes</span>
</label>
<label class="uv-day-checkbox">
<input type="checkbox" id="editTuesday" checked>
<span>Martes</span>
</label>
<label class="uv-day-checkbox">
<input type="checkbox" id="editWednesday" checked>
<span>Miércoles</span>
</label>
<label class="uv-day-checkbox">
<input type="checkbox" id="editThursday" checked>
<span>Jueves</span>
</label>
<label class="uv-day-checkbox">
<input type="checkbox" id="editFriday" checked>
<span>Viernes</span>
</label>
<label class="uv-day-checkbox">
<input type="checkbox" id="editSaturday">
<span>Sábado</span>
</label>
<label class="uv-day-checkbox">
<input type="checkbox" id="editSunday">
<span>Domingo</span>
</label>
</div>
</div>
</div>
</div>
<div class="uv-modal-footer">
<button class="uv-btn uv-btn-secondary" onclick="closeEditHoursModal()">Cancelar</button>
<button id="saveWorkingHoursBtn" class="uv-btn uv-btn-primary" onclick="saveWorkingHours()">
Guardar Cambios
</button>
</div>
</div>
</div>
<script>
function loadWorkingHours() {
fetch('{{ path("helpdesk_report_working_hours_status") }}')
.then(response => response.json())
.then(data => {
displayWorkingHours(data);
})
.catch(error => {
console.error('Error loading working hours:', error);
document.getElementById('working-hours-content').innerHTML =
'<div class="uv-error">Error al cargar los horarios laborales. Por favor, intente nuevamente.</div>';
});
}
function loadRecentTickets() {
fetch('{{ path("helpdesk_report_working_hours_tickets") }}')
.then(response => response.json())
.then(data => {
// Limitar a solo los últimos 5 tickets
const limitedTickets = data.slice(0, 5);
displayRecentTickets(limitedTickets);
})
.catch(error => {
console.error('Error loading recent tickets:', error);
document.getElementById('tickets-content').innerHTML =
'<div class="uv-error">Error al cargar los tickets recientes. Por favor, intente nuevamente.</div>';
});
}
function loadAgents() {
fetch('{{ path("helpdesk_report_working_hours_agents") }}')
.then(response => response.json())
.then(data => {
displayAgents(data);
})
.catch(error => {
console.error('Error loading agents:', error);
document.getElementById('agents-content').innerHTML =
'<div class="uv-error">Error al cargar los agentes. Por favor, intente nuevamente.</div>';
});
}
function displayWorkingHours(data) {
const container = document.getElementById('working-hours-content');
// Calcular estadísticas globales
const countries = Object.values(data);
const totalCountries = countries.length;
const openCountries = countries.filter(c => c.status === 'open').length;
const totalAgents = countries.reduce((sum, c) => sum + (c.agents ? c.agents.length : 0), 0);
const activeAgents = countries.reduce((sum, c) => sum + (c.agents ? c.agents.filter(a => a.isEnabled).length : 0), 0);
let html = `
<div class="uv-countries-dashboard">
<div class="uv-countries-grid">
`;
// Mostrar cada país con información expandida
countries.forEach(country => {
const statusClass = country.status === 'open' ? 'open' : 'closed';
const statusText = country.status === 'open' ? 'Abierto' : 'Cerrado';
const countryFlag = getCountryFlag(country.country);
// Contar agentes activos
const activeAgentsCount = country.agents ? country.agents.filter(agent => agent.isEnabled).length : 0;
const totalAgentsCount = country.agents ? country.agents.length : 0;
html += `
<div class="uv-country-card-expanded ${statusClass}">
<div class="uv-country-main-info">
<div class="uv-country-name">
<span class="uv-country-flag">${countryFlag}</span>
${country.country}
</div>
<div class="uv-country-status ${statusClass}">${statusText}</div>
</div>
<div class="uv-country-details">
<div class="uv-detail-section">
<div class="uv-detail-title">🕐 Hora Local</div>
<div class="uv-detail-content">
<div class="uv-time-display">${country.localTime}</div>
<div style="font-size: 11px; color: #6c757d; margin-top: 2px;">${country.dayOfWeek}</div>
</div>
</div>
<div class="uv-detail-section">
<div class="uv-detail-title">📅 Horario Laboral</div>
<div class="uv-detail-content">
<div style="font-weight: 600;">${country.workingHours}</div>
<div style="font-size: 11px; color: #6c757d; margin-top: 2px;">${country.workingDaysText}</div>
</div>
</div>
<div class="uv-detail-section">
<div class="uv-detail-title">🌍 Zona Horaria</div>
<div class="uv-detail-content">${country.timezone}</div>
</div>
<div class="uv-detail-section">
<div class="uv-detail-title">👥 Agentes</div>
<div class="uv-detail-content">
<div style="font-weight: 600; color: #28a745;">${activeAgentsCount}/${totalAgentsCount} Activos</div>
</div>
</div>
</div>
<div class="uv-agents-section">
<div class="uv-agents-title">
👥 Agentes Asignados (${totalAgentsCount})
</div>
${country.agents && country.agents.length > 0 ?
`<div class="uv-agents-preview">
${country.agents.slice(0, 4).map(agent => `
<div class="uv-agent-chip">
<div class="uv-agent-avatar-small">${getInitials(agent.name)}</div>
<span>${agent.name}</span>
<span style="color: ${agent.isEnabled ? '#28a745' : '#dc3545'};">${agent.isEnabled ? '🟢' : '🔴'}</span>
</div>
`).join('')}
${country.agents.length > 4 ? `<div class="uv-agent-chip" style="background: rgba(102, 126, 234, 0.1); color: #667eea;">+${country.agents.length - 4} más</div>` : ''}
</div>` :
`<div class="uv-no-agents">No hay agentes asignados a esta región</div>`
}
</div>
<div style="margin-top: 15px; text-align: right;">
<button class="uv-edit-hours-btn" onclick="openEditHoursModal('${country.country}', ${JSON.stringify(country).replace(/"/g, '"')})" title="Editar horarios">
<span>✏️</span> Editar Horarios
</button>
</div>
</div>
`;
});
html += `
</div>
<div class="uv-sidebar-stats">
<div class="uv-sidebar-title">
📊 Estadísticas Globales
</div>
<div class="uv-stat-item">
<div class="uv-stat-label">
<span class="uv-stat-icon">🌍</span>
Países Totales
</div>
<div class="uv-stat-value">${totalCountries}</div>
</div>
<div class="uv-stat-item">
<div class="uv-stat-label">
<span class="uv-stat-icon">🟢</span>
Oficinas Abiertas
</div>
<div class="uv-stat-value" style="color: #28a745;">${openCountries}</div>
</div>
<div class="uv-stat-item">
<div class="uv-stat-label">
<span class="uv-stat-icon">🔴</span>
Oficinas Cerradas
</div>
<div class="uv-stat-value" style="color: #dc3545;">${totalCountries - openCountries}</div>
</div>
<div class="uv-stat-item">
<div class="uv-stat-label">
<span class="uv-stat-icon">👥</span>
Agentes Totales
</div>
<div class="uv-stat-value">${totalAgents}</div>
</div>
<div class="uv-stat-item">
<div class="uv-stat-label">
<span class="uv-stat-icon">✅</span>
Agentes Activos
</div>
<div class="uv-stat-value" style="color: #28a745;">${activeAgents}</div>
</div>
<div class="uv-stat-item">
<div class="uv-stat-label">
<span class="uv-stat-icon">⏰</span>
Zonas Horarias
</div>
<div class="uv-stat-value">${new Set(countries.map(c => c.timezone)).size}</div>
</div>
</div>
</div>
`;
container.innerHTML = html;
}
function displayRecentTickets(tickets) {
const container = document.getElementById('tickets-content');
if (!tickets || tickets.length === 0) {
container.innerHTML = '<div class="uv-error">No se encontraron tickets recientes.</div>';
return;
}
let html = `
<table class="uv-tickets-table">
<thead>
<tr>
<th>Ticket ID</th>
<th>Asunto</th>
<th>Cliente</th>
<th>Región</th>
<th>Estado</th>
<th>Prioridad</th>
<th>Agente Asignado</th>
<th>Fecha Creación</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
`;
tickets.forEach(ticket => {
const priorityClass = getPriorityClass(ticket.priority);
const statusClass = getStatusClass(ticket.status);
html += `
<tr>
<td><a href="/en/member/ticket/view/${ticket.ticketId}" class="uv-ticket-id-link" target="_blank">#${ticket.ticketId}</a></td>
<td><div class="uv-ticket-subject" title="${ticket.subject}">${ticket.subject}</div></td>
<td>
<div class="uv-customer-info">
<div class="uv-customer-name">${ticket.customer.name}</div>
<div class="uv-customer-email">${ticket.customer.email}</div>
<small>IDOR: ${ticket.customer.idor || 'N/A'}</small>
</div>
</td>
<td>
<div class="uv-region-info">
<div class="uv-region-name">${ticket.region.name}</div>
<div class="uv-region-description">${ticket.region.description}</div>
</div>
</td>
<td><span class="uv-status-badge-ticket ${statusClass}">${ticket.status}</span></td>
<td><span class="uv-priority-badge ${priorityClass}">${ticket.priority}</span></td>
<td>
${ticket.agent ?
`<div class="uv-agent-assigned">
<div class="uv-agent-assigned-name">${ticket.agent.name}</div>
<div class="uv-agent-assigned-email">${ticket.agent.email}</div>
</div>` :
`<div class="uv-agent-assigned-none">Sin asignar</div>`
}
</td>
<td>${formatDate(ticket.createdAt)}</td>
<td>
<button class="uv-ticket-assign-btn" onclick="openAssignAgentModal(${ticket.ticketId})">
Asignar Agente
</button>
</td>
</tr>
`;
});
html += '</tbody></table>';
container.innerHTML = html;
}
function displayAgents(agents) {
const container = document.getElementById('agents-content');
let html = '<table class="uv-agents-table">';
if (!agents || agents.length === 0) {
html += '<tr><td colspan="9" class="uv-error">No se encontraron agentes asignados.</td></tr>';
} else {
html += `
<thead>
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Email</th>
<th>IDOR</th>
<th>Rol</th>
<th>Región</th>
<th>Estado</th>
<th>Última Actividad</th>
</tr>
</thead>
<tbody>
`;
agents.forEach(agent => {
html += `
<tr>
<td><span class="uv-agent-id">#${agent.id}</span></td>
<td><span class="uv-agent-name">${agent.name}</span></td>
<td><span class="uv-agent-email">${agent.email}</span></td>
<td><span class="uv-agent-idor">${agent.idor || 'N/A'}</span></td>
<td><span class="uv-agent-role">${agent.role}</span></td>
<td>
<div class="uv-agent-region-info">
<div class="uv-agent-region">${agent.region.name}</div>
<div class="uv-agent-region-description">${agent.region.description}</div>
</div>
</td>
<td><span class="uv-agent-status ${agent.isEnabled ? 'active' : 'inactive'}">${agent.isEnabled ? 'Activo' : 'Inactivo'}</span></td>
<td><span class="uv-agent-last-activity">${agent.lastActivity}</span></td>
</tr>
`;
});
}
html += '</tbody></table>';
container.innerHTML = html;
}
function getPriorityClass(priority) {
const priorityMap = {
'low': 'low',
'medium': 'medium',
'high': 'high'
};
return priorityMap[priority.toLowerCase()] || 'low';
}
function getStatusClass(status) {
const statusMap = {
'open': 'open',
'pending': 'pending',
'closed': 'closed'
};
return statusMap[status.toLowerCase()] || 'open';
}
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('es-ES', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
function getCountryFlag(country) {
const flags = {
'Casa Central': '🏢',
'Colombia': '🇨🇴',
'Mexico': '🇲🇽',
'Brasil': '🇧🇷',
'España': '🇪🇸',
'Peru': '🇵🇪',
'Chile': '🇨🇱',
'Venezuela': '🇻🇪',
'Costa Rica': '🇨🇷'
};
return flags[country] || '🌍';
}
function getInitials(name) {
return name.split(' ').map(word => word.charAt(0)).join('').toUpperCase().substring(0, 2);
}
// Función eliminada - ya no necesaria con el nuevo diseño expandido
function refreshAll() {
// Actualizar horarios laborales
document.getElementById('working-hours-content').innerHTML =
'<div class="uv-loading"><i class="fa fa-spinner fa-spin"></i> Actualizando horarios...</div>';
// Actualizar tickets
document.getElementById('tickets-content').innerHTML =
'<div class="uv-loading"><i class="fa fa-spinner fa-spin"></i> Actualizando tickets...</div>';
// Actualizar agentes
document.getElementById('agents-content').innerHTML =
'<div class="uv-loading"><i class="fa fa-spinner fa-spin"></i> Actualizando agentes...</div>';
loadWorkingHours();
loadRecentTickets();
loadAgents();
}
// Load all data when page loads
document.addEventListener('DOMContentLoaded', function() {
loadWorkingHours();
loadRecentTickets();
loadAgents();
// Auto-refresh every 5 minutes
setInterval(function() {
loadWorkingHours();
loadRecentTickets();
loadAgents();
}, 5 * 60 * 1000);
});
// Variables globales para el modal
let currentTicketId = null;
let selectedAgentId = null;
let allAgents = [];
// Función para abrir el modal de asignación de agentes
function openAssignAgentModal(ticketId) {
currentTicketId = ticketId;
selectedAgentId = null;
// Mostrar el modal
document.getElementById('assignAgentModal').classList.add('show');
document.getElementById('modalTicketId').textContent = '#' + ticketId;
// Ocultar mensaje de confirmación
document.getElementById('confirmationMessage').style.display = 'none';
// Deshabilitar botón de confirmación
document.getElementById('confirmAssignBtn').disabled = true;
// Cargar agentes activos
loadActiveAgentsForModal();
}
// Función para cerrar el modal
function closeAssignAgentModal() {
document.getElementById('assignAgentModal').classList.remove('show');
currentTicketId = null;
selectedAgentId = null;
}
// Función para cargar agentes activos en el modal (solo de oficinas abiertas)
function loadActiveAgentsForModal() {
// Siempre cargar agentes de oficinas abiertas en tiempo real
fetch('{{ path("helpdesk_report_working_hours_active_office_agents") }}')
.then(response => response.json())
.then(data => {
allAgents = data;
displayAgentsInModal();
})
.catch(error => {
console.error('Error loading active office agents for modal:', error);
document.getElementById('agentsGrid').innerHTML =
'<div class="uv-error">Error al cargar los agentes de oficinas abiertas. Por favor, intente nuevamente.</div>';
});
}
// Función para mostrar agentes en el modal
function displayAgentsInModal() {
const agentsGrid = document.getElementById('agentsGrid');
const activeAgents = allAgents.filter(agent => agent.isEnabled);
if (allAgents.length === 0) {
agentsGrid.innerHTML = `
<div class="uv-error">
<div style="text-align: center; padding: 20px;">
<div style="font-size: 24px; margin-bottom: 10px;">🏢</div>
<div style="font-weight: 600; margin-bottom: 5px;">No hay oficinas abiertas</div>
<div style="font-size: 12px; color: #6c757d;">
Todas las oficinas están cerradas en este momento.<br>
Los agentes estarán disponibles cuando sus oficinas abran.
</div>
</div>
</div>
`;
return;
}
if (activeAgents.length === 0) {
agentsGrid.innerHTML = '<div class="uv-error">No hay agentes activos disponibles en las oficinas abiertas.</div>';
return;
}
let html = '';
activeAgents.forEach(agent => {
const regionName = agent.region.name || 'Sin región';
const regionFlag = getCountryFlag(regionName.replace('OR ', ''));
html += `
<div class="uv-agent-option" onclick="selectAgent(${agent.id})" data-agent-id="${agent.id}">
<div class="uv-agent-option-avatar">${getInitials(agent.name)}</div>
<div class="uv-agent-option-name">${agent.name}</div>
<div class="uv-agent-option-email">${agent.email}</div>
<div class="uv-agent-option-region" style="display: flex; align-items: center; gap: 4px; margin: 4px 0; font-size: 10px; color: #667eea;">
<span>${regionFlag}</span>
<span>${regionName}</span>
</div>
<div class="uv-agent-option-status">🟢 Activo</div>
</div>
`;
});
agentsGrid.innerHTML = html;
}
// Función para seleccionar un agente
function selectAgent(agentId) {
// Remover selección anterior
document.querySelectorAll('.uv-agent-option').forEach(option => {
option.classList.remove('selected');
});
// Seleccionar el nuevo agente
const selectedOption = document.querySelector(`[data-agent-id="${agentId}"]`);
if (selectedOption) {
selectedOption.classList.add('selected');
}
selectedAgentId = agentId;
// Mostrar mensaje de confirmación
document.getElementById('confirmationMessage').style.display = 'block';
// Habilitar botón de confirmación
document.getElementById('confirmAssignBtn').disabled = false;
}
// Función para confirmar la asignación
function confirmAssignAgent() {
if (!currentTicketId || !selectedAgentId) {
alert('Por favor, selecciona un agente primero.');
return;
}
// Deshabilitar botón durante la operación
document.getElementById('confirmAssignBtn').disabled = true;
document.getElementById('confirmAssignBtn').textContent = 'Asignando...';
// Realizar la asignación
assignTicketToAgent(currentTicketId, selectedAgentId);
}
// Función para asignar el ticket al agente
function assignTicketToAgent(ticketId, agentId) {
fetch('{{ path("helpdesk_report_working_hours_assign_agent") }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
ticketId: ticketId,
agentId: agentId
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Mostrar mensaje de éxito
alert('✅ Ticket asignado exitosamente al agente.');
// Cerrar modal
closeAssignAgentModal();
// Actualizar la lista de tickets
loadRecentTickets();
} else {
alert('❌ Error al asignar el ticket: ' + (data.message || 'Error desconocido'));
}
})
.catch(error => {
console.error('Error assigning ticket:', error);
alert('❌ Error al asignar el ticket. Por favor, intente nuevamente.');
})
.finally(() => {
// Restaurar botón
document.getElementById('confirmAssignBtn').disabled = false;
document.getElementById('confirmAssignBtn').textContent = 'Confirmar Asignación';
});
}
// Cerrar modal al hacer clic fuera de él
document.addEventListener('click', function(event) {
const modal = document.getElementById('assignAgentModal');
if (event.target === modal) {
closeAssignAgentModal();
}
});
// Cerrar modal con tecla Escape
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
closeAssignAgentModal();
}
});
// ===== FUNCIONES PARA EDITAR HORARIOS =====
let currentEditingCountry = null;
// Función para abrir el modal de edición de horarios
function openEditHoursModal(countryName, countryData) {
currentEditingCountry = countryData;
// Llenar el formulario con los datos actuales
document.getElementById('editCountryName').textContent = countryName;
document.getElementById('editTimezone').value = countryData.timezone;
document.getElementById('editStartTime').value = countryData.workingHours.split(' - ')[0];
document.getElementById('editEndTime').value = countryData.workingHours.split(' - ')[1];
// Configurar días laborales
const workingDays = countryData.workingDays;
document.getElementById('editMonday').checked = workingDays.includes('Lunes');
document.getElementById('editTuesday').checked = workingDays.includes('Martes');
document.getElementById('editWednesday').checked = workingDays.includes('Miércoles');
document.getElementById('editThursday').checked = workingDays.includes('Jueves');
document.getElementById('editFriday').checked = workingDays.includes('Viernes');
document.getElementById('editSaturday').checked = workingDays.includes('Sábado');
document.getElementById('editSunday').checked = workingDays.includes('Domingo');
// Mostrar el modal
document.getElementById('editHoursModal').style.display = 'flex';
}
// Función para cerrar el modal de edición
function closeEditHoursModal() {
document.getElementById('editHoursModal').style.display = 'none';
currentEditingCountry = null;
}
// Función para guardar los cambios de horarios
function saveWorkingHours() {
if (!currentEditingCountry) {
alert('No hay país seleccionado para editar.');
return;
}
// Recopilar datos del formulario
const formData = {
countryName: currentEditingCountry.country,
supportGroupId: currentEditingCountry.supportGroupId,
timezone: document.getElementById('editTimezone').value,
startTime: document.getElementById('editStartTime').value,
endTime: document.getElementById('editEndTime').value,
monday: document.getElementById('editMonday').checked ? 1 : 0,
tuesday: document.getElementById('editTuesday').checked ? 1 : 0,
wednesday: document.getElementById('editWednesday').checked ? 1 : 0,
thursday: document.getElementById('editThursday').checked ? 1 : 0,
friday: document.getElementById('editFriday').checked ? 1 : 0,
saturday: document.getElementById('editSaturday').checked ? 1 : 0,
sunday: document.getElementById('editSunday').checked ? 1 : 0
};
// Validar datos
if (!formData.timezone || !formData.startTime || !formData.endTime) {
alert('Por favor, complete todos los campos obligatorios.');
return;
}
// Deshabilitar botón durante la operación
const saveBtn = document.getElementById('saveWorkingHoursBtn');
saveBtn.disabled = true;
saveBtn.textContent = 'Guardando...';
// Enviar datos al servidor
fetch('{{ path("helpdesk_report_working_hours_update_hours") }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('✅ Horarios actualizados exitosamente.');
closeEditHoursModal();
// Actualizar la vista
loadWorkingHours();
} else {
alert('❌ Error al actualizar horarios: ' + (data.message || 'Error desconocido'));
}
})
.catch(error => {
console.error('Error updating working hours:', error);
alert('❌ Error al actualizar horarios. Por favor, intente nuevamente.');
})
.finally(() => {
// Restaurar botón
saveBtn.disabled = false;
saveBtn.textContent = 'Guardar Cambios';
});
}
// Cerrar modal de edición al hacer clic fuera de él
document.addEventListener('click', function(event) {
const modal = document.getElementById('editHoursModal');
if (event.target === modal) {
closeEditHoursModal();
}
});
</script>
{% endblock %}