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 @@
+
+
+
+
+
+
+
+
+
+ This is my body text
+
+
+
+
+
+
+
+
+
+ This is my body text
+
+
+
+
+
+
+
+
+
+ This is my body text
+
+
+
+
+
+
+
+ This is my body text
+
+
+
+
+
+
+
+ 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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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 @@
+
+
+
+
+
+
+
+ Please enter your login information to accesse the Resdeeds application.
+
+
+ If you do not have an account, please click here to register an account.
+
+
+
+
+ User Name
+
+
+
+ Enter your password
+
+
+
+
+ {{errorMessage}}
+
+
+
+
+
+
+
+
\ 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