Able to add artists and songs.

pull/7/head
Sander Vocke 5 years ago
parent 9e5fa87a6a
commit 7b20f62c36
  1. 1
      client/package.json
  2. 42
      client/src/App.tsx
  3. 1
      client/src/api.ts
  4. 3
      client/src/components/ArtistTable.tsx
  5. 34
      client/src/components/EditSongDialog.tsx
  6. 11
      client/yarn.lock
  7. 3
      server/package.json
  8. 11
      server/server.ts

@ -4,6 +4,7 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@material-ui/core": "^4.11.0", "@material-ui/core": "^4.11.0",
"@material-ui/lab": "^4.0.0-alpha.56",
"@testing-library/jest-dom": "^4.2.4", "@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2", "@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2", "@testing-library/user-event": "^7.1.2",

@ -21,8 +21,8 @@ function App() {
name: "" name: ""
}); });
const fetchSongs = () => { const updateSongs = () => {
fetch(serverApi.ListSongsEndpoint) return fetch(serverApi.ListSongsEndpoint)
.then((response: any) => response.json()) .then((response: any) => response.json())
.then((result: serverApi.ListSongsResponse) => { .then((result: serverApi.ListSongsResponse) => {
setSongs(result.map((item: serverApi.ListSongsResponseItem) => { setSongs(result.map((item: serverApi.ListSongsResponseItem) => {
@ -35,18 +35,29 @@ function App() {
} }
const fetchArtists = () => { const fetchArtists = () => {
fetch(serverApi.ListArtistsEndpoint) return fetch(serverApi.ListArtistsEndpoint)
.then((response: any) => response.json()) .then((response: any) => response.json())
.then((result: serverApi.ListArtistsResponse) => { .then((result: serverApi.ListArtistsResponse) => {
setArtists(result.map((item: serverApi.ListArtistsResponseItem) => { return result.map((item: serverApi.ListArtistsResponseItem) => {
return { return {
name: item.name, name: item.name,
id: item.id,
}; };
})); });
});
}
const updateArtists = () => {
return fetchArtists()
.then((artists: any[]) => {
setArtists(artists);
}); });
} }
const createSong = (p:SongProperties) => { const createSong = (p:SongProperties) => {
if(!p.artistId) {
throw "Undefined artist ID for song to be created.";
}
const request:serverApi.CreateSongRequest = { const request:serverApi.CreateSongRequest = {
title: p.title, title: p.title,
artistId: p.artistId, artistId: p.artistId,
@ -56,7 +67,7 @@ function App() {
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: JSON.stringify(request) body: JSON.stringify(request)
}; };
fetch(serverApi.CreateSongEndpoint, requestOpts) return fetch(serverApi.CreateSongEndpoint, requestOpts)
} }
const createArtist = (p:ArtistProperties) => { const createArtist = (p:ArtistProperties) => {
@ -68,7 +79,7 @@ function App() {
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: JSON.stringify(request) body: JSON.stringify(request)
}; };
fetch(serverApi.CreateArtistEndpoint, requestOpts) return fetch(serverApi.CreateArtistEndpoint, requestOpts)
} }
const openCreateSongDialog = () => { const openCreateSongDialog = () => {
@ -80,9 +91,9 @@ function App() {
} }
const onSubmitCreateSongDialog = () => { const onSubmitCreateSongDialog = () => {
createSong(songDialogProperties); createSong(songDialogProperties)
fetchSongs(); .then(updateSongs)
setEditSongDialogOpen(false); .then(() => { setEditSongDialogOpen(false); })
} }
const openCreateArtistDialog = () => { const openCreateArtistDialog = () => {
@ -93,13 +104,13 @@ function App() {
} }
const onSubmitCreateArtistDialog = () => { const onSubmitCreateArtistDialog = () => {
createArtist(artistDialogProperties); createArtist(artistDialogProperties)
fetchArtists(); .then(updateArtists)
setEditArtistDialogOpen(false); .then(() => { setEditArtistDialogOpen(false); })
} }
useEffect(fetchSongs, []); useEffect(() => { updateSongs(); }, []);
useEffect(fetchArtists, []); useEffect(() => { updateArtists(); }, []);
return ( return (
<div style={{ maxWidth: '100%' }}> <div style={{ maxWidth: '100%' }}>
@ -119,6 +130,7 @@ function App() {
onChangeSongProperties={(props:SongProperties) => setSongDialogProperties(props)} onChangeSongProperties={(props:SongProperties) => setSongDialogProperties(props)}
songProperties={songDialogProperties} songProperties={songDialogProperties}
onSubmit={onSubmitCreateSongDialog} onSubmit={onSubmitCreateSongDialog}
artists={artists}
/> />
<EditArtistDialog <EditArtistDialog
dialogOpen={editArtistDialogOpen} dialogOpen={editArtistDialogOpen}

@ -26,6 +26,7 @@ export const ListArtistsEndpoint = '/artist/list';
export interface ListArtistsRequest {} export interface ListArtistsRequest {}
export interface ListArtistsResponseItem { export interface ListArtistsResponseItem {
name: String; name: String;
id: Number;
} }
export interface ListArtistsResponse extends Array<ListArtistsResponseItem>{}; export interface ListArtistsResponse extends Array<ListArtistsResponseItem>{};
export function checkListArtistsRequest(req:any): boolean { export function checkListArtistsRequest(req:any): boolean {

@ -2,7 +2,8 @@ import React from 'react';
import MaterialTable from 'material-table'; import MaterialTable from 'material-table';
export interface Entry { export interface Entry {
name: String name: String,
id: Number,
} }
export interface IProps { export interface IProps {

@ -1,11 +1,17 @@
import React from 'react'; import React, { useState, useEffect } from 'react';
import { Dialog, Grid, Typography, TextField, Button } from '@material-ui/core'; import { Dialog, Grid, Typography, TextField, Button } from '@material-ui/core';
import { Autocomplete } from '@material-ui/lab';
var cloneDeep = require('lodash/cloneDeep'); var cloneDeep = require('lodash/cloneDeep');
export interface SongProperties { export interface SongProperties {
title: String, title: String,
artistId: Number, artistId: Number | undefined,
}
export interface ArtistProperties {
name: String,
id: Number,
} }
export interface IProps { export interface IProps {
@ -14,6 +20,7 @@ export interface IProps {
onChangeSongProperties?: (props: SongProperties) => void, onChangeSongProperties?: (props: SongProperties) => void,
songProperties: SongProperties, songProperties: SongProperties,
onSubmit?: () => void, onSubmit?: () => void,
artists: ArtistProperties[],
} }
export default function EditSongDialog(props: IProps) { export default function EditSongDialog(props: IProps) {
@ -24,10 +31,10 @@ export default function EditSongDialog(props: IProps) {
props.onChangeSongProperties(p); props.onChangeSongProperties(p);
} }
}; };
const onArtistChange = (artist: Number) => { const onArtistChange = (artist: Number | undefined) => {
if (props.onChangeSongProperties) { if (props.onChangeSongProperties) {
const p = cloneDeep(props.songProperties); const p = cloneDeep(props.songProperties);
p.artistHash = artist; p.artistId = artist;
props.onChangeSongProperties(p); props.onChangeSongProperties(p);
} }
}; };
@ -49,12 +56,25 @@ export default function EditSongDialog(props: IProps) {
/> />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<TextField <Autocomplete
options={props.artists}
getOptionLabel={(option) => option.name as string}
onChange={(event, newValue) => {
console.log("Change: ", newValue)
if(newValue) {
onArtistChange(newValue.id);
} else {
onArtistChange(undefined);
}
}}
renderInput={
(params) =>
<TextField {...params}
label="Artist" label="Artist"
value={props.songProperties.artistId}
onChange={(i: any) => onArtistChange(parseInt(i.target.value))}
fullWidth fullWidth
/> />
}
/>
</Grid> </Grid>
</Grid> </Grid>
<Button variant="contained" onClick={props.onSubmit}>Submit</Button> <Button variant="contained" onClick={props.onSubmit}>Submit</Button>

@ -1321,6 +1321,17 @@
react-is "^16.8.0" react-is "^16.8.0"
react-transition-group "^4.4.0" react-transition-group "^4.4.0"
"@material-ui/lab@^4.0.0-alpha.56":
version "4.0.0-alpha.56"
resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.56.tgz#ff63080949b55b40625e056bbda05e130d216d34"
integrity sha512-xPlkK+z/6y/24ka4gVJgwPfoCF4RCh8dXb1BNE7MtF9bXEBLN/lBxNTK8VAa0qm3V2oinA6xtUIdcRh0aeRtVw==
dependencies:
"@babel/runtime" "^7.4.4"
"@material-ui/utils" "^4.10.2"
clsx "^1.0.4"
prop-types "^15.7.2"
react-is "^16.8.0"
"@material-ui/pickers@^3.2.2": "@material-ui/pickers@^3.2.2":
version "3.2.10" version "3.2.10"
resolved "https://registry.yarnpkg.com/@material-ui/pickers/-/pickers-3.2.10.tgz#19df024895876eb0ec7cd239bbaea595f703f0ae" resolved "https://registry.yarnpkg.com/@material-ui/pickers/-/pickers-3.2.10.tgz#19df024895876eb0ec7cd239bbaea595f703f0ae"

@ -2,8 +2,7 @@
"name": "mudbase-server", "name": "mudbase-server",
"version": "1.0.0", "version": "1.0.0",
"scripts": { "scripts": {
"start": "ts-node server.ts", "start": "nodemon server.ts",
"start:watch": "nodemon",
"build": "tsc" "build": "tsc"
}, },
"dependencies": { "dependencies": {

@ -20,8 +20,8 @@ app.post(api.CreateSongEndpoint, (req: any, res: any) => {
const reqObject: api.CreateSongRequest = req.body; const reqObject: api.CreateSongRequest = req.body;
console.log("Request create song: ", reqObject); // TODO: remove console.log("Request create song: ", reqObject); // TODO: remove
// First check that the artist exists. // First check that the artist exists.
models.Song.findAll({ models.Artist.findAll({
where: { artistId: reqObject.artistId } where: { id: reqObject.artistId }
}) })
.then((artist: any[]) => { .then((artist: any[]) => {
if (artist.length != 1) { if (artist.length != 1) {
@ -32,7 +32,7 @@ app.post(api.CreateSongEndpoint, (req: any, res: any) => {
} }
const song = models.Song.create({ const song = models.Song.create({
title: reqObject.title, title: reqObject.title,
artistId: reqObject.artistId ArtistId: reqObject.artistId
}); });
const responseObject: api.CreateSongResponse = { const responseObject: api.CreateSongResponse = {
id: song.id id: song.id
@ -51,12 +51,13 @@ app.get(api.ListSongsEndpoint, (req: any, res: any) => {
include: [models.Artist] include: [models.Artist]
}) })
.then((songs: any[]) => { .then((songs: any[]) => {
console.log(songs);
const response: api.ListSongsResponse = songs.map((song: any) => { const response: api.ListSongsResponse = songs.map((song: any) => {
return { return {
title: song.title, title: song.title,
id: song.id, id: song.id,
artistName: "", // TODO: fetch artist details artistName: song.Artist.name,
artistId: song.artistId, artistId: song.ArtistId,
}; };
}); });
res.send(response); res.send(response);

Loading…
Cancel
Save