2ª actualización importante

This commit is contained in:
naielv
2025-12-14 20:14:33 +01:00
parent 1bc9aa5295
commit 3402183f3c
29 changed files with 130 additions and 623 deletions

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12,5.5A3.5,3.5 0 0,1 15.5,9A3.5,3.5 0 0,1 12,12.5A3.5,3.5 0 0,1 8.5,9A3.5,3.5 0 0,1 12,5.5M5,8C5.56,8 6.08,8.15 6.53,8.42C6.38,9.85 6.8,11.27 7.66,12.38C7.16,13.34 6.16,14 5,14A3,3 0 0,1 2,11A3,3 0 0,1 5,8M19,8A3,3 0 0,1 22,11A3,3 0 0,1 19,14C17.84,14 16.84,13.34 16.34,12.38C17.2,11.27 17.62,9.85 17.47,8.42C17.92,8.15 18.44,8 19,8M5.5,18.25C5.5,16.18 8.41,14.5 12,14.5C15.59,14.5 18.5,16.18 18.5,18.25V20H5.5V18.25M0,20V18.5C0,17.11 1.89,15.94 4.45,15.6C3.86,16.28 3.5,17.22 3.5,18.25V20H0M24,20H20.5V18.25C20.5,17.22 20.14,16.28 19.55,15.6C22.11,15.94 24,17.11 24,18.5V20Z" /></svg>

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M11,9H9V2H7V9H5V2H3V9C3,11.12 4.66,12.84 6.75,12.97V22H9.25V12.97C11.34,12.84 13,11.12 13,9V2H11V9M16,6V14H18.5V22H21V2C18.24,2 16,4.24 16,6Z" /></svg>

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -196,6 +196,7 @@ table {
table tr th {
line-break: auto;
padding: 2px 5px;
}
table tr td {
@@ -365,7 +366,7 @@ hr {
.ribbon-panel {
display: flex;
gap: 10px;
gap: 3px;
background-color: #c8d4eb;
border: 1px solid #a2a9b9;
overflow-x: auto;
@@ -382,10 +383,15 @@ hr {
white-space: nowrap;
margin: 0;
padding: 0;
border: 1px solid lightskyblue;
background: white;
padding: 4px;
display: inline-block;
border-radius: 10px;
}
.ribbon-button img {
height: 48px;
height: 60px;
display: block;
margin: auto;
}

View File

@@ -1131,7 +1131,7 @@ function SetPages() {
a.href = "#" + key;
label.innerText = PAGES[key].Title;
label.className = "label";
img.src = PAGES[key].icon || "static/appico/File_Plugin.svg";
img.src = PAGES[key].icon || "static/appico/application_enterprise.png";
a.append(img, label);
document.getElementById("appendApps2").append(a);
});
@@ -1142,7 +1142,7 @@ function SetPages() {
a.href = "#index,qr";
label.innerText = "Escanear QR";
label.className = "label";
img.src = "static/appico/App_CodyCam.svg";
img.src = "static/appico/barcode.png";
a.append(img, label);
document.getElementById("appendApps2").append(a);
}

View File

@@ -2,7 +2,7 @@ PERMS["aulas"] = "Aulas (Solo docentes!)";
PAGES.aulas = {
//navcss: "btn1",
Title: "Gest-Aula",
icon: "static/appico/Classroom.svg",
icon: "static/appico/components.png",
AccessControl: true,
index: function () {
if (!checkRole("aulas")) {

View File

@@ -1,6 +1,6 @@
PAGES.buscar = {
navcss: "btn1",
icon: "static/appico/File_Plugin.svg",
icon: "static/appico/view.svg",
Title: "Buscar",
AccessControl: true,
Esconder: true,

View File

@@ -1,536 +0,0 @@
PERMS["chat"] = "Chat"
PERMS["chat:edit"] = "&gt; Escribir"
// Global chat notification system
PAGES.chat_notifications = {
listener: null,
Esconder: true,
lastMessageTime: null, // Start with null to allow all messages initially
isActive: false,
init: function() {
console.log("Initializing chat notifications...");
console.log("isActive:", this.isActive);
console.log("checkRole available:", typeof checkRole);
console.log("checkRole('chat'):", typeof checkRole === 'function' ? checkRole("chat") : "function not available");
if (this.isActive) {
console.log("Chat notifications already active");
return;
}
// Check if user has chat permissions (with fallback)
if (typeof checkRole === 'function' && !checkRole("chat")) {
console.log("No chat permissions");
return;
}
this.isActive = true;
console.log("Starting chat notifications listener");
var today = new Date().toISOString().split('T')[0];
var dayPath = `chat_${today}`;
console.log("Listening for notifications on:", dayPath);
console.log("TABLE:", TABLE);
// Set initial timestamp to current time to only notify for new messages from now on
if (!this.lastMessageTime) {
this.lastMessageTime = new Date().toISOString();
console.log("Set initial lastMessageTime:", this.lastMessageTime);
}
// Listen for new messages on today's chat
this.listener = gun.get(TABLE).get(dayPath).map().on((data, messageId, _msg, _ev) => {
console.log("Notification listener received data:", data, "messageId:", messageId);
if (data === null) return; // Ignore deletions
if (typeof data === "string") {
// Encrypted message
console.log("Decrypting notification message...");
TS_decrypt(data, SECRET, (decryptedData) => {
console.log("Decrypted notification data:", decryptedData);
this.handleNewMessage(decryptedData, messageId);
});
} else {
// Unencrypted message
console.log("Processing unencrypted notification:", data);
this.handleNewMessage(data, messageId);
}
});
// Add to cleanup listeners
EventListeners.GunJS.push(this.listener);
console.log("Chat notifications initialized successfully");
},
handleNewMessage: function(messageData, messageId) {
console.log("Handling new message for notification:", messageData);
console.log("Current lastMessageTime:", this.lastMessageTime);
console.log("Message timestamp:", messageData.timestamp);
console.log("Message authorId:", messageData.authorId);
console.log("Current user ID:", SUB_LOGGED_IN_ID);
// Don't notify for our own messages
if (messageData.authorId === SUB_LOGGED_IN_ID) {
console.log("Skipping notification - own message");
return;
}
// Don't notify for old messages (only if we have a baseline)
if (this.lastMessageTime && messageData.timestamp && messageData.timestamp <= this.lastMessageTime) {
console.log("Skipping notification - old message");
return;
}
// Don't notify if user is currently viewing the chat page
var currentHash = location.hash.replace("#", "");
console.log("Current page hash:", currentHash);
if (currentHash === 'chat') {
console.log("Skipping notification - user viewing chat");
return;
}
// Update last message time
if (messageData.timestamp) {
this.lastMessageTime = messageData.timestamp;
console.log("Updated lastMessageTime to:", this.lastMessageTime);
}
// Show notification
var author = messageData.author || 'Usuario Anónimo';
var preview = messageData.text.length > 50 ?
messageData.text.substring(0, 50) + '...' :
messageData.text;
console.log("Showing notification for:", author, "-", preview);
console.log("Testing toastr availability:", typeof toastr);
// Test notification to verify toastr works
console.log("Attempting to show notification...");
toastr.info(
`<strong>${author}:</strong><br>${preview}`,
'💬 Nuevo mensaje en Chat',
{
onclick: function() {
console.log("Notification clicked, navigating to chat");
setUrlHash('chat');
},
timeOut: 8000,
extendedTimeOut: 2000,
closeButton: true,
progressBar: true,
positionClass: 'toast-top-right',
preventDuplicates: true,
newestOnTop: true,
escapeHtml: false
}
);
var msg = `Nuevo mensaje en chat de ${author}. ${preview}`;
let utterance = new SpeechSynthesisUtterance(msg);
utterance.rate = 0.9;
speechSynthesis.speak(utterance);
console.log("Notification call completed");
},
destroy: function() {
if (this.listener) {
this.listener.off();
}
this.isActive = false;
},
// Test function to manually trigger a notification
testNotification: function() {
console.log("Testing notification manually...");
console.log("toastr available:", typeof toastr);
toastr.success("¡Funciona! Esta es una notificación de prueba.", "Test de Notificación", {
timeOut: 5000,
closeButton: true,
progressBar: true,
positionClass: 'toast-top-right'
});
}
};
PAGES.chat = {
navcss: "btn4",
icon: "static/appico/Chat.svg",
AccessControl: true,
Title: "Chat",
index: function() {
console.log("Chat index function called");
console.log("SUB_LOGGED_IN:", SUB_LOGGED_IN);
console.log("checkRole function exists:", typeof checkRole);
if (!checkRole("chat")) {
console.log("No chat permission, redirecting to index");
setUrlHash("index");
return;
}
console.log("Chat permission granted, initializing chat");
// Stop global notifications when viewing chat
PAGES.chat_notifications.destroy();
var messagesList = safeuuid();
var messageInput = safeuuid();
var sendButton = safeuuid();
var daySelector = safeuuid();
var currentDay = new Date().toISOString().split('T')[0]; // YYYY-MM-DD format
console.log("Creating chat UI with currentDay:", currentDay);
container.innerHTML = `
<h1>💬 Chat - ${GROUPID}</h1>
<div style="margin-bottom: 15px;">
<label for="${daySelector}">Seleccionar día:</label>
<input type="date" id="${daySelector}" value="${currentDay}"
style="margin-left: 10px; padding: 5px;">
<button onclick="PAGES.chat.loadDay()" style="margin-left: 10px;">Cargar</button>
</div>
<div style="border: 2px solid #ccc; border-radius: 8px; padding: 10px;
height: 400px; overflow-y: auto; background-color: #f9f9f9;
margin-bottom: 15px;" id="chat-container">
<ul id="${messagesList}" style="list-style: none; padding: 0; margin: 0;">
<!-- Los mensajes aparecerán aquí -->
</ul>
</div>
<div style="display: flex; gap: 10px; align-items: center;">
<input type="text" id="${messageInput}" placeholder="Escribe tu mensaje..."
style="flex: 1; padding: 10px; border: 1px solid #ccc; border-radius: 4px;"
onkeypress="if(event.key==='Enter') document.getElementById('${sendButton}').click()">
<button id="${sendButton}" class="btn5" style="padding: 10px 20px;">
Enviar
</button>
</div>
<div style="margin-top: 10px; font-size: 12px; color: #666;">
<strong>Usuario:</strong> ${SUB_LOGGED_IN_DETAILS.Nombre || 'Usuario Anónimo'}
| <strong>Conectado como:</strong> ${GROUPID}
</div>
`;
// Store element references
PAGES.chat.messagesList = document.getElementById(messagesList);
PAGES.chat.messageInput = document.getElementById(messageInput);
PAGES.chat.sendButton = document.getElementById(sendButton);
PAGES.chat.daySelector = document.getElementById(daySelector);
PAGES.chat.currentDay = currentDay;
// Set up event listeners
PAGES.chat.sendButton.onclick = PAGES.chat.sendMessage;
// Load today's messages
PAGES.chat.loadDay();
},
loadDay: function() {
var selectedDay = PAGES.chat.daySelector.value;
PAGES.chat.currentDay = selectedDay;
console.log("Loading chat for day:", selectedDay);
console.log("TABLE:", TABLE);
console.log("SECRET:", SECRET ? "SET" : "NOT SET");
// Clear current messages
PAGES.chat.messagesList.innerHTML = '<li style="text-align: center; color: #666; padding: 10px;">Cargando mensajes...</li>';
// Clear any existing listeners
if (PAGES.chat.currentListener) {
PAGES.chat.currentListener.off();
}
// Listen for messages from the selected day
var dayPath = `chat_${selectedDay}`;
console.log("Listening on path:", dayPath);
PAGES.chat.currentListener = gun.get(TABLE).get(dayPath).map().on((data, messageId, _msg, _ev) => {
console.log("Received chat data:", data, "messageId:", messageId);
if (data === null) {
// Message deleted
PAGES.chat.removeMessage(messageId);
return;
}
if (typeof data === "string") {
// Encrypted message
console.log("Decrypting message...");
TS_decrypt(data, SECRET, (decryptedData) => {
console.log("Decrypted data:", decryptedData);
PAGES.chat.displayMessage(decryptedData, messageId);
});
} else {
// Unencrypted message (shouldn't happen in production)
console.log("Displaying unencrypted message:", data);
PAGES.chat.displayMessage(data, messageId);
}
});
// Add listener to cleanup
EventListeners.GunJS.push(PAGES.chat.currentListener);
// Clear loading message after a short delay
setTimeout(() => {
var loadingMsg = PAGES.chat.messagesList.querySelector('li');
if (loadingMsg && loadingMsg.textContent.includes('Cargando')) {
console.log("Clearing loading message");
loadingMsg.remove();
}
}, 1000);
},
sendMessage: function() {
var messageText = PAGES.chat.messageInput.value.trim();
console.log("Sending message:", messageText);
console.log("User details:", SUB_LOGGED_IN_DETAILS);
console.log("User ID:", SUB_LOGGED_IN_ID);
if (!messageText) {
toastr.warning("Por favor escribe un mensaje");
return;
}
if (!checkRole("chat:edit")) {
toastr.error("No tienes permisos para escribir en el chat");
console.log("Permission denied for chat:edit");
return;
}
// Create message object
var messageData = {
text: messageText,
author: SUB_LOGGED_IN_DETAILS.Nombre || 'Usuario Anónimo',
authorId: SUB_LOGGED_IN_ID || 'unknown',
timestamp: new Date().toISOString(),
day: PAGES.chat.currentDay
};
console.log("Message data:", messageData);
// Generate unique message ID
var messageId = safeuuid();
console.log("Generated message ID:", messageId);
// Encrypt and save message
TS_encrypt(messageData, SECRET, (encrypted) => {
var dayPath = `chat_${PAGES.chat.currentDay}`;
console.log("Saving to path:", dayPath);
console.log("Encrypted data:", encrypted);
betterGunPut(gun.get(TABLE).get(dayPath).get(messageId), encrypted);
// Clear input
PAGES.chat.messageInput.value = '';
// Show success feedback
toastr.success("Mensaje enviado");
});
},
displayMessage: function(messageData, messageId) {
// Check if message already exists
var existingMessage = document.getElementById(`msg-${messageId}`);
if (existingMessage) {
existingMessage.remove();
}
// Create message element
var messageItem = document.createElement('li');
messageItem.id = `msg-${messageId}`;
messageItem.style.cssText = `
margin-bottom: 10px;
padding: 10px;
border-radius: 8px;
background-color: white;
border-left: 4px solid #007cba;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
`;
// Format timestamp
var timestamp = new Date(messageData.timestamp);
var timeString = timestamp.toLocaleTimeString('es-ES', {
hour: '2-digit',
minute: '2-digit'
});
// Check if message is from current user
var isOwnMessage = messageData.authorId === SUB_LOGGED_IN_ID;
var messageColor = isOwnMessage ? '#e3f2fd' : 'white';
var borderColor = isOwnMessage ? '#2196f3' : '#007cba';
messageItem.style.backgroundColor = messageColor;
messageItem.style.borderLeftColor = borderColor;
// Create message content
var messageContent = `
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 5px;">
<strong style="color: #333; font-size: 14px;">${messageData.author}</strong>
<span style="color: #666; font-size: 12px;">${timeString}</span>
</div>
<div style="color: #444; line-height: 1.4;">
${messageData.text.replace(/\n/g, '<br>')}
</div>
`;
// Add delete button for own messages or if user has admin permissions
if (isOwnMessage || checkRole("admin")) {
messageContent += `
<div style="text-align: right; margin-top: 8px;">
<button onclick="PAGES.chat.deleteMessage('${messageId}')"
style="background: none; border: none; color: #f44336;
cursor: pointer; font-size: 12px; padding: 2px 5px;"
title="Borrar mensaje">
🗑️ Borrar
</button>
</div>
`;
}
messageItem.innerHTML = messageContent;
// Insert message in chronological order
var inserted = false;
var existingMessages = Array.from(PAGES.chat.messagesList.children);
for (var i = 0; i < existingMessages.length; i++) {
var existingMsg = existingMessages[i];
var existingId = existingMsg.id.replace('msg-', '');
var existingData = PAGES.chat.messageCache && PAGES.chat.messageCache[existingId];
if (existingData && new Date(messageData.timestamp) < new Date(existingData.timestamp)) {
PAGES.chat.messagesList.insertBefore(messageItem, existingMsg);
inserted = true;
break;
}
}
if (!inserted) {
PAGES.chat.messagesList.appendChild(messageItem);
}
// Cache message data for sorting
if (!PAGES.chat.messageCache) {
PAGES.chat.messageCache = {};
}
PAGES.chat.messageCache[messageId] = messageData;
// Auto-scroll to bottom if user is viewing current day
var today = new Date().toISOString().split('T')[0];
if (PAGES.chat.currentDay === today) {
var chatContainer = document.getElementById('chat-container');
chatContainer.scrollTop = chatContainer.scrollHeight;
}
},
removeMessage: function(messageId) {
var messageElement = document.getElementById(`msg-${messageId}`);
if (messageElement) {
messageElement.remove();
}
// Remove from cache
if (PAGES.chat.messageCache) {
delete PAGES.chat.messageCache[messageId];
}
},
deleteMessage: function(messageId) {
if (confirm("¿Estás seguro de que quieres borrar este mensaje?")) {
var dayPath = `chat_${PAGES.chat.currentDay}`;
betterGunPut(gun.get(TABLE).get(dayPath).get(messageId), null);
toastr.success("Mensaje borrado");
}
},
// Cleanup when leaving the page
cleanup: function() {
if (PAGES.chat.currentListener) {
PAGES.chat.currentListener.off();
}
PAGES.chat.messageCache = {};
// Restart global notifications when leaving chat
setTimeout(() => {
PAGES.chat_notifications.init();
}, 1000);
}
};
// Add cleanup to the global page change handler
(function() {
var originalOpenPage = open_page;
open_page = function(params) {
// Cleanup chat if we're leaving it
if (PAGES.chat && PAGES.chat.cleanup) {
PAGES.chat.cleanup();
}
return originalOpenPage(params);
};
})();
// Initialize global chat notifications when user is logged in
(function() {
function initChatNotifications() {
console.log("Attempting to initialize chat notifications...");
console.log("SUB_LOGGED_IN:", SUB_LOGGED_IN);
console.log("checkRole available:", typeof checkRole);
// More robust check for login status and permissions
if (SUB_LOGGED_IN === true) {
console.log("User is logged in, checking permissions...");
if (typeof checkRole === 'function') {
var hasPermission = checkRole("chat");
console.log("User has chat permission:", hasPermission);
if (hasPermission) {
setTimeout(() => {
console.log("Calling PAGES.chat_notifications.init()");
PAGES.chat_notifications.init();
}, 2000);
}
} else {
// Fallback if checkRole is not available yet
console.log("checkRole not available, trying to initialize anyway...");
setTimeout(() => {
PAGES.chat_notifications.init();
}, 3000);
}
}
}
// Try to initialize immediately if already logged in
console.log("Initial check for chat notifications...");
if (typeof SUB_LOGGED_IN !== 'undefined') {
initChatNotifications();
}
// Also listen for login events by checking periodically
var initInterval = setInterval(() => {
if (SUB_LOGGED_IN === true && !PAGES.chat_notifications.isActive) {
console.log("Periodic check - attempting notification init");
initChatNotifications();
}
// Stop checking after user is logged in and notifications are active
if (SUB_LOGGED_IN === true && PAGES.chat_notifications.isActive) {
console.log("Notifications active, stopping periodic checks");
clearInterval(initInterval);
}
}, 3000);
// Also try to initialize when page loads
setTimeout(() => {
if (!PAGES.chat_notifications.isActive) {
console.log("Final attempt to initialize notifications");
initChatNotifications();
}
}, 10000);
})();

View File

@@ -1,10 +1,10 @@
PERMS["comedor"] = "Menú comedor"
PERMS["comedor"] = "Comedor"
PERMS["comedor:edit"] = "&gt; Editar"
PAGES.comedor = {
navcss: "btn6",
icon: "static/appico/Meal.svg",
icon: "static/appico/apple.png",
AccessControl: true,
Title: "Menú comedor",
Title: "Comedor",
edit: function (mid) {
if (!checkRole("comedor:edit")) {setUrlHash("comedor");return}
var nameh1 = safeuuid();

View File

@@ -1,6 +1,6 @@
PAGES.dataman = {
navcss: "btn1",
icon: "static/appico/Cogs.svg",
icon: "static/appico/gear_edit.png",
AccessControl: true,
Title: "Ajustes",
edit: function (mid) {

View File

@@ -1,10 +1,11 @@
PAGES.index = {
//navcss: "btn1",
Title: "Inicio",
icon: "static/appico/house.png",
index: function() {
container.innerHTML = `
<h1>¡Hola, ${SUB_LOGGED_IN_DETAILS.Nombre}!<br>Bienvenidx a %%TITLE%%</h1>
<h2>Tienes ${parseFloat(SUB_LOGGED_IN_DETAILS.Monedero_Balance).toString()} € en el monedero.</h2>
<h2>Tienes ${fixfloat(parseFloat(SUB_LOGGED_IN_DETAILS.Monedero_Balance)).toString()} € en el monedero.</h2>
<em>Utiliza el menú superior para abrir un modulo</em>
<br><br>
<button class="btn1" onclick="LogOutTeleSec()">Cerrar sesión</button>

View File

@@ -1,29 +1,33 @@
PERMS["materiales"] = "Materiales"
PERMS["materiales:edit"] = "&gt; Editar"
PERMS["materiales"] = "Almacén";
PERMS["materiales:edit"] = "&gt; Editar";
PAGES.materiales = {
navcss: "btn2",
icon: "static/appico/App_Dropbox.svg",
icon: "static/appico/shelf.png",
AccessControl: true,
Title: "Materiales",
Title: "Almacén",
edit: function (mid) {
if (!checkRole("materiales:edit")) {setUrlHash("materiales");return}
if (!checkRole("materiales:edit")) {
setUrlHash("materiales");
return;
}
var nameh1 = safeuuid();
var field_nombre = safeuuid();
var field_revision = safeuuid();
var field_cantidad = safeuuid();
var field_unidad = safeuuid();
var field_cantidad_min = safeuuid();
var field_ubicacion = safeuuid();
var field_referencia = safeuuid();
var field_notas = safeuuid();
var btn_guardar = safeuuid();
var btn_borrar = safeuuid();
var FECHA_ISO = new Date().toISOString().split("T")[0];
container.innerHTML = `
<h1>Material <code id="${nameh1}"></code></h1>
${BuildQR("materiales," + mid, "Este Material")}
<fieldset>
<label>
Referencia<br>
<input type="text" id="${field_referencia}" value="?"><br><br>
Fecha Revisión<br>
<input type="date" id="${field_revision}"> <a onclick='document.getElementById("${field_revision}").value = "${FECHA_ISO}";'>Hoy - Contado todas las existencias</a><br><br>
</label>
<label>
Nombre<br>
@@ -61,15 +65,16 @@ PAGES.materiales = {
function load_data(data, ENC = "") {
document.getElementById(nameh1).innerText = key;
document.getElementById(field_nombre).value = data["Nombre"] || "";
document.getElementById(field_unidad).value = data["Unidad"] || "unidad(es)";
document.getElementById(field_unidad).value =
data["Unidad"] || "unidad(es)";
document.getElementById(field_cantidad).value =
data["Cantidad"] || "";
document.getElementById(field_cantidad_min).value =
data["Cantidad_Minima"] || "";
document.getElementById(field_ubicacion).value =
data["Ubicacion"] || "-";
document.getElementById(field_referencia).value =
data["Referencia"] || "?";
document.getElementById(field_revision).value =
data["Revision"] || "-";
document.getElementById(field_notas).value = data["Notas"] || "";
}
if (typeof data == "string") {
@@ -87,7 +92,7 @@ PAGES.materiales = {
Cantidad: document.getElementById(field_cantidad).value,
Cantidad_Minima: document.getElementById(field_cantidad_min).value,
Ubicacion: document.getElementById(field_ubicacion).value,
Referencia: document.getElementById(field_referencia).value,
Revision: document.getElementById(field_revision).value,
Notas: document.getElementById(field_notas).value,
};
var enc = TS_encrypt(data, SECRET, (encrypted) => {
@@ -111,12 +116,20 @@ PAGES.materiales = {
};
},
index: function () {
if (!checkRole("materiales")) {setUrlHash("index");return}
if (!checkRole("materiales")) {
setUrlHash("index");
return;
}
var btn_new = safeuuid();
var select_ubicacion = safeuuid();
var check_lowstock = safeuuid();
var tableContainer = safeuuid();
container.innerHTML = `
<h1>Materiales</h1>
<h1>Materiales del Almacén</h1>
<label>
<b>Solo lo que falta:</b>
<input type="checkbox" id="${check_lowstock}" style="height: 25px;width: 25px;">
</label><br>
<label>Filtrar por ubicación:
<select id="${select_ubicacion}">
<option value="">(Todas)</option>
@@ -127,62 +140,68 @@ PAGES.materiales = {
`;
const config = [
{ key: "Referencia", label: "Referencia", type: "text", default: "?" },
{ key: "Nombre", label: "Nombre", type: "text", default: "?" },
{ key: "Ubicacion", label: "Ubicación", type: "text", default: "?" },
{
key: "Cantidad",
label: "Cantidad",
{ key: "Revision", label: "F. Revisión", type: "fecha", default: "" },
{ key: "Nombre", label: "Nombre", type: "text", default: "" },
{ key: "Ubicacion", label: "Ubicación", type: "text", default: "--" },
{
key: "Cantidad",
label: "Cantidad",
type: "template",
template: (data, element) => {
const min = parseFloat(data.Cantidad_Minima);
const act = parseFloat(data.Cantidad);
const style = act < min ? 'style="background-color: lightcoral;"' : '';
element.setAttribute("style", style);
element.innerHTML = `${data.Cantidad || "?"} ${data.Unidad || "?"} - (min. ${data.Cantidad_Minima || "?"})`;
const sma = act < min ? `<small>- min. ${data.Cantidad_Minima || "?"}</small>` : ""
element.innerHTML = `${data.Cantidad || "?"} ${
data.Unidad || "?"
} ${sma}`;
},
default: "?"
default: "?",
},
{ key: "Notas", label: "Notas", type: "text", default: "?" }
{ key: "Notas", label: "Notas", type: "text", default: "" },
];
// Obtener todas las ubicaciones únicas y poblar el <select>, desencriptando si es necesario
gun.get(TABLE).get("materiales").map().once((data, key) => {
try {
if (!data) return;
gun
.get(TABLE)
.get("materiales")
.map()
.once((data, key) => {
try {
if (!data) return;
function addUbicacion(d) {
const ubicacion = d.Ubicacion || "-";
const select = document.getElementById(select_ubicacion);
function addUbicacion(d) {
const ubicacion = d.Ubicacion || "-";
const select = document.getElementById(select_ubicacion);
if (!select) {
console.warn(`Element with ID "${select_ubicacion}" not found.`);
return;
}
const optionExists = Array.from(select.options).some(opt => opt.value === ubicacion);
if (!optionExists) {
const option = document.createElement("option");
option.value = ubicacion;
option.textContent = ubicacion;
select.appendChild(option);
}
}
if (typeof data === "string") {
TS_decrypt(data, SECRET, (dec) => {
if (dec && typeof dec === "object") {
addUbicacion(dec);
if (!select) {
console.warn(`Element with ID "${select_ubicacion}" not found.`);
return;
}
});
} else {
addUbicacion(data);
}
} catch (error) {
console.warn("Error processing ubicacion:", error);
}
});
const optionExists = Array.from(select.options).some(
(opt) => opt.value === ubicacion
);
if (!optionExists) {
const option = document.createElement("option");
option.value = ubicacion;
option.textContent = ubicacion;
select.appendChild(option);
}
}
if (typeof data === "string") {
TS_decrypt(data, SECRET, (dec) => {
if (dec && typeof dec === "object") {
addUbicacion(dec);
}
});
} else {
addUbicacion(data);
}
} catch (error) {
console.warn("Error processing ubicacion:", error);
}
});
// Función para renderizar la tabla filtrada
function renderTable(filtroUbicacion) {
@@ -191,15 +210,29 @@ PAGES.materiales = {
config,
gun.get(TABLE).get("materiales"),
document.getElementById(tableContainer),
function(data, new_tr) {
function (data, new_tr) {
if (parseFloat(data.Cantidad) < parseFloat(data.Cantidad_Minima)) {
new_tr.style.background = "lightcoral"
new_tr.style.background = "#fcfcb0";
}
if (parseFloat(data.Cantidad) <= 0) {
new_tr.style.background = "#ffc0c0";
}
if ((data.Cantidad || "?") == "?") {
new_tr.style.background = "#d0d0ff";
}
if ((data.Revision || "?") == "?") {
new_tr.style.background = "#d0d0ff";
}
},
function(data) {
if (data.Ubicacion == filtroUbicacion) {return false}
if (filtroUbicacion == "") {return false}
return true
function (data) {
var is_low_stock =
!document.getElementById(check_lowstock).checked ||
parseFloat(data.Cantidad) < parseFloat(data.Cantidad_Minima);
var is_region =
filtroUbicacion === "" || data.Ubicacion === filtroUbicacion;
return !(is_low_stock && is_region);
}
);
}
@@ -211,9 +244,13 @@ PAGES.materiales = {
document.getElementById(select_ubicacion).onchange = function () {
renderTable(this.value);
};
// Recargar al cambiar filtro
document.getElementById(check_lowstock).onchange = function () {
renderTable(document.getElementById(select_ubicacion).value);
};
if (!checkRole("materiales:edit")) {
document.getElementById(btn_new).style.display = "none"
document.getElementById(btn_new).style.display = "none";
} else {
document.getElementById(btn_new).onclick = () => {
setUrlHash("materiales," + safeuuid(""));

View File

@@ -2,7 +2,7 @@ PERMS["notas"] = "Notas"
PERMS["notas:edit"] = "&gt; Editar"
PAGES.notas = {
navcss: "btn5",
icon: "static/appico/Notepad.svg",
icon: "static/appico/edit.png",
AccessControl: true,
Title: "Notas",
edit: function (mid) {

View File

@@ -2,7 +2,7 @@ PERMS["pagos"] = "Pagos";
PERMS["pagos:edit"] = "&gt; Editar";
PAGES.pagos = {
navcss: "btn7",
icon: "static/appico/Database.svg",
icon: "static/appico/credit_cards.png",
AccessControl: true,
Title: "Pagos",

View File

@@ -2,7 +2,7 @@ PERMS["personas"] = "Personas";
PERMS["personas:edit"] = "&gt; Editar";
PAGES.personas = {
navcss: "btn3",
icon: "static/appico/File_Person.svg",
icon: "static/appico/users.png",
AccessControl: true,
Title: "Personas",
edit: function (mid) {

View File

@@ -1,9 +1,9 @@
PERMS["resumen_diario"] = "Resumen diario (Solo docentes!)";
PAGES.resumen_diario = {
icon: "static/appico/Newspaper.svg",
icon: "static/appico/calendar.png",
navcss: "btn3",
AccessControl: true,
Title: "Resumen Diario",
Title: "Hoy y mañana",
index: function () {
var data_Comedor = safeuuid();
var data_Tareas = safeuuid();

View File

@@ -1,10 +1,10 @@
PERMS["supercafe"] = "SuperCafé";
PERMS["supercafe"] = "Cafetería";
PERMS["supercafe:edit"] = "&gt; Editar";
PAGES.supercafe = {
navcss: "btn4",
icon: "static/appico/Coffee.svg",
icon: "static/appico/cup.png",
AccessControl: true,
Title: "SuperCafé",
Title: "Cafetería",
edit: function (mid) {
if (!checkRole("supercafe:edit")) {
setUrlHash("supercafe");
@@ -17,10 +17,7 @@ PAGES.supercafe = {
var field_notas = safeuuid();
var field_estado = safeuuid();
var div_actions = safeuuid();
var btn_pagos = safeuuid();
var btn_cocina = safeuuid();
var btn_guardar = safeuuid();
var btn_guardar2 = safeuuid();
var btn_borrar = safeuuid();
container.innerHTML = `
<h1>Comanda <code id="${nameh1}"></code></h1>
@@ -165,7 +162,7 @@ PAGES.supercafe = {
var tts_check = safeuuid();
var old = {};
container.innerHTML = `
<h1>SuperCafé - Total: <span id="${totalprecio}">0</span>c</h1>
<h1>Cafetería - Total: <span id="${totalprecio}">0</span>c</h1>
<button id="${btn_new}" style="${sc_nobtn};">Nueva comanda</button>
<br>
<label>