diff --git a/_old/Dockerfile b/_old/Dockerfile index 4a3bb26..ad5fc75 100644 --- a/_old/Dockerfile +++ b/_old/Dockerfile @@ -28,13 +28,13 @@ RUN apt-get update && apt-get install -y \ python3-dev # # Install Python dependencies -RUN pip install --upgrade pip setuptools wheel -RUN pip install --no-deps \ +RUN pip install --trusted-host pypi.org --upgrade pip setuptools wheel +RUN pip install --trusted-host pypi.org --no-deps \ spinetoolbox==0.7.4 \ spine_engine==0.23.4 \ spine_items==0.21.5 \ spinedb_api==0.30.5 -RUN pip install -r requirements.txt +RUN pip install --trusted-host pypi.org -r requirements.txt # # Install Julia, SpineInterface and SpineOpt RUN python -m jill install --confirm diff --git a/_old/install/install.sh b/_old/install/install.sh index f1c6b22..23a412a 100644 --- a/_old/install/install.sh +++ b/_old/install/install.sh @@ -2,8 +2,8 @@ # Copyright 2023, Battelle Energy Alliance, LLC apt update apt install python3 python3-pip git -y -python3 -m pip install pip --upgrade -python3 -m pip install -r requirements.txt +python3 -m pip install --trusted-host pip --upgrade +python3 -m pip install --trusted-host -r requirements.txt python3 -m jill install --confirm # should lock this to a commit hash julia -e 'using Pkg; Pkg.rm("SpineInterface")' || true diff --git a/api/Dockerfile b/api/Dockerfile index 7f58d5c..b983f9d 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -16,8 +16,8 @@ ENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt \ # Install dependencies RUN apt-get update \ && apt-get install -y build-essential cargo -RUN pip install --upgrade pip \ - && pip install -r requirements-test.txt +RUN pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org--upgrade pip \ + && pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org -r requirements-test.txt COPY ./etc/entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod a+x /usr/local/bin/entrypoint.sh diff --git a/web/angular.json b/web/angular.json index 3d5c0f9..2c961c9 100644 --- a/web/angular.json +++ b/web/angular.json @@ -32,6 +32,7 @@ } ], "styles": [ + "./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css", "src/styles.scss", "src/styles.scss", "node_modules/bootstrap/dist/css/bootstrap.min.css" ], @@ -39,10 +40,8 @@ "node_modules/bootstrap/dist/js/bootstrap.min.js" ], "server": "src/main.server.ts", - "prerender": true, - "ssr": { - "entry": "server.ts" - } + "prerender": false, + "ssr": false }, "configurations": { "production": { diff --git a/web/src/app/about/about.component.html b/web/src/app/about/about.component.html new file mode 100644 index 0000000..6094aa9 --- /dev/null +++ b/web/src/app/about/about.component.html @@ -0,0 +1 @@ +

about works!

