// ==UserScript==
// @name Asistente de Formularios APEX (Panel Fijo)
// @namespace Violentmonkey Scripts
// @match http://192.168.100.83:8080/apex/f*
// @grant none
// @version 2.1
// @author IKAROS
// @description Añade un panel lateral para gestionar órdenes y rellenar datos, con lista de trabajadores personalizable.
// ==/UserScript==
(function() {
'use strict';
// --- CONFIGURACIÓN ---
const APEX_APP_ID = '100';
const APEX_PAGE_ID = '2010';
const APEX_GO_BACK_PAGE = '2300';
const APEX_STATUS_SELECT_ID = 'P2010_TA1R250_CODIGO';
const APEX_TEXT_AREA_ID = 'P2010_RESPUESTA';
const APEX_STATUS_VALUE_TO_SET = 'RES';
const DEFAULT_TRABAJADORES = ['AGUILERA','BUSTAMANTE','MENDOZA','MERCAU','MERCAU L','MUÑOZ','OJEDA','PEREYRA','PINO','RODRIGUEZ','ROSALES','SALINAS'];
// --- FIN DE LA CONFIGURACIÓN ---
const SESSION_DATE_KEY = 'gm_apex_form_date';
const SESSION_WORKERS_KEY = 'gm_apex_form_workers';
const LOCAL_WORKER_LIST_KEY = 'gm_apex_worker_list';
let toastTimer;
function addStyles() {
const panelWidth = 'auto';
const styles = `
body { margin-right: ${panelWidth} !important; }
#gm-side-panel {
position: fixed; top: 0; right: 0; width: ${panelWidth}; height: 100vh;
background-color: #f7f9fc; border-left: 1px solid #dfe3e8; box-shadow: -3px 0 15px rgba(0,0,0,0.08);
z-index: 9999; padding: 15px; overflow-y: auto; font-family: Arial, sans-serif; box-sizing: border-box;
transition: opacity 0.3s ease, display 0.3s ease;
}
#gm-side-panel.hidden {
opacity: 0;
pointer-events: none;
}
#gm-side-panel h2 {
margin: 0; padding-bottom: 10px; color: #333; border-bottom: 2px solid #007bff;
text-align: center; font-size: 18px; margin-bottom: 15px;
}
#gm-side-panel .gm-section {
background-color: #fff; border: 1px solid #dfe3e8; border-radius: 8px; padding: 15px; margin-bottom: 15px;
}
#gm-side-panel label { display: block; margin-bottom: 5px; font-weight: bold; color: #555; }
#gm-side-panel input[type="date"], #gm-side-panel input[type="number"], #gm-side-panel input[type="text"], #gm-side-panel textarea {
width: 100%; padding: 10px; border-radius: 4px; border: 1px solid #ccc; box-sizing: border-box;
}
#gm-side-panel button {
width: 100%; padding: 12px 15px; border: none; border-radius: 5px; cursor: pointer;
font-weight: bold; font-size: 14px; margin-top: 10px;
}
.gm-input-group { display: flex; gap: 10px; }
#gm-trabajadores-list { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 5px; }
#gm-trabajadores-list label { font-weight: normal; display: flex; align-items: center; justify-content: space-between; }
.gm-worker-controls { display: none; } /* Oculto por defecto */
.gm-worker-controls button { background: none; border: none; cursor: pointer; font-weight: bold; padding: 2px 5px; }
.gm-edit-worker-btn { color: #007bff; padding: 5px !important; }
.gm-remove-worker-btn { color: #dc3545; padding: 5px !important; }
.gm-save-worker-btn { color: #28a745; }
.gm-cancel-edit-btn { color: #6c757d; }
#gm-worker-add-group { display: none; } /* Oculto por defecto */
#gm-manage-worker-section.gm-edit-mode #gm-worker-add-group,
#gm-manage-worker-section.gm-edit-mode .gm-worker-controls {
display: flex; /* Se muestra en modo edición */
}
#gm-btn-cargar-orden { background-color: #17a2b8; color: white; }
#gm-btn-colectora { background-color: #28a745; color: white; }
#gm-btn-anexar { background-color: #007bff; color: white; }
#gm-btn-perdida { background-color: #fd7e14; color: white; }
#gm-btn-toggle-worker-edit { background-color: #6c757d; color: white; }
#gm-btn-add-worker { background-color: #007bff; color: white; }
#gm-toast-notification {
visibility: hidden; opacity: 0; position: fixed; bottom: 20px; left: 50%;
transform: translate(-50%, 20px); background-color: #2c3e50; color: white;
padding: 16px 30px; border-radius: 8px; box-shadow: 0 4px 15px rgba(0,0,0,0.2);
transition: all 0.4s ease-in-out; z-index: 10002;
}
#gm-toast-notification.gm-toast-show { visibility: visible; opacity: 1; transform: translate(-50%, 0); }
`;
const styleSheet = document.createElement("style");
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
}
function createUI() {
const sidePanel = document.createElement('div');
sidePanel.id = 'gm-side-panel';
sidePanel.innerHTML = `
<div class="gm-section">
<label for="gm-orden-input">Cargar Orden</label>
<input type="number" id="gm-orden-input" placeholder="N° de orden...">
<button id="gm-btn-cargar-orden">Cargar Orden</button>
</div>
<div id="gm-manage-worker-section" class="gm-section">
<label>1. Generar Suceso</label>
<input type="date" id="gm-fecha-input" style="margin-bottom: 15px;">
<div id="gm-trabajadores-list"></div>
<div id="gm-worker-add-group" class="gm-input-group" style="margin-top: 15px;">
<input type="text" id="gm-new-worker-input" placeholder="Añadir nombre...">
<button id="gm-btn-add-worker" style="margin-top:0; width:auto; padding:0 15px;">Añadir</button>
</div>
<button id="gm-btn-toggle-worker-edit">Editar Lista</button>
</div>
<div class="gm-section">
<label>2. Aplicar a Página</label>
<div class="gm-input-group" style="margin-top:0;">
<button id="gm-btn-colectora" style="margin-top:0;">Colectora</button>
<button id="gm-btn-anexar" style="margin-top:0;">Reiterado</button>
<button id="gm-btn-perdida" style="margin-top:0;">Pérdida</button>
</div>
</div>
`;
document.body.appendChild(sidePanel);
const toast = document.createElement('div');
toast.id = 'gm-toast-notification';
document.body.appendChild(toast);
rebuildWorkerUI();
}
// --- Lógica de Trabajadores ---
function getWorkers() { const d=localStorage.getItem(LOCAL_WORKER_LIST_KEY); if(d)return JSON.parse(d); localStorage.setItem(LOCAL_WORKER_LIST_KEY,JSON.stringify(DEFAULT_TRABAJADORES)); return DEFAULT_TRABAJADORES; }
function saveWorkerList(workers) { localStorage.setItem(LOCAL_WORKER_LIST_KEY,JSON.stringify(workers)); }
function rebuildWorkerUI() {
const workerListDiv = document.getElementById('gm-trabajadores-list');
workerListDiv.innerHTML = '';
getWorkers().forEach(name => {
const workerLabel = document.createElement('label');
const checkbox = document.createElement('input');
checkbox.type = 'checkbox'; checkbox.value = name; checkbox.style.marginRight = '8px';
const nameSpan = document.createElement('span');
nameSpan.textContent = name; nameSpan.style.flexGrow = '1';
const controlsDiv = document.createElement('div');
controlsDiv.className = 'gm-worker-controls';
const editBtn = document.createElement('button');
editBtn.className = 'gm-edit-worker-btn'; editBtn.innerHTML = '✏️'; editBtn.title = `Editar ${name}`;
editBtn.onclick = () => handleEditWorker(name);
const removeBtn = document.createElement('button');
removeBtn.className = 'gm-remove-worker-btn'; removeBtn.innerHTML = '✖️'; removeBtn.title = `Quitar a ${name}`;
removeBtn.onclick = () => {
if (confirm(`¿Está seguro que desea eliminar a ${name}?`)) {
saveWorkerList(getWorkers().filter(w => w !== name));
rebuildWorkerUI();
}
};
controlsDiv.appendChild(editBtn);
controlsDiv.appendChild(removeBtn);
workerLabel.appendChild(checkbox);
workerLabel.appendChild(nameSpan);
workerLabel.appendChild(controlsDiv);
workerListDiv.appendChild(workerLabel);
});
loadPersistedData();
}
function handleEditWorker(originalName) {
rebuildWorkerUI();
const targetLabel = Array.from(document.querySelectorAll('#gm-trabajadores-list label')).find(label => label.querySelector('span').textContent === originalName);
if (!targetLabel) return;
const nameSpan = targetLabel.querySelector('span');
const controlsDiv = targetLabel.querySelector('.gm-worker-controls');
const editInput = document.createElement('input');
editInput.type = 'text'; editInput.value = originalName; editInput.style.width = '100px';
const saveBtn = document.createElement('button');
saveBtn.className = 'gm-save-worker-btn'; saveBtn.innerHTML = '✔️'; saveBtn.title = 'Guardar';
saveBtn.onclick = () => {
const newName = editInput.value.trim().toUpperCase();
if (!newName) return;
const workers = getWorkers();
if (workers.includes(newName) && newName !== originalName) {
showToast(`El nombre "${newName}" ya existe.`);
return;
}
saveWorkerList(workers.map(w => w === originalName ? newName : w));
rebuildWorkerUI();
};
const cancelBtn = document.createElement('button');
cancelBtn.className = 'gm-cancel-edit-btn'; cancelBtn.innerHTML = '✖️'; cancelBtn.title = 'Cancelar';
cancelBtn.onclick = () => rebuildWorkerUI();
nameSpan.style.display = 'none';
controlsDiv.innerHTML = '';
controlsDiv.appendChild(saveBtn);
controlsDiv.appendChild(cancelBtn);
targetLabel.insertBefore(editInput, controlsDiv);
editInput.focus();
}
// --- Lógica Principal y de Utilidad ---
function loadOrderPage() { const d=document.getElementById("gm-orden-input").value.trim(); if(!d){showToast("Por favor, ingrese un número de orden.");return}const e=document.getElementById("pInstance")?.value; if(!e){showToast("Error: No se pudo encontrar el ID de sesión de APEX.");return}const f=`/apex/f?p=${APEX_APP_ID}:${APEX_PAGE_ID}:${e}:UPDATE:NO:${APEX_PAGE_ID}:P${APEX_PAGE_ID}_NUMERO,P${APEX_PAGE_ID}_GO_BACK:${d},${APEX_GO_BACK_PAGE}`; window.location.href=f; }
function applyValuesToPage(text, append = false) {
const statusSelect = document.getElementById(APEX_STATUS_SELECT_ID);
const descriptionTextarea = document.getElementById(APEX_TEXT_AREA_ID);
if (statusSelect) {
statusSelect.value = APEX_STATUS_VALUE_TO_SET;
statusSelect.dispatchEvent(new Event('change', { bubbles: true }));
statusSelect.style.color = 'red';
}
if (descriptionTextarea) {
if (append && descriptionTextarea.value.trim() !== '') {
descriptionTextarea.value += `\n${text}`;
} else {
descriptionTextarea.value = text;
}
descriptionTextarea.dispatchEvent(new Event('input', { bubbles: true }));
descriptionTextarea.style.color = 'red';
}
showToast('✅ ¡Texto aplicado!');
}
function generateColectoraText() {
const fechaInput = document.getElementById('gm-fecha-input').value;
if (!fechaInput) {
showToast('Por favor, seleccione una fecha.');
return null;
}
const checkboxes = document.querySelectorAll('#gm-trabajadores-list input:checked');
if (checkboxes.length === 0) {
showToast('Seleccione al menos un trabajador.');
return null;
}
const fecha = new Date(fechaInput + 'T00:00:00');
const fechaFormateada = fecha.toLocaleDateString('es-AR', { day: '2-digit', month: '2-digit', year: 'numeric' });
const nombres = Array.from(checkboxes).map(cb => cb.value).join(', ');
return `COLECTORA NORMALIZADA - ${nombres} ${fechaFormateada}.`;
}
function generatePerdidaText() {
const fechaInput = document.getElementById('gm-fecha-input').value;
if (!fechaInput) {
showToast('Por favor, seleccione una fecha.');
return null;
}
const checkboxes = document.querySelectorAll('#gm-trabajadores-list input:checked');
if (checkboxes.length === 0) {
showToast('Seleccione al menos un trabajador.');
return null;
}
const fecha = new Date(fechaInput + 'T00:00:00');
const fechaFormateada = fecha.toLocaleDateString('es-AR', { day: '2-digit', month: '2-digit', year: 'numeric' });
const nombres = Array.from(checkboxes).map(cb => cb.value).join(', ');
return `PERDIDA DE AGUA - ${nombres} ${fechaFormateada}.`;
}
function handleColectora() {
const textoFinal = generateColectoraText();
if (textoFinal) {
applyValuesToPage(textoFinal, false); // Sobrescribir
}
}
function handleAnexar() {
const textoFinal = generateColectoraText();
if (textoFinal) {
applyValuesToPage(textoFinal, true); // Anexar
}
}
function handlePerdida() {
const textoFinal = generatePerdidaText();
applyValuesToPage(textoFinal, false); // Sobrescribir
}
function showToast(message) { const e=document.getElementById("gm-toast-notification"); e.textContent=message; e.classList.add("gm-toast-show"); clearTimeout(toastimer); toastimer=setTimeout(()=>{e.classList.remove("gm-toast-show")},3e3); }
function saveDate() { sessionStorage.setItem(SESSION_DATE_KEY,document.getElementById("gm-fecha-input").value); }
function saveWorkersSelection() { const d=Array.from(document.querySelectorAll("#gm-trabajadores-list input:checked")).map(e=>e.value); sessionStorage.setItem(SESSION_WORKERS_KEY,JSON.stringify(d)); }
function loadPersistedData() { const d=sessionStorage.getItem(SESSION_DATE_KEY); if(d)document.getElementById("gm-fecha-input").value=d; const e=sessionStorage.getItem(SESSION_WORKERS_KEY); if(e){const f=JSON.parse(e); document.querySelectorAll("#gm-trabajadores-list input").forEach(g=>{g.checked=f.includes(g.value)})} }
// --- Event Listeners ---
function addEventListeners() {
document.getElementById('gm-btn-cargar-orden').addEventListener('click', loadOrderPage);
document.getElementById('gm-orden-input').addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); loadOrderPage(); } });
document.getElementById('gm-btn-colectora').addEventListener('click', handleColectora);
document.getElementById('gm-btn-anexar').addEventListener('click', handleAnexar);
document.getElementById('gm-btn-perdida').addEventListener('click', handlePerdida);
const manageSection = document.getElementById('gm-manage-worker-section');
const toggleBtn = document.getElementById('gm-btn-toggle-worker-edit');
toggleBtn.addEventListener('click', () => {
manageSection.classList.toggle('gm-edit-mode');
if (manageSection.classList.contains('gm-edit-mode')) {
toggleBtn.textContent = 'Finalizar Edición';
toggleBtn.style.backgroundColor = '#28a745';
} else {
toggleBtn.textContent = 'Editar Lista';
toggleBtn.style.backgroundColor = '#6c757d';
rebuildWorkerUI();
}
});
document.getElementById('gm-btn-add-worker').addEventListener('click', () => {
const input = document.getElementById('gm-new-worker-input');
const newName = input.value.trim().toUpperCase();
if (newName) {
const workers = getWorkers();
if (workers.includes(newName)) { showToast(`"${newName}" ya existe.`); }
else {
workers.push(newName); saveWorkerList(workers); rebuildWorkerUI(); input.value = '';
showToast(`"${newName}" fue añadido.`);
}
}
});
document.getElementById('gm-fecha-input').addEventListener('change', saveDate);
document.getElementById('gm-trabajadores-list').addEventListener('change', saveWorkersSelection);
}
// --- Ejecución Principal con Detección de Logout ---
function main() {
addStyles();
createUI();
addEventListeners();
const sidePanel = document.getElementById('gm-side-panel');
if (!sidePanel) return;
const checkLogoutLink = () => {
const logoutLinks = Array.from(document.querySelectorAll('a[href]'))
.filter(a => a.href.includes('apex_authentication.logout'));
if (logoutLinks.length > 0) {
sidePanel.classList.remove('hidden');
sidePanel.style.display = 'block';
} else {
sidePanel.classList.add('hidden');
setTimeout(() => {
sidePanel.style.display = 'none';
}, 300);
}
};
checkLogoutLink();
const observer = new MutationObserver(() => {
checkLogoutLink();
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: false,
characterData: false
});
window.addEventListener('popstate', checkLogoutLink);
window.addEventListener('hashchange', checkLogoutLink);
}
main();
})();