Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Music Release Project #105

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Describe how you approached to problem, and what tools and techniques you used t

### View it live

Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about.
https://gittes-music-release.netlify.app

## Instructions

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
Expand Down
8 changes: 7 additions & 1 deletion src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import data from "./data.json";
import stretchgoals from "./stretch-goal.json"
import { Album } from "./components/Album"

console.log(data);
console.log(stretchgoals)

export const App = () => {
return <div>Find me in src/app.jsx!</div>;
return (
<>
<Album />
</>)
};
32 changes: 32 additions & 0 deletions src/components/Album.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { albums } from "../data.json"
import { AlbumName } from "./AlbumName"
import { ArtistName } from "./ArtistName"
import { CoverImage } from "./CoverImage"
import { Header } from "./Header"
import { Sidebar } from "./Sidebar"

// Album
export const Album = () => {
return (
<>
<Header />
<div className="main-container">
<div className="album-container">
{albums.items.map((album) => (
<div key={album.id} className="album-card">
<CoverImage
coverImg={album.images.length > 0 ? album.images[0].url : 'fallback-image-url.jpg'}
albumUrl={album.external_urls.spotify}

Choose a reason for hiding this comment

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

great to use a fallback image

/>
<div className="album-info">
<AlbumName name={album.name} albumUrl={album.external_urls.spotify} />
<ArtistName artists={album.artists} artistsUrl={album.external_urls.spotify} />
</div>
</div>
))}
</div>
<Sidebar />
</div>
</>
);
};
24 changes: 24 additions & 0 deletions src/components/AlbumName.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import PropTypes from 'prop-types'

// Album Name
export const AlbumName = ({ name, albumUrl }) => {

Choose a reason for hiding this comment

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

Good practise to only pass along the data that this component needs to know about.

return (
<div>
<a
href={albumUrl}
target="_blank"
rel="noopener noreferrer"
className='album-name-a'>
<h2 className="album-name">{name}</h2>
</a>
</div>
)
}


// Define PropTypes for AlbumName
AlbumName.propTypes = {
name: PropTypes.string.isRequired,
albumUrl: PropTypes.string.isRequired
};

40 changes: 40 additions & 0 deletions src/components/ArtistName.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import PropTypes from 'prop-types'


// Artist Name
export const ArtistName = ({ artists }) => {
return (
<div className='artists-container'>
{artists.map((artist, index) => {
let separator = ", ";
if (artists.length === 2 && index === 0) {
separator = " & ";
} else if (index === artists.length - 2) {
separator = " & ";
} else if (index === artists.length - 1) {
separator = "";
}


return (
<span key={artist.id}>
<a href={artist.external_urls.spotify} target="_blank" rel="noopener noreferrer" className='artist-name-a'>
<h3 className='artist-name'>{artist.name + separator}</h3>
</a>
</span>
)
})}
</div>
)
}


ArtistName.propTypes = {
artists: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
})
).isRequired,
artistsUrl: PropTypes.string.isRequired
};
27 changes: 27 additions & 0 deletions src/components/CoverImage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import PropTypes from "prop-types"
import { DotsButton } from "./DotsButton"
import { HeartButton } from "./HeartButton"
import { PlayButton } from "./PlayButton"


export const CoverImage = ({ coverImg, albumUrl }) => {
return (
<div className="cover-image-container">
<div className="cover-image">
<img className="cover-image-img" alt="Album Cover" src={coverImg} />
<div className="icon-container">
<HeartButton />
<PlayButton albumUrl={albumUrl} />
<DotsButton />
</div>
</div>

</div>
)
}

CoverImage.propTypes = {
coverImg: PropTypes.string.isRequired,
albumUrl: PropTypes.string.isRequired,
}

11 changes: 11 additions & 0 deletions src/components/DotsButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import dotsIcon from "../assets/icons/dots.svg"

export const DotsButton = () => {
return (
<span>
<button type="button" aria-label="link to artist" className="dots-button">
<img src={dotsIcon} className="dots-icon" />
</button>
</span>
)
}
11 changes: 11 additions & 0 deletions src/components/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@


export const Header = () => {
return (
<header>
<h1>
New Vibes
</h1>
</header>
)
}
11 changes: 11 additions & 0 deletions src/components/HeartButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import heartIcon from "../assets/icons/heart.svg"

export const HeartButton = () => {
return (
<span className="heart-button-container">
<button type="button" aria-label="like" className="heart-button">
<img src={heartIcon} className="heart-icon" />
</button>
</span>
)
}
19 changes: 19 additions & 0 deletions src/components/PlayButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import playIcon from "../assets/icons/play.svg"
import PropTypes from "prop-types"

export const PlayButton = ({ albumUrl }) => {
const handlePlayClick = () => {
window.open(albumUrl, "_blank", "noopener,noreferrer")
}
return (
<span className="play-button-container">
<button type="button" aria-label="play" className="play-button" onClick={handlePlayClick}>
<img src={playIcon} className="play-icon" />
</button>
</span>
)
}

PlayButton.propTypes = {
albumUrl: PropTypes.string.isRequired,
}
18 changes: 18 additions & 0 deletions src/components/Sidebar.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { playlists } from "../stretch-goal.json"
import { SidebarAlbum } from "./SidebarAlbum";

export const Sidebar = () => {
return (
<div className="sidebar">
<h2 className="sidebar-header">Editor's Picks</h2>
<div>
{playlists.items.map((item) => (
<SidebarAlbum
key={item.id}
sidebarImg={item.images.length > 0 ? item.images[0].url : 'fallback-image-url.jpg'}
/>
))}
</div>
</div>
);
};
16 changes: 16 additions & 0 deletions src/components/SidebarAlbum.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import PropTypes from "prop-types"
export const SidebarAlbum = ({ sidebarImg }) => {
return (
<>
<section className="sidebar-album-container">
<div className="cover-image-sidebar">
<img className="cover-image-sidebar-img" alt="Single Cover" src={sidebarImg} />
</div>
</section>
</>
)
}

SidebarAlbum.propTypes = {
sidebarImg: PropTypes.string.isRequired,
}
Loading