Add search to videos
This commit is contained in:
parent
7052fc0077
commit
ad83f0643c
|
|
@ -126,6 +126,20 @@ export const videoOperations = {
|
|||
.all(status) as Video[];
|
||||
},
|
||||
|
||||
search: (query: string, status?: string) => {
|
||||
const searchPattern = `%${query}%`;
|
||||
|
||||
if (status) {
|
||||
return db.prepare(
|
||||
'SELECT * FROM videos WHERE title LIKE ? COLLATE NOCASE AND status = ? ORDER BY addedAt DESC'
|
||||
).all(searchPattern, status) as Video[];
|
||||
} else {
|
||||
return db.prepare(
|
||||
'SELECT * FROM videos WHERE title LIKE ? COLLATE NOCASE ORDER BY addedAt DESC'
|
||||
).all(searchPattern) as Video[];
|
||||
}
|
||||
},
|
||||
|
||||
getNextPending: () => {
|
||||
return db.prepare('SELECT * FROM videos WHERE status = ? ORDER BY addedAt ASC LIMIT 1')
|
||||
.get('pending') as Video | undefined;
|
||||
|
|
|
|||
|
|
@ -9,10 +9,17 @@ const router = Router();
|
|||
// Get all videos
|
||||
router.get('/', (req, res) => {
|
||||
try {
|
||||
const { status } = req.query;
|
||||
const { status, search } = req.query;
|
||||
|
||||
let videos;
|
||||
if (status && typeof status === 'string') {
|
||||
|
||||
// If search query is provided, use search
|
||||
if (search && typeof search === 'string') {
|
||||
const statusFilter = status && typeof status === 'string' ? status : undefined;
|
||||
videos = videoOperations.search(search, statusFilter);
|
||||
}
|
||||
// Otherwise use status filter or get all
|
||||
else if (status && typeof status === 'string') {
|
||||
videos = videoOperations.getByStatus(status);
|
||||
} else {
|
||||
videos = videoOperations.getAll();
|
||||
|
|
|
|||
|
|
@ -10,15 +10,18 @@ function VideosPage() {
|
|||
const [addUrl, setAddUrl] = useState('');
|
||||
const [adding, setAdding] = useState(false);
|
||||
const [filter, setFilter] = useState<string>('all');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
loadVideos();
|
||||
}, [filter]);
|
||||
}, [filter, searchQuery]);
|
||||
|
||||
const loadVideos = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = filter === 'all' ? await videosAPI.getAll() : await videosAPI.getAll(filter);
|
||||
const statusFilter = filter === 'all' ? undefined : filter;
|
||||
const search = searchQuery.trim() || undefined;
|
||||
const data = await videosAPI.getAll(statusFilter, search);
|
||||
setVideos(data);
|
||||
setError('');
|
||||
} catch (err) {
|
||||
|
|
@ -127,7 +130,8 @@ function VideosPage() {
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 flex gap-2">
|
||||
<div className="mt-6 space-y-4">
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => setFilter('all')}
|
||||
className={`px-4 py-2 rounded ${
|
||||
|
|
@ -170,6 +174,40 @@ function VideosPage() {
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className="flex-1 relative">
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder="Search videos by title..."
|
||||
className="w-full rounded-md border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-400 shadow-sm focus:border-blue-500 focus:ring-blue-500 pl-10"
|
||||
/>
|
||||
<svg
|
||||
className="absolute left-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-gray-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
{searchQuery && (
|
||||
<button
|
||||
onClick={() => setSearchQuery('')}
|
||||
className="px-4 py-2 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 bg-white dark:bg-gray-800 shadow overflow-hidden sm:rounded-md">
|
||||
{loading ? (
|
||||
<div className="p-8 text-center text-gray-500 dark:text-gray-400">Loading...</div>
|
||||
|
|
|
|||
|
|
@ -56,9 +56,14 @@ export const channelsAPI = {
|
|||
|
||||
// Videos API
|
||||
export const videosAPI = {
|
||||
getAll: (status?: string) => {
|
||||
const url = status
|
||||
? `${API_BASE}/videos?status=${status}`
|
||||
getAll: (status?: string, search?: string) => {
|
||||
const params = new URLSearchParams();
|
||||
if (status) params.append('status', status);
|
||||
if (search) params.append('search', search);
|
||||
|
||||
const queryString = params.toString();
|
||||
const url = queryString
|
||||
? `${API_BASE}/videos?${queryString}`
|
||||
: `${API_BASE}/videos`;
|
||||
return fetchJSON<Video[]>(url);
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue