From 43c7198f22d65d9ead9b213aaba020088bd49824 Mon Sep 17 00:00:00 2001 From: Kerry Johnson Date: Sat, 13 Mar 2021 08:45:04 -0700 Subject: [PATCH] Final state from MSSE692 --- package-lock.json | 36 +++- package.json | 7 +- src/app/@core/core.module.ts | 3 - src/app/@core/data/users.ts | 21 --- src/app/@core/mock/mock-data.module.ts | 2 - src/app/@core/mock/users.service.ts | 53 ------ .../components/header/header.component.html | 12 +- .../components/header/header.component.ts | 47 +++-- src/app/app.module.ts | 13 +- .../data-sharing-agreement.component.html | 34 ++++ .../data-sharing-agreement.component.scss | 0 .../data-sharing-agreement.component.ts | 21 +++ .../detail-page/detail-page.component.html | 19 ++ .../detail-page/detail-page.component.scss | 0 .../detail-page/detail-page.component.ts | 38 ++++ .../list-page/list-page.component.html | 17 ++ .../list-page/list-page.component.scss | 13 ++ .../list-page/list-page.component.ts | 25 +++ .../organization/organization.module.ts | 34 ++++ .../organization/organization.service.ts | 51 +++++ .../modules/user/pages/profile.component.html | 17 ++ .../modules/user/pages/profile.component.scss | 15 ++ .../modules/user/pages/profile.component.ts | 38 ++++ src/app/modules/user/user.module.ts | 67 +++++++ src/app/modules/user/user.service.spec.ts | 16 ++ src/app/modules/user/user.service.ts | 27 +++ .../contacts/contacts.component.html | 23 --- .../contacts/contacts.component.scss | 34 ---- .../dashboard/contacts/contacts.component.ts | 34 ---- .../pages/dashboard/dashboard.component.html | 6 +- src/app/pages/dashboard/dashboard.module.ts | 2 - src/app/pages/pages-menu.ts | 5 + src/app/pages/pages-routing.module.ts | 10 + src/app/user.interceptors.ts | 22 +++ .../widgets/date/date-calendar.component.html | 6 + .../widgets/date/date-calendar.component.scss | 175 ++++++++++++++++++ .../widgets/date/date-calendar.component.ts | 58 ++++++ .../verification-icon.component.html | 2 + .../verification-icon.component.scss | 0 .../verification-icon.component.ts | 12 ++ src/app/widgets/widgets.module.ts | 19 ++ 41 files changed, 816 insertions(+), 218 deletions(-) delete mode 100644 src/app/@core/data/users.ts delete mode 100644 src/app/@core/mock/users.service.ts create mode 100644 src/app/modules/organization/components/data-sharing-agreement/data-sharing-agreement.component.html create mode 100644 src/app/modules/organization/components/data-sharing-agreement/data-sharing-agreement.component.scss create mode 100644 src/app/modules/organization/components/data-sharing-agreement/data-sharing-agreement.component.ts create mode 100644 src/app/modules/organization/components/detail-page/detail-page.component.html create mode 100644 src/app/modules/organization/components/detail-page/detail-page.component.scss create mode 100644 src/app/modules/organization/components/detail-page/detail-page.component.ts create mode 100644 src/app/modules/organization/components/list-page/list-page.component.html create mode 100644 src/app/modules/organization/components/list-page/list-page.component.scss create mode 100644 src/app/modules/organization/components/list-page/list-page.component.ts create mode 100644 src/app/modules/organization/organization.module.ts create mode 100644 src/app/modules/organization/organization.service.ts create mode 100644 src/app/modules/user/pages/profile.component.html create mode 100644 src/app/modules/user/pages/profile.component.scss create mode 100644 src/app/modules/user/pages/profile.component.ts create mode 100644 src/app/modules/user/user.module.ts create mode 100644 src/app/modules/user/user.service.spec.ts create mode 100644 src/app/modules/user/user.service.ts delete mode 100644 src/app/pages/dashboard/contacts/contacts.component.html delete mode 100644 src/app/pages/dashboard/contacts/contacts.component.scss delete mode 100644 src/app/pages/dashboard/contacts/contacts.component.ts create mode 100644 src/app/user.interceptors.ts create mode 100644 src/app/widgets/date/date-calendar.component.html create mode 100644 src/app/widgets/date/date-calendar.component.scss create mode 100644 src/app/widgets/date/date-calendar.component.ts create mode 100644 src/app/widgets/verification/verification-icon.component.html create mode 100644 src/app/widgets/verification/verification-icon.component.scss create mode 100644 src/app/widgets/verification/verification-icon.component.ts create mode 100644 src/app/widgets/widgets.module.ts diff --git a/package-lock.json b/package-lock.json index 7f3058ff..171c9c26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,7 @@ "rxjs-compat": "6.3.0", "socicon": "3.0.5", "style-loader": "^1.1.3", + "thingbook-api": "^1.0.14", "tinymce": "4.5.7", "tslib": "^2.0.0", "typeface-exo": "0.0.22", @@ -649,6 +650,7 @@ "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-11.0.4.tgz", "integrity": "sha512-suhAhsZEv+lLwm8dc524cMvO7gHPi+z2+4tueNS+zDiIObdZc4fs+KoOlnRMdYwba++X/V8mHXuDEQetl3GFcw==", "dependencies": { + "parse5": "^5.0.0", "tslib": "^2.0.0" }, "optionalDependencies": { @@ -911,6 +913,7 @@ "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", + "fsevents": "~2.3.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -4780,6 +4783,7 @@ "dependencies": { "anymatch": "^1.3.0", "async-each": "^1.0.0", + "fsevents": "^1.0.0", "glob-parent": "^2.0.0", "inherits": "^2.0.1", "is-binary-path": "^1.0.0", @@ -10966,7 +10970,8 @@ "dependencies": { "async": "^1.4.0", "optimist": "^0.6.1", - "source-map": "^0.4.4" + "source-map": "^0.4.4", + "uglify-js": "^2.6" }, "bin": { "handlebars": "bin/handlebars" @@ -13695,6 +13700,7 @@ "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", + "fsevents": "~2.3.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -14220,8 +14226,11 @@ "dependencies": { "errno": "^0.1.1", "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", "make-dir": "^2.1.0", "mime": "^1.4.1", + "native-request": "^1.0.5", + "source-map": "~0.6.0", "tslib": "^1.10.0" }, "bin": { @@ -21141,6 +21150,9 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.32.1.tgz", "integrity": "sha512-Op2vWTpvK7t6/Qnm1TTh7VjEZZkN8RWgf0DHbkKzQBwNf748YhXbozHVefqpPp/Fuyk/PQPAnYsBxAEtlMvpUw==", "dev": true, + "dependencies": { + "fsevents": "~2.1.2" + }, "bin": { "rollup": "dist/bin/rollup" }, @@ -21493,6 +21505,7 @@ "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", + "fsevents": "~2.3.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -22832,8 +22845,12 @@ "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", "dashdash": "^1.12.0", - "getpass": "^0.1.1" + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "tweetnacl": "~0.14.0" }, "engines": { "node": ">=0.10.0" @@ -24353,6 +24370,11 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "node_modules/thingbook-api": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/thingbook-api/-/thingbook-api-1.0.14.tgz", + "integrity": "sha512-nX72Sa2SM13Hg1W8PTbJ+IIvoynCNDwTcTEXNScQLrw16b6QcgCRaVJ69W4tzxb0sFffzDN7+xcPoewl//syxg==" + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -25412,8 +25434,10 @@ "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", "dev": true, "dependencies": { + "chokidar": "^3.4.1", "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.1" }, "optionalDependencies": { "chokidar": "^3.4.1", @@ -26366,6 +26390,7 @@ "anymatch": "^2.0.0", "async-each": "^1.0.1", "braces": "^2.3.2", + "fsevents": "^1.2.7", "glob-parent": "^3.1.0", "inherits": "^2.0.3", "is-binary-path": "^1.0.0", @@ -48529,6 +48554,11 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "thingbook-api": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/thingbook-api/-/thingbook-api-1.0.14.tgz", + "integrity": "sha512-nX72Sa2SM13Hg1W8PTbJ+IIvoynCNDwTcTEXNScQLrw16b6QcgCRaVJ69W4tzxb0sFffzDN7+xcPoewl//syxg==" + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", diff --git a/package.json b/package.json index 565fdce0..2965ed2b 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,15 @@ "license": "MIT", "repository": { "type": "git", - "url": "git+https://github.com/akveo/ngx-admin.git" + "url": "git+https://github.com/kerry-t-johnson/thingbook-ui" }, "bugs": { - "url": "https://github.com/akveo/ngx-admin/issues" + "url": "https://github.com/kerry-t-johnson/thingbook-ui/issues" }, "scripts": { "ng": "ng", "conventional-changelog": "conventional-changelog", - "start": "ng serve", + "start": "ng serve --host 0.0.0.0", "build": "ng build", "build:prod": "npm run build -- --prod --aot", "test": "ng test", @@ -70,6 +70,7 @@ "rxjs-compat": "6.3.0", "socicon": "3.0.5", "style-loader": "^1.1.3", + "thingbook-api": "^1.0.14", "tinymce": "4.5.7", "tslib": "^2.0.0", "typeface-exo": "0.0.22", diff --git a/src/app/@core/core.module.ts b/src/app/@core/core.module.ts index 1135104a..3da46bf1 100644 --- a/src/app/@core/core.module.ts +++ b/src/app/@core/core.module.ts @@ -12,7 +12,6 @@ import { SeoService, StateService, } from './utils'; -import { UserData } from './data/users'; import { ElectricityData } from './data/electricity'; import { SmartTableData } from './data/smart-table'; import { UserActivityData } from './data/user-activity'; @@ -32,7 +31,6 @@ import { StatsProgressBarData } from './data/stats-progress-bar'; import { VisitorsAnalyticsData } from './data/visitors-analytics'; import { SecurityCamerasData } from './data/security-cameras'; -import { UserService } from './mock/users.service'; import { ElectricityService } from './mock/electricity.service'; import { SmartTableService } from './mock/smart-table.service'; import { UserActivityService } from './mock/user-activity.service'; @@ -72,7 +70,6 @@ const socialLinks = [ ]; const DATA_SERVICES = [ - { provide: UserData, useClass: UserService }, { provide: ElectricityData, useClass: ElectricityService }, { provide: SmartTableData, useClass: SmartTableService }, { provide: UserActivityData, useClass: UserActivityService }, diff --git a/src/app/@core/data/users.ts b/src/app/@core/data/users.ts deleted file mode 100644 index bb60efc9..00000000 --- a/src/app/@core/data/users.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Observable } from 'rxjs'; - -export interface User { - name: string; - picture: string; -} - -export interface Contacts { - user: User; - type: string; -} - -export interface RecentUsers extends Contacts { - time: number; -} - -export abstract class UserData { - abstract getUsers(): Observable; - abstract getContacts(): Observable; - abstract getRecentUsers(): Observable; -} diff --git a/src/app/@core/mock/mock-data.module.ts b/src/app/@core/mock/mock-data.module.ts index 6e03591f..b4fb0b03 100644 --- a/src/app/@core/mock/mock-data.module.ts +++ b/src/app/@core/mock/mock-data.module.ts @@ -1,7 +1,6 @@ import { NgModule, ModuleWithProviders } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { UserService } from './users.service'; import { ElectricityService } from './electricity.service'; import { SmartTableService } from './smart-table.service'; import { UserActivityService } from './user-activity.service'; @@ -23,7 +22,6 @@ import { VisitorsAnalyticsService } from './visitors-analytics.service'; import { SecurityCamerasService } from './security-cameras.service'; const SERVICES = [ - UserService, ElectricityService, SmartTableService, UserActivityService, diff --git a/src/app/@core/mock/users.service.ts b/src/app/@core/mock/users.service.ts deleted file mode 100644 index 8c748ce0..00000000 --- a/src/app/@core/mock/users.service.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { of as observableOf, Observable } from 'rxjs'; -import { Injectable } from '@angular/core'; -import { Contacts, RecentUsers, UserData } from '../data/users'; - -@Injectable() -export class UserService extends UserData { - - private time: Date = new Date; - - private users = { - nick: { name: 'Nick Jones', picture: 'assets/images/nick.png' }, - eva: { name: 'Eva Moor', picture: 'assets/images/eva.png' }, - jack: { name: 'Jack Williams', picture: 'assets/images/jack.png' }, - lee: { name: 'Lee Wong', picture: 'assets/images/lee.png' }, - alan: { name: 'Alan Thompson', picture: 'assets/images/alan.png' }, - kate: { name: 'Kate Martinez', picture: 'assets/images/kate.png' }, - }; - private types = { - mobile: 'mobile', - home: 'home', - work: 'work', - }; - private contacts: Contacts[] = [ - { user: this.users.nick, type: this.types.mobile }, - { user: this.users.eva, type: this.types.home }, - { user: this.users.jack, type: this.types.mobile }, - { user: this.users.lee, type: this.types.mobile }, - { user: this.users.alan, type: this.types.home }, - { user: this.users.kate, type: this.types.work }, - ]; - private recentUsers: RecentUsers[] = [ - { user: this.users.alan, type: this.types.home, time: this.time.setHours(21, 12)}, - { user: this.users.eva, type: this.types.home, time: this.time.setHours(17, 45)}, - { user: this.users.nick, type: this.types.mobile, time: this.time.setHours(5, 29)}, - { user: this.users.lee, type: this.types.mobile, time: this.time.setHours(11, 24)}, - { user: this.users.jack, type: this.types.mobile, time: this.time.setHours(10, 45)}, - { user: this.users.kate, type: this.types.work, time: this.time.setHours(9, 42)}, - { user: this.users.kate, type: this.types.work, time: this.time.setHours(9, 31)}, - { user: this.users.jack, type: this.types.mobile, time: this.time.setHours(8, 0)}, - ]; - - getUsers(): Observable { - return observableOf(this.users); - } - - getContacts(): Observable { - return observableOf(this.contacts); - } - - getRecentUsers(): Observable { - return observableOf(this.recentUsers); - } -} diff --git a/src/app/@theme/components/header/header.component.html b/src/app/@theme/components/header/header.component.html index 6fdc5d35..632ec880 100644 --- a/src/app/@theme/components/header/header.component.html +++ b/src/app/@theme/components/header/header.component.html @@ -3,7 +3,7 @@ - + {{ theme.name }} @@ -18,12 +18,10 @@ - - + + - + \ No newline at end of file diff --git a/src/app/@theme/components/header/header.component.ts b/src/app/@theme/components/header/header.component.ts index bfe2b9bc..7077b8e8 100644 --- a/src/app/@theme/components/header/header.component.ts +++ b/src/app/@theme/components/header/header.component.ts @@ -1,10 +1,10 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { NbMediaBreakpointsService, NbMenuService, NbSidebarService, NbThemeService } from '@nebular/theme'; -import { UserData } from '../../../@core/data/users'; import { LayoutService } from '../../../@core/utils'; import { map, takeUntil } from 'rxjs/operators'; import { Subject } from 'rxjs'; +import { UserService, User } from '../../../modules/user/user.service'; @Component({ selector: 'ngx-header', @@ -15,45 +15,40 @@ export class HeaderComponent implements OnInit, OnDestroy { private destroy$: Subject = new Subject(); userPictureOnly: boolean = false; - user: any; + user: User; + displayName: string; themes = [ - { - value: 'default', - name: 'Light', - }, - { - value: 'dark', - name: 'Dark', - }, - { - value: 'cosmic', - name: 'Cosmic', - }, - { - value: 'corporate', - name: 'Corporate', - }, + { value: 'default', name: 'Light', }, + { value: 'dark', name: 'Dark', }, + { value: 'cosmic', name: 'Cosmic', }, + { value: 'corporate', name: 'Corporate', }, ]; currentTheme = 'default'; - userMenu = [ { title: 'Profile' }, { title: 'Log out' } ]; + userMenu = [ + { title: 'Profile', icon: 'person-outline', link: 'pages/user/profile', }, + { title: 'Log out' } + ]; constructor(private sidebarService: NbSidebarService, - private menuService: NbMenuService, - private themeService: NbThemeService, - private userService: UserData, - private layoutService: LayoutService, - private breakpointService: NbMediaBreakpointsService) { + private menuService: NbMenuService, + private themeService: NbThemeService, + private userService: UserService, + private layoutService: LayoutService, + private breakpointService: NbMediaBreakpointsService) { } ngOnInit() { this.currentTheme = this.themeService.currentTheme; - this.userService.getUsers() + this.userService.onUserStatus() .pipe(takeUntil(this.destroy$)) - .subscribe((users: any) => this.user = users.nick); + .subscribe((user: any) => { + this.user = user; + this.displayName = user?.first ?? user?.email; + }); const { xl } = this.breakpointService.getBreakpointsMap(); this.themeService.onMediaQueryChange() diff --git a/src/app/app.module.ts b/src/app/app.module.ts index d55f48ee..057e471b 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -6,7 +6,7 @@ import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NgModule } from '@angular/core'; -import { HttpClientModule } from '@angular/common/http'; +import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { CoreModule } from './@core/core.module'; import { ThemeModule } from './@theme/theme.module'; import { AppComponent } from './app.component'; @@ -15,14 +15,19 @@ import { NbChatModule, NbDatepickerModule, NbDialogModule, + NbIconModule, NbMenuModule, NbSidebarModule, NbToastrModule, NbWindowModule, } from '@nebular/theme'; +import { UserModule } from './modules/user/user.module'; + @NgModule({ - declarations: [AppComponent], + declarations: [ + AppComponent, + ], imports: [ BrowserModule, BrowserAnimationsModule, @@ -32,6 +37,7 @@ import { NbMenuModule.forRoot(), NbDatepickerModule.forRoot(), NbDialogModule.forRoot(), + NbIconModule, NbWindowModule.forRoot(), NbToastrModule.forRoot(), NbChatModule.forRoot({ @@ -39,8 +45,11 @@ import { }), CoreModule.forRoot(), ThemeModule.forRoot(), + UserModule.forRoot(), + ], bootstrap: [AppComponent], + providers: [] }) export class AppModule { } diff --git a/src/app/modules/organization/components/data-sharing-agreement/data-sharing-agreement.component.html b/src/app/modules/organization/components/data-sharing-agreement/data-sharing-agreement.component.html new file mode 100644 index 00000000..55fe7999 --- /dev/null +++ b/src/app/modules/organization/components/data-sharing-agreement/data-sharing-agreement.component.html @@ -0,0 +1,34 @@ + + + {{agreement?.name}} + + + + + + +
+ {{agreement?.producer.name}} + {{agreement?.consumer.name}} +
+
+ + +
+
+ + + + {{ fragment.name }} + + {{ fragment.type | titlecase}} + + + + {{fragment.text}} + + + +
+
+
\ No newline at end of file diff --git a/src/app/modules/organization/components/data-sharing-agreement/data-sharing-agreement.component.scss b/src/app/modules/organization/components/data-sharing-agreement/data-sharing-agreement.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/modules/organization/components/data-sharing-agreement/data-sharing-agreement.component.ts b/src/app/modules/organization/components/data-sharing-agreement/data-sharing-agreement.component.ts new file mode 100644 index 00000000..06ef3f0c --- /dev/null +++ b/src/app/modules/organization/components/data-sharing-agreement/data-sharing-agreement.component.ts @@ -0,0 +1,21 @@ +import { Component, Input, OnInit } from "@angular/core"; +import { OrganizationDataSharingAgreement } from "thingbook-api/lib"; +import { OrganizationService } from "../../organization.service"; + +@Component({ + selector: 'org-data-sharing-agreement', + templateUrl: './data-sharing-agreement.component.html', + styleUrls: ['./data-sharing-agreement.component.scss'] +}) +export class DataSharingAgreementComponent implements OnInit { + + @Input() agreement: OrganizationDataSharingAgreement; + + constructor() { + + } + + ngOnInit() { + + } +} \ No newline at end of file diff --git a/src/app/modules/organization/components/detail-page/detail-page.component.html b/src/app/modules/organization/components/detail-page/detail-page.component.html new file mode 100644 index 00000000..2482b9b3 --- /dev/null +++ b/src/app/modules/organization/components/detail-page/detail-page.component.html @@ -0,0 +1,19 @@ +

{{org?.name}}

+ + + + + + + + + + +
Loading...
+
+
+ + TBD + +
\ No newline at end of file diff --git a/src/app/modules/organization/components/detail-page/detail-page.component.scss b/src/app/modules/organization/components/detail-page/detail-page.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/modules/organization/components/detail-page/detail-page.component.ts b/src/app/modules/organization/components/detail-page/detail-page.component.ts new file mode 100644 index 00000000..bf361940 --- /dev/null +++ b/src/app/modules/organization/components/detail-page/detail-page.component.ts @@ -0,0 +1,38 @@ +import { Component, OnInit } from "@angular/core"; +import { ActivatedRoute, ParamMap } from "@angular/router"; +import { switchMap } from "rxjs/operators"; +import { Organization, OrganizationDataSharingAgreement } from "thingbook-api/lib"; +import { OrganizationService } from "../../organization.service"; + +@Component({ + selector: 'org-detail', + templateUrl: './detail-page.component.html', + styleUrls: ['./detail-page.component.scss'], +}) +export class OrganizationDetailPageComponent implements OnInit { + + org: Organization; + agreements: OrganizationDataSharingAgreement[] = []; + + constructor(private route: ActivatedRoute, private orgSvc: OrganizationService) { + + } + + ngOnInit() { + this.route.paramMap.pipe( + switchMap((params: ParamMap) => { + return this.orgSvc.getOrganization(params.get('id')); + }) + ).subscribe(org => this.org = org); + + this.route.paramMap.pipe( + switchMap((params: ParamMap) => { + return this.orgSvc.getOrganizationAgreements(params.get('id')); + }) + ).subscribe(agreements => { + this.agreements = agreements; + console.log(agreements); + }); + } + +} diff --git a/src/app/modules/organization/components/list-page/list-page.component.html b/src/app/modules/organization/components/list-page/list-page.component.html new file mode 100644 index 00000000..3107da3b --- /dev/null +++ b/src/app/modules/organization/components/list-page/list-page.component.html @@ -0,0 +1,17 @@ +
+
+ + Organizations + + + + {{o.name}} + {{o.domainName}} + + Details... + + + + +
+
\ No newline at end of file diff --git a/src/app/modules/organization/components/list-page/list-page.component.scss b/src/app/modules/organization/components/list-page/list-page.component.scss new file mode 100644 index 00000000..30e51bd2 --- /dev/null +++ b/src/app/modules/organization/components/list-page/list-page.component.scss @@ -0,0 +1,13 @@ +@import '../../../../@theme/styles/themes'; + +@include nb-install-component() { + .list-card { + nb-card-header { + border-bottom: none; + } + + nb-card-body { + padding: 0; + } + } +} diff --git a/src/app/modules/organization/components/list-page/list-page.component.ts b/src/app/modules/organization/components/list-page/list-page.component.ts new file mode 100644 index 00000000..2448e3d2 --- /dev/null +++ b/src/app/modules/organization/components/list-page/list-page.component.ts @@ -0,0 +1,25 @@ +import { Component, OnInit } from "@angular/core"; +import { Organization } from "thingbook-api/lib"; +import { OrganizationService } from "../../organization.service"; + +@Component({ + selector: 'organization-list', + templateUrl: './list-page.component.html', + styleUrls: ['./list-page.component.scss'] +}) +export class OrganizationListPageComponent implements OnInit { + + orgs: Organization[]; + + constructor(private orgSvc: OrganizationService) { + + } + + ngOnInit() { + this.orgSvc.getOrganizations() + .subscribe(orgs => { + this.orgs = orgs; + console.log(this.orgs); + }); + } +} \ No newline at end of file diff --git a/src/app/modules/organization/organization.module.ts b/src/app/modules/organization/organization.module.ts new file mode 100644 index 00000000..e2959d39 --- /dev/null +++ b/src/app/modules/organization/organization.module.ts @@ -0,0 +1,34 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { OrganizationListPageComponent } from './components/list-page/list-page.component'; +import { NbAccordionModule, NbCardModule, NbIconModule, NbListModule, NbTabsetModule } from '@nebular/theme'; +import { ThemeModule } from '../../@theme/theme.module'; +import { OrganizationDetailPageComponent } from './components/detail-page/detail-page.component'; +import { DataSharingAgreementComponent } from './components/data-sharing-agreement/data-sharing-agreement.component'; +import { WidgetsModule } from '../../widgets/widgets.module'; + + + +@NgModule({ + declarations: [ + OrganizationListPageComponent, + OrganizationDetailPageComponent, + DataSharingAgreementComponent, + ], + imports: [ + CommonModule, + ThemeModule, + NbAccordionModule, + NbCardModule, + NbIconModule, + NbListModule, + NbTabsetModule, + RouterModule.forChild([ + { path: 'list', component: OrganizationListPageComponent }, + { path: ':id', component: OrganizationDetailPageComponent } + ]), + WidgetsModule, + ] +}) +export class OrganizationModule { } diff --git a/src/app/modules/organization/organization.service.ts b/src/app/modules/organization/organization.service.ts new file mode 100644 index 00000000..a2ddc9c2 --- /dev/null +++ b/src/app/modules/organization/organization.service.ts @@ -0,0 +1,51 @@ +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { Observable, of } from "rxjs"; +import { catchError, tap } from "rxjs/operators"; +import { Organization, OrganizationDataSharingAgreement } from 'thingbook-api'; + +@Injectable({ + providedIn: 'root' +}) +export class OrganizationService { + + constructor(private http: HttpClient) { + + } + + getOrganization(id): Observable { + return this.http.get(`http://localhost:3000/api/v1/organization/${id}`) + .pipe( + catchError(this.handleError('getOrganization', null)) + ); + } + + getOrganizationAgreements(id): Observable { + return this.http.get(`http://localhost:3000/api/v1/organization/${id}/agreement`) + .pipe( + catchError(this.handleError('getOrganizationAgreements', [])) + ); + } + + getOrganizations(): Observable { + return this.http.get('http://localhost:3000/api/v1/organization') + .pipe( + catchError(this.handleError('getOrganizations', [])) + ); + } + + private handleError(operation = 'operation', result?: T) { + return (error: any): Observable => { + + // TODO: send the error to remote logging infrastructure + console.error(error); // log to console instead + + // TODO: better job of transforming error for user consumption + console.log(`${operation} failed: ${error.message}`); + + // Let the app keep running by returning an empty result. + return of(result as T); + }; + } + +} \ No newline at end of file diff --git a/src/app/modules/user/pages/profile.component.html b/src/app/modules/user/pages/profile.component.html new file mode 100644 index 00000000..775fbd39 --- /dev/null +++ b/src/app/modules/user/pages/profile.component.html @@ -0,0 +1,17 @@ + + {{user?.email}} + +
+
+ + +
+
+ + +
+ +
+
+
\ No newline at end of file diff --git a/src/app/modules/user/pages/profile.component.scss b/src/app/modules/user/pages/profile.component.scss new file mode 100644 index 00000000..4328e975 --- /dev/null +++ b/src/app/modules/user/pages/profile.component.scss @@ -0,0 +1,15 @@ +nb-checkbox { + margin-bottom: 1rem; +} + +.form-inline [fullWidth] { + flex: 1; +} + +.form-inline > * { + margin: 0 1.5rem 1.5rem 0; +} + +nb-card.inline-form-card nb-card-body { + padding-bottom: 0; +} diff --git a/src/app/modules/user/pages/profile.component.ts b/src/app/modules/user/pages/profile.component.ts new file mode 100644 index 00000000..429d298d --- /dev/null +++ b/src/app/modules/user/pages/profile.component.ts @@ -0,0 +1,38 @@ +import { Component, OnDestroy, OnInit } from "@angular/core"; +import { Subject } from "rxjs"; +import { takeUntil } from "rxjs/operators"; +import { User, UserService } from "../user.service"; + +@Component({ + selector: 'user-profile', + templateUrl: './profile.component.html', + styleUrls: ['./profile.component.scss'] +}) +export class UserProfileComponent implements OnInit, OnDestroy { + + private destroy$: Subject = new Subject(); + public user: User; + public first: string = "Blah"; + public last: string; + + constructor(private userService: UserService) { + + } + + ngOnInit() { + this.userService.onUserStatus() + .pipe(takeUntil(this.destroy$)) + .subscribe((user: any) => { + this.user = user; + this.user.first = 'Blargle'; + this.first = user?.first || 'Foo!'; + this.last = user?.last || 'Bar!'; + }); + } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + +} \ No newline at end of file diff --git a/src/app/modules/user/user.module.ts b/src/app/modules/user/user.module.ts new file mode 100644 index 00000000..be40c4aa --- /dev/null +++ b/src/app/modules/user/user.module.ts @@ -0,0 +1,67 @@ +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { NbPasswordAuthStrategy, NbAuthModule } from '@nebular/auth'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { LoginInterceptor } from '../../user.interceptors'; +import { RouterModule } from '@angular/router'; +import { UserProfileComponent } from './pages/profile.component'; +import { ThemeModule } from '../../@theme/theme.module'; +import { NbCardModule, NbCheckboxComponent, NbCheckboxModule, NbInputModule, NbMenuModule } from '@nebular/theme'; +import { FormsModule } from '@angular/forms'; + +@NgModule({ + declarations: [ + UserProfileComponent, + ], + imports: [ + CommonModule, + ThemeModule, + NbMenuModule, + NbCardModule, + NbCheckboxModule, + NbInputModule, + FormsModule, + RouterModule.forChild([ + { path: 'profile', component: UserProfileComponent } + ]), + ], + exports: [ + UserProfileComponent, + RouterModule, + ], +}) +export class UserModule { + + static forRoot(): ModuleWithProviders[] { + return [{ + ngModule: UserModule, + providers: [ + { provide: HTTP_INTERCEPTORS, useClass: LoginInterceptor, multi: true } + ], + }, + NbAuthModule.forRoot({ + strategies: [ + NbPasswordAuthStrategy.setup({ + name: 'email', + baseEndpoint: 'http://localhost:3000/api/v1/user', + login: { + endpoint: '/login', + requireValidToken: false, + }, + register: { + endpoint: '/register', + requireValidToken: false, + }, + logout: { + method: 'get', + endpoint: '/logout', + requireValidToken: false, + } + }), + ], + forms: {}, + }), + ]; + } + +} diff --git a/src/app/modules/user/user.service.spec.ts b/src/app/modules/user/user.service.spec.ts new file mode 100644 index 00000000..eb2d718f --- /dev/null +++ b/src/app/modules/user/user.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { UserServiceImpl } from './user.service.impl'; + +describe('UserService', () => { + let service: UserServiceImpl; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(UserServiceImpl); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/modules/user/user.service.ts b/src/app/modules/user/user.service.ts new file mode 100644 index 00000000..73cd7653 --- /dev/null +++ b/src/app/modules/user/user.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; +import { Observable, of as ObservableOf } from 'rxjs'; +import { User } from 'thingbook-api'; +import { LoginInterceptor } from '../../user.interceptors'; + +export { User }; + +@Injectable({ + providedIn: 'root' +}) +export class UserService { + + private user: User = undefined; + + constructor() { + LoginInterceptor.s.subscribe({ + next: (v) => { + this.user = v; + } + }); + } + + public onUserStatus(): Observable { + return ObservableOf(this.user); + } +} + diff --git a/src/app/pages/dashboard/contacts/contacts.component.html b/src/app/pages/dashboard/contacts/contacts.component.html deleted file mode 100644 index 0aac40bf..00000000 --- a/src/app/pages/dashboard/contacts/contacts.component.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - {{ c.time | date: 'shortTime' }} - - - - - - diff --git a/src/app/pages/dashboard/contacts/contacts.component.scss b/src/app/pages/dashboard/contacts/contacts.component.scss deleted file mode 100644 index 787ed0be..00000000 --- a/src/app/pages/dashboard/contacts/contacts.component.scss +++ /dev/null @@ -1,34 +0,0 @@ -@import '../../../@theme/styles/themes'; - -@include nb-install-component() { - nb-card { - overflow: hidden; - } - - nb-tabset { - display: flex; - flex-direction: column; - - ::ng-deep ul { - // make same size as card header - padding-bottom: 1px; - ::ng-deep .tab-link { - padding: 1.25rem 2rem; - } - } - } - - nb-tab { - padding: 0; - } - - .contact { - display: flex; - align-items: center; - justify-content: space-between; - - &:first-child { - border-top: none; - } - } -} diff --git a/src/app/pages/dashboard/contacts/contacts.component.ts b/src/app/pages/dashboard/contacts/contacts.component.ts deleted file mode 100644 index c8a523d6..00000000 --- a/src/app/pages/dashboard/contacts/contacts.component.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Component, OnDestroy } from '@angular/core'; -import { takeWhile } from 'rxjs/operators'; -import { forkJoin } from 'rxjs'; - -import { Contacts, RecentUsers, UserData } from '../../../@core/data/users'; - -@Component({ - selector: 'ngx-contacts', - styleUrls: ['./contacts.component.scss'], - templateUrl: './contacts.component.html', -}) -export class ContactsComponent implements OnDestroy { - - private alive = true; - - contacts: any[]; - recent: any[]; - - constructor(private userService: UserData) { - forkJoin( - this.userService.getContacts(), - this.userService.getRecentUsers(), - ) - .pipe(takeWhile(() => this.alive)) - .subscribe(([contacts, recent]: [Contacts[], RecentUsers[]]) => { - this.contacts = contacts; - this.recent = recent; - }); - } - - ngOnDestroy() { - this.alive = false; - } -} diff --git a/src/app/pages/dashboard/dashboard.component.html b/src/app/pages/dashboard/dashboard.component.html index 6b99758a..797ee28b 100644 --- a/src/app/pages/dashboard/dashboard.component.html +++ b/src/app/pages/dashboard/dashboard.component.html @@ -21,10 +21,6 @@ -
- -
-
@@ -39,4 +35,4 @@
-
+ \ No newline at end of file diff --git a/src/app/pages/dashboard/dashboard.module.ts b/src/app/pages/dashboard/dashboard.module.ts index b938dd67..5df5f954 100644 --- a/src/app/pages/dashboard/dashboard.module.ts +++ b/src/app/pages/dashboard/dashboard.module.ts @@ -15,7 +15,6 @@ import { NgxEchartsModule } from 'ngx-echarts'; import { ThemeModule } from '../../@theme/theme.module'; import { DashboardComponent } from './dashboard.component'; import { StatusCardComponent } from './status-card/status-card.component'; -import { ContactsComponent } from './contacts/contacts.component'; import { RoomsComponent } from './rooms/rooms.component'; import { RoomSelectorComponent } from './rooms/room-selector/room-selector.component'; import { TemperatureComponent } from './temperature/temperature.component'; @@ -51,7 +50,6 @@ import { FormsModule } from '@angular/forms'; DashboardComponent, StatusCardComponent, TemperatureDraggerComponent, - ContactsComponent, RoomSelectorComponent, TemperatureComponent, RoomsComponent, diff --git a/src/app/pages/pages-menu.ts b/src/app/pages/pages-menu.ts index 6134b318..aa3399c2 100644 --- a/src/app/pages/pages-menu.ts +++ b/src/app/pages/pages-menu.ts @@ -1,6 +1,11 @@ import { NbMenuItem } from '@nebular/theme'; export const MENU_ITEMS: NbMenuItem[] = [ + { + title: 'Organizations', + icon: 'keypad-outline', + link: '/pages/org/list', + }, { title: 'E-commerce', icon: 'shopping-cart-outline', diff --git a/src/app/pages/pages-routing.module.ts b/src/app/pages/pages-routing.module.ts index 376cc4fa..31ce8041 100644 --- a/src/app/pages/pages-routing.module.ts +++ b/src/app/pages/pages-routing.module.ts @@ -14,6 +14,16 @@ const routes: Routes = [{ path: 'dashboard', component: ECommerceComponent, }, + { + path: 'user', + loadChildren: () => import('../modules/user/user.module') + .then(m => m.UserModule) + }, + { + path: 'org', + loadChildren: () => import('../modules/organization/organization.module') + .then(m => m.OrganizationModule) + }, { path: 'iot-dashboard', component: DashboardComponent, diff --git a/src/app/user.interceptors.ts b/src/app/user.interceptors.ts new file mode 100644 index 00000000..ec1cfebe --- /dev/null +++ b/src/app/user.interceptors.ts @@ -0,0 +1,22 @@ +import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { BehaviorSubject, Observable, Subject } from "rxjs"; +import { filter, tap } from "rxjs/operators"; +import * as api from 'thingbook-api'; + +@Injectable() +export class LoginInterceptor implements HttpInterceptor { + + public static s: BehaviorSubject = new BehaviorSubject(undefined); + + intercept(httpRequest: HttpRequest, next: HttpHandler): Observable> { + return next.handle(httpRequest).pipe( + tap((event: HttpResponse) => { + if (event instanceof HttpResponse && event.url.endsWith('/user/login')) { + LoginInterceptor.s.next(event.body); + } + }) + ); + } +} + diff --git a/src/app/widgets/date/date-calendar.component.html b/src/app/widgets/date/date-calendar.component.html new file mode 100644 index 00000000..bf708b39 --- /dev/null +++ b/src/app/widgets/date/date-calendar.component.html @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/src/app/widgets/date/date-calendar.component.scss b/src/app/widgets/date/date-calendar.component.scss new file mode 100644 index 00000000..b2444d1e --- /dev/null +++ b/src/app/widgets/date/date-calendar.component.scss @@ -0,0 +1,175 @@ +/****************************************/ +/* Styling rules, such as font and colors */ +.date-as-calendar { + font-variant: normal; + font-style: normal; + font-weight: normal; + font-family: "Helvetica", "Arial", sans-serif; + + /* It seems vertical-align: baseline does not work correctly with display: inline-flex. */ + vertical-align: top; + + /* margin: 1ex; */ + + color: black; + background: white; + background : linear-gradient(to bottom right, #FFF 0%, #EEE 100%); + + border: 1px solid #888; + border-radius: 3px; + overflow: hidden; + + box-shadow: 2px 2px 2px -2px black; +} +.date-as-calendar .weekday, +.date-as-calendar .day, +.date-as-calendar .month, +.date-as-calendar .year { + text-align: center; + line-height: 1.0; +} +.date-as-calendar .month { + font-family: "Oswald", sans-serif; + text-transform: uppercase; + background: #B11; + background : linear-gradient(to bottom right, #D66 0%, #A00 100%); + color: white; +} + +/****************************************/ +/* Layout rules using position: absolute and pixels. */ +.position-pixels.date-as-calendar { + display: inline-block; + position: relative; + width: 64px; + height: 64px; +} +.position-pixels.date-as-calendar .weekday, +.position-pixels.date-as-calendar .day, +.position-pixels.date-as-calendar .month, +.position-pixels.date-as-calendar .year { + display: block; + position: absolute; + left: 0; + right: 0; + width: 100%; + height: 1em; +} +.position-pixels.date-as-calendar .month { + top: 0px; + font-size: 12px; + padding: 2px 0; +} +.position-pixels.date-as-calendar .weekday { + top: 16px; + font-size: 10px; +} +.position-pixels.date-as-calendar .day { + top: 26px; + font-size: 24px; +} +.position-pixels.date-as-calendar .year { + top: 50px; + font-size: 14px; +} + +/****************************************/ +/* Layout rules using position: absolute and relative dimensions using em. */ +.position-em.date-as-calendar { + display: inline-block; + position: relative; + + width: 4em; + height: 4em; +} +.position-em.date-as-calendar .weekday, +.position-em.date-as-calendar .day, +.position-em.date-as-calendar .month, +.position-em.date-as-calendar .year { + display: block; + position: absolute; + left: 0; + right: 0; + width: 100%; + height: 1em; +} +.position-em.date-as-calendar .month { + top: 0px; + font-size: 0.75em; + padding: 0.1em 0; +} +.position-em.date-as-calendar .weekday { + top: 1.6em; + font-size: 0.6125em; +} +.position-em.date-as-calendar .day { + top: 1.1em; + font-size: 1.5em +} +.position-em.date-as-calendar .year { + bottom: 0px; + font-size: 0.87750em; +} + +/****************************************/ +/* Layout rules using display: inline-flex and relative dimensions using em. */ +.inline-flex.date-as-calendar { + display: inline-flex; + flex-direction: column; + flex-wrap: nowrap; + justify-content: space-between; + + width: 4em; + height: 4em; +} +.inline-flex.date-as-calendar .weekday, +.inline-flex.date-as-calendar .day, +.inline-flex.date-as-calendar .month, +.inline-flex.date-as-calendar .year { + display: block; + flex: 1 1 auto; +} +.inline-flex.date-as-calendar .month { + order: 1; + font-size: 0.75em; + padding: 0.1em 0; +} +.inline-flex.date-as-calendar .weekday { + order: 2; + font-size: 0.6125em; +} +.inline-flex.date-as-calendar .day { + order: 3; + font-size: 1.5em; +} +.inline-flex.date-as-calendar .year { + order: 4; + font-size: 0.87750em; +} + +/****************************************/ +/* Multiple sizes. */ +.date-as-calendar.size0_5x { + font-size: 8px; +} +.date-as-calendar.size0_75x { + font-size: 12px; +} +.date-as-calendar.size1x { + font-size: 16px; +} +.date-as-calendar.size1_25x { + font-size: 20px; +} +.date-as-calendar.size1_5x { + font-size: 24px; +} +.date-as-calendar.size1_75x { + font-size: 28px; +} +.date-as-calendar.size2x { + font-size: 32px; +} +.date-as-calendar.size3x { + font-size: 48px; +} \ No newline at end of file diff --git a/src/app/widgets/date/date-calendar.component.ts b/src/app/widgets/date/date-calendar.component.ts new file mode 100644 index 00000000..8fc6da39 --- /dev/null +++ b/src/app/widgets/date/date-calendar.component.ts @@ -0,0 +1,58 @@ +import { getLocaleDateTimeFormat } from "@angular/common"; +import { Component, Input, OnInit } from "@angular/core"; + +@Component({ + selector: 'app-date-calendar', + templateUrl: './date-calendar.component.html', + styleUrls: ['./date-calendar.component.scss'] +}) +export class DateCalendarComponent implements OnInit { + @Input() date: Date | string; + @Input() size: string = "size1x"; + + datestr: string; + weekday: string; + day: string; + month: string; + year: string; + + constructor() { + } + + ngOnInit() { + if (typeof this.date === 'string') { + this.date = new Date(this.date); + } + + this.datestr = this.date.toISOString(); + + const dateParts: any = this.extract(this.date); + + this.weekday = dateParts.weekday; + this.day = dateParts.day; + this.month = dateParts.month; + this.year = dateParts.year; + } + + private extract(date: Date): object { + const formatter: Intl.DateTimeFormat = new Intl.DateTimeFormat(navigator.language, { + weekday: 'long', + year: 'numeric', + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + hour12: true, + timeZone: 'UTC' + }); + const parts: Intl.DateTimeFormatPart[] = formatter.formatToParts(date); + + let result = {} + for (let p of parts) { + result[p.type] = p.value; + } + + return result; + } +} \ No newline at end of file diff --git a/src/app/widgets/verification/verification-icon.component.html b/src/app/widgets/verification/verification-icon.component.html new file mode 100644 index 00000000..1989a7a2 --- /dev/null +++ b/src/app/widgets/verification/verification-icon.component.html @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/app/widgets/verification/verification-icon.component.scss b/src/app/widgets/verification/verification-icon.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/widgets/verification/verification-icon.component.ts b/src/app/widgets/verification/verification-icon.component.ts new file mode 100644 index 00000000..12e194bf --- /dev/null +++ b/src/app/widgets/verification/verification-icon.component.ts @@ -0,0 +1,12 @@ +import { Component, Input } from "@angular/core"; + +@Component({ + selector: 'app-verification-icon', + templateUrl: './verification-icon.component.html', + styleUrls: ['./verification-icon.component.scss'] +}) +export class VerificationIconComponent { + + @Input() value: boolean; + +} \ No newline at end of file diff --git a/src/app/widgets/widgets.module.ts b/src/app/widgets/widgets.module.ts new file mode 100644 index 00000000..8b51b428 --- /dev/null +++ b/src/app/widgets/widgets.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from "@angular/core"; +import { NbIconModule } from "@nebular/theme"; +import { DateCalendarComponent } from "./date/date-calendar.component"; +import { VerificationIconComponent } from "./verification/verification-icon.component"; + +@NgModule({ + declarations: [ + VerificationIconComponent, + DateCalendarComponent, + ], + exports: [ + VerificationIconComponent, + DateCalendarComponent, + ], + imports: [ + NbIconModule, + ] +}) +export class WidgetsModule { } \ No newline at end of file