Skip to main content

Overview

The Nadie Sabe Nada podcast app includes a comprehensive search and filtering system that helps you find episodes quickly. The system combines real-time text search with category filters to narrow down your podcast library.

Search Functionality

The search bar performs instant filtering as you type, with no need to press enter or click a search button.
Search results update in real-time as you type, providing immediate feedback.

Search Implementation

The search functionality is implemented in the main App component:
const handleSearchChange = (e) => {
    dispatch(setFilter("todos"));
    dispatch(setSearchTerm(e.target.value));
};

const handleClear = () => {
    dispatch(setSearchTerm(""));
};

Search Input Component

The search field appears on the home page with custom styling:
<TextField
    value={searchTerm}
    onChange={handleSearchChange}
    placeholder="Buscar Podcast..."
    variant="outlined"
    className={styles.searchInput}
    fullWidth
    size="small"
    sx={{
        "& .MuiInputBase-input": {
            color: "black",
            fontWeight: "bold"
        },
        "& .MuiOutlinedInput-root": {
            "& fieldset": {
                borderColor: "#16DB93",
                borderRadius: "60px"
            },
            "&:hover fieldset": {
                borderColor: "#16DB93",
                borderRadius: "60px"
            },
            "&.Mui-focused fieldset": {
                borderColor: "#16DB93",
                borderRadius: "60px"
            }
        }
    }}
    InputProps={{
        endAdornment: searchTerm && (
            <InputAdornment position="end">
                <IconButton onClick={handleClear}>
                    <ClearIcon />
                </IconButton>
            </InputAdornment>
        )
    }}
/>
The clear button (X) appears automatically when you start typing, allowing you to quickly reset your search.

Search Behavior

  • Case Insensitive - Searches match regardless of capitalization
  • Partial Matching - Results include any episodes containing the search term
  • Title Search - Currently searches only episode titles
  • Auto-Filter Reset - Automatically switches to “All” filter when searching

Filter System

Available Filters

The application provides six category filters:
  1. Todos (All) - Shows all episodes
  2. Empezados (Started) - Episodes with saved playback progress
  3. No Empezados (Not Started) - Episodes never played
  4. Favoritos (Favorites) - Episodes marked as favorites
  5. Escuchar Más Tarde (Listen Later) - Episodes saved for later
  6. Completados (Completed) - Episodes marked as finished

Filter UI

Filters are displayed as icon buttons with tooltips:
<BootstrapTooltip
    title="Todos los Podcasts"
    placement="top"
    arrow
    disableInteractive
    TransitionComponent={Fade}
    TransitionProps={{ timeout: 600 }}
>
    <span>
        <motion.button
            whileHover={{ scale: 1.1, boxShadow: "0px 4px 8px rgba(0, 0, 0, 0.2)" }}
            whileTap={{ scale: 0.95 }}
            className={currentFilter === "todos" ? styles.activeButton : styles.button}
            onClick={() => dispatch(setFilter("todos"))}
        >
            <FormatListBulleted className={styles.headphonesIcon} />
        </motion.button>
    </span>
</BootstrapTooltip>

Filter Icons

FilterIconDescription
AllFormatListBulletedList icon
StartedHeadphonesHeadphones icon
Not StartedHeadsetOffHeadphones off icon
FavoritesFavoriteFilled heart icon
Listen LaterWatchLaterClock icon
CompletedCheckCircleCheckmark icon
The active filter button is highlighted with a different style to show which filter is currently applied.

Combined Search and Filter

Filter Logic

Search and filters work together seamlessly. The filtering logic processes both simultaneously:
const filteredSongs = songs.filter((song) => {
    const matchesSearch = song.title.toLowerCase().includes(searchTerm.toLowerCase());
    const isStarted = playbackTimes[song.title] > 0;
    const isCompleted = completedEpisodes.includes(song.title);

    switch (currentFilter) {
        case "empezados":
            return matchesSearch && isStarted && !isCompleted;
        case "no-empezados":
            return matchesSearch && !isStarted && !isCompleted;
        case "favoritos":
            return matchesSearch && favoriteEpisodes.includes(song.title);
        case "escuchar-mas-tarde":
            return matchesSearch && listenLaterEpisodes.includes(song.title);
        case "completados":
            return matchesSearch && isCompleted;
        default:
            return matchesSearch;
    }
});

How It Works

1

Search Term Check

First checks if the episode title contains the search term (case insensitive)
2

Filter Check

Then applies the selected filter criteria
3

Combine Results

Only episodes matching BOTH criteria are shown
4

Display

Filtered results appear instantly
Searching automatically resets the filter to “All”. This ensures you don’t accidentally miss results due to an active filter.

Redux State Management

Filter Slice

The filter state is managed in a dedicated Redux slice:
const filterSlice = createSlice({
    name: "filter",
    initialState: {
        currentFilter: "todos",
        currentPage: 1,
        songsPerPage: 12
    },
    reducers: {
        setFilter: (state, action) => {
            state.currentFilter = action.payload;
            state.currentPage = 1;
        },
        setCurrentPage: (state, action) => {
            state.currentPage = action.payload;
        }
    }
});
Changing filters automatically resets to page 1 to avoid showing empty pages.

Search State

Search term is stored in the podcast slice:
const podcastSlice = createSlice({
    name: "podcast",
    initialState: {
        songs: [],
        loading: true,
        error: null,
        searchTerm: "",
        // ... other state
    },
    reducers: {
        setSearchTerm: (state, action) => {
            state.searchTerm = action.payload;
        },
        // ... other reducers
    }
});

No Results Handling

When no episodes match the current search and filter combination, a friendly “No Results” component appears:
{filteredSongs.length === 0 ? (
    <NoResults searchTerm={searchTerm} currentFilter={currentFilter} />
) : (
    <motion.div className={styles.playerList}>
        {/* Episode cards */}
    </motion.div>
)}

Pagination with Filters

Filtered results are automatically paginated:
const indexOfLastSong = currentPage * songsPerPage;
const indexOfFirstSong = indexOfLastSong - songsPerPage;
const currentSongs = filteredSongs.slice(indexOfFirstSong, indexOfLastSong);
Pagination controls only appear when there are more than 12 filtered results.

Pagination Display Logic

{filteredSongs.length > 12 && (
    <div className={styles.paginationContainer}>
        <Pagination
            currentPage={currentPage}
            setCurrentPage={(page) => dispatch(setCurrentPage(page))}
            songsPerPage={songsPerPage}
            songs={filteredSongs}
        />
    </div>
)}

Animations

Filters include smooth animations powered by Framer Motion:
const itemVariants = {
    hidden: { y: -20, opacity: 0 },
    visible: {
        y: 0,
        opacity: 1,
        transition: {
            type: "spring",
            stiffness: 400,
            damping: 12,
            mass: 0.95
        }
    },
    hover: {
        scale: 1.05,
        rotate: 1,
        transition: {
            type: "spring",
            stiffness: 300,
            damping: 12
        }
    }
};
Filter buttons scale up slightly on hover, providing visual feedback for better user experience.

Best Practices

1

Use Search for Specific Episodes

When you know part of an episode’s name, use search to find it quickly
2

Use Filters for Browsing

When exploring your library by status, use the category filters
3

Combine Both

You can search within a filtered category for more precise results
4

Clear Search

Click the X button or clear the field to see all episodes again

Search Tips

For better search results:
  • Search is case-insensitive, so “NADIE” and “nadie” give the same results
  • Partial words work - searching “programa” finds “Programa 123”
  • Search clears active filters, showing all matching episodes
  • Use the clear button (X) to quickly reset your search

Build docs developers (and LLMs) love