Add Angular 7 app.

This commit is contained in:
2019-01-28 21:57:43 +01:00
parent 6d90526667
commit 083ea77769
65 changed files with 13580 additions and 0 deletions

13
src/main/ts-v7/.editorconfig Executable file
View File

@@ -0,0 +1,13 @@
# Editor configuration, see http://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

39
src/main/ts-v7/.gitignore vendored Executable file
View File

@@ -0,0 +1,39 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# dependencies
/node_modules
# 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
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db

140
src/main/ts-v7/angular.json Executable file
View File

@@ -0,0 +1,140 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"ts-v7": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {
"@schematics/angular:component": {
"styleext": "scss"
}
},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/ts-v7",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"node_modules/@fortawesome/fontawesome-free/scss/fontawesome.scss",
"node_modules/@fortawesome/fontawesome-free/scss/solid.scss",
"node_modules/@fortawesome/fontawesome-free/scss/regular.scss",
"node_modules/@fortawesome/fontawesome-free/scss/brands.scss",
"node_modules/angular-bootstrap-md/scss/bootstrap/bootstrap.scss",
"node_modules/angular-bootstrap-md/scss/mdb-free.scss",
"src/styles.scss"
],
"scripts": [
"node_modules/chart.js/dist/Chart.js",
"node_modules/hammerjs/hammer.min.js"
]
},
"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
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "ts-v7:build"
},
"configurations": {
"production": {
"browserTarget": "ts-v7:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "ts-v7:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [
"src/styles.scss"
],
"scripts": [],
"assets": [
"src/favicon.ico",
"src/assets"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"ts-v7-e2e": {
"root": "e2e/",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "ts-v7:serve"
},
"configurations": {
"production": {
"devServerTarget": "ts-v7:serve:production"
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "ts-v7"
}

View File

@@ -0,0 +1,28 @@
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
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.e2e.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

View File

@@ -0,0 +1,14 @@
import { AppPage } from './app.po';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getParagraphText()).toEqual('Welcome to ts-v7!');
});
});

View File

@@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get('/');
}
getParagraphText() {
return element(by.css('app-root h1')).getText();
}
}

View File

@@ -0,0 +1,13 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

11407
src/main/ts-v7/package-lock.json generated Executable file

File diff suppressed because it is too large Load Diff

54
src/main/ts-v7/package.json Executable file
View File

@@ -0,0 +1,54 @@
{
"name": "ts-v7",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxy.conf.json",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^7.2.2",
"@angular/common": "^7.2.2",
"@angular/compiler": "^7.2.2",
"@angular/core": "^7.2.2",
"@angular/forms": "^7.2.2",
"@angular/http": "^7.2.2",
"@angular/platform-browser": "^7.2.2",
"@angular/platform-browser-dynamic": "^7.2.2",
"@angular/router": "^7.2.2",
"@fortawesome/fontawesome-free": "^5.6.3",
"@types/chart.js": "^2.7.42",
"angular-bootstrap-md": "^7.3.0",
"chart.js": "^2.7.3",
"core-js": "^2.5.4",
"hammerjs": "^2.0.8",
"rxjs": "~6.3.3",
"tslib": "^1.9.0",
"zone.js": "~0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.12.0",
"@angular/cli": "~7.2.3",
"@angular/compiler-cli": "^7.2.2",
"@angular/language-service": "^7.2.2",
"@types/jasmine": "^2.8.16",
"@types/jasminewd2": "~2.0.3",
"@types/node": "~8.9.4",
"codelyzer": "~4.3.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~3.0.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.11.0",
"typescript": "~3.2.4"
}
}

6
src/main/ts-v7/proxy.conf.json Executable file
View File

@@ -0,0 +1,6 @@
{
"/api": {
"target": "http://localhost:8080",
"secure": false
}
}

View File

@@ -0,0 +1,5 @@
<app-header></app-header>
<main class="container">
<router-outlet></router-outlet>
</main>
<!-- <app-footer></app-footer> -->

View File

View File

@@ -0,0 +1,27 @@
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'ts-v7'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('ts-v7');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to ts-v7!');
}));
});

View File

@@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'codiki';
}

View File

