From 574ecf3fd39ad9fa0c88ad031d926938f10f2c5e Mon Sep 17 00:00:00 2001 From: Trung Vo Date: Sat, 23 Jan 2021 09:16:06 +0800 Subject: [PATCH 01/33] Align the Angular version readme --- README.md | 18 +- frontend/documentation.json | 842 +++++++----------- .../navbar-left/navbar-left.component.html | 8 +- .../work-in-progress.component.html | 12 +- frontend/src/assets/data/project.json | 2 +- frontend/src/index.html | 8 +- frontend/src/index.prod.html | 8 +- frontend/src/stories/Introduction.stories.mdx | 2 +- 8 files changed, 375 insertions(+), 525 deletions(-) diff --git a/README.md b/README.md index 81662a64..6ad2ea17 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# A simplified Jira clone built with Angular 10, Akita and ng-zorro +# A simplified Jira clone built with Angular, Akita and ng-zorro > Phase two will not be completed as planned. Both [Chau Tran][chau] and I was too busy with some other commitments. View our [working in progress Graph QL branch][gql]. > @@ -8,13 +8,13 @@ There have been a handful of cool Jira-cloned apps written in `React`/`VueJS`, which makes me wonder **Why not Angular**? And here you go. -This is not only a simplified Jira clone built with Angular 9, but also an example of a **modern**, **real-world** Angular codebase. +This is not only a simplified Jira clone built with Angular, but also an example of a **modern**, **real-world** Angular codebase.
Table Of Content

