Initial state

This commit is contained in:
SteveSandersonMS
2015-11-02 10:30:36 -08:00
parent 0e1fa2e09d
commit f693bd60e3
110 changed files with 6722 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
<h1>Store Manager</h1>
<router-outlet></router-outlet>

View File

@@ -0,0 +1,21 @@
import * as ng from 'angular2/angular2';
import * as router from 'angular2/router';
import { AlbumsList } from '../albums-list/albums-list';
import { AlbumDetails } from '../album-details/album-details';
import { AlbumEdit } from '../album-edit/album-edit';
@ng.Component({
selector: 'admin-home'
})
@router.RouteConfig([
{ path: 'albums', as: 'Albums', component: AlbumsList },
{ path: 'album/details/:albumId', as: 'AlbumDetails', component: AlbumDetails },
{ path: 'album/edit/:albumId', as: 'AlbumEdit', component: AlbumEdit }
])
@ng.View({
templateUrl: './ng-app/components/admin/admin-home/admin-home.html',
directives: [router.ROUTER_DIRECTIVES]
})
export class AdminHome {
}

View File

@@ -0,0 +1,17 @@
<div class="modal fade">
<div class="modal-dialog" *ng-if="album">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">Delete {{ album.Title }}</h4>
</div>
<div class="modal-body">
<p>Really delete <strong>{{ album.Title }}</strong> by <strong>{{ album.Artist.Name }}</strong>?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger">Confirm Delete</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->

View File

@@ -0,0 +1,25 @@
import * as ng from 'angular2/angular2';
import * as models from '../../../models/models';
@ng.Component({
selector: 'album-delete-prompt'
})
@ng.View({
templateUrl: './ng-app/components/admin/album-delete-prompt/album-delete-prompt.html',
directives: [ng.NgIf]
})
export class AlbumDeletePrompt {
private modalElement: any;
public album: models.Album;
constructor(@ng.Inject(ng.ElementRef) elementRef: ng.ElementRef) {
if (typeof window !== 'undefined') {
this.modalElement = (<any>window).jQuery(".modal", elementRef.nativeElement);
}
}
public show(album: models.Album) {
this.album = album;
this.modalElement.modal();
}
}

View File

@@ -0,0 +1,50 @@
<h2>Album <small>Details</small></h2>
<hr />
<form class="form-horizontal" role="form" *ng-if="albumData">
<div class="form-group">
<label class="col-md-2 control-label">Artist</label>
<div class="col-md-10">
<p class="form-control-static">{{ albumData.Artist.Name }}</p>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Genre</label>
<div class="col-md-10">
<p class="form-control-static">{{ albumData.Genre.Name }}</p>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Title</label>
<div class="col-md-10">
<p class="form-control-static">{{ albumData.Title }}</p>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Price</label>
<div class="col-md-10">
<p class="form-control-static">{{ albumData.Price | currency:'USD':true }}</p>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Album Art URL</label>
<div class="col-md-10">
<p class="form-control-static">{{ albumData.AlbumArtUrl }}</p>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">Album Art</label>
<div class="col-md-10">
<img alt="{{ albumData.Title }}" src="{{ albumData.AlbumArtUrl }}">
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<a class="btn btn-primary" [router-link]="['/Admin/AlbumEdit', { albumId: albumData.AlbumId }]">Edit</a>
<button type="button" class="btn btn-danger" (click)="deleteprompt.show(albumData)">Delete</button>
<a class="btn btn-default" [router-link]="['/Admin/Albums']">Back to List</a>
</div>
</div>
</form>
<album-delete-prompt #deleteprompt></album-delete-prompt>

View File

@@ -0,0 +1,22 @@
import * as ng from 'angular2/angular2';
import * as router from 'angular2/router';
import * as models from '../../../models/models';
import { Http, HTTP_BINDINGS } from 'angular2/http';
import { AlbumDeletePrompt } from '../album-delete-prompt/album-delete-prompt';
@ng.Component({
selector: 'album-details'
})
@ng.View({
templateUrl: './ng-app/components/admin/album-details/album-details.html',
directives: [router.ROUTER_DIRECTIVES, ng.NgIf, AlbumDeletePrompt]
})
export class AlbumDetails {
public albumData: models.Album;
constructor(http: Http, routeParam: router.RouteParams) {
http.get('/api/albums/' + routeParam.params['albumId']).subscribe(result => {
this.albumData = result.json();
});
}
}

View File

