Spaces:
Runtime error
Runtime error
| import { useState, useEffect } from 'react'; | |
| import { Settings, LogOut, Play, Loader2 } from 'lucide-react'; | |
| import ConfigModal from '@/components/ConfigModal'; | |
| import ChatInterface from '@/components/ChatInterface'; | |
| import SpotifyDashboard from '@/components/SpotifyDashboard'; | |
| import axios from 'axios'; | |
| export default function Home() { | |
| const [config, setConfig] = useState(null); | |
| const [showConfig, setShowConfig] = useState(false); | |
| const [isAuthenticated, setIsAuthenticated] = useState(false); | |
| const [accessToken, setAccessToken] = useState(null); | |
| // Spotify Data States | |
| const [userData, setUserData] = useState(null); | |
| const [topTracks, setTopTracks] = useState(null); | |
| const [topArtists, setTopArtists] = useState(null); | |
| const [isLoadingData, setIsLoadingData] = useState(false); | |
| // Chat States | |
| const [messages, setMessages] = useState([]); | |
| const [isChatLoading, setIsChatLoading] = useState(false); | |
| // Initialize config from localStorage | |
| useEffect(() => { | |
| const savedConfig = localStorage.getItem('spotify_ai_config'); | |
| if (savedConfig) { | |
| setConfig(JSON.parse(savedConfig)); | |
| } | |
| // Check for OAuth code in URL | |
| const urlParams = new URLSearchParams(window.location.search); | |
| const code = urlParams.get('code'); | |
| if (code) { | |
| handleCallback(code); | |
| // Clean URL | |
| window.history.replaceState({}, document.title, window.location.pathname); | |
| } | |
| }, []); | |
| const handleSaveConfig = (newConfig) => { | |
| setConfig(newConfig); | |
| localStorage.setItem('spotify_ai_config', JSON.stringify(newConfig)); | |
| }; | |
| const handleLogin = async () => { | |
| if (!config?.spotifyId) { | |
| setShowConfig(true); | |
| return; | |
| } | |
| try { | |
| const redirectUri = `${window.location.origin}/api/auth/callback`; | |
| const res = await axios.post('/api/auth/login', { | |
| clientId: config.spotifyId, | |
| redirectUri | |
| }); | |
| window.location.href = res.data.url; | |
| } catch (error) { | |
| console.error('Login error:', error); | |
| alert('Failed to initiate login'); | |
| } | |
| }; | |
| const handleCallback = async (code) => { | |
| try { | |
| const savedConfig = JSON.parse(localStorage.getItem('spotify_ai_config')); | |
| if (!savedConfig) return; | |
| const redirectUri = `${window.location.origin}/api/auth/callback`; | |
| const res = await axios.post('/api/auth/callback', { | |
| code, | |
| clientId: savedConfig.spotifyId, | |
| clientSecret: savedConfig.spotifySecret, | |
| redirectUri | |
| }); | |
| setAccessToken(res.data.access_token); | |
| setIsAuthenticated(true); | |
| fetchSpotifyData(res.data.access_token); | |
| } catch (error) { | |
| console.error('Callback error:', error); | |
| alert('Authentication failed. Please check your credentials.'); | |
| } | |
| }; | |
| const fetchSpotifyData = async (token) => { | |
| setIsLoadingData(true); | |
| try { | |
| const [profileRes, tracksRes, artistsRes] = await Promise.all([ | |
| axios.get(`/api/spotify/data?type=profile&accessToken=${token}`), | |
| axios.get(`/api/spotify/data?type=tracks&accessToken=${token}`), | |
| axios.get(`/api/spotify/data?type=artists&accessToken=${token}`) | |
| ]); | |
| setUserData(profileRes.data); | |
| setTopTracks(tracksRes.data.items); | |
| setTopArtists(artistsRes.data.items); | |
| } catch (error) { | |
| console.error('Data fetch error:', error); | |
| // Only alert if it's not a 401 (handled elsewhere) | |
| if (error.response?.status !== 401) { | |
| alert('Failed to load profile data'); | |
| } | |
| } finally { | |
| setIsLoadingData(false); | |
| } | |
| }; | |
| const handleLogout = () => { | |
| setAccessToken(null); | |
| setIsAuthenticated(false); | |
| setUserData(null); | |
| setTopTracks(null); | |
| setTopArtists(null); | |
| setMessages([]); | |
| }; | |
| const handleSendMessage = async (content) => { | |
| if (!config?.aiApiKey) { | |
| setShowConfig(true); | |
| alert('Please configure your AI API Key first.'); | |
| return; | |
| } | |
| const newMessage = { role: 'user', content }; | |
| setMessages(prev => [...prev, newMessage]); | |
| setIsChatLoading(true); | |
| try { | |
| const res = await axios.post('/api/ai/chat', { | |
| messages: [...messages, newMessage], | |
| spotifyContext: { profile: userData, topTracks, topArtists }, | |
| apiKey: config.aiApiKey, | |
| provider: config.aiProvider, | |
| model: config.aiModel | |
| }); | |
| const aiMessage = { role: 'assistant', content: res.data.content }; | |
| setMessages(prev => [...prev, aiMessage]); | |
| } catch (error) { | |
| console.error('Chat error:', error); | |
| const errorMsg = { | |
| role: 'assistant', | |
| content: `Sorry, I encountered an error: ${error.response?.data?.details || error.message}` | |
| }; | |
| setMessages(prev => [...prev, errorMsg]); | |
| } finally { | |
| setIsChatLoading(false); | |
| } | |
| }; | |
| return ( | |
| <div className="min-h-screen flex flex-col"> | |
| {/* Main Content Grid */} | |
| <div className="flex-1 grid grid-cols-1 lg:grid-cols-12 gap-6 h-[calc(100vh-80px)]"> |