@@ -0,0 +1,65 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { HttpClientModule } from '@angular/common/http';
import { RouterModule } from '@angular/router';
// Dependencies
import { MDBBootstrapModule } from 'angular-bootstrap-md';
// Router
import { appRoutes } from './app.routing';
// Components
import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
import { LoginComponent } from './login/login.component';
import { DisconnectionComponent } from './disconnection/disconnection.component';
import { HomeComponent } from './home/home.component';
import { MyPostsComponent } from './posts/myPosts/my-posts.component';
// Reusable components
import { PostCardComponent } from './core/post-card/post-card.component';
import { SpinnerComponent } from './core/directives/spinner/spinner.component';
// Services
import { HeaderService } from './header/header.service';
import { AuthService } from './core/services/auth.service';
import { HomeService } from './home/home.service';
import { LoginService } from './login/login.service';
import { MyPostsService } from './posts/myPosts/my-posts.service';
@NgModule({
declarations: [
AppComponent,
HeaderComponent,
LoginComponent,
DisconnectionComponent,
HomeComponent,
PostCardComponent,
SpinnerComponent,
MyPostsComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
HttpClientModule,
MDBBootstrapModule.forRoot(),
RouterModule.forRoot(
appRoutes,
// { enableTracing: true } // Enabling tracing
{ onSameUrlNavigation: 'reload' }
)
],
providers: [
HeaderService,
AuthService,
HomeService,
LoginService,
MyPostsService
],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@@ -0,0 +1,14 @@
import { Routes } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { HomeComponent } from './home/home.component';
import { DisconnectionComponent } from './disconnection/disconnection.component';
import { MyPostsComponent } from './posts/myPosts/my-posts.component';
export const appRoutes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'login', component: LoginComponent },
{ path: 'disconnection', component: DisconnectionComponent },
{ path: 'myPosts', component: MyPostsComponent},
{ path: '**', redirectTo: '/home' }
];

View File

