Files
party-mix-app/apps/web/src/components/SoloTab/SoloTab.tsx

107 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useState, useEffect } from 'react'
import { usePartyStore } from '@/store/partyStore'
import { fetchTopCharts } from '@/lib/api'
import type { SearchResult } from '@/types'
function TopChartsCard({ tracks }: { tracks: string[] }) {
const { loadPlaylist } = usePartyStore()
const [launched, setLaunched] = useState(false)
const handlePlay = () => {
loadPlaylist(tracks)
setLaunched(true)
window.scrollTo({ top: 0, behavior: 'smooth' })
setTimeout(() => setLaunched(false), 2500)
}
return (
<div className="bg-surface border border-white/[0.07] rounded-app p-4 mb-5 relative overflow-hidden">
<div className="absolute top-0 left-0 right-0 h-[2px] bg-accent" style={{ opacity: 0.8 }} />
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<div className="flex items-center gap-2 mb-1.5">
<span className="w-1.5 h-1.5 rounded-full bg-accent animate-pulse shrink-0" />
<span className="text-[10px] font-display font-bold tracking-[1.3px] uppercase text-accent">
Прямо сейчас
</span>
</div>
<h3 className="font-display font-bold text-[15px] text-app-text leading-tight">Топ чарт</h3>
<p className="text-[11px] text-muted mt-0.5">{tracks.length} треков</p>
</div>
<button
onClick={handlePlay}
className="text-[12px] font-display font-bold px-3 py-1.5 rounded-[9px] transition-all duration-200 cursor-pointer whitespace-nowrap shrink-0 mt-1"
style={{
background: launched ? '#c8ff00' : 'rgba(200,255,0,0.15)',
color: launched ? '#0a0a0f' : '#c8ff00',
}}
>
{launched ? '▶ Играет' : '▶ Слушать'}
</button>
</div>
</div>
)
}
export default function SoloTab() {
const [search, setSearch] = useState('')
const [topTracks, setTopTracks] = useState<string[] | null>(null)
const { loadPlaylist } = usePartyStore()
useEffect(() => {
fetchTopCharts().then((results: SearchResult[]) => {
if (!results.length) { setTopTracks([]); return }
setTopTracks(
results.map((r) => (r.artist ? `${r.artist}${r.title}` : r.title))
)
})
}, [])
const handleSearch = (e: React.FormEvent) => {
e.preventDefault()
const q = search.trim()
if (!q) return
loadPlaylist([q])
setSearch('')
window.scrollTo({ top: 0, behavior: 'smooth' })
}
return (
<div className="animate-fadeUp">
{topTracks === null && (
<div className="bg-surface border border-white/[0.07] rounded-app p-4 mb-5 flex items-center gap-2.5 text-muted text-[13px]">
<div className="w-3.5 h-3.5 rounded-full border-2 border-surface2 border-t-accent animate-spin shrink-0" />
Загружаем чарт...
</div>
)}
{topTracks !== null && topTracks.length > 0 && (
<TopChartsCard tracks={topTracks} />
)}
<div className="bg-surface border border-white/[0.07] rounded-app p-4">
<p className="text-[11px] font-display font-semibold tracking-[1.2px] uppercase text-muted mb-3">
Быстрый поиск
</p>
<form onSubmit={handleSearch} className="flex gap-2">
<input
type="text"
placeholder="Исполнитель — Название трека"
value={search}
onChange={(e) => setSearch(e.target.value)}
className="flex-1 font-sans text-[13px] bg-surface2 border border-white/[0.07] rounded-[9px] px-3 py-2.5 text-app-text outline-none focus:border-accent/35 placeholder:text-muted transition-colors"
/>
<button
type="submit"
disabled={!search.trim()}
className="px-4 py-2.5 bg-accent text-bg font-display font-bold text-sm rounded-[9px] hover:brightness-110 active:scale-[0.97] transition-all duration-150 disabled:opacity-40 disabled:cursor-not-allowed cursor-pointer"
>
</button>
</form>
</div>
</div>
)
}