Skip to content

Commit

Permalink
Merge pull request #38 from davewalker5/MC-226-Album-Editor
Browse files Browse the repository at this point in the history
MC-226 Add an album editor
  • Loading branch information
davewalker5 authored Nov 27, 2023
2 parents 6b3956c + f09e2b2 commit 3e34d4d
Show file tree
Hide file tree
Showing 33 changed files with 623 additions and 87 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ MusicCatalogue.LookupTool --lookup "John Coltrane" "Blue Train" catalogue
<img src="diagrams/purchase-details.png" alt="Purchase Details" width="600">

- Note that retailers must be added using the retailer list and retailer editing page (see below) before they will appear in the drop-down on the purchase details page
- Clicking on the "Edit" icon opens the album editor to edit the album properties:

<img src="diagrams/album-editor.png" alt="Track List" width="600">

- Clicking on the "Add" button at the bottom of the album list will open a blank album editor to add and save a new album for the current artist
- Clicking anywhere else on a row opens the track list for the album shown in that row:

<img src="diagrams/track-list.png" alt="Track List" width="600">
Expand Down
Binary file added diagrams/album-editor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified diagrams/album-list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions src/MusicCatalogue.Api/Controllers/AlbumsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,34 @@ public async Task<ActionResult<IEnumerable<Album>>> GetAlbumsAsync([FromBody] Al
return albums;
}

/// <summary>
/// Update an album from a template contained in the request body
/// </summary>
/// <param name="template"></param>
/// <returns></returns>
[HttpPost]
[Route("")]
public async Task<ActionResult<Album>> AddAlbumAsync([FromBody] Album template)
{
// Make sure the "other" Genre exists as a fallback for album updates where no genre is given
var otherGenre = _factory.Genres.AddAsync(OtherGenre);

// Add the album
var album = await _factory.Albums.AddAsync(
template.ArtistId,
template.GenreId ?? otherGenre.Id,
template.Title,
template.Released,
template.CoverUrl,
template.IsWishListItem,
template.Purchased,
template.Price,
template.RetailerId);

// Return the new album
return album;
}

