Phase 9: division/season tracking, club customization, pack history, notifications
CI / Build, lint & test (push) Failing after 1m42s

- GET /division returns live season stats (points, record, promotion threshold)
- PUT /club allows updating club name and manager_name
- GET /packs/history returns opened packs with full card definitions
- GET /notifications dynamically surfaces completed objectives, expiring loans, season end
- Club model gains manager_name column (migration 0006 already added it)
- Pack model gains opened_cards and opened_at; pack SELECT queries updated
- 9 new integration tests — all 45 pass, clippy clean

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-06-25 16:50:35 -07:00
parent bfd6de6896
commit a749fba93c
11 changed files with 412 additions and 11 deletions
+44
View File
@@ -57,6 +57,50 @@ pub async fn get_packs(State(state): State<AppState>) -> AppResult<Json<Value>>
Ok(Json(json!({ "packs": with_defs })))
}
/// Return recently opened packs with the card IDs they contained.
pub async fn get_pack_history(State(state): State<AppState>) -> AppResult<Json<Value>> {
let profile = profile_svc::get_active_profile(&state.pool).await?;
let club = club_svc::get_club_by_profile(&state.pool, &profile.id).await?;
let opened = sqlx::query_as::<_, crate::models::pack::Pack>(
"SELECT id, club_id, definition_id, opened, created_at, opened_cards, opened_at \
FROM packs WHERE club_id = ? AND opened = 1 ORDER BY opened_at DESC LIMIT 50",
)
.bind(&club.id)
.fetch_all(&state.pool)
.await?;
let history: Vec<Value> = opened
.iter()
.map(|p| {
let def = state.pack_defs.iter().find(|d| d.id == p.definition_id);
// Expand card_ids into full card definitions
let cards: Vec<Value> = p
.opened_cards
.as_deref()
.and_then(|s| serde_json::from_str::<Vec<String>>(s).ok())
.unwrap_or_default()
.iter()
.map(|id| {
json!({
"card_id": id,
"card": state.card_db.get(id),
})
})
.collect();
json!({
"pack_id": p.id,
"definition_id": p.definition_id,
"name": def.map(|d| &d.name),
"opened_at": p.opened_at,
"cards": cards,
})
})
.collect();
Ok(Json(json!({ "history": history, "total": history.len() })))
}
pub async fn post_open_pack(
State(state): State<AppState>,
Path(pack_id): Path<String>,