-- [A simplified Jira clone built with Angular 10, Akita and ng-zorro](#a-simplified-jira-clone-built-with-angular-10-akita-and-ng-zorro) +- [A simplified Jira clone built with Angular, Akita and ng-zorro](#a-simplified-jira-clone-built-with-angular-akita-and-ng-zorro) - [Merry Christmas](#merry-christmas) - [Working application](#working-application) - [Storybook](#storybook) @@ -50,13 +50,13 @@ This is not only a simplified Jira clone built with Angular 9, but also an examp Thank you for your support! -> https://jira.trungk18.com/project/issue/2020 -![Jira clone built with Angular 9 and Akita][christmas2020] +![Jira clone built with Angular and Akita][christmas2020] ## Working application Check out the **live demo** -> https://jira.trungk18.com -![Jira clone built with Angular 9 and Akita][demo] +![Jira clone built with Angular and Akita][demo] ## Storybook @@ -77,7 +77,7 @@ This is the collection of components that I wrote for [jira.trungk18.com][jira], Check out the **storybook demo** -> https://jira-storybook.trungk18.com/ -![Jira clone built with Angular 9 and Akita][demo-storybook] +![Jira clone built with Angular and Akita][demo-storybook] ## Support @@ -138,7 +138,7 @@ As requested by [@eric_cart][eric_cart], I draw a simple high-level design for t I have an AppModule that will import: -![Jira clone built with Angular 9 and Akita - Application architecture][application-architecture] +![Jira clone built with Angular and Akita - Application architecture][application-architecture] - Angular needed modules such as `BrowserModule` and any module that need to run `forRoot`. - The application core modules such as `AuthModule` that need to available on the whole platform. @@ -149,7 +149,7 @@ I have an AppModule that will import: As I am using [Akita][akita] state management, I follow the Akita documentation for the data flow. I found it is simple to understand comparing with ngrx terms (`reducer`, `selector`, `effect`) -![Jira clone built with Angular 9 and Akita - Simple data interaction flow][interaction-data-flow] +![Jira clone built with Angular and Akita - Simple data interaction flow][interaction-data-flow] I set up a [project state with initial data][project-store]. The main heavy lifting part I think is the [project service][project-service], it contains all the interacting with [project store][project-store]. Such as after fetching the project successfully, I update the store immediately inside the service itself. The last lego block was to expose the data through [project query][project-query]. Any components can start to inject [project query][project-query] and consume data from there. @@ -239,7 +239,7 @@ According to waka time report, I have spent about 45 hours working on this proje I really enjoyed working on this project. The interactive kanban board took me sometimes, it is challenging but exciting at the same time. -![Jira clone built with Angular 9 and Akita - Time spending][time] +![Jira clone built with Angular and Akita - Time spending][time] ## What's currently missing? diff --git a/frontend/documentation.json b/frontend/documentation.json index 0654cd94..015ab063 100644 --- a/frontend/documentation.json +++ b/frontend/documentation.json @@ -430,7 +430,7 @@ }, { "name": "AuthService", - "id": "injectable-AuthService-73cf92c7edf89f99ca62fd93fee78f54", + "id": "injectable-AuthService-a97ceeda1662316451e042e102b84ebe", "file": "src/app/project/auth/auth.service.ts", "properties": [ { @@ -438,7 +438,7 @@ "type": "string", "optional": false, "description": "", - "line": 11, + "line": 12, "modifierKind": [ 112 ] @@ -455,7 +455,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 16, + "line": 17, "jsdoctags": [ { "type": "LoginPayload", @@ -467,7 +467,7 @@ } ], "description": "", - "sourceCode": "import { HttpClient } from '@angular/common/http';\nimport { Injectable } from '@angular/core';\nimport { JUser } from '@trungk18/interface/user';\nimport { of } from 'rxjs';\nimport { catchError, finalize, map } from 'rxjs/operators';\nimport { AuthStore } from './auth.store';\nimport { environment } from 'src/environments/environment';\n\n@Injectable({ providedIn: 'root' })\nexport class AuthService {\n private baseUrl: string;\n constructor(private _http: HttpClient, private _store: AuthStore) {\n this.baseUrl = environment.apiUrl;\n }\n\n login({ email = '', password = '' }: LoginPayload) {\n this._store.setLoading(true);\n this._http\n .get(`${this.baseUrl}/auth.json`)\n .pipe(\n map((user) => {\n this._store.update((state) => ({\n ...state,\n ...user\n }));\n }),\n finalize(() => {\n this._store.setLoading(false);\n }),\n catchError((err) => {\n this._store.setError(err);\n return of(err);\n })\n )\n .subscribe();\n }\n}\n\nexport class LoginPayload {\n email: string;\n password: string;\n constructor() {\n this.email = 'trungk18@gmail.com';\n this.password = `${new Date().getTime()}`;\n }\n}\n", + "sourceCode": "import { HttpClient } from '@angular/common/http';\nimport { Injectable } from '@angular/core';\nimport { JUser } from '@trungk18/interface/user';\nimport { of } from 'rxjs';\nimport { catchError, finalize, map } from 'rxjs/operators';\nimport { AuthStore } from './auth.store';\nimport { environment } from 'src/environments/environment';\nimport { LoginPayload } from '@trungk18/project/auth/loginPayload';\n\n@Injectable({ providedIn: 'root' })\nexport class AuthService {\n private baseUrl: string;\n constructor(private _http: HttpClient, private _store: AuthStore) {\n this.baseUrl = environment.apiUrl;\n }\n\n login({ email = '', password = '' }: LoginPayload) {\n this._store.setLoading(true);\n this._http\n .get(`${this.baseUrl}/auth.json`)\n .pipe(\n map((user) => {\n this._store.update((state) => ({\n ...state,\n ...user\n }));\n }),\n finalize(() => {\n this._store.setLoading(false);\n }),\n catchError((err) => {\n this._store.setError(err);\n return of(err);\n })\n )\n .subscribe();\n }\n}\n\n\n", "constructorObj": { "name": "constructor", "description": "", @@ -481,7 +481,7 @@ "type": "AuthStore" } ], - "line": 11, + "line": 12, "jsdoctags": [ { "name": "_http", @@ -708,72 +708,29 @@ }, { "name": "GoogleAnalyticsService", - "id": "injectable-GoogleAnalyticsService-15d59bdd8ff8dfd38fa153d52391f837", + "id": "injectable-GoogleAnalyticsService-9088c886dc4afa5810bfb1ecfcb12add", "file": "src/app/core/services/google-analytics.service.ts", - "properties": [], - "methods": [ + "properties": [ + { + "name": "gtag", + "type": "any", + "optional": false, + "description": "", + "line": 8 + }, { "name": "sendEvent", - "args": [ - { - "name": "eventName", - "type": "string" - }, - { - "name": "eventCategory", - "type": "string" - }, - { - "name": "eventLabel", - "type": "string", - "defaultValue": "null" - }, - { - "name": "eventValue", - "type": "number", - "defaultValue": "null" - } - ], + "defaultValue": "() => {...}", + "type": "", "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 11, + "description": "", + "line": 15, "modifierKind": [ 114 - ], - "jsdoctags": [ - { - "name": "eventName", - "type": "string", - "tagName": { - "text": "param" - } - }, - { - "name": "eventCategory", - "type": "string", - "tagName": { - "text": "param" - } - }, - { - "name": "eventLabel", - "type": "string", - "defaultValue": "null", - "tagName": { - "text": "param" - } - }, - { - "name": "eventValue", - "type": "number", - "defaultValue": "null", - "tagName": { - "text": "param" - } - } ] - }, + } + ], + "methods": [ { "name": "sendPageView", "args": [ @@ -785,7 +742,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 27, + "line": 31, "modifierKind": [ 114 ], @@ -801,18 +758,18 @@ } ], "description": "", - "sourceCode": "import { Injectable } from '@angular/core';\ndeclare var gtag: any;\nconst GOOGLE_ANALYTICS_ID = 'UA-80363801-4';\n@Injectable({\n providedIn: 'root'\n})\nexport class GoogleAnalyticsService {\n constructor() {\n }\n\n public sendEvent(\n eventName: string,\n eventCategory: string,\n eventLabel: string = null,\n eventValue: number = null\n ) {\n if (!gtag) {\n return;\n }\n gtag('event', eventName, {\n event_category: eventCategory,\n event_label: eventLabel,\n value: eventValue\n });\n }\n\n public sendPageView(url: string) {\n if (!gtag) {\n return;\n }\n gtag('config', GOOGLE_ANALYTICS_ID, { page_path: url });\n }\n}\n", + "sourceCode": "import { Injectable } from '@angular/core';\ndeclare var gtag: any;\nconst GOOGLE_ANALYTICS_ID = 'UA-80363801-4';\n@Injectable({\n providedIn: 'root'\n})\nexport class GoogleAnalyticsService {\n gtag: any;\n constructor() {\n if (typeof gtag !== 'undefined') {\n this.gtag = gtag;\n }\n }\n\n public sendEvent = (\n eventName: string,\n eventCategory: string,\n eventLabel: string = null,\n eventValue: number = null\n ) => {\n if (!this.gtag) {\n return;\n }\n this.gtag('event', eventName, {\n event_category: eventCategory,\n event_label: eventLabel,\n value: eventValue\n });\n }\n\n public sendPageView(url: string) {\n if (!this.gtag) {\n return;\n }\n this.gtag('config', GOOGLE_ANALYTICS_ID, { page_path: url });\n }\n}\n", "constructorObj": { "name": "constructor", "description": "", "args": [], - "line": 7 + "line": 8 }, "type": "injectable" }, { "name": "ProjectQuery", - "id": "injectable-ProjectQuery-2f11b0cdf53f0286e07279c928574f1f", + "id": "injectable-ProjectQuery-5043c96ae676ad526dce8352ec24bc70", "file": "src/app/project/state/project/project.query.ts", "properties": [ { @@ -876,7 +833,7 @@ "optional": false, "returnType": "any", "typeParameters": [], - "line": 36, + "line": 35, "jsdoctags": [ { "name": "issueId", @@ -889,7 +846,7 @@ } ], "description": "", - "sourceCode": "import { ProjectState, ProjectStore } from './project.store';\nimport { Injectable } from '@angular/core';\nimport { Query } from '@datorama/akita';\nimport { IssueStatus, JIssue } from '@trungk18/interface/issue';\nimport { filter, map, delay } from 'rxjs/operators';\nimport { of, Observable } from 'rxjs';\n@Injectable({\n providedIn: 'root'\n})\nexport class ProjectQuery extends Query {\n constructor(protected store: ProjectStore) {\n super(store);\n }\n isLoading$ = this.selectLoading();\n all$ = this.select();\n issues$ = this.select('issues');\n users$ = this.select('users');\n\n lastIssuePosition = (status: IssueStatus): number => {\n const raw = this.store.getValue();\n const issuesByStatus = raw.issues.filter(x => x.status === status);\n return issuesByStatus.length;\n }\n\n issueByStatusSorted$ = (status: IssueStatus): Observable => {\n return this.issues$.pipe(\n map((issues) => {\n const filteredIssues = issues\n .filter((x) => x.status === status)\n .sort((a, b) => a.listPosition - b.listPosition);\n return filteredIssues;\n })\n );\n }\n\n issueById$(issueId: string){\n return this.issues$.pipe(\n delay(500),\n map((issues) => {\n const issue = issues.find(x => x.id === issueId);\n return issue;\n })\n );\n }\n}\n", + "sourceCode": "import { ProjectState, ProjectStore } from './project.store';\nimport { Injectable } from '@angular/core';\nimport { Query } from '@datorama/akita';\nimport { IssueStatus, JIssue } from '@trungk18/interface/issue';\nimport { map, delay } from 'rxjs/operators';\nimport { Observable } from 'rxjs';\n@Injectable({\n providedIn: 'root'\n})\nexport class ProjectQuery extends Query {\n constructor(protected store: ProjectStore) {\n super(store);\n }\n isLoading$ = this.selectLoading();\n all$ = this.select();\n issues$ = this.select('issues');\n users$ = this.select('users');\n\n lastIssuePosition = (status: IssueStatus): number => {\n const raw = this.store.getValue();\n const issuesByStatus = raw.issues.filter(x => x.status === status);\n return issuesByStatus.length;\n }\n\n issueByStatusSorted$ = (status: IssueStatus): Observable => {\n return this.issues$.pipe(\n map((issues) => {\n return issues\n .filter((x) => x.status === status)\n .sort((a, b) => a.listPosition - b.listPosition);\n })\n );\n }\n\n issueById$(issueId: string){\n return this.issues$.pipe(\n delay(500),\n map((issues) => {\n return issues.find(x => x.id === issueId);\n })\n );\n }\n}\n", "constructorObj": { "name": "constructor", "description": "", @@ -1597,15 +1554,15 @@ }, { "name": "LoginPayload", - "id": "class-LoginPayload-73cf92c7edf89f99ca62fd93fee78f54", - "file": "src/app/project/auth/auth.service.ts", + "id": "class-LoginPayload-39ccb3fe79ba50cac141f7f6c2342bf2", + "file": "src/app/project/auth/loginPayload.ts", "type": "class", - "sourceCode": "import { HttpClient } from '@angular/common/http';\nimport { Injectable } from '@angular/core';\nimport { JUser } from '@trungk18/interface/user';\nimport { of } from 'rxjs';\nimport { catchError, finalize, map } from 'rxjs/operators';\nimport { AuthStore } from './auth.store';\nimport { environment } from 'src/environments/environment';\n\n@Injectable({ providedIn: 'root' })\nexport class AuthService {\n private baseUrl: string;\n constructor(private _http: HttpClient, private _store: AuthStore) {\n this.baseUrl = environment.apiUrl;\n }\n\n login({ email = '', password = '' }: LoginPayload) {\n this._store.setLoading(true);\n this._http\n .get(`${this.baseUrl}/auth.json`)\n .pipe(\n map((user) => {\n this._store.update((state) => ({\n ...state,\n ...user\n }));\n }),\n finalize(() => {\n this._store.setLoading(false);\n }),\n catchError((err) => {\n this._store.setError(err);\n return of(err);\n })\n )\n .subscribe();\n }\n}\n\nexport class LoginPayload {\n email: string;\n password: string;\n constructor() {\n this.email = 'trungk18@gmail.com';\n this.password = `${new Date().getTime()}`;\n }\n}\n", + "sourceCode": "export class LoginPayload {\n email: string;\n password: string;\n constructor() {\n this.email = 'trungk18@gmail.com';\n this.password = `${new Date().getTime()}`;\n }\n}\n", "constructorObj": { "name": "constructor", "description": "", "args": [], - "line": 41 + "line": 3 }, "properties": [ { @@ -1613,14 +1570,14 @@ "type": "string", "optional": false, "description": "", - "line": 40 + "line": 2 }, { "name": "password", "type": "string", "optional": false, "description": "", - "line": 41 + "line": 3 } ], "methods": [], @@ -1871,11 +1828,11 @@ "directives": [ { "name": "AutofocusDirective", - "id": "directive-AutofocusDirective-2c83d6ba5fe0f9f69bee78ad14598520", + "id": "directive-AutofocusDirective-4ed6dd09b0f4119701c7ea034dc26e4e", "file": "src/app/core/directives/autofocus.directive.ts", "type": "directive", "description": "", - "sourceCode": "import { AfterContentInit, Directive, ElementRef, Input, OnDestroy } from '@angular/core';\n\nconst BASE_TIMER_DELAY = 10;\n\n@Directive({\n selector: '[jAutofocus]'\n})\nexport class AutofocusDirective implements AfterContentInit, OnDestroy {\n @Input('jAutofocus') enable: boolean | string;\n @Input() timerDelay: number = BASE_TIMER_DELAY;\n\n private elementRef: ElementRef;\n private timer: any;\n\n constructor(elementRef: ElementRef) {\n this.elementRef = elementRef;\n this.timer = null;\n }\n\n setDefaultValue() {\n if (this.enable === false) {\n return;\n }\n this.enable = true;\n }\n\n public ngAfterContentInit(): void {\n this.setDefaultValue();\n if (this.enable) {\n this.startFocusWorkflow();\n }\n }\n\n public ngOnDestroy(): void {\n this.stopFocusWorkflow();\n }\n\n private startFocusWorkflow(): void {\n if (this.timer) {\n return;\n }\n\n this.timer = setTimeout((): void => {\n this.timer = null;\n this.elementRef.nativeElement.focus();\n }, this.timerDelay);\n }\n\n private stopFocusWorkflow(): void {\n clearTimeout(this.timer);\n this.timer = null;\n }\n}\n", + "sourceCode": "import { AfterContentInit, Directive, ElementRef, Input, OnDestroy } from '@angular/core';\n\nconst BASE_TIMER_DELAY = 10;\n\n@Directive({\n selector: '[jAutofocus]'\n})\nexport class AutofocusDirective implements AfterContentInit, OnDestroy {\n @Input('jAutofocus') enable: boolean | string;\n @Input() timerDelay: number = BASE_TIMER_DELAY;\n\n private elementRef: ElementRef;\n private timer: any;\n\n constructor(elementRef: ElementRef) {\n this.elementRef = elementRef;\n this.timer = null;\n }\n\n public ngAfterContentInit(): void {\n this.setDefaultValue();\n if (this.enable) {\n this.startFocusWorkflow();\n }\n }\n\n public ngOnDestroy(): void {\n this.stopFocusWorkflow();\n }\n\n private setDefaultValue() {\n if (this.enable === false) {\n return;\n }\n this.enable = true;\n }\n\n private startFocusWorkflow(): void {\n if (this.timer) {\n return;\n }\n\n this.timer = setTimeout((): void => {\n this.timer = null;\n this.elementRef.nativeElement.focus();\n }, this.timerDelay);\n }\n\n private stopFocusWorkflow(): void {\n clearTimeout(this.timer);\n this.timer = null;\n }\n}\n", "selector": "[jAutofocus]", "providers": [], "inputsClass": [ @@ -1923,7 +1880,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 27, + "line": 20, "modifierKind": [ 114 ] @@ -1934,7 +1891,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 34, + "line": 27, "modifierKind": [ 114 ] @@ -1945,7 +1902,10 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 20 + "line": 31, + "modifierKind": [ + 112 + ] }, { "name": "startFocusWorkflow", @@ -2171,11 +2131,11 @@ } } }, - "templateData": "

\n
\n
\n Create issue\n
\n
\n \n \n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n \n
\n
\n \n \n \n
\n
\n \n \n \n
\n
\n \n Create Issue\n \n \n Cancel\n \n
\n
\n
" + "templateData": "
\n
\n
\n Create issue\n
\n
\n \n \n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n \n
\n
\n \n \n \n
\n
\n \n \n \n
\n
\n \n Create Issue\n \n \n Cancel\n \n
\n
\n
" }, { "name": "AppComponent", - "id": "component-AppComponent-c5a07f40e6fc0207a348406a1ee821da", + "id": "component-AppComponent-24478e46957da0d685d13f9d2000be4c", "file": "src/app/app.component.ts", "encapsulation": [ "ViewEncapsulation.None" @@ -2196,6 +2156,14 @@ "inputsClass": [], "outputsClass": [], "propertiesClass": [ + { + "name": "handleGoogleAnalytics", + "defaultValue": "() => {...}", + "type": "", + "optional": false, + "description": "", + "line": 29 + }, { "name": "projectQuery", "type": "ProjectQuery", @@ -2218,21 +2186,13 @@ } ], "methodsClass": [ - { - "name": "handleGoogleAnalytics", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 28 - }, { "name": "ngAfterViewInit", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 36 + "line": 35 } ], "hostBindings": [], @@ -2240,11 +2200,11 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, ViewEncapsulation, AfterViewInit, ChangeDetectorRef } from '@angular/core';\nimport { Router, NavigationEnd } from '@angular/router';\nimport { environment } from '../environments/environment';\nimport { ProjectQuery } from './project/state/project/project.query';\nimport { ProjectService } from './project/state/project/project.service';\nimport { GoogleAnalyticsService } from './core/services/google-analytics.service';\n\n@Component({\n selector: 'app-root',\n templateUrl: './app.component.html',\n styleUrls: ['./app.component.scss'],\n encapsulation: ViewEncapsulation.None\n})\nexport class AppComponent implements AfterViewInit {\n constructor(\n public router: Router,\n public projectQuery: ProjectQuery,\n private _cdr: ChangeDetectorRef,\n private _projectService: ProjectService,\n private _googleAnalytics: GoogleAnalyticsService\n ) {\n this._projectService.setLoading(true);\n if (environment.production) {\n this.handleGoogleAnalytics();\n }\n }\n\n handleGoogleAnalytics() {\n this.router.events.subscribe((event) => {\n if (event instanceof NavigationEnd) {\n this._googleAnalytics.sendPageView(event.urlAfterRedirects);\n }\n });\n }\n\n ngAfterViewInit() {\n this._cdr.detectChanges();\n }\n}\n", + "sourceCode": "import { Component, ViewEncapsulation, AfterViewInit, ChangeDetectorRef } from '@angular/core';\nimport { Router, NavigationEnd } from '@angular/router';\nimport { environment } from '../environments/environment';\nimport { ProjectQuery } from './project/state/project/project.query';\nimport { ProjectService } from './project/state/project/project.service';\nimport { GoogleAnalyticsService } from './core/services/google-analytics.service';\n\n@Component({\n selector: 'app-root',\n templateUrl: './app.component.html',\n styleUrls: ['./app.component.scss'],\n encapsulation: ViewEncapsulation.None\n})\nexport class AppComponent implements AfterViewInit {\n constructor(\n public router: Router,\n public projectQuery: ProjectQuery,\n private _cdr: ChangeDetectorRef,\n private _projectService: ProjectService,\n private _googleAnalytics: GoogleAnalyticsService\n ) {\n this._projectService.setLoading(true);\n\n if (environment.production) {\n this.router.events.subscribe(this.handleGoogleAnalytics);\n }\n }\n\n handleGoogleAnalytics = (event: any): void => {\n if (event instanceof NavigationEnd) {\n this._googleAnalytics.sendPageView(event.urlAfterRedirects);\n }\n }\n\n ngAfterViewInit() {\n this._cdr.detectChanges();\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { - "data": "@import 'http://github.com/core/styles/custom';\n\n#app-frame {\n position: absolute;\n width: 100vw;\n height: 100vh;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n\n > nz-spin {\n @apply flex flex-grow;\n\n > .ant-spin-container {\n @apply flex flex-grow;\n }\n }\n}\n\napp-project {\n @apply flex;\n flex-grow: 1;\n flex-direction: column;\n /* for Firefox */\n min-height: 0;\n}\n\nboard-dnd {\n @apply flex flex-grow;\n}\n\n.global-spinner {\n @apply min-h-screen;\n .ant-spin {\n max-height: unset !important;\n }\n}\n\n//ng-zorro override\nbody,\n.ant-modal {\n @apply text-textDark;\n}\n\n.ant-modal-body {\n padding: 0;\n}\n", + "data": "@import 'http://github.com/core/styles/custom';\n\n#app-frame {\n position: absolute;\n width: 100vw;\n height: 100vh;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n\n > nz-spin {\n @apply flex flex-grow;\n\n > .ant-spin-container {\n @apply flex flex-grow;\n }\n }\n}\n\napp-project {\n @apply flex;\n flex-grow: 1;\n flex-direction: column;\n /* for Firefox */\n min-height: 0;\n}\n\nboard-dnd {\n @apply flex flex-grow;\n}\n\n.global-spinner {\n @apply min-h-screen;\n .ant-spin {\n max-height: unset !important;\n }\n}\n\n//ng-zorro override\nbody {\n overflow: hidden;\n}\n\nbody,\n.ant-modal {\n @apply text-textDark;\n}\n\n.ant-modal-body {\n padding: 0;\n}\n", "styleUrl": "./app.component.scss" } ], @@ -2320,7 +2280,7 @@ }, { "name": "AvatarComponent", - "id": "component-AvatarComponent-e9c46e8eb86a24509f8138855100c3a0", + "id": "component-AvatarComponent-0a4aea88bd44d1bef72a211595bb15a3", "file": "src/app/jira-control/avatar/avatar.component.ts", "encapsulation": [], "entryComponents": [], @@ -2349,7 +2309,7 @@ }, { "name": "name", - "defaultValue": "\"\"", + "defaultValue": "''", "line": 11 }, { @@ -2371,7 +2331,7 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, Input } from '@angular/core';\n\n@Component({\n selector: 'j-avatar',\n templateUrl: './avatar.component.html',\n styleUrls: ['./avatar.component.scss']\n})\nexport class AvatarComponent {\n @Input() avatarUrl: string;\n @Input() size = 12;\n @Input() name = \"\";\n @Input() rounded = true;\n @Input() className = '';\n\n get style() {\n return {\n width: `${this.size}px`,\n height: `${this.size}px`,\n 'background-image': `url('http://github.com/$%7Bthis.avatarUrl%7D')`,\n 'border-radius': this.rounded ? '100%' : '3px'\n };\n } \n}\n", + "sourceCode": "import { Component, Input } from '@angular/core';\n\n@Component({\n selector: 'j-avatar',\n templateUrl: './avatar.component.html',\n styleUrls: ['./avatar.component.scss']\n})\nexport class AvatarComponent {\n @Input() avatarUrl: string;\n @Input() size = 12;\n @Input() name = '';\n @Input() rounded = true;\n @Input() className = '';\n\n get style() {\n return {\n width: `${this.size}px`,\n height: `${this.size}px`,\n 'background-image': `url('http://github.com/$%7Bthis.avatarUrl%7D')`,\n 'border-radius': this.rounded ? '100%' : '3px'\n };\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -2395,7 +2355,7 @@ }, { "name": "BoardComponent", - "id": "component-BoardComponent-912992667e943e68c42ed5b64c7bfda6", + "id": "component-BoardComponent-c5f4b6faebd0faa9afb327f7c7981d89", "file": "src/app/project/pages/board/board.component.ts", "encapsulation": [], "entryComponents": [], @@ -2424,21 +2384,13 @@ } ], "methodsClass": [ - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 14 - }, { "name": "sendTwitterEventButton", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 16 + "line": 14 } ], "hostBindings": [], @@ -2446,7 +2398,7 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit } from '@angular/core';\nimport { GoogleAnalyticsService } from '@trungk18/core/services/google-analytics.service';\n\n@Component({\n selector: 'board',\n templateUrl: './board.component.html',\n styleUrls: ['./board.component.scss']\n})\nexport class BoardComponent implements OnInit {\n breadcrumbs: string[] = ['Projects', 'Angular Jira Clone', 'Kanban Board'];\n\n constructor(private _googleAnalytics: GoogleAnalyticsService) {}\n\n ngOnInit(): void {}\n\n sendTwitterEventButton() {\n this._googleAnalytics.sendEvent('Share Twitter', 'button');\n }\n}\n", + "sourceCode": "import { Component } from '@angular/core';\nimport { GoogleAnalyticsService } from '@trungk18/core/services/google-analytics.service';\n\n@Component({\n selector: 'board',\n templateUrl: './board.component.html',\n styleUrls: ['./board.component.scss']\n})\nexport class BoardComponent {\n breadcrumbs: string[] = ['Projects', 'Angular Jira Clone', 'Kanban Board'];\n\n constructor(private _googleAnalytics: GoogleAnalyticsService) {}\n\n sendTwitterEventButton() {\n this._googleAnalytics.sendEvent('Share Twitter', 'button');\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -2475,14 +2427,11 @@ } ] }, - "implements": [ - "OnInit" - ], "templateData": "" }, { "name": "BoardDndComponent", - "id": "component-BoardDndComponent-0685eaa0e08945efb8dac7191b84fab9", + "id": "component-BoardDndComponent-e43e798fcaf484725f451476382165e6", "file": "src/app/project/components/board/board-dnd/board-dnd.component.ts", "encapsulation": [], "entryComponents": [], @@ -2528,20 +2477,11 @@ ] } ], - "methodsClass": [ - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 22 - } - ], + "methodsClass": [], "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit } from '@angular/core';\nimport { UntilDestroy } from '@ngneat/until-destroy';\nimport { IssueStatus } from '@trungk18/interface/issue';\nimport { ProjectQuery } from '@trungk18/project/state/project/project.query';\nimport { AuthQuery } from '@trungk18/project/auth/auth.query';\n@UntilDestroy()\n@Component({\n selector: 'board-dnd',\n templateUrl: './board-dnd.component.html',\n styleUrls: ['./board-dnd.component.scss']\n})\nexport class BoardDndComponent implements OnInit {\n issueStatuses: IssueStatus[] = [\n IssueStatus.BACKLOG,\n IssueStatus.SELECTED,\n IssueStatus.IN_PROGRESS,\n IssueStatus.DONE\n ];\n\n constructor(public projectQuery: ProjectQuery, public authQuery: AuthQuery) {}\n\n ngOnInit(): void {}\n}\n", + "sourceCode": "import { Component } from '@angular/core';\nimport { UntilDestroy } from '@ngneat/until-destroy';\nimport { IssueStatus } from '@trungk18/interface/issue';\nimport { ProjectQuery } from '@trungk18/project/state/project/project.query';\nimport { AuthQuery } from '@trungk18/project/auth/auth.query';\n@UntilDestroy()\n@Component({\n selector: 'board-dnd',\n templateUrl: './board-dnd.component.html',\n styleUrls: ['./board-dnd.component.scss']\n})\nexport class BoardDndComponent {\n issueStatuses: IssueStatus[] = [\n IssueStatus.BACKLOG,\n IssueStatus.SELECTED,\n IssueStatus.IN_PROGRESS,\n IssueStatus.DONE\n ];\n\n constructor(public projectQuery: ProjectQuery, public authQuery: AuthQuery) {}\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -2581,9 +2521,6 @@ } ] }, - "implements": [ - "OnInit" - ], "templateData": "
\n
\n
\n
" }, { @@ -3023,7 +2960,7 @@ }, { "name": "BreadcrumbsComponent", - "id": "component-BreadcrumbsComponent-a5dfba8296f689b8e4b362c4d0ef1188", + "id": "component-BreadcrumbsComponent-2a7ebe3885fa67ac94fd8c77fa00641c", "file": "src/app/jira-control/breadcrumbs/breadcrumbs.component.ts", "encapsulation": [], "entryComponents": [], @@ -3049,22 +2986,13 @@ ], "outputsClass": [], "propertiesClass": [], - "methodsClass": [ - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 12 - } - ], + "methodsClass": [], "hostBindings": [], "hostListeners": [], "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit, Input } from '@angular/core';\n\n@Component({\n selector: 'breadcrumbs',\n templateUrl: './breadcrumbs.component.html',\n styleUrls: ['./breadcrumbs.component.scss']\n})\nexport class BreadcrumbsComponent implements OnInit {\n @Input() items: string[] = [];\n constructor() {}\n\n ngOnInit(): void {}\n}\n", + "sourceCode": "import { Component, Input } from '@angular/core';\n\n@Component({\n selector: 'breadcrumbs',\n templateUrl: './breadcrumbs.component.html',\n styleUrls: ['./breadcrumbs.component.scss']\n})\nexport class BreadcrumbsComponent {\n @Input() items: string[] = [];\n constructor() {}\n\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -3079,14 +3007,11 @@ "args": [], "line": 9 }, - "implements": [ - "OnInit" - ], "templateData": "
\n \n /\n {{ item }}\n \n
" }, { "name": "ButtonComponent", - "id": "component-ButtonComponent-3e01dce1d9dbcdbefac84f827585ab74", + "id": "component-ButtonComponent-af0912c1e763bff248a9dd57271b105b", "file": "src/app/jira-control/button/button.component.ts", "encapsulation": [], "entryComponents": [], @@ -3141,22 +3066,13 @@ ], "outputsClass": [], "propertiesClass": [], - "methodsClass": [ - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 19 - } - ], + "methodsClass": [], "hostBindings": [], "hostListeners": [], "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, Input, OnInit } from '@angular/core';\n\n@Component({\n selector: 'j-button',\n templateUrl: './button.component.html',\n styleUrls: ['./button.component.scss']\n})\nexport class ButtonComponent implements OnInit {\n @Input() type = 'button';\n @Input() className = 'btn-primary';\n @Input() icon: string;\n @Input() iconSize = 18;\n @Input() isWorking: boolean;\n @Input() isActive: boolean;\n @Input() disabled: boolean;\n\n constructor() {}\n\n ngOnInit(): void {}\n\n}\n", + "sourceCode": "import { Component, Input } from '@angular/core';\n\n@Component({\n selector: 'j-button',\n templateUrl: './button.component.html',\n styleUrls: ['./button.component.scss']\n})\nexport class ButtonComponent {\n @Input() type = 'button';\n @Input() className = 'btn-primary';\n @Input() icon: string;\n @Input() iconSize = 18;\n @Input() isWorking: boolean;\n @Input() isActive: boolean;\n @Input() disabled: boolean;\n\n constructor() {}\n\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -3171,9 +3087,6 @@ "args": [], "line": 15 }, - "implements": [ - "OnInit" - ], "templateData": "" }, { @@ -3649,7 +3562,7 @@ }, { "name": "IssueAssigneesSelectComponent", - "id": "component-IssueAssigneesSelectComponent-604189e9f9e6e2afb50d23e7743162d7", + "id": "component-IssueAssigneesSelectComponent-e317ff594fb4bed3cc8620bd7bcd9a63", "file": "src/app/project/components/add-issue-modal/issue-assignees-select/issue-assignees-select.component.ts", "encapsulation": [ "ViewEncapsulation.None" @@ -3693,7 +3606,7 @@ "optional": false, "returnType": "any", "typeParameters": [], - "line": 19, + "line": 17, "jsdoctags": [ { "name": "userId", @@ -3703,14 +3616,6 @@ } } ] - }, - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 17 } ], "hostBindings": [], @@ -3718,7 +3623,7 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit, Input, ChangeDetectorRef, ViewEncapsulation } from '@angular/core';\nimport { JUser } from '@trungk18/interface/user';\nimport { FormControl } from '@angular/forms';\n\n@Component({\n selector: 'issue-assignees-select',\n templateUrl: './issue-assignees-select.component.html',\n styleUrls: ['./issue-assignees-select.component.scss'],\n encapsulation: ViewEncapsulation.None\n})\nexport class IssueAssigneesSelectComponent implements OnInit {\n @Input() control: FormControl;\n @Input() users: JUser[];\n\n constructor(private cdr: ChangeDetectorRef) {}\n\n ngOnInit(): void {}\n\n getUser(userId: string) {\n return this.users.find((user) => user.id === userId);\n }\n}\n", + "sourceCode": "import { Component, Input, ViewEncapsulation } from '@angular/core';\nimport { JUser } from '@trungk18/interface/user';\nimport { FormControl } from '@angular/forms';\n\n@Component({\n selector: 'issue-assignees-select',\n templateUrl: './issue-assignees-select.component.html',\n styleUrls: ['./issue-assignees-select.component.scss'],\n encapsulation: ViewEncapsulation.None\n})\nexport class IssueAssigneesSelectComponent {\n @Input() control: FormControl;\n @Input() users: JUser[];\n\n constructor() {}\n\n getUser(userId: string): any {\n return this.users.find((user) => user.id === userId);\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -3730,31 +3635,14 @@ "constructorObj": { "name": "constructor", "description": "", - "args": [ - { - "name": "cdr", - "type": "ChangeDetectorRef" - } - ], - "line": 13, - "jsdoctags": [ - { - "name": "cdr", - "type": "ChangeDetectorRef", - "tagName": { - "text": "param" - } - } - ] + "args": [], + "line": 13 }, - "implements": [ - "OnInit" - ], "templateData": "\n \n \n \n\n\n \n\n\n No user found.\n" }, { "name": "IssueCardComponent", - "id": "component-IssueCardComponent-aceaf75a3a68a779dfd30164711ddb46", + "id": "component-IssueCardComponent-9aeec0fb3473131ae732b625d5769d43", "file": "src/app/project/components/issues/issue-card/issue-card.component.ts", "encapsulation": [], "entryComponents": [], @@ -3860,7 +3748,7 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';\nimport { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';\nimport { JIssue } from '@trungk18/interface/issue';\nimport { IssuePriorityIcon } from '@trungk18/interface/issue-priority-icon';\nimport { JUser } from '@trungk18/interface/user';\nimport { ProjectQuery } from '@trungk18/project/state/project/project.query';\nimport { IssueUtil } from '@trungk18/project/utils/issue';\nimport { NzModalService } from 'ng-zorro-antd/modal';\nimport { IssueModalComponent } from '../issue-modal/issue-modal.component';\n\n@Component({\n selector: 'issue-card',\n templateUrl: './issue-card.component.html',\n styleUrls: ['./issue-card.component.scss']\n})\n@UntilDestroy()\nexport class IssueCardComponent implements OnChanges {\n @Input() issue: JIssue;\n assignees: JUser[];\n issueTypeIcon: string;\n priorityIcon: IssuePriorityIcon;\n\n constructor(private _projectQuery: ProjectQuery, private _modalService: NzModalService) {}\n\n ngOnInit(): void {\n this._projectQuery.users$.pipe(untilDestroyed(this)).subscribe((users) => {\n this.assignees = this.issue.userIds.map((userId) => users.find((x) => x.id === userId));\n });\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n const issueChange = changes.issue;\n if (issueChange?.currentValue !== issueChange.previousValue) {\n this.issueTypeIcon = IssueUtil.getIssueTypeIcon(this.issue.type);\n this.priorityIcon = IssueUtil.getIssuePriorityIcon(this.issue.priority);\n }\n }\n\n openIssueModal(issueId: string) {\n this._modalService.create({\n nzContent: IssueModalComponent,\n nzWidth: 1040,\n nzClosable: false,\n nzFooter: null,\n nzComponentParams: {\n issue$: this._projectQuery.issueById$(issueId)\n }\n });\n }\n}\n", + "sourceCode": "import {Component, Input, OnChanges, OnInit, SimpleChanges} from '@angular/core';\nimport { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';\nimport { JIssue } from '@trungk18/interface/issue';\nimport { IssuePriorityIcon } from '@trungk18/interface/issue-priority-icon';\nimport { JUser } from '@trungk18/interface/user';\nimport { ProjectQuery } from '@trungk18/project/state/project/project.query';\nimport { IssueUtil } from '@trungk18/project/utils/issue';\nimport { NzModalService } from 'ng-zorro-antd/modal';\nimport { IssueModalComponent } from '../issue-modal/issue-modal.component';\n\n@Component({\n selector: 'issue-card',\n templateUrl: './issue-card.component.html',\n styleUrls: ['./issue-card.component.scss']\n})\n@UntilDestroy()\nexport class IssueCardComponent implements OnChanges, OnInit {\n @Input() issue: JIssue;\n assignees: JUser[];\n issueTypeIcon: string;\n priorityIcon: IssuePriorityIcon;\n\n constructor(private _projectQuery: ProjectQuery, private _modalService: NzModalService) {}\n\n ngOnInit(): void {\n this._projectQuery.users$.pipe(untilDestroyed(this)).subscribe((users) => {\n this.assignees = this.issue.userIds.map((userId) => users.find((x) => x.id === userId));\n });\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n const issueChange = changes.issue;\n if (issueChange?.currentValue !== issueChange.previousValue) {\n this.issueTypeIcon = IssueUtil.getIssueTypeIcon(this.issue.type);\n this.priorityIcon = IssueUtil.getIssuePriorityIcon(this.issue.priority);\n }\n }\n\n openIssueModal(issueId: string) {\n this._modalService.create({\n nzContent: IssueModalComponent,\n nzWidth: 1040,\n nzClosable: false,\n nzFooter: null,\n nzComponentParams: {\n issue$: this._projectQuery.issueById$(issueId)\n }\n });\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -3901,13 +3789,14 @@ ] }, "implements": [ - "OnChanges" + "OnChanges", + "OnInit" ], "templateData": "
\n
\n

\n {{ issue.title }}\n

\n
\n
\n \n \n \n {{issue.type}}-{{issue.id}}\n \n
\n
\n \n \n\n \n \n
\n
\n
\n
" }, { "name": "IssueCommentComponent", - "id": "component-IssueCommentComponent-7e9be0ac9769ff8e59ed5fa272e59d4f", + "id": "component-IssueCommentComponent-aa1fe1f3ba56f35f9ed5bbf6860debec", "file": "src/app/project/components/issues/issue-comment/issue-comment.component.ts", "encapsulation": [], "entryComponents": [], @@ -3984,7 +3873,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 51 + "line": 54 }, { "name": "cancelAddComment", @@ -3992,7 +3881,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 63 + "line": 66 }, { "name": "ngOnInit", @@ -4000,7 +3889,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 37 + "line": 40 }, { "name": "setCommentEdit", @@ -4013,7 +3902,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 47, + "line": 50, "jsdoctags": [ { "name": "mode", @@ -4038,13 +3927,13 @@ "argsDecorator": [ "$event" ], - "line": 27 + "line": 30 } ], "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, Input, OnInit, HostListener, ElementRef, ViewChild } from '@angular/core';\nimport { FormControl } from '@angular/forms';\nimport { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';\nimport { JComment } from '@trungk18/interface/comment';\nimport { JUser } from '@trungk18/interface/user';\nimport { AuthQuery } from '@trungk18/project/auth/auth.query';\nimport { ProjectService } from '@trungk18/project/state/project/project.service';\n\n@Component({\n selector: 'issue-comment',\n templateUrl: './issue-comment.component.html',\n styleUrls: ['./issue-comment.component.scss']\n})\n@UntilDestroy()\nexport class IssueCommentComponent implements OnInit {\n @Input() issueId: string;\n @Input() comment: JComment;\n @Input() createMode: boolean;\n @ViewChild('commentBoxRef') commentBoxRef: ElementRef;\n commentControl: FormControl;\n user: JUser;\n isEditing: boolean;\n\n constructor(private _authQuery: AuthQuery, private projectService: ProjectService) {}\n\n @HostListener('window:keyup', ['$event'])\n keyEvent(event: KeyboardEvent) {\n if (!this.createMode || this.isEditing) {\n return;\n }\n if (event.key == 'M') {\n this.commentBoxRef.nativeElement.focus();\n this.isEditing = true;\n }\n }\n\n ngOnInit(): void {\n this.commentControl = new FormControl('');\n this._authQuery.user$.pipe(untilDestroyed(this)).subscribe((user) => {\n this.user = user;\n if (this.createMode) {\n this.comment = new JComment(this.issueId, this.user);\n }\n });\n }\n\n setCommentEdit(mode: boolean) {\n this.isEditing = mode;\n }\n\n addComment() {\n const now = new Date();\n this.projectService.updateIssueComment(this.issueId, {\n ...this.comment,\n id: `${now.getTime()}`,\n createdAt: now.toISOString(),\n updatedAt: now.toISOString(),\n body: this.commentControl.value\n });\n this.cancelAddComment();\n }\n\n cancelAddComment() {\n this.commentControl.patchValue('');\n this.setCommentEdit(false);\n }\n}\n", + "sourceCode": "import { Component, Input, OnInit, HostListener, ElementRef, ViewChild } from '@angular/core';\nimport { FormControl } from '@angular/forms';\nimport { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';\nimport { JComment } from '@trungk18/interface/comment';\nimport { JUser } from '@trungk18/interface/user';\nimport { AuthQuery } from '@trungk18/project/auth/auth.query';\nimport { ProjectService } from '@trungk18/project/state/project/project.service';\n\n@Component({\n selector: 'issue-comment',\n templateUrl: './issue-comment.component.html',\n styleUrls: ['./issue-comment.component.scss']\n})\n@UntilDestroy()\nexport class IssueCommentComponent implements OnInit {\n @Input() issueId: string;\n @Input() comment: JComment;\n @Input() createMode: boolean;\n @ViewChild('commentBoxRef') commentBoxRef: ElementRef;\n commentControl: FormControl;\n user: JUser;\n isEditing: boolean;\n\n constructor(\n private _authQuery: AuthQuery,\n private projectService: ProjectService\n ) {}\n\n @HostListener('window:keyup', ['$event'])\n keyEvent(event: KeyboardEvent) {\n if (!this.createMode || this.isEditing) {\n return;\n }\n if (event.key === 'M') {\n this.commentBoxRef.nativeElement.focus();\n this.isEditing = true;\n }\n }\n\n ngOnInit(): void {\n this.commentControl = new FormControl('');\n this._authQuery.user$.pipe(untilDestroyed(this)).subscribe((user) => {\n this.user = user;\n if (this.createMode) {\n this.comment = new JComment(this.issueId, this.user);\n }\n });\n }\n\n setCommentEdit(mode: boolean) {\n this.isEditing = mode;\n }\n\n addComment() {\n const now = new Date();\n this.projectService.updateIssueComment(this.issueId, {\n ...this.comment,\n id: `${now.getTime()}`,\n createdAt: now.toISOString(),\n updatedAt: now.toISOString(),\n body: this.commentControl.value\n });\n this.cancelAddComment();\n }\n\n cancelAddComment() {\n this.commentControl.patchValue('');\n this.setCommentEdit(false);\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -4091,7 +3980,7 @@ }, { "name": "IssueCommentsComponent", - "id": "component-IssueCommentsComponent-df2cb29b41db9cf6b13421ef08e0438f", + "id": "component-IssueCommentsComponent-5c6190a8890a83885139a56fb457a6ea", "file": "src/app/project/components/issues/issue-comments/issue-comments.component.ts", "encapsulation": [], "entryComponents": [], @@ -4116,22 +4005,13 @@ ], "outputsClass": [], "propertiesClass": [], - "methodsClass": [ - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 14 - } - ], + "methodsClass": [], "hostBindings": [], "hostListeners": [], "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, Input, OnInit } from '@angular/core';\nimport { JIssue } from '@trungk18/interface/issue';\n\n@Component({\n selector: 'issue-comments',\n templateUrl: './issue-comments.component.html',\n styleUrls: ['./issue-comments.component.scss']\n})\nexport class IssueCommentsComponent implements OnInit {\n @Input() issue: JIssue;\n\n constructor() {}\n\n ngOnInit(): void {}\n}\n", + "sourceCode": "import { Component, Input } from '@angular/core';\nimport { JIssue } from '@trungk18/interface/issue';\n\n@Component({\n selector: 'issue-comments',\n templateUrl: './issue-comments.component.html',\n styleUrls: ['./issue-comments.component.scss']\n})\nexport class IssueCommentsComponent {\n @Input() issue: JIssue;\n\n constructor() {}\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -4146,14 +4026,11 @@ "args": [], "line": 10 }, - "implements": [ - "OnInit" - ], "templateData": "\n\n\n\n" }, { "name": "IssueDeleteModalComponent", - "id": "component-IssueDeleteModalComponent-73c6f213bf6a0a212bcfd65fef4d8ef9", + "id": "component-IssueDeleteModalComponent-790adfe01b0ab9aa62804f179ceaa0a8", "file": "src/app/project/components/issues/issue-delete-modal/issue-delete-modal.component.ts", "encapsulation": [], "entryComponents": [], @@ -4195,7 +4072,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 23 + "line": 21 }, { "name": "deleteIssue", @@ -4203,14 +4080,6 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 19 - }, - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], "line": 17 } ], @@ -4219,7 +4088,7 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit, Output, EventEmitter } from '@angular/core';\nimport { NzModalRef } from 'ng-zorro-antd/modal';\nimport { DeleteIssueModel } from '@trungk18/interface/ui-model/delete-issue-model';\n\n@Component({\n selector: 'issue-delete-modal',\n templateUrl: './issue-delete-modal.component.html',\n styleUrls: ['./issue-delete-modal.component.scss']\n})\nexport class IssueDeleteModalComponent implements OnInit {\n issueId: string;\n\n onDelete = new EventEmitter();\n\n constructor(private _modalRef: NzModalRef) {}\n\n ngOnInit(): void {}\n\n deleteIssue() {\n this.onDelete.emit(new DeleteIssueModel(this.issueId, this._modalRef));\n }\n\n closeModal() {\n this._modalRef.close();\n }\n}\n", + "sourceCode": "import { Component, EventEmitter } from '@angular/core';\nimport { NzModalRef } from 'ng-zorro-antd/modal';\nimport { DeleteIssueModel } from '@trungk18/interface/ui-model/delete-issue-model';\n\n@Component({\n selector: 'issue-delete-modal',\n templateUrl: './issue-delete-modal.component.html',\n styleUrls: ['./issue-delete-modal.component.scss']\n})\nexport class IssueDeleteModalComponent {\n issueId: string;\n\n onDelete = new EventEmitter();\n\n constructor(private _modalRef: NzModalRef) {}\n\n deleteIssue() {\n this.onDelete.emit(new DeleteIssueModel(this.issueId, this._modalRef));\n }\n\n closeModal() {\n this._modalRef.close();\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -4248,14 +4117,11 @@ } ] }, - "implements": [ - "OnInit" - ], "templateData": "
\n
\n Are you sure you want to delete this issue?\n
\n

This action cannot be undone.

\n
\n Delete\n \n Cancel\n
\n
" }, { "name": "IssueDescriptionComponent", - "id": "component-IssueDescriptionComponent-0e0181c5ea1a1bb85feb17f7b0c6184f", + "id": "component-IssueDescriptionComponent-91eec2541d4dcdd74ce3149d5adb8b52", "file": "src/app/project/components/issues/issue-description/issue-description.component.ts", "encapsulation": [ "ViewEncapsulation.None" @@ -4319,7 +4185,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 45 + "line": 47 }, { "name": "editorCreated", @@ -4365,21 +4231,13 @@ } ] }, - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 50 - }, { "name": "save", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 37 + "line": 39 }, { "name": "setEditMode", @@ -4409,7 +4267,7 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core';\nimport { JIssue } from '@trungk18/interface/issue';\nimport { FormControl } from '@angular/forms';\nimport { quillConfiguration } from '@trungk18/project/config/editor';\nimport { ProjectService } from '@trungk18/project/state/project/project.service';\n\n@Component({\n selector: 'issue-description',\n templateUrl: './issue-description.component.html',\n styleUrls: ['./issue-description.component.scss'],\n encapsulation: ViewEncapsulation.None\n})\nexport class IssueDescriptionComponent implements OnChanges {\n @Input() issue: JIssue;\n descriptionControl: FormControl;\n editorOptions = quillConfiguration;\n isEditing: boolean;\n isWorking: boolean;\n\n constructor(private _projectService: ProjectService) {}\n\n ngOnChanges(changes: SimpleChanges): void {\n const issueChange = changes.issue;\n if (issueChange.currentValue !== issueChange.previousValue) {\n this.descriptionControl = new FormControl(this.issue.description);\n }\n }\n\n setEditMode(mode: boolean) {\n this.isEditing = mode;\n }\n\n editorCreated(editor: any) {\n editor.focus && editor.focus();\n }\n\n save() {\n this._projectService.updateIssue({\n ...this.issue,\n description: this.descriptionControl.value\n });\n this.setEditMode(false);\n }\n\n cancel() {\n this.descriptionControl.patchValue(this.issue.description);\n this.setEditMode(false);\n }\n\n ngOnInit(): void {}\n}\n", + "sourceCode": "import { Component, Input, OnChanges, SimpleChanges, ViewEncapsulation } from '@angular/core';\nimport { JIssue } from '@trungk18/interface/issue';\nimport { FormControl } from '@angular/forms';\nimport { quillConfiguration } from '@trungk18/project/config/editor';\nimport { ProjectService } from '@trungk18/project/state/project/project.service';\n\n@Component({\n selector: 'issue-description',\n templateUrl: './issue-description.component.html',\n styleUrls: ['./issue-description.component.scss'],\n encapsulation: ViewEncapsulation.None\n})\nexport class IssueDescriptionComponent implements OnChanges {\n @Input() issue: JIssue;\n descriptionControl: FormControl;\n editorOptions = quillConfiguration;\n isEditing: boolean;\n isWorking: boolean;\n\n constructor(private _projectService: ProjectService) {}\n\n ngOnChanges(changes: SimpleChanges): void {\n const issueChange = changes.issue;\n if (issueChange.currentValue !== issueChange.previousValue) {\n this.descriptionControl = new FormControl(this.issue.description);\n }\n }\n\n setEditMode(mode: boolean) {\n this.isEditing = mode;\n }\n\n editorCreated(editor: any) {\n if (editor && editor.focus) {\n editor.focus();\n }\n }\n\n save() {\n this._projectService.updateIssue({\n ...this.issue,\n description: this.descriptionControl.value\n });\n this.setEditMode(false);\n }\n\n cancel() {\n this.descriptionControl.patchValue(this.issue.description);\n this.setEditMode(false);\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -4445,7 +4303,7 @@ }, { "name": "IssueDetailComponent", - "id": "component-IssueDetailComponent-8a0104d839ef7a9fb6ce27f2fed1dc3f", + "id": "component-IssueDetailComponent-af2e17b265d8c797fd0366174172b80c", "file": "src/app/project/components/issues/issue-detail/issue-detail.component.ts", "encapsulation": [], "entryComponents": [], @@ -4517,15 +4375,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 40 - }, - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 23 + "line": 38 }, { "name": "openDeleteIssueModal", @@ -4533,7 +4383,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 25 + "line": 23 }, { "name": "openIssuePage", @@ -4541,7 +4391,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 44 + "line": 42 } ], "hostBindings": [], @@ -4549,7 +4399,7 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';\nimport { JIssue } from '@trungk18/interface/issue';\nimport { ProjectQuery } from '@trungk18/project/state/project/project.query';\nimport { NzModalService } from 'ng-zorro-antd/modal';\nimport { IssueDeleteModalComponent } from '../issue-delete-modal/issue-delete-modal.component';\nimport { DeleteIssueModel } from '@trungk18/interface/ui-model/delete-issue-model';\n\n@Component({\n selector: 'issue-detail',\n templateUrl: './issue-detail.component.html',\n styleUrls: ['./issue-detail.component.scss']\n})\nexport class IssueDetailComponent implements OnInit {\n @Input() issue: JIssue;\n @Input() isShowFullScreenButton: boolean;\n @Input() isShowCloseButton: boolean;\n @Output() onClosed = new EventEmitter();\n @Output() onOpenIssue = new EventEmitter();\n @Output() onDelete = new EventEmitter();\n\n constructor(public projectQuery: ProjectQuery, private _modalService: NzModalService) {}\n\n ngOnInit(): void {}\n\n openDeleteIssueModal() {\n this._modalService.create({\n nzContent: IssueDeleteModalComponent,\n nzClosable: false,\n nzFooter: null,\n nzStyle: {\n top: '140px'\n },\n nzComponentParams: {\n issueId: this.issue.id,\n onDelete: this.onDelete\n }\n });\n }\n\n closeModal() {\n this.onClosed.emit();\n }\n\n openIssuePage() {\n this.onOpenIssue.emit(this.issue.id);\n }\n}\n", + "sourceCode": "import { Component, Input, Output, EventEmitter } from '@angular/core';\nimport { JIssue } from '@trungk18/interface/issue';\nimport { ProjectQuery } from '@trungk18/project/state/project/project.query';\nimport { NzModalService } from 'ng-zorro-antd/modal';\nimport { IssueDeleteModalComponent } from '../issue-delete-modal/issue-delete-modal.component';\nimport { DeleteIssueModel } from '@trungk18/interface/ui-model/delete-issue-model';\n\n@Component({\n selector: 'issue-detail',\n templateUrl: './issue-detail.component.html',\n styleUrls: ['./issue-detail.component.scss']\n})\nexport class IssueDetailComponent{\n @Input() issue: JIssue;\n @Input() isShowFullScreenButton: boolean;\n @Input() isShowCloseButton: boolean;\n @Output() onClosed = new EventEmitter();\n @Output() onOpenIssue = new EventEmitter();\n @Output() onDelete = new EventEmitter();\n\n constructor(public projectQuery: ProjectQuery, private _modalService: NzModalService) {}\n\n openDeleteIssueModal() {\n this._modalService.create({\n nzContent: IssueDeleteModalComponent,\n nzClosable: false,\n nzFooter: null,\n nzStyle: {\n top: '140px'\n },\n nzComponentParams: {\n issueId: this.issue.id,\n onDelete: this.onDelete\n }\n });\n }\n\n closeModal() {\n this.onClosed.emit();\n }\n\n openIssuePage() {\n this.onOpenIssue.emit(this.issue.id);\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -4589,14 +4439,11 @@ } ] }, - "implements": [ - "OnInit" - ], "templateData": "
\n
\n \n
\n \n \n Give Feedback\n \n \n \n \n \n \n \n \n
\n
\n
\n \n\n
Description
\n \n\n
Comments
\n \n
\n
\n \n \n \n \n \n \n\n
\n
\n Created - {{ issue.createdAt | date:\"medium\" }}\n
\n
\n Updated - {{ issue.updatedAt | date:\"medium\" }}\n
\n
\n
\n
\n
\n\n\n \n" }, { "name": "IssueLoaderComponent", - "id": "component-IssueLoaderComponent-3e3f5d6afd5a5e640f972681a179dc58", + "id": "component-IssueLoaderComponent-5cdf1b78513df9886bdb31662d44e88a", "file": "src/app/project/components/issues/issue-loader/issue-loader.component.ts", "encapsulation": [], "entryComponents": [], @@ -4615,24 +4462,15 @@ "inputsClass": [], "outputsClass": [], "propertiesClass": [], - "methodsClass": [ - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 11 - } - ], - "hostBindings": [], - "hostListeners": [], - "description": "", - "rawdescription": "", - "type": "component", - "sourceCode": "import { Component, OnInit } from '@angular/core';\n\n@Component({\n selector: 'issue-loader',\n templateUrl: './issue-loader.component.html',\n styleUrls: ['./issue-loader.component.scss']\n})\nexport class IssueLoaderComponent implements OnInit {\n constructor() {}\n\n ngOnInit(): void {}\n}\n", - "assetsDirs": [], - "styleUrlsData": [ + "methodsClass": [], + "hostBindings": [], + "hostListeners": [], + "description": "", + "rawdescription": "", + "type": "component", + "sourceCode": "import { Component } from '@angular/core';\n\n@Component({\n selector: 'issue-loader',\n templateUrl: './issue-loader.component.html',\n styleUrls: ['./issue-loader.component.scss']\n})\nexport class IssueLoaderComponent {\n constructor() {}\n}\n", + "assetsDirs": [], + "styleUrlsData": [ { "data": "", "styleUrl": "./issue-loader.component.scss" @@ -4645,14 +4483,11 @@ "args": [], "line": 8 }, - "implements": [ - "OnInit" - ], - "templateData": "
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n" + "templateData": "
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n" }, { "name": "IssueModalComponent", - "id": "component-IssueModalComponent-ed20a7d2eb4e12a059e341076a2670c7", + "id": "component-IssueModalComponent-2cd41e1070ddf55abb2e40bcec53a7b5", "file": "src/app/project/components/issues/issue-modal/issue-modal.component.ts", "encapsulation": [], "entryComponents": [], @@ -4684,7 +4519,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 25 + "line": 23 }, { "name": "deleteIssue", @@ -4696,7 +4531,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 34, + "line": 32, "jsdoctags": [ { "type": "DeleteIssueModel", @@ -4706,14 +4541,6 @@ } ] }, - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 23 - }, { "name": "openIssuePage", "args": [ @@ -4725,7 +4552,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 29, + "line": 27, "jsdoctags": [ { "name": "issueId", @@ -4742,7 +4569,7 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, Input, OnInit } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { JIssue } from '@trungk18/interface/issue';\nimport { ProjectService } from '@trungk18/project/state/project/project.service';\nimport { NzModalRef } from 'ng-zorro-antd/modal';\nimport { Observable } from 'rxjs';\nimport { DeleteIssueModel } from '@trungk18/interface/ui-model/delete-issue-model';\n\n@Component({\n selector: 'issue-modal',\n templateUrl: './issue-modal.component.html',\n styleUrls: ['./issue-modal.component.scss']\n})\nexport class IssueModalComponent implements OnInit {\n @Input() issue$: Observable;\n\n constructor(\n private _modal: NzModalRef,\n private _router: Router,\n private _projectService: ProjectService\n ) {}\n\n ngOnInit(): void {}\n\n closeModal() {\n this._modal.close();\n }\n\n openIssuePage(issueId: string) {\n this.closeModal();\n this._router.navigate(['project', 'issue', issueId]);\n }\n\n deleteIssue({ issueId, deleteModalRef }: DeleteIssueModel) {\n this._projectService.deleteIssue(issueId);\n deleteModalRef.close();\n this.closeModal();\n }\n}\n", + "sourceCode": "import { Component, Input } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { JIssue } from '@trungk18/interface/issue';\nimport { ProjectService } from '@trungk18/project/state/project/project.service';\nimport { NzModalRef } from 'ng-zorro-antd/modal';\nimport { Observable } from 'rxjs';\nimport { DeleteIssueModel } from '@trungk18/interface/ui-model/delete-issue-model';\n\n@Component({\n selector: 'issue-modal',\n templateUrl: './issue-modal.component.html',\n styleUrls: ['./issue-modal.component.scss']\n})\nexport class IssueModalComponent {\n @Input() issue$: Observable;\n\n constructor(\n private _modal: NzModalRef,\n private _router: Router,\n private _projectService: ProjectService\n ) {}\n\n closeModal() {\n this._modal.close();\n }\n\n openIssuePage(issueId: string) {\n this.closeModal();\n this._router.navigate(['project', 'issue', issueId]);\n }\n\n deleteIssue({ issueId, deleteModalRef }: DeleteIssueModel) {\n this._projectService.deleteIssue(issueId);\n deleteModalRef.close();\n this.closeModal();\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -4793,9 +4620,6 @@ } ] }, - "implements": [ - "OnInit" - ], "templateData": "
\n \n \n
" }, { @@ -4955,7 +4779,7 @@ }, { "name": "IssuePrioritySelectComponent", - "id": "component-IssuePrioritySelectComponent-61cc6169086a3e3d5d0bd33b10e350a6", + "id": "component-IssuePrioritySelectComponent-d2972e2dc089370d8f28aa6261fa1627", "file": "src/app/project/components/add-issue-modal/issue-priority-select/issue-priority-select.component.ts", "encapsulation": [], "entryComponents": [], @@ -5010,14 +4834,6 @@ } } ] - }, - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 25 } ], "hostBindings": [], @@ -5025,7 +4841,7 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit, Input } from '@angular/core';\nimport { FormControl } from '@angular/forms';\nimport { IssuePriorityIcon } from '@trungk18/interface/issue-priority-icon';\nimport { IssueUtil } from '@trungk18/project/utils/issue';\nimport { IssuePriority } from '@trungk18/interface/issue';\nimport { ProjectConst } from '@trungk18/project/config/const';\n\n@Component({\n selector: 'issue-priority-select',\n templateUrl: './issue-priority-select.component.html',\n styleUrls: ['./issue-priority-select.component.scss']\n})\nexport class IssuePrioritySelectComponent implements OnInit {\n @Input() control: FormControl;\n priorities: IssuePriorityIcon[];\n\n constructor() {\n this.priorities = ProjectConst.PrioritiesWithIcon;\n }\n\n getPriorityIcon(priority: IssuePriority) {\n return IssueUtil.getIssuePriorityIcon(priority);\n }\n\n ngOnInit(): void {}\n}\n", + "sourceCode": "import { Component, Input } from '@angular/core';\nimport { FormControl } from '@angular/forms';\nimport { IssuePriorityIcon } from '@trungk18/interface/issue-priority-icon';\nimport { IssueUtil } from '@trungk18/project/utils/issue';\nimport { IssuePriority } from '@trungk18/interface/issue';\nimport { ProjectConst } from '@trungk18/project/config/const';\n\n@Component({\n selector: 'issue-priority-select',\n templateUrl: './issue-priority-select.component.html',\n styleUrls: ['./issue-priority-select.component.scss']\n})\nexport class IssuePrioritySelectComponent {\n @Input() control: FormControl;\n priorities: IssuePriorityIcon[];\n\n constructor() {\n this.priorities = ProjectConst.PrioritiesWithIcon;\n }\n\n getPriorityIcon(priority: IssuePriority) {\n return IssueUtil.getIssuePriorityIcon(priority);\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -5040,14 +4856,11 @@ "args": [], "line": 15 }, - "implements": [ - "OnInit" - ], "templateData": "\n \n
\n \n \n {{ priority.value }}\n
\n
\n
\n\n
\n \n \n {{ selected.nzValue }}\n
\n
" }, { "name": "IssueReporterComponent", - "id": "component-IssueReporterComponent-2a8e118cb64027a4b17c63b88e12617f", + "id": "component-IssueReporterComponent-a948e1e2ad94516a52a3a2d0e0a884f7", "file": "src/app/project/components/issues/issue-reporter/issue-reporter.component.ts", "encapsulation": [], "entryComponents": [], @@ -5097,7 +4910,7 @@ "optional": false, "returnType": "boolean", "typeParameters": [], - "line": 29, + "line": 27, "jsdoctags": [ { "name": "user", @@ -5119,7 +4932,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 22, + "line": 20, "jsdoctags": [ { "name": "changes", @@ -5130,14 +4943,6 @@ } ] }, - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 20 - }, { "name": "updateIssue", "args": [ @@ -5149,7 +4954,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 33, + "line": 31, "jsdoctags": [ { "name": "user", @@ -5166,7 +4971,7 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';\nimport { UntilDestroy } from '@ngneat/until-destroy';\nimport { JIssue } from '@trungk18/interface/issue';\nimport { JUser } from '@trungk18/interface/user';\nimport { ProjectService } from '@trungk18/project/state/project/project.service';\n\n@Component({\n selector: 'issue-reporter',\n templateUrl: './issue-reporter.component.html',\n styleUrls: ['./issue-reporter.component.scss']\n})\n@UntilDestroy()\nexport class IssueReporterComponent implements OnInit, OnChanges {\n @Input() issue: JIssue;\n @Input() users: JUser[];\n reporter: JUser;\n\n constructor(private _projectService: ProjectService) {}\n\n ngOnInit(): void {}\n\n ngOnChanges(changes: SimpleChanges) {\n const issueChange = changes.issue;\n if (this.users && issueChange.currentValue !== issueChange.previousValue) {\n this.reporter = this.users.find((x) => x.id === this.issue.reporterId);\n }\n }\n\n isUserSelected(user: JUser) {\n return user.id === this.issue.reporterId;\n }\n\n updateIssue(user: JUser) {\n this._projectService.updateIssue({\n ...this.issue,\n reporterId: user.id\n });\n }\n}\n", + "sourceCode": "import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';\nimport { UntilDestroy } from '@ngneat/until-destroy';\nimport { JIssue } from '@trungk18/interface/issue';\nimport { JUser } from '@trungk18/interface/user';\nimport { ProjectService } from '@trungk18/project/state/project/project.service';\n\n@Component({\n selector: 'issue-reporter',\n templateUrl: './issue-reporter.component.html',\n styleUrls: ['./issue-reporter.component.scss']\n})\n@UntilDestroy()\nexport class IssueReporterComponent implements OnChanges {\n @Input() issue: JIssue;\n @Input() users: JUser[];\n reporter: JUser;\n\n constructor(private _projectService: ProjectService) {}\n\n ngOnChanges(changes: SimpleChanges) {\n const issueChange = changes.issue;\n if (this.users && issueChange.currentValue !== issueChange.previousValue) {\n this.reporter = this.users.find((x) => x.id === this.issue.reporterId);\n }\n }\n\n isUserSelected(user: JUser) {\n return user.id === this.issue.reporterId;\n }\n\n updateIssue(user: JUser) {\n this._projectService.updateIssue({\n ...this.issue,\n reporterId: user.id\n });\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -5196,14 +5001,13 @@ ] }, "implements": [ - "OnInit", "OnChanges" ], "templateData": "
\n Reporter\n
\n\n \n\n\n
    \n \n
  • \n \n
  • \n
    \n
\n
" }, { "name": "IssueReporterSelectComponent", - "id": "component-IssueReporterSelectComponent-1aa469eb0163b62f231af1c3ce575af1", + "id": "component-IssueReporterSelectComponent-9aeeb2df7f4dd5d97af718e798a1d1e5", "file": "src/app/project/components/add-issue-modal/issue-reporter-select/issue-reporter-select.component.ts", "encapsulation": [], "entryComponents": [], @@ -5245,7 +5049,7 @@ "optional": false, "returnType": "any", "typeParameters": [], - "line": 18, + "line": 16, "jsdoctags": [ { "name": "userId", @@ -5255,14 +5059,6 @@ } } ] - }, - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 16 } ], "hostBindings": [], @@ -5270,7 +5066,7 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit, Input } from '@angular/core';\nimport { FormControl } from '@angular/forms';\nimport { JUser } from '@trungk18/interface/user';\n\n@Component({\n selector: 'issue-reporter-select',\n templateUrl: './issue-reporter-select.component.html',\n styleUrls: ['./issue-reporter-select.component.scss']\n})\nexport class IssueReporterSelectComponent implements OnInit {\n @Input() control: FormControl;\n @Input() users: JUser[];\n\n constructor() {}\n\n ngOnInit(): void {}\n\n getUser(userId: string) {\n return this.users.find((user) => user.id === userId);\n }\n}\n", + "sourceCode": "import { Component, Input } from '@angular/core';\nimport { FormControl } from '@angular/forms';\nimport { JUser } from '@trungk18/interface/user';\n\n@Component({\n selector: 'issue-reporter-select',\n templateUrl: './issue-reporter-select.component.html',\n styleUrls: ['./issue-reporter-select.component.scss']\n})\nexport class IssueReporterSelectComponent {\n @Input() control: FormControl;\n @Input() users: JUser[];\n\n constructor() {}\n\n getUser(userId: string) {\n return this.users.find((user) => user.id === userId);\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -5285,14 +5081,11 @@ "args": [], "line": 12 }, - "implements": [ - "OnInit" - ], "templateData": "\n \n \n \n\n\n \n" }, { "name": "IssueResultComponent", - "id": "component-IssueResultComponent-12cc29a580ed6067b051db5f1c4a9ac4", + "id": "component-IssueResultComponent-a2fe7b70107501d338b4a95d49304800", "file": "src/app/project/components/search/issue-result/issue-result.component.ts", "encapsulation": [], "entryComponents": [], @@ -5317,22 +5110,13 @@ ], "outputsClass": [], "propertiesClass": [], - "methodsClass": [ - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 19 - } - ], + "methodsClass": [], "hostBindings": [], "hostListeners": [], "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit, Input } from '@angular/core';\nimport { JIssue } from '@trungk18/interface/issue';\nimport { IssueUtil } from '@trungk18/project/utils/issue';\n\n@Component({\n selector: 'issue-result',\n templateUrl: './issue-result.component.html',\n styleUrls: ['./issue-result.component.scss']\n})\nexport class IssueResultComponent implements OnInit {\n @Input() issue: JIssue;\n\n get issueTypeIcon() {\n return IssueUtil.getIssueTypeIcon(this.issue?.type);\n }\n\n constructor() {}\n\n ngOnInit(): void {}\n}\n", + "sourceCode": "import { Component, Input } from '@angular/core';\nimport { JIssue } from '@trungk18/interface/issue';\nimport { IssueUtil } from '@trungk18/project/utils/issue';\n\n@Component({\n selector: 'issue-result',\n templateUrl: './issue-result.component.html',\n styleUrls: ['./issue-result.component.scss']\n})\nexport class IssueResultComponent {\n @Input() issue: JIssue;\n\n get issueTypeIcon() {\n return IssueUtil.getIssueTypeIcon(this.issue?.type);\n }\n\n constructor() {}\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -5347,9 +5131,6 @@ "args": [], "line": 15 }, - "implements": [ - "OnInit" - ], "accessors": { "issueTypeIcon": { "name": "issueTypeIcon", @@ -5774,7 +5555,7 @@ }, { "name": "IssueTypeSelectComponent", - "id": "component-IssueTypeSelectComponent-917540d2fd66a67bf95af94c979cffe1", + "id": "component-IssueTypeSelectComponent-490d2f5686656bbeeef33e3dd662483a", "file": "src/app/project/components/add-issue-modal/issue-type-select/issue-type-select.component.ts", "encapsulation": [], "entryComponents": [], @@ -5819,7 +5600,7 @@ "optional": false, "returnType": "any", "typeParameters": [], - "line": 24, + "line": 22, "jsdoctags": [ { "name": "issueType", @@ -5829,14 +5610,6 @@ } } ] - }, - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 22 } ], "hostBindings": [], @@ -5844,7 +5617,7 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit, Input } from '@angular/core';\nimport { IssueType } from '@trungk18/interface/issue';\nimport { IssueUtil } from '@trungk18/project/utils/issue';\nimport { IssueTypeWithIcon } from '@trungk18/interface/issue-type-icon';\nimport { FormControl } from '@angular/forms';\nimport { ProjectConst } from '@trungk18/project/config/const';\n\n@Component({\n selector: 'issue-type-select',\n templateUrl: './issue-type-select.component.html',\n styleUrls: ['./issue-type-select.component.scss']\n})\nexport class IssueTypeSelectComponent implements OnInit {\n @Input() control: FormControl;\n\n issueTypes: IssueTypeWithIcon[];\n\n constructor() {\n this.issueTypes = ProjectConst.IssueTypesWithIcon;\n }\n\n ngOnInit(): void {}\n\n getIssueTypeIcon(issueType: IssueType) {\n return IssueUtil.getIssueTypeIcon(issueType);\n }\n}\n", + "sourceCode": "import { Component, Input } from '@angular/core';\nimport { IssueType } from '@trungk18/interface/issue';\nimport { IssueUtil } from '@trungk18/project/utils/issue';\nimport { IssueTypeWithIcon } from '@trungk18/interface/issue-type-icon';\nimport { FormControl } from '@angular/forms';\nimport { ProjectConst } from '@trungk18/project/config/const';\n\n@Component({\n selector: 'issue-type-select',\n templateUrl: './issue-type-select.component.html',\n styleUrls: ['./issue-type-select.component.scss']\n})\nexport class IssueTypeSelectComponent {\n @Input() control: FormControl;\n\n issueTypes: IssueTypeWithIcon[];\n\n constructor() {\n this.issueTypes = ProjectConst.IssueTypesWithIcon;\n }\n\n getIssueTypeIcon(issueType: IssueType) {\n return IssueUtil.getIssueTypeIcon(issueType);\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -5859,9 +5632,6 @@ "args": [], "line": 16 }, - "implements": [ - "OnInit" - ], "templateData": "\n \n
\n \n \n {{ type.value }}\n
\n
\n
\n\n
\n \n \n {{ selected.nzValue }}\n
\n
" }, { @@ -5988,11 +5758,11 @@ "implements": [ "OnInit" ], - "templateData": "\n\n\n
\n

This is a simplified Jira clone built with Angular 9, Akita and ng-zorro

\n

Thanks a bunch for stopping by and supporting me!

\n

Reach out to me via trungk18@gmail.com

\n \n Visit My Blog\n \n
\n
" + "templateData": "\n\n\n
\n

This is a simplified Jira clone built with Angular, Akita and ng-zorro

\n

Thanks a bunch for stopping by and supporting me!

\n

Reach out to me via trungk18@gmail.com

\n \n Visit My Blog\n \n
\n
" }, { "name": "NavigationComponent", - "id": "component-NavigationComponent-cf93963fd4864c1c0b73a9f33a5f29b6", + "id": "component-NavigationComponent-56e4b5f030b2fb1e9c0944326d24ad0b", "file": "src/app/project/components/navigation/navigation/navigation.component.ts", "encapsulation": [], "entryComponents": [], @@ -6025,21 +5795,13 @@ ], "propertiesClass": [], "methodsClass": [ - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 13 - }, { "name": "toggle", "args": [], "optional": false, "returnType": "void", "typeParameters": [], - "line": 15 + "line": 13 } ], "hostBindings": [], @@ -6047,7 +5809,7 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';\n\n@Component({\n selector: 'app-navigation',\n templateUrl: './navigation.component.html',\n styleUrls: ['./navigation.component.scss']\n})\nexport class NavigationComponent implements OnInit {\n @Input() expanded: boolean;\n @Output() manualToggle = new EventEmitter();\n constructor() {}\n\n ngOnInit(): void {}\n\n toggle() {\n this.manualToggle.emit();\n }\n}\n", + "sourceCode": "import { Component, Input, Output, EventEmitter } from '@angular/core';\n\n@Component({\n selector: 'app-navigation',\n templateUrl: './navigation.component.html',\n styleUrls: ['./navigation.component.scss']\n})\nexport class NavigationComponent {\n @Input() expanded: boolean;\n @Output() manualToggle = new EventEmitter();\n constructor() {}\n\n toggle() {\n this.manualToggle.emit();\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -6062,14 +5824,11 @@ "args": [], "line": 10 }, - "implements": [ - "OnInit" - ], "templateData": "
\n
\n \n \n
\n \n
" }, { "name": "ProjectComponent", - "id": "component-ProjectComponent-946f3149e59cd556486e8b92343c1a44", + "id": "component-ProjectComponent-ccd442e5fbc99afb1462f0da10d64bd1", "file": "src/app/project/project.component.ts", "encapsulation": [], "entryComponents": [], @@ -6093,7 +5852,7 @@ "type": "boolean", "optional": false, "description": "", - "line": 11 + "line": 12 } ], "methodsClass": [ @@ -6103,7 +5862,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 22 + "line": 23 }, { "name": "manualToggle", @@ -6111,7 +5870,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 30 + "line": 31 }, { "name": "ngOnInit", @@ -6119,7 +5878,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 16 + "line": 17 } ], "hostBindings": [], @@ -6127,7 +5886,7 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit } from '@angular/core';\nimport { ProjectService } from './state/project/project.service';\nimport { AuthService, LoginPayload } from './auth/auth.service';\n\n@Component({\n selector: 'app-project',\n templateUrl: './project.component.html',\n styleUrls: ['./project.component.scss']\n})\nexport class ProjectComponent implements OnInit {\n expanded: boolean;\n constructor(private _projectService: ProjectService, private _authService: AuthService) {\n this.expanded = true;\n }\n\n ngOnInit(): void {\n this._authService.login(new LoginPayload());\n this._projectService.getProject();\n this.handleResize();\n }\n\n handleResize() {\n const match = window.matchMedia('(min-width: 1024px)');\n match.addEventListener('change', (e) => {\n console.log(e);\n this.expanded = e.matches;\n });\n }\n\n manualToggle() {\n this.expanded = !this.expanded;\n }\n}\n", + "sourceCode": "import { Component, OnInit } from '@angular/core';\nimport { ProjectService } from './state/project/project.service';\nimport { AuthService } from './auth/auth.service';\nimport { LoginPayload } from '@trungk18/project/auth/loginPayload';\n\n@Component({\n selector: 'app-project',\n templateUrl: './project.component.html',\n styleUrls: ['./project.component.scss']\n})\nexport class ProjectComponent implements OnInit {\n expanded: boolean;\n constructor(private _projectService: ProjectService, private _authService: AuthService) {\n this.expanded = true;\n }\n\n ngOnInit(): void {\n this._authService.login(new LoginPayload());\n this._projectService.getProject();\n this.handleResize();\n }\n\n handleResize() {\n const match = window.matchMedia('(min-width: 1024px)');\n match.addEventListener('change', (e) => {\n console.log(e);\n this.expanded = e.matches;\n });\n }\n\n manualToggle() {\n this.expanded = !this.expanded;\n }\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -6149,7 +5908,7 @@ "type": "AuthService" } ], - "line": 11, + "line": 12, "jsdoctags": [ { "name": "_projectService", @@ -6174,7 +5933,7 @@ }, { "name": "ResizerComponent", - "id": "component-ResizerComponent-03a0b5748f25339d6194a61b8b8fe075", + "id": "component-ResizerComponent-b85b41172e19188a46a1c858719df4b3", "file": "src/app/project/components/navigation/resizer/resizer.component.ts", "encapsulation": [], "entryComponents": [], @@ -6199,22 +5958,13 @@ ], "outputsClass": [], "propertiesClass": [], - "methodsClass": [ - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 16 - } - ], + "methodsClass": [], "hostBindings": [], "hostListeners": [], "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';\n\n@Component({\n selector: 'app-resizer',\n templateUrl: './resizer.component.html',\n styleUrls: ['./resizer.component.scss']\n})\nexport class ResizerComponent implements OnInit {\n @Input() expanded: boolean;\n\n get icon() {\n return this.expanded ? 'chevron-left' : 'chevron-right';\n }\n constructor() {}\n\n ngOnInit(): void {}\n}\n", + "sourceCode": "import { Component, Input } from '@angular/core';\n\n@Component({\n selector: 'app-resizer',\n templateUrl: './resizer.component.html',\n styleUrls: ['./resizer.component.scss']\n})\nexport class ResizerComponent {\n @Input() expanded: boolean;\n\n get icon() {\n return this.expanded ? 'chevron-left' : 'chevron-right';\n }\n constructor() {}\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -6229,9 +5979,6 @@ "args": [], "line": 13 }, - "implements": [ - "OnInit" - ], "accessors": { "icon": { "name": "icon", @@ -6696,19 +6443,21 @@ "templateData": "
\n
\n
\n \n \n
\n
\n {{ project.name }}\n
\n
\n {{ project.category }} Project\n
\n
\n
\n\n
\n \n\n \n \n\n
{{ link.name }}
\n
\n \n\n
\n
\n
\n
" }, { - "name": "SvgDefinitionsComponent", - "id": "component-SvgDefinitionsComponent-2e9d7ce174414a7f32da311e4c687aca", - "file": "src/app/jira-control/svg-definitions/svg-definitions.component.ts", + "name": "SnowComponent", + "id": "component-SnowComponent-50ee76b5bad7b7675aa9fe5dc93a4a75", + "file": "src/app/core/snow/snow.component.ts", "encapsulation": [], "entryComponents": [], "inputs": [], "outputs": [], "providers": [], - "selector": "svg-definitions", - "styleUrls": [], + "selector": "j-snow", + "styleUrls": [ + "./snow.component.scss" + ], "styles": [], "templateUrl": [ - "./svg-definitions.component.html" + "./snow.component.html" ], "viewProviders": [], "inputsClass": [], @@ -6721,7 +6470,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 10 + "line": 12 } ], "hostBindings": [], @@ -6729,24 +6478,66 @@ "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit } from '@angular/core';\n\n@Component({\n selector: 'svg-definitions',\n templateUrl: './svg-definitions.component.html'\n})\nexport class SvgDefinitionsComponent implements OnInit {\n constructor() {}\n\n ngOnInit(): void {}\n}\n", + "sourceCode": "import { Component, OnInit } from '@angular/core';\n\n@Component({\n selector: 'j-snow',\n templateUrl: './snow.component.html',\n styleUrls: ['./snow.component.scss']\n})\nexport class SnowComponent implements OnInit {\n\n constructor() { }\n\n ngOnInit(): void {\n }\n\n}\n", "assetsDirs": [], - "styleUrlsData": "", + "styleUrlsData": [ + { + "data": "//https://codepen.io/alphardex/pen/dyPorwJ\n\n@function random_range($min, $max) {\n $rand: random();\n $random_range: $min + floor($rand * (($max - $min) + 1));\n @return $random_range;\n}\n\n.snow {\n $total: 200;\n position: absolute;\n width: 20px;\n height: 20px; \n font-size: 20px;\n border-radius: 50%;\n pointer-events: none;\n color: #a3b1bc;\n\n @for $i from 1 through $total {\n $random-x: random(1000000) * 0.0001vw;\n $random-offset: random_range(-100000, 100000) * 0.0001vw;\n $random-x-end: $random-x + $random-offset;\n $random-x-end-yoyo: $random-x + ($random-offset / 2);\n $random-yoyo-time: random_range(30000, 80000) / 100000;\n $random-yoyo-y: $random-yoyo-time * 100vh;\n $random-scale: random(10000) * 0.0001;\n $fall-duration: random_range(10, 30) * 1s;\n $fall-delay: random(30) * -1s;\n\n &:nth-child(#{$i}) {\n opacity: random(8000) * 0.0001;\n transform: translate($random-x, -10px) scale($random-scale);\n animation: fall-#{$i} $fall-duration $fall-delay linear infinite;\n }\n\n @keyframes fall-#{$i} {\n #{percentage($random-yoyo-time)} {\n transform: translate($random-x-end, $random-yoyo-y) scale($random-scale);\n }\n \n to {\n transform: translate($random-x-end-yoyo, 100vh) scale($random-scale);\n }\n }\n }\n}\n", + "styleUrl": "./snow.component.scss" + } + ], "stylesData": "", "constructorObj": { "name": "constructor", "description": "", "args": [], - "line": 7 + "line": 8 }, "implements": [ "OnInit" ], + "templateData": "
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
" + }, + { + "name": "SvgDefinitionsComponent", + "id": "component-SvgDefinitionsComponent-7064ee768bf7535caa16ee903243b51f", + "file": "src/app/jira-control/svg-definitions/svg-definitions.component.ts", + "encapsulation": [], + "entryComponents": [], + "inputs": [], + "outputs": [], + "providers": [], + "selector": "svg-definitions", + "styleUrls": [], + "styles": [], + "templateUrl": [ + "./svg-definitions.component.html" + ], + "viewProviders": [], + "inputsClass": [], + "outputsClass": [], + "propertiesClass": [], + "methodsClass": [], + "hostBindings": [], + "hostListeners": [], + "description": "", + "rawdescription": "", + "type": "component", + "sourceCode": "import { Component } from '@angular/core';\n\n@Component({\n selector: 'svg-definitions',\n templateUrl: './svg-definitions.component.html'\n})\nexport class SvgDefinitionsComponent {\n constructor() {}\n\n}\n", + "assetsDirs": [], + "styleUrlsData": "", + "stylesData": "", + "constructorObj": { + "name": "constructor", + "description": "", + "args": [], + "line": 7 + }, "templateData": "\n \n \n Created by Handicon\n from the Noun Project\n \n \n \n Created by Handicon\n from the Noun Project\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n \n \n" }, { "name": "SvgIconComponent", - "id": "component-SvgIconComponent-84f933054648fdbfd0851652354efc78", + "id": "component-SvgIconComponent-6a19664e54b9329c9aadb77223de412a", "file": "src/app/jira-control/svg-icon/svg-icon.component.ts", "encapsulation": [], "entryComponents": [], @@ -6769,7 +6560,7 @@ { "name": "name", "line": 8, - "type": "String" + "type": "string" }, { "name": "size", @@ -6778,14 +6569,23 @@ } ], "outputsClass": [], - "propertiesClass": [], + "propertiesClass": [ + { + "name": "window", + "defaultValue": "window", + "type": "any", + "optional": false, + "description": "", + "line": 11 + } + ], "methodsClass": [], "hostBindings": [], "hostListeners": [], "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, Input } from '@angular/core';\n\n@Component({\n selector: 'svg-icon',\n templateUrl: './svg-icon.component.html'\n})\nexport class SvgIconComponent {\n @Input() name: String;\n @Input() size = 16;\n @Input() fill = 'currentColor';\n\n constructor() {}\n\n get iconUrl() {\n return `${window.location.href}#${this.name}`;\n }\n}\n", + "sourceCode": "import { Component, Input } from '@angular/core';\n\n@Component({\n selector: 'svg-icon',\n templateUrl: './svg-icon.component.html'\n})\nexport class SvgIconComponent {\n @Input() name: string;\n @Input() size = 16;\n @Input() fill = 'currentColor';\n window: any = window;\n\n constructor() {}\n\n get iconUrl() {\n return `${this.window.location.href}#${this.name}`;\n }\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "", @@ -6793,7 +6593,7 @@ "name": "constructor", "description": "", "args": [], - "line": 10 + "line": 11 }, "accessors": { "iconUrl": { @@ -6802,7 +6602,7 @@ "name": "iconUrl", "type": "", "returnType": "", - "line": 14 + "line": 15 } } }, @@ -6810,7 +6610,7 @@ }, { "name": "UserComponent", - "id": "component-UserComponent-53898cbeb206de8c1983683f7b592229", + "id": "component-UserComponent-f1549c7d2c03cf1442e0e0b89ed780aa", "file": "src/app/project/components/user/user.component.ts", "encapsulation": [], "entryComponents": [], @@ -6835,22 +6635,13 @@ ], "outputsClass": [], "propertiesClass": [], - "methodsClass": [ - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 14 - } - ], + "methodsClass": [], "hostBindings": [], "hostListeners": [], "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit, Input } from '@angular/core';\nimport { JUser } from '@trungk18/interface/user';\n\n@Component({\n selector: 'j-user',\n templateUrl: './user.component.html',\n styleUrls: ['./user.component.scss']\n})\nexport class UserComponent implements OnInit {\n @Input() user: JUser;\n\n constructor() {}\n\n ngOnInit(): void {}\n}\n", + "sourceCode": "import { Component, Input } from '@angular/core';\nimport { JUser } from '@trungk18/interface/user';\n\n@Component({\n selector: 'j-user',\n templateUrl: './user.component.html',\n styleUrls: ['./user.component.scss']\n})\nexport class UserComponent {\n @Input() user: JUser;\n\n constructor() {}\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -6865,14 +6656,11 @@ "args": [], "line": 10 }, - "implements": [ - "OnInit" - ], "templateData": "
\n \n \n \n {{ user?.name }}\n \n
" }, { "name": "WorkInProgressComponent", - "id": "component-WorkInProgressComponent-4e281e737a6b48f01d582c3de7c30483", + "id": "component-WorkInProgressComponent-706e199ca52362b9f6e350f414cd4e21", "file": "src/app/work-in-progress/work-in-progress.component.ts", "encapsulation": [], "entryComponents": [], @@ -6891,22 +6679,13 @@ "inputsClass": [], "outputsClass": [], "propertiesClass": [], - "methodsClass": [ - { - "name": "ngOnInit", - "args": [], - "optional": false, - "returnType": "void", - "typeParameters": [], - "line": 12 - } - ], + "methodsClass": [], "hostBindings": [], "hostListeners": [], "description": "", "rawdescription": "", "type": "component", - "sourceCode": "import { Component, OnInit } from '@angular/core';\n\n@Component({\n selector: 'work-in-progress',\n templateUrl: './work-in-progress.component.html',\n styleUrls: ['./work-in-progress.component.scss']\n})\nexport class WorkInProgressComponent implements OnInit {\n\n constructor() { }\n\n ngOnInit(): void {\n }\n\n}\n", + "sourceCode": "import { Component } from '@angular/core';\n\n@Component({\n selector: 'work-in-progress',\n templateUrl: './work-in-progress.component.html',\n styleUrls: ['./work-in-progress.component.scss']\n})\nexport class WorkInProgressComponent {\n\n constructor() { }\n\n}\n", "assetsDirs": [], "styleUrlsData": [ { @@ -6921,10 +6700,7 @@ "args": [], "line": 8 }, - "implements": [ - "OnInit" - ], - "templateData": "
\n

