18-vm/assets/js/main.js
Flatlogic Bot 00a5c5a638 Version 1
2026-01-19 00:06:34 +00:00

277 lines
10 KiB
JavaScript

document.addEventListener('DOMContentLoaded', () => {
const chatForm = document.getElementById('chat-form');
const chatInput = document.getElementById('chat-input');
const chatBox = document.getElementById('chat-box');
const activityLogBody = document.getElementById('activity-log-body');
const logCount = document.getElementById('log-count');
const emptyLogRew = document.getElementById('empty-log-row');
const manualLogForm = document.getElementById('manual-log-form');
const clockElement = document.getElementById('clock');
const pastTimesheetsTab = document.getElementById('past-timesheets-tab');
const pastTimesheetsContainer = document.getElementById('past-timesheets');
function updateClock() {
if (clockElement) {
const now = new Date();
const time = now.toLocaleTimeString();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const year = now.getFullYear();
const date = `${month}-${day}-${year}`;
clockElement.textContent = `${time} ${date}`;
}
}
updateClock();
setInterval(updateClock, 1000);
if (pastTimesheetsTab) {
pastTimesheetsTab.addEventListener('shown.bs.tab', () => {
loadPastTimesheets();
});
}
async function loadPastTimesheets() {
pastTimesheetsContainer.innerHTML = '<div class="text-center"><div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div></div>';
try {
const response = await fetch('api/chat.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ action: 'get_past_timesheets' })
});
if (!response.ok) {
pastTimesheetsContainer.innerHTML = '<p class="text-center text-danger">Failed to load past timesheets.</p>';
return;
}
const data = await response.json();
if (data.error) {
pastTimesheetsContainer.innerHTML = `<p class="text-center text-danger">${data.error}</p>`;
return;
}
if (!data.past_timesheets || data.past_timesheets.length === 0) {
pastTimesheetsContainer.innerHTML = '<div class="card"><div class="card-body text-center text-muted">No past timesheets found.</div></div>';
return;
}
const accordion = document.createElement('div');
accordion.className = 'accordion';
accordion.id = 'past-timesheets-accordion';
data.past_timesheets.forEach((sheet, index) => {
const activities = JSON.parse(sheet.activities);
const item = document.createElement('div');
item.className = 'accordion-item';
const header = document.createElement('h2');
header.className = 'accordion-header';
header.innerHTML = `
<button class="accordion-button ${index === 0 ? '' : 'collapsed'}" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-${index}" aria-expanded="${index === 0}" aria-controls="collapse-${index}">
${sheet.timesheet_date}
</button>
`;
const collapse = document.createElement('div');
collapse.id = `collapse-${index}`;
collapse.className = `accordion-collapse collapse ${index === 0 ? 'show' : ''}`;
collapse.setAttribute('data-bs-parent', '#past-timesheets-accordion');
const body = document.createElement('div');
body.className = 'accordion-body';
let tableHtml = `
<div class="table-responsive">
<table class="table table-sm table-striped">
<thead>
<tr>
<th>Timestamp</th>
<th>Task</th>
<th>Project</th>
<th class="text-center">Output</th>
<th class="text-end">Duration</th>
</tr>
</thead>
<tbody>
`;
activities.forEach(activity => {
const badgeClass = activity.output_type === 'AI' ? 'badge-ai' : 'badge-human';
tableHtml += `
<tr>
<td>${activity.timestamp}</td>
<td>${escapeHTML(activity.task)}</td>
<td>${escapeHTML(activity.project)}</td>
<td class="text-center"><span class="badge rounded-pill ${badgeClass}">${escapeHTML(activity.output_type)}</span></td>
<td class="text-end">${escapeHTML(activity.duration)}</td>
</tr>
`;
});
tableHtml += '</tbody></table></div>';
body.innerHTML = tableHtml;
collapse.appendChild(body);
item.appendChild(header);
item.appendChild(collapse);
accordion.appendChild(item);
});
pastTimesheetsContainer.innerHTML = '';
pastTimesheetsContainer.appendChild(accordion);
} catch (error) {
console.error('Error loading past timesheets:', error);
pastTimesheetsContainer.innerHTML = '<p class="text-center text-danger">An error occurred while fetching data.</p>';
}
}
chatForm.addEventListener('submit', async (e) => {
e.preventDefault();
const userMessage = chatInput.value.trim();
if (!userMessage) return;
appendMessage(userMessage, 'user');
chatInput.value = '';
showTypingIndicator();
try {
const response = await fetch('api/chat.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ message: userMessage })
});
removeTypingIndicator();
if (!response.ok) {
const errorData = await response.json();
const errorMsg = errorData.error || 'An unknown error occurred.';
appendMessage(`Error: ${errorMsg}`, 'bot');
return;
}
const data = await response.json();
if (data.error) {
appendMessage(`Error: ${data.error}`, 'bot');
return;
}
appendMessage(data.reply, 'bot');
if (data.new_log_entry) {
updateActivityLog(data.new_log_entry);
}
} catch (error) {
removeTypingIndicator();
appendMessage('Could not connect to the server. Please try again later.', 'bot');
console.error("Fetch Error:", error);
}
});
manualLogForm.addEventListener('submit', async (e) => {
e.preventDefault();
const actionType = document.getElementById('action-type').value;
const project = document.getElementById('manual-project').value.trim();
const task = document.getElementById('manual-task').value.trim();
if (!project || !task) {
// You might want to show an error to the user
return;
}
const payload = {
action: 'manual_log',
type: actionType,
project: project,
task: task
};
try {
const response = await fetch('api/chat.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
if (!response.ok) {
console.error('Manual log submission failed');
return;
}
const data = await response.json();
if (data.new_log_entry) {
updateActivityLog(data.new_log_entry);
manualLogForm.reset();
}
} catch (error) {
console.error("Fetch Error:", error);
}
});
function appendMessage(message, sender) {
const messageElement = document.createElement('div');
messageElement.classList.add('message', `${sender}-message`);
messageElement.textContent = message;
chatBox.appendChild(messageElement);
chatBox.scrollTop = chatBox.scrollHeight;
}
function showTypingIndicator() {
const typingIndicator = document.createElement('div');
typingIndicator.classList.add('message', 'bot-message', 'typing-indicator');
typingIndicator.innerHTML = '<div class="dot-flashing"></div>';
typingIndicator.id = 'typing-indicator';
chatBox.appendChild(typingIndicator);
chatBox.scrollTop = chatBox.scrollHeight;
}
function removeTypingIndicator() {
const typingIndicator = document.getElementById('typing-indicator');
if (typingIndicator) {
typingIndicator.remove();
}
}
function updateActivityLog(logEntry) {
if(emptyLogRew) {
emptyLogRew.remove();
}
const newRow = document.createElement('tr');
const badgeClass = logEntry.output_type === 'AI' ? 'badge-ai' : 'badge-human';
newRow.innerHTML = `
<td>${logEntry.timestamp}</td>
<td>${escapeHTML(logEntry.task)}</td>
<td>${escapeHTML(logEntry.project)}</td>
<td class="text-center">
<span class="badge rounded-pill ${badgeClass}">
${escapeHTML(logEntry.output_type)}
</span>
</td>
<td class="text-end">${escapeHTML(logEntry.duration)}</td>
`;
activityLogBody.prepend(newRow);
logCount.textContent = parseInt(logCount.textContent) + 1;
}
function escapeHTML(str) {
if (typeof str !== 'string') return '';
const p = document.createElement('p');
p.appendChild(document.createTextNode(str));
return p.innerHTML;
}
});