mirror of
https://github.com/samnyan/aqua-viewer.git
synced 2026-03-21 17:24:53 -05:00
Init
This commit is contained in:
commit
6a4ac09721
13
.editorconfig
Normal file
13
.editorconfig
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
46
.gitignore
vendored
Normal file
46
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
# Only exists if Bazel was run
|
||||
/bazel-out
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# profiling files
|
||||
chrome-profiler-events*.json
|
||||
speed-measure-plugin*.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
27
README.md
Normal file
27
README.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# AquaViewer
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.3.20.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
||||
132
angular.json
Normal file
132
angular.json
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"aqua-viewer": {
|
||||
"projectType": "application",
|
||||
"schematics": {},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/aqua-viewer",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"aot": false,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css",
|
||||
"src/styles.css"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "aqua-viewer:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "aqua-viewer:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "aqua-viewer:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css",
|
||||
"src/styles.css"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.spec.json",
|
||||
"e2e/tsconfig.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "e2e/protractor.conf.js",
|
||||
"devServerTarget": "aqua-viewer:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "aqua-viewer:serve:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deploy": {
|
||||
"builder": "angular-cli-ghpages:deploy",
|
||||
"options": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "aqua-viewer"
|
||||
}
|
||||
12
browserslist
Normal file
12
browserslist
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
> 0.5%
|
||||
last 2 versions
|
||||
Firefox ESR
|
||||
not dead
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
||||
33
e2e/protractor.conf.js
Normal file
33
e2e/protractor.conf.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const {SpecReporter} = require('jasmine-spec-reporter');
|
||||
|
||||
/**
|
||||
* @type { import("protractor").Config }
|
||||
*/
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function () {
|
||||
}
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.json')
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({spec: {displayStacktrace: true}}));
|
||||
}
|
||||
};
|
||||
23
e2e/src/app.e2e-spec.ts
Normal file
23
e2e/src/app.e2e-spec.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import {AppPage} from './app.po';
|
||||
import {browser, logging} from 'protractor';
|
||||
|
||||
describe('workspace-project App', () => {
|
||||
let page: AppPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AppPage();
|
||||
});
|
||||
|
||||
it('should display welcome message', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getTitleText()).toEqual('aqua-viewer app is running!');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Assert that there are no errors emitted from the browser
|
||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||
expect(logs).not.toContain(jasmine.objectContaining({
|
||||
level: logging.Level.SEVERE,
|
||||
} as logging.Entry));
|
||||
});
|
||||
});
|
||||
11
e2e/src/app.po.ts
Normal file
11
e2e/src/app.po.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import {browser, by, element} from 'protractor';
|
||||
|
||||
export class AppPage {
|
||||
navigateTo() {
|
||||
return browser.get(browser.baseUrl) as Promise<any>;
|
||||
}
|
||||
|
||||
getTitleText() {
|
||||
return element(by.css('app-root .content span')).getText() as Promise<string>;
|
||||
}
|
||||
}
|
||||
13
e2e/tsconfig.json
Normal file
13
e2e/tsconfig.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
||||
32
karma.conf.js
Normal file
32
karma.conf.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/aqua-viewer'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
13204
package-lock.json
generated
Normal file
13204
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
52
package.json
Normal file
52
package.json
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"name": "aqua-viewer",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~8.2.14",
|
||||
"@angular/cdk": "~8.2.3",
|
||||
"@angular/common": "~8.2.14",
|
||||
"@angular/compiler": "~8.2.14",
|
||||
"@angular/core": "~8.2.14",
|
||||
"@angular/flex-layout": "^8.0.0-beta.27",
|
||||
"@angular/forms": "~8.2.14",
|
||||
"@angular/material": "^8.2.3",
|
||||
"@angular/platform-browser": "~8.2.14",
|
||||
"@angular/platform-browser-dynamic": "~8.2.14",
|
||||
"@angular/router": "~8.2.14",
|
||||
"angular-cli-ghpages": "^0.6.2",
|
||||
"hammerjs": "^2.0.8",
|
||||
"rxjs": "~6.4.0",
|
||||
"tslib": "^1.10.0",
|
||||
"zone.js": "~0.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.803.20",
|
||||
"@angular/cli": "~8.3.20",
|
||||
"@angular/compiler-cli": "~8.2.14",
|
||||
"@angular/language-service": "~8.2.14",
|
||||
"@types/node": "~8.9.4",
|
||||
"@types/jasmine": "~3.3.8",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"codelyzer": "^5.0.0",
|
||||
"jasmine-core": "~3.4.0",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~4.1.0",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||
"karma-jasmine": "~2.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.0",
|
||||
"protractor": "~5.4.0",
|
||||
"ts-node": "~7.0.0",
|
||||
"tslint": "~5.15.0",
|
||||
"typescript": "~3.5.3"
|
||||
}
|
||||
}
|
||||
39
src/app/api.service.ts
Normal file
39
src/app/api.service.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import {HttpClient, HttpParams} from '@angular/common/http';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {AuthenticationService} from './auth/authentication.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ApiService {
|
||||
|
||||
constructor(private http: HttpClient,
|
||||
private authenticationService: AuthenticationService) {
|
||||
}
|
||||
|
||||
get(path: string, params?: HttpParams) {
|
||||
return this.http.get<any>(this.getHost() + path, {params});
|
||||
}
|
||||
|
||||
post(path: string, data?: object, params?: HttpParams) {
|
||||
return this.http.post<any>(this.getHost() + path, data, {params});
|
||||
}
|
||||
|
||||
put(path: string, data?: object, params?: HttpParams) {
|
||||
return this.http.put<any>(this.getHost() + path, data, {params});
|
||||
}
|
||||
|
||||
delete(path: string, params?: HttpParams) {
|
||||
return this.http.delete<any>(this.getHost() + path, {params});
|
||||
}
|
||||
|
||||
getHost(): string {
|
||||
return this.authenticationService.currentUserValue.apiServer + '/';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class Resp {
|
||||
status: string;
|
||||
data: object;
|
||||
}
|
||||
25
src/app/app-routing.module.ts
Normal file
25
src/app/app-routing.module.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {LoginComponent} from './login/login.component';
|
||||
import {DashboardComponent} from './dashboard/dashboard.component';
|
||||
import {AuthGuardService} from './auth/auth-guard.service';
|
||||
|
||||
const routes: Routes = [
|
||||
{path: '', redirectTo: '/login', pathMatch: 'full'},
|
||||
{path: 'login', component: LoginComponent},
|
||||
{path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuardService]},
|
||||
{path: 'diva', loadChildren: () => import('./sega/diva/diva.module').then(mod => mod.DivaModule), canLoad: [AuthGuardService]},
|
||||
{
|
||||
path: 'amazon',
|
||||
loadChildren: () => import('./sega/chunithm/amazon/amazon.module').then(mod => mod.AmazonModule),
|
||||
canLoad: [AuthGuardService]
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
|
||||
export class AppRoutingModule {
|
||||
}
|
||||
60
src/app/app.component.css
Normal file
60
src/app/app.component.css
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.is-mobile .toolbar {
|
||||
position: fixed;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.sidenav {
|
||||
flex: 1;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
mat-sidenav {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
mat-toolbar {
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.content {
|
||||
background-origin: content-box;
|
||||
height: auto;
|
||||
overflow-y: visible;
|
||||
width: 92%;
|
||||
max-width: 1280px;
|
||||
box-sizing: border-box;
|
||||
padding: 30px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.bg {
|
||||
transition-duration: .218s;
|
||||
transition-property: background;
|
||||
transition-timing-function: ease-in;
|
||||
}
|
||||
|
||||
.dark {
|
||||
background-color: #202124;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 599px) {
|
||||
.content {
|
||||
background-origin: content-box;
|
||||
height: auto;
|
||||
overflow-y: visible;
|
||||
width: 96%;
|
||||
box-sizing: border-box;
|
||||
padding: 15px;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
39
src/app/app.component.html
Normal file
39
src/app/app.component.html
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<div class="container">
|
||||
<mat-toolbar class="toolbar mat-elevation-z6" color="primary">
|
||||
<button (click)="drawer.toggle()" mat-icon-button>
|
||||
<mat-icon aria-label="Side nav toggle icon">menu</mat-icon>
|
||||
</button>
|
||||
<span>Aqua Viewer</span>
|
||||
<span class="spacer-util"></span>
|
||||
|
||||
<button *ngIf="user" [matMenuTriggerFor]="userMenu" mat-icon-button>
|
||||
<mat-icon aria-hidden="false" aria-label="User menu">face</mat-icon>
|
||||
</button>
|
||||
<mat-menu #userMenu="matMenu">
|
||||
<button (click)="logout()" mat-menu-item>Logout</button>
|
||||
</mat-menu>
|
||||
|
||||
</mat-toolbar>
|
||||
|
||||
<mat-sidenav-container class="sidenav">
|
||||
<mat-sidenav #drawer [fixedInViewport]="true" [mode]="mobileQuery.matches ? 'over' : 'side'"
|
||||
fixedTopGap="56">
|
||||
<mat-nav-list *ngIf="user">
|
||||
<a mat-list-item routerLink="/dashboard">Dashboard</a>
|
||||
<mat-toolbar>Project DIVA AFT</mat-toolbar>
|
||||
<a *ngFor="let item of divaMenus" mat-list-item routerLink="/{{item.url}}">{{item.name}}</a>
|
||||
<mat-divider></mat-divider>
|
||||
<mat-toolbar>CHUNITHM Amazon</mat-toolbar>
|
||||
<a *ngFor="let item of amazonMenus" mat-list-item routerLink="/{{item.url}}">{{item.name}}</a>
|
||||
</mat-nav-list>
|
||||
</mat-sidenav>
|
||||
|
||||
<mat-sidenav-content>
|
||||
<div class="content">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
<app-message></app-message>
|
||||
|
||||
</mat-sidenav-content>
|
||||
</mat-sidenav-container>
|
||||
</div>
|
||||
31
src/app/app.component.spec.ts
Normal file
31
src/app/app.component.spec.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import {async, TestBed} from '@angular/core/testing';
|
||||
import {AppComponent} from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'aqua-viewer'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app.title).toEqual('aqua-viewer');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.debugElement.nativeElement;
|
||||
expect(compiled.querySelector('.content span').textContent).toContain('aqua-viewer app is running!');
|
||||
});
|
||||
});
|
||||
97
src/app/app.component.ts
Normal file
97
src/app/app.component.ts
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import {ChangeDetectorRef, Component, OnChanges, OnDestroy} from '@angular/core';
|
||||
import {AuthenticationService, User} from './auth/authentication.service';
|
||||
import {MediaMatcher} from '@angular/cdk/layout';
|
||||
import {Router} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent implements OnChanges, OnDestroy {
|
||||
title = 'aqua-viewer';
|
||||
|
||||
user: User;
|
||||
|
||||
mobileQuery: MediaQueryList;
|
||||
|
||||
dark = 'dark';
|
||||
|
||||
divaMenus: Menu[] = [
|
||||
{
|
||||
id: 0,
|
||||
name: 'Profile',
|
||||
url: 'diva/profile'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: 'PvRecord',
|
||||
url: 'diva/record'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Recent Play',
|
||||
url: 'diva/recent'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Setting',
|
||||
url: 'diva/setting'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Management',
|
||||
url: 'diva/management'
|
||||
},
|
||||
];
|
||||
|
||||
amazonMenus: Menu[] = [
|
||||
{
|
||||
id: 0,
|
||||
name: 'Profile',
|
||||
url: 'amazon/profile'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
name: 'Rating',
|
||||
url: 'amazon/rating'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Recent Play',
|
||||
url: 'amazon/recent'
|
||||
}
|
||||
];
|
||||
|
||||
private _mobileQueryListener: () => void;
|
||||
|
||||
constructor(
|
||||
changeDetectorRef: ChangeDetectorRef,
|
||||
media: MediaMatcher,
|
||||
private authenticationService: AuthenticationService,
|
||||
private route: Router
|
||||
) {
|
||||
this.mobileQuery = media.matchMedia('(max-width: 600px)');
|
||||
this._mobileQueryListener = () => changeDetectorRef.detectChanges();
|
||||
this.mobileQuery.addListener(this._mobileQueryListener);
|
||||
this.user = authenticationService.currentUserValue;
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.user = this.authenticationService.currentUserValue;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.mobileQuery.removeListener(this._mobileQueryListener);
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.authenticationService.logout();
|
||||
}
|
||||
}
|
||||
|
||||
export class Menu {
|
||||
id: number;
|
||||
name: string;
|
||||
url: string;
|
||||
}
|
||||
55
src/app/app.module.ts
Normal file
55
src/app/app.module.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
import {AppComponent} from './app.component';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {AppRoutingModule} from './app-routing.module';
|
||||
import {ReactiveFormsModule} from '@angular/forms';
|
||||
import {
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatListModule,
|
||||
MatMenuModule,
|
||||
MatNativeDateModule,
|
||||
MatSelectModule,
|
||||
MatSidenavModule,
|
||||
MatToolbarModule
|
||||
} from '@angular/material';
|
||||
import {MessageModule} from './message/message.module';
|
||||
import {DashboardModule} from './dashboard/dashboard.module';
|
||||
import {LoginModule} from './login/login.module';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {DivaModule} from './sega/diva/diva.module';
|
||||
import {AmazonModule} from './sega/chunithm/amazon/amazon.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
BrowserAnimationsModule,
|
||||
HttpClientModule,
|
||||
|
||||
MessageModule,
|
||||
AppRoutingModule,
|
||||
DashboardModule,
|
||||
LoginModule,
|
||||
DivaModule,
|
||||
AmazonModule,
|
||||
|
||||
ReactiveFormsModule,
|
||||
MatButtonModule,
|
||||
MatToolbarModule,
|
||||
MatSidenavModule,
|
||||
MatIconModule,
|
||||
MatListModule,
|
||||
MatSelectModule,
|
||||
MatMenuModule,
|
||||
MatNativeDateModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
22
src/app/auth/auth-guard.service.spec.ts
Normal file
22
src/app/auth/auth-guard.service.spec.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* tslint:disable:no-unused-variable */
|
||||
|
||||
import {inject, TestBed} from '@angular/core/testing';
|
||||
import {AuthGuardService} from './auth-guard.service';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
|
||||
describe('Service: AuthGuard', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
RouterTestingModule
|
||||
],
|
||||
providers: [AuthGuardService]
|
||||
});
|
||||
});
|
||||
|
||||
it('should ...', inject([AuthGuardService], (service: AuthGuardService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
37
src/app/auth/auth-guard.service.ts
Normal file
37
src/app/auth/auth-guard.service.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import {AuthenticationService} from './authentication.service';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {ActivatedRouteSnapshot, Router, RouterStateSnapshot} from '@angular/router';
|
||||
import {Route} from '@angular/compiler/src/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthGuardService {
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private authenticationService: AuthenticationService
|
||||
) {
|
||||
}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
const currentUser = this.authenticationService.currentUserValue;
|
||||
if (currentUser) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.router.navigate(['/login']);
|
||||
return false;
|
||||
}
|
||||
|
||||
canLoad(route: Route) {
|
||||
const currentUser = this.authenticationService.currentUserValue;
|
||||
if (currentUser) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.router.navigate(['/login']);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
18
src/app/auth/authentication.service.spec.ts
Normal file
18
src/app/auth/authentication.service.spec.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/* tslint:disable:no-unused-variable */
|
||||
|
||||
import {inject, TestBed} from '@angular/core/testing';
|
||||
import {AuthenticationService} from './authentication.service';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
|
||||
describe('Service: Authentication', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientModule],
|
||||
providers: [AuthenticationService]
|
||||
});
|
||||
});
|
||||
|
||||
it('should ...', inject([AuthenticationService], (service: AuthenticationService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
53
src/app/auth/authentication.service.ts
Normal file
53
src/app/auth/authentication.service.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {map} from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthenticationService {
|
||||
public currentUser: Observable<User>;
|
||||
private currentUserSubject: BehaviorSubject<User>;
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
this.currentUserSubject = new BehaviorSubject<User>(JSON.parse(localStorage.getItem('currentUser')));
|
||||
this.currentUser = this.currentUserSubject.asObservable();
|
||||
}
|
||||
|
||||
public get currentUserValue(): User {
|
||||
return this.currentUserSubject.value;
|
||||
}
|
||||
|
||||
login(accessCode: string, server: string) {
|
||||
return this.http.post<any>(server + '/' + 'api/sega/aime/getByAccessCode', {accessCode})
|
||||
.pipe(
|
||||
map(
|
||||
resp => {
|
||||
if (resp && resp.extId) {
|
||||
const user = new User(resp.extId, server);
|
||||
localStorage.setItem('currentUser', JSON.stringify(user));
|
||||
this.currentUserSubject.next(user);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
logout() {
|
||||
localStorage.removeItem('currentUser');
|
||||
this.currentUserSubject.next(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class User {
|
||||
extId: number;
|
||||
apiServer: string;
|
||||
|
||||
constructor(extId: number, apiServer: string) {
|
||||
this.extId = extId;
|
||||
this.apiServer = apiServer;
|
||||
}
|
||||
}
|
||||
0
src/app/dashboard/dashboard.component.css
Normal file
0
src/app/dashboard/dashboard.component.css
Normal file
3
src/app/dashboard/dashboard.component.html
Normal file
3
src/app/dashboard/dashboard.component.html
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<p>
|
||||
Welcome to Aqua Server WebUI<br>
|
||||
</p>
|
||||
26
src/app/dashboard/dashboard.component.spec.ts
Normal file
26
src/app/dashboard/dashboard.component.spec.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* tslint:disable:no-unused-variable */
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {DashboardComponent} from './dashboard.component';
|
||||
|
||||
describe('DashboardComponent', () => {
|
||||
let component: DashboardComponent;
|
||||
let fixture: ComponentFixture<DashboardComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DashboardComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DashboardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
16
src/app/dashboard/dashboard.component.ts
Normal file
16
src/app/dashboard/dashboard.component.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
templateUrl: './dashboard.component.html',
|
||||
styleUrls: ['./dashboard.component.css']
|
||||
})
|
||||
export class DashboardComponent implements OnInit {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
12
src/app/dashboard/dashboard.module.ts
Normal file
12
src/app/dashboard/dashboard.module.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
|
||||
import {DashboardComponent} from './dashboard.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [],
|
||||
exports: [],
|
||||
declarations: [DashboardComponent],
|
||||
providers: [],
|
||||
})
|
||||
export class DashboardModule {
|
||||
}
|
||||
7
src/app/login/login.component.css
Normal file
7
src/app/login/login.component.css
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
min-width: 120px;
|
||||
}
|
||||
19
src/app/login/login.component.html
Normal file
19
src/app/login/login.component.html
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<mat-card class="login-card">
|
||||
<mat-card-title>
|
||||
Login
|
||||
</mat-card-title>
|
||||
<mat-card-content>
|
||||
<form (ngSubmit)="onSubmit()" [formGroup]="loginForm">
|
||||
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>Access Code</mat-label>
|
||||
<input formControlName="accessCode" matInput required type="text">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>ApiServer</mat-label>
|
||||
<input formControlName="apiServer" matInput required type="text">
|
||||
</mat-form-field>
|
||||
<button [disabled]="!loginForm.valid" mat-raised-button type="submit">Login</button>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
62
src/app/login/login.component.spec.ts
Normal file
62
src/app/login/login.component.spec.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
|
||||
import {LoginComponent} from './login.component';
|
||||
import {AppRoutingModule} from '../app-routing.module';
|
||||
import {LayoutModule} from '@angular/cdk/layout';
|
||||
import {DashboardModule} from '../dashboard/dashboard.module';
|
||||
import {ContainerModule} from '../container/container.module';
|
||||
import {RegisterModule} from '../register/register.module';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {PlayerboardListModule} from '../playerboard-list/playerboard-list.module';
|
||||
|
||||
describe('LoginComponent', () => {
|
||||
let component: LoginComponent;
|
||||
let fixture: ComponentFixture<LoginComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [LoginComponent],
|
||||
imports: [
|
||||
LayoutModule,
|
||||
FormsModule,
|
||||
AppRoutingModule,
|
||||
|
||||
HttpClientModule,
|
||||
DashboardModule,
|
||||
ContainerModule,
|
||||
RegisterModule,
|
||||
ReactiveFormsModule,
|
||||
MatButtonModule,
|
||||
MatInputModule,
|
||||
MatCardModule,
|
||||
PlayerboardListModule
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LoginComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should compile', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`form should be invalid`, async(() => {
|
||||
component.loginForm.controls.email.setValue('');
|
||||
component.loginForm.controls.password.setValue('');
|
||||
expect(component.loginForm.invalid).toBeTruthy();
|
||||
}));
|
||||
|
||||
it(`form should be valid`, async(() => {
|
||||
component.loginForm.controls.email.setValue('test@eamil.com');
|
||||
component.loginForm.controls.password.setValue('12345');
|
||||
expect(component.loginForm.invalid).toBeFalsy();
|
||||
}));
|
||||
});
|
||||
61
src/app/login/login.component.ts
Normal file
61
src/app/login/login.component.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import {Router} from '@angular/router';
|
||||
import {AuthenticationService} from '../auth/authentication.service';
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||
import {MessageService} from '../message.service';
|
||||
import {first} from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.css']
|
||||
})
|
||||
export class LoginComponent implements OnInit {
|
||||
loginForm: FormGroup;
|
||||
|
||||
|
||||
constructor(
|
||||
private fb: FormBuilder,
|
||||
private router: Router,
|
||||
private authenticationService: AuthenticationService,
|
||||
private messageService: MessageService
|
||||
) {
|
||||
}
|
||||
|
||||
get f() {
|
||||
return this.loginForm.controls;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.loginForm = this.fb.group({
|
||||
accessCode: ['', Validators.required],
|
||||
apiServer: ['http://localhost:80', Validators.required],
|
||||
});
|
||||
if (this.authenticationService.currentUserValue) {
|
||||
this.router.navigateByUrl('/dashboard');
|
||||
}
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
if (this.loginForm.invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.authenticationService.login(this.f.accessCode.value, this.f.apiServer.value).pipe(first())
|
||||
.subscribe(
|
||||
data => {
|
||||
if (data != null) {
|
||||
this.messageService.notice('OK');
|
||||
location.reload(true);
|
||||
} else {
|
||||
this.messageService.notice('No such Card');
|
||||
}
|
||||
},
|
||||
error => {
|
||||
this.messageService.notice(error.message);
|
||||
console.warn('login fail', error);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
27
src/app/login/login.module.ts
Normal file
27
src/app/login/login.module.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
|
||||
import {LoginComponent} from './login.component';
|
||||
import {LayoutModule} from '@angular/cdk/layout';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {AppRoutingModule} from '../app-routing.module';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
LayoutModule,
|
||||
FormsModule,
|
||||
AppRoutingModule,
|
||||
|
||||
ReactiveFormsModule,
|
||||
MatButtonModule,
|
||||
MatInputModule,
|
||||
MatCardModule,
|
||||
],
|
||||
exports: [],
|
||||
declarations: [LoginComponent],
|
||||
providers: [],
|
||||
})
|
||||
export class LoginModule {
|
||||
}
|
||||
14
src/app/message.service.ts
Normal file
14
src/app/message.service.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import {MessageComponent} from './message/message.component';
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class MessageService {
|
||||
constructor(private messageComponent: MessageComponent) {
|
||||
}
|
||||
|
||||
notice(message: string) {
|
||||
this.messageComponent.openSnackBar(message);
|
||||
}
|
||||
}
|
||||
0
src/app/message/message.component.css
Normal file
0
src/app/message/message.component.css
Normal file
0
src/app/message/message.component.html
Normal file
0
src/app/message/message.component.html
Normal file
31
src/app/message/message.component.spec.ts
Normal file
31
src/app/message/message.component.spec.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {MessageModule} from './message.module';
|
||||
/* tslint:disable:no-unused-variable */
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {MessageComponent} from './message.component';
|
||||
|
||||
describe('MessageComponent', () => {
|
||||
let component: MessageComponent;
|
||||
let fixture: ComponentFixture<MessageComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
MessageModule,
|
||||
BrowserAnimationsModule,
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MessageComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
31
src/app/message/message.component.ts
Normal file
31
src/app/message/message.component.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import {Component, Injectable, OnInit} from '@angular/core';
|
||||
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
@Component({
|
||||
selector: 'app-message',
|
||||
templateUrl: './message.component.html',
|
||||
styleUrls: ['./message.component.css']
|
||||
})
|
||||
export class MessageComponent implements OnInit {
|
||||
|
||||
|
||||
constructor(
|
||||
private _snackBar: MatSnackBar
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
this.openSnackBar('Initialized');
|
||||
}
|
||||
|
||||
public openSnackBar(message: string) {
|
||||
this._snackBar.open(message, 'OK', {
|
||||
duration: 2000,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
17
src/app/message/message.module.ts
Normal file
17
src/app/message/message.module.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
|
||||
import {MessageComponent} from './message.component';
|
||||
import {MatSnackBarModule} from '@angular/material/snack-bar';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
MatSnackBarModule,
|
||||
MatButtonModule
|
||||
],
|
||||
exports: [MessageComponent],
|
||||
declarations: [MessageComponent],
|
||||
providers: [],
|
||||
})
|
||||
export class MessageModule {
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
/*mat-card {*/
|
||||
/* background-image: url("https://i.loli.net/2019/11/25/Xv4M5Npl9wib3Em.png");*/
|
||||
/* background-repeat: no-repeat;*/
|
||||
/* background-position: right top;*/
|
||||
/* background-size: 50%;*/
|
||||
/*}*/
|
||||
|
||||
mat-card-content {
|
||||
font-size: 18px;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<mat-card *ngIf="profile">
|
||||
<mat-card-title>Player Profile</mat-card-title>
|
||||
<mat-card-content>
|
||||
<table class="zebra">
|
||||
<tr>
|
||||
<th>Player Name</th>
|
||||
<td>{{profile.userName}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Player Rating</th>
|
||||
<td>{{profile.playerRating / 100}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Player Level</th>
|
||||
<td>{{profile.level}}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Total Play Count</th>
|
||||
<td>{{profile.playCount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Name Plate Id</th>
|
||||
<td>{{profile.nameplateId}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Frame Id</th>
|
||||
<td>{{profile.frameId}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Character Id</th>
|
||||
<td>{{profile.characterId}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Last Play</th>
|
||||
<td>{{profile.lastPlayDate}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {AmazonProfileComponent} from './amazon-profile.component';
|
||||
|
||||
describe('AmazonProfileComponent', () => {
|
||||
let component: AmazonProfileComponent;
|
||||
let fixture: ComponentFixture<AmazonProfileComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AmazonProfileComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AmazonProfileComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {MessageService} from '../../../../message.service';
|
||||
import {AmazonProfile} from '../model/AmazonProfile';
|
||||
import {ApiService} from '../../../../api.service';
|
||||
import {AuthenticationService} from '../../../../auth/authentication.service';
|
||||
import {HttpParams} from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'app-amazon-profile',
|
||||
templateUrl: './amazon-profile.component.html',
|
||||
styleUrls: ['./amazon-profile.component.css']
|
||||
})
|
||||
export class AmazonProfileComponent implements OnInit {
|
||||
|
||||
profile: AmazonProfile;
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private auth: AuthenticationService,
|
||||
private messageService: MessageService,
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const aimeId = String(this.auth.currentUserValue.extId);
|
||||
const param = new HttpParams().set('aimeId', aimeId);
|
||||
this.api.get('api/game/chuni/amazon/profile', param).subscribe(
|
||||
data => {
|
||||
this.profile = data;
|
||||
},
|
||||
error => this.messageService.notice(error.statusText)
|
||||
);
|
||||
}
|
||||
|
||||
getRatingRank(s: number): number {
|
||||
switch (true) {
|
||||
case (s < 2.0):
|
||||
return 1;
|
||||
case (s < 4.0):
|
||||
return 2;
|
||||
case (s < 7.0):
|
||||
return 3;
|
||||
case (s < 10.0):
|
||||
return 4;
|
||||
case (s < 12.0):
|
||||
return 5;
|
||||
case (s < 13.0):
|
||||
return 6;
|
||||
case (s < 14.0):
|
||||
return 7;
|
||||
case (s < 14.5):
|
||||
return 8;
|
||||
case (s < 15.0):
|
||||
return 9;
|
||||
case (s >= 15.0):
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
|
||||
formatNumber(value: number, length?: number): string {
|
||||
let str = value.toString();
|
||||
while (str.length < length) {
|
||||
str = '0' + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
getClass(v: number): number {
|
||||
switch (true) {
|
||||
case (v === 10):
|
||||
return 1;
|
||||
case (v === 11):
|
||||
return 2;
|
||||
case (v === 12):
|
||||
return 3;
|
||||
case (v === 13):
|
||||
return 4;
|
||||
case (v === 14):
|
||||
return 5;
|
||||
case (v === 20):
|
||||
return 6; // infinity
|
||||
case (v === 21):
|
||||
return 7; // class unknown
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
.skill-card table {
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.skill-card h3 {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.skill-card img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
td {
|
||||
max-width: 130px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 599px) {
|
||||
.skill-card table {
|
||||
width: 100%;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.skill-card img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<div>
|
||||
<mat-card>
|
||||
<h3>Total Rating <span
|
||||
class="{{(topTotal + recentTotal) / 40 | toRating | ratingClass}}">{{(topTotal + recentTotal) / 40 | toRating }}</span>
|
||||
</h3>
|
||||
<h3>Top <span class="{{topTotal / 30 | toRating | ratingClass}}">{{topTotal / 30 | toRating }}</span></h3>
|
||||
<h3>Recent <span class="{{recentTotal / 10 | toRating | ratingClass}}">{{recentTotal / 10 | toRating }}</span></h3>
|
||||
</mat-card>
|
||||
</div>
|
||||
<div fxLayout="row wrap" fxLayout.sm="column" fxLayout.xs="column">
|
||||
|
||||
<div class="skill-card" fxFlex="45">
|
||||
<mat-card>
|
||||
<h3>Top</h3>
|
||||
<table class="zebra">
|
||||
<thead>
|
||||
<th>Music</th>
|
||||
<th>Diff</th>
|
||||
<th>Score</th>
|
||||
<th>Rating</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of topRating; index as i" routerLink="/exchain/playrecord/{{item.musicId}}">
|
||||
<td>
|
||||
{{i + 1}}: {{item.musicName}}
|
||||
</td>
|
||||
<td>
|
||||
{{item.ratingBase|toRating}}
|
||||
</td>
|
||||
<td>
|
||||
{{item.score}}
|
||||
</td>
|
||||
<td>
|
||||
<span class="{{item.rating|toRating|ratingClass}}">{{item.rating|toRating}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</mat-card>
|
||||
</div>
|
||||
<div fxFlex="10">
|
||||
</div>
|
||||
<div class="skill-card" fxFlex="45">
|
||||
<mat-card>
|
||||
<h3>Recent</h3>
|
||||
<table class="zebra">
|
||||
<thead>
|
||||
<th>Music</th>
|
||||
<th>Diff</th>
|
||||
<th>Score</th>
|
||||
<th>Rating</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of recentRating; index as i" routerLink="/exchain/playrecord/{{item.musicId}}">
|
||||
<td>
|
||||
{{i + 1}}: {{item.musicName}}
|
||||
</td>
|
||||
<td>
|
||||
{{item.ratingBase|toRating}}
|
||||
</td>
|
||||
<td>
|
||||
{{item.score}}
|
||||
</td>
|
||||
<td>
|
||||
<span class="{{item.rating|toRating|ratingClass}}">{{item.rating|toRating}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {AmazonRatingComponent} from './amazon-rating.component';
|
||||
|
||||
describe('AmazonRatingComponent', () => {
|
||||
let component: AmazonRatingComponent;
|
||||
let fixture: ComponentFixture<AmazonRatingComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AmazonRatingComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AmazonRatingComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {MessageService} from '../../../../message.service';
|
||||
import {ApiService} from '../../../../api.service';
|
||||
import {HttpParams} from '@angular/common/http';
|
||||
import {AuthenticationService} from '../../../../auth/authentication.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-amazon-rating',
|
||||
templateUrl: './amazon-rating.component.html',
|
||||
styleUrls: ['./amazon-rating.component.css']
|
||||
})
|
||||
export class AmazonRatingComponent implements OnInit {
|
||||
|
||||
private topRating: RatingItem[] = [];
|
||||
private recentRating: RatingItem[] = [];
|
||||
private topTotal = 0;
|
||||
private recentTotal = 0;
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private auth: AuthenticationService,
|
||||
private messageService: MessageService
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const aimeId = String(this.auth.currentUserValue.extId);
|
||||
const param = new HttpParams().set('aimeId', aimeId);
|
||||
this.api.get('api/game/chuni/amazon/rating', param).subscribe(
|
||||
data => {
|
||||
this.topRating = data;
|
||||
if (this.topRating.length === 0) {
|
||||
this.messageService.notice('No Data');
|
||||
}
|
||||
this.topRating.forEach(item => this.topTotal += item.rating);
|
||||
},
|
||||
error => this.messageService.notice(error.statusText)
|
||||
);
|
||||
|
||||
this.api.get('api/game/chuni/amazon/rating/recent', param).subscribe(
|
||||
data => {
|
||||
this.recentRating = data;
|
||||
if (this.recentRating.length === 0) {
|
||||
this.messageService.notice('No Data');
|
||||
}
|
||||
this.recentRating.forEach(item => this.recentTotal += item.rating);
|
||||
},
|
||||
error => this.messageService.notice(error.statusText)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export interface RatingItem {
|
||||
musicId: number;
|
||||
musicName: string;
|
||||
artistName: string;
|
||||
level: number;
|
||||
score: number;
|
||||
ratingBase: number;
|
||||
rating: number;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
mat-card-title {
|
||||
font-size: 1.2em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.song-info .title {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.song-info .info {
|
||||
opacity: 70%;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.result-content .left {
|
||||
min-width: 30%;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.achievement-value {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.score-value {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
<div *ngIf="recent">
|
||||
<mat-card *ngFor="let item of recent">
|
||||
<mat-card-title>{{item.userPlayDate}}</mat-card-title>
|
||||
<mat-card-content>
|
||||
<div class="song-info">
|
||||
<span class="title">{{item.songInfo != null ? item.songInfo.name : 'musicId:' + item.musicId}}</span><br>
|
||||
<span
|
||||
class="info">{{item.songInfo != null ? item.songInfo.artistName : ''}}</span>
|
||||
</div>
|
||||
<div class="result-content">
|
||||
<div class="left">
|
||||
Rank:<br>
|
||||
<span class="achievement-value">{{item.rank|toRank}}</span>
|
||||
<br>
|
||||
<br>Score:<br>
|
||||
<span class="score-value">{{item.score}}</span>
|
||||
<br>
|
||||
<br>{{item.isNewRecord ? 'NEW RECORD' : ''}}<br>
|
||||
</div>
|
||||
<table class="zebra">
|
||||
<tr>
|
||||
<th>JUSTICE C.</th>
|
||||
<td>{{item.judgeCritical}}</td>
|
||||
<td>TAP</td>
|
||||
<td>{{item.rateTap / 100}}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>JUSTICE</th>
|
||||
<td>{{item.judgeJustice}}</td>
|
||||
<td>HOLD</td>
|
||||
<td>{{item.rateHold / 100}}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>ATTACK</th>
|
||||
<td>{{item.judgeAttack}}</td>
|
||||
<td>SLIDE</td>
|
||||
<td>{{item.rateSlide / 100}}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>MISS</th>
|
||||
<td>{{item.judgeGuilty}}</td>
|
||||
<td>AIR</td>
|
||||
<td>{{item.rateAir / 100}}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Combo</th>
|
||||
<td>{{item.maxCombo}}</td>
|
||||
<td>FLICK</td>
|
||||
<td>{{item.rateFlick / 100}}%</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {AmazonRecentComponent} from './amazon-recent.component';
|
||||
|
||||
describe('AmazonRecentComponent', () => {
|
||||
let component: AmazonRecentComponent;
|
||||
let fixture: ComponentFixture<AmazonRecentComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AmazonRecentComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AmazonRecentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {ApiService} from '../../../../api.service';
|
||||
import {AuthenticationService} from '../../../../auth/authentication.service';
|
||||
import {MessageService} from '../../../../message.service';
|
||||
import {HttpParams} from '@angular/common/http';
|
||||
import {AmazonPlayLog} from '../model/AmazonPlayLog';
|
||||
import {ChuniMusicDbService} from '../chuni-music-db.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-amazon-recent',
|
||||
templateUrl: './amazon-recent.component.html',
|
||||
styleUrls: ['./amazon-recent.component.css']
|
||||
})
|
||||
export class AmazonRecentComponent implements OnInit {
|
||||
|
||||
recent: AmazonPlayLog[] = [];
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private auth: AuthenticationService,
|
||||
private messageService: MessageService,
|
||||
private musicDb: ChuniMusicDbService
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const aimeId = String(this.auth.currentUserValue.extId);
|
||||
const param = new HttpParams().set('aimeId', aimeId);
|
||||
this.api.get('api/game/chuni/amazon/recent', param).subscribe(
|
||||
data => {
|
||||
data.forEach(x => {
|
||||
x.songInfo = this.musicDb.getMusicDb().get(x.musicId);
|
||||
this.recent.push(x);
|
||||
});
|
||||
},
|
||||
error => this.messageService.notice(error.statusText)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
69
src/app/sega/chunithm/amazon/amazon.module.ts
Normal file
69
src/app/sega/chunithm/amazon/amazon.module.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatToolbarModule} from '@angular/material/toolbar';
|
||||
import {MatSidenavModule} from '@angular/material/sidenav';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatListModule} from '@angular/material/list';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {MatTableModule} from '@angular/material/table';
|
||||
import {MatPaginatorModule} from '@angular/material/paginator';
|
||||
import {MatSortModule} from '@angular/material/sort';
|
||||
import {MatDialogModule} from '@angular/material/dialog';
|
||||
import {MatSnackBarModule} from '@angular/material/snack-bar';
|
||||
import {FlexLayoutModule} from '@angular/flex-layout';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {AmazonRoutes} from './amazon.routing';
|
||||
import {AmazonProfileComponent} from './amazon-profile/amazon-profile.component';
|
||||
import {AmazonRatingComponent} from './amazon-rating/amazon-rating.component';
|
||||
import {ToRatingPipe} from './util/to-rating.pipe';
|
||||
import {FormatnumberPipe} from './util/formatnumber.pipe';
|
||||
import {RatingClass} from './util/rating-class.pipe';
|
||||
import {CourceIdToClassPipe} from './util/cource-id-to-class.pipe';
|
||||
import {AmazonRecentComponent} from './amazon-recent/amazon-recent.component';
|
||||
import {ToRankPipe} from './util/to-rank.pipe';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
|
||||
AmazonRoutes,
|
||||
|
||||
MatFormFieldModule,
|
||||
MatCardModule,
|
||||
MatButtonModule,
|
||||
MatToolbarModule,
|
||||
MatSidenavModule,
|
||||
MatIconModule,
|
||||
MatListModule,
|
||||
MatInputModule,
|
||||
MatSelectModule,
|
||||
ReactiveFormsModule,
|
||||
MatMenuModule,
|
||||
MatTableModule,
|
||||
MatPaginatorModule,
|
||||
MatSortModule,
|
||||
MatDialogModule,
|
||||
MatSnackBarModule,
|
||||
FlexLayoutModule
|
||||
],
|
||||
declarations: [
|
||||
AmazonProfileComponent,
|
||||
AmazonRatingComponent,
|
||||
ToRatingPipe,
|
||||
FormatnumberPipe,
|
||||
RatingClass,
|
||||
CourceIdToClassPipe,
|
||||
AmazonRecentComponent,
|
||||
ToRankPipe
|
||||
],
|
||||
entryComponents: [],
|
||||
exports: []
|
||||
})
|
||||
export class AmazonModule {
|
||||
}
|
||||
12
src/app/sega/chunithm/amazon/amazon.routing.ts
Normal file
12
src/app/sega/chunithm/amazon/amazon.routing.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {AmazonProfileComponent} from './amazon-profile/amazon-profile.component';
|
||||
import {AmazonRatingComponent} from './amazon-rating/amazon-rating.component';
|
||||
import {AmazonRecentComponent} from './amazon-recent/amazon-recent.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{path: 'profile', component: AmazonProfileComponent},
|
||||
{path: 'rating', component: AmazonRatingComponent},
|
||||
{path: 'recent', component: AmazonRecentComponent},
|
||||
];
|
||||
|
||||
export const AmazonRoutes = RouterModule.forChild(routes);
|
||||
38
src/app/sega/chunithm/amazon/chuni-music-db.service.ts
Normal file
38
src/app/sega/chunithm/amazon/chuni-music-db.service.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {ChuniMusic} from './model/ChuniMusic';
|
||||
import {ApiService} from '../../../api.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ChuniMusicDbService {
|
||||
|
||||
musicDb: Map<number, ChuniMusic>;
|
||||
|
||||
constructor(private api: ApiService) {
|
||||
let db = localStorage.getItem('chuniMusicDb');
|
||||
if (db == null) {
|
||||
this.api.get('api/game/chuni/amazon/music').subscribe(
|
||||
data => {
|
||||
db = data;
|
||||
localStorage.setItem('chuniMusicDb', JSON.stringify(db));
|
||||
this.musicDb = this.parse(JSON.parse(db));
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.musicDb = this.parse(JSON.parse(db));
|
||||
}
|
||||
}
|
||||
|
||||
getMusicDb(): Map<number, ChuniMusic> {
|
||||
return this.musicDb;
|
||||
}
|
||||
|
||||
private parse(db: ChuniMusic[]): Map<number, ChuniMusic> {
|
||||
const result: Map<number, ChuniMusic> = new Map();
|
||||
db.forEach(x => {
|
||||
result.set(x.musicId, x);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
38
src/app/sega/chunithm/amazon/model/AmazonPlayLog.ts
Normal file
38
src/app/sega/chunithm/amazon/model/AmazonPlayLog.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import {ChuniMusic} from './ChuniMusic';
|
||||
|
||||
export interface AmazonPlayLog {
|
||||
playDate: Date;
|
||||
userPlayDate: Date;
|
||||
musicId: number;
|
||||
songInfo?: ChuniMusic;
|
||||
level: number;
|
||||
customId: number;
|
||||
playedCustom1: number;
|
||||
playedCustom2: number;
|
||||
playedCustom3: number;
|
||||
track: number;
|
||||
score: number;
|
||||
rank: number;
|
||||
maxCombo: number;
|
||||
maxChain: number;
|
||||
rateTap: number;
|
||||
rateHold: number;
|
||||
rateSlide: number;
|
||||
rateAir: number;
|
||||
rateFlick: number;
|
||||
judgeGuilty: number;
|
||||
judgeAttack: number;
|
||||
judgeJustice: number;
|
||||
judgeCritical: number;
|
||||
playerRating: number;
|
||||
fullChainKind: number;
|
||||
characterId: number;
|
||||
skillId: number;
|
||||
playKind: number;
|
||||
skillLevel: number;
|
||||
skillEffect: number;
|
||||
isNewRecord: boolean;
|
||||
isFullCombo: boolean;
|
||||
isAllJustice: boolean;
|
||||
isClear: boolean;
|
||||
}
|
||||
36
src/app/sega/chunithm/amazon/model/AmazonProfile.ts
Normal file
36
src/app/sega/chunithm/amazon/model/AmazonProfile.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
export interface AmazonProfile {
|
||||
userName: string;
|
||||
level: number;
|
||||
exp: number;
|
||||
point: number;
|
||||
totalPoint: bigint;
|
||||
playCount: number;
|
||||
multiPlayCount: number;
|
||||
multiWinCount: number;
|
||||
requestResCount: number;
|
||||
acceptResCount: number;
|
||||
successResCount: number;
|
||||
playerRating: number;
|
||||
highestRating: number;
|
||||
nameplateId: number;
|
||||
frameId: number;
|
||||
characterId: number;
|
||||
trophyId: number;
|
||||
playedTutorialBit: number;
|
||||
firstTutorialCancelNum: number;
|
||||
masterTutorialCancelNum: number;
|
||||
totalRepertoireCount: number;
|
||||
totalMapNum: number;
|
||||
totalHiScore: bigint;
|
||||
totalBasicHighScore: bigint;
|
||||
totalAdvancedHighScore: bigint;
|
||||
totalExpertHighScore: bigint;
|
||||
totalMasterHighScore: bigint;
|
||||
friendCount: number;
|
||||
firstGameId: string;
|
||||
firstRomVersion: string;
|
||||
firstDataVersion: string;
|
||||
firstPlayDate: Date;
|
||||
lastPlayDate: Date;
|
||||
courseClass: number;
|
||||
}
|
||||
9
src/app/sega/chunithm/amazon/model/ChuniMusic.ts
Normal file
9
src/app/sega/chunithm/amazon/model/ChuniMusic.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
export interface ChuniMusic {
|
||||
musicId: number;
|
||||
name: string;
|
||||
sotrName: string;
|
||||
copyright: string;
|
||||
artistName: string;
|
||||
genre: string;
|
||||
releaseVersion: string;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import {CourceIdToClassPipe} from './cource-id-to-class.pipe';
|
||||
|
||||
describe('CourceIdToClassPipe', () => {
|
||||
it('create an instance', () => {
|
||||
const pipe = new CourceIdToClassPipe();
|
||||
expect(pipe).toBeTruthy();
|
||||
});
|
||||
});
|
||||
27
src/app/sega/chunithm/amazon/util/cource-id-to-class.pipe.ts
Normal file
27
src/app/sega/chunithm/amazon/util/cource-id-to-class.pipe.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'courceIdToClass'
|
||||
})
|
||||
export class CourceIdToClassPipe implements PipeTransform {
|
||||
|
||||
transform(v: number): number {
|
||||
switch (true) {
|
||||
case (v === 10):
|
||||
return 1;
|
||||
case (v === 11):
|
||||
return 2;
|
||||
case (v === 12):
|
||||
return 3;
|
||||
case (v === 13):
|
||||
return 4;
|
||||
case (v === 14):
|
||||
return 5;
|
||||
case (v === 20):
|
||||
return 6; // infinity
|
||||
case (v === 21):
|
||||
return 7; // class unknown
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
10
src/app/sega/chunithm/amazon/util/formatnumber.pipe.spec.ts
Normal file
10
src/app/sega/chunithm/amazon/util/formatnumber.pipe.spec.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/* tslint:disable:no-unused-variable */
|
||||
|
||||
import {FormatnumberPipe} from './formatnumber.pipe';
|
||||
|
||||
describe('Pipe: Formatnumbere', () => {
|
||||
it('create an instance', () => {
|
||||
let pipe = new FormatnumberPipe();
|
||||
expect(pipe).toBeTruthy();
|
||||
});
|
||||
});
|
||||
16
src/app/sega/chunithm/amazon/util/formatnumber.pipe.ts
Normal file
16
src/app/sega/chunithm/amazon/util/formatnumber.pipe.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'formatNumber'
|
||||
})
|
||||
export class FormatnumberPipe implements PipeTransform {
|
||||
|
||||
public transform(value: number, length?: number): string {
|
||||
let str = value.toString();
|
||||
while (str.length < length) {
|
||||
str = '0' + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
}
|
||||
33
src/app/sega/chunithm/amazon/util/rating-class.pipe.ts
Normal file
33
src/app/sega/chunithm/amazon/util/rating-class.pipe.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'ratingClass'
|
||||
})
|
||||
export class RatingClass implements PipeTransform {
|
||||
|
||||
transform(s: number, args?: any): string {
|
||||
switch (true) {
|
||||
case (s < 2.0):
|
||||
return 'lv8';
|
||||
case (s < 4.0):
|
||||
return 'lv6';
|
||||
case (s < 7.0):
|
||||
return 'lv2';
|
||||
case (s < 10.0):
|
||||
return 'lv12';
|
||||
case (s < 12.0):
|
||||
return 'lv10';
|
||||
case (s < 13.0):
|
||||
return 'lv14';
|
||||
case (s < 14.0):
|
||||
return 'lv15';
|
||||
case (s < 14.5):
|
||||
return 'lv16';
|
||||
case (s < 15.0):
|
||||
return 'lv0';
|
||||
case (s >= 15.0):
|
||||
return 'lv17';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
8
src/app/sega/chunithm/amazon/util/to-rank.pipe.spec.ts
Normal file
8
src/app/sega/chunithm/amazon/util/to-rank.pipe.spec.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import {ToRankPipe} from './to-rank.pipe';
|
||||
|
||||
describe('ToRankPipe', () => {
|
||||
it('create an instance', () => {
|
||||
const pipe = new ToRankPipe();
|
||||
expect(pipe).toBeTruthy();
|
||||
});
|
||||
});
|
||||
47
src/app/sega/chunithm/amazon/util/to-rank.pipe.ts
Normal file
47
src/app/sega/chunithm/amazon/util/to-rank.pipe.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'toRank'
|
||||
})
|
||||
export class ToRankPipe implements PipeTransform {
|
||||
|
||||
transform(value: number): string {
|
||||
if (value === 0) {
|
||||
return 'D';
|
||||
}
|
||||
if (value === 1) {
|
||||
return 'C';
|
||||
}
|
||||
if (value === 2) {
|
||||
return 'B';
|
||||
}
|
||||
if (value === 3) {
|
||||
return 'BB';
|
||||
}
|
||||
if (value === 4) {
|
||||
return 'BBB';
|
||||
}
|
||||
if (value === 5) {
|
||||
return 'A';
|
||||
}
|
||||
if (value === 6) {
|
||||
return 'AA';
|
||||
}
|
||||
if (value === 7) {
|
||||
return 'AAA';
|
||||
}
|
||||
if (value === 8) {
|
||||
return 'S';
|
||||
}
|
||||
if (value === 9) {
|
||||
return 'SS';
|
||||
}
|
||||
if (value === 10) {
|
||||
return 'SSS';
|
||||
}
|
||||
if (value > 10) {
|
||||
return 'SSS';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
8
src/app/sega/chunithm/amazon/util/to-rating.pipe.spec.ts
Normal file
8
src/app/sega/chunithm/amazon/util/to-rating.pipe.spec.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import {ToRatingPipe} from './to-rating.pipe';
|
||||
|
||||
describe('ToRatingPipe', () => {
|
||||
it('create an instance', () => {
|
||||
const pipe = new ToRatingPipe();
|
||||
expect(pipe).toBeTruthy();
|
||||
});
|
||||
});
|
||||
12
src/app/sega/chunithm/amazon/util/to-rating.pipe.ts
Normal file
12
src/app/sega/chunithm/amazon/util/to-rating.pipe.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'toRating'
|
||||
})
|
||||
export class ToRatingPipe implements PipeTransform {
|
||||
|
||||
transform(value: number): number {
|
||||
return Math.floor(value) / 100;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
<mat-card>
|
||||
<mat-card-title>
|
||||
<div class="title">Contest ID: {{f.id.value}}</div>
|
||||
</mat-card-title>
|
||||
<mat-card-content>
|
||||
<form (ngSubmit)="onSubmit()" [formGroup]="contestForm">
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>Name</mat-label>
|
||||
<input formControlName="name" matInput required type="text">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-checkbox class="full-width" formControlName="enable">Enabled</mat-checkbox>
|
||||
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>Description</mat-label>
|
||||
<textarea formControlName="description" matInput required type="text"></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>Start Time</mat-label>
|
||||
<input [matDatepicker]="startTime" formControlName="startTime" matInput placeholder="Choose a start date">
|
||||
<mat-datepicker-toggle [for]="startTime" matSuffix></mat-datepicker-toggle>
|
||||
<mat-datepicker #startTime></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>End Time</mat-label>
|
||||
<input [matDatepicker]="endTime" formControlName="endTime" matInput placeholder="Choose a end date">
|
||||
<mat-datepicker-toggle [for]="endTime" matSuffix></mat-datepicker-toggle>
|
||||
<mat-datepicker #endTime></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>League</mat-label>
|
||||
<mat-select formControlName="league">
|
||||
<mat-option value="0">Beginner</mat-option>
|
||||
<mat-option value="1">Intermediate</mat-option>
|
||||
<mat-option value="2">Advanced</mat-option>
|
||||
<mat-option value="3">Professional</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>Stars</mat-label>
|
||||
<input formControlName="stars" matInput required type="number">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>minComplexity</mat-label>
|
||||
<input formControlName="minComplexity" matInput required type="number">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>maxComplexity</mat-label>
|
||||
<input formControlName="maxComplexity" matInput required type="number">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>Stages</mat-label>
|
||||
<input formControlName="stages" matInput required type="number">
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>Stage Limit</mat-label>
|
||||
<mat-select formControlName="stageLimit">
|
||||
<mat-option value="0">Unlimited</mat-option>
|
||||
<mat-option value="1">Limited</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>Norma Type</mat-label>
|
||||
<mat-select formControlName="normaType">
|
||||
<mat-option value="0">Score</mat-option>
|
||||
<mat-option value="1">Percentage</mat-option>
|
||||
<mat-option value="2">Cool Percentage</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>bronzeBorders</mat-label>
|
||||
<input formControlName="bronzeBorders" matInput required type="number">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>sliverBorders</mat-label>
|
||||
<input formControlName="sliverBorders" matInput required type="number">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>goldBorders</mat-label>
|
||||
<input formControlName="goldBorders" matInput required type="number">
|
||||
</mat-form-field>
|
||||
<button [disabled]="!contestForm.valid" mat-flat-button type="submit">Submit</button>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {DivaContestEditComponent} from './diva-contest-edit.component';
|
||||
|
||||
describe('DivaContestEditComponent', () => {
|
||||
let component: DivaContestEditComponent;
|
||||
let fixture: ComponentFixture<DivaContestEditComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DivaContestEditComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DivaContestEditComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||
import {ApiService} from '../../../../../api.service';
|
||||
import {MessageService} from '../../../../../message.service';
|
||||
import {Router} from '@angular/router';
|
||||
import {Contest} from '../../../model/mannagement/contest';
|
||||
import {DivaContestService} from '../diva-contest.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-diva-contest-edit',
|
||||
templateUrl: './diva-contest-edit.component.html',
|
||||
styleUrls: ['./diva-contest-edit.component.css']
|
||||
})
|
||||
export class DivaContestEditComponent implements OnInit {
|
||||
|
||||
contest: Contest;
|
||||
contestForm: FormGroup;
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private messageService: MessageService,
|
||||
private fb: FormBuilder,
|
||||
private contestService: DivaContestService,
|
||||
private router: Router
|
||||
) {
|
||||
}
|
||||
|
||||
get f() {
|
||||
return this.contestForm.controls;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.contest = this.contestService.contest;
|
||||
this.contestForm = this.fb.group({
|
||||
id: [this.contest.id, Validators.required],
|
||||
enable: [this.contest.enable],
|
||||
name: [this.contest.name, Validators.required],
|
||||
description: [this.contest.description, Validators.required],
|
||||
startTime: [this.contest.startTime, Validators.required],
|
||||
endTime: [this.contest.endTime, Validators.required],
|
||||
league: [String(this.contest.league), Validators.required],
|
||||
stars: [this.contest.stars, Validators.required],
|
||||
minComplexity: [this.contest.minComplexity, Validators.required],
|
||||
maxComplexity: [this.contest.maxComplexity, Validators.required],
|
||||
stages: [this.contest.stages, Validators.required],
|
||||
stageLimit: [String(this.contest.stageLimit), Validators.required],
|
||||
normaType: [String(this.contest.normaType), Validators.required],
|
||||
bronzeBorders: [this.contest.bronzeBorders, Validators.required],
|
||||
sliverBorders: [this.contest.sliverBorders, Validators.required],
|
||||
goldBorders: [this.contest.goldBorders, Validators.required],
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.api.put('api/game/diva/manage/contest', this.contestForm.value).subscribe(
|
||||
data => {
|
||||
console.log(data);
|
||||
this.router.navigateByUrl('/diva/management/contest');
|
||||
},
|
||||
error => this.messageService.notice(error)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
mat-card-title {
|
||||
font-size: 1.2em;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
mat-card-title button {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<mat-card>
|
||||
<mat-card-title>
|
||||
<div>Contest List, Length: {{contests ? contests.length : 0}}</div>
|
||||
<button (click)="edit(undefined)" mat-flat-button>Add</button>
|
||||
</mat-card-title>
|
||||
</mat-card>
|
||||
<div *ngIf="contests">
|
||||
<mat-card *ngFor="let contest of contests">
|
||||
<mat-card-content>
|
||||
<table class="zebra">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<td>{{contest.id}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Enabled</th>
|
||||
<td>{{contest.enable}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<td>{{contest.name}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<td>{{contest.description}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Start Time</th>
|
||||
<td>{{contest.startTime}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>End Time</th>
|
||||
<td>{{contest.endTime}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>League</th>
|
||||
<td>{{contestLeague[contest.league]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Star</th>
|
||||
<td>{{contest.stars}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Min Complexity</th>
|
||||
<td>{{contest.minComplexity}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Max Complexity</th>
|
||||
<td>{{contest.maxComplexity}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Stages</th>
|
||||
<td>{{contest.stages}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Stage Limit</th>
|
||||
<td>{{contestStageLimit[contest.stageLimit]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Norma Type</th>
|
||||
<td>{{contestNormaType[contest.normaType]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Bronze borders</th>
|
||||
<td>{{contest.bronzeBorders}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Sliver borders</th>
|
||||
<td>{{contest.sliverBorders}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Gold borders</th>
|
||||
<td>{{contest.goldBorders}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button (click)="edit(contest)" mat-flat-button>Edit</button>
|
||||
<button (click)="delete(contest.id)" mat-flat-button>Delete</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {DivaContestComponent} from './diva-contest.component';
|
||||
|
||||
describe('DivaContestComponent', () => {
|
||||
let component: DivaContestComponent;
|
||||
let fixture: ComponentFixture<DivaContestComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DivaContestComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DivaContestComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {ApiService} from '../../../../api.service';
|
||||
import {MessageService} from '../../../../message.service';
|
||||
import {Router} from '@angular/router';
|
||||
import {DivaContestService} from './diva-contest.service';
|
||||
import {Contest, ContestLeague, ContestNormaType, ContestStageLimit} from '../../model/mannagement/contest';
|
||||
|
||||
@Component({
|
||||
selector: 'app-diva-contest',
|
||||
templateUrl: './diva-contest.component.html',
|
||||
styleUrls: ['./diva-contest.component.css']
|
||||
})
|
||||
export class DivaContestComponent implements OnInit {
|
||||
|
||||
contests: Contest[];
|
||||
contestLeague = ContestLeague;
|
||||
contestStageLimit = ContestStageLimit;
|
||||
contestNormaType = ContestNormaType;
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private messageService: MessageService,
|
||||
private contestService: DivaContestService,
|
||||
private router: Router
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.load();
|
||||
}
|
||||
|
||||
load() {
|
||||
this.api.get('api/game/diva/manage/contest').subscribe(
|
||||
data => this.contests = data,
|
||||
error => this.messageService.notice(error)
|
||||
);
|
||||
}
|
||||
|
||||
delete(id) {
|
||||
this.api.delete('api/game/diva/manage/contest/' + id).subscribe(
|
||||
() => {
|
||||
this.messageService.notice('OK');
|
||||
this.load();
|
||||
},
|
||||
error => {
|
||||
this.messageService.notice(error.statusText);
|
||||
this.load();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
edit(contest) {
|
||||
this.contestService.contest = contest;
|
||||
this.router.navigateByUrl('/diva/management/contest/edit');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
import {DivaContestService} from './diva-contest.service';
|
||||
|
||||
describe('DivaContestService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: DivaContestService = TestBed.get(DivaContestService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {Contest, ContestLeague, ContestNormaType, ContestStageLimit} from '../../model/mannagement/contest';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DivaContestService {
|
||||
|
||||
currentContest: Contest = undefined;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
get contest() {
|
||||
return this.currentContest === undefined ? {
|
||||
id: -1,
|
||||
enable: true,
|
||||
startTime: new Date(),
|
||||
endTime: new Date(),
|
||||
name: 'Untitled',
|
||||
description: 'description',
|
||||
league: ContestLeague.Intermediate,
|
||||
stars: 16,
|
||||
minComplexity: 10,
|
||||
maxComplexity: 20,
|
||||
stages: 4,
|
||||
stageLimit: ContestStageLimit.Limited,
|
||||
normaType: ContestNormaType.Percentage,
|
||||
bronzeBorders: 16000,
|
||||
sliverBorders: 28000,
|
||||
goldBorders: 32000
|
||||
} : this.currentContest;
|
||||
}
|
||||
|
||||
set contest(f: Contest) {
|
||||
this.currentContest = f;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<mat-card>
|
||||
<mat-card-title>
|
||||
<div class="title">Festa</div>
|
||||
</mat-card-title>
|
||||
<mat-card-content>
|
||||
<form (ngSubmit)="onSubmit()" [formGroup]="festaForm">
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>Name</mat-label>
|
||||
<input formControlName="name" matInput required type="text">
|
||||
</mat-form-field>
|
||||
<mat-checkbox class="full-width" formControlName="enable">Enabled</mat-checkbox>
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>Kind</mat-label>
|
||||
<mat-select formControlName="kind">
|
||||
<mat-option value="0">PinkFesta</mat-option>
|
||||
<mat-option value="1">GreenFesta</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>Difficulty Limit</mat-label>
|
||||
<mat-select formControlName="difficulty">
|
||||
<mat-option value="-1">Unset</mat-option>
|
||||
<mat-option value="0">Easy</mat-option>
|
||||
<mat-option value="1">Normal</mat-option>
|
||||
<mat-option value="2">Hard</mat-option>
|
||||
<mat-option value="3">Extreme</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>PV List</mat-label>
|
||||
<input formControlName="pvList" matInput required type="text">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>Attributes</mat-label>
|
||||
<input formControlName="attributes" matInput required type="text">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>Add VP</mat-label>
|
||||
<input formControlName="addVP" matInput required type="number">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>VP Multiplier</mat-label>
|
||||
<input formControlName="vpMultiplier" matInput required type="number">
|
||||
</mat-form-field>
|
||||
<button [disabled]="!festaForm.valid" mat-flat-button type="submit">Submit</button>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {DivaFestaEditComponent} from './diva-festa-edit.component';
|
||||
|
||||
describe('DivaFestaEditComponent', () => {
|
||||
let component: DivaFestaEditComponent;
|
||||
let fixture: ComponentFixture<DivaFestaEditComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DivaFestaEditComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DivaFestaEditComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {ApiService} from '../../../../../api.service';
|
||||
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||
import {DivaFestaService} from '../diva-festa.service';
|
||||
import {Festa} from '../../../model/mannagement/Festa';
|
||||
import {Router} from '@angular/router';
|
||||
import {MessageService} from '../../../../../message.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-diva-festa-edit',
|
||||
templateUrl: './diva-festa-edit.component.html',
|
||||
styleUrls: ['./diva-festa-edit.component.css']
|
||||
})
|
||||
export class DivaFestaEditComponent implements OnInit {
|
||||
|
||||
festa: Festa;
|
||||
festaForm: FormGroup;
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private messageService: MessageService,
|
||||
private fb: FormBuilder,
|
||||
private festaService: DivaFestaService,
|
||||
private router: Router
|
||||
) {
|
||||
}
|
||||
|
||||
get f() {
|
||||
return this.festaForm.controls;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.festa = this.festaService.festa;
|
||||
this.festaForm = this.fb.group({
|
||||
id: [this.festa.id, Validators.required],
|
||||
name: [this.festa.name, Validators.required],
|
||||
enable: [this.festa.enable],
|
||||
kind: [String(this.festa.kind), Validators.required],
|
||||
difficulty: [String(this.festa.difficulty), Validators.required],
|
||||
pvList: [this.festa.pvList, Validators.required],
|
||||
attributes: [this.festa.attributes, Validators.required],
|
||||
addVP: [this.festa.addVP, Validators.required],
|
||||
vpMultiplier: [this.festa.vpMultiplier, Validators.required],
|
||||
start: [this.festa.start, Validators.required],
|
||||
end: [this.festa.end, Validators.required],
|
||||
createDate: [this.festa.createDate, Validators.required],
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.api.put('api/game/diva/manage/festa', this.festaForm.value).subscribe(
|
||||
data => {
|
||||
console.log(data);
|
||||
this.router.navigateByUrl('/diva/management/festa');
|
||||
},
|
||||
error => this.messageService.notice(error)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
mat-card-title {
|
||||
font-size: 1.2em;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
mat-card-title button {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<mat-card>
|
||||
<mat-card-title>
|
||||
<div>Festa List, Length: {{festas ? festas.length : 0}}</div>
|
||||
<button (click)="edit(undefined)" mat-flat-button>Add</button>
|
||||
</mat-card-title>
|
||||
</mat-card>
|
||||
<div *ngIf="festas">
|
||||
<mat-card *ngFor="let festa of festas">
|
||||
<mat-card-content>
|
||||
<table class="zebra">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<td>{{festa.id}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<td>{{festa.name}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>FestaKind</th>
|
||||
<td>{{festaKind[festa.kind]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Difficulty</th>
|
||||
<td>{{difficulty[festa.difficulty]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>PvList</th>
|
||||
<td>{{festa.pvList}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Attributes</th>
|
||||
<td>{{festa.attributes}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Add VP</th>
|
||||
<td>{{festa.addVP}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>VP Multiplier</th>
|
||||
<td>{{festa.vpMultiplier}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Start</th>
|
||||
<td>{{festa.start}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>End</th>
|
||||
<td>{{festa.end}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Create Date</th>
|
||||
<td>{{festa.createDate}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</mat-card-content>
|
||||
<mat-card-actions>
|
||||
<button (click)="edit(festa)" mat-flat-button>Edit</button>
|
||||
<button (click)="delete(festa.id)" mat-flat-button>Delete</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {DivaFestaComponent} from './diva-festa.component';
|
||||
|
||||
describe('DivaFestaComponent', () => {
|
||||
let component: DivaFestaComponent;
|
||||
let fixture: ComponentFixture<DivaFestaComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DivaFestaComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DivaFestaComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {ApiService} from '../../../../api.service';
|
||||
import {MessageService} from '../../../../message.service';
|
||||
import {Festa, FestaKind} from '../../model/mannagement/Festa';
|
||||
import {DivaFestaService} from './diva-festa.service';
|
||||
import {Router} from '@angular/router';
|
||||
import {Difficulty} from '../../model/DivaPvRecord';
|
||||
|
||||
@Component({
|
||||
selector: 'app-diva-festa',
|
||||
templateUrl: './diva-festa.component.html',
|
||||
styleUrls: ['./diva-festa.component.css']
|
||||
})
|
||||
export class DivaFestaComponent implements OnInit {
|
||||
|
||||
festas: Festa[];
|
||||
festaKind = FestaKind;
|
||||
difficulty = Difficulty;
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private messageService: MessageService,
|
||||
private festaService: DivaFestaService,
|
||||
private router: Router
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.load();
|
||||
}
|
||||
|
||||
load() {
|
||||
this.api.get('api/game/diva/manage/festa').subscribe(
|
||||
data => this.festas = data,
|
||||
error => this.messageService.notice(error)
|
||||
);
|
||||
}
|
||||
|
||||
delete(id) {
|
||||
this.api.delete('api/game/diva/manage/festa/' + id).subscribe(
|
||||
() => {
|
||||
this.messageService.notice('OK');
|
||||
this.load();
|
||||
},
|
||||
error => {
|
||||
this.messageService.notice(error.statusText);
|
||||
this.load();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
edit(festa) {
|
||||
this.festaService.festa = festa;
|
||||
this.router.navigateByUrl('/diva/management/festa/edit');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {Festa} from '../../model/mannagement/Festa';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DivaFestaService {
|
||||
|
||||
currentFesta: Festa = undefined;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
get festa() {
|
||||
return this.currentFesta === undefined ? {
|
||||
id: -1,
|
||||
enable: true,
|
||||
name: 'Untitled',
|
||||
kind: 0,
|
||||
difficulty: -1,
|
||||
pvList: 'ALL',
|
||||
attributes: '7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',
|
||||
addVP: 0,
|
||||
vpMultiplier: 1,
|
||||
start: new Date(),
|
||||
end: new Date(),
|
||||
createDate: new Date()
|
||||
} : this.currentFesta;
|
||||
}
|
||||
|
||||
set festa(f: Festa) {
|
||||
this.currentFesta = f;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<h2>Management</h2>
|
||||
<mat-nav-list>
|
||||
<mat-list-item routerLink="/diva/management/festa">
|
||||
<a matLine>Festa</a>
|
||||
</mat-list-item>
|
||||
<mat-list-item routerLink="/diva/management/contest">
|
||||
<a matLine>Contest</a>
|
||||
</mat-list-item>
|
||||
</mat-nav-list>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {DivaManagementComponent} from './diva-management.component';
|
||||
|
||||
describe('DivaManagementComponent', () => {
|
||||
let component: DivaManagementComponent;
|
||||
let fixture: ComponentFixture<DivaManagementComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DivaManagementComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DivaManagementComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-diva-management',
|
||||
templateUrl: './diva-management.component.html',
|
||||
styleUrls: ['./diva-management.component.css']
|
||||
})
|
||||
export class DivaManagementComponent implements OnInit {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
38
src/app/sega/diva/diva-music-db.service.ts
Normal file
38
src/app/sega/diva/diva-music-db.service.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {ApiService} from '../../api.service';
|
||||
import {DivaPv} from './model/DivaPv';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DivaMusicDbService {
|
||||
|
||||
musicDb: Map<number, DivaPv>;
|
||||
|
||||
constructor(private api: ApiService) {
|
||||
let db = localStorage.getItem('divaMusicDb');
|
||||
if (db == null) {
|
||||
this.api.get('api/game/diva/data/musicList').subscribe(
|
||||
data => {
|
||||
db = data;
|
||||
localStorage.setItem('divaMusicDb', JSON.stringify(db));
|
||||
this.musicDb = this.parse(JSON.parse(db));
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.musicDb = this.parse(JSON.parse(db));
|
||||
}
|
||||
}
|
||||
|
||||
getMusicDb(): Map<number, DivaPv> {
|
||||
return this.musicDb;
|
||||
}
|
||||
|
||||
private parse(db): Map<number, DivaPv> {
|
||||
const result: Map<number, DivaPv> = new Map();
|
||||
db.forEach(x => {
|
||||
result.set(x.pvId, x);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
23
src/app/sega/diva/diva-profile/diva-profile.component.html
Normal file
23
src/app/sega/diva/diva-profile/diva-profile.component.html
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<mat-card *ngIf="profile">
|
||||
<mat-card-title>Player Profile</mat-card-title>
|
||||
<mat-card-content>
|
||||
<table class="zebra">
|
||||
<tr>
|
||||
<th>Player Name</th>
|
||||
<td>{{profile.playerName}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Player Level</th>
|
||||
<td>{{profile.level}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Level Exp</th>
|
||||
<td>{{profile.levelExp}}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Player Title</th>
|
||||
<td>{{profile.levelTitle}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {DivaProfileComponent} from './diva-profile.component';
|
||||
|
||||
describe('DivaProfileComponent', () => {
|
||||
let component: DivaProfileComponent;
|
||||
let fixture: ComponentFixture<DivaProfileComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DivaProfileComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DivaProfileComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
33
src/app/sega/diva/diva-profile/diva-profile.component.ts
Normal file
33
src/app/sega/diva/diva-profile/diva-profile.component.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {ApiService} from '../../../api.service';
|
||||
import {DivaProfile} from '../model/DivaProfile';
|
||||
import {HttpParams} from '@angular/common/http';
|
||||
import {AuthenticationService} from '../../../auth/authentication.service';
|
||||
import {MessageService} from '../../../message.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-diva-profile',
|
||||
templateUrl: './diva-profile.component.html',
|
||||
styleUrls: ['./diva-profile.component.css']
|
||||
})
|
||||
export class DivaProfileComponent implements OnInit {
|
||||
|
||||
profile: DivaProfile;
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private auth: AuthenticationService,
|
||||
private messageService: MessageService
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const pdId = String(this.auth.currentUserValue.extId);
|
||||
const param = new HttpParams().set('pdId', pdId);
|
||||
this.api.get('api/game/diva/playerInfo', param).subscribe(
|
||||
data => this.profile = data,
|
||||
error => this.messageService.notice(error)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
mat-card-title {
|
||||
font-size: 1.2em;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.level {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.song-info .info {
|
||||
opacity: 70%;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.result-content .left {
|
||||
min-width: 30%;
|
||||
text-align: center;
|
||||
margin: 10px auto;
|
||||
}
|
||||
|
||||
.achievement-value {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.score-value {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<div *ngIf="pvRecords">
|
||||
<mat-card *ngFor="let item of pvRecords" routerLink="{{item.pvId}}">
|
||||
<mat-card-title>
|
||||
<div class="title">{{item.songInfo != null ? item.songInfo.song_name : 'pvId:' + item.pvId}}</div>
|
||||
<div class="level">
|
||||
{{item.edition === 1 ? edition[item.edition] : ''}}
|
||||
{{difficulty[item.difficulty]}}
|
||||
</div>
|
||||
</mat-card-title>
|
||||
<mat-card-content>
|
||||
<div class="song-info">
|
||||
<span
|
||||
class="info">{{item.songInfo != null ? 'Lyric: ' + item.songInfo.lyrics + ' Song: ' + item.songInfo.music : ''}}</span>
|
||||
</div>
|
||||
<div class="result-content">
|
||||
<div class="left">
|
||||
Achievement:<br>
|
||||
<span class="achievement-value">{{item.maxAttain|divaDecimal}}%</span>
|
||||
</div>
|
||||
<div class="left">
|
||||
Score:<br>
|
||||
<span class="score-value">{{item.maxScore}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<button (click)="load()" mat-flat-button>Load More</button>
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
|
||||
import {DivaPvRecordComponent} from './diva-pv-record.component';
|
||||
|
||||
describe('DivaPvRecordComponent', () => {
|
||||
let component: DivaPvRecordComponent;
|
||||
let fixture: ComponentFixture<DivaPvRecordComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DivaPvRecordComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DivaPvRecordComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
56
src/app/sega/diva/diva-pv-record/diva-pv-record.component.ts
Normal file
56
src/app/sega/diva/diva-pv-record/diva-pv-record.component.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {ApiService} from '../../../api.service';
|
||||
import {AuthenticationService} from '../../../auth/authentication.service';
|
||||
import {MessageService} from '../../../message.service';
|
||||
import {HttpParams} from '@angular/common/http';
|
||||
import {DivaMusicDbService} from '../diva-music-db.service';
|
||||
import {Difficulty, DivaPvRecord, Edition} from '../model/DivaPvRecord';
|
||||
|
||||
@Component({
|
||||
selector: 'app-diva-pv-record',
|
||||
templateUrl: './diva-pv-record.component.html',
|
||||
styleUrls: ['./diva-pv-record.component.css']
|
||||
})
|
||||
export class DivaPvRecordComponent implements OnInit {
|
||||
|
||||
edition = Edition;
|
||||
difficulty = Difficulty;
|
||||
|
||||
pvRecords: DivaPvRecord[] = [];
|
||||
|
||||
currentPage = 0;
|
||||
totalPages = 0;
|
||||
|
||||
constructor(
|
||||
private api: ApiService,
|
||||
private auth: AuthenticationService,
|
||||
private messageService: MessageService,
|
||||
private musicDb: DivaMusicDbService
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.load();
|
||||
}
|
||||
|
||||
load() {
|
||||
const pdId = String(this.auth.currentUserValue.extId);
|
||||
const param = new HttpParams().set('pdId', pdId).set('page', String(this.currentPage));
|
||||
this.api.get('api/game/diva/pvRecord', param).subscribe(
|
||||
data => {
|
||||
if (data.content.length === 0) {
|
||||
this.messageService.notice('No more record');
|
||||
return;
|
||||
}
|
||||
this.currentPage = data.page + 1;
|
||||
this.totalPages = data.totalPages;
|
||||
data.content.forEach(x => {
|
||||
x.songInfo = this.musicDb.getMusicDb().get(x.pvId);
|
||||
this.pvRecords.push(x);
|
||||
});
|
||||
},
|
||||
error => this.messageService.notice(error)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
30
src/app/sega/diva/diva-recent/diva-recent.component.css
Normal file
30
src/app/sega/diva/diva-recent/diva-recent.component.css
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
mat-card-title {
|
||||
font-size: 1.2em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.song-info .title {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.song-info .info {
|
||||
opacity: 70%;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.result-content .left {
|
||||
min-width: 30%;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.achievement-value {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.score-value {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
66
src/app/sega/diva/diva-recent/diva-recent.component.html
Normal file
66
src/app/sega/diva/diva-recent/diva-recent.component.html
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<div *ngIf="playLogList">
|
||||
<mat-card *ngFor="let item of playLogList" routerLink="/diva/record/{{item.pvId}}">
|
||||
<mat-card-title>{{item.dateTime}}</mat-card-title>
|
||||
<mat-card-content>
|
||||
<div class="song-info">
|
||||
<span class="title">{{item.songInfo != null ? item.songInfo.song_name : 'pvId:' + item.pvId}}</span><br>
|
||||
<span
|
||||
class="info">{{item.songInfo != null ? 'Lyric: ' + item.songInfo.lyrics + ' Song: ' + item.songInfo.music : ''}}</span>
|
||||
</div>
|
||||
<div class="result-content">
|
||||
<div class="left">
|
||||
Achievement:<br>
|
||||
<span class="achievement-value">{{item.attainPoint|divaDecimal}}%</span>
|
||||
<br>
|
||||
<br>Score:<br>
|
||||
<span class="score-value">{{item.score}}</span>
|
||||
</div>
|
||||
<table class="zebra">
|
||||
<tr>
|
||||
<th>Cool</th>
|
||||
<td>{{item.coolCount}}</td>
|
||||
<td>{{item.coolPercent|divaDecimal}}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Fine</th>
|
||||
<td>{{item.fineCount}}</td>
|
||||
<td>{{item.finePercent|divaDecimal}}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Safe</th>
|
||||
<td>{{item.safeCount}}</td>
|
||||
<td>{{item.safePercent|divaDecimal}}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Sad</th>
|
||||
<td>{{item.sadCount}}</td>
|
||||
<td>{{item.sadPercent|divaDecimal}}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Worst/Wrong</th>
|
||||
<td>{{item.wrongCount}}</td>
|
||||
<td>{{item.wrongPercent|divaDecimal}}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Combo</th>
|
||||
<td></td>
|
||||
<td>{{item.maxCombo}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Challenge Time</th>
|
||||
<td></td>
|
||||
<td>{{item.chanceTime}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Hold</th>
|
||||
<td></td>
|
||||
<td>{{item.holdScore}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<button (click)="load()" mat-flat-button>Load More</button>
|
||||
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user