feat: 10.0 - Frontend - Check habit interaction
This commit is contained in:
@@ -144,6 +144,46 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Habit checkbox */
|
||||
.habit-checkbox {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
flex-shrink: 0;
|
||||
background: var(--bg-base);
|
||||
}
|
||||
|
||||
.habit-checkbox:hover:not(.disabled) {
|
||||
border-color: var(--accent);
|
||||
background: var(--accent-light, rgba(99, 102, 241, 0.1));
|
||||
}
|
||||
|
||||
.habit-checkbox.checked {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.habit-checkbox.checked svg {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.habit-checkbox.disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.habit-checkbox svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* Loading state */
|
||||
.loading-state {
|
||||
text-align: center;
|
||||
@@ -575,8 +615,16 @@
|
||||
// Determine icon based on frequency
|
||||
const iconName = habit.frequency === 'daily' ? 'calendar' : 'clock';
|
||||
|
||||
// Checkbox state
|
||||
const isChecked = habit.checkedToday || false;
|
||||
const checkboxClass = isChecked ? 'habit-checkbox checked disabled' : 'habit-checkbox';
|
||||
const checkIcon = isChecked ? '<i data-lucide="check"></i>' : '';
|
||||
|
||||
// Create card HTML
|
||||
card.innerHTML = `
|
||||
<div class="${checkboxClass}" data-habit-id="${habit.id}" onclick="checkHabit('${habit.id}', this)">
|
||||
${checkIcon}
|
||||
</div>
|
||||
<div class="habit-icon">
|
||||
<i data-lucide="${iconName}"></i>
|
||||
</div>
|
||||
@@ -585,7 +633,7 @@
|
||||
<div class="habit-frequency">${habit.frequency === 'daily' ? 'Zilnic' : 'Săptămânal'}</div>
|
||||
</div>
|
||||
<div class="habit-streak">
|
||||
<span>${habit.streak || 0}</span>
|
||||
<span id="streak-${habit.id}">${habit.streak || 0}</span>
|
||||
<span>🔥</span>
|
||||
</div>
|
||||
`;
|
||||
@@ -600,6 +648,58 @@
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
// Check habit (mark as done for today)
|
||||
async function checkHabit(habitId, checkboxElement) {
|
||||
// Don't allow checking if already checked
|
||||
if (checkboxElement.classList.contains('disabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Optimistic UI update
|
||||
checkboxElement.classList.add('checked', 'disabled');
|
||||
checkboxElement.innerHTML = '<i data-lucide="check"></i>';
|
||||
lucide.createIcons();
|
||||
|
||||
// Store original state for rollback
|
||||
const originalCheckbox = checkboxElement.cloneNode(true);
|
||||
const streakElement = document.getElementById(`streak-${habitId}`);
|
||||
const originalStreak = streakElement ? streakElement.textContent : '0';
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/habits/${habitId}/check`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to check habit');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Update streak with server value
|
||||
if (streakElement && data.habit && data.habit.streak !== undefined) {
|
||||
streakElement.textContent = data.habit.streak;
|
||||
}
|
||||
|
||||
showToast('Obișnuință bifată! 🎉');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error checking habit:', error);
|
||||
|
||||
// Revert checkbox on error
|
||||
checkboxElement.classList.remove('checked', 'disabled');
|
||||
checkboxElement.innerHTML = '';
|
||||
|
||||
// Revert streak
|
||||
if (streakElement) {
|
||||
streakElement.textContent = originalStreak;
|
||||
}
|
||||
|
||||
showToast('Eroare la bifarea obișnuinței. Încearcă din nou.');
|
||||
}
|
||||
}
|
||||
|
||||
// Load habits on page load
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadHabits();
|
||||
|
||||
Reference in New Issue
Block a user