Add weekly progress and validation
This commit is contained in:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user