@@ -0,0 +1,112 @@
.progress {
position: relative;
height: 4px;
display: block;
width: 100%;
background-color: #3F729B;
border-radius: 2px;
background-clip: padding-box;
margin: 0.5rem 0 1rem 0;
overflow: hidden;
-webkit-animation: random-background 5s infinite;
animation: random-background 5s infinite;transition: all 0.3s ease-out;
}
@keyframes random-background {
15% { background-color: #ff444480; }
30% { background-color: #ffbb3381; }
45% { background-color: rgba(0, 200, 80, 0.575); }
60% { background-color: #33b6e58c; }
75% { background-color: #aa66cc7c; }
}
.progress .indeterminate {
background-color: #1C2331;
-webkit-animation: random-bar 5s infinite;
animation: random-bar 5s infinite;transition: all 0.3s ease-out;
}
@keyframes random-bar {
15% { background-color: #ff4444; }
30% { background-color: #ffbb33; }
45% { background-color: #00C851; }
60% { background-color: #33b5e5; }
75% { background-color: #aa66cc; }
}
.progress .indeterminate:before {
content: '';
position: absolute;
background-color: inherit;
top: 0;
left: 0;
bottom: 0;
will-change: left, right;
-webkit-animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
}
.progress .indeterminate:after {
content: '';
position: absolute;
background-color: inherit;
top: 0;
left: 0;
bottom: 0;
will-change: left, right;
-webkit-animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
-webkit-animation-delay: 1.15s;
animation-delay: 1.15s;
}
@-webkit-keyframes indeterminate {
0% {
left: -35%;
right: 100%;
}
60% {
left: 100%;
right: -90%;
}
100% {
left: 100%;
right: -90%;
}
}
@keyframes indeterminate {
0% {
left: -35%;
right: 100%;
}
60% {
left: 100%;
right: -90%;
}
100% {
left: 100%;
right: -90%;
}
}
@-webkit-keyframes indeterminate-short {
0% {
left: -200%;
right: 100%;
}
60% {
left: 107%;
right: -8%;
}
100% {
left: 107%;
right: -8%;
}
}
@keyframes indeterminate-short {
0% {
left: -200%;
right: 100%;
}
60% {
left: 107%;
right: -8%;
}
100% {
left: 107%;
right: -8%;
}
}

View File

@@ -0,0 +1,12 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-progress-bar',
template: `
<div class="progress">
<div class="indeterminate"></div>
</div>
`,
styleUrls: ['./progress-bar.component.scss']
})
export class ProgressBarComponent {}

View File

@@ -0,0 +1,66 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
 @Component({
selector: 'app-search-bar',
template: `
<div id="search-bar">
<input id="search" name="search" type="text" [(ngModel)]="model" (keyup.enter)="search()" />
<label for="search" class="label-icon">
<i id="search-icon" class="fa fa-search" (click)="search()"></i>
</label>
</div>
`,
styles: [`
div#search-bar {
position: relative;
margin-right: 5px;
}
input#search, input#search:focus {
border-bottom: none;
}
input#search {
width: 400px;
height: 36px;
color: white;
background-color: #5c6bc0;
border-radius: 2px;
border-style: unset;
padding-left: 10px;
padding-right: 35px;
}
input#search:focus {
background: white;
color: #3f51b5;
}
i#search-icon {
font-size: 20px;
position: absolute;
right: 15px;
top: 8px;
color: #9e9e9e;
cursor: pointer;
}
`]
})
export class SearchBarComponent {
model: string;
constructor(
private router: Router
) {}
search(): void {
if (this.model) {
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.navigateByUrl(`/posts/search/${this.model}`).then(() => {
this.router.navigated = false;
this.router.navigate([this.router.url]);
});
}
}
}

View File

@@ -0,0 +1,84 @@
$green: #00C851;
$blue: #33b5e5;
$red: #ff4444;
$yellow: #ffbb33;
$white: #eee;
$width: 100px;
body {
background-color: $white;
}
.showbox {
padding: 5%;
}
.loader {
position: relative;
margin: 0 auto;
width: $width;
&:before {
content: '';
display: block;
padding-top: 100%;
}
}
.circular {
animation: rotate 2s linear infinite;
height: 100%;
transform-origin: center center;
width: 100%;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
.path {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
animation: dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite;
stroke-linecap: round;
}
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
@keyframes dash {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -35px;
}
100% {
stroke-dasharray: 89, 200;
stroke-dashoffset: -124px;
}
}
@keyframes color {
100%,
0% {
stroke: $red;
}
40% {
stroke: $blue;
}
66% {
stroke: $green;
}
80%,
90% {
stroke: $yellow;
}
}

View File

@@ -0,0 +1,18 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-spinner',
template: `
<div class="showbox">
<div class="loader">
<svg class="circular" viewBox="25 25 50 50">
<circle class="path" cx="50" cy="50" r="20" fill="none" stroke-width="2" stroke-miterlimit="10"/>
</svg>
</div>
</div>
`,
styleUrls: ['./spinner.component.scss']
})
export class SpinnerComponent {
}

View File

@@ -0,0 +1,75 @@
export class Role {
constructor(
public id: number,
public name: string
) { }
}
export class User {
constructor(
public key: string,
public name: string,
public email: string,
public password: string,
public image: string,
public inscriptionDate: Date,
public role: Role,
public token: string
) { }
}
export class Post {
constructor(
public key: string,
public title: string,
public text: string,
public description: string,
public image: string,
public creationDate: Date,
public author: User,
public category: Category
) { }
}
export class Category {
constructor(
public id: number,
public name: string,
public listSubCategories: Array<Category>
) { }
}
export class Image {
constructor(
public id: number,
public link: string
) { }
}
/**
* Class to send the new password to backoffice in order to change the user password.
*/
export class PasswordWrapper {
constructor(
public oldPassword: string,
public newPassword: string,
public confirmPassword: string
) { }
}
export class Version {
constructor(
public id: number,
public number: string,
public active: boolean
) { }
}
export class VersionRevision {
constructor(
public id: number,
public text: string,
public version: Version,
public bugfix: boolean
) { }
}

View File

@@ -0,0 +1,59 @@
import { Component, Input } from '@angular/core';
import { Post } from '../entities';
@Component({
selector: 'app-post-card',
template: `
<div class="card hoverable">
<div class="view hm-white-slight waves-light" mdbRippleRadius>
<img id="post-image" class="img-fluid" [src]="post.image" alt="Article" />
<a routerLink="/posts/{{post.key}}">
<div class="mask"></div>
</a>
</div>
<div class="card-body">
<h4 class="card-title">{{post.title}}</h4>
<p class="card-text">{{post.description}}</p>
</div>
<div class="card-data">
<img [src]="getAvatarUrl()"
class="author-img"
[alt]="post.author.name"
[mdbTooltip]="post.author.name"
placement="bottom"/>
Article écrit par {{post.author.name}}
<span class="creation-date-area">({{post.creationDate | date:'yyyy-MM-dd HH:mm:ss'}})</span>
</div>
</div>`,
styles: [`
div.card {
margin-bottom: 50px;
}
.card .card-data {
padding: 15px;
background-color: #f5f5f5;
}
.creation-date-area {
color: #bdbdbd;
font-style: italic;
}
.author-img {
width: 60px;
height: 60px;
border-radius: 50%;
margin-right: 15px;
}
#post-image {
width: 100%;
}
`]
})
export class PostCardComponent {
@Input() post: Post;
getAvatarUrl(): string {
return this.post.author.image
? `/api/images/loadAvatar/${this.post.author.image}`
: './assets/images/default_user.png';
}
}

View File

@@ -0,0 +1,32 @@
import { Injectable } from '@angular/core';
import { User } from '../entities';
const PARAM_USER = 'user';
@Injectable()
export class AuthService {
constructor() {}
public setAuthenticated(user: User): void {
this.setUser(user);
}
public setAnonymous(): void {
localStorage.clear();
}
public isAuthenticated(): boolean {
return this.getUser() != null;
}
public isAdmin(): boolean {
return false;
}
private setUser(user: User): void {
localStorage.setItem(PARAM_USER, JSON.stringify(user));
}
public getUser(): User {
return JSON.parse(localStorage.getItem(PARAM_USER));
}
}

View File

@@ -0,0 +1,29 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Http } from '@angular/http';
import { AuthService } from '../core/services/auth.service';
@Component({
selector: 'app-disconnection',
template: 'Déconnexion...'
})
export class DisconnectionComponent implements OnInit {
constructor(
private authService: AuthService,
private router: Router,
private http: Http
) {}
ngOnInit(): void {
this.http.get('/api/account/logout').subscribe(() => {
console.log('Logout success.');
this.authService.setAnonymous();
}, error => {
console.error('Error during logout from API.', error);
}, () => {
this.router.navigate(['/home']);
});
}
}

View File

@@ -0,0 +1,62 @@
<mdb-navbar SideClass="navbar fixed-top navbar-expand-lg navbar-dark indigo scrolling-navbar ie-nav" [containerInside]="false">
<logo>
<a class="logo navbar-brand" routerLink="/">
<img id="logo" src="./assets/images/codiki.png" alt="logo" />
<span id="title" *ngIf="!title">Codiki</span>
<span id="title" *ngIf="title">Codiki - {{title}}</span>
</a>
</logo>
<links>
<div class="navbar-nav ml-auto nav-flex-icons">
<!-- <app-search-bar></app-search-bar> -->
</div>
<ul class="navbar-nav ml-auto nav-flex-icons" style="margin-left: 0 !important;">
<li class="nav-item" *ngIf="!isAuthenticated()">
<a routerLink="/login" class="nav-link waves-light" mdbRippleRadius>
<i class="fa fa-sign-in"></i> Connexion
</a>
</li>
<li class="nav-item dropdown" dropdown *ngIf="isAuthenticated()">
<a dropdownToggle mdbRippleRadius type="button" class="nav-link dropdown-toggle waves-light" mdbRippleRadius>
<i class="fa fa-user-circle"></i> Mon Compte
</a>
<div *dropdownMenu class="dropdown-menu dropdown-menu-right dropdown dropdown-primary" role="menu">
<a class="dropdown-item waves-light" mdbRippleRadius routerLink="/myPosts">
<i class="fa fa-list-alt"></i> Mes articles
</a>
<a class="dropdown-item waves-light" mdbRippleRadius routerLink="/accountSettings">
<i class="fa fa-cog"></i> Paramètres
</a>
<a class="dropdown-item waves-light danger-color-dark" mdbRippleRadius routerLink="/disconnection">
<span class="white-text">
<i class="fa fa-sign-out-alt"></i> Déconnexion
</span>
</a>
</div>
</li>
</ul>
<a (click)="openSidebar()">
<i id="sidebarButton" class="fa fa-lg fa-bars white-text"></i>
</a>
</links>
</mdb-navbar>
<div id="sidenav" class="sidenav">
<h3>Catégories</h3>
<a class="closebtn" (click)="closeSidebar()">
<i class="fa fa-chevron-left"></i>
</a>
<div *ngFor="let category of listCategories">
<a [id]="'category-' + category.id" class="collapsible" *ngIf="category.listSubCategories.length" (click)="openCategoriesLinks(category)">
{{category.name}} <i class="fa fa-chevron-down pull-right"></i>
</a>
<div class="categoriesLinks" >
<a *ngFor="let subCategory of category.listSubCategories"
(click)="openPostsByCategory(subCategory.id)">
{{subCategory.name}}
</a>
</div>
</div>
</div>
<div id="overlay" (click)="closeSidebar()"></div>

View File

@@ -0,0 +1,87 @@
#title {
color: white;
font-weight: 400;
}
#logo {
width: 30px;
height: 30px;
margin-top: -7px;
vertical-align: middle;
margin-right: 10px;
}
#sidebarButton {
margin-left: 10px;
}
/* The side navigation menu */
.sidenav {
height: 100%; /* 100% Full-height */
width: 0; /* 0 width - change this with JavaScript */
position: fixed; /* Stay in place */
z-index: 1000; /* Stay on top */
top: 0; /* Stay at the top */
left: 0;
background-color: #3f51b5;
overflow-x: hidden; /* Disable horizontal scroll */
padding-top: 20px; /* Place content 60px from the top */
transition: 0.3s; /* 0.5 second transition effect to slide in the sidenav */
}
/* The navigation menu links */
.sidenav a {
padding: 8px 32px 8px 32px;
text-decoration: none;
color: white;
display: block;
transition: 0.3s;
}
/* When you mouse over the navigation links, change their color */
.sidenav a:hover {
color: #ccc;
background-color: #5c6bc0;
}
.sidenav h3 {
padding: 8px 8px 8px 32px;
color: white;
padding-bottom: 25px;
border-bottom: 1px solid #5c6bc0;
}
/* Position and style the close button (top right corner) */
.sidenav .closebtn {
position: absolute;
top: 25px;
right: 25px;
margin-left: 50px;
padding: 8px;
}
/* On smaller screens, where height is less than 450px, change the style of the sidenav (less padding and a smaller font size) */
@media screen and (max-height: 450px) {
.sidenav {padding-top: 15px;}
.sidenav a {font-size: 18px;}
}
#overlay {
position: fixed; /* Sit on top of the page content */
display: none; /* Hidden by default */
width: 100%; /* Full width (cover the whole page) */
height: 100%; /* Full height (cover the whole page) */
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0,0,0,0.5); /* Black background with opacity */
z-index: 999; /* Specify a stack order in case you're using a different order for other elements */
transition: 0.5s;
}
.categoriesLinks {
background-color: #303f9f;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
}

