feat: Add profile editing (bio) to settings page

This commit is contained in:
fullsizemalt 2025-12-20 02:36:05 -08:00
parent 3575ac4700
commit f9cdd626f4
2 changed files with 86 additions and 0 deletions

View file

@ -1,6 +1,7 @@
from typing import List, Optional from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query from fastapi import APIRouter, Depends, HTTPException, Query
from sqlmodel import Session, select, func from sqlmodel import Session, select, func
from pydantic import BaseModel
from database import get_session from database import get_session
from models import User, Review, Attendance, Group, GroupMember, Show from models import User, Review, Attendance, Group, GroupMember, Show
from schemas import UserRead, ReviewRead, ShowRead, GroupRead from schemas import UserRead, ReviewRead, ShowRead, GroupRead
@ -8,6 +9,27 @@ from auth import get_current_user
router = APIRouter(prefix="/users", tags=["users"]) router = APIRouter(prefix="/users", tags=["users"])
class UserProfileUpdate(BaseModel):
bio: Optional[str] = None
avatar: Optional[str] = None
@router.patch("/me", response_model=UserRead)
def update_my_profile(
update: UserProfileUpdate,
current_user: User = Depends(get_current_user),
session: Session = Depends(get_session)
):
"""Update current user's bio and avatar"""
if update.bio is not None:
current_user.bio = update.bio
if update.avatar is not None:
current_user.avatar = update.avatar
session.add(current_user)
session.commit()
session.refresh(current_user)
return current_user
# --- User Stats --- # --- User Stats ---
@router.get("/{user_id}/stats") @router.get("/{user_id}/stats")

View file

@ -1,12 +1,50 @@
"use client" "use client"
import { useState, useEffect } from "react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Label } from "@/components/ui/label" import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch" import { Switch } from "@/components/ui/switch"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { Button } from "@/components/ui/button"
import { usePreferences } from "@/contexts/preferences-context" import { usePreferences } from "@/contexts/preferences-context"
import { useAuth } from "@/contexts/auth-context"
import { getApiUrl } from "@/lib/api-config"
export default function SettingsPage() { export default function SettingsPage() {
const { preferences, updatePreferences, loading } = usePreferences() const { preferences, updatePreferences, loading } = usePreferences()
const { user } = useAuth()
const [bio, setBio] = useState("")
const [saving, setSaving] = useState(false)
const [saved, setSaved] = useState(false)
useEffect(() => {
if (user?.bio) {
setBio(user.bio)
}
}, [user])
const handleSaveProfile = async () => {
setSaving(true)
setSaved(false)
const token = localStorage.getItem("token")
try {
await fetch(`${getApiUrl()}/users/me`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify({ bio })
})
setSaved(true)
setTimeout(() => setSaved(false), 2000)
} catch (e) {
console.error(e)
} finally {
setSaving(false)
}
}
if (loading) { if (loading) {
return <div>Loading settings...</div> return <div>Loading settings...</div>
@ -16,6 +54,32 @@ export default function SettingsPage() {
<div className="max-w-2xl mx-auto space-y-6"> <div className="max-w-2xl mx-auto space-y-6">
<h1 className="text-3xl font-bold tracking-tight">Settings</h1> <h1 className="text-3xl font-bold tracking-tight">Settings</h1>
{/* Profile Section */}
<Card>
<CardHeader>
<CardTitle>Profile</CardTitle>
<CardDescription>
Tell other fans about yourself.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="bio">Bio</Label>
<Textarea
id="bio"
placeholder="Been following the band since 2019..."
value={bio}
onChange={(e) => setBio(e.target.value)}
rows={3}
/>
</div>
<Button onClick={handleSaveProfile} disabled={saving}>
{saving ? "Saving..." : saved ? "Saved ✓" : "Save Profile"}
</Button>
</CardContent>
</Card>
{/* Preferences Section */}
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle>Preferences</CardTitle> <CardTitle>Preferences</CardTitle>