diff --git a/music-app/app/adapters/album.js b/music-app/app/adapters/album.js
new file mode 100644
index 0000000..a68370b
--- /dev/null
+++ b/music-app/app/adapters/album.js
@@ -0,0 +1,5 @@
+import DS from 'ember-data';
+
+export default DS.JSONAPIAdapter.extend({
+
+});
\ No newline at end of file
diff --git a/music-app/app/adapters/application.js b/music-app/app/adapters/application.js
new file mode 100644
index 0000000..3128ba5
--- /dev/null
+++ b/music-app/app/adapters/application.js
@@ -0,0 +1,5 @@
+import DS from 'ember-data';
+
+export default DS.JSONAPIAdapter.extend({
+
+});
\ No newline at end of file
diff --git a/music-app/app/components/album-detail/component.js b/music-app/app/components/album-detail/component.js
new file mode 100644
index 0000000..8475a5b
--- /dev/null
+++ b/music-app/app/components/album-detail/component.js
@@ -0,0 +1,47 @@
+import Component from '@ember/component';
+import { inject as service } from '@ember/service';
+
+export default Component.extend({
+
+ album: null,
+
+ showForm: false,
+
+ newSong: null,
+
+ genres: null,
+
+ artists: null,
+
+ store: service(),
+
+ init() {
+ this._super(...arguments);
+
+ this.set('genres', this.store.findAll('genre'));
+ this.set('artists', this.store.findAll('artist'));
+ this.set('newSong', {
+ title: undefined
+ });
+ },
+
+ actions: {
+ toggleForm() {
+ this.toggleProperty('showForm');
+ },
+
+ addSong() {
+ const selectGenreId = document.getElementById('selectGenre');
+ const selectGenre = this.get('genres').findBy('id', selectGenreId.options[selectGenreId.selectedIndex].value);
+ let song = this.store.createRecord('song', {
+ title: this.get('newSong.title'),
+ album: this.get('album'),
+ genre: selectGenre
+ });
+
+ song.save();
+ this.set(this.get('newSong.title', undefined));
+ this.toggleProperty('showForm');
+ }
+ }
+});
diff --git a/music-app/app/components/album-detail/template.hbs b/music-app/app/components/album-detail/template.hbs
new file mode 100644
index 0000000..c8a2d53
--- /dev/null
+++ b/music-app/app/components/album-detail/template.hbs
@@ -0,0 +1,40 @@
+{{#link-to "application"}}Back{{/link-to}}
+Songs for {{album.name}} album:
+
+
+
+
+ | Title |
+ Genre |
+ Artists |
+
+
+
+ {{#each album.songs as |song|}}
+ {{song-detail song=song genres=genres}}
+ {{/each}}
+
+
+
+
+
+ {{#if showForm}}
+
+ Name: {{input value=newSong.title}}
+
+ Genre:
+
+
+ Artists:
+
+ {{#each artists as |artist|}}
+ {{input type="checkbox" checked=foo}}
+
+ {{/each}}
+
+
+ {{/if}}
\ No newline at end of file
diff --git a/music-app/app/components/music-list.js b/music-app/app/components/music-list.js
deleted file mode 100644
index bb93d73..0000000
--- a/music-app/app/components/music-list.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import Component from '@ember/component';
-
-export default Component.extend({
-});
diff --git a/music-app/app/components/music-list/component.js b/music-app/app/components/music-list/component.js
new file mode 100644
index 0000000..d34f9cd
--- /dev/null
+++ b/music-app/app/components/music-list/component.js
@@ -0,0 +1,21 @@
+import Component from '@ember/component';
+
+export default Component.extend({
+
+ albums: null,
+
+ albumsToShow: null,
+
+ textToSearch: '',
+
+ init() {
+ this._super(...arguments);
+ this.set('albumsToShow', this.get('albums'));
+ },
+
+ actions: {
+ searchByText(textToSearch) {
+ this.set('albumsToShow', this.get('albums').filter((album) => album.name.toLowerCase().includes(textToSearch.toLowerCase())));
+ }
+ }
+});
diff --git a/music-app/app/components/music-list/template.hbs b/music-app/app/components/music-list/template.hbs
new file mode 100644
index 0000000..6739545
--- /dev/null
+++ b/music-app/app/components/music-list/template.hbs
@@ -0,0 +1,23 @@
+
+
+
+ {{input value=textToSearch}}
+
+
+
+ {{#each albumsToShow as |album|}}
+ -
+ {{#link-to "albums" album.id}}
+
+
+

+
+
+
{{album.name}}
+
+
+ {{/link-to}}
+
+ {{/each}}
+
+
\ No newline at end of file
diff --git a/music-app/app/components/song-detail/component.js b/music-app/app/components/song-detail/component.js
new file mode 100644
index 0000000..5b4caa3
--- /dev/null
+++ b/music-app/app/components/song-detail/component.js
@@ -0,0 +1,59 @@
+import Component from '@ember/component';
+import { inject as service } from '@ember/service';
+
+export default Component.extend({
+
+ tagName: 'tr',
+
+ song: null,
+
+ songToEdit: null,
+
+ showForm: false,
+
+ store: service(),
+
+ artistNames: undefined,
+
+ init() {
+ this._super(...arguments);
+ this.set('artistNames', this.getArtistsNames(this.get('song.artists')));
+ this.set('songToEdit', {
+ title: undefined
+ });
+ },
+
+ getArtistsNames(artists) {
+ let names = '';
+
+ if (artists && artists.length) {
+ artists.forEach((artist) => {
+ names = `${names}, ${artist.name}`;
+ });
+ names = names.slice(0, -2);
+ }
+
+ return names;
+ },
+
+ actions: {
+ toggleForm() {
+ this.toggleProperty('showForm');
+ },
+
+ updateSong() {
+ const titleToUpdate = this.get('songToEdit.title');
+ const selectGenreId = document.getElementById('selectGenre');
+ const selectGenre = this.get('genres').findBy('id', selectGenreId.options[selectGenreId.selectedIndex].value);
+
+ this.store.findRecord('song', this.get('song.id')).then(function(songToUpdate) {
+ songToUpdate.title = titleToUpdate;
+ songToUpdate.genre = selectGenre;
+
+ songToUpdate.save();
+ });
+ this.set('songToEdit.title', null);
+ this.toggleProperty('showForm');
+ }
+ }
+});
diff --git a/music-app/app/components/song-detail/template.hbs b/music-app/app/components/song-detail/template.hbs
new file mode 100644
index 0000000..19913ef
--- /dev/null
+++ b/music-app/app/components/song-detail/template.hbs
@@ -0,0 +1,20 @@
+{{#if showForm}}
+ {{input value=songToEdit.title placeholder=song.title}} |
+
+
+ |
+ //TODO |
+
+
+
+ |
+{{else}}
+ {{song.title}} |
+ {{song.genre.name}} |
+ {{artistNames}} |
+ |
+{{/if}}
\ No newline at end of file
diff --git a/music-app/app/models/album.js b/music-app/app/models/album.js
index 1f71205..6f1526b 100644
--- a/music-app/app/models/album.js
+++ b/music-app/app/models/album.js
@@ -1,5 +1,10 @@
-import Model from '@ember-data/model';
+import Model, { attr, hasMany } from '@ember-data/model';
export default Model.extend({
+ name: attr('string'),
+
+ cover: attr('string'),
+
+ songs: hasMany('song')
});
diff --git a/music-app/app/models/artist.js b/music-app/app/models/artist.js
index 1f71205..139205b 100644
--- a/music-app/app/models/artist.js
+++ b/music-app/app/models/artist.js
@@ -1,5 +1,7 @@
-import Model from '@ember-data/model';
+import Model, { attr } from '@ember-data/model';
export default Model.extend({
+ name: attr()
+
});
diff --git a/music-app/app/models/genre.js b/music-app/app/models/genre.js
index 1f71205..139205b 100644
--- a/music-app/app/models/genre.js
+++ b/music-app/app/models/genre.js
@@ -1,5 +1,7 @@
-import Model from '@ember-data/model';
+import Model, { attr } from '@ember-data/model';
export default Model.extend({
+ name: attr()
+
});
diff --git a/music-app/app/models/song.js b/music-app/app/models/song.js
index 1f71205..bc59513 100644
--- a/music-app/app/models/song.js
+++ b/music-app/app/models/song.js
@@ -1,5 +1,12 @@
-import Model from '@ember-data/model';
+import Model, { attr, belongsTo, hasMany } from '@ember-data/model';
export default Model.extend({
+ title: attr(),
+
+ album: belongsTo('album'),
+
+ genre: belongsTo('genre', { inverse: null }),
+
+ artists: hasMany('artist', { inverse: null })
});
diff --git a/music-app/app/router.js b/music-app/app/router.js
index 224ca42..0d4ad59 100644
--- a/music-app/app/router.js
+++ b/music-app/app/router.js
@@ -7,4 +7,5 @@ export default class Router extends EmberRouter {
}
Router.map(function() {
+ this.route('albums', { path: '/albums/:id' });
});
diff --git a/music-app/app/routes/albums.js b/music-app/app/routes/albums.js
new file mode 100644
index 0000000..e6c52b0
--- /dev/null
+++ b/music-app/app/routes/albums.js
@@ -0,0 +1,7 @@
+import Route from '@ember/routing/route';
+
+export default Route.extend({
+ model(transition) {
+ return this.store.findRecord('album', transition.id);
+ }
+})
\ No newline at end of file
diff --git a/music-app/app/routes/application.js b/music-app/app/routes/application.js
new file mode 100644
index 0000000..88ffc5b
--- /dev/null
+++ b/music-app/app/routes/application.js
@@ -0,0 +1,7 @@
+import Route from '@ember/routing/route';
+
+export default Route.extend({
+ model() {
+ return this.store.findAll('album');
+ }
+})
\ No newline at end of file
diff --git a/music-app/app/serializers/application.js b/music-app/app/serializers/application.js
new file mode 100644
index 0000000..5fc2c6c
--- /dev/null
+++ b/music-app/app/serializers/application.js
@@ -0,0 +1,4 @@
+import JSONAPISerializer from '@ember-data/serializer/json-api';
+
+export default class ApplicationSerializer extends JSONAPISerializer {
+}
\ No newline at end of file
diff --git a/music-app/app/styles/app.scss b/music-app/app/styles/app.scss
index e69de29..e80f7ca 100644
--- a/music-app/app/styles/app.scss
+++ b/music-app/app/styles/app.scss
@@ -0,0 +1,26 @@
+.music-list-filter {
+ text-align: center;
+}
+
+.album-list {
+ display: grid;
+ margin: auto;
+ width: 400px;
+ grid-template-columns: 150px 150px;
+}
+
+.album {
+ margin: 10px;
+ background-color: #fff;
+ box-shadow: 1px 0.5px 5px 1px hsla(0,0%,39.2%,.5);
+ border: 1px solid #bfc0bf;
+ list-style-type: none;
+}
+
+.show {
+ display: block;
+}
+
+.hide {
+ display: none;
+}
\ No newline at end of file
diff --git a/music-app/app/templates/albums.hbs b/music-app/app/templates/albums.hbs
new file mode 100644
index 0000000..87cbbdc
--- /dev/null
+++ b/music-app/app/templates/albums.hbs
@@ -0,0 +1 @@
+{{album-detail album=model}}
\ No newline at end of file
diff --git a/music-app/app/templates/application.hbs b/music-app/app/templates/application.hbs
index 30941c3..b53239e 100644
--- a/music-app/app/templates/application.hbs
+++ b/music-app/app/templates/application.hbs
@@ -1,3 +1,3 @@
-
-
+{{music-list albums=model}}
+
{{outlet}}
\ No newline at end of file
diff --git a/music-app/app/templates/components/.gitkeep b/music-app/app/templates/components/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/music-app/app/templates/components/music-list.hbs b/music-app/app/templates/components/music-list.hbs
deleted file mode 100644
index 889d9ee..0000000
--- a/music-app/app/templates/components/music-list.hbs
+++ /dev/null
@@ -1 +0,0 @@
-{{yield}}
diff --git a/music-app/mirage/config.js b/music-app/mirage/config.js
new file mode 100644
index 0000000..08982c0
--- /dev/null
+++ b/music-app/mirage/config.js
@@ -0,0 +1,39 @@
+export default function() {
+
+ this.namespace = '';
+
+ this.get('/albums');
+ this.get('/albums/:id');
+ this.get('/songs');
+ this.get('/songs/:id');
+ this.post('/songs');
+ this.patch('/songs/:id');
+ this.get('/genres');
+ this.get('/genres/:id');
+ this.get('/artists');
+ this.get('/artists/:id');
+
+ // These comments are here to help you get started. Feel free to delete them.
+
+ /*
+ Config (with defaults).
+
+ Note: these only affect routes defined *after* them!
+ */
+
+ // this.urlPrefix = ''; // make this `http://localhost:8080`, for example, if your API is on a different server
+ // this.namespace = ''; // make this `/api`, for example, if your API is namespaced
+ // this.timing = 400; // delay for each request, automatically set to 0 during testing
+
+ /*
+ Shorthand cheatsheet:
+
+ this.get('/posts');
+ this.post('/posts');
+ this.get('/posts/:id');
+ this.put('/posts/:id'); // or this.patch
+ this.del('/posts/:id');
+
+ https://www.ember-cli-mirage.com/docs/route-handlers/shorthands
+ */
+}
diff --git a/music-app/mirage/factories/album.js b/music-app/mirage/factories/album.js
new file mode 100644
index 0000000..1402ce0
--- /dev/null
+++ b/music-app/mirage/factories/album.js
@@ -0,0 +1,12 @@
+import { Factory } from 'ember-cli-mirage';
+
+export default Factory.extend({
+
+ name(i) {
+ return `Album title ${i+1}`;
+ },
+
+ afterCreate(album, server) {
+ album.update({ songs: server.createList('song', Math.floor(Math.random() * 10) + 3) });
+ }
+});
diff --git a/music-app/mirage/factories/artist.js b/music-app/mirage/factories/artist.js
new file mode 100644
index 0000000..2b21be6
--- /dev/null
+++ b/music-app/mirage/factories/artist.js
@@ -0,0 +1,8 @@
+import { Factory } from 'ember-cli-mirage';
+
+export default Factory.extend({
+
+ name(i) {
+ return `Artist name ${i+1}`;
+ }
+});
diff --git a/music-app/mirage/factories/genre.js b/music-app/mirage/factories/genre.js
new file mode 100644
index 0000000..3c8ece9
--- /dev/null
+++ b/music-app/mirage/factories/genre.js
@@ -0,0 +1,8 @@
+import { Factory } from 'ember-cli-mirage';
+
+export default Factory.extend({
+
+ name(i) {
+ return `Genre name ${i+1}`;
+ }
+});
diff --git a/music-app/mirage/factories/song.js b/music-app/mirage/factories/song.js
new file mode 100644
index 0000000..b125fc2
--- /dev/null
+++ b/music-app/mirage/factories/song.js
@@ -0,0 +1,19 @@
+import { Factory } from 'ember-cli-mirage';
+
+export default Factory.extend({
+
+ title(i) {
+ return `Song title ${i+1}`;
+ },
+
+ afterCreate(song, server) {
+ const genres = server.schema.genres.all();
+ const randomGenre = genres.models[Math.floor(Math.random() * genres.length)];
+ const artists = server.schema.artists.all();
+ const selectedArtists = artists.filter(() =>
+ Math.floor(Math.random() * 2) % 2 === 0
+ );
+
+ song.update({ genre: randomGenre, artists: selectedArtists });
+ }
+});
diff --git a/music-app/mirage/models/album.js b/music-app/mirage/models/album.js
new file mode 100644
index 0000000..54058e9
--- /dev/null
+++ b/music-app/mirage/models/album.js
@@ -0,0 +1,5 @@
+import { Model, hasMany } from 'ember-cli-mirage';
+
+export default Model.extend({
+ songs: hasMany('song')
+});
diff --git a/music-app/mirage/models/artist.js b/music-app/mirage/models/artist.js
new file mode 100644
index 0000000..1486a72
--- /dev/null
+++ b/music-app/mirage/models/artist.js
@@ -0,0 +1,4 @@
+import { Model } from 'ember-cli-mirage';
+
+export default Model.extend({
+});
diff --git a/music-app/mirage/models/genre.js b/music-app/mirage/models/genre.js
new file mode 100644
index 0000000..1486a72
--- /dev/null
+++ b/music-app/mirage/models/genre.js
@@ -0,0 +1,4 @@
+import { Model } from 'ember-cli-mirage';
+
+export default Model.extend({
+});
diff --git a/music-app/mirage/models/song.js b/music-app/mirage/models/song.js
new file mode 100644
index 0000000..3fa0358
--- /dev/null
+++ b/music-app/mirage/models/song.js
@@ -0,0 +1,7 @@
+import { Model, belongsTo, hasMany } from 'ember-cli-mirage';
+
+export default Model.extend({
+ genre: belongsTo(),
+
+ artists: hasMany()
+});
diff --git a/music-app/mirage/scenarios/default.js b/music-app/mirage/scenarios/default.js
new file mode 100644
index 0000000..7eb0d27
--- /dev/null
+++ b/music-app/mirage/scenarios/default.js
@@ -0,0 +1,5 @@
+export default function(server) {
+ server.createList('genre', 7);
+ server.createList('artist', 15);
+ server.createList('album', 10);
+}
diff --git a/music-app/mirage/serializers/application.js b/music-app/mirage/serializers/application.js
new file mode 100644
index 0000000..619bf2e
--- /dev/null
+++ b/music-app/mirage/serializers/application.js
@@ -0,0 +1,5 @@
+import { JSONAPISerializer } from 'ember-cli-mirage';
+
+export default JSONAPISerializer.extend({
+ alwaysIncludeLinkageData: true
+});
diff --git a/music-app/package.json b/music-app/package.json
index 2491ca3..1eb0113 100644
--- a/music-app/package.json
+++ b/music-app/package.json
@@ -29,6 +29,7 @@
"ember-cli-eslint": "^5.1.0",
"ember-cli-htmlbars": "^4.0.5",
"ember-cli-inject-live-reload": "^2.0.1",
+ "ember-cli-mirage": "^1.1.6",
"ember-cli-sass": "^10.0.1",
"ember-cli-sri": "^2.1.1",
"ember-cli-template-lint": "^1.0.0-beta.3",