View File

@@ -0,0 +1,80 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../core/services/auth.service';
import { Category } from '../core/entities';
import { HeaderService } from './header.service';
import { environment } from '../../environments/environment';
const SIDENAV_WIDTH = '300px';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
isAdmin: boolean;
listCategories: Array<Category> = [];
title: string;
constructor(
private authService: AuthService,
private headerService: HeaderService,
private router: Router
) {}
ngOnInit() {
this.title = environment.title;
this.isAdmin = this.authService.isAdmin();
this.headerService.getAllCategories().subscribe(listCategories => {
this.listCategories = listCategories;
});
}
isAuthenticated(): boolean {
// console.log('Checking if user is connected... for header');
return this.authService.isAuthenticated();
}
openSidebar(): void {
document.getElementById('sidenav').style.width = SIDENAV_WIDTH;
document.getElementById('overlay').style.display = 'block';
}
closeSidebar(): void {
document.getElementById('sidenav').style.width = '0';
document.getElementById('overlay').style.display = 'none';
}
/**
* Redirect the user to the page to show the posts of the category which its id is in parameters.
*
* @param categoryId The id of the category which we need to show its posts after redirection.
*/
openPostsByCategory(categoryId: string): void {
this.closeSidebar();
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.navigateByUrl('/posts/byCategory/' + categoryId).then(() => {
this.router.navigated = false;
this.router.navigate([this.router.url]);
});
}
/**
* Opens the accordion which correspond to the category in parameters.
*
* @param category The category which its accodion needs to be open.
*/
openCategoriesLinks(category: Category): void {
const divCategoriesLinks = document.getElementById('category-' + category.id);
divCategoriesLinks.classList.toggle('active');
const divContent = divCategoriesLinks.nextElementSibling as HTMLElement;
if (divContent.style.maxHeight) {
divContent.style.maxHeight = null;
} else {
divContent.style.maxHeight = divContent.scrollHeight + 'px';
}
}
}

