From f551dfe00cae086955a1847ccb045999eded8af3 Mon Sep 17 00:00:00 2001 From: jafreli Date: Sat, 19 Jul 2025 00:40:43 +0200 Subject: [PATCH] Umbau auf 7 reihen --- app.py | 80 +++++++++++++++++++++++++++++++----- habits.json | 2 +- static/css/style.css | 37 +++++++++++++++++ static/js/script.js | 98 +++++++++++++++++++++++++++++++++++--------- 4 files changed, 185 insertions(+), 32 deletions(-) diff --git a/app.py b/app.py index 2c9d71b..12d213b 100644 --- a/app.py +++ b/app.py @@ -18,6 +18,32 @@ def get_last_30_days(): dates.append(date.strftime('%Y-%m-%d')) return dates[::-1] # Reverse to have oldest first +# Helper function to get dates organized by weekday for more weeks to fill the width +def get_weekly_dates(): + today = datetime.now() + # Find the most recent Monday (start of current week) + days_since_monday = today.weekday() # Monday = 0, Sunday = 6 + current_monday = today - timedelta(days=days_since_monday) + + weekly_data = {} + weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'] + + # Use more weeks to fill the available width (about 15-20 weeks) + num_weeks = 18 # This should provide good coverage without being too much + + for weekday_idx in range(7): # 0 = Monday, 6 = Sunday + weekly_data[weekdays[weekday_idx]] = [] + for week_offset in range(num_weeks): + date = current_monday + timedelta(days=weekday_idx - (week_offset * 7)) + if date <= today: # Only include dates up to today + weekly_data[weekdays[weekday_idx]].append(date.strftime('%Y-%m-%d')) + + # Reverse each weekday list to show oldest first (left to right) + for weekday in weekly_data: + weekly_data[weekday] = weekly_data[weekday][::-1] + + return weekly_data + @app.route('/') def index(): return render_template('index.html') @@ -26,7 +52,7 @@ def index(): def get_habits(): raw_habits = db.all() formatted_habits = [] - last_30_days = get_last_30_days() + weekly_dates = get_weekly_dates() for habit_doc in raw_habits: # Handle migration from old format (list) to new format (dict) @@ -48,17 +74,21 @@ def get_habits(): 'daily_target': habit_doc.get('daily_target', 1) } - completion_history = {} - for date_str in last_30_days: - completed_count = completed_dates.get(date_str, 0) - target_count = current_habit_data['daily_target'] - completion_history[date_str] = { - 'completed': completed_count, - 'target': target_count, - 'is_complete': completed_count >= target_count - } + # Create weekly completion history + weekly_completion = {} + for weekday, dates in weekly_dates.items(): + weekly_completion[weekday] = [] + for date_str in dates: + completed_count = completed_dates.get(date_str, 0) + target_count = current_habit_data['daily_target'] + weekly_completion[weekday].append({ + 'date': date_str, + 'completed': completed_count, + 'target': target_count, + 'is_complete': completed_count >= target_count + }) - current_habit_data['completion_history'] = completion_history + current_habit_data['weekly_completion'] = weekly_completion formatted_habits.append(current_habit_data) return jsonify(formatted_habits) @@ -160,6 +190,34 @@ def update_habit(habit_id): db.update(update_data, doc_ids=[habit_id]) return jsonify({'message': f'Habit {habit_id} updated', **update_data}) +@app.route('/habits//reset', methods=['POST']) +def reset_habit(habit_id): + date_to_reset = request.json.get('date', get_current_date()) + + habit = db.get(doc_id=habit_id) + if not habit: + return jsonify({'error': 'Habit not found'}), 404 + + # Handle migration from old format (list) to new format (dict) + completed_dates = habit.get('completed_dates', {}) + if isinstance(completed_dates, list): + new_completed_dates = {} + for date in completed_dates: + new_completed_dates[date] = 1 + completed_dates = new_completed_dates + + # Reset completion count for the date to 0 + if date_to_reset in completed_dates: + del completed_dates[date_to_reset] + + db.update({'completed_dates': completed_dates}, doc_ids=[habit_id]) + + return jsonify({ + 'message': f'Habit {habit_id} reset for {date_to_reset}', + 'completed_dates': completed_dates, + 'current_count': 0 + }) + @app.route('/habits/', methods=['DELETE']) def delete_habit(habit_id): habit = db.get(doc_id=habit_id) diff --git a/habits.json b/habits.json index 7699372..dcd5928 100644 --- a/habits.json +++ b/habits.json @@ -1 +1 @@ -{"_default": {"1": {"name": "Mega Zock", "completed_dates": {"2025-07-14": 1, "2025-08-01": 1, "2025-08-09": 1, "2025-08-17": 1, "2025-07-01": 1, "2025-07-16": 1, "2025-07-10": 1, "2025-07-15": 1, "2025-07-09": 1, "2025-07-11": 1, "2025-07-12": 1, "2025-07-13": 1, "2025-07-06": 1, "2025-07-05": 1, "2025-07-04": 1, "2025-07-03": 1, "2025-07-02": 1, "2025-07-08": 1, "2025-07-07": 1}, "color": "#14cc1a"}, "2": {"name": "Laufen", "completed_dates": {}, "color": "#1a3ed1"}, "4": {"name": "Essen", "completed_dates": {"2025-07-14": 3, "2025-07-15": 3, "2025-07-16": 3, "2025-07-17": 3, "2025-07-18": 3}, "color": "#f01000", "daily_target": 3}}} \ No newline at end of file +{"_default": {"1": {"name": "Mega Zock", "completed_dates": {"2025-07-14": 1, "2025-08-01": 1, "2025-08-09": 1, "2025-08-17": 1, "2025-07-01": 1, "2025-07-10": 1, "2025-07-15": 1, "2025-07-11": 1, "2025-07-12": 1, "2025-07-13": 1, "2025-07-06": 1, "2025-07-05": 1, "2025-07-04": 1, "2025-07-03": 1, "2025-07-08": 1, "2025-07-07": 1, "2025-07-19": 1, "2025-07-16": 1, "2025-07-02": 1, "2025-07-09": 1, "2025-07-17": 1, "2025-07-18": 1}, "color": "#14cc1a"}, "2": {"name": "Laufen", "completed_dates": {"2025-07-14": 3, "2025-07-15": 4, "2025-06-02": 1, "2025-06-03": 1, "2025-07-19": 1}, "color": "#1a3ed1", "daily_target": 1}, "4": {"name": "Essen", "completed_dates": {"2025-07-14": 3, "2025-07-15": 3, "2025-07-16": 3, "2025-07-17": 3, "2025-07-18": 3}, "color": "#f01000", "daily_target": 3}}} \ No newline at end of file diff --git a/static/css/style.css b/static/css/style.css index 9c5dca1..be9d975 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -105,6 +105,38 @@ h1 { color: var(--habit-color, #4CAF50); } +/* Weekly Grid Layout */ +.weekly-grid { + display: flex; + flex-direction: column; + gap: 3px; + padding: 10px 0; +} + +.weekday-row { + display: flex; + align-items: center; + gap: 3px; +} + +.weekday-label { + width: 30px; + font-size: 0.8em; + font-weight: bold; + color: #b0b0b0; + text-align: center; + flex-shrink: 0; +} + +.weekday-row .date-square { + flex: 1; + min-width: 0; + aspect-ratio: 1; + max-width: 25px; + height: auto; +} + +/* Legacy date-grid for backward compatibility */ .date-grid { display: grid; grid-template-columns: repeat(30, 1fr); @@ -133,6 +165,11 @@ h1 { cursor: pointer; } +.date-square-readonly { + cursor: default; + opacity: 0.9; +} + .date-square.completed { background-color: var(--habit-color, #4CAF50); } diff --git a/static/js/script.js b/static/js/script.js index e7a6fe7..8c7a7fd 100644 --- a/static/js/script.js +++ b/static/js/script.js @@ -79,7 +79,6 @@ async function fetchHabits() { function renderHabits(habits) { habitListDiv.innerHTML = ''; // Clear previous habits - const last30Days = getPastDates(30); habits.forEach(habit => { const habitItem = document.createElement('div'); @@ -127,27 +126,48 @@ function renderHabits(habits) { habitHeader.appendChild(habitActions); habitItem.appendChild(habitHeader); - const dateGrid = document.createElement('div'); - dateGrid.className = 'date-grid'; + // Create weekly grid instead of single row + const weeklyGrid = document.createElement('div'); + weeklyGrid.className = 'weekly-grid'; - last30Days.forEach(date => { - const dateSquare = document.createElement('div'); - dateSquare.className = 'date-square'; - const dateCount = habit.completed_dates[date] || 0; - const isCompleted = dateCount >= dailyTarget; + const weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag']; - // Apply gradient styling based on completion percentage - applyCompletionStyling(dateSquare, dateCount, dailyTarget, habitColor); + weekdays.forEach(weekday => { + const weekdayRow = document.createElement('div'); + weekdayRow.className = 'weekday-row'; - // Show count in tooltip if target > 1 - if (dailyTarget > 1) { - dateSquare.title = `${date}: ${dateCount}/${dailyTarget}`; - } else { - dateSquare.title = date; - } - dateGrid.appendChild(dateSquare); + // Weekday label + const weekdayLabel = document.createElement('div'); + weekdayLabel.className = 'weekday-label'; + weekdayLabel.textContent = weekday.substring(0, 2); // Show first 2 letters + weekdayRow.appendChild(weekdayLabel); + + // Date squares for this weekday + const weekdayDates = habit.weekly_completion[weekday] || []; + weekdayDates.forEach(dateInfo => { + const dateSquare = document.createElement('div'); + dateSquare.className = 'date-square date-square-readonly'; + dateSquare.dataset.date = dateInfo.date; + + // Apply gradient styling based on completion percentage + applyCompletionStyling(dateSquare, dateInfo.completed, dateInfo.target, habitColor); + + // Show count in tooltip if target > 1 + if (dateInfo.target > 1) { + dateSquare.title = `${dateInfo.date}: ${dateInfo.completed}/${dateInfo.target}`; + } else { + dateSquare.title = dateInfo.date; + } + + // No click handler - dates are read-only in main view + + weekdayRow.appendChild(dateSquare); + }); + + weeklyGrid.appendChild(weekdayRow); }); - habitItem.appendChild(dateGrid); + + habitItem.appendChild(weeklyGrid); // Streak Counter hinzufügen const streakContainer = document.createElement('div'); @@ -421,15 +441,53 @@ async function deleteHabitFromModal() { } } -async function toggleTodayCompletion(habitId, date, buttonElement, dailyTarget = 1) { +async function toggleDateCompletion(event, habitId, date) { try { - // Always increment completion count + // Always increment completion count for weekly grid clicks const response = await fetch(`/habits/${habitId}/complete`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ date: date }) }); + const data = await response.json(); + if (response.ok) { + // Refresh the entire habits display to show updated completion status + fetchHabits(); + } else { + console.error('Failed to toggle habit completion:', data.error); + } + } catch (error) { + console.error('Error toggling habit completion:', error); + } +} + +async function toggleTodayCompletion(habitId, date, buttonElement, dailyTarget = 1) { + try { + // Get current count first to check if we should increment or reset + const currentHabits = await fetch('/habits'); + const habits = await currentHabits.json(); + const currentHabit = habits.find(h => h.id === habitId); + const currentCount = currentHabit?.completed_dates[date] || 0; + + let response; + + if (currentCount >= dailyTarget) { + // If already at or above target, reset to 0 with single API call + response = await fetch(`/habits/${habitId}/reset`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ date: date }) + }); + } else { + // Increment completion count + response = await fetch(`/habits/${habitId}/complete`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ date: date }) + }); + } + const data = await response.json(); if (response.ok) { const newCount = data.current_count;