feat: goals — "watch N movies in YEAR" with progress bar

Domain: Goal entity, UserSettings (federation toggle), RemoteGoalEntry.
Ports: GoalRepository, UserSettingsRepository, RemoteGoalRepository.
Adapters: sqlite + postgres repos, migrations, AP content query extensions.
Application: CRUD use cases (create/update/delete/get/list), settings use cases.
API: 7 endpoints (/goals CRUD, /users/{id}/goals, /settings) with utoipa docs.
Federation: GoalObject (Note + goal discriminator), outbound broadcast with
per-user toggle, inbound GoalObjectHandler in CompositeObjectHandler.
SPA: API client + hooks, GoalCard (shadcn Card+Progress+DropdownMenu),
GoalSheet (Drawer), profile integration (editable own, read-only others),
federation toggle in settings (Switch).
Classic HTML: glassmorphic goal card on profile, Frutiger Aero styling.
Progress computed from existing reviews — backwards compatible.
This commit is contained in:
2026-06-08 22:37:52 +02:00
parent 213f9a2433
commit fff5f4af2f
67 changed files with 2747 additions and 28 deletions

View File

@@ -1300,3 +1300,52 @@ form button[type="submit"]:hover {
.wu-card { padding: 1.5rem; }
.wu-genre-name { width: 5rem; }
}
/* ── Goals ─────────────────────────────────────────────────────────────────── */
.goals-section { margin: 1rem 0; display: flex; flex-direction: column; gap: 0.5rem; }
.goal-card {
background: var(--glass-bg);
backdrop-filter: var(--blur);
-webkit-backdrop-filter: var(--blur);
border: 1px solid var(--glass-border);
border-radius: 12px;
padding: 0.75rem 1rem;
box-shadow: var(--glass-shadow), var(--glass-inset);
position: relative;
overflow: hidden;
}
.goal-card::before {
content: "";
position: absolute;
top: 0; left: 0; right: 0;
height: 50%;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.08), transparent);
border-radius: inherit;
pointer-events: none;
}
.goal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.4rem; }
.goal-label { font-weight: 700; font-size: 0.85rem; color: var(--primary); }
.goal-count { font-size: 0.8rem; color: var(--text-muted); }
.progress-track {
background: rgba(255, 255, 255, 0.08);
border-radius: 999px;
height: 6px;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.progress-fill {
height: 100%;
border-radius: 999px;
background: linear-gradient(90deg, var(--primary-mid), var(--primary));
box-shadow: 0 0 8px var(--primary-glow);
transition: width 0.4s ease;
}
.goal-complete {
font-size: 0.75rem;
color: var(--primary);
margin-top: 0.3rem;
display: inline-flex;
align-items: center;
gap: 0.25rem;
text-shadow: 0 0 6px var(--primary-glow);
}