View File

@@ -0,0 +1,16 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Category } from '../core/entities';
@Injectable()
export class HeaderService {
constructor(
private http: HttpClient
) {}
getAllCategories(): Observable<Array<Category>> {
return this.http.get<Array<Category>>('/api/categories/');
}
}

View File

@@ -0,0 +1,7 @@
<div>
<h1>Derniers articles</h1>
<app-spinner *ngIf="!listArticle"></app-spinner>
<div *ngIf="listArticle" class="col-lg-8 offset-lg-2">
<app-post-card *ngFor="let post of listArticle" [post]="post"></app-post-card>
</div>
</div>

View File

@@ -0,0 +1,3 @@
div.card {
margin-bottom: 50px;
}

View File

@@ -0,0 +1,24 @@
import { Component, OnInit } from '@angular/core';
import { HomeService } from './home.service';
import { Post } from '../core/entities';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
listArticle: Array<Post>;
title: string;
constructor(
private homeService: HomeService
) {}
ngOnInit(): void {
this.homeService.getLastPosts().subscribe(lastPosts => {
this.listArticle = lastPosts;
});
}
}

View File

@@ -0,0 +1,14 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Post } from '../core/entities';
@Injectable()
export class HomeService {
constructor(private http: HttpClient) {}
getLastPosts(): Observable<Array<Post>> {
return this.http.get<Array<Post>>('/api/posts/last');
}
}

