doggy-tracker / index.html
cmauck10's picture
Add 2 files
e82d290 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dog Potty Tracker</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.timer {
font-family: 'Courier New', monospace;
}
.event-table {
max-height: 300px;
overflow-y: auto;
}
.fade-in {
animation: fadeIn 0.5s;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.delete-btn {
transition: all 0.2s;
}
.delete-btn:hover {
transform: scale(1.1);
color: #ef4444;
}
</style>
</head>
<body class="bg-gradient-to-b from-blue-50 to-purple-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<header class="text-center mb-8">
<h1 class="text-4xl font-bold text-blue-600 mb-2">🐶 Dog Potty Tracker</h1>
<p class="text-gray-600">Keep track of your dog's bathroom activities</p>
</header>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-8">
<!-- Pee Section -->
<div class="bg-white rounded-xl shadow-lg p-6 text-center">
<div class="flex justify-center items-center mb-4">
<i class="fas fa-tint text-blue-500 text-4xl mr-3"></i>
<h2 class="text-2xl font-semibold text-blue-700">Pee Tracker</h2>
</div>
<div class="timer text-5xl font-bold text-blue-800 mb-6" id="pee-timer">
00:00:00
</div>
<button id="pee-btn" class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 px-6 rounded-full text-lg transition-all transform hover:scale-105">
<i class="fas fa-plus mr-2"></i> Record Pee
</button>
<div id="pee-confirmation" class="mt-4 text-green-600 font-medium hidden fade-in">
<i class="fas fa-check-circle mr-2"></i> Pee recorded!
</div>
</div>
<!-- Poop Section -->
<div class="bg-white rounded-xl shadow-lg p-6 text-center">
<div class="flex justify-center items-center mb-4">
<i class="fas fa-poop text-amber-700 text-4xl mr-3"></i>
<h2 class="text-2xl font-semibold text-amber-800">Poop Tracker</h2>
</div>
<div class="timer text-5xl font-bold text-amber-800 mb-6" id="poop-timer">
00:00:00
</div>
<button id="poop-btn" class="bg-amber-600 hover:bg-amber-700 text-white font-bold py-3 px-6 rounded-full text-lg transition-all transform hover:scale-105">
<i class="fas fa-plus mr-2"></i> Record Poop
</button>
<div id="poop-confirmation" class="mt-4 text-green-600 font-medium hidden fade-in">
<i class="fas fa-check-circle mr-2"></i> Poop recorded!
</div>
</div>
</div>
<!-- Event History -->
<div class="bg-white rounded-xl shadow-lg p-6">
<h2 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center">
<i class="fas fa-history mr-3 text-purple-600"></i> Event History
</h2>
<div class="event-table">
<table class="w-full">
<thead>
<tr class="border-b-2 border-gray-200">
<th class="py-3 text-left">Type</th>
<th class="py-3 text-left">Time</th>
<th class="py-3 text-left">Date</th>
<th class="py-3 text-left">Duration Since Last</th>
<th class="py-3 text-left">Actions</th>
</tr>
</thead>
<tbody id="event-table-body">
<!-- Events will be added here -->
</tbody>
</table>
<p id="no-events" class="text-gray-500 text-center py-4">No events recorded yet</p>
</div>
</div>
<!-- Stats Section -->
<div class="mt-6 grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="bg-white rounded-lg shadow p-4 text-center">
<div class="text-gray-500">Today's Pee Count</div>
<div class="text-3xl font-bold text-blue-600" id="today-pee-count">0</div>
</div>
<div class="bg-white rounded-lg shadow p-4 text-center">
<div class="text-gray-500">Today's Poop Count</div>
<div class="text-3xl font-bold text-amber-600" id="today-poop-count">0</div>
</div>
<div class="bg-white rounded-lg shadow p-4 text-center">
<div class="text-gray-500">Total Events</div>
<div class="text-3xl font-bold text-purple-600" id="total-events">0</div>
</div>
</div>
</div>
<script>
// Initialize variables
let lastPeeTime = localStorage.getItem('lastPeeTime') ? new Date(localStorage.getItem('lastPeeTime')) : null;
let lastPoopTime = localStorage.getItem('lastPoopTime') ? new Date(localStorage.getItem('lastPoopTime')) : null;
let events = JSON.parse(localStorage.getItem('events')) || [];
// DOM elements
const peeTimer = document.getElementById('pee-timer');
const poopTimer = document.getElementById('poop-timer');
const peeBtn = document.getElementById('pee-btn');
const poopBtn = document.getElementById('poop-btn');
const peeConfirmation = document.getElementById('pee-confirmation');
const poopConfirmation = document.getElementById('poop-confirmation');
const eventTableBody = document.getElementById('event-table-body');
const noEvents = document.getElementById('no-events');
const todayPeeCount = document.getElementById('today-pee-count');
const todayPoopCount = document.getElementById('today-poop-count');
const totalEvents = document.getElementById('total-events');
// Update timers
function updateTimers() {
const now = new Date();
// Pee timer
if (lastPeeTime) {
const diff = now - lastPeeTime;
peeTimer.textContent = formatTime(diff);
} else {
peeTimer.textContent = "No record";
}
// Poop timer
if (lastPoopTime) {
const diff = now - lastPoopTime;
poopTimer.textContent = formatTime(diff);
} else {
poopTimer.textContent = "No record";
}
}
// Format time (ms to HH:MM:SS)
function formatTime(ms) {
let seconds = Math.floor(ms / 1000);
let hours = Math.floor(seconds / 3600);
seconds %= 3600;
let minutes = Math.floor(seconds / 60);
seconds %= 60;
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
}
// Add event to history
function addEvent(type, time) {
const now = new Date();
const event = {
type: type,
time: time,
date: now.toLocaleDateString(),
timestamp: now.getTime()
};
events.unshift(event); // Add to beginning of array
saveEvents();
renderEvents();
updateStats();
updateLastTimes();
}
// Delete event from history
function deleteEvent(index) {
events.splice(index, 1);
saveEvents();
renderEvents();
updateStats();
updateLastTimes();
}
// Save events to localStorage
function saveEvents() {
localStorage.setItem('events', JSON.stringify(events));
}
// Update last pee/poop times based on remaining events
function updateLastTimes() {
// Find most recent pee event
const peeEvents = events.filter(e => e.type === 'pee');
lastPeeTime = peeEvents.length > 0 ? new Date(peeEvents[0].timestamp) : null;
if (lastPeeTime) {
localStorage.setItem('lastPeeTime', lastPeeTime);
} else {
localStorage.removeItem('lastPeeTime');
}
// Find most recent poop event
const poopEvents = events.filter(e => e.type === 'poop');
lastPoopTime = poopEvents.length > 0 ? new Date(poopEvents[0].timestamp) : null;
if (lastPoopTime) {
localStorage.setItem('lastPoopTime', lastPoopTime);
} else {
localStorage.removeItem('lastPoopTime');
}
updateTimers();
}
// Render events in table
function renderEvents() {
if (events.length === 0) {
noEvents.classList.remove('hidden');
eventTableBody.innerHTML = '';
return;
}
noEvents.classList.add('hidden');
eventTableBody.innerHTML = '';
events.forEach((event, index) => {
const row = document.createElement('tr');
row.className = index % 2 === 0 ? 'bg-gray-50' : 'bg-white';
// Calculate duration since last event of same type
let durationText = 'First record';
if (index > 0) {
// Find previous event of the same type
const prevEventIndex = events.slice(index + 1).findIndex(e => e.type === event.type);
if (prevEventIndex !== -1) {
const prevEvent = events[index + 1 + prevEventIndex];
const duration = event.timestamp - prevEvent.timestamp;
durationText = formatTime(duration);
}
}
row.innerHTML = `
<td class="py-3">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
event.type === 'pee' ? 'bg-blue-100 text-blue-800' : 'bg-amber-100 text-amber-800'
}">
${event.type === 'pee' ? 'Pee' : 'Poop'}
</span>
</td>
<td class="py-3">${event.time}</td>
<td class="py-3">${event.date}</td>
<td class="py-3">${durationText}</td>
<td class="py-3">
<button class="delete-btn text-gray-400 hover:text-red-500" data-index="${index}">
<i class="fas fa-trash-alt"></i>
</button>
</td>
`;
eventTableBody.appendChild(row);
});
// Add event listeners to delete buttons
document.querySelectorAll('.delete-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const index = parseInt(btn.getAttribute('data-index'));
if (confirm('Are you sure you want to delete this event?')) {
deleteEvent(index);
}
});
});
}
// Update statistics
function updateStats() {
const today = new Date().toLocaleDateString();
const peeCount = events.filter(e => e.type === 'pee' && e.date === today).length;
const poopCount = events.filter(e => e.type === 'poop' && e.date === today).length;
todayPeeCount.textContent = peeCount;
todayPoopCount.textContent = poopCount;
totalEvents.textContent = events.length;
}
// Event listeners
peeBtn.addEventListener('click', () => {
const now = new Date();
const timeString = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
lastPeeTime = now;
localStorage.setItem('lastPeeTime', lastPeeTime);
addEvent('pee', timeString);
// Show confirmation
peeConfirmation.classList.remove('hidden');
setTimeout(() => {
peeConfirmation.classList.add('hidden');
}, 2000);
});
poopBtn.addEventListener('click', () => {
const now = new Date();
const timeString = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
lastPoopTime = now;
localStorage.setItem('lastPoopTime', lastPoopTime);
addEvent('poop', timeString);
// Show confirmation
poopConfirmation.classList.remove('hidden');
setTimeout(() => {
poopConfirmation.classList.add('hidden');
}, 2000);
});
// Initialize
updateTimers();
renderEvents();
updateStats();
// Update timers every second
setInterval(updateTimers, 1000);
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=cmauck10/doggy-tracker" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>