diff --git a/README.md b/README.md index 38807b33..4e68ea48 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,12 @@ # Music Releases -Replace this readme with your own information about your project. - -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. +This week, we will practice React and creating components that will use data from Spotify, which then transfer to its children's components using props. The task is to build a page that shows new albums and single releases. ## Getting Started with the Project +Step 1: Install Node.js +Step 2: Verify Node.js Installation Path on Windows +Step 3: Restart Git Bash in VS Code and then the setup is done ### Dependency Installation & Startup Development Server @@ -24,7 +25,9 @@ npm i && code . && npm run dev ### The Problem -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? +The #1 problem is how to get start with npm which took me some effort to identify the install of Node.js and set the right "Environment Variables" on my PC of the windows system. + + ### View it live diff --git a/index.html b/index.html index 3e0bcb75..9ef89ad9 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,17 @@ - - - - - Music Releases - - -
- - - + + + + + + Xings Spotify Music Releases + + + + +
+ + + + \ No newline at end of file diff --git a/pull_request_template.md b/pull_request_template.md index 7a29e744..48a92543 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -1,6 +1,5 @@ ## Netlify link -Add your Netlify link here. -PS. Don't forget to add it in your readme as well. +https://xingsmusiclibary.netlify.app ## Collaborators -Add any collaborators here, as well as in the title of the PR. \ No newline at end of file +chatGpt :> \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index a13f8faf..81041e6c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,7 +1,20 @@ import data from "./data.json"; +import Header from "./components/Header"; +import Footer from "./components/Footer"; +import { AlbumList } from "./components/AlbumList"; + + +import './index.css'; + console.log(data); export const App = () => { - return
Find me in src/app.jsx!
; + return ( +
+
+ +
+ ); }; diff --git a/src/assets/icons/dots.svg b/src/assets/icons/dots.svg index fe8ef52d..3f72794c 100644 --- a/src/assets/icons/dots.svg +++ b/src/assets/icons/dots.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/src/assets/icons/heart.svg b/src/assets/icons/heart.svg index b8024025..53e512e4 100644 --- a/src/assets/icons/heart.svg +++ b/src/assets/icons/heart.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/src/assets/icons/play.svg b/src/assets/icons/play.svg index fba0c84f..dc5bcddd 100644 --- a/src/assets/icons/play.svg +++ b/src/assets/icons/play.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/src/assets/icons/spotify.svg b/src/assets/icons/spotify.svg new file mode 100644 index 00000000..3696adcb --- /dev/null +++ b/src/assets/icons/spotify.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Album.jsx b/src/components/Album.jsx new file mode 100644 index 00000000..7ff99f29 --- /dev/null +++ b/src/components/Album.jsx @@ -0,0 +1,50 @@ +import PropTypes from 'prop-types'; +import { ArtistName } from './ArtistName'; +import { CoverImage } from './CoverImage'; +import { AlbumName } from './AlbumName'; + +export const Album = ({ album }) => { + return ( +
+ +
+
+ +
+
+ {album.artists.map((artist, index) => ( + + ))} +
+
+
+ ); +}; + +Album.propTypes = { + album: PropTypes.shape({ + name: PropTypes.string.isRequired, + images: PropTypes.arrayOf( + PropTypes.shape({ + url: PropTypes.string.isRequired, + }) + ).isRequired, + external_urls: PropTypes.shape({ + spotify: PropTypes.string.isRequired, + }).isRequired, + artists: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string.isRequired, + external_urls: PropTypes.shape({ + spotify: PropTypes.string.isRequired, + }).isRequired, + }) + ).isRequired, + }).isRequired, +}; + diff --git a/src/components/AlbumList.jsx b/src/components/AlbumList.jsx new file mode 100644 index 00000000..79f7fffd --- /dev/null +++ b/src/components/AlbumList.jsx @@ -0,0 +1,16 @@ +import PropTypes from 'prop-types'; +import { Album } from './Album'; + +export const AlbumList = ({ albums }) => { + return ( +
+ {albums.map((album) => ( + + ))} +
+ ); +}; + +AlbumList.propTypes = { + albums: PropTypes.arrayOf(PropTypes.object).isRequired, +}; diff --git a/src/components/AlbumName.jsx b/src/components/AlbumName.jsx new file mode 100644 index 00000000..704aeb73 --- /dev/null +++ b/src/components/AlbumName.jsx @@ -0,0 +1,20 @@ +import PropTypes from 'prop-types'; + +export const AlbumName = ({ name, url }) => { + return ( + + {name} + + ); +}; + +AlbumName.propTypes = { + name: PropTypes.string.isRequired, + url: PropTypes.string.isRequired, +}; + diff --git a/src/components/ArtistName.jsx b/src/components/ArtistName.jsx new file mode 100644 index 00000000..7df4a0c5 --- /dev/null +++ b/src/components/ArtistName.jsx @@ -0,0 +1,23 @@ +import PropTypes from 'prop-types'; + +export const ArtistName = ({ name, url, isLast }) => { + return ( + <> + + {name} + + {!isLast && ', '} + + ); +}; + +ArtistName.propTypes = { + name: PropTypes.string.isRequired, + url: PropTypes.string.isRequired, + isLast: PropTypes.bool.isRequired, +}; diff --git a/src/components/CoverImage.jsx b/src/components/CoverImage.jsx new file mode 100644 index 00000000..f35429c3 --- /dev/null +++ b/src/components/CoverImage.jsx @@ -0,0 +1,28 @@ +import PropTypes from 'prop-types'; +import dotsIcon from '../assets/icons/dots.svg'; +import heartIcon from '../assets/icons/heart.svg'; +import playIcon from '../assets/icons/play.svg'; + +export const CoverImage = ({ imageUrl }) => { + return ( +
+ Album cover +
+ + + +
+
+ ); +}; + +CoverImage.propTypes = { + imageUrl: PropTypes.string.isRequired, +}; + diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx new file mode 100644 index 00000000..1095e53c --- /dev/null +++ b/src/components/Footer.jsx @@ -0,0 +1,10 @@ +const Footer = () => { + return ( + + ); +}; + +export default Footer; \ No newline at end of file diff --git a/src/components/Header.jsx b/src/components/Header.jsx new file mode 100644 index 00000000..7018a646 --- /dev/null +++ b/src/components/Header.jsx @@ -0,0 +1,15 @@ +import spotifyIcon from '../assets/icons/spotify.svg'; + +const Header = () => { + return ( +
+

+ Spotify Logo + New albums and single releases +

+

Find you next Favorite

+
+ ); +}; + +export default Header; \ No newline at end of file diff --git a/src/index.css b/src/index.css index 4558f538..58e2eed7 100644 --- a/src/index.css +++ b/src/index.css @@ -1,13 +1,146 @@ -:root { +/* General Styles */ +body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + padding: 0; + font-family: Helvetica, Arial, sans-serif; + background-color: #292929; + color: #ffffff; } -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; +/* Header and Footer Styling */ +header h1 { + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; + font-size: 2.25rem; +} + +.spotifyIcon { + padding-right: 20px; + width: 80px; + -webkit-box-reflect: below 0.5px linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(116, 115, 115, 0.1)); +} + +header p { + font-size: 1.25rem; + text-align: center; +} + +footer { + padding: 20px; + text-align: center; + font-weight: 600; + font-size: 1rem; + line-height: 0.8rem; + color: #a0a0a0; +} + +/* Album Container Styles */ +.album-container { + display: grid; + gap: 20px; + padding: 20px; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + + /* Responsive Grid Layout */ + @media (min-width: 768px) { + grid-template-columns: repeat(2, 1fr); + } + + @media (min-width: 1024px) { + grid-template-columns: repeat(4, 1fr); + } +} + +/* Album Card Styles */ +.album-card { + position: relative; + overflow: hidden; + transition: transform 0.3s; + display: flex; + flex-direction: column; +} + +.album-info { + margin: 16px 8px; +} + +/* Shared Text Styling for Album and Artist Names */ +.album-name, .artist-name { + font-family: Helvetica, Arial, sans-serif; + text-decoration: none; +} + +.album-name { + font-size: 18px; + color: #ffffff; + font-weight: 600; + margin-bottom: 8px; +} + +.album-name:hover, +.artist-name:hover { + text-decoration: underline; /* Underline effect on hover */ +} + +.artist-name { + color: #a0a0a0; +} + +/* Cover Image and Hover Effect */ +.cover-image-wrapper { + position: relative; + display: flex; + justify-content: center; + align-items: center; +} + +.cover-image { + width: 100%; + transition: filter 0.3s ease; +} + +.cover-image-wrapper:hover .cover-image { + filter: brightness(0.5); +} + +/* Hover Buttons Setup */ +.hover-buttons { + display: none; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + gap: 15px; +} + +.cover-image-wrapper:hover .hover-buttons { + display: flex; +} + +.hover-button { + background: transparent; + border: none; + border-radius: 50%; + padding: 10px; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + transition: transform 0.3s ease; +} + +.hover-button:focus { + outline: 2px solid #007bff; /* Custom focus state for accessibility */ +} + +.hover-button:hover { + transform: scale(1.3); /* Enlarges the button on hover */ +} + +/* Icon Styling */ +.playIcon, .heartIcon, .dotsIcon { + width: 50px; + height: 50px; }