View File

@@ -0,0 +1,49 @@
<div class="card col-md-8 offset-md-2 col-lg-6 offset-lg-3">
<div class="card-body">
<h4 class="card-title">Connexion</h4>
<form id="form" (ngSubmit)="onSubmit()" #loginForm="ngForm">
<div class="md-form">
<i class="fa fa-envelope prefix grey-text"></i>
<input mdbInputDirective
id="email"
name="email"
type="email"
class="form-control"
[(ngModel)]="model.email"
#email="ngModel"
data-error="Veuillez saisir une adresse email valide"
[validateSuccess]="false"
required />
<label for="email">Email</label>
</div>
<div class="md-form">
<i class="fa fa-lock prefix grey-text"></i>
<input mdbInputDirective
id="password"
name="password"
type="password"
class="form-control"
[(ngModel)]="model.password"
#password="ngModel"
data-error="Veuillez saisir votre mot de passe"
[validateSuccess]="false"
required />
<label for="password">Mot de passe</label>
</div>
<div id="errorMsg" class="card red lighten-2 text-center z-depth-2">
<div class="card-body">
<p class="white-text mb-0">{{loginError}}</p>
</div>
</div>
<div class="col submitFormArea">
<a routerLink="/signin" class="indigo-text">
Je n'ai pas de compte
</a>
<button class="float-right waves-effect waves-light indigo btn"
type="submit" [disabled]="!loginForm.form.valid">
Suivant
</button>
</div>
</form>
</div>
</div>

View File