diff --git a/web/src/app/about/about.component.scss b/web/src/app/about/about.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/about/about.component.spec.ts b/web/src/app/about/about.component.spec.ts new file mode 100644 index 0000000..74d6d9e --- /dev/null +++ b/web/src/app/about/about.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AboutComponent } from './about.component'; + +describe('AboutComponent', () => { + let component: AboutComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AboutComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AboutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/about/about.component.ts b/web/src/app/about/about.component.ts new file mode 100644 index 0000000..6f52bf4 --- /dev/null +++ b/web/src/app/about/about.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-about', + standalone: true, + imports: [], + templateUrl: './about.component.html', + styleUrl: './about.component.scss' +}) +export class AboutComponent { + +} diff --git a/web/src/app/app.component.html b/web/src/app/app.component.html index c260f76..5192b63 100644 --- a/web/src/app/app.component.html +++ b/web/src/app/app.component.html @@ -5,12 +5,20 @@ ResDEEDS - - - +
+
+ + +
+
+ + + + + + + + + + + diff --git a/web/src/app/app.component.scss b/web/src/app/app.component.scss index e69de29..661a6ad 100644 --- a/web/src/app/app.component.scss +++ b/web/src/app/app.component.scss @@ -0,0 +1,8 @@ +.main{ + font-family: "Roboto", sans-serif; + margin:0 +} + +.content{ + margin:0 +} \ No newline at end of file diff --git a/web/src/app/app.component.ts b/web/src/app/app.component.ts index 8f20fd9..0c7fc5e 100644 --- a/web/src/app/app.component.ts +++ b/web/src/app/app.component.ts @@ -1,6 +1,8 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; +import { LayoutComponent } from './layout/layout.component'; + @Component({ selector: 'app-root', imports: [RouterOutlet], diff --git a/web/src/app/app.config.server.ts b/web/src/app/app.config.server.ts index b4d57c9..c08aab3 100644 --- a/web/src/app/app.config.server.ts +++ b/web/src/app/app.config.server.ts @@ -1,10 +1,19 @@ import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; import { provideServerRendering } from '@angular/platform-server'; import { appConfig } from './app.config'; +import { LOCAL_STORAGE } from './tokens'; const serverConfig: ApplicationConfig = { providers: [ - provideServerRendering() + provideServerRendering(), + { + provide: LOCAL_STORAGE, + useFactory: () => ({ + getItem: () => {}, + setItem: () => {}, + removeItem: () => {}, + }), + }, ] }; diff --git a/web/src/app/app.config.ts b/web/src/app/app.config.ts index 52cd710..3ee0985 100644 --- a/web/src/app/app.config.ts +++ b/web/src/app/app.config.ts @@ -1,9 +1,38 @@ -import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { ApplicationConfig, provideZoneChangeDetection, PLATFORM_ID } from '@angular/core'; import { provideRouter } from '@angular/router'; - +import { isPlatformServer } from '@angular/common'; +import { LOCAL_STORAGE } from './tokens'; import { routes } from './app.routes'; import { provideClientHydration } from '@angular/platform-browser'; +import { provideAnimations } from '@angular/platform-browser/animations'; +import { ConfigService } from './services/config-service'; +import { DashService } from './services/dashboard-service'; +import { AuthService } from './services/auth-service'; +import { provideHttpClient, withFetch } from '@angular/common/http'; +import {ReactiveFormsModule} from '@angular/forms'; +import { LoginService } from './services/login.service'; export const appConfig: ApplicationConfig = { - providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideClientHydration()] + providers: [ + provideZoneChangeDetection({ eventCoalescing: true }), + provideRouter(routes), + provideClientHydration(), + provideAnimations(), + ConfigService, + AuthService, + DashService, + LoginService, + provideHttpClient(withFetch()), + ReactiveFormsModule, + { + provide: LOCAL_STORAGE, + useFactory: (platformId: object) => { + if (isPlatformServer(platformId)) { + return {}; // Return an empty object on the server + } + return localStorage; // Use the browser's localStorage + }, + deps: [PLATFORM_ID], + }, + ] }; diff --git a/web/src/app/app.routes.ts b/web/src/app/app.routes.ts index 7ad11ae..a27d06e 100644 --- a/web/src/app/app.routes.ts +++ b/web/src/app/app.routes.ts @@ -1,6 +1,71 @@ import { Routes } from '@angular/router'; -import { DiagramComponent } from './diagram/diagram.component'; +import { LayoutComponent } from './layout/layout.component'; +import { DashboardComponent } from './dashboard/dashboard.component'; +import { LayoutLoginComponent } from './layout-login/layout-login.component'; +import { AboutComponent } from './about/about.component'; +import { NotFoundComponent } from './not-found/not-found.component'; +import { UserManagementComponent } from './user-management/user-management.component'; +import { LoginComponent } from './login/login.component'; +import { RegisterComponent } from './register/register.component'; +import { PasswordRestoreComponent } from './password-restore/password-restore.component'; +import { AuthGuardGeneral } from './services/authGuards/auth-guard-general'; +import { InvestigationMainComponent } from './investigation/investigation-main/investigation-main.component'; export const routes: Routes = [ - { path: "diagram", component: DiagramComponent }, + + + { path: '', redirectTo: 'login/signin', pathMatch: 'full' }, //default route + { + path: 'login', + component: LayoutLoginComponent, + children: [ + { + path: "signin", + component: LoginComponent + }, + { + path: "register", + component: RegisterComponent + }, + { + path: "passowrdReset", + component: PasswordRestoreComponent, + }, + { path: '**', component: NotFoundComponent } + ] + }, + + { + path: 'main', + component: LayoutComponent, + children: [ + { + path: "", + redirectTo: 'dashboard' , + pathMatch: 'full' + }, + { + path: "dashboard", + component: DashboardComponent, + canActivate: [AuthGuardGeneral] + }, + { + path: "investigation/:id", + component: InvestigationMainComponent, + canActivate: [AuthGuardGeneral] + }, + { + path: "about", + component: AboutComponent, + canActivate: [AuthGuardGeneral] + }, + { + path: "user", + component: UserManagementComponent, + canActivate: [AuthGuardGeneral], + }, + { path: '**', component: NotFoundComponent } + ] + }, + ]; diff --git a/web/src/app/dashboard/dashboard.component.html b/web/src/app/dashboard/dashboard.component.html new file mode 100644 index 0000000..d765e8c --- /dev/null +++ b/web/src/app/dashboard/dashboard.component.html @@ -0,0 +1,59 @@ + +
+ +
+
+
Recent Items
+
+ +
+
+
+
Header Test
+
+
+ This is my body text +
+
+
+
+
+
+
Header Test
+
+
+ This is my body text +
+
+
+
+
+
+
Header Test
+
+
+ This is my body text +
+
+
+
+
Header Test
+
+
+ This is my body text +
+
+
+
+
Header Test
+
+
+ This is my body text +
+
+
+
+ +
\ No newline at end of file diff --git a/web/src/app/dashboard/dashboard.component.scss b/web/src/app/dashboard/dashboard.component.scss new file mode 100644 index 0000000..0d0b7a4 --- /dev/null +++ b/web/src/app/dashboard/dashboard.component.scss @@ -0,0 +1,15 @@ +.dash-container{ + display: flex; + flex-direction: column; + flex-grow: 1; + padding: 1em; + height: calc(100% - 1em); + max-width: 1180px; + margin: auto; +} +.dash-header{ + display: flex; + flex-direction: column; + + border-bottom: solid 1px var(--black); +} \ No newline at end of file diff --git a/web/src/app/dashboard/dashboard.component.spec.ts b/web/src/app/dashboard/dashboard.component.spec.ts new file mode 100644 index 0000000..30e39a2 --- /dev/null +++ b/web/src/app/dashboard/dashboard.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DashboardComponent } from './dashboard.component'; + +describe('DashboardComponent', () => { + let component: DashboardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DashboardComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DashboardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/dashboard/dashboard.component.ts b/web/src/app/dashboard/dashboard.component.ts new file mode 100644 index 0000000..93a1979 --- /dev/null +++ b/web/src/app/dashboard/dashboard.component.ts @@ -0,0 +1,31 @@ +import { Component, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ConfigService } from '../services/config-service'; +import { DashService } from '../services/dashboard-service'; +import { HostBinding } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; +import { SafeStyle } from '@angular/platform-browser'; +import { RecentItemsComponent } from './recent-items/recent-items.component'; + +@Component({ + selector: 'app-dashboard', + standalone: true, + imports: [RecentItemsComponent], + templateUrl: './dashboard.component.html', + styleUrl: './dashboard.component.scss', + // schemas: [NO_ERRORS_SCHEMA] +}) +export class DashboardComponent { + constructor( + public configSvc: ConfigService, + public dashSvc: DashService, + public sanitizer: DomSanitizer, + ){ + } + ngOnint(){ + console.log(this.dashSvc.loadDashBoard()) + } + @HostBinding('style') + get myStyle(): SafeStyle { + return this.sanitizer.bypassSecurityTrustStyle('display: flex; flex-grow: 1'); + } +} diff --git a/web/src/app/dashboard/recent-items/recent-items.component.html b/web/src/app/dashboard/recent-items/recent-items.component.html new file mode 100644 index 0000000..01b9073 --- /dev/null +++ b/web/src/app/dashboard/recent-items/recent-items.component.html @@ -0,0 +1,18 @@ +
+ + + + + + + + + + + + + + + +
Title {{element.title}} Date {{element.date | date}}
+
\ No newline at end of file diff --git a/web/src/app/dashboard/recent-items/recent-items.component.scss b/web/src/app/dashboard/recent-items/recent-items.component.scss new file mode 100644 index 0000000..6e79032 --- /dev/null +++ b/web/src/app/dashboard/recent-items/recent-items.component.scss @@ -0,0 +1,8 @@ +.recent-item-table{ + overflow-y: scroll; +} +.recent-items-container{ + overflow: auto; + display: flex; + flex-grow: 1; +} \ No newline at end of file diff --git a/web/src/app/dashboard/recent-items/recent-items.component.spec.ts b/web/src/app/dashboard/recent-items/recent-items.component.spec.ts new file mode 100644 index 0000000..a4e8c32 --- /dev/null +++ b/web/src/app/dashboard/recent-items/recent-items.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RecentItemsComponent } from './recent-items.component'; + +describe('RecentItemsComponent', () => { + let component: RecentItemsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RecentItemsComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(RecentItemsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/dashboard/recent-items/recent-items.component.ts b/web/src/app/dashboard/recent-items/recent-items.component.ts new file mode 100644 index 0000000..256b097 --- /dev/null +++ b/web/src/app/dashboard/recent-items/recent-items.component.ts @@ -0,0 +1,68 @@ +import { Component } from '@angular/core'; +import {MatTableModule} from '@angular/material/table'; +import { ConfigService } from '../../services/config-service'; +import { HttpClient } from '@angular/common/http'; +import {DatePipe} from '@angular/common'; +import { HostBinding } from '@angular/core'; +import { SafeStyle } from '@angular/platform-browser'; +import { DomSanitizer } from '@angular/platform-browser'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-recent-items', + standalone: true, + imports: [MatTableModule,DatePipe], + templateUrl: './recent-items.component.html', + styleUrl: './recent-items.component.scss' +}) +export class RecentItemsComponent { + + constructor( + private configSvc: ConfigService, + private http: HttpClient, + public sanitizer: DomSanitizer, + private router: Router, + ){ + this.getRecentItems(); + } + + displayedColumns: string[] = ['title', 'date']; + dataSource: Array = []; + + getRecentItems(){ + if(this.configSvc.offline){ + //return default item + var timeSpan = 0 + var retVal = []; + var now =new Date(); + for(var i = 0; i < 8; i++){ + var rand = Math.floor(Math.random() * 20000); + timeSpan += rand; + retVal.push({"id": i, "title": "Title " + i, "date": new Date(now.getTime() - timeSpan)}) + } + this.dataSource = retVal; + + + } else { + this.http.get(this.configSvc.apiUrl + 'user/getRecentItems').subscribe({ + next: (v) => {}, + error: (e) => {}, + complete: () => {}, + }) + } + + } + + select(id: number ){ + this.router.navigate(['main/investigation/' + id]) + } + + @HostBinding('style') + get myStyle(): SafeStyle { + return this.sanitizer.bypassSecurityTrustStyle('display: flex; flex-grow: 1, flex-direction: column'); + } + + + + +} diff --git a/web/src/app/diagram-details/diagram-details.component.html b/web/src/app/diagram-details/diagram-details.component.html new file mode 100644 index 0000000..b37c192 --- /dev/null +++ b/web/src/app/diagram-details/diagram-details.component.html @@ -0,0 +1 @@ +

diagram-details works!

diff --git a/web/src/app/diagram-details/diagram-details.component.scss b/web/src/app/diagram-details/diagram-details.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/diagram-details/diagram-details.component.spec.ts b/web/src/app/diagram-details/diagram-details.component.spec.ts new file mode 100644 index 0000000..d171ffc --- /dev/null +++ b/web/src/app/diagram-details/diagram-details.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DiagramDetailsComponent } from './diagram-details.component'; + +describe('DiagramDetailsComponent', () => { + let component: DiagramDetailsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DiagramDetailsComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DiagramDetailsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/diagram-details/diagram-details.component.ts b/web/src/app/diagram-details/diagram-details.component.ts new file mode 100644 index 0000000..8337258 --- /dev/null +++ b/web/src/app/diagram-details/diagram-details.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-diagram-details', + standalone: true, + imports: [], + templateUrl: './diagram-details.component.html', + styleUrl: './diagram-details.component.scss' +}) +export class DiagramDetailsComponent { + +} diff --git a/web/src/app/investigation/investigation-main/investigation-main.component.html b/web/src/app/investigation/investigation-main/investigation-main.component.html new file mode 100644 index 0000000..fffdab5 --- /dev/null +++ b/web/src/app/investigation/investigation-main/investigation-main.component.html @@ -0,0 +1 @@ +

investigation-main works!

diff --git a/web/src/app/investigation/investigation-main/investigation-main.component.scss b/web/src/app/investigation/investigation-main/investigation-main.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/investigation/investigation-main/investigation-main.component.spec.ts b/web/src/app/investigation/investigation-main/investigation-main.component.spec.ts new file mode 100644 index 0000000..0531c9e --- /dev/null +++ b/web/src/app/investigation/investigation-main/investigation-main.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { InvestigationMainComponent } from './investigation-main.component'; + +describe('InvestigationMainComponent', () => { + let component: InvestigationMainComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [InvestigationMainComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(InvestigationMainComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/investigation/investigation-main/investigation-main.component.ts b/web/src/app/investigation/investigation-main/investigation-main.component.ts new file mode 100644 index 0000000..62a48e4 --- /dev/null +++ b/web/src/app/investigation/investigation-main/investigation-main.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-investigation-main', + standalone: true, + imports: [], + templateUrl: './investigation-main.component.html', + styleUrl: './investigation-main.component.scss' +}) +export class InvestigationMainComponent { + +} diff --git a/web/src/app/layout-login/layout-login.component.html b/web/src/app/layout-login/layout-login.component.html new file mode 100644 index 0000000..7bb0591 --- /dev/null +++ b/web/src/app/layout-login/layout-login.component.html @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/web/src/app/layout-login/layout-login.component.scss b/web/src/app/layout-login/layout-login.component.scss new file mode 100644 index 0000000..2e9dac6 --- /dev/null +++ b/web/src/app/layout-login/layout-login.component.scss @@ -0,0 +1,80 @@ +.layout-login-container{ + height: 100%; + font-family: "Roboto", sans-serif; + margin: 0; + display: flex; + flex-direction: column; + flex-grow: 1; + width: 100%; + overflow-y: hidden; +} + +.layout-login-header{ + padding: 12px; + width: calc(100% - 24px); + font-size: 1.75em; + font-weight: 700; + display: flex; + flex-direction: row; + cursor: pointer; + +} + +.layout-login-body{ + display: flex; + flex-direction: column; + flex-grow: 1; + height: 100%; + +} + +.layout-login-footer{ + display: flex; + height: 3em; + flex-direction: row; + background-color: var(--secondary-color); +} + +// Header +.header-title{ + margin-right: -6px; +} + +.header-right-items{ + display:flex; + margin-left: auto; + // border-left: 1px solid white; + align-items: center; +} + +.header-icons{ + font-size: 26px; +} +.header-icons:hover{ + color:white; + cursor: pointer; +} +.header-settings-icon{ + width: 24px !important; + height: 24px !important; + padding: 0px !important; + display: inline-flex !important; + align-items: center; + justify-content: center; + + & > *[role=img] { + width: 20px; + height: 20px; + font-size: 20px; + + svg { + width: 20px; + height: 20px; + } + } + + .mat-mdc-button-touch-target { + width: 24px !important; + height: 24px !important; + } +} diff --git a/web/src/app/layout-login/layout-login.component.spec.ts b/web/src/app/layout-login/layout-login.component.spec.ts new file mode 100644 index 0000000..9d27533 --- /dev/null +++ b/web/src/app/layout-login/layout-login.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LayoutLoginComponent } from './layout-login.component'; + +describe('LayoutLoginComponent', () => { + let component: LayoutLoginComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LayoutLoginComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LayoutLoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/layout-login/layout-login.component.ts b/web/src/app/layout-login/layout-login.component.ts new file mode 100644 index 0000000..a34544f --- /dev/null +++ b/web/src/app/layout-login/layout-login.component.ts @@ -0,0 +1,36 @@ +import { Component } from '@angular/core'; +import { Router, RouterOutlet } from '@angular/router'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import {MatMenuModule} from '@angular/material/menu'; +import {MatIconModule} from '@angular/material/icon'; +import {MatButtonModule} from '@angular/material/button'; +import { ConfigService } from '../services/config-service'; +import { HostBinding } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; +import { SafeStyle } from '@angular/platform-browser'; + +@Component({ + selector: 'app-layout-login', + standalone: true, + imports: [RouterOutlet,MatIconModule,MatSlideToggleModule,MatMenuModule,MatButtonModule], + templateUrl: './layout-login.component.html', + styleUrl: './layout-login.component.scss' +}) +export class LayoutLoginComponent { + + constructor( + public configSvc: ConfigService, + private router: Router, + public sanitizer: DomSanitizer, + ){} + + + ngOnint(){ + console.log("Run any initial api checks here") + } + + @HostBinding('style') + get myStyle(): SafeStyle { + return this.sanitizer.bypassSecurityTrustStyle('height: 100%, width: 100%'); + } +} diff --git a/web/src/app/layout/layout.component.html b/web/src/app/layout/layout.component.html new file mode 100644 index 0000000..e9c5698 --- /dev/null +++ b/web/src/app/layout/layout.component.html @@ -0,0 +1,64 @@ + +
+ +
+
+ Resdeeds +

+ logo image needed +

+
+
+ +
+ {{userName}} +
+ + + + + + + + + +
+ +
+ + + +
+ +
+ + +
+ My Footer +
+
+
+
\ No newline at end of file diff --git a/web/src/app/layout/layout.component.scss b/web/src/app/layout/layout.component.scss new file mode 100644 index 0000000..27dd915 --- /dev/null +++ b/web/src/app/layout/layout.component.scss @@ -0,0 +1,83 @@ +.layout-container{ + height: 100%; + font-family: "Roboto", sans-serif; + margin: 0; + display: flex; + flex-direction: column; + flex-grow: 1; + width: 100%; + overflow-y: hidden; +} + +.layout-header{ + padding: 12px; + width: calc(100% - 24px); + font-size: 1.75em; + font-weight: 700; + display: flex; + flex-direction: row; + cursor: pointer; + +} + +.layout-body{ + display: flex; + flex-direction: column; + flex-grow: 1; + +} + +.layout-footer{ + display: flex; + height: 3em; + flex-direction: row; + background-color: var(--secondary-color); +} + +// Header +.header-title{ + margin-right: -6px; +} + +.header-right-items{ + display:flex; + margin-left: auto; + // border-left: 1px solid white; + align-items: center; +} + +.header-icons{ + font-size: 26px; +} +.header-icons:hover{ + color:white; + cursor: pointer; +} +.header-settings-icon{ + width: 24px !important; + height: 24px !important; + padding: 0px !important; + display: inline-flex !important; + align-items: center; + justify-content: center; + + & > *[role=img] { + width: 20px; + height: 20px; + font-size: 20px; + + svg { + width: 20px; + height: 20px; + } + } + + .mat-mdc-button-touch-target { + width: 24px !important; + height: 24px !important; + } +} +.header-username-container{ + font-size: 50%; + margin-right: 2em; +} diff --git a/web/src/app/layout/layout.component.spec.ts b/web/src/app/layout/layout.component.spec.ts new file mode 100644 index 0000000..2c3d7ac --- /dev/null +++ b/web/src/app/layout/layout.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import {MatIconModule} from '@angular/material/icon'; +import { LayoutComponent } from './layout.component'; + +describe('LayoutComponent', () => { + let component: LayoutComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LayoutComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LayoutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/layout/layout.component.ts b/web/src/app/layout/layout.component.ts new file mode 100644 index 0000000..cf9214d --- /dev/null +++ b/web/src/app/layout/layout.component.ts @@ -0,0 +1,48 @@ +import { Component } from '@angular/core'; +import { Router, RouterOutlet } from '@angular/router'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import {MatMenuModule} from '@angular/material/menu'; +import {MatIconModule} from '@angular/material/icon'; +import {MatButtonModule} from '@angular/material/button'; +import { userToken } from '../../models/userTokenModel'; +import { LoginService } from '../services/login.service'; + +@Component({ + selector: 'app-layout', + standalone: true, + imports: [RouterOutlet,MatIconModule,MatSlideToggleModule,MatMenuModule,MatButtonModule], + templateUrl: './layout.component.html', + styleUrl: './layout.component.scss' +}) +export class LayoutComponent { + + userName: string = "User Not Set" + + + constructor( + private router: Router, + private loginSvc: LoginService, + ) {} + public test(){ + console.log("TEST") + } + + public getUserName(){ + var userTokenString = localStorage.getItem("userToken"); + if(userTokenString == null){ + console.log("User has navigated to the main layout without being logged in. Please check AuthGuards") + return "User Token Error"; + } + return JSON.parse(userTokenString).username; + } + public navigate(url: string){ + //do any work needed before swapping urls (checking for save, leave confirmation, etc) + console.log(url) + this.router.navigate(['/main/' + url]); + + } + public logout(){ + this.loginSvc.logout(); + } + +} diff --git a/web/src/app/login/login.component.html b/web/src/app/login/login.component.html new file mode 100644 index 0000000..78b946d --- /dev/null +++ b/web/src/app/login/login.component.html @@ -0,0 +1,48 @@ + + \ No newline at end of file diff --git a/web/src/app/login/login.component.scss b/web/src/app/login/login.component.scss new file mode 100644 index 0000000..f7e5919 --- /dev/null +++ b/web/src/app/login/login.component.scss @@ -0,0 +1,18 @@ +.login-card{ + width: 20em; + // height: 30em; + flex-grow: 0 !important; +} + +.login-form-field{ + width: 100%; +} + +.login-instructions{ + margin-bottom: 1em; + font-size: 85%; +} +.login-buttons{ + display: flex; + flex-direction: row-reverse; +} \ No newline at end of file diff --git a/web/src/app/login/login.component.spec.ts b/web/src/app/login/login.component.spec.ts new file mode 100644 index 0000000..18f3685 --- /dev/null +++ b/web/src/app/login/login.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginComponent } from './login.component'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LoginComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/login/login.component.ts b/web/src/app/login/login.component.ts new file mode 100644 index 0000000..770f2b5 --- /dev/null +++ b/web/src/app/login/login.component.ts @@ -0,0 +1,105 @@ + +import {ChangeDetectionStrategy, Component, signal} from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { HostBinding } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; +import { SafeStyle } from '@angular/platform-browser'; +import { ConfigService } from '../services/config-service'; +import {MatButtonModule} from '@angular/material/button'; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatIconModule} from '@angular/material/icon'; +import {MatInputModule} from '@angular/material/input'; +import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms'; +import {ReactiveFormsModule} from '@angular/forms'; +import { Router, RouterOutlet } from '@angular/router'; +import { LoginService } from '../services/login.service'; +import { stringify } from 'querystring'; +import { BehaviorSubject, Observable } from 'rxjs'; + +@Component({ + selector: 'app-login', + standalone: true, + imports: [CommonModule,MatFormFieldModule, MatInputModule, MatButtonModule, MatIconModule,ReactiveFormsModule, RouterOutlet], + templateUrl: './login.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + styleUrl: './login.component.scss' +}) +export class LoginComponent { + + hide = signal(true); + loginFormGroup!: FormGroup; + errorMessage: string = ""; + showError: BehaviorSubject = new BehaviorSubject(false); + + + clickEvent(event: MouseEvent) { + this.hide.set(!this.hide()); + event.stopPropagation(); + } + + constructor( + public configSvc: ConfigService, + public sanitizer: DomSanitizer, + private router: Router, + public loginSvc: LoginService, + ){ + if(this.loginSvc.isLoggedIn()){ + + this.router.navigate(['/' + "main"]); + } + this.loginFormGroup = new FormGroup({ + userName: new FormControl('JohnDoe',[Validators.required]), + password: new FormControl('password',[Validators.required]), + }); + console.log(this.loginFormGroup) + } + + @HostBinding('style') + get myStyle(): SafeStyle { + return this.sanitizer.bypassSecurityTrustStyle('display: flex; flex-grow: 1'); + } + + + validLogin(){ + if(this.loginFormGroup.valid){ + console.log("valid") + return true; + } else { + console.log("invalid") + return false; + } + } + + async login(){ + if(!this.validLogin()){ + this.showErrorTimer(this.configSvc.errorTime); + return; + } + console.log("start login") + var username = this.loginFormGroup.get("userName")?.value + var password = this.loginFormGroup.get("password")?.value + var result = await this.loginSvc.login(username,password); + if(!result.success){ + console.log("login call failed") + this.errorMessage = result.errorMessage; + this.showErrorTimer(this.configSvc.errorTime); + return; + } + + + this.router.navigate(['/' + "main"]); + + } + register(){ + console.log("register") + } + async showErrorTimer(ms: number){ + this.showError.next(true); + return setTimeout(() => { + console.log("error timer hit") + this.showError.next(false) + this.errorMessage = ""; + console.log(this.showError) + },ms); + } +} diff --git a/web/src/app/main-diagram/main-diagram.component.html b/web/src/app/main-diagram/main-diagram.component.html new file mode 100644 index 0000000..db1a63a --- /dev/null +++ b/web/src/app/main-diagram/main-diagram.component.html @@ -0,0 +1 @@ +

main-diagram works!

diff --git a/web/src/app/main-diagram/main-diagram.component.scss b/web/src/app/main-diagram/main-diagram.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/main-diagram/main-diagram.component.spec.ts b/web/src/app/main-diagram/main-diagram.component.spec.ts new file mode 100644 index 0000000..8834581 --- /dev/null +++ b/web/src/app/main-diagram/main-diagram.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MainDiagramComponent } from './main-diagram.component'; + +describe('MainDiagramComponent', () => { + let component: MainDiagramComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MainDiagramComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(MainDiagramComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/main-diagram/main-diagram.component.ts b/web/src/app/main-diagram/main-diagram.component.ts new file mode 100644 index 0000000..7c23ab0 --- /dev/null +++ b/web/src/app/main-diagram/main-diagram.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-main-diagram', + standalone: true, + imports: [], + templateUrl: './main-diagram.component.html', + styleUrl: './main-diagram.component.scss' +}) +export class MainDiagramComponent { + +} diff --git a/web/src/app/not-found/not-found.component.html b/web/src/app/not-found/not-found.component.html new file mode 100644 index 0000000..8071020 --- /dev/null +++ b/web/src/app/not-found/not-found.component.html @@ -0,0 +1 @@ +

not-found works!

diff --git a/web/src/app/not-found/not-found.component.scss b/web/src/app/not-found/not-found.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/not-found/not-found.component.spec.ts b/web/src/app/not-found/not-found.component.spec.ts new file mode 100644 index 0000000..5b65d9e --- /dev/null +++ b/web/src/app/not-found/not-found.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NotFoundComponent } from './not-found.component'; + +describe('NotFoundComponent', () => { + let component: NotFoundComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [NotFoundComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(NotFoundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/not-found/not-found.component.ts b/web/src/app/not-found/not-found.component.ts new file mode 100644 index 0000000..01bdc09 --- /dev/null +++ b/web/src/app/not-found/not-found.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-not-found', + standalone: true, + imports: [], + templateUrl: './not-found.component.html', + styleUrl: './not-found.component.scss' +}) +export class NotFoundComponent { + +} diff --git a/web/src/app/password-restore/password-restore.component.html b/web/src/app/password-restore/password-restore.component.html new file mode 100644 index 0000000..edf4665 --- /dev/null +++ b/web/src/app/password-restore/password-restore.component.html @@ -0,0 +1 @@ +

password-restore works!

diff --git a/web/src/app/password-restore/password-restore.component.scss b/web/src/app/password-restore/password-restore.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/password-restore/password-restore.component.spec.ts b/web/src/app/password-restore/password-restore.component.spec.ts new file mode 100644 index 0000000..08389b3 --- /dev/null +++ b/web/src/app/password-restore/password-restore.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PasswordRestoreComponent } from './password-restore.component'; + +describe('PasswordRestoreComponent', () => { + let component: PasswordRestoreComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PasswordRestoreComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(PasswordRestoreComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/password-restore/password-restore.component.ts b/web/src/app/password-restore/password-restore.component.ts new file mode 100644 index 0000000..2e055b6 --- /dev/null +++ b/web/src/app/password-restore/password-restore.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-password-restore', + standalone: true, + imports: [], + templateUrl: './password-restore.component.html', + styleUrl: './password-restore.component.scss' +}) +export class PasswordRestoreComponent { + +} diff --git a/web/src/app/register/register.component.html b/web/src/app/register/register.component.html new file mode 100644 index 0000000..6b0ba2e --- /dev/null +++ b/web/src/app/register/register.component.html @@ -0,0 +1 @@ +

register works!

diff --git a/web/src/app/register/register.component.scss b/web/src/app/register/register.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/register/register.component.spec.ts b/web/src/app/register/register.component.spec.ts new file mode 100644 index 0000000..757b895 --- /dev/null +++ b/web/src/app/register/register.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegisterComponent } from './register.component'; + +describe('RegisterComponent', () => { + let component: RegisterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RegisterComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(RegisterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/register/register.component.ts b/web/src/app/register/register.component.ts new file mode 100644 index 0000000..141d7e8 --- /dev/null +++ b/web/src/app/register/register.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-register', + standalone: true, + imports: [], + templateUrl: './register.component.html', + styleUrl: './register.component.scss' +}) +export class RegisterComponent { + +} diff --git a/web/src/app/services/auth-service.ts b/web/src/app/services/auth-service.ts new file mode 100644 index 0000000..453000d --- /dev/null +++ b/web/src/app/services/auth-service.ts @@ -0,0 +1,52 @@ +import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ConfigService } from './config-service'; + + +@Injectable() +export class AuthService { + + + constructor( + private http: HttpClient, + private router: Router, + private configSvc: ConfigService, + private route: ActivatedRoute, + ) { + } + + userId(): number { + return parseInt(localStorage.getItem('userId') ?? "0", 10); + } + + getUsers(){ + return this.http.get(this.configSvc.apiUrl + 'user/getUsers'); + } + getUserDetail(id :number){ + return this.http.get(this.configSvc.apiUrl + 'user/getUserDetail/' + id); + } + getRoles(){ + return this.http.get(this.configSvc.apiUrl + 'user/getRoles/'); + } + getRoleDetail(id : number){ + return this.http.get(this.configSvc.apiUrl + 'user/getRoleDetail/' + id); + } + + createRole(role: any){ + return this.http.post(this.configSvc.apiUrl + 'user/createRole/', { roleName: role.roleName }) + } + updateUser(user: any){ + return this.http.post(this.configSvc.apiUrl + 'user/updateUser', user) + } + updateRole(role: any){ + return this.http.post(this.configSvc.apiUrl + 'user/updateRole', role) + } + deleteRole(roleid: any){ + return this.http.post(this.configSvc.apiUrl + 'user/deleteRole', roleid) + } + + + + +} diff --git a/web/src/app/services/authGuards/auth-guard-admin.ts b/web/src/app/services/authGuards/auth-guard-admin.ts new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/services/authGuards/auth-guard-general.ts b/web/src/app/services/authGuards/auth-guard-general.ts new file mode 100644 index 0000000..43543aa --- /dev/null +++ b/web/src/app/services/authGuards/auth-guard-general.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@angular/core'; +import { CanActivate, CanActivateChild, CanDeactivate, CanLoad, Router } from '@angular/router'; +import { LoginService } from '../login.service'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthGuardGeneral implements CanActivate { + constructor(private loginSvc: LoginService, private router: Router) {} + + canActivate(): boolean { + return this.checkAuth(); + } + + canActivateChild(): boolean { + return this.checkAuth(); + } + + canLoad(): boolean { + return this.checkAuth(); + } + + private checkAuth(): boolean { + if (this.loginSvc.isLoggedIn()) { + return true; + } else { + // Redirect to the login page if the user is not authenticated + localStorage.removeItem("userToken") + this.router.navigate(['/login/signin']); + return false; + } + } + +} \ No newline at end of file diff --git a/web/src/app/services/config-service.ts b/web/src/app/services/config-service.ts new file mode 100644 index 0000000..b12aba7 --- /dev/null +++ b/web/src/app/services/config-service.ts @@ -0,0 +1,55 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable, APP_INITIALIZER } from '@angular/core'; +import { environemnt } from '../../environments/environment'; + + + +@Injectable() +export class ConfigService { + + apiUrl!: string; + configUrl!: string; + offline!: boolean;; + errorTime!: number; + tokenTimeOutDefault !: number; + + + + constructor(private http: HttpClient) { + console.log("config conrtuctor") + this.loadConfig(); + + } + + /** + * + */ + loadConfig() { + this.configUrl = '/assets/config.json'; + + this.apiUrl = + environemnt.api.protocol + "//" + + environemnt.api.url + ":" + + environemnt.api.port + + this.offline = environemnt.offlineTesting; + this.errorTime = environemnt.errorMessageTime; + this.tokenTimeOutDefault = environemnt.tokenTimeOutDefault; + // return this.http.get(this.configUrl) + // .toPromise() + // .then((data: any) => { + // console.log(data) + // let apiPort = data.api.port != "" ? ":" + data.api.port : ""; + // let apiProtocol = data.api.protocol + "://"; + // if (localStorage.getItem("apiUrl") != null) { + // this.apiUrl = localStorage.getItem("apiUrl") + "/" + data.api.apiIdentifier + "/"; + // } else { + // console.log(data) + // this.apiUrl = apiProtocol + data.api.url + apiPort + "/" + data.api.apiIdentifier + "/"; + // } + // console.log(this.apiUrl) + + // }).catch(error => console.log('Failed to load config file: ' + (error).message)); + // } + } +} diff --git a/web/src/app/services/dashboard-service.ts b/web/src/app/services/dashboard-service.ts new file mode 100644 index 0000000..e02217a --- /dev/null +++ b/web/src/app/services/dashboard-service.ts @@ -0,0 +1,19 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable, APP_INITIALIZER } from '@angular/core'; +import { ConfigService } from './config-service'; + + + +@Injectable() +export class DashService { + + + + constructor(private config: ConfigService) { + } + + loadDashBoard(){ + console.log("Using " + this.config.apiUrl + "call to load dashboard") + } + +} diff --git a/web/src/app/services/login.service.ts b/web/src/app/services/login.service.ts new file mode 100644 index 0000000..b8b9936 --- /dev/null +++ b/web/src/app/services/login.service.ts @@ -0,0 +1,89 @@ +import { afterNextRender, Injectable } from '@angular/core'; +import { ConfigService } from './config-service'; +import { HttpClient } from '@angular/common/http'; +import { Router, RouterOutlet } from '@angular/router'; +import { httpReponseContext } from '../../models/httpReponseContext'; +import { userToken } from '../../models/userTokenModel'; + +@Injectable({ + providedIn: 'root' +}) +export class LoginService { + + + constructor( + private configSvc: ConfigService, + private router: Router, + private http: HttpClient, + ) { + } + + isLoggedIn(){ + var userTokenString = localStorage.getItem("userToken"); + if(userTokenString == null) return false; + var userToken = JSON.parse(userTokenString); + if(!this.tokenValid(userToken)){ + return false; + } + return true; + } + + tokenValid(token: userToken){ + //TODO: check actual JWT token for validity, currently testing token timeout for offline testing purposes + //if(token.token.isValid){return true;} + var now = new Date().getTime(); + var exp = new Date(token.expTime).getTime(); + console.log(exp) + console.log(now) + if(exp < now){ + console.log("Token timeout hit") + console.log(token) + console.log(new Date()) + return false; + } else { + console.log(token) + console.log(new Date()) + return true; + } + } + async login(username : string, password: string){ + var retVal = new httpReponseContext(false,{},"dafault login error, check code for issue"); + if(this.configSvc.offline){ + console.log(password) + if(username == "JohnDoe" && password == "password"){ + var retVal = new httpReponseContext(true,{},"test"); + retVal.success = true + localStorage.setItem("userToken",JSON.stringify(this.genUserToken(username,{}))); + localStorage.setItem("userName",username); + return retVal; + } else { + return new httpReponseContext(false,{},"Invalid username / password combination. In Offline mode please use JohnDoe and password") + } + } else { + this.http.get(this.configSvc.apiUrl + 'user/getUsers').subscribe({ + next: (v) => console.log(v), + error: (e) => console.error(e), + complete: () => console.info('complete') + }) + } + return retVal; + } + + genUserToken(username : string, token: Object){ + var retval = new userToken(); + var date = new Date(); + retval.expTime = new Date(date.getTime() + this.configSvc.tokenTimeOutDefault); + retval.username = username; + var token = token; + return retval; + } + clearUserToken(){ + localStorage.removeItem("userToken") + localStorage.removeItem("userName") + } + + logout(){ + this.clearUserToken(); + this.router.navigate(['/login/signin']); + } +} diff --git a/web/src/app/services/storage-service.ts b/web/src/app/services/storage-service.ts new file mode 100644 index 0000000..7205181 --- /dev/null +++ b/web/src/app/services/storage-service.ts @@ -0,0 +1,18 @@ +import { inject, Injectable } from '@angular/core'; +import { LOCAL_STORAGE } from '../tokens'; +@Injectable({ + providedIn: 'root', +}) +export class StorageService { + private storage = inject(LOCAL_STORAGE); + setItem(key: string, value: any): void { + this.storage.setItem(key, JSON.stringify(value)); + } + getItem(key: string): T | null { + const storedValue = this.storage.getItem(key); + return storedValue ? (JSON.parse(storedValue) as T) : null; + } + removeItem(key: string): void { + this.storage.removeItem(key); + } +} \ No newline at end of file diff --git a/web/src/app/tokens/index.ts b/web/src/app/tokens/index.ts new file mode 100644 index 0000000..d4fce5a --- /dev/null +++ b/web/src/app/tokens/index.ts @@ -0,0 +1,3 @@ + +import { InjectionToken } from '@angular/core'; +export const LOCAL_STORAGE = new InjectionToken('Local Storage'); \ No newline at end of file diff --git a/web/src/app/user-management/user-management.component.html b/web/src/app/user-management/user-management.component.html new file mode 100644 index 0000000..1ca51cf --- /dev/null +++ b/web/src/app/user-management/user-management.component.html @@ -0,0 +1 @@ +

user-management works!

diff --git a/web/src/app/user-management/user-management.component.scss b/web/src/app/user-management/user-management.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/web/src/app/user-management/user-management.component.spec.ts b/web/src/app/user-management/user-management.component.spec.ts new file mode 100644 index 0000000..5a81f8c --- /dev/null +++ b/web/src/app/user-management/user-management.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UserManagementComponent } from './user-management.component'; + +describe('UserManagementComponent', () => { + let component: UserManagementComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [UserManagementComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(UserManagementComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/web/src/app/user-management/user-management.component.ts b/web/src/app/user-management/user-management.component.ts new file mode 100644 index 0000000..c503d96 --- /dev/null +++ b/web/src/app/user-management/user-management.component.ts @@ -0,0 +1,12 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-user-management', + standalone: true, + imports: [], + templateUrl: './user-management.component.html', + styleUrl: './user-management.component.scss' +}) +export class UserManagementComponent { + +} diff --git a/web/src/assets/config.json b/web/src/assets/config.json new file mode 100644 index 0000000..b8c4f32 --- /dev/null +++ b/web/src/assets/config.json @@ -0,0 +1,24 @@ +{ + "appCode": "ResDeeds", + "app":{ + "protocol": "http", + "url": "localhost", + "port":"4200" + }, + "api":{ + "protocol":"http", + "url": "localhost", + "port":"5000", + "apiIdentifier": "api", + "documentsIdentifier": "Documents" + }, + "versionApi":{ + "protocol":"http", + "host": "api", + "port":"5000", + "apiIdentifier": "api", + "documentsIdentifier": "Documents" + }, + "helpContactEmail": "dylan.johnson@inl.gov", + "defaultPasswordSubject": "Your password has been reset" +} diff --git a/web/src/environments/environment.prod.ts b/web/src/environments/environment.prod.ts new file mode 100644 index 0000000..0db265c --- /dev/null +++ b/web/src/environments/environment.prod.ts @@ -0,0 +1,27 @@ +export const environemnt = { + "appCode": "ResDeeds", + "offlineTesting": false, + "errorMessageTime": 2000, + "tokenTimeOutDefault": 5 * 60000, //Value * milliseconds = amount of minutes to timeout + "app":{ + "protocol": "http", + "url": "localhost", + "port":"4200" + }, + "api":{ + "protocol":"http", + "url": "localhost", + "port":"5000", + "apiIdentifier": "api", + "documentsIdentifier": "Documents" + }, + "versionApi":{ + "protocol":"http", + "host": "api", + "port":"5000", + "apiIdentifier": "api", + "documentsIdentifier": "Documents" + }, + "helpContactEmail": "dylan.johnson@inl.gov", + "defaultPasswordSubject": "Your password has been reset" +} diff --git a/web/src/environments/environment.ts b/web/src/environments/environment.ts new file mode 100644 index 0000000..dc99224 --- /dev/null +++ b/web/src/environments/environment.ts @@ -0,0 +1,27 @@ +export const environemnt = { + "appCode": "ResDeeds", + "offlineTesting": true, + "errorMessageTime": 2000, + "tokenTimeOutDefault": 5 * 60000, //Value * milliseconds = amount of minutes to timeout + "app":{ + "protocol": "http", + "url": "localhost", + "port":"4200" + }, + "api":{ + "protocol":"http", + "url": "localhost", + "port":"5000", + "apiIdentifier": "api", + "documentsIdentifier": "Documents" + }, + "versionApi":{ + "protocol":"http", + "host": "api", + "port":"5000", + "apiIdentifier": "api", + "documentsIdentifier": "Documents" + }, + "helpContactEmail": "dylan.johnson@inl.gov", + "defaultPasswordSubject": "Your password has been reset" +} diff --git a/web/src/index.html b/web/src/index.html index 00e4e9c..e7c11ec 100644 --- a/web/src/index.html +++ b/web/src/index.html @@ -1,13 +1,14 @@ - - + + + Resdeeds - + diff --git a/web/src/models/httpReponseContext.ts b/web/src/models/httpReponseContext.ts new file mode 100644 index 0000000..1c4444c --- /dev/null +++ b/web/src/models/httpReponseContext.ts @@ -0,0 +1,11 @@ +export class httpReponseContext { + success: boolean = false; + messageBody: any = null; + errorMessage: string = "Error, please define error message if needed" + + constructor(success: boolean = false, messageBody: any = {}, errorMessage: any = "Error Not Defined"){ + this.success = success, + this.messageBody = messageBody + this.errorMessage = errorMessage + }; +} \ No newline at end of file diff --git a/web/src/models/userTokenModel.ts b/web/src/models/userTokenModel.ts new file mode 100644 index 0000000..ead5c39 --- /dev/null +++ b/web/src/models/userTokenModel.ts @@ -0,0 +1,8 @@ +export class userToken { + username: string = ""; + token: any = null; + expTime: Date = new Date(); + + constructor(){ + }; +} \ No newline at end of file diff --git a/web/src/styles.scss b/web/src/styles.scss index 90d4ee0..d8bcb98 100644 --- a/web/src/styles.scss +++ b/web/src/styles.scss @@ -1 +1,175 @@ /* You can add global styles to this file, and also import other style files */ + +//================Base Colors=========================== + +:root{ + --primary-color: #507d69; + --secondary-color:#6ead90; + --tertiary-color: #738A80; + --tertiary-color-dark: #4B5043; + --button-main-color: #218263; + --button-secondary-color: var(--white); + --link: #2b7dcf; + --black: #090909; + --white: #f0f0f0; + --light-gray: #e0e0e0; + --card-background: #d0d0d0; + + --card-spacing: .2em; +} + +.color-primary{ + background-color: var(--primary-color); + color: var(--black); +} + +.color-secondary{ + +} + +.color-background{ + background-color: var(--light-gray); +} + + +//================General Layout======================== + +//Flex +.flex{ + display: flex; +} + + +//Width decimal +.w-100{ + width: 100%; +} +.w-75{ + width: 75%; +} +.w-50{ + width: 50%; +} +.w-25{ + width: 25%; +} +.w-10{ + width: 10%; +} +//Width Duodecimal +.w-1{ + width: 8.3%; +} +.w-2{ + width: 16.6%; +} +.w-3{ + width: 25%; +} +.w-4{ + width: 33.2%; +} +.w-5{ + width: 41.6%; +} +.w-6{ + width: 50%; +} +.w-7{ + width: 58.3%; +} +.w-8{ + width: 66.7%; +} +.w-9{ + width: 75%; +} +.w-10{ + width: 83.3%; +} +.w-11{ + width: 91.6%; +} +.w-12{ + width: 100%; +} + +.centered { + margin: auto; +} + +.clickable-text{ + color: var(--link); + cursor: pointer;; +} +.clickable-text:hover{ + text-decoration: underline; +} + +.btn-main{ + background-color: var(--button-main-color) !important; + color: var(--white) !important; +} +.btn-secondary{ + background-color: var(--button-secondary-color) !important; + color: var(--black) !important; +} + +.card-row{ + display: flex; + flex-direction: row; + margin-bottom: 1em; + flex-basis: 95%; + max-height: 33%; +} + +.card{ + display: flex; + flex-grow: 1; + flex-direction: column; + background-color: var(--card-background); + font-size: 150%; + border-radius: 20px; +} + +.card-header{ + border-radius: 20px 20px 0 0 ; + width:100; + padding: .45em .75em; + background-color: var(--tertiary-color); + color: car(--tertiary-color-dark); +} +.card-body{ + padding: 1em; + display: flex; + padding: 1em; + flex-grow: 1; + height: calc(100% - 4em); +} +.card-item { + padding: 1em; + font-size: 70%;; +} + + +.card-row > .card{ + margin: 0 var(--card-spacing); +} +.card-row > .card:first-child{ + margin-left: 0em; +} +.card-row > .card:last-child{ + margin-right: 0em; +} + +// .card-row .card:first{ +// margin-left: 0; +// } + +.clickable-mat-row{ + cursor: pointer; +} +.clickable-mat-row:hover{ + background-color: var(--light-gray); + cursor: pointer; +} \ No newline at end of file diff --git a/web/src/tsconfig.app.json b/web/src/tsconfig.app.json new file mode 100644 index 0000000..0607a80 --- /dev/null +++ b/web/src/tsconfig.app.json @@ -0,0 +1,19 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/app", + "baseUrl": "./", + "types": ["node"], + "experimentalDecorators": true, + "allowJs": true, + "target": "ES2022", + }, + "files": [ + "main.ts", + "polyfills.ts" + ], + "include": [ + "src/**/*.d.ts" + ] + } + \ No newline at end of file diff --git a/web/src/tsconfig.spec.json b/web/src/tsconfig.spec.json new file mode 100644 index 0000000..9fe4fdb --- /dev/null +++ b/web/src/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/spec", + "baseUrl": "./", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "test.ts", + "polyfills.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] + } + \ No newline at end of file