@@ -0,0 +1,45 @@
<h2>Album <small>Edit</small></h2>
<hr />
<form class="form-horizontal" [ng-form-model]="form" (ng-submit)="onSubmitModelBased()">
<form-field label="Artist" [validate]="form.controls.artist">
<select class="form-control" ng-control="artist">
<option value="">-- choose Artist --</option>
<option *ng-for="#artist of artists" [value]="artist.ArtistId">{{ artist.Name }}</option>
</select>
</form-field>
<form-field label="Genre" [validate]="form.controls.genre">
<select class="form-control" ng-control="genre">
<option value="">-- choose Genre --</option>
<option *ng-for="#genre of genres" [value]="genre.GenreId">{{ genre.Name }}</option>
</select>
</form-field>
<form-field label="Title" [validate]="form.controls.title">
<input class="form-control" type="text" ng-control="title">
</form-field>
<form-field label="Price" [validate]="form.controls.price">
<div class="input-group">
<span class="input-group-addon">$</span>
<input class="form-control" type="text" ng-control="price">
</div>
</form-field>
<form-field label="Album Art URL" [validate]="form.controls.albumArtUrl">
<input class="form-control" ng-control="albumArtUrl">
</form-field>
<form-field label="Album Art">
<img src="{{ form.controls.albumArtUrl.value }}">
</form-field>
<form-field>
<button type="submit" class="btn btn-primary">Submit</button>
<button type="button" class="btn btn-danger" (click)="deleteprompt.show(originalAlbum)">Delete</button>
<a class="btn btn-default" [router-link]="['/Admin/Albums']">Back to List</a>
</form-field>
</form>
<album-delete-prompt #deleteprompt></album-delete-prompt>

View File

@@ -0,0 +1,82 @@
import * as ng from 'angular2/angular2';
import * as router from 'angular2/router';
import * as models from '../../../models/models';
import { Http, HTTP_BINDINGS, Headers } from 'angular2/http';
import { AlbumDeletePrompt } from '../album-delete-prompt/album-delete-prompt';
import { FormField } from '../form-field/form-field';
@ng.Component({
selector: 'album-edit'
})
@ng.View({
templateUrl: './ng-app/components/admin/album-edit/album-edit.html',
directives: [router.ROUTER_DIRECTIVES, ng.NgIf, ng.NgFor, AlbumDeletePrompt, FormField, ng.FORM_DIRECTIVES]
})
export class AlbumEdit {
public form: ng.ControlGroup;
public artists: models.Artist[];
public genres: models.Genre[];
public originalAlbum: models.Album;
private _http: Http;
constructor(fb: ng.FormBuilder, http: Http, routeParam: router.RouteParams) {
this._http = http;
http.get('/api/albums/' + routeParam.params['albumId']).subscribe(result => {
var json = result.json();
this.originalAlbum = json;
(<ng.Control>this.form.controls['title']).updateValue(json.Title);
(<ng.Control>this.form.controls['price']).updateValue(json.Price);
(<ng.Control>this.form.controls['artist']).updateValue(json.ArtistId);
(<ng.Control>this.form.controls['genre']).updateValue(json.GenreId);
(<ng.Control>this.form.controls['albumArtUrl']).updateValue(json.AlbumArtUrl);
});
http.get('/api/artists/lookup').subscribe(result => {
this.artists = result.json();
});
http.get('/api/genres/genre-lookup').subscribe(result => {
this.genres = result.json();
});
this.form = fb.group(<any>{
artist: fb.control('', ng.Validators.required),
genre: fb.control('', ng.Validators.required),
title: fb.control('', ng.Validators.required),
price: fb.control('', ng.Validators.compose([ng.Validators.required, AlbumEdit._validatePrice])),
albumArtUrl: fb.control('', ng.Validators.required)
});
}
public onSubmitModelBased() {
// Force all fields to show any validation errors even if they haven't been touched
Object.keys(this.form.controls).forEach(controlName => {
this.form.controls[controlName].markAsTouched();
});
if (this.form.valid) {
var controls = this.form.controls;
var albumId = this.originalAlbum.AlbumId;
(<any>window).fetch(`/api/albums/${ albumId }/update`, {
method: 'put',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
AlbumArtUrl: controls['albumArtUrl'].value,
AlbumId: albumId,
ArtistId: controls['artist'].value,
GenreId: controls['genre'].value,
Price: controls['price'].value,
Title: controls['title'].value
})
}).then(response => {
console.log(response);
});
}
}
private static _validatePrice(control: ng.Control): { [key: string]: boolean } {
return /^\d+\.\d+$/.test(control.value) ? null : { price: true };
}
}

View File

