From 2dae22a68b0e20af4f830696c7a11d60c6eec17b Mon Sep 17 00:00:00 2001 From: Dave Walker Date: Fri, 24 Nov 2023 07:06:53 +0000 Subject: [PATCH] Added a track editing component --- src/music-catalogue-ui/components/app.js | 5 +- .../components/componentPicker.js | 43 +++-- .../components/trackEditor.js | 165 ++++++++++++++++++ .../components/trackEditor.module.css | 25 +++ src/music-catalogue-ui/components/trackRow.js | 15 ++ src/music-catalogue-ui/helpers/navigation.js | 9 +- 6 files changed, 241 insertions(+), 21 deletions(-) create mode 100644 src/music-catalogue-ui/components/trackEditor.js create mode 100644 src/music-catalogue-ui/components/trackEditor.module.css diff --git a/src/music-catalogue-ui/components/app.js b/src/music-catalogue-ui/components/app.js index 1135a57..765f939 100644 --- a/src/music-catalogue-ui/components/app.js +++ b/src/music-catalogue-ui/components/app.js @@ -15,9 +15,10 @@ const defaultContext = { // Current page page: pages.artists, - // Artist, album and retailer context + // Artist, album, track and retailer context artist: null, album: null, + track: null, retailer: null, // Data retrieval/filering criteria @@ -38,6 +39,7 @@ const App = () => { page = pages.artists, artist = null, album = null, + track = null, retailer = null, genre = null, filter = "A", @@ -48,6 +50,7 @@ const App = () => { page: page, artist: typeof artist != "undefined" ? artist : null, album: typeof album != "undefined" ? album : null, + track: typeof track != "undefined" ? track : null, retailer: typeof retailer != "undefined" ? retailer : null, genre: typeof genre != "undefined" ? genre : null, filter: typeof filter != "undefined" ? filter : "A", diff --git a/src/music-catalogue-ui/components/componentPicker.js b/src/music-catalogue-ui/components/componentPicker.js index 9202b5d..e777872 100644 --- a/src/music-catalogue-ui/components/componentPicker.js +++ b/src/music-catalogue-ui/components/componentPicker.js @@ -13,6 +13,7 @@ import GenreList from "./genreList"; import RetailerList from "./retailerList"; import RetailerDetails from "./retailerDetails"; import RetailerEditor from "./retailerEditor"; +import TrackEditor from "./trackEditor"; /** * Component using the current context to select and render the current page @@ -33,8 +34,6 @@ const ComponentPicker = ({ context, navigate, logout }) => { logout={logout} /> ); - case pages.genres: - return ; case pages.albums: return ( { logout={logout} /> ); + case pages.albumPurchaseDetails: + return ( + + ); case pages.tracks: return ( { logout={logout} /> ); - case pages.lookup: - return ; - case pages.export: - return ; - case pages.artistStatisticsReport: - return ; - case pages.genreStatisticsReport: - return ; - case pages.jobStatusReport: - return ; - case pages.monthlySpendReport: - return ; - case pages.albumPurchaseDetails: + case pages.trackEditor: return ( - ); + case pages.lookup: + return ; case pages.retailers: return ; case pages.retailerDetails: @@ -93,6 +92,18 @@ const ComponentPicker = ({ context, navigate, logout }) => { logout={logout} /> ); + case pages.genres: + return ; + case pages.export: + return ; + case pages.artistStatisticsReport: + return ; + case pages.genreStatisticsReport: + return ; + case pages.jobStatusReport: + return ; + case pages.monthlySpendReport: + return ; default: return ; } diff --git a/src/music-catalogue-ui/components/trackEditor.js b/src/music-catalogue-ui/components/trackEditor.js new file mode 100644 index 0000000..84867ee --- /dev/null +++ b/src/music-catalogue-ui/components/trackEditor.js @@ -0,0 +1,165 @@ +import styles from "./trackEditor.module.css"; +import pages from "@/helpers/navigation"; +import FormInputField from "./formInputField"; +import { useState, useCallback } from "react"; +import { apiCreateTrack, apiUpdateTrack } from "@/helpers/apiTracks"; + +const TrackEditor = ({ track, album, artist, navigate, logout }) => { + // Split the track's formatted duration on the ":" + let initialMinutes = null; + let initialSeconds = null; + if (track.formattedDuration != null) { + const elements = track.formattedDuration.split(":"); + if (elements.length > 0) { + initialMinutes = elements[0]; + initialSeconds = elements[1]; + } + } + + const [title, setTitle] = useState(track.title); + const [number, setNumber] = useState(track.number); + const [minutes, setMinutes] = useState(initialMinutes); + const [seconds, setSeconds] = useState(initialSeconds); + const [error, setError] = useState(""); + + /* Callback to save retailer details */ + const saveTrack = useCallback( + async (e) => { + // Prevent the default action associated with the click event + e.preventDefault(); + + // Clear pre-existing errors + setError(""); + + try { + // Calculate the duration from the indiviidual minutes and seconds + // inputs + const durationMinutes = Number(minutes); + const durationSeconds = Number(seconds); + const duration = 1000 * (60 * durationMinutes + durationSeconds); + + // Either add or update the track, depending on whether they currently + // have an ID + let updatedTrack = null; + if (track.id <= 0) { + // Invalid ID, so create a new track + updatedTrack = await apiCreateTrack( + title, + number, + duration, + album.id, + logout + ); + } else { + // Has a valid ID, so update an existing track + updatedTrack = await apiUpdateTrack( + track.id, + title, + number, + duration, + album.id, + logout + ); + } + + // If all's well, navigate back to the track list page. Otherwise, show an error + if (updatedTrack == null) { + const action = track.Id <= 0 ? "adding" : "updating"; + setError(`An error occurred ${action} the track`); + } else { + navigate({ + page: pages.tracks, + artist: artist, + album: album, + }); + } + } catch (e) { + setError( + e.message + //"Error converting the supplied minutes and seconds to a duration" + ); + } + }, + [album, artist, track, title, number, minutes, seconds, navigate, logout] + ); + + return ( + <> +
+
{title}
+
+
+
+
+ {error != "" ? ( +
{error}
+ ) : ( + <> + )} +
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+
+ +
+
+
+ + ); +}; + +export default TrackEditor; diff --git a/src/music-catalogue-ui/components/trackEditor.module.css b/src/music-catalogue-ui/components/trackEditor.module.css new file mode 100644 index 0000000..b54b52f --- /dev/null +++ b/src/music-catalogue-ui/components/trackEditor.module.css @@ -0,0 +1,25 @@ +.trackEditorFormContainer { + display: flex; + justify-content: center; + align-items: center; +} + +.trackEditorForm { + width: 80%; + padding-top: 20px; + padding-bottom: 20px; +} + +.trackEditorButton { + margin-left: 10px; + float: right; +} + +.trackEditorError { + font-weight: bold; + color: red; +} + +.trackEditorGeocode { + margin-left: 10px; +} diff --git a/src/music-catalogue-ui/components/trackRow.js b/src/music-catalogue-ui/components/trackRow.js index 6cd1dd1..449f954 100644 --- a/src/music-catalogue-ui/components/trackRow.js +++ b/src/music-catalogue-ui/components/trackRow.js @@ -1,6 +1,8 @@ import DeleteTrackActionIcon from "./deleteTrackActionIcon"; import styles from "./trackRow.module.css"; import pages from "@/helpers/navigation"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faPenToSquare } from "@fortawesome/free-solid-svg-icons"; /** * Component to render a row containing the details for a single track @@ -41,6 +43,19 @@ const TrackRow = ({ setTracks={setTracks} /> + + + navigate({ + page: pages.trackEditor, + artist: artist, + album: album, + track: track, + }) + } + /> + ); }; diff --git a/src/music-catalogue-ui/helpers/navigation.js b/src/music-catalogue-ui/helpers/navigation.js index 64fda69..f0d8935 100644 --- a/src/music-catalogue-ui/helpers/navigation.js +++ b/src/music-catalogue-ui/helpers/navigation.js @@ -3,8 +3,12 @@ const pages = { genres: "Genres", wishlistArtists: "WishlistArtists", albums: "Albums", - wishlistAlbums: "wishlistAlbums", + wishlistAlbums: "WishlistAlbums", tracks: "Tracks", + trackEditor: "TrackEditor", + retailers: "Retailers", + retailerDetails: "RetailerDetails", + retailerEditor: "RetailerEditor", lookup: "Lookup", export: "Export", artistStatisticsReport: "ArtistStatisticsReport", @@ -12,9 +16,6 @@ const pages = { jobStatusReport: "JobStatusReport", monthlySpendReport: "MonthlySpendReport", albumPurchaseDetails: "AlbumPurchaseDetails", - retailers: "Retailers", - retailerDetails: "RetailerDetails", - retailerEditor: "RetailerEditor", }; export default pages;