Skip to content

Commit

Permalink
Added the genre statistics report to the UI
Browse files Browse the repository at this point in the history
  • Loading branch information
davewalker5 committed Nov 13, 2023
1 parent fb2ee8a commit a772fee
Show file tree
Hide file tree
Showing 12 changed files with 244 additions and 16 deletions.
3 changes: 3 additions & 0 deletions src/music-catalogue-ui/components/componentPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import AlbumList from "./albumList";
import TrackList from "./trackList";
import LookupAlbum from "./lookupAlbum";
import ExportCatalogue from "./exportCatalogue";
import GenreStatusReport from "./genreStatisticsReport";
import JobStatusReport from "./jobStatusReport";
import AlbumPurchaseDetails from "./albumPurchaseDetails";

Expand Down Expand Up @@ -48,6 +49,8 @@ const ComponentPicker = ({ context, navigate, logout }) => {
return <LookupAlbum navigate={navigate} logout={logout} />;
case pages.export:
return <ExportCatalogue logout={logout} />;
case pages.genreStatisticsReport:
return <GenreStatusReport logout={logout} />;
case pages.jobStatusReport:
return <JobStatusReport logout={logout} />;
case pages.albumPurchaseDetails:
Expand Down
27 changes: 27 additions & 0 deletions src/music-catalogue-ui/components/dateAndTimeFormatter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import config from "../config.json";

/**
* Format a value as a date and time using the locale from the config file
* @param {*} param0
* @returns
*/
const DateAndTimeFormatter = ({ value }) => {
// Check there's a value to format
if (value != null) {
// Create a formatter to format the value
const formatter = new Intl.DateTimeFormat(config.region.locale, {
dateStyle: "short",
timeStyle: "medium",
});

// Format the value
const dateToFormat = new Date(value);
const formattedValue = formatter.format(dateToFormat);

return <>{formattedValue}</>;
} else {
return <></>;
}
};

export default DateAndTimeFormatter;
94 changes: 94 additions & 0 deletions src/music-catalogue-ui/components/genreStatisticsReport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React, { useCallback, useState } from "react";
import styles from "./reports.module.css";
import Select from "react-select";
import "react-datepicker/dist/react-datepicker.css";
import { apiGenreStatisticsReport } from "@/helpers/apiReports";
import GenreStatisticsRow from "./genreStatisticsRow";

/**
* Component to display the job status search page and its results
* @param {*} logout
* @returns
*/
const GenreStatisticsReport = ({ logout }) => {
const [catalogue, setCatalogue] = useState(0);
const [records, setRecords] = useState(null);

// Callback to request the genre statistics report from the API
const getReportCallback = useCallback(
async (e) => {
// Prevent the default action associated with the click event
e.preventDefault();

// Set the wishlist flag from the drop-down selection
const forWishList = catalogue.value == "wishlist";

// Fetch the report
const fetchedRecords = await apiGenreStatisticsReport(
forWishList,
logout
);
setRecords(fetchedRecords);
},
[catalogue, logout]
);

// Construct a list of select list options for the directory
const options = [
{ value: "catalogue", label: "Main Catalogue" },
{ value: "wishlist", label: "Wish List" },
];

return (
<>
<div className="row mb-2 pageTitle">
<h5 className="themeFontColor text-center">Genre Statistics Report</h5>
</div>
<div className={styles.reportFormContainer}>
<form className={styles.reportForm}>
<div className="row" align="center">
<div className="mt-3">
<div className="d-inline-flex align-items-center">
<label className={styles.reportFormLabel}>
Generate Report For
</label>
<Select
className={styles.reportCatalogueSelector}
defaultValue={catalogue}
onChange={setCatalogue}
options={options}
/>
</div>
<button
className="btn btn-primary"
onClick={(e) => getReportCallback(e)}
>
Search
</button>
</div>
</div>
</form>
</div>
<table className="table table-hover">
<thead>
<tr>
<th>Genre</th>
<th>Artists</th>
<th>Albums</th>
<th>Tracks</th>
<th>Total Spend</th>
</tr>
</thead>
{records != null && (
<tbody>
{records.map((r) => (
<GenreStatisticsRow key={r.id} record={r} />
))}
</tbody>
)}
</table>
</>
);
};

export default GenreStatisticsReport;
22 changes: 22 additions & 0 deletions src/music-catalogue-ui/components/genreStatisticsRow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import CurrencyFormatter from "./currencyFormatter";

/**
* Component to render a row containing the details for a single genre statistics record
* @param {*} record
* @returns
*/
const GenreStatisticsRow = ({ record }) => {
return (
<tr>
<td>{record.genre}</td>
<td>{record.artists}</td>
<td>{record.albums}</td>
<td>{record.tracks}</td>
<td>
<CurrencyFormatter value={record.spend} renderZeroAsBlank={true} />
</td>
</tr>
);
};

export default GenreStatisticsRow;
23 changes: 17 additions & 6 deletions src/music-catalogue-ui/components/jobStatusReport.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useCallback, useState } from "react";
import styles from "./jobStatusReport.module.css";
import styles from "./reports.module.css";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { apiJobStatusReport } from "@/helpers/apiReports";
Expand All @@ -16,10 +16,21 @@ const JobStatusReport = ({ logout }) => {
const [records, setRecords] = useState(null);

// Callback to request the job status report from the API
const getReportCallback = useCallback(async () => {
const fetchedRecords = await apiJobStatusReport(startDate, endDate, logout);
setRecords(fetchedRecords);
}, [startDate, endDate, logout]);
const getReportCallback = useCallback(
async (e) => {
// Prevent the default action associated with the click event
e.preventDefault();

// Fetch the report
const fetchedRecords = await apiJobStatusReport(
startDate,
endDate,
logout
);
setRecords(fetchedRecords);
},
[startDate, endDate, logout]
);

return (
<>
Expand All @@ -46,7 +57,7 @@ const JobStatusReport = ({ logout }) => {
</div>
<button
className="btn btn-primary"
onClick={() => getReportCallback()}
onClick={(e) => getReportCallback(e)}
>
Search
</button>
Expand Down
10 changes: 8 additions & 2 deletions src/music-catalogue-ui/components/jobStatusRow.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import DateAndTimeFormatter from "./dateAndTimeFormatter";

/**
* Component to render a row containing the details of a single job status record
* @param {*} record
Expand All @@ -8,8 +10,12 @@ const JobStatusRow = ({ record }) => {
<tr>
<td>{record.name}</td>
<td>{record.parameters}</td>
<td>{record.start}</td>
<td>{record.end}</td>
<td>
<DateAndTimeFormatter value={record.start} />
</td>
<td>
<DateAndTimeFormatter value={record.end} />
</td>
<td>{record.error}</td>
</tr>
);
Expand Down
13 changes: 7 additions & 6 deletions src/music-catalogue-ui/components/lookupAlbum.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const LookupAlbum = ({ navigate, logout }) => {
const [artistName, setArtistName] = useState("");
const [albumTitle, setAlbumTitle] = useState("");
const [errorMessage, setErrorMessage] = useState("");
const [target, setTarget] = useState("wishlist");
const [catalogue, setCatalogue] = useState("wishlist");

// Lookup navigation callback
const lookup = useCallback(
Expand All @@ -25,7 +25,7 @@ const LookupAlbum = ({ navigate, logout }) => {
e.preventDefault();

// Determine where to store new albums based on the target drop-down
const storeInWishList = target.value == "wishlist";
const storeInWishList = catalogue.value == "wishlist";

// Lookup the album - this will preferentially use the local database via the
// REST API and fallback to the external API if needed
Expand All @@ -49,7 +49,7 @@ const LookupAlbum = ({ navigate, logout }) => {
setErrorMessage(`Album "${albumTitle}" by "${artistName}" not found`);
}
},
[artistName, albumTitle, target, navigate, logout]
[artistName, albumTitle, catalogue, navigate, logout]
);

// Construct a list of select list options for the target directory
Expand Down Expand Up @@ -87,10 +87,11 @@ const LookupAlbum = ({ navigate, logout }) => {
/>
</div>
<div className="form-group mt-3">
<label className={styles.lookupFormLabel}>Target Directory</label>
<label className={styles.lookupFormLabel}>Save Album To</label>
<Select
defaultValue={target}
onChange={setTarget}
className={styles.lookupCatalogueSelector}
defaultValue={catalogue}
onChange={setCatalogue}
options={options}
/>
</div>
Expand Down
4 changes: 4 additions & 0 deletions src/music-catalogue-ui/components/lookupAlbum.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@
.lookupButton {
float: right;
}

.lookupCatalogueSelector {
width: 300px;
}
7 changes: 7 additions & 0 deletions src/music-catalogue-ui/components/menuBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ const MenuBar = ({ navigate, logout }) => {
</div>
</button>
<div className={styles.dropdownContent}>
<a
onClick={() =>
navigate(pages.genreStatisticsReport, null, null, false)
}
>
Genre Statistics
</a>
<a
onClick={() => navigate(pages.jobStatusReport, null, null, false)}
>
Expand Down
32 changes: 32 additions & 0 deletions src/music-catalogue-ui/components/reports.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.reportFormContainer {
display: flex;
justify-content: center;
align-items: center;
}

.reportForm {
width: 100vw;
padding-top: 20px;
padding-bottom: 20px;
}

.reportFormLabel {
margin-right: 20px;
font-size: 14px;
font-weight: 600;
color: rgb(34, 34, 34);
}

.reportForm * input {
margin-left: 20px;
margin-right: 20px;
}

.reportForm * button {
margin-left: 20px;
margin-right: 20px;
}

.reportCatalogueSelector {
width: 300px;
}
24 changes: 22 additions & 2 deletions src/music-catalogue-ui/helpers/apiReports.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,27 @@ const apiJobStatusReport = async (from, to, logout) => {
const fromRouteSegment = apiFormatDateTime(startDate);
const toRouteSegment = apiFormatDateTime(endDate);
const url = `${config.api.baseUrl}/reports/jobs/${fromRouteSegment}/${toRouteSegment}`;
console.log(url);

// Call the API to get content for the report
const response = await fetch(url, {
method: "GET",
headers: apiGetHeaders(),
});

const records = await apiReadResponseData(response, logout);
return records;
};

/**
* Call the API to retrieve the genre statistics report
* @param {*} wishlist
* @param {*} logout
* @returns
*/
const apiGenreStatisticsReport = async (wishlist, logout) => {
// Construct the route
const url = `${config.api.baseUrl}/reports/genres/${wishlist}`;

// Call the API to get content for the report
const response = await fetch(url, {
method: "GET",
Expand All @@ -46,4 +66,4 @@ const apiJobStatusReport = async (from, to, logout) => {
return records;
};

export { apiJobStatusReport };
export { apiJobStatusReport, apiGenreStatisticsReport };
1 change: 1 addition & 0 deletions src/music-catalogue-ui/helpers/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const pages = {
tracks: "Tracks",
lookup: "Lookup",
export: "Export",
genreStatisticsReport: "GenreStatisticsReport",
jobStatusReport: "JobStatusReport",
albumPurchaseDetails: "AlbumPurchaseDetails",
};
Expand Down

0 comments on commit a772fee

Please sign in to comment.