Inspired by a dashboard design the user shared. - App: new palette and fonts, borderless rounded panels with soft shadows, a pink-to-blue gradient card for your own song (text and controls switch to white on it), flat cards for friends, a 'Hi Chris, welcome back' header, an outline bell, and a pink pill on the active tab. - Website: the same look, plus a wide-screen layout with a sidebar, the page in a rounded panel, and a right column with your profile, recent activity and recent plays. Phones keep the top bar and bottom tabs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Y2iUsKEWDuRGjK5FPmcUP
38 lines
1.3 KiB
TypeScript
38 lines
1.3 KiB
TypeScript
'use client';
|
|
|
|
import Link from 'next/link';
|
|
import { useState } from 'react';
|
|
import useSWR from 'swr';
|
|
import type { UserSummaryDTO } from '@musicshare/shared';
|
|
import { fetcher } from '@/lib/api';
|
|
import { useDebounced } from '@/lib/useDebounced';
|
|
import { Avatar } from './Avatar';
|
|
|
|
export function UserSearch({ placeholder = 'Find friends by username' }: { placeholder?: string }) {
|
|
const [q, setQ] = useState('');
|
|
const debounced = useDebounced(q.trim(), 250);
|
|
const { data } = useSWR<UserSummaryDTO[]>(
|
|
debounced.length >= 2 ? `/users/search?q=${encodeURIComponent(debounced)}` : null,
|
|
fetcher,
|
|
);
|
|
return (
|
|
<div className="search">
|
|
<input placeholder={placeholder} aria-label="Search people" value={q} onChange={(e) => setQ(e.target.value)} />
|
|
{data && debounced.length >= 2 && (
|
|
<ul className="search-results">
|
|
{data.length === 0 && <li className="muted">No one found</li>}
|
|
{data.map((u) => (
|
|
<li key={u.id}>
|
|
<Link href={`/u/${u.username}`} className="user-chip">
|
|
<Avatar user={u} size={28} />
|
|
<span>{u.displayName ?? u.username}</span>
|
|
<span className="muted">@{u.username}</span>
|
|
</Link>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|