Internal
Public Access
Features: - Django-based REST API with web interface - Task management with tags, priorities, and due dates - Time tracking with start/stop timers - Subtasks support - Task filtering (all, today, upcoming, overdue, completed) - Tag-based organization with color coding - Sorting by due date and priority - Auto-assign tags when filtering - Responsive 3-pane layout (sidebar, task list, detail panel) - Task sharing between users - Mobile-responsive design with dark mode support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
100 lines
2.7 KiB
JavaScript
100 lines
2.7 KiB
JavaScript
/**
|
|
* KeepItGoing - Main JavaScript
|
|
*/
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Initialize components
|
|
initTimerDisplay();
|
|
initTaskToggle();
|
|
initConfirmDialogs();
|
|
});
|
|
|
|
/**
|
|
* Update timer display
|
|
*/
|
|
function initTimerDisplay() {
|
|
const timerDisplay = document.querySelector('.timer-display');
|
|
if (!timerDisplay) return;
|
|
|
|
const startedAt = new Date(timerDisplay.dataset.started);
|
|
|
|
function updateTimer() {
|
|
const now = new Date();
|
|
const diff = Math.floor((now - startedAt) / 1000);
|
|
const hours = Math.floor(diff / 3600).toString().padStart(2, '0');
|
|
const minutes = Math.floor((diff % 3600) / 60).toString().padStart(2, '0');
|
|
const seconds = (diff % 60).toString().padStart(2, '0');
|
|
timerDisplay.textContent = `${hours}:${minutes}:${seconds}`;
|
|
}
|
|
|
|
updateTimer();
|
|
setInterval(updateTimer, 1000);
|
|
}
|
|
|
|
/**
|
|
* Handle task toggle without page reload (optional enhancement)
|
|
*/
|
|
function initTaskToggle() {
|
|
// For now, we let the form submit normally
|
|
// This could be enhanced with AJAX in the future
|
|
}
|
|
|
|
/**
|
|
* Initialize confirmation dialogs
|
|
*/
|
|
function initConfirmDialogs() {
|
|
const deleteButtons = document.querySelectorAll('[data-confirm]');
|
|
deleteButtons.forEach(button => {
|
|
button.addEventListener('click', function(e) {
|
|
const message = this.dataset.confirm || 'Are you sure?';
|
|
if (!confirm(message)) {
|
|
e.preventDefault();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Format duration in seconds to human-readable string
|
|
*/
|
|
function formatDuration(seconds) {
|
|
if (seconds < 60) {
|
|
return `${seconds}s`;
|
|
} else if (seconds < 3600) {
|
|
const mins = Math.floor(seconds / 60);
|
|
return `${mins}m`;
|
|
} else {
|
|
const hours = Math.floor(seconds / 3600);
|
|
const mins = Math.floor((seconds % 3600) / 60);
|
|
return `${hours}h ${mins}m`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Format date to relative string (today, tomorrow, etc.)
|
|
*/
|
|
function formatRelativeDate(dateString) {
|
|
const date = new Date(dateString);
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0);
|
|
|
|
const tomorrow = new Date(today);
|
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
|
|
const yesterday = new Date(today);
|
|
yesterday.setDate(yesterday.getDate() - 1);
|
|
|
|
const dateOnly = new Date(date);
|
|
dateOnly.setHours(0, 0, 0, 0);
|
|
|
|
if (dateOnly.getTime() === today.getTime()) {
|
|
return 'Today';
|
|
} else if (dateOnly.getTime() === tomorrow.getTime()) {
|
|
return 'Tomorrow';
|
|
} else if (dateOnly.getTime() === yesterday.getTime()) {
|
|
return 'Yesterday';
|
|
} else {
|
|
return date.toLocaleDateString();
|
|
}
|
|
}
|