/// <summary>
/// Update an album from a template contained in the request body
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions src/MusicCatalogue.Logic/Database/AlbumManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public async Task<Album> AddAsync(

if (album == null)
{
// Add the album
album = new Album
{
ArtistId = artistId,
Expand All @@ -82,6 +83,9 @@ public async Task<Album> AddAsync(
};
await Context.Albums.AddAsync(album);
await Context.SaveChangesAsync();

// Now re-retrieve it to populate related entities
album = await GetAsync(x => x.Id == album.Id);
}

return album;
Expand Down
190 changes: 190 additions & 0 deletions src/music-catalogue-ui/components/albumEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import styles from "./albumEditor.module.css";
import pages from "@/helpers/navigation";
import catalogues from "@/helpers/catalogues";
import FormInputField from "./formInputField";
import CatalogueSelector from "./catalogueSelector";
import { apiCreateAlbum, apiUpdateAlbum } from "@/helpers/apiAlbums";
import { useState, useCallback } from "react";
import GenreSelector from "./genreSelector";

/**
* Component to render an album editor, excluding purchase details that are
* maintained via their own component and the catalogue, which is maintained
* via the album list
* @param {*} artist
* @param {*} album
* @param {*} isWishList
* @param {*} navigate
* @param {*} logout
* @returns
*/
const AlbumEditor = ({ artist, album, isWishList, navigate, logout }) => {
// Get the initial genre selection
let initialGenre = null;
if (album != null) {
initialGenre = album.genre;
}

// Get initial values for the remaining album properties
const initialTitle = album != null ? album.title : null;
const initialReleased = album != null ? album.released : null;
const initialCoverUrl = album != null ? album.coverUrl : null;

// Setup state
const [title, setTitle] = useState(initialTitle);
const [genre, setGenre] = useState(initialGenre);
const [released, setReleased] = useState(initialReleased);
const [coverUrl, setCoverUrl] = useState(initialCoverUrl);
const [error, setError] = useState("");

const saveAlbum = useCallback(
async (e) => {
// Prevent the default action associated with the click event
e.preventDefault();

// Clear pre-existing errors
setError("");

try {
// Get the genre ID
const genreId = genre != null ? genre.id : null;

// Either add or update the album, depending on whether there's an
// existing album or not
let updatedAlbum = null;
if (album == null) {
// Create the album
updatedAlbum = await apiCreateAlbum(
artist.id,
genreId,
title,
released,
coverUrl,
isWishList,
null,
null,
null,
logout
);
} else {
// Update the existing album
updatedAlbum = await apiUpdateAlbum(
album.id,
artist.id,
genreId,
title,
released,
coverUrl,
album.isWishListItem,
album.purchased,
album.price,
album.retailerId,
logout
);
}

// Go back to the album list for the artist, which should reflect the
// updated details
navigate({
page: pages.albums,
artist: artist,
isWishList: isWishList,
});
} catch (ex) {
setError(`Error saving the updated album details: ${ex.message}`);
}
},
[
album,
artist,
title,
genre,
released,
coverUrl,
isWishList,
navigate,
logout,
]
);

// Set the page title
const pageTitle =
album != null
? `${album.title} - ${artist.name}`
: `New Album - ${artist.name}`;

return (
<>
<div className="row mb-2 pageTitle">
<h5 className="themeFontColor text-center">{pageTitle}</h5>
</div>
<div className={styles.albumEditorFormContainer}>
<form className={styles.albumEditorForm}>
<div className="row">
{error != "" ? (
<div className={styles.albumEditorError}>{error}</div>
) : (
<></>
)}
</div>
<div className="row align-items-start">
<FormInputField
label="Title"
name="title"
value={title}
setValue={setTitle}
/>
</div>
<div className="form-group mt-3">
<label className={styles.albumEditorFormLabel}>Genre</label>
<div>
<GenreSelector
initialGenre={genre}
genreChangedCallback={setGenre}
logout={logout}
/>
</div>
</div>
<div className="row align-items-start">
<FormInputField
label="Released"
name="released"
value={released}
setValue={setReleased}
/>
</div>
<div className="row align-items-start">
<FormInputField
label="Cover URL"
name="coverUrl"
value={coverUrl}
setValue={setCoverUrl}
/>
</div>
<div className="d-grid gap-2 mt-3"></div>
<div className="d-grid gap-2 mt-3"></div>
<div className={styles.albumEditorButton}>
<button className="btn btn-primary" onClick={(e) => saveAlbum(e)}>
Save
</button>
</div>
<div className={styles.albumEditorButton}>
<button
className="btn btn-primary"
onClick={() =>
navigate({
page: pages.albums,
artist: artist,
})
}
>
Cancel
</button>
</div>
</form>
</div>
</>
);
};

export default AlbumEditor;
27 changes: 27 additions & 0 deletions src/music-catalogue-ui/components/albumEditor.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.albumEditorFormContainer {
display: flex;
justify-content: center;
align-items: center;
}

.albumEditorForm {
width: 80%;
padding-top: 20px;
padding-bottom: 20px;
}

.albumEditorFormLabel {
font-size: 14px;
font-weight: 600;
color: rgb(34, 34, 34);
}

.albumEditorButton {
margin-left: 10px;
float: right;
}

.albumEditorError {
font-weight: bold;
color: red;
}
17 changes: 17 additions & 0 deletions src/music-catalogue-ui/components/albumList.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import styles from "./albumList.module.css";
import pages from "@/helpers/navigation";
import useAlbums from "@/hooks/useAlbums";
import AlbumRow from "./albumRow";

Expand Down Expand Up @@ -39,6 +41,7 @@ const AlbumList = ({ artist, isWishList, navigate, logout }) => {
<th>Retailer</th>
<th />
<th />
<th />
</tr>
</thead>
<tbody>
Expand All @@ -56,6 +59,20 @@ const AlbumList = ({ artist, isWishList, navigate, logout }) => {
))}
</tbody>
</table>
<div className={styles.albumListAddButton}>
<button
className="btn btn-primary"
onClick={() =>
navigate({
page: pages.albumEditor,
artist: artist,
isWishList: isWishList,
})
}
>
Add
</button>
</div>
</>
);
};
Expand Down
3 changes: 3 additions & 0 deletions src/music-catalogue-ui/components/albumList.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.albumListAddButton {
float: right;
}
Loading

0 comments on commit 3e34d4d

Please sign in to comment.