Add 2 files
Browse files- index.html +684 -51
- prompts.txt +2 -0
index.html
CHANGED
|
@@ -3,9 +3,11 @@
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
-
<title>Job Application Tracker</title>
|
| 7 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
|
|
|
|
|
| 9 |
<style>
|
| 10 |
.fade-in {
|
| 11 |
animation: fadeIn 0.3s ease-in-out;
|
|
@@ -15,20 +17,37 @@
|
|
| 15 |
to { opacity: 1; transform: translateY(0); }
|
| 16 |
}
|
| 17 |
.pulse {
|
| 18 |
-
animation: pulse
|
| 19 |
}
|
| 20 |
@keyframes pulse {
|
| 21 |
0% { transform: scale(1); }
|
| 22 |
50% { transform: scale(1.05); }
|
| 23 |
100% { transform: scale(1); }
|
| 24 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
</style>
|
| 26 |
</head>
|
| 27 |
<body class="bg-gradient-to-br from-blue-50 to-indigo-100 min-h-screen">
|
| 28 |
-
<div class="container mx-auto px-4 py-8 max-w-
|
| 29 |
<header class="text-center mb-8">
|
| 30 |
-
<h1 class="text-4xl font-bold text-indigo-800 mb-2">Job Application Tracker</h1>
|
| 31 |
-
<p class="text-indigo-600">
|
| 32 |
</header>
|
| 33 |
|
| 34 |
<div class="bg-white rounded-xl shadow-lg p-6 mb-8">
|
|
@@ -36,14 +55,54 @@
|
|
| 36 |
<div class="text-center md:text-left mb-4 md:mb-0">
|
| 37 |
<h2 class="text-2xl font-semibold text-gray-800">Today's Applications</h2>
|
| 38 |
<p class="text-gray-500" id="today-date">Loading date...</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
</div>
|
| 40 |
<div class="flex items-center space-x-4">
|
| 41 |
-
<div class="
|
| 42 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
</div>
|
| 44 |
-
<button id="add-btn" class="bg-indigo-600 hover:bg-indigo-700 text-white rounded-full w-14 h-14 flex items-center justify-center shadow-md transition-all transform hover:scale-105">
|
| 45 |
-
<i class="fas fa-plus text-2xl"></i>
|
| 46 |
-
</button>
|
| 47 |
</div>
|
| 48 |
</div>
|
| 49 |
|
|
@@ -52,29 +111,140 @@
|
|
| 52 |
<h3 class="font-medium text-gray-700">Total Applications</h3>
|
| 53 |
<span class="text-xl font-bold text-indigo-800" id="total-count">0</span>
|
| 54 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
</div>
|
| 56 |
</div>
|
| 57 |
|
| 58 |
<div class="bg-white rounded-xl shadow-lg p-6">
|
| 59 |
<div class="flex justify-between items-center mb-4">
|
| 60 |
<h2 class="text-2xl font-semibold text-gray-800">Application History</h2>
|
| 61 |
-
<
|
| 62 |
-
<
|
| 63 |
-
|
| 64 |
-
|
|
|
|
|
|
|
| 65 |
</div>
|
| 66 |
|
| 67 |
<div id="history-list" class="space-y-3">
|
| 68 |
<div class="text-center py-8 text-gray-400" id="empty-state">
|
| 69 |
<i class="fas fa-clipboard-list text-4xl mb-2"></i>
|
| 70 |
<p>No applications recorded yet</p>
|
|
|
|
|
|
|
|
|
|
| 71 |
</div>
|
| 72 |
<!-- History items will be added here dynamically -->
|
| 73 |
</div>
|
| 74 |
</div>
|
| 75 |
|
| 76 |
<div class="mt-6 text-center text-sm text-gray-500">
|
| 77 |
-
<p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
</div>
|
| 79 |
</div>
|
| 80 |
|
|
@@ -82,22 +252,153 @@
|
|
| 82 |
document.addEventListener('DOMContentLoaded', function() {
|
| 83 |
// Get elements
|
| 84 |
const addBtn = document.getElementById('add-btn');
|
|
|
|
|
|
|
| 85 |
const todayCountEl = document.getElementById('today-count');
|
| 86 |
const totalCountEl = document.getElementById('total-count');
|
| 87 |
const historyList = document.getElementById('history-list');
|
| 88 |
const emptyState = document.getElementById('empty-state');
|
| 89 |
-
const
|
| 90 |
const todayDateEl = document.getElementById('today-date');
|
|
|
|
|
|
|
|
|
|
| 91 |
|
| 92 |
-
//
|
| 93 |
-
const
|
| 94 |
-
const
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
|
| 102 |
// Initialize data from localStorage or create new
|
| 103 |
let appData = JSON.parse(localStorage.getItem('jobAppData')) || {
|
|
@@ -106,11 +407,10 @@
|
|
| 106 |
};
|
| 107 |
|
| 108 |
// Check if today's date exists in history
|
| 109 |
-
const todayKey = today.toISOString().split('T')[0];
|
| 110 |
let todayEntry = appData.history.find(entry => entry.date === todayKey);
|
| 111 |
|
| 112 |
if (!todayEntry) {
|
| 113 |
-
todayEntry = { date: todayKey, count: 0 };
|
| 114 |
appData.history.unshift(todayEntry);
|
| 115 |
saveData();
|
| 116 |
}
|
|
@@ -118,6 +418,24 @@
|
|
| 118 |
// Update UI
|
| 119 |
updateCounts();
|
| 120 |
renderHistory();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
|
| 122 |
// Add button click handler
|
| 123 |
addBtn.addEventListener('click', function() {
|
|
@@ -126,26 +444,204 @@
|
|
| 126 |
|
| 127 |
// Add animation effect
|
| 128 |
this.classList.add('pulse');
|
| 129 |
-
setTimeout(() => this.classList.remove('pulse'),
|
| 130 |
|
| 131 |
saveData();
|
| 132 |
updateCounts();
|
| 133 |
renderHistory();
|
|
|
|
| 134 |
});
|
| 135 |
|
| 136 |
-
//
|
| 137 |
-
|
| 138 |
-
if (
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
history: []
|
| 142 |
-
};
|
| 143 |
-
todayEntry = { date: todayKey, count: 0 };
|
| 144 |
-
appData.history.unshift(todayEntry);
|
| 145 |
|
| 146 |
saveData();
|
| 147 |
updateCounts();
|
| 148 |
renderHistory();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
}
|
| 150 |
});
|
| 151 |
|
|
@@ -154,6 +650,14 @@
|
|
| 154 |
todayCountEl.textContent = todayEntry.count;
|
| 155 |
totalCountEl.textContent = appData.total;
|
| 156 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
// Add animation when count changes
|
| 158 |
todayCountEl.classList.add('fade-in');
|
| 159 |
totalCountEl.classList.add('fade-in');
|
|
@@ -173,42 +677,171 @@
|
|
| 173 |
emptyState.style.display = 'none';
|
| 174 |
historyList.innerHTML = '';
|
| 175 |
|
| 176 |
-
|
|
|
|
|
|
|
|
|
|
| 177 |
if (entry.count === 0 && entry.date === todayKey) return;
|
| 178 |
|
| 179 |
const entryDate = new Date(entry.date);
|
| 180 |
const isToday = entry.date === todayKey;
|
| 181 |
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
}
|
| 188 |
|
| 189 |
const historyItem = document.createElement('div');
|
| 190 |
-
historyItem.className = 'flex justify-between items-center p-3 bg-gray-50 rounded-lg fade-in';
|
| 191 |
if (isToday) {
|
| 192 |
-
historyItem.classList.add('border-l-4',
|
| 193 |
}
|
| 194 |
|
| 195 |
historyItem.innerHTML = `
|
| 196 |
<div class="flex items-center">
|
| 197 |
-
<div class="w-10 h-10 rounded-full bg-
|
| 198 |
-
<i class="fas fa-briefcase text-
|
| 199 |
</div>
|
| 200 |
<div>
|
| 201 |
-
<p class="font-medium ${isToday ?
|
| 202 |
-
|
| 203 |
</div>
|
| 204 |
</div>
|
| 205 |
-
<
|
|
|
|
|
|
|
| 206 |
`;
|
| 207 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 208 |
historyList.appendChild(historyItem);
|
| 209 |
});
|
| 210 |
}
|
| 211 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
// Save data to localStorage
|
| 213 |
function saveData() {
|
| 214 |
localStorage.setItem('jobAppData', JSON.stringify(appData));
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Job Application Tracker Pro</title>
|
| 7 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 9 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
|
| 10 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.34/moment-timezone-with-data.min.js"></script>
|
| 11 |
<style>
|
| 12 |
.fade-in {
|
| 13 |
animation: fadeIn 0.3s ease-in-out;
|
|
|
|
| 17 |
to { opacity: 1; transform: translateY(0); }
|
| 18 |
}
|
| 19 |
.pulse {
|
| 20 |
+
animation: pulse 1s infinite;
|
| 21 |
}
|
| 22 |
@keyframes pulse {
|
| 23 |
0% { transform: scale(1); }
|
| 24 |
50% { transform: scale(1.05); }
|
| 25 |
100% { transform: scale(1); }
|
| 26 |
}
|
| 27 |
+
.modal {
|
| 28 |
+
transition: opacity 0.3s ease;
|
| 29 |
+
}
|
| 30 |
+
.modal-overlay {
|
| 31 |
+
background-color: rgba(0, 0, 0, 0.5);
|
| 32 |
+
}
|
| 33 |
+
.chart-bar {
|
| 34 |
+
transition: height 0.5s ease-out;
|
| 35 |
+
}
|
| 36 |
+
.progress-ring__circle {
|
| 37 |
+
transition: stroke-dashoffset 0.5s ease-out;
|
| 38 |
+
transform: rotate(-90deg);
|
| 39 |
+
transform-origin: 50% 50%;
|
| 40 |
+
}
|
| 41 |
+
.glow {
|
| 42 |
+
box-shadow: 0 0 15px rgba(99, 102, 241, 0.5);
|
| 43 |
+
}
|
| 44 |
</style>
|
| 45 |
</head>
|
| 46 |
<body class="bg-gradient-to-br from-blue-50 to-indigo-100 min-h-screen">
|
| 47 |
+
<div class="container mx-auto px-4 py-8 max-w-4xl">
|
| 48 |
<header class="text-center mb-8">
|
| 49 |
+
<h1 class="text-4xl font-bold text-indigo-800 mb-2">Job Application Tracker Pro</h1>
|
| 50 |
+
<p class="text-indigo-600">Track, analyze, and optimize your job search</p>
|
| 51 |
</header>
|
| 52 |
|
| 53 |
<div class="bg-white rounded-xl shadow-lg p-6 mb-8">
|
|
|
|
| 55 |
<div class="text-center md:text-left mb-4 md:mb-0">
|
| 56 |
<h2 class="text-2xl font-semibold text-gray-800">Today's Applications</h2>
|
| 57 |
<p class="text-gray-500" id="today-date">Loading date...</p>
|
| 58 |
+
<div class="mt-2 flex items-center justify-center md:justify-start">
|
| 59 |
+
<span class="text-xs bg-indigo-100 text-indigo-800 px-2 py-1 rounded-full flex items-center">
|
| 60 |
+
<i class="fas fa-clock mr-1 text-xs"></i>
|
| 61 |
+
<span id="timezone-display">Detecting timezone...</span>
|
| 62 |
+
</span>
|
| 63 |
+
</div>
|
| 64 |
</div>
|
| 65 |
<div class="flex items-center space-x-4">
|
| 66 |
+
<div class="relative">
|
| 67 |
+
<svg class="w-24 h-24">
|
| 68 |
+
<circle
|
| 69 |
+
class="text-gray-200"
|
| 70 |
+
stroke-width="8"
|
| 71 |
+
stroke="currentColor"
|
| 72 |
+
fill="transparent"
|
| 73 |
+
r="36"
|
| 74 |
+
cx="40"
|
| 75 |
+
cy="40"
|
| 76 |
+
/>
|
| 77 |
+
<circle
|
| 78 |
+
class="progress-ring__circle text-indigo-600"
|
| 79 |
+
stroke-width="8"
|
| 80 |
+
stroke-linecap="round"
|
| 81 |
+
stroke="currentColor"
|
| 82 |
+
fill="transparent"
|
| 83 |
+
r="36"
|
| 84 |
+
cx="40"
|
| 85 |
+
cy="40"
|
| 86 |
+
id="progress-ring"
|
| 87 |
+
/>
|
| 88 |
+
</svg>
|
| 89 |
+
<div class="absolute inset-0 flex items-center justify-center">
|
| 90 |
+
<span class="text-2xl font-bold text-indigo-800" id="today-count">0</span>
|
| 91 |
+
</div>
|
| 92 |
+
</div>
|
| 93 |
+
<div class="flex flex-col space-y-2">
|
| 94 |
+
<div class="flex space-x-2">
|
| 95 |
+
<button id="decrease-btn" class="bg-red-500 hover:bg-red-600 text-white rounded-full w-12 h-12 flex items-center justify-center shadow-md transition-all transform hover:scale-105">
|
| 96 |
+
<i class="fas fa-minus text-lg"></i>
|
| 97 |
+
</button>
|
| 98 |
+
<button id="add-btn" class="bg-indigo-600 hover:bg-indigo-700 text-white rounded-full w-12 h-12 flex items-center justify-center shadow-md transition-all transform hover:scale-105">
|
| 99 |
+
<i class="fas fa-plus text-lg"></i>
|
| 100 |
+
</button>
|
| 101 |
+
</div>
|
| 102 |
+
<button id="add-custom-btn" class="bg-indigo-100 hover:bg-indigo-200 text-indigo-700 rounded-full w-full h-8 flex items-center justify-center shadow-md text-sm transition-all">
|
| 103 |
+
<i class="fas fa-calendar-day mr-1"></i> Custom Date
|
| 104 |
+
</button>
|
| 105 |
</div>
|
|
|
|
|
|
|
|
|
|
| 106 |
</div>
|
| 107 |
</div>
|
| 108 |
|
|
|
|
| 111 |
<h3 class="font-medium text-gray-700">Total Applications</h3>
|
| 112 |
<span class="text-xl font-bold text-indigo-800" id="total-count">0</span>
|
| 113 |
</div>
|
| 114 |
+
<div class="w-full bg-gray-200 rounded-full h-2.5">
|
| 115 |
+
<div id="total-progress" class="bg-indigo-600 h-2.5 rounded-full" style="width: 0%"></div>
|
| 116 |
+
</div>
|
| 117 |
+
</div>
|
| 118 |
+
</div>
|
| 119 |
+
|
| 120 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
|
| 121 |
+
<div class="bg-white rounded-xl shadow-lg p-6">
|
| 122 |
+
<h2 class="text-xl font-semibold text-gray-800 mb-4">Weekly Overview</h2>
|
| 123 |
+
<div class="flex justify-between items-end h-40 mt-6">
|
| 124 |
+
<!-- Weekly chart bars will be added here -->
|
| 125 |
+
</div>
|
| 126 |
+
</div>
|
| 127 |
+
<div class="bg-white rounded-xl shadow-lg p-6">
|
| 128 |
+
<h2 class="text-xl font-semibold text-gray-800 mb-4">Monthly Progress</h2>
|
| 129 |
+
<div class="flex justify-center">
|
| 130 |
+
<div id="monthly-chart" class="w-full h-40">
|
| 131 |
+
<!-- Monthly chart will be added here -->
|
| 132 |
+
</div>
|
| 133 |
+
</div>
|
| 134 |
</div>
|
| 135 |
</div>
|
| 136 |
|
| 137 |
<div class="bg-white rounded-xl shadow-lg p-6">
|
| 138 |
<div class="flex justify-between items-center mb-4">
|
| 139 |
<h2 class="text-2xl font-semibold text-gray-800">Application History</h2>
|
| 140 |
+
<div class="flex space-x-2">
|
| 141 |
+
<button id="settings-btn" class="text-indigo-600 hover:text-indigo-800 flex items-center">
|
| 142 |
+
<i class="fas fa-cog mr-1"></i>
|
| 143 |
+
<span>Settings</span>
|
| 144 |
+
</button>
|
| 145 |
+
</div>
|
| 146 |
</div>
|
| 147 |
|
| 148 |
<div id="history-list" class="space-y-3">
|
| 149 |
<div class="text-center py-8 text-gray-400" id="empty-state">
|
| 150 |
<i class="fas fa-clipboard-list text-4xl mb-2"></i>
|
| 151 |
<p>No applications recorded yet</p>
|
| 152 |
+
<button id="get-started-btn" class="mt-4 px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-md">
|
| 153 |
+
Add Your First Application
|
| 154 |
+
</button>
|
| 155 |
</div>
|
| 156 |
<!-- History items will be added here dynamically -->
|
| 157 |
</div>
|
| 158 |
</div>
|
| 159 |
|
| 160 |
<div class="mt-6 text-center text-sm text-gray-500">
|
| 161 |
+
<p>Every application is a step closer to your dream job. Keep going!</p>
|
| 162 |
+
</div>
|
| 163 |
+
</div>
|
| 164 |
+
|
| 165 |
+
<!-- Modal for custom date entry -->
|
| 166 |
+
<div id="date-modal" class="fixed inset-0 flex items-center justify-center z-50 modal opacity-0 pointer-events-none">
|
| 167 |
+
<div class="modal-overlay absolute inset-0"></div>
|
| 168 |
+
<div class="bg-white rounded-xl shadow-xl p-6 z-10 w-full max-w-md transform transition-all scale-95">
|
| 169 |
+
<div class="flex justify-between items-center mb-4">
|
| 170 |
+
<h3 class="text-xl font-semibold text-gray-800">Add/Edit Applications</h3>
|
| 171 |
+
<button id="close-modal" class="text-gray-500 hover:text-gray-700">
|
| 172 |
+
<i class="fas fa-times"></i>
|
| 173 |
+
</button>
|
| 174 |
+
</div>
|
| 175 |
+
<div class="mb-4">
|
| 176 |
+
<label for="custom-date" class="block text-sm font-medium text-gray-700 mb-1">Select Date</label>
|
| 177 |
+
<input type="date" id="custom-date" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
| 178 |
+
</div>
|
| 179 |
+
<div class="mb-4">
|
| 180 |
+
<label for="custom-count" class="block text-sm font-medium text-gray-700 mb-1">Number of Applications</label>
|
| 181 |
+
<div class="flex items-center">
|
| 182 |
+
<button id="decrease-custom" class="bg-gray-200 hover:bg-gray-300 text-gray-800 rounded-l-md w-10 h-10 flex items-center justify-center">
|
| 183 |
+
<i class="fas fa-minus"></i>
|
| 184 |
+
</button>
|
| 185 |
+
<input type="number" id="custom-count" min="0" value="1" class="w-full px-3 py-2 border-t border-b border-gray-300 text-center focus:outline-none">
|
| 186 |
+
<button id="increase-custom" class="bg-gray-200 hover:bg-gray-300 text-gray-800 rounded-r-md w-10 h-10 flex items-center justify-center">
|
| 187 |
+
<i class="fas fa-plus"></i>
|
| 188 |
+
</button>
|
| 189 |
+
</div>
|
| 190 |
+
</div>
|
| 191 |
+
<div class="mb-4">
|
| 192 |
+
<label for="custom-notes" class="block text-sm font-medium text-gray-700 mb-1">Notes (optional)</label>
|
| 193 |
+
<textarea id="custom-notes" rows="2" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="Add any notes about these applications..."></textarea>
|
| 194 |
+
</div>
|
| 195 |
+
<div class="flex justify-end space-x-3">
|
| 196 |
+
<button id="delete-entry" class="px-4 py-2 text-red-600 hover:text-red-800 hidden">
|
| 197 |
+
<i class="fas fa-trash mr-1"></i> Delete
|
| 198 |
+
</button>
|
| 199 |
+
<button id="cancel-modal" class="px-4 py-2 text-gray-600 hover:text-gray-800">
|
| 200 |
+
Cancel
|
| 201 |
+
</button>
|
| 202 |
+
<button id="save-custom" class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-md">
|
| 203 |
+
Save
|
| 204 |
+
</button>
|
| 205 |
+
</div>
|
| 206 |
+
</div>
|
| 207 |
+
</div>
|
| 208 |
+
|
| 209 |
+
<!-- Settings Modal -->
|
| 210 |
+
<div id="settings-modal" class="fixed inset-0 flex items-center justify-center z-50 modal opacity-0 pointer-events-none">
|
| 211 |
+
<div class="modal-overlay absolute inset-0"></div>
|
| 212 |
+
<div class="bg-white rounded-xl shadow-xl p-6 z-10 w-full max-w-md transform transition-all scale-95">
|
| 213 |
+
<div class="flex justify-between items-center mb-4">
|
| 214 |
+
<h3 class="text-xl font-semibold text-gray-800">Settings</h3>
|
| 215 |
+
<button id="close-settings" class="text-gray-500 hover:text-gray-700">
|
| 216 |
+
<i class="fas fa-times"></i>
|
| 217 |
+
</button>
|
| 218 |
+
</div>
|
| 219 |
+
<div class="mb-4">
|
| 220 |
+
<label for="timezone-select" class="block text-sm font-medium text-gray-700 mb-1">Timezone</label>
|
| 221 |
+
<select id="timezone-select" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
| 222 |
+
<option value="auto">Auto-detect</option>
|
| 223 |
+
<!-- Timezone options will be added dynamically -->
|
| 224 |
+
</select>
|
| 225 |
+
</div>
|
| 226 |
+
<div class="mb-4">
|
| 227 |
+
<label for="daily-goal" class="block text-sm font-medium text-gray-700 mb-1">Daily Goal</label>
|
| 228 |
+
<input type="number" id="daily-goal" min="1" value="5" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500">
|
| 229 |
+
</div>
|
| 230 |
+
<div class="mb-4">
|
| 231 |
+
<label class="block text-sm font-medium text-gray-700 mb-1">Visual Theme</label>
|
| 232 |
+
<div class="flex space-x-2">
|
| 233 |
+
<button data-theme="indigo" class="theme-btn w-8 h-8 rounded-full bg-indigo-600 border-2 border-indigo-800"></button>
|
| 234 |
+
<button data-theme="emerald" class="theme-btn w-8 h-8 rounded-full bg-emerald-600 border-2 border-transparent"></button>
|
| 235 |
+
<button data-theme="rose" class="theme-btn w-8 h-8 rounded-full bg-rose-600 border-2 border-transparent"></button>
|
| 236 |
+
<button data-theme="violet" class="theme-btn w-8 h-8 rounded-full bg-violet-600 border-2 border-transparent"></button>
|
| 237 |
+
<button data-theme="sky" class="theme-btn w-8 h-8 rounded-full bg-sky-600 border-2 border-transparent"></button>
|
| 238 |
+
</div>
|
| 239 |
+
</div>
|
| 240 |
+
<div class="flex justify-end space-x-3">
|
| 241 |
+
<button id="cancel-settings" class="px-4 py-2 text-gray-600 hover:text-gray-800">
|
| 242 |
+
Cancel
|
| 243 |
+
</button>
|
| 244 |
+
<button id="save-settings" class="px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-md">
|
| 245 |
+
Save Settings
|
| 246 |
+
</button>
|
| 247 |
+
</div>
|
| 248 |
</div>
|
| 249 |
</div>
|
| 250 |
|
|
|
|
| 252 |
document.addEventListener('DOMContentLoaded', function() {
|
| 253 |
// Get elements
|
| 254 |
const addBtn = document.getElementById('add-btn');
|
| 255 |
+
const decreaseBtn = document.getElementById('decrease-btn');
|
| 256 |
+
const addCustomBtn = document.getElementById('add-custom-btn');
|
| 257 |
const todayCountEl = document.getElementById('today-count');
|
| 258 |
const totalCountEl = document.getElementById('total-count');
|
| 259 |
const historyList = document.getElementById('history-list');
|
| 260 |
const emptyState = document.getElementById('empty-state');
|
| 261 |
+
const getStartedBtn = document.getElementById('get-started-btn');
|
| 262 |
const todayDateEl = document.getElementById('today-date');
|
| 263 |
+
const timezoneDisplay = document.getElementById('timezone-display');
|
| 264 |
+
const progressRing = document.getElementById('progress-ring');
|
| 265 |
+
const totalProgress = document.getElementById('total-progress');
|
| 266 |
|
| 267 |
+
// Modal elements
|
| 268 |
+
const dateModal = document.getElementById('date-modal');
|
| 269 |
+
const closeModal = document.getElementById('close-modal');
|
| 270 |
+
const cancelModal = document.getElementById('cancel-modal');
|
| 271 |
+
const saveCustom = document.getElementById('save-custom');
|
| 272 |
+
const customDate = document.getElementById('custom-date');
|
| 273 |
+
const customCount = document.getElementById('custom-count');
|
| 274 |
+
const customNotes = document.getElementById('custom-notes');
|
| 275 |
+
const decreaseCustom = document.getElementById('decrease-custom');
|
| 276 |
+
const increaseCustom = document.getElementById('increase-custom');
|
| 277 |
+
const deleteEntryBtn = document.getElementById('delete-entry');
|
| 278 |
+
|
| 279 |
+
// Settings modal elements
|
| 280 |
+
const settingsModal = document.getElementById('settings-modal');
|
| 281 |
+
const settingsBtn = document.getElementById('settings-btn');
|
| 282 |
+
const closeSettings = document.getElementById('close-settings');
|
| 283 |
+
const cancelSettings = document.getElementById('cancel-settings');
|
| 284 |
+
const saveSettings = document.getElementById('save-settings');
|
| 285 |
+
const timezoneSelect = document.getElementById('timezone-select');
|
| 286 |
+
const dailyGoalInput = document.getElementById('daily-goal');
|
| 287 |
+
const themeButtons = document.querySelectorAll('.theme-btn');
|
| 288 |
+
|
| 289 |
+
// Initialize with default settings
|
| 290 |
+
let userSettings = JSON.parse(localStorage.getItem('jobAppSettings')) || {
|
| 291 |
+
timezone: 'auto',
|
| 292 |
+
dailyGoal: 5,
|
| 293 |
+
theme: 'indigo'
|
| 294 |
+
};
|
| 295 |
+
|
| 296 |
+
// Apply theme
|
| 297 |
+
function applyTheme(theme) {
|
| 298 |
+
const colors = {
|
| 299 |
+
indigo: { primary: 'indigo', secondary: 'blue' },
|
| 300 |
+
emerald: { primary: 'emerald', secondary: 'green' },
|
| 301 |
+
rose: { primary: 'rose', secondary: 'pink' },
|
| 302 |
+
violet: { primary: 'violet', secondary: 'purple' },
|
| 303 |
+
sky: { primary: 'sky', secondary: 'cyan' }
|
| 304 |
+
};
|
| 305 |
+
|
| 306 |
+
const colorSet = colors[theme] || colors.indigo;
|
| 307 |
+
|
| 308 |
+
// Update all elements with theme classes
|
| 309 |
+
document.querySelectorAll('[class*="bg-indigo-"]').forEach(el => {
|
| 310 |
+
el.className = el.className.replace(/bg-indigo-\d+/g, `bg-${colorSet.primary}-600`);
|
| 311 |
+
});
|
| 312 |
+
|
| 313 |
+
document.querySelectorAll('[class*="text-indigo-"]').forEach(el => {
|
| 314 |
+
el.className = el.className.replace(/text-indigo-\d+/g, `text-${colorSet.primary}-800`);
|
| 315 |
+
});
|
| 316 |
+
|
| 317 |
+
document.querySelectorAll('[class*="border-indigo-"]').forEach(el => {
|
| 318 |
+
el.className = el.className.replace(/border-indigo-\d+/g, `border-${colorSet.primary}-800`);
|
| 319 |
+
});
|
| 320 |
+
|
| 321 |
+
document.querySelectorAll('[class*="from-blue-"]').forEach(el => {
|
| 322 |
+
el.className = el.className.replace(/from-blue-\d+/g, `from-${colorSet.secondary}-50`);
|
| 323 |
+
});
|
| 324 |
+
|
| 325 |
+
document.querySelectorAll('[class*="to-indigo-"]').forEach(el => {
|
| 326 |
+
el.className = el.className.replace(/to-indigo-\d+/g, `to-${colorSet.primary}-100`);
|
| 327 |
+
});
|
| 328 |
+
|
| 329 |
+
// Update progress bar
|
| 330 |
+
if (totalProgress) {
|
| 331 |
+
totalProgress.className = totalProgress.className.replace(/bg-indigo-\d+/g, `bg-${colorSet.primary}-600`);
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
// Update theme buttons
|
| 335 |
+
themeButtons.forEach(btn => {
|
| 336 |
+
if (btn.dataset.theme === theme) {
|
| 337 |
+
btn.classList.replace('border-transparent', `border-${colorSet.primary}-800`);
|
| 338 |
+
} else {
|
| 339 |
+
btn.classList.replace(`border-${colorSet.primary}-800`, 'border-transparent');
|
| 340 |
+
}
|
| 341 |
+
});
|
| 342 |
+
}
|
| 343 |
+
|
| 344 |
+
// Apply saved theme
|
| 345 |
+
applyTheme(userSettings.theme);
|
| 346 |
+
|
| 347 |
+
// Populate timezone dropdown
|
| 348 |
+
function populateTimezones() {
|
| 349 |
+
const timezones = moment.tz.names();
|
| 350 |
+
timezoneSelect.innerHTML = '<option value="auto">Auto-detect</option>';
|
| 351 |
+
|
| 352 |
+
timezones.forEach(tz => {
|
| 353 |
+
const option = document.createElement('option');
|
| 354 |
+
option.value = tz;
|
| 355 |
+
option.textContent = tz;
|
| 356 |
+
if (tz === userSettings.timezone && userSettings.timezone !== 'auto') {
|
| 357 |
+
option.selected = true;
|
| 358 |
+
}
|
| 359 |
+
timezoneSelect.appendChild(option);
|
| 360 |
+
});
|
| 361 |
+
}
|
| 362 |
+
|
| 363 |
+
populateTimezones();
|
| 364 |
+
|
| 365 |
+
// Get current date based on user's timezone
|
| 366 |
+
function getCurrentDate() {
|
| 367 |
+
if (userSettings.timezone === 'auto') {
|
| 368 |
+
return moment().format('YYYY-MM-DD');
|
| 369 |
+
} else {
|
| 370 |
+
return moment().tz(userSettings.timezone).format('YYYY-MM-DD');
|
| 371 |
+
}
|
| 372 |
+
}
|
| 373 |
+
|
| 374 |
+
// Format date for display
|
| 375 |
+
function formatDateDisplay(dateStr) {
|
| 376 |
+
if (userSettings.timezone === 'auto') {
|
| 377 |
+
return moment(dateStr).format('dddd, MMMM D, YYYY');
|
| 378 |
+
} else {
|
| 379 |
+
return moment.tz(dateStr, userSettings.timezone).format('dddd, MMMM D, YYYY');
|
| 380 |
+
}
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
// Set today's date
|
| 384 |
+
const todayKey = getCurrentDate();
|
| 385 |
+
todayDateEl.textContent = formatDateDisplay(todayKey);
|
| 386 |
+
|
| 387 |
+
// Display timezone
|
| 388 |
+
function displayTimezone() {
|
| 389 |
+
if (userSettings.timezone === 'auto') {
|
| 390 |
+
const detectedTz = moment.tz.guess();
|
| 391 |
+
timezoneDisplay.textContent = detectedTz;
|
| 392 |
+
} else {
|
| 393 |
+
timezoneDisplay.textContent = userSettings.timezone;
|
| 394 |
+
}
|
| 395 |
+
}
|
| 396 |
+
|
| 397 |
+
displayTimezone();
|
| 398 |
+
|
| 399 |
+
// Set default date in modal to today
|
| 400 |
+
customDate.value = todayKey;
|
| 401 |
+
dailyGoalInput.value = userSettings.dailyGoal;
|
| 402 |
|
| 403 |
// Initialize data from localStorage or create new
|
| 404 |
let appData = JSON.parse(localStorage.getItem('jobAppData')) || {
|
|
|
|
| 407 |
};
|
| 408 |
|
| 409 |
// Check if today's date exists in history
|
|
|
|
| 410 |
let todayEntry = appData.history.find(entry => entry.date === todayKey);
|
| 411 |
|
| 412 |
if (!todayEntry) {
|
| 413 |
+
todayEntry = { date: todayKey, count: 0, notes: '' };
|
| 414 |
appData.history.unshift(todayEntry);
|
| 415 |
saveData();
|
| 416 |
}
|
|
|
|
| 418 |
// Update UI
|
| 419 |
updateCounts();
|
| 420 |
renderHistory();
|
| 421 |
+
renderCharts();
|
| 422 |
+
|
| 423 |
+
// Update progress ring
|
| 424 |
+
function updateProgressRing() {
|
| 425 |
+
const radius = 36;
|
| 426 |
+
const circumference = radius * 2 * Math.PI;
|
| 427 |
+
const progress = (todayEntry.count / userSettings.dailyGoal) * 100;
|
| 428 |
+
const offset = circumference - (progress / 100) * circumference;
|
| 429 |
+
|
| 430 |
+
progressRing.style.strokeDasharray = `${circumference} ${circumference}`;
|
| 431 |
+
progressRing.style.strokeDashoffset = offset;
|
| 432 |
+
|
| 433 |
+
if (progress >= 100) {
|
| 434 |
+
progressRing.classList.add('glow');
|
| 435 |
+
} else {
|
| 436 |
+
progressRing.classList.remove('glow');
|
| 437 |
+
}
|
| 438 |
+
}
|
| 439 |
|
| 440 |
// Add button click handler
|
| 441 |
addBtn.addEventListener('click', function() {
|
|
|
|
| 444 |
|
| 445 |
// Add animation effect
|
| 446 |
this.classList.add('pulse');
|
| 447 |
+
setTimeout(() => this.classList.remove('pulse'), 1000);
|
| 448 |
|
| 449 |
saveData();
|
| 450 |
updateCounts();
|
| 451 |
renderHistory();
|
| 452 |
+
renderCharts();
|
| 453 |
});
|
| 454 |
|
| 455 |
+
// Decrease button click handler
|
| 456 |
+
decreaseBtn.addEventListener('click', function() {
|
| 457 |
+
if (todayEntry.count > 0) {
|
| 458 |
+
todayEntry.count--;
|
| 459 |
+
appData.total--;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 460 |
|
| 461 |
saveData();
|
| 462 |
updateCounts();
|
| 463 |
renderHistory();
|
| 464 |
+
renderCharts();
|
| 465 |
+
}
|
| 466 |
+
});
|
| 467 |
+
|
| 468 |
+
// Add custom date button click handler
|
| 469 |
+
addCustomBtn.addEventListener('click', function() {
|
| 470 |
+
// Reset modal values
|
| 471 |
+
customDate.value = todayKey;
|
| 472 |
+
customCount.value = 1;
|
| 473 |
+
customNotes.value = '';
|
| 474 |
+
deleteEntryBtn.classList.add('hidden');
|
| 475 |
+
|
| 476 |
+
// Show modal
|
| 477 |
+
openModal();
|
| 478 |
+
});
|
| 479 |
+
|
| 480 |
+
// Get started button
|
| 481 |
+
getStartedBtn.addEventListener('click', function() {
|
| 482 |
+
addBtn.click();
|
| 483 |
+
});
|
| 484 |
+
|
| 485 |
+
// Modal open/close handlers
|
| 486 |
+
function openModal() {
|
| 487 |
+
dateModal.classList.remove('opacity-0', 'pointer-events-none');
|
| 488 |
+
dateModal.classList.add('opacity-100', 'pointer-events-auto');
|
| 489 |
+
setTimeout(() => {
|
| 490 |
+
dateModal.querySelector('.z-10').classList.remove('scale-95');
|
| 491 |
+
dateModal.querySelector('.z-10').classList.add('scale-100');
|
| 492 |
+
}, 10);
|
| 493 |
+
}
|
| 494 |
+
|
| 495 |
+
function closeModalHandler() {
|
| 496 |
+
dateModal.querySelector('.z-10').classList.remove('scale-100');
|
| 497 |
+
dateModal.querySelector('.z-10').classList.add('scale-95');
|
| 498 |
+
setTimeout(() => {
|
| 499 |
+
dateModal.classList.remove('opacity-100', 'pointer-events-auto');
|
| 500 |
+
dateModal.classList.add('opacity-0', 'pointer-events-none');
|
| 501 |
+
}, 200);
|
| 502 |
+
}
|
| 503 |
+
|
| 504 |
+
closeModal.addEventListener('click', closeModalHandler);
|
| 505 |
+
cancelModal.addEventListener('click', closeModalHandler);
|
| 506 |
+
|
| 507 |
+
// Settings modal handlers
|
| 508 |
+
function openSettingsModal() {
|
| 509 |
+
settingsModal.classList.remove('opacity-0', 'pointer-events-none');
|
| 510 |
+
settingsModal.classList.add('opacity-100', 'pointer-events-auto');
|
| 511 |
+
setTimeout(() => {
|
| 512 |
+
settingsModal.querySelector('.z-10').classList.remove('scale-95');
|
| 513 |
+
settingsModal.querySelector('.z-10').classList.add('scale-100');
|
| 514 |
+
}, 10);
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
+
function closeSettingsHandler() {
|
| 518 |
+
settingsModal.querySelector('.z-10').classList.remove('scale-100');
|
| 519 |
+
settingsModal.querySelector('.z-10').classList.add('scale-95');
|
| 520 |
+
setTimeout(() => {
|
| 521 |
+
settingsModal.classList.remove('opacity-100', 'pointer-events-auto');
|
| 522 |
+
settingsModal.classList.add('opacity-0', 'pointer-events-none');
|
| 523 |
+
}, 200);
|
| 524 |
+
}
|
| 525 |
+
|
| 526 |
+
settingsBtn.addEventListener('click', openSettingsModal);
|
| 527 |
+
closeSettings.addEventListener('click', closeSettingsHandler);
|
| 528 |
+
cancelSettings.addEventListener('click', closeSettingsHandler);
|
| 529 |
+
|
| 530 |
+
// Save settings
|
| 531 |
+
saveSettings.addEventListener('click', function() {
|
| 532 |
+
userSettings.timezone = timezoneSelect.value;
|
| 533 |
+
userSettings.dailyGoal = parseInt(dailyGoalInput.value);
|
| 534 |
+
|
| 535 |
+
// Find and apply selected theme
|
| 536 |
+
themeButtons.forEach(btn => {
|
| 537 |
+
if (btn.classList.contains('border-indigo-800') ||
|
| 538 |
+
btn.classList.contains('border-emerald-800') ||
|
| 539 |
+
btn.classList.contains('border-rose-800') ||
|
| 540 |
+
btn.classList.contains('border-violet-800') ||
|
| 541 |
+
btn.classList.contains('border-sky-800')) {
|
| 542 |
+
userSettings.theme = btn.dataset.theme;
|
| 543 |
+
}
|
| 544 |
+
});
|
| 545 |
+
|
| 546 |
+
localStorage.setItem('jobAppSettings', JSON.stringify(userSettings));
|
| 547 |
+
applyTheme(userSettings.theme);
|
| 548 |
+
displayTimezone();
|
| 549 |
+
closeSettingsHandler();
|
| 550 |
+
|
| 551 |
+
// Update today's date if timezone changed
|
| 552 |
+
const newTodayKey = getCurrentDate();
|
| 553 |
+
if (newTodayKey !== todayKey) {
|
| 554 |
+
// Need to handle timezone change - this would require more complex logic
|
| 555 |
+
// For now, we'll just refresh the display
|
| 556 |
+
location.reload();
|
| 557 |
+
}
|
| 558 |
+
});
|
| 559 |
+
|
| 560 |
+
// Theme selection
|
| 561 |
+
themeButtons.forEach(btn => {
|
| 562 |
+
btn.addEventListener('click', function() {
|
| 563 |
+
themeButtons.forEach(b => {
|
| 564 |
+
b.classList.replace(/border-\w+-800/g, 'border-transparent');
|
| 565 |
+
});
|
| 566 |
+
|
| 567 |
+
const theme = this.dataset.theme;
|
| 568 |
+
const colorSet = {
|
| 569 |
+
indigo: 'indigo',
|
| 570 |
+
emerald: 'emerald',
|
| 571 |
+
rose: 'rose',
|
| 572 |
+
violet: 'violet',
|
| 573 |
+
sky: 'sky'
|
| 574 |
+
}[theme];
|
| 575 |
+
|
| 576 |
+
this.classList.replace('border-transparent', `border-${colorSet}-800`);
|
| 577 |
+
});
|
| 578 |
+
});
|
| 579 |
+
|
| 580 |
+
// Custom count controls
|
| 581 |
+
decreaseCustom.addEventListener('click', function() {
|
| 582 |
+
if (customCount.value > 0) {
|
| 583 |
+
customCount.value--;
|
| 584 |
+
}
|
| 585 |
+
});
|
| 586 |
+
|
| 587 |
+
increaseCustom.addEventListener('click', function() {
|
| 588 |
+
customCount.value++;
|
| 589 |
+
});
|
| 590 |
+
|
| 591 |
+
// Save custom entry
|
| 592 |
+
saveCustom.addEventListener('click', function() {
|
| 593 |
+
const selectedDate = customDate.value;
|
| 594 |
+
const count = parseInt(customCount.value);
|
| 595 |
+
const notes = customNotes.value.trim();
|
| 596 |
+
|
| 597 |
+
if (isNaN(count) || count < 0) {
|
| 598 |
+
alert('Please enter a valid number of applications');
|
| 599 |
+
return;
|
| 600 |
+
}
|
| 601 |
+
|
| 602 |
+
// Find or create entry for selected date
|
| 603 |
+
let entry = appData.history.find(entry => entry.date === selectedDate);
|
| 604 |
+
|
| 605 |
+
if (entry) {
|
| 606 |
+
// Calculate the difference to update total correctly
|
| 607 |
+
const diff = count - (entry.count || 0);
|
| 608 |
+
entry.count = count;
|
| 609 |
+
entry.notes = notes;
|
| 610 |
+
appData.total += diff;
|
| 611 |
+
} else {
|
| 612 |
+
// Create new entry
|
| 613 |
+
entry = { date: selectedDate, count: count, notes: notes };
|
| 614 |
+
appData.history.unshift(entry);
|
| 615 |
+
appData.total += count;
|
| 616 |
+
}
|
| 617 |
+
|
| 618 |
+
// If this is today's entry, update todayEntry reference
|
| 619 |
+
if (selectedDate === todayKey) {
|
| 620 |
+
todayEntry = entry;
|
| 621 |
+
}
|
| 622 |
+
|
| 623 |
+
saveData();
|
| 624 |
+
updateCounts();
|
| 625 |
+
renderHistory();
|
| 626 |
+
renderCharts();
|
| 627 |
+
closeModalHandler();
|
| 628 |
+
});
|
| 629 |
+
|
| 630 |
+
// Delete entry
|
| 631 |
+
deleteEntryBtn.addEventListener('click', function() {
|
| 632 |
+
const selectedDate = customDate.value;
|
| 633 |
+
const entryIndex = appData.history.findIndex(entry => entry.date === selectedDate);
|
| 634 |
+
|
| 635 |
+
if (entryIndex !== -1) {
|
| 636 |
+
const entry = appData.history[entryIndex];
|
| 637 |
+
appData.total -= entry.count;
|
| 638 |
+
appData.history.splice(entryIndex, 1);
|
| 639 |
+
|
| 640 |
+
saveData();
|
| 641 |
+
updateCounts();
|
| 642 |
+
renderHistory();
|
| 643 |
+
renderCharts();
|
| 644 |
+
closeModalHandler();
|
| 645 |
}
|
| 646 |
});
|
| 647 |
|
|
|
|
| 650 |
todayCountEl.textContent = todayEntry.count;
|
| 651 |
totalCountEl.textContent = appData.total;
|
| 652 |
|
| 653 |
+
// Update progress ring
|
| 654 |
+
updateProgressRing();
|
| 655 |
+
|
| 656 |
+
// Update total progress (simple visualization)
|
| 657 |
+
const maxTotal = 100; // Just for visualization purposes
|
| 658 |
+
const progressPercent = Math.min((appData.total / maxTotal) * 100, 100);
|
| 659 |
+
totalProgress.style.width = `${progressPercent}%`;
|
| 660 |
+
|
| 661 |
// Add animation when count changes
|
| 662 |
todayCountEl.classList.add('fade-in');
|
| 663 |
totalCountEl.classList.add('fade-in');
|
|
|
|
| 677 |
emptyState.style.display = 'none';
|
| 678 |
historyList.innerHTML = '';
|
| 679 |
|
| 680 |
+
// Sort history by date (newest first)
|
| 681 |
+
const sortedHistory = [...appData.history].sort((a, b) => new Date(b.date) - new Date(a.date));
|
| 682 |
+
|
| 683 |
+
sortedHistory.forEach(entry => {
|
| 684 |
if (entry.count === 0 && entry.date === todayKey) return;
|
| 685 |
|
| 686 |
const entryDate = new Date(entry.date);
|
| 687 |
const isToday = entry.date === todayKey;
|
| 688 |
|
| 689 |
+
let formattedEntryDate;
|
| 690 |
+
if (userSettings.timezone === 'auto') {
|
| 691 |
+
formattedEntryDate = moment(entry.date).format('ddd, MMM D, YYYY');
|
| 692 |
+
} else {
|
| 693 |
+
formattedEntryDate = moment.tz(entry.date, userSettings.timezone).format('ddd, MMM D, YYYY');
|
| 694 |
+
}
|
| 695 |
|
| 696 |
const historyItem = document.createElement('div');
|
| 697 |
+
historyItem.className = 'flex justify-between items-center p-3 bg-gray-50 rounded-lg fade-in hover:bg-gray-100 cursor-pointer transition-colors';
|
| 698 |
if (isToday) {
|
| 699 |
+
historyItem.classList.add('border-l-4', `border-${userSettings.theme}-500`);
|
| 700 |
}
|
| 701 |
|
| 702 |
historyItem.innerHTML = `
|
| 703 |
<div class="flex items-center">
|
| 704 |
+
<div class="w-10 h-10 rounded-full bg-${userSettings.theme}-100 flex items-center justify-center mr-3">
|
| 705 |
+
<i class="fas fa-briefcase text-${userSettings.theme}-600"></i>
|
| 706 |
</div>
|
| 707 |
<div>
|
| 708 |
+
<p class="font-medium ${isToday ? `text-${userSettings.theme}-800` : 'text-gray-800'}">${formattedEntryDate}</p>
|
| 709 |
+
${entry.notes ? `<p class="text-xs text-gray-500 truncate max-w-xs">${entry.notes}</p>` : ''}
|
| 710 |
</div>
|
| 711 |
</div>
|
| 712 |
+
<div class="flex items-center space-x-2">
|
| 713 |
+
<span class="text-lg font-bold ${isToday ? `text-${userSettings.theme}-800` : 'text-gray-700'}">${entry.count}</span>
|
| 714 |
+
</div>
|
| 715 |
`;
|
| 716 |
|
| 717 |
+
// Add click handler to edit entry
|
| 718 |
+
historyItem.addEventListener('click', function() {
|
| 719 |
+
customDate.value = entry.date;
|
| 720 |
+
customCount.value = entry.count;
|
| 721 |
+
customNotes.value = entry.notes || '';
|
| 722 |
+
deleteEntryBtn.classList.remove('hidden');
|
| 723 |
+
|
| 724 |
+
// Change save button behavior to update existing entry
|
| 725 |
+
saveCustom.onclick = function() {
|
| 726 |
+
const newCount = parseInt(customCount.value);
|
| 727 |
+
const notes = customNotes.value.trim();
|
| 728 |
+
|
| 729 |
+
if (isNaN(newCount) || newCount < 0) {
|
| 730 |
+
alert('Please enter a valid number of applications');
|
| 731 |
+
return;
|
| 732 |
+
}
|
| 733 |
+
|
| 734 |
+
// Calculate the difference to update total correctly
|
| 735 |
+
const diff = newCount - entry.count;
|
| 736 |
+
entry.count = newCount;
|
| 737 |
+
entry.notes = notes;
|
| 738 |
+
appData.total += diff;
|
| 739 |
+
|
| 740 |
+
// If this is today's entry, update todayEntry reference
|
| 741 |
+
if (entry.date === todayKey) {
|
| 742 |
+
todayEntry = entry;
|
| 743 |
+
}
|
| 744 |
+
|
| 745 |
+
saveData();
|
| 746 |
+
updateCounts();
|
| 747 |
+
renderHistory();
|
| 748 |
+
renderCharts();
|
| 749 |
+
closeModalHandler();
|
| 750 |
+
};
|
| 751 |
+
|
| 752 |
+
openModal();
|
| 753 |
+
});
|
| 754 |
+
|
| 755 |
historyList.appendChild(historyItem);
|
| 756 |
});
|
| 757 |
}
|
| 758 |
|
| 759 |
+
// Render charts
|
| 760 |
+
function renderCharts() {
|
| 761 |
+
// Weekly chart
|
| 762 |
+
const weeklyChart = document.querySelector('.flex.justify-between.items-end');
|
| 763 |
+
weeklyChart.innerHTML = '';
|
| 764 |
+
|
| 765 |
+
// Get last 7 days
|
| 766 |
+
const days = [];
|
| 767 |
+
for (let i = 6; i >= 0; i--) {
|
| 768 |
+
const date = new Date();
|
| 769 |
+
date.setDate(date.getDate() - i);
|
| 770 |
+
const dateKey = moment(date).format('YYYY-MM-DD');
|
| 771 |
+
days.push(dateKey);
|
| 772 |
+
}
|
| 773 |
+
|
| 774 |
+
// Find max count for scaling
|
| 775 |
+
let maxCount = 1;
|
| 776 |
+
days.forEach(day => {
|
| 777 |
+
const entry = appData.history.find(e => e.date === day);
|
| 778 |
+
if (entry && entry.count > maxCount) {
|
| 779 |
+
maxCount = entry.count;
|
| 780 |
+
}
|
| 781 |
+
});
|
| 782 |
+
|
| 783 |
+
// Create bars for each day
|
| 784 |
+
days.forEach(day => {
|
| 785 |
+
const entry = appData.history.find(e => e.date === day) || { count: 0 };
|
| 786 |
+
const heightPercent = (entry.count / maxCount) * 100;
|
| 787 |
+
const isToday = day === todayKey;
|
| 788 |
+
|
| 789 |
+
const dayName = moment(day).format('ddd');
|
| 790 |
+
|
| 791 |
+
const barContainer = document.createElement('div');
|
| 792 |
+
barContainer.className = 'flex flex-col items-center';
|
| 793 |
+
|
| 794 |
+
const bar = document.createElement('div');
|
| 795 |
+
bar.className = `chart-bar w-8 rounded-t-md bg-${userSettings.theme}-${isToday ? '600' : '400'} mb-1`;
|
| 796 |
+
bar.style.height = `${heightPercent}%`;
|
| 797 |
+
bar.style.minHeight = '4px';
|
| 798 |
+
|
| 799 |
+
const dayLabel = document.createElement('span');
|
| 800 |
+
dayLabel.className = 'text-xs text-gray-500';
|
| 801 |
+
dayLabel.textContent = dayName;
|
| 802 |
+
|
| 803 |
+
const countLabel = document.createElement('span');
|
| 804 |
+
countLabel.className = `text-xs font-medium text-${userSettings.theme}-800`;
|
| 805 |
+
countLabel.textContent = entry.count;
|
| 806 |
+
|
| 807 |
+
barContainer.appendChild(countLabel);
|
| 808 |
+
barContainer.appendChild(bar);
|
| 809 |
+
barContainer.appendChild(dayLabel);
|
| 810 |
+
weeklyChart.appendChild(barContainer);
|
| 811 |
+
});
|
| 812 |
+
|
| 813 |
+
// Monthly chart (simplified)
|
| 814 |
+
const monthlyChart = document.getElementById('monthly-chart');
|
| 815 |
+
monthlyChart.innerHTML = '';
|
| 816 |
+
|
| 817 |
+
// Create a simple bar for monthly progress
|
| 818 |
+
const thisMonth = moment().format('YYYY-MM');
|
| 819 |
+
const monthEntries = appData.history.filter(entry => entry.date.startsWith(thisMonth));
|
| 820 |
+
const monthTotal = monthEntries.reduce((sum, entry) => sum + entry.count, 0);
|
| 821 |
+
|
| 822 |
+
const monthContainer = document.createElement('div');
|
| 823 |
+
monthContainer.className = 'flex flex-col h-full justify-center';
|
| 824 |
+
|
| 825 |
+
const monthBar = document.createElement('div');
|
| 826 |
+
monthBar.className = `h-6 rounded-full bg-${userSettings.theme}-200 relative overflow-hidden`;
|
| 827 |
+
|
| 828 |
+
const monthProgress = document.createElement('div');
|
| 829 |
+
monthProgress.className = `absolute top-0 left-0 h-full bg-${userSettings.theme}-600 rounded-full`;
|
| 830 |
+
monthProgress.style.width = `${Math.min((monthTotal / 50) * 100, 100)}%`;
|
| 831 |
+
|
| 832 |
+
const monthText = document.createElement('div');
|
| 833 |
+
monthText.className = 'flex justify-between items-center mt-2';
|
| 834 |
+
monthText.innerHTML = `
|
| 835 |
+
<span class="text-sm text-gray-600">${moment().format('MMMM')}</span>
|
| 836 |
+
<span class="text-sm font-medium text-${userSettings.theme}-800">${monthTotal} apps</span>
|
| 837 |
+
`;
|
| 838 |
+
|
| 839 |
+
monthBar.appendChild(monthProgress);
|
| 840 |
+
monthContainer.appendChild(monthBar);
|
| 841 |
+
monthContainer.appendChild(monthText);
|
| 842 |
+
monthlyChart.appendChild(monthContainer);
|
| 843 |
+
}
|
| 844 |
+
|
| 845 |
// Save data to localStorage
|
| 846 |
function saveData() {
|
| 847 |
localStorage.setItem('jobAppData', JSON.stringify(appData));
|
prompts.txt
CHANGED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
add a few functionalities for me to also decrease the number if I added by mistake. also let me choose another date if I want to add the number for previous days.
|
| 2 |
+
add option for decreasing as well. also you got the todays date wrong. add the option to add time zone or location so it can figure the todays day correct. also make the visualization better and delete the clear data option. add as many editing options as possible.
|