Skip to content

feat: made for you section#24

Merged
frank-mendez merged 2 commits into
mainfrom
feature/made-for-you
Mar 20, 2026
Merged

feat: made for you section#24
frank-mendez merged 2 commits into
mainfrom
feature/made-for-you

Conversation

@frank-mendez

Copy link
Copy Markdown
Owner

No description provided.

Copilot AI review requested due to automatic review settings March 20, 2026 15:44
@vercel

vercel Bot commented Mar 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
react-typescript-spotify Ready Ready Preview, Comment Mar 20, 2026 3:52pm

@github-actions

github-actions Bot commented Mar 20, 2026

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 66.87% (🎯 60%) 2734 / 4088
🔵 Statements 66.87% (🎯 60%) 2734 / 4088
🔵 Functions 64.72% (🎯 60%) 200 / 309
🔵 Branches 77.2% (🎯 60%) 437 / 566
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/components/features/NowPlaying.tsx 50% 14.28% 50% 50% 12-19, 29-32, 40-48, 52-57, 61-63
src/components/features/home/HomeSection.tsx 100% 100% 100% 100%
src/components/features/home/MadeForYouSection.tsx 27.53% 25% 33.33% 27.53% 21-82, 102-103, 110-115, 119-120, 123-136, 141-153, 162-169, 174-182
src/components/features/home/NavHeader.tsx 75.51% 100% 50% 75.51% 17-28
src/components/features/home/RecentlyPlayedCard.tsx 83.92% 41.66% 75% 83.92% 29, 77-84
src/components/features/home/RecentlyPlayedSection.tsx 100% 80% 50% 100%
src/components/layout/MainPanel.tsx 64.28% 33.33% 100% 64.28% 11, 22-33
src/hooks/useSpotifyQueries.ts 28.9% 92.3% 36% 28.9% 10-18, 21-29, 32-39, 42-50, 53-60, 63-75, 78-90, 93-100, 103-110, 113-129, 132-139, 142-149, 152-159, 162-169, 172-179, 204-211
src/pages/Dashboard.tsx 100% 100% 100% 100%
Generated in workflow #47 for commit aa1a656 by the Vitest Coverage Report Action

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new “Made For You” carousel to the Dashboard home view, alongside a refactor of “Recently Played” to focus on track-based cards and updated playback interactions. Also includes a few layout tweaks to support the new panel density.

Changes:

  • Add MadeForYouSection and render it in the home MainPanel.
  • Refactor RecentlyPlayedSection/card + tests to dedupe and play individual tracks (vs album/artist cards).
  • Add Spotify browse query hooks for categories and category playlists; tweak layout widths/spacing.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/pages/Dashboard.tsx Adds min-w-0 to the main scroll area to prevent flex overflow.
src/hooks/useSpotifyQueries.ts Adds useCategories and useCategoryPlaylists React Query hooks.
src/components/layout/MainPanel.tsx Renders the new MadeForYouSection on the home view.
src/components/features/NowPlaying.tsx Widens the Now Playing aside (w-60 → w-72).
src/components/features/home/RecentlyPlayedSection.tsx Switches recently played items to track-based cards + track URI playback.
src/components/features/home/RecentlyPlayedSection.test.tsx Updates tests to match new track-based recently played behavior.
src/components/features/home/RecentlyPlayedCard.tsx Updates RecentItem type to track only.
src/components/features/home/RecentlyPlayedCard.test.tsx Updates tests to match the new RecentItem shape and track URIs.
src/components/features/home/NavHeader.tsx Adds mt-4 to the sticky filter header layout.
src/components/features/home/MadeForYouSection.tsx Introduces the new “Made For You” playlist carousel with play/pause + scroll arrows.
src/components/features/home/HomeSection.tsx Makes title optional and conditionally renders the heading.

imageUrl: track.album?.images[1]?.url ?? track.album?.images[0]?.url,
type: 'track',
uri: track.uri,
navigationPath: `/album/${track.album?.id}`,

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

navigationPath is built with track.album?.id, which will produce /album/undefined when track.album is missing (a case this component explicitly supports). This will navigate to a broken route when the card is clicked. Consider guarding navigation (e.g., make navigationPath optional and disable navigation when absent) or provide a valid fallback route.

Suggested change
navigationPath: `/album/${track.album?.id}`,
navigationPath: track.album ? `/album/${track.album.id}` : undefined,

Copilot uses AI. Check for mistakes.
Comment on lines 6 to 10
}