A simplified Jira clone built with Angular 9 and Akita

\n\n \n\n

\n Visit the work in progress app |\n View front-end code\n

\n\n
\n
\n
\n
\n
\n

Work in progress!

\n

\n It is still under development.\n

\n

\n I am planning to finish coding in about two weeks (13 - 27 Jun 2020) in my spare time.\n

\n

\n In the meantime, I am gradually writing a series about how I built it step by step in my blog. Please visit trungk18.com\n

\n
\n
\n
\n

\n Made with in Singapore\n

\n
© 2020 Trung Vo
\n
" + "templateData": "
\n

A simplified Jira clone built with Angular and Akita

\n\n \n\n

\n Visit the work in progress app |\n View front-end code\n

\n\n
\n
\n
\n
\n
\n

Work in progress!

\n

\n It is still under development.\n

\n

\n I am planning to finish coding in about two weeks (13 - 27 Jun 2020) in my spare time.\n

\n

\n In the meantime, I am gradually writing a series about how I built it step by step in my blog. Please visit trungk18.com\n

\n
\n
\n
\n

\n Made with in Singapore\n

\n
© 2020 Trung Vo
\n
" } ], "modules": [ @@ -7233,6 +7009,43 @@ } ] }, + { + "name": "SnowModule", + "children": [ + { + "type": "providers", + "elements": [] + }, + { + "type": "declarations", + "elements": [ + { + "name": "SnowComponent" + } + ] + }, + { + "type": "imports", + "elements": [] + }, + { + "type": "exports", + "elements": [ + { + "name": "SnowComponent" + } + ] + }, + { + "type": "bootstrap", + "elements": [] + }, + { + "type": "classes", + "elements": [] + } + ] + }, { "name": "WorkInProgressModule", "children": [ @@ -7354,17 +7167,17 @@ "name": "environment", "ctype": "miscellaneous", "subtype": "variable", - "file": "src/environments/environment.ts", + "file": "src/environments/environment.prod.ts", "type": "EnvironmentModel", - "defaultValue": "{\n production: false,\n apiUrl: '/assets/data' // 'http://localhost:3000/'\n}" + "defaultValue": "{\n production: true,\n apiUrl: '/assets/data' // 'https://jira-clone-angular-api.herokuapp.com/'\n}" }, { "name": "environment", "ctype": "miscellaneous", "subtype": "variable", - "file": "src/environments/environment.prod.ts", + "file": "src/environments/environment.ts", "type": "EnvironmentModel", - "defaultValue": "{\n production: true,\n apiUrl: '/assets/data' // 'https://jira-clone-angular-api.herokuapp.com/'\n}" + "defaultValue": "{\n production: false,\n apiUrl: '/assets/data' // 'http://localhost:3000/'\n}" }, { "name": "Flat", @@ -7389,6 +7202,14 @@ "file": "src/app/core/services/google-analytics.service.ts", "type": "any" }, + { + "name": "initSentry", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "src/main.ts", + "type": "", + "defaultValue": "() => {\n Sentry.init({\n dsn: 'https://b2af8332e38f486d910f06b79df66365@o495789.ingest.sentry.io/5569161',\n autoSessionTracking: true,\n integrations: [\n new Integrations.BrowserTracing({\n tracingOrigins: ['localhost', 'https://jira.trungk18.com/'],\n routingInstrumentation: Sentry.routingInstrumentation\n })\n ],\n\n tracesSampleRate: 1.0\n });\n}" + }, { "name": "IssuePriorityColors", "ctype": "miscellaneous", @@ -7500,6 +7321,14 @@ "type": "Story", "defaultValue": "(args: BreadcrumbsComponent) => ({\n component: BreadcrumbsComponent,\n props: args\n})" }, + { + "name": "Template", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "src/app/jira-control/button/button.stories.ts", + "type": "Story", + "defaultValue": "({ label, className }: Partial) => ({\n component: ButtonComponent,\n moduleMetadata: {\n declarations: [ButtonComponent], // Removed if no template\n imports: []\n },\n template: `${label}`\n})" + }, { "name": "Template", "ctype": "miscellaneous", @@ -7515,14 +7344,6 @@ "file": "src/app/jira-control/input/input.stories.ts", "type": "Story", "defaultValue": "(args: InputComponent) => ({\n component: InputComponent,\n props: args\n})" - }, - { - "name": "Template", - "ctype": "miscellaneous", - "subtype": "variable", - "file": "src/app/jira-control/button/button.stories.ts", - "type": "Story", - "defaultValue": "({ label, className }: Partial) => ({\n component: ButtonComponent,\n moduleMetadata: {\n declarations: [ButtonComponent], // Removed if no template\n imports: []\n },\n template: `${label}`\n})" } ], "functions": [ @@ -7796,24 +7617,24 @@ "defaultValue": "({ label, className }: Partial) => ({\n component: ButtonComponent,\n moduleMetadata: {\n declarations: [ButtonComponent], // Removed if no template\n imports: []\n },\n template: `${label}`\n})" } ], - "src/environments/environment.ts": [ + "src/environments/environment.prod.ts": [ { "name": "environment", "ctype": "miscellaneous", "subtype": "variable", - "file": "src/environments/environment.ts", + "file": "src/environments/environment.prod.ts", "type": "EnvironmentModel", - "defaultValue": "{\n production: false,\n apiUrl: '/assets/data' // 'http://localhost:3000/'\n}" + "defaultValue": "{\n production: true,\n apiUrl: '/assets/data' // 'https://jira-clone-angular-api.herokuapp.com/'\n}" } ], - "src/environments/environment.prod.ts": [ + "src/environments/environment.ts": [ { "name": "environment", "ctype": "miscellaneous", "subtype": "variable", - "file": "src/environments/environment.prod.ts", + "file": "src/environments/environment.ts", "type": "EnvironmentModel", - "defaultValue": "{\n production: true,\n apiUrl: '/assets/data' // 'https://jira-clone-angular-api.herokuapp.com/'\n}" + "defaultValue": "{\n production: false,\n apiUrl: '/assets/data' // 'http://localhost:3000/'\n}" } ], "src/app/jira-control/input/input.stories.ts": [ @@ -7851,6 +7672,16 @@ "type": "any" } ], + "src/main.ts": [ + { + "name": "initSentry", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "src/main.ts", + "type": "", + "defaultValue": "() => {\n Sentry.init({\n dsn: 'https://b2af8332e38f486d910f06b79df66365@o495789.ingest.sentry.io/5569161',\n autoSessionTracking: true,\n integrations: [\n new Integrations.BrowserTracing({\n tracingOrigins: ['localhost', 'https://jira.trungk18.com/'],\n routingInstrumentation: Sentry.routingInstrumentation\n })\n ],\n\n tracesSampleRate: 1.0\n });\n}" + } + ], "src/app/interface/issue.ts": [ { "name": "IssuePriorityColors", @@ -8152,7 +7983,7 @@ "linktype": "injectable", "name": "GoogleAnalyticsService", "coveragePercent": 0, - "coverageCount": "0/4", + "coverageCount": "0/5", "status": "low" }, { @@ -8175,6 +8006,15 @@ "coverageCount": "0/1", "status": "low" }, + { + "filePath": "src/app/core/snow/snow.component.ts", + "type": "component", + "linktype": "component", + "name": "SnowComponent", + "coveragePercent": 0, + "coverageCount": "0/3", + "status": "low" + }, { "filePath": "src/app/core/validators/no-whitespace.validator.ts", "type": "function", @@ -8331,7 +8171,7 @@ "linktype": "component", "name": "BreadcrumbsComponent", "coveragePercent": 0, - "coverageCount": "0/4", + "coverageCount": "0/3", "status": "low" }, { @@ -8360,7 +8200,7 @@ "linktype": "component", "name": "ButtonComponent", "coveragePercent": 0, - "coverageCount": "0/10", + "coverageCount": "0/9", "status": "low" }, { @@ -8457,7 +8297,7 @@ "linktype": "component", "name": "SvgDefinitionsComponent", "coveragePercent": 0, - "coverageCount": "0/3", + "coverageCount": "0/2", "status": "low" }, { @@ -8466,7 +8306,7 @@ "linktype": "component", "name": "SvgIconComponent", "coveragePercent": 0, - "coverageCount": "0/5", + "coverageCount": "0/6", "status": "low" }, { @@ -8478,15 +8318,6 @@ "coverageCount": "0/4", "status": "low" }, - { - "filePath": "src/app/project/auth/auth.service.ts", - "type": "class", - "linktype": "classe", - "name": "LoginPayload", - "coveragePercent": 0, - "coverageCount": "0/4", - "status": "low" - }, { "filePath": "src/app/project/auth/auth.service.ts", "type": "injectable", @@ -8523,6 +8354,15 @@ "coverageCount": "0/1", "status": "low" }, + { + "filePath": "src/app/project/auth/loginPayload.ts", + "type": "class", + "linktype": "classe", + "name": "LoginPayload", + "coveragePercent": 0, + "coverageCount": "0/4", + "status": "low" + }, { "filePath": "src/app/project/components/add-issue-modal/add-issue-modal.component.ts", "type": "component", @@ -8538,7 +8378,7 @@ "linktype": "component", "name": "IssueAssigneesSelectComponent", "coveragePercent": 0, - "coverageCount": "0/6", + "coverageCount": "0/5", "status": "low" }, { @@ -8547,7 +8387,7 @@ "linktype": "component", "name": "IssuePrioritySelectComponent", "coveragePercent": 0, - "coverageCount": "0/6", + "coverageCount": "0/5", "status": "low" }, { @@ -8556,7 +8396,7 @@ "linktype": "component", "name": "IssueReporterSelectComponent", "coveragePercent": 0, - "coverageCount": "0/6", + "coverageCount": "0/5", "status": "low" }, { @@ -8565,7 +8405,7 @@ "linktype": "component", "name": "IssueTypeSelectComponent", "coveragePercent": 0, - "coverageCount": "0/6", + "coverageCount": "0/5", "status": "low" }, { @@ -8583,7 +8423,7 @@ "linktype": "component", "name": "BoardDndComponent", "coveragePercent": 0, - "coverageCount": "0/6", + "coverageCount": "0/5", "status": "low" }, { @@ -8648,7 +8488,7 @@ "linktype": "component", "name": "IssueCommentsComponent", "coveragePercent": 0, - "coverageCount": "0/4", + "coverageCount": "0/3", "status": "low" }, { @@ -8657,7 +8497,7 @@ "linktype": "component", "name": "IssueDeleteModalComponent", "coveragePercent": 0, - "coverageCount": "0/7", + "coverageCount": "0/6", "status": "low" }, { @@ -8666,7 +8506,7 @@ "linktype": "component", "name": "IssueDescriptionComponent", "coveragePercent": 0, - "coverageCount": "0/13", + "coverageCount": "0/12", "status": "low" }, { @@ -8675,7 +8515,7 @@ "linktype": "component", "name": "IssueDetailComponent", "coveragePercent": 0, - "coverageCount": "0/13", + "coverageCount": "0/12", "status": "low" }, { @@ -8684,7 +8524,7 @@ "linktype": "component", "name": "IssueLoaderComponent", "coveragePercent": 0, - "coverageCount": "0/3", + "coverageCount": "0/2", "status": "low" }, { @@ -8693,7 +8533,7 @@ "linktype": "component", "name": "IssueModalComponent", "coveragePercent": 0, - "coverageCount": "0/7", + "coverageCount": "0/6", "status": "low" }, { @@ -8711,7 +8551,7 @@ "linktype": "component", "name": "IssueReporterComponent", "coveragePercent": 0, - "coverageCount": "0/9", + "coverageCount": "0/8", "status": "low" }, { @@ -8784,7 +8624,7 @@ "linktype": "component", "name": "NavigationComponent", "coveragePercent": 0, - "coverageCount": "0/6", + "coverageCount": "0/5", "status": "low" }, { @@ -8793,7 +8633,7 @@ "linktype": "component", "name": "ResizerComponent", "coveragePercent": 0, - "coverageCount": "0/4", + "coverageCount": "0/3", "status": "low" }, { @@ -8811,7 +8651,7 @@ "linktype": "component", "name": "IssueResultComponent", "coveragePercent": 0, - "coverageCount": "0/4", + "coverageCount": "0/3", "status": "low" }, { @@ -8829,7 +8669,7 @@ "linktype": "component", "name": "UserComponent", "coveragePercent": 0, - "coverageCount": "0/4", + "coverageCount": "0/3", "status": "low" }, { @@ -8877,7 +8717,7 @@ "linktype": "component", "name": "BoardComponent", "coveragePercent": 0, - "coverageCount": "0/5", + "coverageCount": "0/4", "status": "low" }, { @@ -9030,7 +8870,7 @@ "linktype": "component", "name": "WorkInProgressComponent", "coveragePercent": 0, - "coverageCount": "0/3", + "coverageCount": "0/2", "status": "low" }, { @@ -9062,6 +8902,16 @@ "coverageCount": "0/1", "status": "low" }, + { + "filePath": "src/main.ts", + "type": "variable", + "linktype": "miscellaneous", + "linksubtype": "variable", + "name": "initSentry", + "coveragePercent": 0, + "coverageCount": "0/1", + "status": "low" + }, { "filePath": "src/test.ts", "type": "variable", diff --git a/frontend/src/app/project/components/navigation/navbar-left/navbar-left.component.html b/frontend/src/app/project/components/navigation/navbar-left/navbar-left.component.html index 37c908b9..db1c070e 100644 --- a/frontend/src/app/project/components/navigation/navbar-left/navbar-left.component.html +++ b/frontend/src/app/project/components/navigation/navbar-left/navbar-left.component.html @@ -2,7 +2,7 @@