Project is created

This commit is contained in:
병준 박 2019-04-14 16:29:05 +09:00
commit b2dd82ef08
1024 changed files with 32072 additions and 0 deletions

13
.editorconfig Executable file
View 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 Executable file
View File

@ -0,0 +1,46 @@
# 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
yarn\.lock
.angulardoc.json
/dev
desktop.ini
\.mongod/

15
.vscode/settings.json vendored Executable file
View File

@ -0,0 +1,15 @@
{
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.autoClosingBrackets": true,
"editor.trimAutoWhitespace": true,
"files.trimTrailingWhitespace": true,
"files.trimFinalNewlines": true,
"git.ignoreLimitWarning": true,
"prettier.singleQuote": true,
"debug.node.autoAttach": "on"
}

28
README.md Executable file
View File

@ -0,0 +1,28 @@
# AngularUniversal
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.0.6.
## 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).
`

166
angular.json Executable file
View File

@ -0,0 +1,166 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"2nd-round": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {
"@schematics/angular:component": {
"styleext": "scss"
}
},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/2nd-round",
"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/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss"
],
"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"
}
]
},
"hmr": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.hmr.ts"
}
]
},
"test": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.test.ts"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "2nd-round:build"
},
"configurations": {
"production": {
"browserTarget": "2nd-round:build:production"
},
"hmr": {
"hmrWarning": false,
"hmr": true,
"browserTarget": "2nd-round:build:hmr"
},
"test": {
"hmrWarning": false,
"hmr": true,
"browserTarget": "2nd-round:build:test"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "2nd-round: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/**"
]
}
}
}
},
"2nd-round-e2e": {
"root": "e2e/",
"projectType": "application",
"prefix": "",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "2nd-round:serve"
},
"configurations": {
"production": {
"devServerTarget": "2nd-round:serve:production"
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "e2e/tsconfig.e2e.json",
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "2nd-round"
}

28
e2e/protractor.conf.js Executable file
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 } }));
}
};

14
e2e/src/app.e2e-spec.ts Executable file
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.getTitleText()).toEqual('Welcome to 2nd-round!');
});
});

11
e2e/src/app.po.ts Executable file
View File

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

13
e2e/tsconfig.e2e.json Executable file
View File

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

15
nodemon.json Executable file
View File

@ -0,0 +1,15 @@
{
"ignore": [
"**/client/**",
"**/*.test.ts",
"**/*.spec.ts",
".git",
"node_modules"
],
"watch": [
"src/server",
"src/shared"
],
"exec": "yarn start:server",
"ext": "ts"
}

138
package.json Executable file
View File

@ -0,0 +1,138 @@
{
"name": "2nd-round",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "npm-run-all -p start:server start:client:hmr",
"start:client": "ng serve",
"start:client:hmr": "ng serve --configuration hmr",
"start:client:test": "ng serve --configuration=test --host 0.0.0.0 --port 4200",
"start:server": "cross-env TS_NODE_PROJECT=\"src/tsconfig.server.json\" node --inspect -r ts-node/register ./src/server/main.ts",
"start:server:nodemon": "nodemon",
"build": "ng build",
"build:server": "webpack --config webpack.server.config.js --progress --colors",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~7.0.4",
"@angular/cdk": "^7.1.0",
"@angular/cdk-experimental": "^7.2.0",
"@angular/common": "~7.0.4",
"@angular/compiler": "~7.0.4",
"@angular/core": "~7.0.4",
"@angular/flex-layout": "^7.0.0-beta.19",
"@angular/forms": "~7.0.4",
"@angular/http": "~7.0.4",
"@angular/material": "^7.1.0",
"@angular/platform-browser": "~7.0.4",
"@angular/platform-browser-dynamic": "~7.0.4",
"@angular/router": "~7.0.4",
"@ngrx/effects": "^6.1.2",
"@ngrx/router-store": "^6.1.2",
"@ngrx/schematics": "^6.1.2",
"@ngrx/store": "^6.1.2",
"@ngrx/store-devtools": "^6.1.2",
"@ngx-translate/core": "^11.0.1",
"@ngx-translate/http-loader": "^4.0.0",
"@tsed/common": "^5.0.8",
"@tsed/core": "^5.0.8",
"@tsed/di": "^5.0.8",
"@tsed/mongoose": "^5.0.8",
"@tsed/multipartfiles": "^5.0.8",
"@tsed/swagger": "^5.0.8",
"@tsed/testing": "^5.0.8",
"@tsed/typeorm": "^5.0.8",
"axios": "^0.18.0",
"body-parser": "^1.18.3",
"compression": "^1.7.3",
"cookie-parser": "^1.4.3",
"core-js": "^2.5.4",
"cors": "^2.8.5",
"cropperjs": "^1.4.3",
"cross-env": "^5.2.0",
"express": "^4.16.4",
"fs-extra": "^7.0.1",
"gifshot": "^0.4.5",
"hammerjs": "^2.0.8",
"jimp": "^0.6.0",
"jsonwebtoken": "^8.4.0",
"method-override": "^3.0.0",
"mongodb": "^3.1.10",
"mongoose": "^5.4.4",
"ms": "^2.1.1",
"multer": "^1.4.1",
"ngx-clipboard": "^11.1.9",
"ngx-cookie-service": "^2.0.2",
"ngx-hm-carousel": "^1.4.0",
"ngx-image-cropper": "^1.3.7",
"ngx-virtual-scroller": "^1.0.16",
"oneall": "^0.1.5",
"passport": "^0.4.0",
"passport-jwt": "^4.0.0",
"passport-kakao": "^0.0.5",
"passport-local": "^1.0.0",
"passport-naver": "^1.0.6",
"reflect-metadata": "^0.1.12",
"rxjs": "~6.3.3",
"short-uuid": "^3.1.0",
"typeorm": "^0.2.9",
"uuid": "^3.3.2",
"zone.js": "~0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.10.0",
"@angular/cli": "~7.0.6",
"@angular/compiler-cli": "~7.0.4",
"@angular/language-service": "~7.0.4",
"@angularclass/hmr": "^2.1.3",
"@types/bcrypt": "^3.0.0",
"@types/cropperjs": "^1.1.4",
"@types/express": "^4.16.0",
"@types/fs-extra": "^5.0.4",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"@types/jimp": "^0.2.28",
"@types/jsonwebtoken": "^8.3.0",
"@types/mongoose": "^5.3.7",
"@types/ms": "^0.7.30",
"@types/multer": "^1.3.7",
"@types/node": "~8.9.4",
"@types/passport": "^0.4.7",
"@types/passport-jwt": "^3.0.1",
"@types/passport-kakao": "^0.2.0",
"@types/passport-local": "^1.0.33",
"@types/passport-naver": "^0.2.0",
"@types/swagger-schema-official": "^2.0.13",
"@types/unzipper": "^0.9.1",
"@types/uuid": "^3.4.4",
"@types/webpack": "^4.4.19",
"awesome-typescript-loader": "^5.2.1",
"codelyzer": "~4.5.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"json-loader": "^0.5.7",
"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",
"mocha": "^5.2.0",
"mongodb-memory-server": "^2.9.1",
"nodemon": "^1.18.7",
"npm-run-all": "^4.1.3",
"protractor": "~5.4.0",
"resize-observer-polyfill": "^1.5.1",
"sqlite3": "^4.0.4",
"ts-node": "^7.0.1",
"tslint": "~5.11.0",
"typescript": "~3.1.6",
"unzipper": "^0.9.8",
"webpack-cli": "^3.1.2"
},
"resolutions": {
"**/event-stream": "^4.0.1"
}
}

76
src/app/app-provider.module.ts Executable file
View File

@ -0,0 +1,76 @@
/**
* : app-provider.module.ts
* 작성일자: 2018-12-23
* : 박병준
* : app provider를 .
* :
* :
* :
* :
*/
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { CookieService } from 'ngx-cookie-service';
import { ClipboardModule, ClipboardService } from 'ngx-clipboard';
import { AccountsModule } from '../modules/accounts/accounts.module';
import { ArticleModule } from '../modules/article/article.module';
import { AttachmentsModule } from '../modules/attachments/attachments.module';
import { CartoonsModule } from '../modules/cartoons/cartoons.module';
import { ContentsModule } from '../modules/contents/contents.module';
import { IllustrationsModule } from '../modules/illustrations/illustrations.module';
import { NovelModule } from '../modules/novel/novel.module';
import { TagModule } from '../modules/tag/tag.module';
import { UserModule } from '../modules/user/user.module';
import { UserAnalysisModule } from '../modules/user-analysis/user-analysis.module';
import { UserSupportModule } from '../modules/user-support/user-support.module';
import { MetaModule } from '../modules/meta/meta.module';
import { AuthHttpInterceptor } from './service/auth.http.interceptor';
import { AppService } from './service/app.service';
import { GUARDS } from './guard';
export function initApp(appService: AppService) {
return () => appService.initApp();
}
@NgModule({
imports: [
AccountsModule.forRoot(),
ArticleModule.forRoot(),
AttachmentsModule.forRoot(),
CartoonsModule.forRoot(),
ContentsModule.forRoot(),
IllustrationsModule.forRoot(),
NovelModule.forRoot(),
TagModule.forRoot(),
UserModule.forRoot(),
UserAnalysisModule.forRoot(),
UserSupportModule.forRoot(),
MetaModule.forRoot(),
ClipboardModule
],
exports: [],
providers: [
AppService,
...GUARDS,
CookieService,
ClipboardService,
{
provide: APP_INITIALIZER,
useFactory: initApp,
deps: [AppService],
multi: true
},
{ provide: HTTP_INTERCEPTORS, useClass: AuthHttpInterceptor, multi: true },
{ provide: 'virtualScroller.scrollThrottlingTime', useValue: 0 },
{ provide: 'virtualScroller.scrollDebounceTime', useValue: 0 },
{ provide: 'virtualScroller.scrollAnimationTime', useValue: 750 },
{ provide: 'virtualScroller.scrollbarWidth', useValue: undefined },
{ provide: 'virtualScroller.scrollbarHeight', useValue: undefined },
{ provide: 'virtualScroller.checkResizeInterval', useValue: 1000 },
{ provide: 'virtualScroller.resizeBypassRefreshThreshold', useValue: 5 }
]
})
export class AppProviderModule { }

41
src/app/app-routing.module.ts Executable file
View File

@ -0,0 +1,41 @@
/**
* : app-routing.module.ts
* 작성일자: 2018-12-17
* : 박병준
* : app routing을 .
* :
* :
* :
* :
*/
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [
{
path: '',
loadChildren: './pages/main/main.page.module#MainPageModule'
},
{
path: 'accounts',
loadChildren: './pages/accounts/accounts.page.module#AccountsPageModule'
},
{
path: 'user',
loadChildren: './pages/user/user.page.module#UserPageModule'
},
{
path: 'article',
loadChildren: './pages/article/article.page.module#ArticlePageModule'
},
{
path: 'admin',
loadChildren: './pages/admin/admin.page.module#AdminPageModule'
}
];
@NgModule({
imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload' })],
exports: [RouterModule]
})
export class AppRoutingModule { }

59
src/app/app-store.module.ts Executable file
View File

@ -0,0 +1,59 @@
/**
* : app-store.module.ts
* 작성일자: 2018-12-17
* : 박병준
* : app ngrx를 .
* :
* :
* :
* :
*/
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import {
StoreRouterConnectingModule,
} from '@ngrx/router-store';
import { EffectsModule } from '@ngrx/effects';
import { environment } from '../environments/environment';
import { REDUCERS, META_REDUCERS, EFFECTS } from './store';
@NgModule({
exports: [
StoreModule,
],
imports: [
StoreModule.forRoot(REDUCERS, { metaReducers: META_REDUCERS }),
/**
* @ngrx/router-store keeps router state up-to-date in the store.
*/
StoreRouterConnectingModule.forRoot({
/*
They stateKey defines the name of the state used by the router-store reducer.
This matches the key defined in the map of reducers
*/
stateKey: 'router',
}),
/**
* Store devtools instrument the store retaining past versions of state
* and recalculating new states. This enables powerful time-travel
* debugging.
*
* To use the debugger, install the Redux Devtools extension for either
* Chrome or Firefox
*
* See: https://github.com/zalmoxisus/redux-devtools-extension
*/
StoreDevtoolsModule.instrument({
name: 'WebApp DevTools',
maxAge: 50,
logOnly: environment.production,
}),
EffectsModule.forRoot(EFFECTS),
],
providers: [
],
})
export class AppStoreModule { }

36
src/app/app-translate.module.ts Executable file
View File

@ -0,0 +1,36 @@
/**
* : app-tanslate.module.ts
* 작성일자: 2018-12-17
* : 박병준
* : app i18n을 .
* :
* :
* :
* :
*/
import { NgModule } from '@angular/core';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { HttpClient } from '@angular/common/http';
// AoT requires an exported function for factories
export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, '../assets/i18n/', '.json');
}
@NgModule({
imports: [
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [HttpClient]
}
}),
],
exports: [
],
providers: [
]
})
export class AppTranslateModule { }

1
src/app/app.component.html Executable file
View File

@ -0,0 +1 @@
<router-outlet></router-outlet>

29
src/app/app.component.scss Executable file
View File

@ -0,0 +1,29 @@
app-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 2;
}
pp-root>app-component-sidenav {
flex: 1;
}
app-root>app-homepage,
app-root>app-guides,
app-root>guide-viewer {
overflow-y: auto;
}
@media (max-width: 720px) {
app-root {
top: 92px;
}
app-root>app-homepage,
app-root>app-guides,
app-root>guide-viewer {
overflow-y: visible;
}
}

35
src/app/app.component.spec.ts Executable file
View File

@ -0,0 +1,35 @@
import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
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 '2nd-round'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('2nd-round');
});
it('should render title in a h1 tag', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to 2nd-round!');
});
});

320
src/app/app.component.ts Executable file
View File

@ -0,0 +1,320 @@
/**
* : app.component.ts
* 작성일자: 2018-12-17
* : 박병준
* : app component를 .
* 수정일시: 2010-01-02
* : 조현정
* 수정내용: SVG ICONS
* :
*/
import { Component, OnInit } from '@angular/core';
// Mat-Icons
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
constructor(iconRegistry: MatIconRegistry, sanitizer: DomSanitizer) {
// Mat - Icons: ToolBar;
iconRegistry.addSvgIcon(
'home-outline',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/toolbar-icons/bot_home.svg'
)
);
iconRegistry.addSvgIcon(
'search',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/toolbar-icons/bot_search.svg'
)
);
iconRegistry.addSvgIcon(
'upload',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/toolbar-icons/bot_upload.svg'
)
);
iconRegistry.addSvgIcon(
'bookmark',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/toolbar-icons/bot_bookmark.svg'
)
);
iconRegistry.addSvgIcon(
'myprofil',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/toolbar-icons/bot_my.svg'
)
);
// Mat - Icons: General Icons
iconRegistry.addSvgIcon(
'file-delete-circle',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/close-circle.svg'
)
);
iconRegistry.addSvgIcon(
'btn_bookmark',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/btn_bookmark.svg'
)
);
iconRegistry.addSvgIcon(
'btn_bookmark_checked',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/bookmark-check.svg'
)
);
iconRegistry.addSvgIcon(
'btn_close',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/btn_close.svg')
);
iconRegistry.addSvgIcon(
'btn_more',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/btn_more.svg')
);
iconRegistry.addSvgIcon(
'btn_settings',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/btn_settings.svg')
);
iconRegistry.addSvgIcon(
'btn_share',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/btn_share.svg')
);
iconRegistry.addSvgIcon(
'btn_tracing',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/btn_tracing.svg'
)
);
iconRegistry.addSvgIcon(
'btn_reply',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/ico_01.svg')
);
iconRegistry.addSvgIcon(
'btn_like',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/ico_02.svg')
);
iconRegistry.addSvgIcon(
'btn_like_checked',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/cards-heart.svg')
);
iconRegistry.addSvgIcon(
'btn_views',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/ico_03.svg')
);
iconRegistry.addSvgIcon(
'btn_camera',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/btn_camera.svg'
)
);
iconRegistry.addSvgIcon(
'top_recommend',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/top_recommend.svg'
)
);
iconRegistry.addSvgIcon(
'top_upload',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/top_upload.svg'
)
);
iconRegistry.addSvgIcon(
'tab_mypro_01',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/my_ico_01.svg')
);
iconRegistry.addSvgIcon(
'tab_mypro_02',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/my_ico_02.svg')
);
iconRegistry.addSvgIcon(
'tab_mypro_03',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/my_ico_03.svg')
);
iconRegistry.addSvgIcon(
'tab_mypro_04',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/my_ico_04.svg')
);
iconRegistry.addSvgIcon(
'tab_write_01',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/btn_illustration.svg'
)
);
iconRegistry.addSvgIcon(
'tab_write_02',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/btn_cartoon.svg'
)
);
iconRegistry.addSvgIcon(
'tab_write_03',
sanitizer.bypassSecurityTrustResourceUrl('assets/img/icons/btn_novel.svg')
);
iconRegistry.addSvgIcon(
'ico_set_01',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/ico_set_01.svg'
)
);
iconRegistry.addSvgIcon(
'ico_set_02',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/ico_set_02.svg'
)
);
iconRegistry.addSvgIcon(
'ico_set_03',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/ico_set_03.svg'
)
);
iconRegistry.addSvgIcon(
'ico_set_04',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/ico_set_04.svg'
)
);
iconRegistry.addSvgIcon(
'ico_set_05',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/ico_set_05.svg'
)
);
iconRegistry.addSvgIcon(
'ico_set_06',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/ico_set_06.svg'
)
);
iconRegistry.addSvgIcon(
'ico_set_07',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/ico_set_07.svg'
)
);
iconRegistry.addSvgIcon(
'ico_set_08',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/ico_set_08.svg'
)
);
iconRegistry.addSvgIcon(
'login_logo_facebook',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/login/login_logo_facebook.svg'
)
);
iconRegistry.addSvgIcon(
'login_logo_google',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/login/login_logo_google.svg'
)
);
iconRegistry.addSvgIcon(
'login_logo_kakao',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/login/login_logo_kakao.svg'
)
);
iconRegistry.addSvgIcon(
'login_logo_line',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/login/login_logo_line.svg'
)
);
iconRegistry.addSvgIcon(
'login_logo_naver',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/login/login_logo_naver.svg'
)
);
iconRegistry.addSvgIcon(
'login_logo_twitter',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/login/login_logo_twitter.svg'
)
);
iconRegistry.addSvgIcon(
'login_logo_weibo',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/login/login_logo_weibo.svg'
)
);
iconRegistry.addSvgIcon(
'login_logo_instagram',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/login/login_logo_insta.svg'
)
);
iconRegistry.addSvgIcon(
'login_logo_googlePlus',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/login/login_logo_gplus.svg'
)
);
iconRegistry.addSvgIcon(
'login_caution',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/login/login_ico_01.svg'
)
);
iconRegistry.addSvgIcon(
'ico_share_03',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/ico_share_03.svg'
)
);
iconRegistry.addSvgIcon(
'ico_share_04',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/ico_share_04.svg'
)
);
iconRegistry.addSvgIcon(
'step-backward',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/ico_check2.svg'
)
);
iconRegistry.addSvgIcon(
'step_backward',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/step-backward.svg'
)
);
iconRegistry.addSvgIcon(
'msg_ok',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/comment-check.svg'
)
);
iconRegistry.addSvgIcon(
'msg_notice',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/message-alert.svg'
)
);
iconRegistry.addSvgIcon(
'msg_fail',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/message-bulleted-off.svg'
)
);
iconRegistry.addSvgIcon(
'msg_bigfail',
sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/icons/alert-octagram.svg'
)
);
}
ngOnInit(): void { }
}

44
src/app/app.module.ts Executable file
View File

@ -0,0 +1,44 @@
/**
* : app.module.ts
* 작성일자: 2018-12-17
* : 박병준
* : app module .
* :
* :
* :
* :
*/
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { SharedModule } from '../modules/common/shared/shared.module';
import { AppRoutingModule } from './app-routing.module';
import { AppTranslateModule } from './app-translate.module';
import { AppStoreModule } from './app-store.module';
import { AppProviderModule } from './app-provider.module';
import { AppComponent } from './app.component';
import { LayoutModule } from './layout/layout.module';
@NgModule({
imports: [
BrowserModule,
HttpClientModule,
BrowserAnimationsModule,
AppRoutingModule,
AppStoreModule,
AppProviderModule,
AppTranslateModule,
SharedModule,
LayoutModule,
],
declarations: [
AppComponent,
],
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -0,0 +1,53 @@
import { Injectable } from '@angular/core';
import {
CanActivate,
ActivatedRouteSnapshot,
RouterStateSnapshot,
Router,
} from '@angular/router';
import { Store, } from '@ngrx/store';
import { of } from 'rxjs';
import { map, take, catchError } from 'rxjs/operators';
import { ArticleGetService } from '../../modules/article/service/article.get.service';
import { ArticleMongodb } from '../../shared/article/model/article.mongodb.model';
import { AccountsUtil } from 'src/modules/accounts/util/accounts.util';
@Injectable()
export class ArticleOwnerGuard implements CanActivate {
constructor(
private store: Store<any>,
private router: Router,
private articleGetService: ArticleGetService,
) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
const aid = route.paramMap.get('aid');
if (!aid) {
return resolve(true);
}
this.articleGetService.get(aid).pipe(
take(1),
map(async (articleMongodb: ArticleMongodb) => {
if (!articleMongodb) {
return resolve(false);
}
const isOwner = await AccountsUtil.checkOwner(this.store, articleMongodb.userId);
if (!isOwner) {
this.router.navigate(['/']);
return resolve(false);
}
return resolve(true);
}),
catchError(error => {
reject(error);
return of(error);
}),
).subscribe();
});
}
}

View File

@ -0,0 +1,40 @@
import { Injectable } from '@angular/core';
import {
CanActivate,
ActivatedRouteSnapshot,
RouterStateSnapshot,
Router,
} from '@angular/router';
import { Store, } from '@ngrx/store';
import * as EventsStore from '../../modules/common/shared/store/events';
import { AccountsUtil } from '../../modules/accounts/util/accounts.util';
@Injectable()
export class AuthNicknameGuard implements CanActivate {
constructor(
private store: Store<any>,
private router: Router,
) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
return new Promise<boolean>(async (resolve, reject) => {
const user = await AccountsUtil.getUser(this.store);
if (!user) {
return resolve(true);
}
if (!user.nickname) {
this.store.dispatch(new EventsStore.ExecutionFailure({
title: 'Nickname is not valid',
message: 'You need to specify your nickname before you can author.',
}));
this.router.navigate(['/user/edit']);
return resolve(false);
}
return resolve(true);
});
}
}

47
src/app/guard/auth.guard.ts Executable file
View File

@ -0,0 +1,47 @@
import { Injectable } from '@angular/core';
import {
CanActivate,
ActivatedRouteSnapshot,
RouterStateSnapshot,
Router,
} from '@angular/router';
import { Store, select } from '@ngrx/store';
import { Observable } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { CookieService } from 'ngx-cookie-service';
import { AuthSelector } from '../../modules/accounts/store';
import * as AppLoginStore from '../store/login';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(
private store: Store<any>,
private router: Router,
private cookieService: CookieService,
) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.store.pipe(
take(1),
select(AuthSelector.selectLogined),
map((logined) => {
if (!logined) {
if (this.cookieService.check('jwt')) {
this.store.dispatch(new AppLoginStore.LoginToken({
token: this.cookieService.get('jwt'),
returnURL: state.url
}));
} else {
this.store.dispatch(new AppLoginStore.LoginRedirect({ returnURL: state.url }));
}
return false;
}
return true;
}),
);
}
}

View File

@ -0,0 +1,27 @@
import { Injectable } from '@angular/core';
import {
ActivatedRouteSnapshot,
RouterStateSnapshot,
CanDeactivate as RouterCanDeactivate,
} from '@angular/router';
import { Observable } from 'rxjs';
export interface CanDeactivate {
canDeactivate(): Observable<boolean> | Promise<boolean> | boolean;
}
@Injectable()
export class CanDeactivateGuard implements RouterCanDeactivate<CanDeactivate> {
constructor() { }
canDeactivate(
component: CanDeactivate,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot): boolean | Observable<boolean> | Promise<boolean> {
return component.canDeactivate ? component.canDeactivate() : true;
}
}

11
src/app/guard/index.ts Executable file
View File

@ -0,0 +1,11 @@
import { ArticleOwnerGuard } from './article-owner.guard';
import { AuthGuard } from './auth.guard';
import { AuthNicknameGuard } from './auth-nickname.guard';
import { CanDeactivateGuard } from './can-deactivate.guard';
export const GUARDS = [
ArticleOwnerGuard,
AuthGuard,
AuthNicknameGuard,
CanDeactivateGuard,
];

View File

@ -0,0 +1,3 @@
<div class="accountsLayout">
<router-outlet></router-outlet>
</div>

View File

@ -0,0 +1,4 @@
.accountsLayout {
margin-top: -55px;
margin-bottom: -80px;
}

View File

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

View File

@ -0,0 +1,25 @@
/**
* : accounts.layout.component.ts
* 작성일자: 2018-12-21
* : 박병준
* : Accounts layout component를 .
* :
* :
* :
* :
*/
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-layout-accounts',
templateUrl: './accounts.layout.component.html',
styleUrls: ['./accounts.layout.component.scss']
})
export class AccountsLayoutComponent implements OnInit {
constructor(
) {
}
ngOnInit(): void {
}
}

View File

@ -0,0 +1,3 @@
<div (click)="handleClickEvent($event)" class="avatar-container">
<img *ngIf="src" [src]="src" [width]="size" [height]="size" class="avatar-content" />
</div>

View File

@ -0,0 +1,8 @@
:host {
cursor: pointer;
}
.avatar-content {
max-width: 100%;
border-radius: 50%;
}

View File

@ -0,0 +1,28 @@
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-avatar',
templateUrl: './avatar.component.html',
styleUrls: ['./avatar.component.scss']
})
export class AvatarComponent {
@Input()
src: string;
@Input()
size = 50;
@Output()
clickOnAvatar: EventEmitter<any> = new EventEmitter<any>();
constructor(
) {
}
handleClickEvent(event: any) {
if (event) {
this.clickOnAvatar.emit();
}
}
}

View File

@ -0,0 +1,18 @@
<ng-container *ngIf="environment$ | async as environment" [ngSwitch]="environment">
<!--for mobile-->
<app-navbar-mobile *ngSwitchCase="'Mobile'"></app-navbar-mobile>
<mat-progress-bar *ngIf="showProgressBar$ | async" mode="indeterminate" class="progress-bar"></mat-progress-bar>
<app-toolbar *ngSwitchCase="'Mobile'"></app-toolbar>
<div class="defaultLayout" *ngSwitchCase="'Mobile'">
<router-outlet></router-outlet>
</div>
<!--for mobile-->
<!--for PC-->
<div class="pcLayout" *ngSwitchCase="'Desktop'">
<app-navbar></app-navbar>
<div class="defaultLayout">
<router-outlet></router-outlet>
</div>
</div>
<!--for PC-->
</ng-container>

View File

@ -0,0 +1,37 @@
app-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 2;
}
pp-root>app-component-sidenav {
flex: 1;
}
app-root>app-homepage,
app-root>app-guides,
app-root>guide-viewer {
overflow-y: auto;
}
app-navbar-mobile {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 2;
}
pp-root>app-component-sidenav {
flex: 1;
}
app-root>app-homepage,
app-root>app-guides,
app-root>guide-viewer {
overflow-y: auto;
}

View File

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

View File

@ -0,0 +1,47 @@
/**
* : default.layout.component.ts
* 작성일자: 2018-12-21
* : 박병준
* : Default layout component를 .
* :
* :
* :
* :
*/
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { Subscription, of, Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import * as AppStore from '../../../store';
import { Environment } from '../../../type/environment.type';
@Component({
selector: 'app-layout-default',
templateUrl: './default.layout.component.html',
styleUrls: ['./default.layout.component.scss']
})
export class DefaultLayoutComponent implements OnInit, OnDestroy {
environment$: Observable<Environment>;
showProgressBar$: Observable<boolean>;
constructor(
private store: Store<any>,
) {
}
ngOnInit(): void {
this.environment$ = this.store.pipe(
select(AppStore.AppLayoutSelector.selectEnvironment),
);
this.showProgressBar$ = this.store.pipe(
select(AppStore.AppEventsSelector.selectShowProgressBar),
);
}
ngOnDestroy(): void {
}
}

View File

@ -0,0 +1,17 @@
import { AccountsLayoutComponent } from './accounts/accounts.layout.component';
import { AvatarComponent } from './avatar/avatar.component';
import { DefaultLayoutComponent } from './default/default.layout.component';
import { MainLayoutComponent } from './main/main.layout.component';
import { NavBarComponent } from './navbar/navbar.component';
import { NavBarMobileComponent } from './navbar/navbar-mobile/navbar-mobile.component';
import { ToolBarComponent } from './toolbar/toolbar.component';
export const COMPONENTS = [
AccountsLayoutComponent,
AvatarComponent,
DefaultLayoutComponent,
MainLayoutComponent,
NavBarComponent,
NavBarMobileComponent,
ToolBarComponent
];

View File

@ -0,0 +1,21 @@
<ng-container *ngIf="environment$ | async as environment" [ngSwitch]="environment">
<!--for mobile-->
<app-navbar-mobile *ngSwitchCase="'Mobile'"></app-navbar-mobile>
<mat-progress-bar *ngIf="showProgressBar$ | async" mode="indeterminate" class="progress-bar"></mat-progress-bar>
<app-toolbar *ngSwitchCase="'Mobile'"></app-toolbar>
<div class="mainLayout" *ngSwitchCase="'Mobile'">
<router-outlet></router-outlet>
</div>
<!--for mobile-->
<!--for PC-->
<div class="pcLayout" *ngSwitchCase="'Desktop'">
<app-navbar></app-navbar>
<div class="mainLayout">
<router-outlet></router-outlet>
</div>
<aside class="main-rcommend">
PC용 추천작가 표출 영역
</aside>
</div>
<!--for PC-->
</ng-container>

View File

@ -0,0 +1,37 @@
app-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 2;
}
pp-root>app-component-sidenav {
flex: 1;
}
app-root>app-homepage,
app-root>app-guides,
app-root>guide-viewer {
overflow-y: auto;
}
app-navbar-mobile {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 2;
}
pp-root>app-component-sidenav {
flex: 1;
}
app-root>app-homepage,
app-root>app-guides,
app-root>guide-viewer {
overflow-y: auto;
}

View File

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

View File

@ -0,0 +1,47 @@
/**
* : Main.layout.component.ts
* 작성일자: 2019-1-23
* : 조현정
* : Main layout component를 .
* :
* :
* :
* :
*/
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { Subscription, of, Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import * as AppStore from '../../../store';
import { Environment } from '../../../type/environment.type';
@Component({
selector: 'app-layout-main',
templateUrl: './main.layout.component.html',
styleUrls: ['./main.layout.component.scss']
})
export class MainLayoutComponent implements OnInit, OnDestroy {
environment$: Observable<Environment>;
showProgressBar$: Observable<boolean>;
constructor(
private store: Store<any>,
) {
}
ngOnInit(): void {
this.environment$ = this.store.pipe(
select(AppStore.AppLayoutSelector.selectEnvironment),
);
this.showProgressBar$ = this.store.pipe(
select(AppStore.AppEventsSelector.selectShowProgressBar),
);
}
ngOnDestroy(): void {
}
}

View File

@ -0,0 +1,17 @@
<mat-toolbar>
<mat-toolbar-row>
<div class="tool-left">
<button mat-button onClick="javascript:history.go(-1);">
<mat-icon svgIcon="step_backward">Back ward</mat-icon>
</button></div>
<h1 class="logo">
<a [routerLink]="['/']">
<img src="../../../../assets/img/logo/logo.svg" alt="coco's logo">
</a>
</h1>
<div class="tool-right">
<button mat-button [routerLink]="['/user/recommendations']">
<mat-icon svgIcon="top_recommend">my profile</mat-icon>
</button></div>
</mat-toolbar-row>
</mat-toolbar>

View File

@ -0,0 +1,41 @@
@import '../../default/default.layout.component.scss';
$topbar-height:54px;
.mat-toolbar-multiple-rows {
min-height: $topbar-height;
}
.mat-toolbar-row,
.mat-toolbar-single-row {
height: $topbar-height;
}
.logo {
text-align: center;
display: flex;
margin: auto;
a {
display: flex;
margin: auto;
img {
height: 50px;
}
}
}
mat-toolbar {
background: #fff;
border-bottom: 1px solid #e9e9e9;
.mat-button,
.mat-button * {
min-width: 24px !important;
}
.mat-button {
padding: 0 3px !important;
color: #bababa;
}
}

View File

@ -0,0 +1,28 @@
import { Component, ViewChild } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { MatMenuTrigger } from '@angular/material';
import { take } from 'rxjs/operators';
@Component({
selector: 'app-navbar-mobile',
templateUrl: './navbar-mobile.component.html',
styleUrls: ['./navbar-mobile.component.scss']
})
export class NavBarMobileComponent {
@ViewChild('notificationMenuBtn', { read: MatMenuTrigger })
protected notificationMenuBtn: MatMenuTrigger;
constructor(
private translateService: TranslateService
) {
}
changeLanguage(lang: string) {
this.translateService.use(lang).pipe(take(1)).subscribe((res: any) => {
localStorage.setItem('defaultLang', lang);
});
}
}

View File

@ -0,0 +1,34 @@
<mat-toolbar>
<mat-toolbar-row>
<div class="tool-left">
<button mat-button onClick="javascript:history.go(-1);">
<mat-icon svgIcon="step_backward">Back ward</mat-icon>
</button></div>
<h1 class="logo">
<a [routerLink]="['/']">
<img src="../../../../assets/img/logo/logo.svg" alt="coco's logo">
</a>
</h1>
<div class="tool-right">
<button mat-button [routerLink]="['/article/search']" routerLinkActive="router-link-active"
[routerLinkActiveOptions]="{exact:
true}">
<mat-icon svgIcon="search">search</mat-icon>
</button>
<button mat-button [routerLink]="['/article/write']" routerLinkActive="router-link-active"
[routerLinkActiveOptions]="{exact:
true}">
<mat-icon svgIcon="upload">upload</mat-icon>
</button>
<button mat-button [routerLink]="['/user/bookmarks']" routerLinkActive="router-link-active"
[routerLinkActiveOptions]="{exact:
true}">
<mat-icon svgIcon="bookmark">bookmark</mat-icon>
</button>
<!-- session.getId의 값이 parameter로 입력되어야 함.-->
<button mat-button [routerLink]="myProfileLink" routerLinkActive="router-link-active" [routerLinkActiveOptions]="{exact: true}">
<mat-icon svgIcon="myprofil">my profile</mat-icon>
</button>
</div>
</mat-toolbar-row>
</mat-toolbar>

View File

@ -0,0 +1,44 @@
@import '../default/default.layout.component.scss';
$topbar-height:54px;
.mat-toolbar-multiple-rows {
min-height: $topbar-height;
max-width: 800px;
margin: 0 auto;
}
.mat-toolbar-row,
.mat-toolbar-single-row {
height: $topbar-height;
}
.logo {
text-align: center;
display: flex;
margin: auto;
padding-left: 125px;
a {
display: flex;
margin: auto;
img {
height: 50px;
}
}
}
mat-toolbar {
background: #fff;
border-bottom: 1px solid #e9e9e9;
.mat-button,
.mat-button * {
min-width: 24px !important;
}
.mat-button {
padding: 0 3px !important;
color: #bababa;
}
}

View File

@ -0,0 +1,46 @@
import { Component, ViewChild, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { MatMenuTrigger } from '@angular/material';
import { take } from 'rxjs/operators';
import { Router } from '@angular/router';
import { Store, } from '@ngrx/store';
import { User } from '../../../../shared/user/model/user.model';
import { AccountsUtil } from 'src/modules/accounts/util/accounts.util';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.scss']
})
export class NavBarComponent implements OnInit {
user$: Promise<User>;
myProfileLink = ['/accounts/authentication'];
@ViewChild('notificationMenuBtn', { read: MatMenuTrigger })
protected notificationMenuBtn: MatMenuTrigger;
constructor(
private translateService: TranslateService,
private router: Router,
private store: Store<any>
) {
}
ngOnInit() {
AccountsUtil.getUser(this.store)
.then((user) => {
if (user) {
this.myProfileLink = ['/article', user.id];
}
})
.catch((reason) => {
console.log(reason);
});
}
changeLanguage(lang: string) {
this.translateService.use(lang).pipe(take(1)).subscribe((res: any) => {
localStorage.setItem('defaultLang', lang);
});
}
}

View File

@ -0,0 +1,28 @@
<mat-toolbar class="flexToolbar">
<button mat-button [routerLink]="['/']" routerLinkActive="router-link-active" [routerLinkActiveOptions]="{exact:
true}">
<mat-icon svgIcon="home-outline">home</mat-icon>
</button>
<!-- This fills the remaining space of the current row -->
<span class="fill-remaining-space"></span>
<button mat-button [routerLink]="['/article/search']" routerLinkActive="router-link-active" [routerLinkActiveOptions]="{exact:
true}">
<mat-icon svgIcon="search">search</mat-icon>
</button>
<span class="fill-remaining-space"></span>
<button mat-button [routerLink]="['/article/write']" routerLinkActive="router-link-active" [routerLinkActiveOptions]="{exact:
true}">
<mat-icon svgIcon="upload">upload</mat-icon>
</button>
<span class="fill-remaining-space"></span>
<button mat-button [routerLink]="['/user/bookmarks']" routerLinkActive="router-link-active" [routerLinkActiveOptions]="{exact:
true}">
<mat-icon svgIcon="bookmark">bookmark</mat-icon>
</button>
<span class="fill-remaining-space"></span>
<!-- session.getId의 값이 parameter로 입력되어야 함.-->
<button mat-button [routerLink]="myProfileLink" routerLinkActive="router-link-active" [routerLinkActiveOptions]="{exact: true}">
<mat-icon svgIcon="myprofil">my profile</mat-icon>
</button>
</mat-toolbar>

View File

@ -0,0 +1,30 @@
/** No CSS for this*/
.fill-remaining-space {
flex: 1 1 auto;
}
mat-toolbar {
$toolbarBG: #f9f9f9;
$toolbarColor: #000;
position: fixed;
bottom: 0;
z-index: 100;
width: 100%;
background: $toolbarBG;
.mat-button {
padding: 0 3px !important;
color: $toolbarColor;
}
/* toolbar size responsive
*/
@media (max-width: 800px) {
padding: 0 16px;
height: 64px;
}
@media (min-width: 801px) {
display: none;
}
}

View File

@ -0,0 +1,29 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Store, } from '@ngrx/store';
import { User } from '../../../../shared/user/model/user.model';
import { AccountsUtil } from 'src/modules/accounts/util/accounts.util';
@Component({
selector: 'app-toolbar',
templateUrl: './toolbar.component.html',
styleUrls: ['./toolbar.component.scss']
})
export class ToolBarComponent implements OnInit {
user$: Promise<User>;
myProfileLink = ['/accounts/authentication'];
constructor(private router: Router, private store: Store<any>) { }
ngOnInit() {
AccountsUtil.getUser(this.store)
.then((user) => {
if (user) {
this.myProfileLink = ['/article', user.id];
}
})
.catch((reason) => {
console.log(reason);
});
}
}

View File

@ -0,0 +1,15 @@
<mat-card class="confirm-card">
<mat-card-header>
<mat-card-title>{{data.title}}</mat-card-title>
<mat-card-subtitle></mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<p>
{{data.message}}
</p>
</mat-card-content>
<mat-card-actions class="button-farm flex-row">
<button mat-stroked-button (click)="onClickChoice(false)" class="mat-primary">No</button>
<button mat-flat-button (click)="onClickChoice(true)" class="mat-primary">Yes</button>
</mat-card-actions>
</mat-card>

View File

@ -0,0 +1,38 @@
import { Component, OnInit, Input, Output, EventEmitter, Inject } from '@angular/core';
import { FormGroupDirective, NgForm, FormControl, FormGroup, FormBuilder, Validators } from '@angular/forms';
import { ErrorStateMatcher, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { Observable, of } from 'rxjs';
import { take, tap, map, catchError, switchMap, finalize } from 'rxjs/operators';
import { Store } from '@ngrx/store';
export interface CanDeactivateDialogData {
title: string;
message: string;
}
export interface CanDeactivateDialogResult {
choice: boolean;
}
@Component({
selector: 'app-can-deactivate-dialog',
templateUrl: './can-deactivate.dialog.component.html',
styleUrls: ['./can-deactivate.dialog.component.scss']
})
export class CanDeactivateDialogComponent implements OnInit {
constructor(
public dialogRef: MatDialogRef<CanDeactivateDialogComponent, CanDeactivateDialogResult>,
@Inject(MAT_DIALOG_DATA) private data: CanDeactivateDialogData,
) {
}
ngOnInit() {
}
onClickChoice(choice: boolean) {
this.dialogRef.close({
choice: choice,
});
}
}

View File

@ -0,0 +1,15 @@
<mat-card class="confirm-card">
<mat-card-header>
<mat-card-title>{{data.title}}</mat-card-title>
<!-- <mat-card-subtitle>Confirm</mat-card-subtitle> -->
</mat-card-header>
<mat-card-content>
<p class="notice">
{{data.message}}
</p>
</mat-card-content>
<mat-card-actions class="button-farm flex-row">
<button mat-stroked-button (click)="onClickChoice(false)" class="mat-primary">No</button>
<button mat-flat-button (click)="onClickChoice(true)" class="mat-primary">Yes</button>
</mat-card-actions>
</mat-card>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ConfirmDialogComponent } from './confirm.dialog.component';
describe('ConfirmDialogComponent', () => {
let component: ConfirmDialogComponent;
let fixture: ComponentFixture<ConfirmDialogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ConfirmDialogComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ConfirmDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,49 @@
/**
* : confirm.dialog.component.ts
* 작성일자: 2019-01-13
* : 박병준
* : Confirm Dialog component를 .
* :
* :
* :
* :
*/
import { Component, OnInit, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
export interface ConfirmDialogData {
title: string;
message?: string;
}
export interface ConfirmDialogResult {
choice: boolean;
}
@Component({
selector: 'app-confirm-dialog',
templateUrl: './confirm.dialog.component.html',
styleUrls: [
'./confirm.dialog.component.scss',
],
})
export class ConfirmDialogComponent implements OnInit {
tempAgeLimits = [];
constructor(
public dialogRef: MatDialogRef<ConfirmDialogComponent, ConfirmDialogResult>,
@Inject(MAT_DIALOG_DATA) public data: ConfirmDialogData
) {
}
ngOnInit(): void {
}
onClickChoice(choice: boolean): void {
this.dialogRef.close({
choice: choice,
});
}
}

View File

@ -0,0 +1,12 @@
<div class="sb-wrap">
<b class="title-failure">
<mat-icon svgIcon="msg_fail">failure</mat-icon> {{data?.title}}
</b>
<span class="notice"> {{data?.message}}</span>
</div>
<!--
<mat-icon svgIcon="msg_ok">success</mat-icon> as ok
<mat-icon svgIcon="msg_notice">notice</mat-icon> as notice
<mat-icon svgIcon="msg_fail">fail</mat-icon> as fail
<mat-icon svgIcon="msg_bigfail">Big fail</mat-icon> as BBBBig fail
-->

View File

@ -0,0 +1,24 @@
import { Component, OnInit, ViewChild, ElementRef, Inject } from '@angular/core';
import { MAT_SNACK_BAR_DATA } from '@angular/material';
export interface FailureSnackBarData {
title?: string;
message?: string;
error?: Error;
}
@Component({
selector: 'app-events-failure-snackbar',
templateUrl: './failure.snackbar.component.html',
styleUrls: ['./failure.snackbar.component.scss']
})
export class FailureSnackBarComponent implements OnInit {
constructor(
@Inject(MAT_SNACK_BAR_DATA) public data: FailureSnackBarData,
) {
}
ngOnInit() { }
}

View File

@ -0,0 +1,7 @@
import { FailureSnackBarComponent } from './failure/failure.snackbar.component';
import { SuccessSnackBarComponent } from './success/success.snackbar.component';
export const EVENTS_DIALOGS = [
FailureSnackBarComponent,
SuccessSnackBarComponent,
];

View File

@ -0,0 +1,12 @@
<div class="sb-wrap">
<b class="title-success">
<mat-icon svgIcon="msg_ok">success</mat-icon> {{data?.title}}
</b>
<span class="notice"> {{data?.message}}</span>
</div>
<!--
<mat-icon svgIcon="msg_ok">success</mat-icon> as ok
<mat-icon svgIcon="msg_notice">notice</mat-icon> as notice
<mat-icon svgIcon="msg_fail">fail</mat-icon> as fail
<mat-icon svgIcon="msg_bigfail">Big fail</mat-icon> as BBBBig fail
-->

View File

@ -0,0 +1,23 @@
import { Component, OnInit, Inject } from '@angular/core';
import { MAT_SNACK_BAR_DATA } from '@angular/material';
export interface SuccessSnackBarData {
title?: string;
message?: string;
}
@Component({
selector: 'app-events-success-snackbar',
templateUrl: './success.snackbar.component.html',
styleUrls: ['./success.snackbar.component.scss']
})
export class SuccessSnackBarComponent implements OnInit {
constructor(
@Inject(MAT_SNACK_BAR_DATA) public data: SuccessSnackBarData,
) {
}
ngOnInit() { }
}

9
src/app/layout/dialog/index.ts Executable file
View File

@ -0,0 +1,9 @@
import { CanDeactivateDialogComponent } from './can-deactivate/can-deactivate.dialog.component';
import { ConfirmDialogComponent } from './confirm/confirm.dialog.component';
import { EVENTS_DIALOGS } from './events';
export const DIALOGS = [
CanDeactivateDialogComponent,
ConfirmDialogComponent,
...EVENTS_DIALOGS,
];

57
src/app/layout/layout.module.ts Executable file
View File

@ -0,0 +1,57 @@
/**
* : accounts.module.ts
* 작성일자: 2018-12-20
* : 박병준
* : accounts module .
* :
* :
* :
* :
*/
import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { SharedModule } from '../../modules/common/shared/shared.module';
import { AccountsModule } from '../../modules/accounts/accounts.module';
import { COMPONENTS } from './component';
import { DIALOGS } from './dialog';
@NgModule({
imports: [
CommonModule,
RouterModule,
TranslateModule,
SharedModule,
AccountsModule,
],
exports: [
...COMPONENTS,
],
declarations: [
...COMPONENTS,
...DIALOGS,
],
entryComponents: [
...DIALOGS,
],
})
export class LayoutModule {
public static forRoot(): ModuleWithProviders<LayoutRootModule> {
return {
ngModule: LayoutRootModule,
providers: [
],
};
}
}
@NgModule({
imports: [
],
exports: [
]
})
export class LayoutRootModule {
}

View File

@ -0,0 +1,45 @@
/**
* : accounts-routing.module.ts
* 작성일자: 2018-12-20
* : 박병준
* : account routing을 .
* :
* :
* :
* :
*/
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AccountsLayoutComponent } from '../../layout/component/accounts/accounts.layout.component';
import { AuthenticationPageComponent } from './component/authentication/authentication.page.component';
import { WelcomePageComponent } from './component/welcome/welcome.page.component';
import { AuthenticationCallbackPageComponent } from './component/authentication/authentication-callback.page.component';
const routes: Routes = [
{
path: '',
component: AccountsLayoutComponent,
children: [
{
path: 'authentication',
component: AuthenticationPageComponent
},
{
path: 'authentication/callback',
component: AuthenticationCallbackPageComponent
},
{
path: 'welcome',
component: WelcomePageComponent
}
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AccountsRoutingPageModule {}

View File

@ -0,0 +1,34 @@
/**
* : accounts.module.ts
* 작성일자: 2018-12-20
* : 박병준
* : accounts page module .
* :
* :
* :
* :
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { LayoutModule } from '../../layout/layout.module';
import { AccountsRoutingPageModule } from './accounts-routing.page.module';
import { AccountsModule } from '../../../modules/accounts/accounts.module';
import { COMPONENTS } from './component';
@NgModule({
imports: [
CommonModule,
TranslateModule,
LayoutModule,
AccountsRoutingPageModule,
AccountsModule,
],
declarations: [
...COMPONENTS,
],
})
export class AccountsPageModule { }

View File

@ -0,0 +1,42 @@
/**
* : authentication-callback.page.component.ts
* 작성일자: 2018-12-20
* : 박병준
* : authentication callback page component를 .
* :
* :
* :
* :
*/
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { ActivatedRoute } from '@angular/router';
import * as AppLoginStore from '../../../../store/login';
@Component({
selector: 'app-page-accounts-authentication-callback',
templateUrl: './authentication-callback.page.component.html',
styleUrls: ['./authentication-callback.page.component.scss'],
})
export class AuthenticationCallbackPageComponent implements OnInit {
constructor(
private store: Store<any>,
private activatedRoute: ActivatedRoute,
) {
}
ngOnInit() {
this.activatedRoute.queryParams.subscribe(queryParams => {
const token = queryParams['token'] || null;
const result = queryParams['result'] || null;
let returnURL = queryParams['returnURL'];
if (!returnURL || 'null' === returnURL || '' === returnURL) {
returnURL = '/';
}
this.store.dispatch(new AppLoginStore.LoginToken({ token, returnURL: returnURL }));
});
}
}

View File

@ -0,0 +1,3 @@
<div class="login-layout">
<app-authentication *ngIf="params$ | async as params" [params]="params"></app-authentication>
</div>

View File

@ -0,0 +1,61 @@
@mixin calc($property, $expression) {
#{$property}: -moz-calc(#{$expression});
#{$property}: -webkit-calc(#{$expression});
#{$property}: calc(#{$expression});
}
$marginHeight: 170px;
.login-layout {
display: block;
width: 100%;
@include calc(height, '100vh - '$marginHeight);
margin: 0 auto;
margin-top: 55px;
padding: 0;
background: url(../../../../../assets/img/login/bg.png) no-repeat right 205px;
background-size: 100% auto;
background-attachment: fixed;
@media (max-height: 640px) {
background: url(../../../../../assets/img/login/bg.png) no-repeat right 195px;
background-size: 100% auto;
background-attachment: fixed;
}
@media (max-height: 640px) and (min-width: 760px) {
background: url(../../../../../assets/img/login/bg.png) no-repeat right -45px;
background-size: 100% auto;
background-attachment: fixed;
}
@media (min-height: 641px) and (max-height: 1024px) {
background: url(../../../../../assets/img/login/bg.png) no-repeat right 65px;
background-size: 100% auto;
background-attachment: fixed;
}
@media (min-width:480px) and (max-width: 760px) and (min-height: 641px) and (max-height: 1024px) {
background: url(../../../../../assets/img/login/bg.png) no-repeat right 40px;
background-size: 100% auto;
background-attachment: fixed;
}
@media (min-width: 761px) and (max-width:1024px) and (min-height: 641px) and (max-height: 1024px) {
background: url(../../../../../assets/img/login/bg.png) no-repeat right 0px;
background-size: 500px auto;
background-attachment: fixed;
}
@media (min-width: 1025px) and (min-height: 641px) and (max-height: 1024px) {
background: url(../../../../../assets/img/login/bg.png) no-repeat right 0px;
background-size: 500px auto;
background-attachment: fixed;
}
@media (min-height: 1025px) {
background: url(../../../../../assets/img/login/bg.png) no-repeat right 30px;
background-size: 100% auto;
background-attachment: fixed;
}
}

View File

@ -0,0 +1,43 @@
/**
* : authentication.page.component.ts
* 작성일자: 2018-12-20
* : 박병준
* : authentication page component를 .
* :
* :
* :
* :
*/
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { of, Observable } from 'rxjs';
import { take, map, catchError } from 'rxjs/operators';
@Component({
selector: 'app-page-accounts-authentication',
templateUrl: './authentication.page.component.html',
styleUrls: ['./authentication.page.component.scss'],
})
export class AuthenticationPageComponent implements OnInit {
params$: Observable<Map<string, string>>;
constructor(
private activatedRoute: ActivatedRoute,
) {
}
ngOnInit(): void {
this.params$ = this.activatedRoute.queryParamMap.pipe(
take(1),
map(params => {
const _params = new Map();
_params.set('returnURL', params.get('returnURL'));
return _params;
}),
catchError(error => {
return of(error);
}),
);
}
}

View File

@ -0,0 +1,9 @@
import { AuthenticationPageComponent } from './authentication/authentication.page.component';
import { AuthenticationCallbackPageComponent } from './authentication/authentication-callback.page.component';
import { WelcomePageComponent } from './welcome/welcome.page.component';
export const COMPONENTS = [
AuthenticationPageComponent,
AuthenticationCallbackPageComponent,
WelcomePageComponent,
];

View File

@ -0,0 +1 @@
welcome

View File

@ -0,0 +1,23 @@
/**
* : welcome.page.component.ts
* 작성일자: 2018-12-23
* : 박병준
* : welcome page component를 .
* :
* :
* :
* :
*/
import { Component } from '@angular/core';
@Component({
selector: 'app-page-accounts-welcome',
templateUrl: './welcome.page.component.html',
styleUrls: ['./welcome.page.component.scss'],
})
export class WelcomePageComponent {
constructor() {
}
}

View File

@ -0,0 +1,42 @@
/**
* : admin-routing.page.module.ts
* 작성일자: 2019-01-08
* : 최지련
* : user routing을 .
* :
* :
* :
* :
*/
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { DefaultLayoutComponent } from '../../layout/component/default/default.layout.component';
import { ReportPageComponent } from './component/report/report.page.component';
import { ErrorPageComponent } from './component/error/error.page.component';
import { DataPageComponent } from './component/data/data.page.component';
const routes: Routes = [
{
path: '',
component: DefaultLayoutComponent,
children: [
{
path: 'report',
component: ReportPageComponent
},
{
path: 'data',
component: DataPageComponent,
},
{
path: '404',
component: ErrorPageComponent
}
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AdminRoutingPageModule { }

View File

@ -0,0 +1,30 @@
/**
* : admin.page.module.ts
* 작성일자: 2019-01-08
* : 최지련
* : admin page module .
* :
* :
* :
* :
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { COMPONENTS } from './component';
import { AdminRoutingPageModule } from './admin-routing.page.module';
import { LayoutModule } from '../../layout/layout.module';
import { SharedModule } from '../../../modules/common/shared/shared.module';
@NgModule({
imports: [
CommonModule,
TranslateModule,
AdminRoutingPageModule,
SharedModule,
LayoutModule,
],
declarations: [...COMPONENTS]
})
export class AdminPageModule { }

View File

@ -0,0 +1,50 @@
<section>
<div class="section-wrap">
<h2>관리자 화면 기준 데이터 생성</h2>
<div class="overflowTable">
<mat-list>
<!-- <mat-list-item>
<h4 mat-line>Article Tag 데이터 생성</h4>
<p mat-line> Article Tag </p>
<button mat-raised-button (click)="onClickArticleTagData()">생성</button>
</mat-list-item>
<mat-list-item>
<h4 mat-line>User Analysis Favor Tag 데이터 생성</h4>
<p mat-line> User Analysis Favor Tag </p>
<button mat-raised-button (click)="onClickUserAnalysisFavorTagData()">생성</button>
</mat-list-item> -->
<!-- <mat-list-item>
<h4 mat-line>이벤트 데이터 생성</h4>
<p mat-line> User Support Event </p>
<button mat-raised-button (click)="onClickUserSupportEventData()">생성</button>
</mat-list-item> -->
<!-- <mat-list-item>
<h4 mat-line>Meta Comments Tag 데이터 생성</h4>
<p mat-line> Meta Comments Tag </p>
<button mat-raised-button (click)="onClickMetaCommentsTagData()">생성</button>
</mat-list-item> -->
<mat-list-item>
<h4 mat-line>Thumbnails 생성</h4>
<p mat-line> Thumbnails </p>
<button mat-raised-button (click)="onClickThumbnails()">생성</button>
</mat-list-item>
<mat-list-item>
<h4 mat-line>Mongodb Article 생성</h4>
<p mat-line> Mongodb Article </p>
<button mat-raised-button (click)="onClickMongodbArticleData()">생성</button>
</mat-list-item>
<mat-list-item>
<h4 mat-line>Random Article 생성</h4>
<p mat-line> Random Article <input type="text" style="width: 40px;" #randomArticleCount value="100" /> </p>
<button mat-raised-button (click)="onClickRandomArticleData(randomArticleCount.value)">생성</button>
</mat-list-item>
<mat-list-item>
<h4 mat-line>Random Author 생성</h4>
<p mat-line> Random Author <input type="text" style="width: 40px;" #randomAuthorCount value="100" /> </p>
<button mat-raised-button (click)="onClickRandomAuthorData(randomAuthorCount.value)">생성</button>
</mat-list-item>
</mat-list>
</div>
</div>
</section>

View File

@ -0,0 +1,4 @@
.example-button-row button,
.example-button-row a {
margin-right: 8px;
}

View File

@ -0,0 +1,197 @@
/**
* : data.page.component.ts
* 작성일자: 2019-01-08
* : 최지련
* : admin data page component를 .
* :
* :
* :
* :
*/
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Store } from '@ngrx/store';
import { of } from 'rxjs';
import { take, map, catchError, tap, finalize } from 'rxjs/operators';
import { ArticleTag } from '../../../../../shared/article/model/article-tag.model';
import { UserAnalysisFavorTag } from '../../../../../shared/user-analysis/model/user-analysis-favor-tag.model';
import { ArticleTagService } from '../../../../../modules/article/service/article-tag.service';
import { UserAnalysisFavorTagService } from '../../../../../modules/user-analysis/service/user-analysis-favor-tag.service';
import { ContentsService } from '../../../../../modules/contents/service/contents.service';
import * as AppEventsStore from '../../../../store/events';
import { MetaCommentsTag } from '../../../../../shared/meta/model/meta-comments-tag.model';
import { MetaCommentsTagService } from '../../../../../modules/meta/service/meta-comments-tag.service';
@Component({
selector: 'app-page-admin-data',
templateUrl: './data.page.component.html',
styleUrls: ['./data.page.component.scss'],
})
export class DataPageComponent {
constructor(
private httpClient: HttpClient,
private store: Store<any>,
private articleTagService: ArticleTagService,
private userAnalysisFavorTagService: UserAnalysisFavorTagService,
private contentsService: ContentsService,
private metaCommentsTagService: MetaCommentsTagService,
) {
}
onClickArticleTagData() {
this.httpClient.get('./assets/json/article-tag.json').pipe(
take(1),
map((articleTagList: ArticleTag[]) => {
this.addAllArticleTagList(articleTagList);
}),
catchError((err) => {
console.log(err);
return of(err);
})
).subscribe();
}
private addAllArticleTagList(articleTagList: ArticleTag[]) {
if (!articleTagList || 0 === articleTagList.length) {
console.log('articleTagList is not valid');
return;
}
for (const articleTag of articleTagList) {
this.articleTagService.add(articleTag).pipe(
take(1),
map((_articleTag) => {
console.log('articleTag added', _articleTag);
}),
catchError((err) => {
console.log(err);
return of(err);
})
).subscribe();
}
}
onClickUserAnalysisFavorTagData() {
this.httpClient.get('./assets/json/user-analysis-favor-tag.json').pipe(
take(1),
map((userAnalysisFavorTagList: UserAnalysisFavorTag[]) => {
this.addAllUserAnalysisFavorTagList(userAnalysisFavorTagList);
}),
catchError((err) => {
console.log(err);
return of(err);
})
).subscribe();
}
private addAllUserAnalysisFavorTagList(userAnalysisFavorTagList: UserAnalysisFavorTag[]) {
if (!userAnalysisFavorTagList || 0 === userAnalysisFavorTagList.length) {
console.log('userAnalysisFavorTagList is not valid');
return;
}
for (const userAnalysisFavorTag of userAnalysisFavorTagList) {
this.userAnalysisFavorTagService.add(userAnalysisFavorTag).pipe(
take(1),
map((_userAnalysisFavorTag) => {
console.log('userAnalysisFavorTag added', _userAnalysisFavorTag);
}),
catchError((err) => {
console.log(err);
return of(err);
})
).subscribe();
}
}
onClickUserSupportEventData() {
}
onClickMetaCommentsTagData() {
this.httpClient.get('./assets/json/meta-comments-tag.json').pipe(
take(1),
map((metaCommentsTagList: MetaCommentsTag[]) => {
this.addAllMetaCommentsTagList(metaCommentsTagList);
}),
catchError((err) => {
console.log(err);
return of(err);
})
).subscribe();
}
private addAllMetaCommentsTagList(metaCommentsTagList: MetaCommentsTag[]) {
if (!metaCommentsTagList || 0 === metaCommentsTagList.length) {
console.log('metaCommentsTagList is not valid');
return;
}
for (const metaCommentsTag of metaCommentsTagList) {
this.metaCommentsTagService.add(metaCommentsTag).pipe(
take(1),
map((_metaCommentsTag) => {
console.log('metaCommentsTag added', _metaCommentsTag);
}),
catchError((err) => {
console.log(err);
return of(err);
})
).subscribe();
}
}
onClickMongodbArticleData() {
this.contentsService.updateMongodbArticle().pipe(
take(1),
tap(() => {
this.store.dispatch(new AppEventsStore.ShowProgressBar());
}),
map((_count) => {
console.log('Mongodb Article added', _count);
}),
catchError((err) => {
console.log(err);
return of(err);
}),
finalize(() => {
this.store.dispatch(new AppEventsStore.HideProgressBar());
}),
).subscribe();
}
onClickRandomArticleData(count: number) {
this.contentsService.updateRandomArticle(count).pipe(
take(1),
map((_count) => {
console.log('Random Article added', _count);
}),
catchError((err) => {
console.log(err);
return of(err);
})
).subscribe();
}
onClickRandomAuthorData(count: number) {
this.contentsService.updateRandomAuthor(count).pipe(
take(1),
map((_count) => {
console.log('Random Author added', _count);
}),
catchError((err) => {
console.log(err);
return of(err);
})
).subscribe();
}
onClickThumbnails() {
}
}

View File

@ -0,0 +1,7 @@
<div class="error-page">
<object class="ico-error" type="image/svg+xml" data="../../../../assets/img/error/ico_error.svg">Error</object>
<h2 class="error-title">
죄송합니다.
</h2>
<p class="error-description">페이지를 표시할 수 없습니다.</p>
</div>

View File

@ -0,0 +1,18 @@
.error-page {
margin: 50px auto;
text-align: center;
.ico-error {
width: 100px;
height: 100px;
}
.error-title {
text-align: center;
margin: 15px 0 10px 0;
}
.error-description {
color: #a5a5a5;
}
}

View File

@ -0,0 +1,20 @@
/**
* : error.page.component.ts
* 작성일자: 2019-01-09
* : 조현정
* : error page component를 .
* :
* :
* :
* :
*/
import { Component } from '@angular/core';
@Component({
selector: 'app-page-error',
templateUrl: './error.page.component.html',
styleUrls: ['./error.page.component.scss']
})
export class ErrorPageComponent {
constructor() {}
}

View File

@ -0,0 +1,9 @@
import { DataPageComponent } from './data/data.page.component';
import { ReportPageComponent } from './report/report.page.component';
import { ErrorPageComponent } from './error/error.page.component';
export const COMPONENTS = [
DataPageComponent,
ReportPageComponent,
ErrorPageComponent,
];

View File

@ -0,0 +1,53 @@
<section>
<div class="section-wrap">
<h2>관리자 화면 신고 접수 및 처리</h2>
<div class="overflowTable">
<table class="table-admin mat-table">
<colgroup class="col-width">
<col class="t1">
<col class="t2">
<col class="t3">
<col class="t4">
<col class="t5">
<col class="t6">
</colgroup>
<thead>
<tr>
<th> No. </th>
<th> 신고 콘텐트 </th>
<th> 신고내용 </th>
<th> 신고일시 </th>
<th> 상태 </th>
<th> </th>
</tr>
</thead>
<tbody>
<tr>
<td> 1 </td>
<td> xanNskdakNsjd98 </td>
<td> 저작권 위반</td>
<td> 2019-01-20 </td>
<td> 신고접수 </td>
<td>
<button mat-raised-button class="mat-primary">해제</button>
<!-- <button mat-raised-button class="mat-secondary">이의기각</button> -->
<button mat-raised-button class="mat-accent">삭제</button>
<!-- <button mat-raised-button class="mat-warn ">블라인드</button> -->
</td>
</tr>
<tr>
<td> 2 </td>
<td> sdflksldkfdslksf </td>
<td> 부적절</td>
<td> 2019-01-20 </td>
<td> 블라인드 완료 </td>
<td>
<button mat-raised-button class="mat-primary">해제</button>
<button mat-raised-button class="mat-warn">이의기각</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>

View File

@ -0,0 +1,56 @@
.table-admin {
width: 100%;
border-spacing: 0;
$border: #666;
font-size: 12px;
box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.2),
0px 8px 10px 1px rgba(0, 0, 0, 0.14),
0px 3px 14px 2px rgba(0, 0, 0, 0.12);
tr {
height: 48px;
th {
padding: 0 6px;
border-bottom: 1px solid $border;
text-align: center;
}
td {
padding: 3px 6px;
border-bottom: 1px solid $border;
text-align: center;
}
}
.col-width {
.t1 {
width: 40px;
}
.t2 {
width: 110px;
}
.t3 {
width: 100px;
}
.t4 {
width: 100px;
}
.t5 {
width: 90px;
}
}
}
button {
margin: 2px;
min-width: 24px !important;
padding: 0 5px !important;
line-height: 24px !important;
border-radius: 2px !important;
}

View File

@ -0,0 +1,23 @@
/**
* : report.page.component.ts
* 작성일자: 2019-01-08
* : 최지련
* : admin report page component를 .
* :
* :
* :
* :
*/
import { Component } from '@angular/core';
@Component({
selector: 'app-page-admin-report',
templateUrl: './report.page.component.html',
styleUrls: ['./report.page.component.scss'],
})
export class ReportPageComponent {
constructor() {
}
}

View File

@ -0,0 +1,81 @@
/**
* : accounts-routing.module.ts
* 작성일자: 2018-12-20
* : 박병준
* : account routing을 .
* 수정일시: 2018-12-27
* : 조현정
* 수정내용: Layout change
* :
*/
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { DefaultLayoutComponent } from '../../layout/component/default/default.layout.component';
import { ProfilePageComponent } from './component/profile/profile.page.component';
import { FormPageComponent as SearchFormPageComponent } from './component/search/form/form.page.component';
import { ResultPageComponent as SearchResultPageComponent } from './component/search/result/result.page.component';
import { DetailsPageComponent } from './component/details/details.page.component';
import { WritePageComponent } from './component/write/write.page.component';
import { EmotionsPageComponent } from './component/emotions/emotions.page.component';
import { AuthGuard } from '../../guard/auth.guard';
import { AuthNicknameGuard } from '../../guard/auth-nickname.guard';
import { CanDeactivateGuard } from '../../guard/can-deactivate.guard';
import { ArticleOwnerGuard } from '../../guard/article-owner.guard';
const routes: Routes = [
{
path: '',
component: DefaultLayoutComponent,
children: [
{
path: 'write',
component: WritePageComponent,
canActivate: [
AuthGuard,
AuthNicknameGuard,
],
canDeactivate: [CanDeactivateGuard],
runGuardsAndResolvers: 'always',
},
{
path: 'write/:aid',
component: WritePageComponent,
canActivate: [
AuthGuard,
AuthNicknameGuard,
ArticleOwnerGuard,
],
canDeactivate: [CanDeactivateGuard],
runGuardsAndResolvers: 'always',
},
{
path: 'search',
component: SearchFormPageComponent
},
{
path: 'emotions',
component: EmotionsPageComponent
},
{
path: 'p/:aid',
component: DetailsPageComponent,
},
{
path: ':uid',
component: ProfilePageComponent,
},
{
path: 'search/p/:tagName',
component: SearchResultPageComponent
}
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ArticleRoutingPageModule { }

View File

@ -0,0 +1,50 @@
/**
* : article.page.module.ts
* 작성일자: 2018-12-20
* : 박병준
* : article page module .
* :
* :
* :
* :
*/
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { LayoutModule } from '../../layout/layout.module';
import { SharedModule } from '../../../modules/common/shared/shared.module';
import { UserModule } from '../../../modules/user/user.module';
import { UserSupportModule } from '../../../modules/user-support/user-support.module';
import { ArticleRoutingPageModule } from './article-routing.page.module';
import { CartoonsModule } from '../../../modules/cartoons/cartoons.module';
import { IllustrationsModule } from '../../../modules/illustrations/illustrations.module';
import { NovelModule } from '../../../modules/novel/novel.module';
import { AttachmentsModule } from '../../../modules/attachments/attachments.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { COMPONENTS } from './component';
import { ArticleModule } from 'src/modules/article/article.module';
@NgModule({
imports: [
CommonModule,
TranslateModule,
SharedModule,
LayoutModule,
CartoonsModule,
IllustrationsModule,
NovelModule,
ArticleRoutingPageModule,
FormsModule,
ReactiveFormsModule,
AttachmentsModule,
UserModule,
UserSupportModule,
ArticleModule
],
declarations: [...COMPONENTS]
})
export class ArticlePageModule { }

View File

@ -0,0 +1,17 @@
<div
*ngIf="(articleMongodb$ | async) as articleMongodb"
[ngSwitch]="articleMongodb.type"
>
<app-illustrations-details
*ngSwitchCase="'Illustrations'"
[article]="articleMongodb.article"
></app-illustrations-details>
<app-cartoons-details
*ngSwitchCase="'Cartoons'"
[article]="articleMongodb.article"
></app-cartoons-details>
<app-novel-details
*ngSwitchCase="'Novel'"
[article]="articleMongodb.article"
></app-novel-details>
</div>

View File

@ -0,0 +1,76 @@
/**
* : details.page.component.ts
* 작성일자: 2018-12-20
* : 박병준
* : details page component를 .
* :
* :
* :
* :
*/
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable, of } from 'rxjs';
import {
switchMap,
tap,
map,
catchError,
take,
finalize
} from 'rxjs/operators';
import { ArticleGetService } from '../../../../../modules/article/service/article.get.service';
import { ArticleType } from '../../../../../shared/article/type/article-type.type';
import { ArticleMongodb } from '../../../../../shared/article/model/article.mongodb.model';
@Component({
selector: 'app-page-cartoons-details',
templateUrl: './details.page.component.html',
styleUrls: ['./details.page.component.scss']
})
export class DetailsPageComponent implements OnInit {
aid: string;
articleMongodb$: Observable<ArticleMongodb>;
constructor(
private activatedRoute: ActivatedRoute,
private articleGetService: ArticleGetService
) {}
ngOnInit(): void {
this.articleMongodb$ = this.activatedRoute.paramMap.pipe(
take(1),
tap(() => {
// display progressbar
}),
switchMap(params => {
// (+) before `params.get()` turns the string into a number
// (+) before `params.get()` turns the string into a number
this.aid = params.get('aid');
console.log('aid >> ' + this.aid);
if (this.aid) {
return this.articleGetService.get(this.aid).pipe(
map((articleMongodb: ArticleMongodb) => {
console.log(articleMongodb);
return articleMongodb;
}),
catchError(error => {
console.log(error);
return of(error);
})
);
}
}),
catchError(error => {
console.log(error);
return of();
}),
finalize(() => {
// remove progressbar
})
);
}
}

View File

@ -0,0 +1 @@
Emotions

View File

@ -0,0 +1,23 @@
/**
* : emotions.page.component.ts
* 작성일자: 2018-12-20
* : 박병준
* : emotions page component를 .
* :
* :
* :
* :
*/
import { Component } from '@angular/core';
@Component({
selector: 'app-page-cartoons-emotions',
templateUrl: './emotions.page.component.html',
styleUrls: ['./emotions.page.component.scss'],
})
export class EmotionsPageComponent {
constructor() {
}
}

View File

@ -0,0 +1,13 @@
import { DetailsPageComponent } from './details/details.page.component';
import { EmotionsPageComponent } from './emotions/emotions.page.component';
import { ProfilePageComponent } from './profile/profile.page.component';
import { WritePageComponent } from './write/write.page.component';
import { SEARCH_COMPONENTS } from './search';
export const COMPONENTS = [
DetailsPageComponent,
EmotionsPageComponent,
ProfilePageComponent,
WritePageComponent,
...SEARCH_COMPONENTS,
];

View File

@ -0,0 +1,82 @@
<app-user-profile-box></app-user-profile-box>
<div #profileScroll class="my-profile">
<virtual-scroller #scroll [items]="articleMongodbList" [enableUnequalChildrenSizes]="true" [parentScroll]="profileScroll"
(vsEnd)="onVSEnd($event)" (vsUpdate)="viewPortItems = $event">
<div *ngIf="user">
<app-user-profile-box [uid]="uid" [articleCount]="articleCount" [followersCount]="followersCount"
[followingCount]="followingCount" (isFollowingYn)="setIsFollowing($event)" [favorTagList]="favorTagList">
</app-user-profile-box>
<mat-tab-group animationDuration="0ms" mat-stretch-tabs class="example-stretched-tabs" (selectedTabChange)="onSelectedTabChange($event)">
<mat-tab>
<ng-template mat-tab-label>
<mat-icon svgIcon="tab_mypro_01">article thumbnails</mat-icon>
</ng-template>
<!-- 다른 component 사용 test
randomImages => 이미지 url 리스트,
columnCnt => 이미지 column 갯수,
viewTitleYn => 타이틀을 보여줄 지 여부
-->
<!-- <app-article-display-grid [articles]="articleGridList" [columnCnt]="3" [viewTitleYn]="true">
</app-article-display-grid> -->
<div #container class="thumb-wrap">
<article *ngFor="let articleMongodb of viewPortItems" class="article-display-tile">
<ng-container [ngSwitch]="articleMongodb.type">
<app-illustrations-display-tile *ngSwitchCase="'Illustrations'" [article]="articleMongodb.article"
class="pickMeup"></app-illustrations-display-tile>
<app-cartoons-display-tile *ngSwitchCase="'Cartoons'" [article]="articleMongodb.article" class="pickMeup"></app-cartoons-display-tile>
<app-novel-display-tile *ngSwitchCase="'Novel'" [article]="articleMongodb.article" class="pickMeup">
</app-novel-display-tile>
</ng-container>
</article>
</div>
</mat-tab>
<mat-tab>
<ng-template mat-tab-label>
<mat-icon svgIcon="tab_mypro_02">article cards</mat-icon>
</ng-template>
<!-- one card list가 들어갈 자리 -->
<div #container>
<article *ngFor="let articleMongodb of viewPortItems" class="article-display-card">
<ng-container [ngSwitch]="articleMongodb.type">
<app-illustrations-display-card *ngSwitchCase="'Illustrations'" [article]="articleMongodb.article">
</app-illustrations-display-card>
<app-cartoons-display-card *ngSwitchCase="'Cartoons'" [article]="articleMongodb.article"
[authorFollowYn]="authorFollowYn">
</app-cartoons-display-card>
<app-novel-display-card *ngSwitchCase="'Novel'" [article]="articleMongodb.article">
</app-novel-display-card>
</ng-container>
</article>
</div>
</mat-tab>
<!-- 팔로워 목록 -->
<mat-tab>
<ng-template mat-tab-label>
<mat-icon svgIcon="tab_mypro_03">followerList</mat-icon>
</ng-template>
<div *ngIf="user">
<ng-container *ngFor="let followerUser of followersList">
<app-user-followers-display-list [followers]="followerUser" [uid]="uid" [currUser]="followerUser.user"></app-user-followers-display-list>
</ng-container>
<!-- <app-user-followers-display-list [followers]="followersList"></app-user-followers-display-list> -->
</div>
</mat-tab>
<!-- 팔로잉 목록 -->
<mat-tab>
<ng-template mat-tab-label>
<mat-icon svgIcon="tab_mypro_04">followingList</mat-icon>
</ng-template>
<div *ngIf="user">
<ng-container *ngFor="let followerUser of followingList">
<app-user-followers-display-list [followers]="followerUser" [uid]="uid" [currUser]="followerUser.target"></app-user-followers-display-list>
</ng-container>
<!-- <app-user-followers-display-list [followers]="followingList"></app-user-followers-display-list> -->
</div>
</mat-tab>
</mat-tab-group>
</div>
</virtual-scroller>
</div>

Some files were not shown because too many files have changed in this diff Show More