Add weekly progress and validation

This commit is contained in:
2026-07-10 16:23:50 +02:00
parent 23416ee382
commit 1cdef43a87
6 changed files with 39 additions and 11 deletions
+4 -2
View File
@@ -9,6 +9,7 @@ Habit Tracker ist eine PHP-Webanwendung, mit der Benutzer eigene Gewohnheiten an
- Habits erstellen, bearbeiten, loeschen und fuer heute abhaken - Habits erstellen, bearbeiten, loeschen und fuer heute abhaken
- Kategorien und Statusfilter im Dashboard - Kategorien und Statusfilter im Dashboard
- Tagesprotokoll ueber `habit_logs` - Tagesprotokoll ueber `habit_logs`
- Wochenfortschritt im Dashboard gegen das persoenliche Wochenziel
- Responsive Oberflaeche fuer Desktop und Smartphone - Responsive Oberflaeche fuer Desktop und Smartphone
- Serverseitige Validierung und klare Fehlermeldungen - Serverseitige Validierung und klare Fehlermeldungen
- Schutz gegen SQL-Injection durch PDO Prepared Statements - Schutz gegen SQL-Injection durch PDO Prepared Statements
@@ -67,8 +68,9 @@ Die Datenbank besteht aus 5 Tabellen:
3. Habit mit Kategorie und Wochenziel erstellen. 3. Habit mit Kategorie und Wochenziel erstellen.
4. Habit im Dashboard filtern. 4. Habit im Dashboard filtern.
5. Habit fuer heute abhaken. 5. Habit fuer heute abhaken.
6. Habit bearbeiten und loeschen. 6. Wochenfortschritt im Dashboard kontrollieren.
7. Seite auf Smartphone-Breite pruefen. 7. Habit bearbeiten und loeschen.
8. Seite auf Smartphone-Breite pruefen.
## Bewertungsbezug ## Bewertungsbezug
+10 -1
View File
@@ -119,6 +119,16 @@ class HabitController
if ($title === '') { if ($title === '') {
$errors['title'] = 'Bitte gib einen Namen fuer den Habit ein.'; $errors['title'] = 'Bitte gib einen Namen fuer den Habit ein.';
} elseif (mb_strlen($title) > 120) {
$errors['title'] = 'Der Name darf maximal 120 Zeichen lang sein.';
}
if (mb_strlen($description) > 1000) {
$errors['description'] = 'Die Beschreibung darf maximal 1000 Zeichen lang sein.';
}
if ($categoryId !== '' && !Category::exists((int) $categoryId)) {
$errors['category_id'] = 'Bitte waehle eine gueltige Kategorie aus.';
} }
if ($targetPerWeek < 1 || $targetPerWeek > 7) { if ($targetPerWeek < 1 || $targetPerWeek > 7) {
@@ -134,4 +144,3 @@ class HabitController
]; ];
} }
} }
+9 -1
View File
@@ -1,4 +1,4 @@
<?php <?php
class Category class Category
{ {
@@ -8,4 +8,12 @@ class Category
->query('SELECT id, name, color FROM categories ORDER BY name') ->query('SELECT id, name, color FROM categories ORDER BY name')
->fetchAll(); ->fetchAll();
} }
public static function exists(int $id): bool
{
$statement = Database::connection()->prepare('SELECT id FROM categories WHERE id = :id LIMIT 1');
$statement->execute(['id' => $id]);
return (bool) $statement->fetch();
}
} }
+6 -2
View File
@@ -1,4 +1,4 @@
<?php <?php
class Habit class Habit
{ {
@@ -19,7 +19,11 @@ class Habit
} }
$sql = 'SELECT h.*, c.name AS category_name, c.color AS category_color, $sql = 'SELECT h.*, c.name AS category_name, c.color AS category_color,
CASE WHEN hl.id IS NULL THEN 0 ELSE 1 END AS done_today CASE WHEN hl.id IS NULL THEN 0 ELSE 1 END AS done_today,
(SELECT COUNT(*)
FROM habit_logs hwl
WHERE hwl.habit_id = h.id
AND YEARWEEK(hwl.completed_on, 1) = YEARWEEK(CURRENT_DATE, 1)) AS completed_this_week
FROM habits h FROM habits h
LEFT JOIN categories c ON c.id = h.category_id LEFT JOIN categories c ON c.id = h.category_id
LEFT JOIN habit_logs hl LEFT JOIN habit_logs hl
+1 -2
View File
@@ -43,7 +43,7 @@
<p><?= e($habit['description']) ?></p> <p><?= e($habit['description']) ?></p>
<small> <small>
<?= e($habit['category_name'] ?? 'Ohne Kategorie') ?> | <?= e($habit['category_name'] ?? 'Ohne Kategorie') ?> |
Ziel: <?= (int) $habit['target_per_week'] ?>x pro Woche Woche: <?= (int) $habit['completed_this_week'] ?> / <?= (int) $habit['target_per_week'] ?> erledigt
</small> </small>
</div> </div>
</div> </div>
@@ -64,4 +64,3 @@
</article> </article>
<?php endforeach; ?> <?php endforeach; ?>
</section> </section>
+9 -3
View File
@@ -14,6 +14,7 @@
id="title" id="title"
name="title" name="title"
type="text" type="text"
maxlength="120"
class="<?= !empty($errors['title']) ? 'input-error' : '' ?>" class="<?= !empty($errors['title']) ? 'input-error' : '' ?>"
value="<?= e($habit['title'] ?? '') ?>" value="<?= e($habit['title'] ?? '') ?>"
required required
@@ -24,10 +25,13 @@
<?php endif; ?> <?php endif; ?>
<label for="description">Beschreibung <span>optional</span></label> <label for="description">Beschreibung <span>optional</span></label>
<textarea id="description" name="description" rows="4"><?= e($habit['description'] ?? '') ?></textarea> <textarea id="description" name="description" rows="4" maxlength="1000"><?= e($habit['description'] ?? '') ?></textarea>
<?php if (!empty($errors['description'])): ?>
<small class="field-error"><?= e($errors['description']) ?></small>
<?php endif; ?>
<label for="category_id">Kategorie</label> <label for="category_id">Kategorie</label>
<select id="category_id" name="category_id"> <select id="category_id" name="category_id" class="<?= !empty($errors['category_id']) ? 'input-error' : '' ?>">
<option value="">Keine Kategorie</option> <option value="">Keine Kategorie</option>
<?php foreach ($categories as $category): ?> <?php foreach ($categories as $category): ?>
<option value="<?= (int) $category['id'] ?>" <?= (string) ($habit['category_id'] ?? '') === (string) $category['id'] ? 'selected' : '' ?>> <option value="<?= (int) $category['id'] ?>" <?= (string) ($habit['category_id'] ?? '') === (string) $category['id'] ? 'selected' : '' ?>>
@@ -35,6 +39,9 @@
</option> </option>
<?php endforeach; ?> <?php endforeach; ?>
</select> </select>
<?php if (!empty($errors['category_id'])): ?>
<small class="field-error"><?= e($errors['category_id']) ?></small>
<?php endif; ?>
<label for="target_per_week">Ziel pro Woche</label> <label for="target_per_week">Ziel pro Woche</label>
<input <input
@@ -55,4 +62,3 @@
<button type="submit"><?= $isEdit ? 'Speichern' : 'Erstellen' ?></button> <button type="submit"><?= $isEdit ? 'Speichern' : 'Erstellen' ?></button>
</form> </form>
</section> </section>