feat: daily suggestion cache, saved ideas (Swipe File), remove Piani Attivi

Backend:
- Suggestions cached in DB per user, regenerated only after 24h
- ?force=true parameter to regenerate on demand
- New endpoints: GET/POST/DELETE /content/ideas for saved ideas
- POST /content/ideas/{id}/mark-used to track usage

Frontend:
- Dashboard: suggestions loaded from cache, not regenerated on every visit
- Dashboard: "Salva idea" button on each suggestion card
- Dashboard: "Dammi altri suggerimenti" CTA to force regeneration
- Dashboard: removed "Piani Attivi" stat card
- SavedIdeas page: list saved ideas, add new, delete, generate from idea
- Sidebar: added "Idee" nav item after "Contenuti"
- App.jsx: added /ideas route

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michele
2026-04-06 01:57:44 +02:00
parent 72c5379706
commit 228edf2a91
5 changed files with 390 additions and 48 deletions

View File

@@ -342,34 +342,14 @@ def approve_post(
return post
@router.get("/suggestions")
def get_topic_suggestions(
character_id: int | None = Query(None),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Suggest 3 content topics based on character profile and recent posts."""
if character_id:
character = (
db.query(Character)
.filter(Character.id == character_id, Character.user_id == current_user.id)
.first()
)
else:
character = (
db.query(Character)
.filter(Character.user_id == current_user.id, Character.is_active == True)
.first()
)
if not character:
return {"suggestions": [], "character_id": None}
def _generate_suggestions(db: Session, current_user: User, character) -> list[str]:
"""Internal: call LLM to generate topic suggestions."""
provider_name = _get_setting(db, "llm_provider", current_user.id)
api_key = _get_setting(db, "llm_api_key", current_user.id)
model = _get_setting(db, "llm_model", current_user.id)
if not provider_name or not api_key:
return {"suggestions": [], "character_id": character.id, "needs_setup": True}
return []
recent_posts = (
db.query(Post)
@@ -406,12 +386,174 @@ def get_topic_suggestions(
try:
result = llm.generate(prompt, system=system_prompt)
lines = [line.strip() for line in result.strip().splitlines() if line.strip()]
suggestions = lines[:3]
return lines[:3]
except Exception:
suggestions = []
return []
@router.get("/suggestions")
def get_topic_suggestions(
force: bool = Query(False),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Get cached daily suggestions or generate new ones. Use force=true to regenerate."""
character = (
db.query(Character)
.filter(Character.user_id == current_user.id, Character.is_active == True)
.first()
)
if not character:
return {"suggestions": [], "character_id": None}
provider_name = _get_setting(db, "llm_provider", current_user.id)
api_key = _get_setting(db, "llm_api_key", current_user.id)
if not provider_name or not api_key:
return {"suggestions": [], "character_id": character.id, "needs_setup": True}
today = date.today().isoformat()
# Check cache
if not force:
cached = _get_setting(db, "daily_suggestions", current_user.id)
if cached and isinstance(cached, dict) and cached.get("date") == today:
return {
"suggestions": cached.get("suggestions", []),
"character_id": cached.get("character_id", character.id),
"character_name": cached.get("character_name", character.name),
"cached": True,
}
# Generate new suggestions
suggestions = _generate_suggestions(db, current_user, character)
# Save to cache
cache_data = {
"date": today,
"suggestions": suggestions,
"character_id": character.id,
"character_name": character.name,
}
existing = (
db.query(SystemSetting)
.filter(SystemSetting.key == "daily_suggestions", SystemSetting.user_id == current_user.id)
.first()
)
if existing:
existing.value = cache_data
existing.updated_at = datetime.utcnow()
else:
db.add(SystemSetting(key="daily_suggestions", value=cache_data, user_id=current_user.id))
db.commit()
return {
"suggestions": suggestions,
"character_id": character.id,
"character_name": character.name,
"cached": False,
}
# === Saved Ideas (Swipe File) ===
@router.get("/ideas")
def get_saved_ideas(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Get all saved ideas for the user."""
setting = (
db.query(SystemSetting)
.filter(SystemSetting.key == "saved_ideas", SystemSetting.user_id == current_user.id)
.first()
)
ideas = setting.value if setting and isinstance(setting.value, list) else []
return {"ideas": ideas, "total": len(ideas)}
@router.post("/ideas")
def save_idea(
data: dict,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Save an idea for later use."""
text = data.get("text", "").strip()
if not text:
raise HTTPException(status_code=400, detail="Text is required")
setting = (
db.query(SystemSetting)
.filter(SystemSetting.key == "saved_ideas", SystemSetting.user_id == current_user.id)
.first()
)
ideas = setting.value if setting and isinstance(setting.value, list) else []
new_idea = {
"id": str(uuid.uuid4())[:8],
"text": text,
"note": data.get("note", ""),
"saved_at": datetime.utcnow().isoformat(),
"used": False,
}
ideas.insert(0, new_idea)
if setting:
setting.value = ideas
setting.updated_at = datetime.utcnow()
else:
db.add(SystemSetting(key="saved_ideas", value=ideas, user_id=current_user.id))
db.commit()
return new_idea
@router.delete("/ideas/{idea_id}")
def delete_idea(
idea_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Delete a saved idea."""
setting = (
db.query(SystemSetting)
.filter(SystemSetting.key == "saved_ideas", SystemSetting.user_id == current_user.id)
.first()
)
if not setting or not isinstance(setting.value, list):
raise HTTPException(status_code=404, detail="Idea not found")
ideas = [i for i in setting.value if i.get("id") != idea_id]
if len(ideas) == len(setting.value):
raise HTTPException(status_code=404, detail="Idea not found")
setting.value = ideas
setting.updated_at = datetime.utcnow()
db.commit()
return {"ok": True}
@router.post("/ideas/{idea_id}/mark-used")
def mark_idea_used(
idea_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Mark an idea as used."""
setting = (
db.query(SystemSetting)
.filter(SystemSetting.key == "saved_ideas", SystemSetting.user_id == current_user.id)
.first()
)
if not setting or not isinstance(setting.value, list):
raise HTTPException(status_code=404, detail="Idea not found")
for idea in setting.value:
if idea.get("id") == idea_id:
idea["used"] = True
break
else:
raise HTTPException(status_code=404, detail="Idea not found")
setting.updated_at = datetime.utcnow()
db.commit()
return {"ok": True}