export function HomeSection({ title, children }: Readonly<HomeSectionProps>) {
return (
<section>

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HomeSection now allows omitting title but still renders a <section> landmark. A <section> without a heading/accessible name can create unnamed landmarks for screen readers. Consider either keeping title required, or adding an aria-label/aria-labelledby prop (or rendering a <div> when no title is provided).

Suggested change
}
export function HomeSection({ title, children }: Readonly<HomeSectionProps>) {
return (
<section>
'aria-label'?: string;
'aria-labelledby'?: string;
}
export function HomeSection({ title, children, ...sectionProps }: Readonly<HomeSectionProps>) {
const hasAccessibleName =
Boolean(title) ||
Boolean(sectionProps['aria-label']) ||
Boolean(sectionProps['aria-labelledby']);
if (!hasAccessibleName) {
return (
<div>
{title && <h2 className="text-text-primary font-bold text-xl mb-3">{title}</h2>}
{children}
</div>
);
}
return (
<section {...sectionProps}>

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +57
return (
<button
className="flex flex-col gap-2 w-40 shrink-0 text-left group focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white rounded-md"
onClick={() => navigate(`/playlist/${playlist.id}`)}
>
<div className="relative w-40 h-40 rounded-md overflow-hidden bg-surface-hover">
{image ? (
<img
src={image}
alt={playlist.name}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
/>
) : (
<div className="w-full h-full flex items-center justify-center bg-accent/20 text-accent font-bold text-2xl">
{initials}
</div>
)}

{/* Play/Pause button — bottom right corner */}
<div
className={`absolute bottom-2 right-2 transition-all duration-200 ${isActive ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-2 group-hover:opacity-100 group-hover:translate-y-0'}`}
onClick={handlePlayPause}
>

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The play/pause control is a clickable <div> inside a clickable <button>. This makes the play/pause action non-focusable/non-keyboard accessible and mixes interactive semantics (nested interactive region). Consider restructuring so the card container is not a <button> (e.g., use a <div>/<Link> for navigation) and render the play/pause as a real <button type="button"> with an accessible label.

Copilot uses AI. Check for mistakes.
Comment on lines +100 to +117
const updateArrows = () => {
const el = scrollRef.current;
if (!el) return;
setCanScrollLeft(el.scrollLeft > 0);
setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
};

useEffect(() => {
updateArrows();
const el = scrollRef.current;
if (!el) return;
el.addEventListener('scroll', updateArrows);
window.addEventListener('resize', updateArrows);
return () => {
el.removeEventListener('scroll', updateArrows);
window.removeEventListener('resize', updateArrows);
};
}, [data]);

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This effect registers event listeners using updateArrows, but updateArrows is re-created on every render and isn't included in the dependency list. With eslint-plugin-react-hooks enabled, this will trigger an exhaustive-deps warning and can lead to subtle listener/closure issues later. Consider moving updateArrows inside the effect or wrapping it in useCallback and including it in the deps array.

Copilot uses AI. Check for mistakes.
Comment on lines +159 to +160
className="flex gap-4 overflow-x-auto pb-2"
style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inline scrollbar-hiding styles (scrollbarWidth / msOverflowStyle) only cover Firefox/legacy IE; Chrome/Safari will still show a scrollbar unless ::-webkit-scrollbar is handled. If the intent is to hide the scrollbar cross-browser, consider adding a shared CSS utility (similar to scrollbar-thin in src/index.css) and applying it here instead of inline styles.

Suggested change
className="flex gap-4 overflow-x-auto pb-2"
style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}
className="flex gap-4 overflow-x-auto pb-2 scrollbar-thin"

Copilot uses AI. Check for mistakes.
Comment on lines +85 to +95
export function MadeForYouSection() {
const { data, isLoading } = useUserPlaylists(50);
const { data: profile } = useCurrentUserProfile();
const { data: playback } = useCurrentPlayback();
const { play, pause } = usePlaybackControls();
const { deviceId } = usePlayerStore();

const sectionTitle = `Made For ${profile?.display_name ?? 'You'}`;
const activeContextUri = playback?.context?.uri ?? null;
const isPlaybackActive = playback?.is_playing ?? false;

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MadeForYouSection introduces non-trivial UI logic (play/pause behavior tied to playback state + horizontal scrolling/arrow affordances) but has no accompanying tests. Since other home sections are covered by RTL/Vitest tests, adding a MadeForYouSection.test.tsx covering rendering, arrow visibility behavior, and play/pause click behavior would help prevent regressions.

Copilot generated this review using guidance from repository custom instructions.
@sonarqubecloud

Copy link
Copy Markdown

@frank-mendez frank-mendez merged commit 2a4dd59 into main Mar 20, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants