feat: Add profile editing (bio) to settings page
This commit is contained in:
parent
3575ac4700
commit
f9cdd626f4
2 changed files with 86 additions and 0 deletions
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue