mirror of https://github.com/itflow-org/itflow
Merge pull request #803 from twetech/See-All-Timers
Add timers modal to see all timers
This commit is contained in:
commit
75238ce0fb
|
|
@ -15,6 +15,7 @@
|
||||||
<script src="plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
|
<script src="plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||||
|
|
||||||
<!-- Custom js-->
|
<!-- Custom js-->
|
||||||
|
<script src="js/header_timers.js"></script>
|
||||||
<script src="plugins/moment/moment.min.js"></script>
|
<script src="plugins/moment/moment.min.js"></script>
|
||||||
<script src="plugins/chart.js/Chart.min.js"></script>
|
<script src="plugins/chart.js/Chart.min.js"></script>
|
||||||
<script src="plugins/tempusdominus-bootstrap-4/js/tempusdominus-bootstrap-4.min.js"></script>
|
<script src="plugins/tempusdominus-bootstrap-4/js/tempusdominus-bootstrap-4.min.js"></script>
|
||||||
|
|
@ -26,6 +27,7 @@
|
||||||
<script src="plugins/clipboardjs/clipboard.min.js"></script>
|
<script src="plugins/clipboardjs/clipboard.min.js"></script>
|
||||||
<script src="js/keepalive.js"></script>
|
<script src="js/keepalive.js"></script>
|
||||||
|
|
||||||
|
|
||||||
<!-- AdminLTE App -->
|
<!-- AdminLTE App -->
|
||||||
<script src="dist/js/adminlte.min.js"></script>
|
<script src="dist/js/adminlte.min.js"></script>
|
||||||
<script src="js/app.js"></script>
|
<script src="js/app.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
// ticketCounter.js
|
||||||
|
(function() {
|
||||||
|
function getRunningTicketCount() {
|
||||||
|
let count = 0;
|
||||||
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
let key = localStorage.key(i);
|
||||||
|
if (key.includes("ticket-timer-running")) {
|
||||||
|
let isRunning = JSON.parse(localStorage.getItem(key));
|
||||||
|
if (isRunning) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTicketCountDisplay() {
|
||||||
|
let count = getRunningTicketCount();
|
||||||
|
let countDisplay = document.getElementById("runningTicketsCount");
|
||||||
|
if (countDisplay) {
|
||||||
|
countDisplay.innerText = count;
|
||||||
|
}
|
||||||
|
if (count == 0) {
|
||||||
|
countDisplay.classList.remove('badge-danger');
|
||||||
|
} else {
|
||||||
|
//check to see if more than one ticket
|
||||||
|
if (count > 1) {
|
||||||
|
countDisplay.classList.add('badge-danger');
|
||||||
|
}
|
||||||
|
//if count is one, check to see if its open in the current window by looking at the post variable ticket_id in url
|
||||||
|
if (count == 1) {
|
||||||
|
let urlParams = new URLSearchParams(window.location.search);
|
||||||
|
let ticketID = urlParams.get('ticket_id');
|
||||||
|
console.log(ticketID);
|
||||||
|
// If ticket number equals one in local storage, then add badge-danger class
|
||||||
|
if (localStorage.getItem("ticket-timer-running-") == ticketID) {
|
||||||
|
countDisplay.classList.add('badge-danger');
|
||||||
|
} else {
|
||||||
|
countDisplay.classList.remove('badge-danger');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getElapsedSeconds(ticketID) {
|
||||||
|
let storedStartTime = parseInt(localStorage.getItem(ticketID + "-startTime") || "0");
|
||||||
|
let pausedTime = parseInt(localStorage.getItem(ticketID + "-pausedTime") || "0");
|
||||||
|
if (!storedStartTime) return pausedTime;
|
||||||
|
let timeSinceStart = Math.floor((Date.now() - storedStartTime) / 1000);
|
||||||
|
return pausedTime + timeSinceStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(seconds) {
|
||||||
|
let hours = Math.floor(seconds / 3600);
|
||||||
|
let minutes = Math.floor((seconds % 3600) / 60);
|
||||||
|
let secs = seconds % 60;
|
||||||
|
return `${hours}h ${minutes}m ${secs}s`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadOpenTickets() {
|
||||||
|
let openTicketsContainer = document.getElementById('openTicketsContainer');
|
||||||
|
openTicketsContainer.innerHTML = ''; // Clear existing content
|
||||||
|
|
||||||
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
let key = localStorage.key(i);
|
||||||
|
|
||||||
|
if (key.startsWith("ticket-timer-running-")) {
|
||||||
|
let ticketID = key.replace("ticket-timer-running-", "");
|
||||||
|
let isRunning = JSON.parse(localStorage.getItem(key));
|
||||||
|
|
||||||
|
let ticketDiv = document.createElement('div');
|
||||||
|
ticketDiv.classList.add('card', 'card-outline', 'mb-3');
|
||||||
|
// Add class based on ticket status
|
||||||
|
ticketDiv.classList.add(isRunning ? 'card-info' : 'card-warning');
|
||||||
|
ticketDiv.id = 'ticket-' + ticketID;
|
||||||
|
|
||||||
|
let elapsedSecs = getElapsedSeconds(ticketID);
|
||||||
|
let timeString = formatTime(elapsedSecs);
|
||||||
|
|
||||||
|
ticketDiv.innerHTML = `
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">Ticket ID: ${ticketID}</h3>
|
||||||
|
<a href="/ticket.php?ticket_id=${ticketID}" class="btn btn-primary float-right">View Ticket</a>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p id="time-${ticketID}">Total Time: ${timeString}</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
openTicketsContainer.appendChild(ticketDiv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(() => updateRunningTickets());
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateRunningTickets() {
|
||||||
|
let runningTickets = document.querySelectorAll('[id^="ticket-"]');
|
||||||
|
runningTickets.forEach(ticket => {
|
||||||
|
let ticketID = ticket.id.replace("ticket-", "");
|
||||||
|
let isRunning = JSON.parse(localStorage.getItem("ticket-timer-running-" + ticketID));
|
||||||
|
|
||||||
|
if (isRunning) {
|
||||||
|
let updatedTime = formatTime(getElapsedSeconds(ticketID));
|
||||||
|
document.getElementById('time-' + ticketID).innerText = 'Total Time: ' + updatedTime;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
requestAnimationFrame(updateRunningTickets);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAllTimers() {
|
||||||
|
// Collect keys to be removed
|
||||||
|
let keysToRemove = [];
|
||||||
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
let key = localStorage.key(i);
|
||||||
|
if (key.startsWith("ticket-timer-running-") || key.endsWith("-startTime") || key.endsWith("-pausedTime")) {
|
||||||
|
keysToRemove.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove collected keys
|
||||||
|
keysToRemove.forEach(key => localStorage.removeItem(key));
|
||||||
|
|
||||||
|
// Update the display and redirect
|
||||||
|
updateTicketCountDisplay();
|
||||||
|
window.location.href = "/tickets.php";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial update on script load
|
||||||
|
updateTicketCountDisplay();
|
||||||
|
|
||||||
|
// update every 10 seconds
|
||||||
|
setInterval(updateTicketCountDisplay, 10000);
|
||||||
|
|
||||||
|
// Add event listener to modal
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
let modal = document.getElementById('openTicketsModal');
|
||||||
|
if (modal) {
|
||||||
|
$('#openTicketsModal').on('show.bs.modal', loadOpenTickets);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add event listener to clear all timers button
|
||||||
|
document.getElementById('clearAllTimers').addEventListener('click', clearAllTimers);
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
var ticketID = getCurrentTicketID();
|
var ticketID = getCurrentTicketID();
|
||||||
var elapsedSecs = getElapsedSeconds();
|
var elapsedSecs = getElapsedSeconds();
|
||||||
|
|
||||||
|
updateRunningTicketsCount();
|
||||||
|
|
||||||
function getCurrentTicketID() {
|
function getCurrentTicketID() {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
return urlParams.get('ticket_id');
|
return urlParams.get('ticket_id');
|
||||||
|
|
@ -53,6 +55,9 @@
|
||||||
timerInterval = setInterval(countTime, 1000);
|
timerInterval = setInterval(countTime, 1000);
|
||||||
isPaused = false;
|
isPaused = false;
|
||||||
document.getElementById("startStopTimer").innerText = "Pause";
|
document.getElementById("startStopTimer").innerText = "Pause";
|
||||||
|
updateRunningTicketsCount();
|
||||||
|
localStorage.setItem("ticket-timer-running-" + ticketID, "true");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function pauseTimer() {
|
function pauseTimer() {
|
||||||
|
|
@ -65,11 +70,15 @@
|
||||||
localStorage.removeItem(getLocalStorageKey("startTime"));
|
localStorage.removeItem(getLocalStorageKey("startTime"));
|
||||||
isPaused = true;
|
isPaused = true;
|
||||||
document.getElementById("startStopTimer").innerText = "Start";
|
document.getElementById("startStopTimer").innerText = "Start";
|
||||||
|
updateRunningTicketsCount();
|
||||||
|
localStorage.setItem("ticket-timer-running-" + ticketID, "false");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearTimeStorage() {
|
function clearTimeStorage() {
|
||||||
localStorage.removeItem(getLocalStorageKey("startTime"));
|
localStorage.removeItem(getLocalStorageKey("startTime"));
|
||||||
localStorage.removeItem(getLocalStorageKey("pausedTime"));
|
localStorage.removeItem(getLocalStorageKey("pausedTime"));
|
||||||
|
localStorage.removeItem("ticket-timer-running-" + ticketID);
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetTimer() {
|
function resetTimer() {
|
||||||
|
|
@ -81,6 +90,8 @@
|
||||||
displayTime();
|
displayTime();
|
||||||
document.getElementById("startStopTimer").innerText = "Start";
|
document.getElementById("startStopTimer").innerText = "Start";
|
||||||
}
|
}
|
||||||
|
localStorage.setItem("ticket-timer-running-" + ticketID, "false");
|
||||||
|
updateRunningTicketsCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
function forceResetTimer() {
|
function forceResetTimer() {
|
||||||
|
|
@ -116,6 +127,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function updateRunningTicketsCount() {
|
||||||
|
let runningTickets = parseInt(document.getElementById('runningTicketsCount').innerText, 10);
|
||||||
|
|
||||||
|
if (!isPaused && timerInterval) {
|
||||||
|
runningTickets += 1;
|
||||||
|
} else {
|
||||||
|
runningTickets = Math.max(0, runningTickets - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('runningTicketsCount').innerText = runningTickets.toString();
|
||||||
|
}
|
||||||
|
|
||||||
// Function to check status and pause timer
|
// Function to check status and pause timer
|
||||||
function checkStatusAndPauseTimer() {
|
function checkStatusAndPauseTimer() {
|
||||||
var status = document.querySelector('select[name="status"]').value;
|
var status = document.querySelector('select[name="status"]').value;
|
||||||
|
|
@ -124,6 +147,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
document.getElementById("hours").addEventListener('change', updateTimeFromInput);
|
document.getElementById("hours").addEventListener('change', updateTimeFromInput);
|
||||||
document.getElementById("minutes").addEventListener('change', updateTimeFromInput);
|
document.getElementById("minutes").addEventListener('change', updateTimeFromInput);
|
||||||
|
|
@ -153,7 +177,12 @@
|
||||||
// Wait for other synchronous actions (if any) to complete before resetting the timer.
|
// Wait for other synchronous actions (if any) to complete before resetting the timer.
|
||||||
setTimeout(forceResetTimer, 100); // 100ms delay should suffice, but you can adjust as needed.
|
setTimeout(forceResetTimer, 100); // 100ms delay should suffice, but you can adjust as needed.
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById("ticket_close").addEventListener('click', function() {
|
||||||
|
// Wait for other synchronous actions (if any) to complete before resetting the timer.
|
||||||
|
setTimeout(clearTimeStorage, 100); // 100ms delay should suffice, but you can adjust as needed.
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
displayTime();
|
displayTime();
|
||||||
if (!localStorage.getItem(getLocalStorageKey("startTime")) && !localStorage.getItem(getLocalStorageKey("pausedTime"))) {
|
if (!localStorage.getItem(getLocalStorageKey("startTime")) && !localStorage.getItem(getLocalStorageKey("pausedTime"))) {
|
||||||
|
|
|
||||||
|
|
@ -315,6 +315,7 @@ if (isset($_GET['ticket_id'])) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Time Tracking: Hours -->
|
<!-- Time Tracking: Hours -->
|
||||||
|
|
||||||
<div class="col-sm-3 col-lg-2">
|
<div class="col-sm-3 col-lg-2">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
|
|
@ -325,6 +326,7 @@ if (isset($_GET['ticket_id'])) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Time Tracking: Minutes -->
|
<!-- Time Tracking: Minutes -->
|
||||||
|
|
||||||
<div class="col-sm-1">
|
<div class="col-sm-1">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
<input type="text" class="form-control" inputmode="numeric" id="minutes" name="minutes" placeholder="Mins" min="0" max="59" pattern="[0-5]?[0-9]">
|
<input type="text" class="form-control" inputmode="numeric" id="minutes" name="minutes" placeholder="Mins" min="0" max="59" pattern="[0-5]?[0-9]">
|
||||||
|
|
@ -332,6 +334,7 @@ if (isset($_GET['ticket_id'])) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Time Tracking: Seconds -->
|
<!-- Time Tracking: Seconds -->
|
||||||
|
|
||||||
<div class="col-sm-1">
|
<div class="col-sm-1">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
<input type="text" class="form-control" inputmode="numeric" id="seconds" name="seconds" placeholder="Secs" min="0" max="59" pattern="[0-5]?[0-9]">
|
<input type="text" class="form-control" inputmode="numeric" id="seconds" name="seconds" placeholder="Secs" min="0" max="59" pattern="[0-5]?[0-9]">
|
||||||
|
|
@ -817,7 +820,7 @@ if (isset($_GET['ticket_id'])) {
|
||||||
<?php }
|
<?php }
|
||||||
|
|
||||||
if ($ticket_status !== "Closed") { ?>
|
if ($ticket_status !== "Closed") { ?>
|
||||||
<a href="post.php?close_ticket=<?php echo $ticket_id; ?>" class="btn btn-secondary btn-block confirm-link">
|
<a href="post.php?close_ticket=<?php echo $ticket_id; ?>" class="btn btn-secondary btn-block confirm-link" id="ticket_close">
|
||||||
<i class="fas fa-fw fa-gavel mr-2"></i>Close Ticket
|
<i class="fas fa-fw fa-gavel mr-2"></i>Close Ticket
|
||||||
</a>
|
</a>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
|
|
||||||
13
top_nav.php
13
top_nav.php
|
|
@ -36,6 +36,15 @@
|
||||||
<i class="fas fa-fw fa-question"></i>
|
<i class="fas fa-fw fa-question"></i>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<?php if ($config_module_enable_ticketing == 1) { ?>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#" data-toggle="modal" data-target="#openTicketsModal">
|
||||||
|
<i class="fas fa-hourglass-half"></i>
|
||||||
|
<span class="badge" id="runningTicketsCount">0</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
|
||||||
<!-- New Notifications Dropdown -->
|
<!-- New Notifications Dropdown -->
|
||||||
<?php
|
<?php
|
||||||
|
|
@ -151,4 +160,8 @@
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<?php if ($config_module_enable_ticketing == 1) {
|
||||||
|
include_once "top_nav_tickets_modal.php";
|
||||||
|
} ?>
|
||||||
<!-- /.navbar -->
|
<!-- /.navbar -->
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
<!-- Open Tickets Modal -->
|
||||||
|
<div class="modal fade" id="openTicketsModal" tabindex="-1" role="dialog" aria-labelledby="openTicketsModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="openTicketsModalLabel">Open Tickets</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div id="openTicketsContainer">
|
||||||
|
<!-- Open tickets will be loaded here via JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" id="clearAllTimers">Clear All</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
Loading…
Reference in New Issue