- navigate(pages.tracks, artist, album, isWishList)}>
- {artist.name}
- |
- navigate(pages.tracks, artist, album, isWishList)}>
- {album.title}
- |
- navigate(pages.tracks, artist, album, isWishList)}>
- {genreName}
- |
- navigate(pages.tracks, artist, album, isWishList)}>
- {album.released}
- |
- navigate(pages.tracks, artist, album, isWishList)}>
+ | {artist.name} |
+ {album.title} |
+ {genreName} |
+ {album.released} |
+
|
- navigate(pages.tracks, artist, album, isWishList)}>
+ |
|
- navigate(pages.tracks, artist, album, isWishList)}>
- {retailerName}
- |
+ {retailerName} |
- navigate(pages.albumPurchaseDetails, artist, album, false)
+ navigate({
+ page: pages.albumPurchaseDetails,
+ artist: artist,
+ album: album,
+ })
}
/>
|
diff --git a/src/music-catalogue-ui/components/app.js b/src/music-catalogue-ui/components/app.js
index 49ceef2..04ee80c 100644
--- a/src/music-catalogue-ui/components/app.js
+++ b/src/music-catalogue-ui/components/app.js
@@ -6,10 +6,20 @@ import { apiClearToken } from "@/helpers/apiToken";
import useIsLoggedIn from "@/hooks/useIsLoggedIn";
import MenuBar from "./menuBar";
+/**
+ * Default application state:
+ */
const defaultContext = {
+ // Current page
page: pages.artists,
+
+ // Artist and album context
artist: null,
album: null,
+
+ // Data retrieval/filering criteria
+ genre: null,
+ filter: "A",
isWishList: false,
};
@@ -17,18 +27,29 @@ const App = () => {
// Hook to determine the current logged in state, and a method to change it
const { isLoggedIn, setIsLoggedIn } = useIsLoggedIn();
- // Application-wide context
+ // Application state
const [context, setContext] = useState(defaultContext);
// Callback to set the context
- const navigate = useCallback((page, artist, album, isWishList) => {
- setContext({
+ const navigate = ({
+ page = pages.artists,
+ artist = null,
+ album = null,
+ genre = null,
+ filter = "A",
+ isWishList = false,
+ } = {}) => {
+ // Set the context, applying defaults to any values that are undefined
+ const updatedContext = {
page: page,
- artist: artist,
- album: album,
- isWishList: isWishList,
- });
- }, []);
+ artist: typeof artist != "undefined" ? artist : null,
+ album: typeof album != "undefined" ? album : null,
+ genre: typeof genre != "undefined" ? genre : null,
+ filter: typeof filter != "undefined" ? filter : "A",
+ isWishList: typeof isWishList != "undefined" ? isWishList : false,
+ };
+ setContext(updatedContext);
+ };
// Callbacks to set the logged in flag
const login = useCallback(() => {
diff --git a/src/music-catalogue-ui/components/artistFilter.js b/src/music-catalogue-ui/components/artistFilter.js
index 8826541..cdab152 100644
--- a/src/music-catalogue-ui/components/artistFilter.js
+++ b/src/music-catalogue-ui/components/artistFilter.js
@@ -1,28 +1,13 @@
-import { useCallback } from "react";
import styles from "./artistFilter.module.css";
-import { apiFetchArtists } from "../helpers/apiArtists";
-
-const ArtistFilter = ({
- label,
- separator,
- filter,
- isWishList,
- setArtists,
- logout,
-}) => {
- /* Callback to filter the artist list by this filter */
- const filterArtists = useCallback(async () => {
- try {
- // Get a list of artists matching the specified criteria
- var fetchedArtists = await apiFetchArtists(filter, isWishList, logout);
- setArtists(fetchedArtists);
- } catch {}
- }, [filter, isWishList, logout, setArtists]);
+const ArtistFilter = ({ label, separator, filter, setFilter }) => {
return (
<>
{separator != "" ? {separator} : <>>}
-
+ setFilter(filter)}
+ >
{label}
>
diff --git a/src/music-catalogue-ui/components/artistFilterBar.js b/src/music-catalogue-ui/components/artistFilterBar.js
index bcbf2e9..e24cc2d 100644
--- a/src/music-catalogue-ui/components/artistFilterBar.js
+++ b/src/music-catalogue-ui/components/artistFilterBar.js
@@ -1,6 +1,6 @@
import ArtistFilter from "./artistFilter";
-const ArtistFilterBar = ({ isWishList, logout, setArtists }) => {
+const ArtistFilterBar = ({ setFilter }) => {
// Construct the filtering , starting with "All"
let options = [{ id: 0, label: "All", filter: "*", separator: "" }];
@@ -21,9 +21,7 @@ const ArtistFilterBar = ({ isWishList, logout, setArtists }) => {
label={o.label}
separator={o.separator}
filter={o.filter}
- isWishList={isWishList}
- setArtists={setArtists}
- logout={logout}
+ setFilter={setFilter}
/>
))}
>
diff --git a/src/music-catalogue-ui/components/artistList.js b/src/music-catalogue-ui/components/artistList.js
index 85540be..e1372f9 100644
--- a/src/music-catalogue-ui/components/artistList.js
+++ b/src/music-catalogue-ui/components/artistList.js
@@ -1,25 +1,34 @@
-import styles from "./artistList.module.css";
+import pages from "../helpers/navigation";
import useArtists from "@/hooks/useArtists";
import ArtistRow from "./artistRow";
import ArtistFilterBar from "./artistFilterBar";
/**
* Component to render a table listing all the artists in the catalogue
+ * @param {*} filter
* @param {*} isWishList
* @param {*} navigate
* @param {*} logout
* @returns
*/
-const ArtistList = ({ filter, isWishList, navigate, logout }) => {
- const { artists, setArtists } = useArtists(filter, isWishList, logout);
+const ArtistList = ({ filter, genre, isWishList, navigate, logout }) => {
+ const { artists, setArtists } = useArtists(filter, genre, isWishList, logout);
// Callback to pass to child components to set the artist list
- const setArtistsCallback = (artists) => {
- setArtists(artists);
+ const setFilterCallback = (updatedFilter) => {
+ navigate({
+ page: pages.artists,
+ filter: updatedFilter,
+ genre: genre,
+ isWishList: isWishList,
+ });
};
// Set the page title to reflect whether we're viewing the wish list
- const title = isWishList ? "Wish List Artists" : "Artists";
+ let title = isWishList ? "Wish List Artists" : "Artists";
+ if (genre != null) {
+ title = `${title} - ${genre.name}`;
+ }
return (
<>
@@ -28,11 +37,7 @@ const ArtistList = ({ filter, isWishList, navigate, logout }) => {
diff --git a/src/music-catalogue-ui/components/artistList.module.css b/src/music-catalogue-ui/components/artistList.module.css
deleted file mode 100644
index 2f0004e..0000000
--- a/src/music-catalogue-ui/components/artistList.module.css
+++ /dev/null
@@ -1,3 +0,0 @@
-.artistListFilterBar {
- align-self: center;
-}
diff --git a/src/music-catalogue-ui/components/artistRow.js b/src/music-catalogue-ui/components/artistRow.js
index 507bf2b..47ae1cc 100644
--- a/src/music-catalogue-ui/components/artistRow.js
+++ b/src/music-catalogue-ui/components/artistRow.js
@@ -9,7 +9,11 @@ import pages from "@/helpers/navigation";
*/
const ArtistRow = ({ artist, isWishList, navigate }) => {
return (
- navigate(pages.albums, artist, null, isWishList)}>
+
+ navigate({ page: pages.albums, artist: artist, isWishList: isWishList })
+ }
+ >
{artist.name} |
);
diff --git a/src/music-catalogue-ui/components/componentPicker.js b/src/music-catalogue-ui/components/componentPicker.js
index d9b6a74..5888dfa 100644
--- a/src/music-catalogue-ui/components/componentPicker.js
+++ b/src/music-catalogue-ui/components/componentPicker.js
@@ -9,10 +9,10 @@ import JobStatusReport from "./jobStatusReport";
import AlbumPurchaseDetails from "./albumPurchaseDetails";
import ArtistStatisticsReport from "./artistStatisticsReport";
import MonthlySpendReport from "./monthlySpendReport";
+import GenreList from "./genreList";
/**
- * Component using the current page name to render the components required
- * by that page
+ * Component using the current context to select and render the current page
* @param {*} context
* @param {*} navigate
* @param {*} logout
@@ -23,12 +23,15 @@ const ComponentPicker = ({ context, navigate, logout }) => {
case pages.artists:
return (
);
+ case pages.genres:
+ return ;
case pages.albums:
return (
{
+ const { genres, setGenres } = useGenres(false, logout);
+
+ // Callback to pass to child components to set the genre
+ const setGenreCallback = (genre) => {
+ navigate({
+ page: pages.artists,
+ genre: genre,
+ filter: "*",
+ });
+ };
+
+ return (
+ <>
+
+
Genres
+
+
+
+
+ Name |
+
+
+ {genres != null && (
+
+ {genres.map((g) => (
+
+ ))}
+
+ )}
+
+ >
+ );
+};
+
+export default GenreList;
diff --git a/src/music-catalogue-ui/components/genreRow.js b/src/music-catalogue-ui/components/genreRow.js
new file mode 100644
index 0000000..bb62b71
--- /dev/null
+++ b/src/music-catalogue-ui/components/genreRow.js
@@ -0,0 +1,15 @@
+/**
+ * Component to render a row containing the details for a single genre
+ * @param {*} genre
+ * @param {*} setGenre
+ * @returns
+ */
+const GenreRow = ({ genre, setGenre }) => {
+ return (
+
+ setGenre(genre)}>{genre.name} |
+
+ );
+};
+
+export default GenreRow;
diff --git a/src/music-catalogue-ui/components/lookupAlbum.js b/src/music-catalogue-ui/components/lookupAlbum.js
index b9cddb8..c2287ed 100644
--- a/src/music-catalogue-ui/components/lookupAlbum.js
+++ b/src/music-catalogue-ui/components/lookupAlbum.js
@@ -42,7 +42,12 @@ const LookupAlbum = ({ navigate, logout }) => {
const artist = await apiFetchArtistById(album.artistId, logout);
if (artist != null) {
// Navigate to the track list
- navigate(pages.tracks, artist, album, storeInWishList);
+ navigate({
+ page: pages.tracks,
+ artist: artist,
+ album: album,
+ isWishList: storeInWishList,
+ });
} else {
setErrorMessage(`Artist with id ${album.artistId} not found`);
}
diff --git a/src/music-catalogue-ui/components/menuBar.js b/src/music-catalogue-ui/components/menuBar.js
index 1948d21..517c5a6 100644
--- a/src/music-catalogue-ui/components/menuBar.js
+++ b/src/music-catalogue-ui/components/menuBar.js
@@ -18,7 +18,7 @@ const MenuBar = ({ navigate, logout }) => {
src="./logo.png"
alt="Music Catalogue"
className={styles.logo}
- onClick={() => navigate(pages.artists, null, null, false)}
+ onClick={() => navigate({ page: pages.artists })}
/>
Music Catalogue
@@ -31,41 +31,32 @@ const MenuBar = ({ navigate, logout }) => {
-
- navigate(pages.artistStatisticsReport, null, null, false)
- }
- >
+ navigate({ page: pages.artistStatisticsReport })}>
Artist Statistics
-
- navigate(pages.genreStatisticsReport, null, null, false)
- }
- >
+ navigate({ page: pages.genreStatisticsReport })}>
Genre Statistics
-
navigate(pages.jobStatusReport, null, null, false)}
- >
+ navigate({ page: pages.jobStatusReport })}>
Job Status
-
- navigate(pages.monthlySpendReport, null, null, false)
- }
- >
+ navigate({ page: pages.monthlySpendReport })}>
Monthly Spend
- navigate(pages.export, null, null, false)}>Export
+ navigate({ page: pages.export })}>Export
Import
- navigate(pages.lookup, null, null, false)}>Search
- navigate(pages.artists, null, null, true)}>
+ navigate({ page: pages.lookup })}>Search
+
+ navigate({ page: pages.artists, filter: "A", isWishList: true })
+ }
+ >
Wish List
- navigate(pages.artists, null, null, false)}>
+ navigate({ page: pages.genres })}>Genres
+ navigate({ page: pages.artists, filter: "A" })}>
Artists
diff --git a/src/music-catalogue-ui/components/trackList.js b/src/music-catalogue-ui/components/trackList.js
index 65d6fda..b2f5c0e 100644
--- a/src/music-catalogue-ui/components/trackList.js
+++ b/src/music-catalogue-ui/components/trackList.js
@@ -27,7 +27,7 @@ const TrackList = ({ artist, album, isWishList, navigate, logout }) => {
// Backwards navigation callback
const navigateBack = useCallback(() => {
- navigate(pages.albums, artist, null, isWishList);
+ navigate({ page: pages.albums, artist: artist, isWishList: isWishList });
}, [navigate, artist, isWishList]);
return (
diff --git a/src/music-catalogue-ui/components/trackRow.js b/src/music-catalogue-ui/components/trackRow.js
index f0e8d5b..e4d0e96 100644
--- a/src/music-catalogue-ui/components/trackRow.js
+++ b/src/music-catalogue-ui/components/trackRow.js
@@ -12,7 +12,13 @@ const TrackRow = ({ artist, album, track, isWishList, navigate }) => {
{album.title} |
navigate(pages.albums, artist, null, isWishList)}
+ onClick={() =>
+ navigate({
+ page: pages.albums,
+ artist: artist,
+ isWishList: isWishList,
+ })
+ }
>
{artist.name}
|
diff --git a/src/music-catalogue-ui/helpers/apiArtists.js b/src/music-catalogue-ui/helpers/apiArtists.js
index 6761938..8ff85f9 100644
--- a/src/music-catalogue-ui/helpers/apiArtists.js
+++ b/src/music-catalogue-ui/helpers/apiArtists.js
@@ -5,16 +5,17 @@ import { apiGetHeaders, apiGetPostHeaders } from "./apiHeaders";
/**
* Fetch a list of artists from the Music Catalogue REST API
* @param {*} filter
+ * @param {*} genreId
* @param {*} isWishList
* @param {*} logout
* @returns
*/
-const apiFetchArtists = async (filter, isWishList, logout) => {
+const apiFetchArtists = async (filter, genreId, isWishList, logout) => {
// Construct the filtering criteria as the request body and convert to JSON
const criteria = {
namePrefix: filter,
wishList: isWishList,
- genreId: null,
+ genreId: genreId,
};
const body = JSON.stringify(criteria);
diff --git a/src/music-catalogue-ui/helpers/apiGenres.js b/src/music-catalogue-ui/helpers/apiGenres.js
new file mode 100644
index 0000000..928d1b5
--- /dev/null
+++ b/src/music-catalogue-ui/helpers/apiGenres.js
@@ -0,0 +1,30 @@
+import config from "../config.json";
+import { apiReadResponseData } from "./apiUtils";
+import { apiGetPostHeaders } from "./apiHeaders";
+
+/**
+ * Fetch a list of genres from the Music Catalogue REST API
+ * @param {*} isWishList
+ * @param {*} logout
+ * @returns
+ */
+const apiFetchGenres = async (isWishList, logout) => {
+ // Construct the filtering criteria as the request body and convert to JSON
+ const criteria = {
+ wishList: isWishList,
+ };
+ const body = JSON.stringify(criteria);
+
+ // Call the API to get a list of genres
+ const url = `${config.api.baseUrl}/genres/search/`;
+ const response = await fetch(url, {
+ method: "POST",
+ headers: apiGetPostHeaders(),
+ body: body,
+ });
+
+ const genres = await apiReadResponseData(response, logout);
+ return genres;
+};
+
+export { apiFetchGenres };
diff --git a/src/music-catalogue-ui/helpers/navigation.js b/src/music-catalogue-ui/helpers/navigation.js
index 9dfc460..c91118c 100644
--- a/src/music-catalogue-ui/helpers/navigation.js
+++ b/src/music-catalogue-ui/helpers/navigation.js
@@ -1,5 +1,6 @@
const pages = {
artists: "Artists",
+ genres: "Genres",
wishlistArtists: "WishlistArtists",
albums: "Albums",
wishlistAlbums: "wishlistAlbums",
diff --git a/src/music-catalogue-ui/hooks/useAlbums.js b/src/music-catalogue-ui/hooks/useAlbums.js
index fb61967..a1a36c8 100644
--- a/src/music-catalogue-ui/hooks/useAlbums.js
+++ b/src/music-catalogue-ui/hooks/useAlbums.js
@@ -16,8 +16,7 @@ const useAlbums = (artistId, isWishList, logout) => {
useEffect(() => {
const fetchAlbums = async (artistId) => {
try {
- // Get a list of albums via the service, store it in state and clear the
- // loading status
+ // Get a list of albums via the service and store it in state
var fetchedAlbums = await apiFetchAlbumsByArtist(
artistId,
isWishList,
diff --git a/src/music-catalogue-ui/hooks/useArtists.js b/src/music-catalogue-ui/hooks/useArtists.js
index e68b725..77cafbf 100644
--- a/src/music-catalogue-ui/hooks/useArtists.js
+++ b/src/music-catalogue-ui/hooks/useArtists.js
@@ -8,16 +8,23 @@ import { apiFetchArtists } from "@/helpers/apiArtists";
* @param {*} logout
* @returns
*/
-const useArtists = (filter, isWishlist, logout) => {
+const useArtists = (filter, genre, isWishlist, logout) => {
// Current list of artists and the method to change it
const [artists, setArtists] = useState([]);
useEffect(() => {
const fetchArtists = async () => {
try {
- // Get a list of artists via the service, store it in state and clear the
- // loading status
- var fetchedArtists = await apiFetchArtists(filter, isWishlist, logout);
+ // Get the genre Id
+ const genreId = genre != null ? genre.id : null;
+
+ // Get a list of artists via the service and store it in state
+ var fetchedArtists = await apiFetchArtists(
+ filter,
+ genreId,
+ isWishlist,
+ logout
+ );
setArtists(fetchedArtists);
} catch {}
};
diff --git a/src/music-catalogue-ui/hooks/useGenres.js b/src/music-catalogue-ui/hooks/useGenres.js
new file mode 100644
index 0000000..a77ff66
--- /dev/null
+++ b/src/music-catalogue-ui/hooks/useGenres.js
@@ -0,0 +1,29 @@
+import { useState, useEffect } from "react";
+import { apiFetchGenres } from "@/helpers/apiGenres";
+
+/**
+ * Hook that uses the API helpers to retrieve a list of genres from the
+ * Music Catalogue REST API
+ * @param {*} logout
+ * @returns
+ */
+const useGenres = (logout) => {
+ // Current list of artists and the method to change it
+ const [genres, setGenres] = useState([]);
+
+ useEffect(() => {
+ const fetchGenres = async () => {
+ try {
+ // Get a list of genres via the service and store it in state
+ var fetchedGenres = await apiFetchGenres(logout);
+ setGenres(fetchedGenres);
+ } catch {}
+ };
+
+ fetchGenres();
+ }, [logout]);
+
+ return { genres, setGenres };
+};
+
+export default useGenres;