@@ -0,0 +1,44 @@
<h2>Albums</h2>
<album-delete-prompt #deleteprompt></album-delete-prompt>
<table class="table">
<thead>
<tr>
<th><a>Genre</a></th>
<th><a>Artist</a></th>
<th><a (click)="sortBy('Title')">Title</a></th>
<th><a (click)="sortBy('Price')">Price</a></th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ng-for="#row of rows">
<td>{{ row.Genre.Name }}</td>
<td>{{ row.Artist.Name }}</td>
<td>{{ row.Title }}</td>
<td>{{ row.Price | currency:'USD':true }}</td>
<td>
<div class="btn-group btn-group-xs">
<a class="btn btn-default" [router-link]="['/Admin/AlbumDetails', { albumId: row.AlbumId }]">Details</a>
<a class="btn btn-default" [router-link]="['/Admin/AlbumEdit', { albumId: row.AlbumId }]">Edit</a>
<a class="btn btn-default" (click)="deleteprompt.show(row)">Delete</a>
</div>
</td>
</tr>
</tbody>
</table>
<div class="btn-group">
<button class="btn btn-default" [disabled]="!canGoBack" (click)="goToPage(1)">First</button>
<button class="btn btn-default" [disabled]="!canGoBack" (click)="goToPage(pageIndex - 1)">Previous</button>
<button class="btn" *ng-for="#page of pageLinks"
[ng-class]="{ 'btn-info': page.isCurrent, 'btn-default': !page.isCurrent }"
(click)="goToPage(page.index)">
{{ page.text }}
</button>
<button class="btn btn-default" [disabled]="!canGoForward" (click)="goToPage(pageIndex + 1)">Next</button>
<button class="btn btn-default" [disabled]="!canGoForward" (click)="goToLast()">Last</button>
</div>
<p>{{ totalCount }} total albums</p>

View File

@@ -0,0 +1,70 @@
import * as ng from 'angular2/angular2';
import * as router from 'angular2/router';
import { Http, HTTP_BINDINGS } from 'angular2/http';
import * as models from '../../../models/models';
import { AlbumDeletePrompt } from '../album-delete-prompt/album-delete-prompt';
@ng.Component({
selector: 'albums-list'
})
@ng.View({
templateUrl: './ng-app/components/admin/albums-list/albums-list.html',
directives: [ng.NgFor, ng.NgClass, router.ROUTER_DIRECTIVES, AlbumDeletePrompt]
})
export class AlbumsList {
public rows: models.Album[];
public canGoBack: boolean;
public canGoForward: boolean;
public pageLinks: any[];
public totalCount: number;
public get pageIndex() {
return this._pageIndex;
}
private _http: Http;
private _pageIndex = 1;
private _sortBy = "Title";
private _sortByDesc = false;
constructor(http: Http) {
this._http = http;
this.refreshData();
}
public sortBy(col: string) {
this._sortByDesc = col === this._sortBy ? !this._sortByDesc : false;
this._sortBy = col;
this.refreshData();
}
public goToPage(pageIndex: number) {
this._pageIndex = pageIndex;
this.refreshData();
}
public goToLast() {
this.goToPage(this.pageLinks[this.pageLinks.length - 1].index);
}
refreshData() {
var sortBy = this._sortBy + (this._sortByDesc ? ' DESC' : '');
this._http.get(`/api/albums?page=${ this._pageIndex }&pageSize=50&sortBy=${ sortBy }`).subscribe(result => {
var json = result.json();
this.rows = json.Data;
var numPages = Math.ceil(json.TotalCount / json.PageSize);
this.pageLinks = [];
for (var i = 1; i <= numPages; i++) {
this.pageLinks.push({
index: i,
text: i.toString(),
isCurrent: i === json.Page
});
}
this.canGoBack = this.pageLinks.length && !this.pageLinks[0].isCurrent;
this.canGoForward = this.pageLinks.length && !this.pageLinks[this.pageLinks.length - 1].isCurrent;
this.totalCount = json.TotalCount;
});
}
}

View File

@@ -0,0 +1,9 @@
<div class="form-group" [class.has-error]="errorMessages.length">
<label class="col-md-2 control-label">{{ label }}</label>
<div class="col-md-5">
<ng-content></ng-content>
<div class="alert alert-danger" role="alert" *ng-if="errorMessages.length">
<p *ng-for="#message of errorMessages">{{ message }}</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,20 @@
import * as ng from 'angular2/angular2';
@ng.Component({
selector: 'form-field',
properties: ['label', 'validate']
})
@ng.View({
templateUrl: './ng-app/components/admin/form-field/form-field.html',
directives: [ng.NgIf, ng.NgFor]
})
export class FormField {
private validate: ng.AbstractControl;
public get errorMessages() {
var errors = (this.validate && this.validate.touched && this.validate.errors) || {};
return Object.keys(errors).map(key => {
return 'Error: ' + key;
});
}
}