@@ -0,0 +1,66 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { User } from '../core/entities';
import { LoginService } from './login.service';
import { AuthService } from '../core/services/auth.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styles: [`
#form {
padding-bottom: 10px;
}
.submitFormArea {
line-height: 50px;
}
#errorMsg {
max-height: 0;
overflow: hidden;
transition: max-height 0.5s ease-out;
margin: 0;
}
`]
})
export class LoginComponent {
model: User = new User('', '', '', '', '', null, null, '');
loginError: string;
constructor(
private router: Router,
private loginService: LoginService,
private authService: AuthService
) {}
onSubmit(): void {
this.loginService.login(this.model).subscribe((pUser: User) => {
console.log('Login success.');
this.authService.setAuthenticated(pUser);
this.router.navigate(['/home']);
}, (error) => {
if (error.status === 401) {
console.log('Login attempt failed.');
this.setMessage('Adresse email ou mot de passe incorrect.');
} else {
console.error('Error during login attempt.', error);
this.setMessage('Une erreur est survenue lors de la connexion.');
}
});
}
setMessage(message: string): void {
this.loginError = message;
const resultMsgDiv = document.getElementById('errorMsg');
resultMsgDiv.style.maxHeight = '64px';
setTimeout(() => {
resultMsgDiv.style.maxHeight = '0px';
setTimeout(() => {
this.loginError = undefined;
}, 550);
}, 3000);
}
}

View File

@@ -0,0 +1,16 @@
import { Injectable } from '@angular/core';
import { User } from '../core/entities';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
@Injectable()
export class LoginService {
constructor(
private http: HttpClient
) {}
public login(user: User): Observable<User> {
return this.http.post<User>('/api/account/login', user);
}
}

View File

@@ -0,0 +1,11 @@
<div>
<h1>Mes articles</h1>
<app-spinner *ngIf="!listPosts"></app-spinner>
<div *ngIf="listPosts" class="col-lg-8 offset-lg-2">
<app-post-card *ngFor="let post of listPosts" [post]="post"></app-post-card>
</div>
<span *ngIf="listPosts?.length === 0">
Aucun article.
</span>
<a routerLink="/posts/new" class="fixed-action-btn green white-text">+</a>
</div>

View File

@@ -0,0 +1,20 @@
$btnSize: 55px;
.fixed-action-btn {
display: block;
position: fixed;
bottom: 23px;
right: 23px;
z-index: 997;
width: $btnSize;
height: $btnSize;
border-radius: 50%;
line-height: $btnSize;
text-align: center;
font-size: 30px;
box-shadow: 0 5px 11px 0 rgba(0,0,0,.18), 0 4px 15px 0 rgba(0,0,0,.15);
transition: box-shadow 0.3s ease-in-out;
}
.fixed-action-btn:hover {
box-shadow: 0 8px 17px 0 rgba(0,0,0,.2), 0 6px 20px 0 rgba(0,0,0,.19);
}

View File

@@ -0,0 +1,23 @@
import { Component, OnInit } from '@angular/core';
import { Post } from '../../core/entities';
import { MyPostsService } from './my-posts.service';
@Component({
selector: 'app-my-posts',
templateUrl: './my-posts.component.html',
styleUrls: ['./my-posts.component.scss']
})
export class MyPostsComponent implements OnInit {
listPosts: Array<Post>;
constructor(
private myPostsService: MyPostsService
) {}
ngOnInit(): void {
this.myPostsService.getMyPosts().subscribe(listPosts => {
this.listPosts = listPosts;
});
}
}

View File

@@ -0,0 +1,15 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { Post } from '../../core/entities';
@Injectable()
export class MyPostsService {
constructor(private http: HttpClient) {}
getMyPosts(): Observable<Array<Post>> {
return this.http.get<Array<Post>>(`/api/posts/myPosts`);
}
}

View File

@@ -0,0 +1,271 @@
/* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript+apacheconf+c+bash+batch+cpp+csharp+ruby+css-extras+django+docker+git+http+ini+java+json+latex+less+lua+makefile+markdown+nginx+php+php-extras+powershell+properties+python+jsx+rest+rust+sass+scss+scala+sql+typescript+vim+wiki+yaml&plugins=line-numbers+autolinker+file-highlight+toolbar+unescaped-markup+command-line+show-language+copy-to-clipboard */
/**
* okaidia theme for JavaScript, CSS and HTML
* Loosely based on Monokai textmate theme by http://www.monokai.nl/
* @author ocodia
*/
code[class*="language-"],
pre[class*="language-"] {
color: #f8f8f2;
background: none;
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
border-radius: 0.3em;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #272822;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #f8f8f2;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.constant,
.token.symbol,
.token.deleted {
color: #f92672;
}
.token.boolean,
.token.number {
color: #ae81ff;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #a6e22e;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: #f8f8f2;
}
.token.atrule,
.token.attr-value,
.token.function {
color: #e6db74;
}
.token.keyword {
color: #66d9ef;
}
.token.regex,
.token.important {
color: #fd971f;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
pre.line-numbers {
position: relative;
padding-left: 3.8em;
counter-reset: linenumber;
}
pre.line-numbers > code {
position: relative;
white-space: inherit;
}
.line-numbers .line-numbers-rows {
position: absolute;
pointer-events: none;
top: 0;
font-size: 100%;
left: -3.8em;
width: 3em; /* works for line-numbers below 1000 lines */
letter-spacing: -1px;
border-right: 1px solid #999;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.line-numbers-rows > span {
pointer-events: none;
display: block;
counter-increment: linenumber;
}
.line-numbers-rows > span:before {
content: counter(linenumber);
color: #999;
display: block;
padding-right: 0.8em;
text-align: right;
}
.token a {
color: inherit;
}
pre.code-toolbar {
position: relative;
}
pre.code-toolbar > .toolbar {
position: absolute;
top: .3em;
right: .2em;
transition: opacity 0.3s ease-in-out;
opacity: 0;
}
pre.code-toolbar:hover > .toolbar {
opacity: 1;
}
pre.code-toolbar > .toolbar .toolbar-item {
display: inline-block;
}
pre.code-toolbar > .toolbar a {
cursor: pointer;
}
pre.code-toolbar > .toolbar button {
background: none;
border: 0;
color: inherit;
font: inherit;
line-height: normal;
overflow: visible;
padding: 0;
-webkit-user-select: none; /* for button */
-moz-user-select: none;
-ms-user-select: none;
}
pre.code-toolbar > .toolbar a,
pre.code-toolbar > .toolbar button,
pre.code-toolbar > .toolbar span {
color: #bbb;
font-size: .8em;
padding: 0 .5em;
background: #f5f2f0;
background: rgba(224, 224, 224, 0.2);
box-shadow: 0 2px 0 0 rgba(0,0,0,0.2);
border-radius: .5em;
}
pre.code-toolbar > .toolbar a:hover,
pre.code-toolbar > .toolbar a:focus,
pre.code-toolbar > .toolbar button:hover,
pre.code-toolbar > .toolbar button:focus,
pre.code-toolbar > .toolbar span:hover,
pre.code-toolbar > .toolbar span:focus {
color: inherit;
text-decoration: none;
}
/* Fallback, in case JS does not run, to ensure the code is at least visible */
.lang-markup script[type='text/plain'],
.language-markup script[type='text/plain'],
script[type='text/plain'].lang-markup,
script[type='text/plain'].language-markup {
display: block;
font: 100% Consolas, Monaco, monospace;
white-space: pre;
overflow: auto;
}
.command-line-prompt {
border-right: 1px solid #999;
display: block;
float: left;
font-size: 100%;
letter-spacing: -1px;
margin-right: 1em;
pointer-events: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.command-line-prompt > span:before {
color: #999;
content: ' ';
display: block;
padding-right: 0.8em;
}
.command-line-prompt > span[data-user]:before {
content: "[" attr(data-user) "@" attr(data-host) "] $";
}
.command-line-prompt > span[data-user="root"]:before {
content: "[" attr(data-user) "@" attr(data-host) "] #";
}
.command-line-prompt > span[data-prompt]:before {
content: attr(data-prompt);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because one or more lines are too long

11
src/main/ts-v7/src/browserslist Executable file
View File

@@ -0,0 +1,11 @@
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
#
# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11

View File

@@ -0,0 +1,3 @@
export const environment = {
production: true
};

View File

@@ -0,0 +1,18 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
appVersion: '1.0.1',
title: 'Développement'
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

17
src/main/ts-v7/src/index.html Executable file
View File

@@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<title>Codiki</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<base href="/" />
<link rel="icon" type="image/x-icon" href="assets/images/favicon.png" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
<link href="./assets/css/prism.css" rel="stylesheet" />
</head>
<body>
<app-root></app-root>
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.5.8/clipboard.min.js"></script>
<!-- <script type="text/javascript" src="./assets/js/prism.js"></script> -->
</body>
</html>

View File

@@ -0,0 +1,31 @@
// 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'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};

13
src/main/ts-v7/src/main.ts Executable file
View File

@@ -0,0 +1,13 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

80
src/main/ts-v7/src/polyfills.ts Executable file
View File

@@ -0,0 +1,80 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/array';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';
/** Evergreen browsers require these. **/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
**/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
*/
// (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
// (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
// (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
/*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*/
// (window as any).__Zone_enable_cross_context_check = true;
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

38
src/main/ts-v7/src/styles.scss Executable file
View File

@@ -0,0 +1,38 @@
/* You can add global styles to this file, and also import other style files */
html {
height: 100%;
}
body {
// 64px for the header and 15px for a litle margin.
padding-top: 90px;
padding-bottom: 30px;
display: flex;
position: relative;
flex-direction: column;
justify-content:space-between;
min-height: 100%;
}
/* ***** Floating card button ***** */
.btn-card-floating {
border-radius: 50%;
width: 47px;
height: 47px;
margin: -23px 20px;
margin-left: auto;
line-height: 0;
display: inline-block;
text-align: center;
line-height: 47px;
padding-left: 2px;
z-index: 1;
box-shadow: 0 5px 11px 0 rgba(0,0,0,.18), 0 4px 15px 0 rgba(0,0,0,.15);
background-color: #3f51b5;
transition: box-shadow 0.3s ease-in-out;
}
.btn-card-floating:hover {
box-shadow: 0 8px 17px 0 rgba(0,0,0,.2), 0 6px 20px 0 rgba(0,0,0,.19);
}
/* ***** End of floating card button ***** */

20
src/main/ts-v7/src/test.ts Executable file
View File

@@ -0,0 +1,20 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@@ -0,0 +1,11 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": []
},
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}

View File

@@ -0,0 +1,18 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"test.ts",
"polyfills.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

17
src/main/ts-v7/src/tslint.json Executable file
View File

@@ -0,0 +1,17 @@
{
"extends": "../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
]
}
}

23
src/main/ts-v7/tsconfig.json Executable file
View File

@@ -0,0 +1,23 @@
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"importHelpers": true,
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"module": "es2015",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowJs": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
}
}

132
src/main/ts-v7/tslint.json Executable file
View File

@@ -0,0 +1,132 @@
{
"rulesDirectory": [
"node_modules/codelyzer"
],
"rules": {
"arrow-return-shorthand": true,
"callable-types": true,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"deprecation": {
"severity": "warn"
},
"eofline": true,
"forin": true,
"import-blacklist": [
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": [
true,
"tabs",
2
],
"interface-over-type-literal": true,
"label-position": true,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-super": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"prefer-const": true,
"quotemark": [
true,
"single"
],
"radix": true,
"semicolon": [
true,
"always"
],
"triple-equals": [
true,
"allow-null-check"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"unified-signatures": true,
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type"
],
"no-output-on-prefix": true,
"use-input-property-decorator": true,
"use-output-property-decorator": true,
"use-host-property-decorator": true,
"no-input-rename": true,
"no-output-rename": true,
"use-life-cycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"directive-class-suffix": true
}
}