add energy rate and provider service with enum

This commit is contained in:
DavidGudEnough 2025-01-15 19:56:11 -05:00
parent 0fb92aab85
commit 01d10171e9
Signed by: david.nguyen
GPG Key ID: 0B95DC36355BEB37
24 changed files with 26144 additions and 5018 deletions

21175
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,9 @@
"@angular/platform-browser": "^19.0.0",
"@angular/platform-browser-dynamic": "^19.0.0",
"@angular/router": "^19.0.0",
"@openharbor/data": "^1.0.0-alpha.1",
"@openharbor/ngx-data": "^18.0.0-alpha.9",
"angular-auth-oidc-client": "^19.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"

View File

@ -6,10 +6,10 @@
}
},
"auth": {
"endpoint": "https://login-planb.openharbor.io/realms/plan-b",
"clientId": "",
"scopes": "",
"responseType": "",
"endpoint": "https://login-planb.openharbor.io/realms/constellation-heating",
"clientId": "web-app",
"scopes": "openid profile offline_access",
"responseType": "code",
"secureRoutes": [
"https://localhost:7184/"
]

View File

@ -1,9 +1,72 @@
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import {Observable} from 'rxjs';
import { routes } from './app.routes';
import { provideRouter } from '@angular/router';
import {SettingService} from './services/setting.service';
import {provideHttpClient, withInterceptors} from '@angular/common/http';
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import
{
provideAuth,
authInterceptor,
StsConfigLoader,
OpenIdConfiguration,
StsConfigHttpLoader,
AbstractSecurityStorage,
DefaultLocalStorageService,
withAppInitializerAuthCheck
} from 'angular-auth-oidc-client';
function appInitialized() {
return async ()=> {
return Promise.all([])
}
}
export const appConfig: ApplicationConfig = {
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideAnimationsAsync()]
function authConfigFactory(setting: SettingService) {
const config$ = new Observable<OpenIdConfiguration>(observer => {
setting.initialize()
.then(() => {
const config = {
authority: setting.auth.endpoint,
redirectUrl: window.location.origin,
postLogoutRedirectUri: window.location.origin,
clientId: setting.auth.clientId,
scope: setting.auth.scopes,
responseType: setting.auth.responseType,
silentRenew: true,
useRefreshToken: true,
renewTimeBeforeTokenExpiresInSeconds: 30,
secureRoutes: setting.auth.secureRoutes
};
observer.next(config);
observer.complete();
});
});
return new StsConfigHttpLoader(config$);
}
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(withInterceptors([authInterceptor()])),
provideAnimationsAsync(),
provideAuth(
{
loader: {
provide: StsConfigLoader,
useFactory: authConfigFactory,
deps: [SettingService],
},
},
withAppInitializerAuthCheck()
),
{
provide: AbstractSecurityStorage,
useClass: DefaultLocalStorageService,
},
]
};

View File

@ -1,15 +1,10 @@
import { Routes } from '@angular/router';
import {HomeComponent} from './pages/home/home.component';
import {TestPage} from './pages/test/test.page'
import {autoLoginPartialRoutesGuard} from 'angular-auth-oidc-client';
export const routes: Routes = [
{
path: 'test',
title: 'Test Page',
component: TestPage
},
{
path: '',
title: 'Home Component',
component: HomeComponent
loadChildren: () => import('./layouts/default/default.routes')
.then(child => child.routes),
canMatch: [autoLoginPartialRoutesGuard]
},
];

4
src/app/enum/currency.ts Normal file
View File

@ -0,0 +1,4 @@
export enum Currency {
USD,
CAD,
}

View File

@ -0,0 +1,3 @@
<p>default layout works !</p>
<router-outlet />

View File

@ -0,0 +1,38 @@
main {
padding: 15px;
}
mat-list-item {
width: 100%;
button {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 8px; /* Adjust spacing between label and icon */
width: 100%;
}
}
mat-drawer-container {
height: 100%;
}
.account-container {
padding: { bottom: 0; left: 25px; right: 25px; top: 25px; };
display: flex;
flex-direction: column;
align-items: center;
}
.navigation-container {
max-width: 200px;
}
.organization-button {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 8px; /* Adjust spacing between label and icon */
width: 100%;
}

View File

@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HomeComponent } from './home.component';
import { DefaultLayout } from './default.layout';
describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>;
describe('DefaultLayout', () => {
let component: DefaultLayout;
let fixture: ComponentFixture<DefaultLayout>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [HomeComponent]
imports: [DefaultLayout]
})
.compileComponents();
fixture = TestBed.createComponent(HomeComponent);
fixture = TestBed.createComponent(DefaultLayout);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@ -0,0 +1,14 @@
import { Component } from '@angular/core';
import {RouterOutlet} from '@angular/router';
@Component({
selector: 'app-default-layout',
imports: [
RouterOutlet
],
templateUrl: './default.layout.html',
styleUrl: './default.layout.scss'
})
export class DefaultLayout {
}

View File

@ -0,0 +1,22 @@
import {DefaultLayout} from './default.layout';
import {Routes} from '@angular/router';
export const routes: Routes = [
{
path: '',
component: DefaultLayout,
children: [
{
path: '',
pathMatch: 'full',
redirectTo: 'home',
},
{
path: 'home',
loadComponent: () =>
import('../../pages/home/home.page')
.then(page => page.HomePage),
},
],
},
];

View File

@ -1,11 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
imports: [],
templateUrl: './home.component.html',
styleUrl: './home.component.scss'
})
export class HomeComponent {
}

View File

@ -1,18 +1,18 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TestPage } from './test.page';
import { HomePage } from './home.page';
describe('TestPage', () => {
let component: TestPage;
let fixture: ComponentFixture<TestPage>;
describe('HomePage', () => {
let component: HomePage;
let fixture: ComponentFixture<HomePage>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TestPage]
imports: [HomePage]
})
.compileComponents();
fixture = TestBed.createComponent(TestPage);
fixture = TestBed.createComponent(HomePage);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-home',
imports: [],
templateUrl: './home.page.html',
styleUrl: './home.page.scss'
})
export class HomePage {
}

View File

@ -1 +0,0 @@
<p>test works!</p>

View File

@ -1,11 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-test',
imports: [],
templateUrl: './test.page.html',
styleUrl: './test.page.scss'
})
export class TestPage {
}

View File

@ -0,0 +1,138 @@
import {Router} from '@angular/router';
import {BehaviorSubject, of} from 'rxjs';
import {SettingService} from './setting.service';
import { inject, Injectable } from '@angular/core';
import {HttpDataSourceService} from '@openharbor/ngx-data';
import {IDataSource, IQueryCriteria} from '@openharbor/data';
import {OidcSecurityService} from 'angular-auth-oidc-client';
export interface IEnergyProvider
{
id: number;
name: string;
active: boolean;
disabled_at: string;
created_at: string;
updated_at: string;
}
export interface IAddEnergyProvider
{
name: string;
}
export interface IDisabledEnergyProvider
{
providerId: number;
disabled_at: string;
}
export interface IEnableEnergyProvider
{
providerId: number;
}
export interface IUpdateEnergyProvider
{
providerId: number;
name: string;
}
@Injectable({
providedIn: 'root'
})
export class EnergyProviderService {
private readonly hdss = inject(HttpDataSourceService);
private readonly settingService = inject(SettingService);
private readonly oidcSecurityService = inject(OidcSecurityService);
private readonly router = inject(Router);
private energyProviders: IEnergyProvider[] = [];
dataSource?: IDataSource<IQueryCriteria, IEnergyProvider>
current$ = new BehaviorSubject<IEnergyProvider | null>(null);
energyProviders$ = new BehaviorSubject<IEnergyProvider[]>([]);
constructor() {
this.internalDataSource();
this.oidcSecurityService.isAuthenticated$
.subscribe(result => {
if (result.isAuthenticated) {
this.dataSource?.refresh();
}
});
}
dataSourceBuilder() {
return this.hdss.builder<IQueryCriteria, IEnergyProvider>()
.keyResolver(model => model.id)
.addCommandByUrl<IAddEnergyProvider, void>(
'addEnergyProvider',
this.settingService.getMainCommandUrl('addEnergyProvider'),
ev => {
return of({
name: ev.model.name
})
})
.addCommandByUrl<IDisabledEnergyProvider, void>(
'disableEnergyProvider',
this.settingService.getMainCommandUrl('disableEnergyProvider'),
ev => {
return of({
providerId: ev.model.id,
disabled_at: ev.model.disabled_at
})
}
)
.addCommandByUrl<IEnableEnergyProvider, void>(
'enableEnergyProvider',
this.settingService.getMainCommandUrl('enableEnergyProvider'),
ev => {
return of({
providerId: ev.model.id,
})
}
)
.addCommandByUrl<IUpdateEnergyProvider, void>(
'updateEnergyProvider',
this.settingService.getMainCommandUrl('updateEnergyProvider'),
ev => {
return of({
providerId: ev.model.id,
name: ev.model.name
})
}
);
}
selectEnergyProvider(id: number) {
const energyProvider = this.energyProviders.find(energyProvider => energyProvider.id === id);
if (undefined === energyProvider)
return;
this.current$.next(energyProvider);
}
private internalDataSource() {
if (undefined !== this.dataSource)
return;
this.dataSource = this.dataSourceBuilder()
.createDataSource();
this.dataSource.data$
.subscribe(result => {
if (null === result || undefined === result.data)
return;
let updateSelection = false;
if (this.energyProviders.length === 0) {
updateSelection = true;
}
this.energyProviders = result.data;
this.energyProviders$.next(this.energyProviders);
if (updateSelection && this.energyProviders.length > 0)
this.selectEnergyProvider(this.energyProviders[0].id)
})
}
}

View File

@ -0,0 +1,160 @@
import {Router} from '@angular/router';
import {BehaviorSubject, of} from 'rxjs';
import {Currency} from '../enum/currency';
import {SettingService} from './setting.service';
import { inject, Injectable } from '@angular/core';
import {HttpDataSourceService} from '@openharbor/ngx-data';
import {IDataSource, IQueryCriteria} from '@openharbor/data';
import {OidcSecurityService} from 'angular-auth-oidc-client';
import {
IAddEnergyProvider,
IDisabledEnergyProvider,
IEnableEnergyProvider,
IEnergyProvider, IUpdateEnergyProvider
} from './energy-provider.service';
export interface IEnergyRate
{
id: number;
providerId: number;
name: string;
rate: number;
currency: Currency;
active: boolean;
started_at: string;
disabled_at: string;
created_at: string;
updated_at: string;
}
export interface IAddEnergyRate
{
providerId: number;
name: string;
rate: number;
currency: Currency;
active: boolean;
}
export interface IDisabledEnergyRate
{
rateId: number;
disabled_at: string;
}
export interface IEnableEnergyRate
{
rateId: number;
}
export interface IUpdateEnergyRate
{
rateId: number;
started_at : string;
name: string;
rate: number;
}
@Injectable({
providedIn: 'root'
})
export class EnergyRateService {
private readonly hdss = inject(HttpDataSourceService);
private readonly settingService = inject(SettingService);
private readonly oidcSecurityService = inject(OidcSecurityService);
private readonly router = inject(Router);
private energyRates: IEnergyRate[] = [];
dataSource?: IDataSource<IQueryCriteria, IEnergyRate>;
current$ = new BehaviorSubject<IEnergyRate | null>(null);
energyRates$ = new BehaviorSubject<IEnergyRate[]>([]);
constructor() {
this.internalDataSource();
this.oidcSecurityService.isAuthenticated$
.subscribe(result => {
if (result.isAuthenticated) {
this.dataSource?.refresh();
}
});
}
dataSourceBuilder() {
return this.hdss.builder<IQueryCriteria, IEnergyRate>()
.keyResolver(model => model.id)
.addCommandByUrl<IAddEnergyRate, void>(
'addEnergyRate',
this.settingService.getMainCommandUrl('addEnergyRate'),
ev => {
return of({
providerId: ev.model.providerId,
name: ev.model.name,
rate: ev.model.rate,
currency: ev.model.currency,
active: ev.model.active,
})
})
.addCommandByUrl<IDisabledEnergyRate, void>(
'disableEnergyRate',
this.settingService.getMainCommandUrl('disableEnergyRate'),
ev => {
return of({
rateId: ev.model.id,
disabled_at: ev.model.disabled_at
})
}
)
.addCommandByUrl<IEnableEnergyRate, void>(
'enableEnergyRate',
this.settingService.getMainCommandUrl('enableEnergyRate'),
ev => {
return of({
rateId: ev.model.id,
})
}
)
.addCommandByUrl<IUpdateEnergyRate, void>(
'updateEnergyRate',
this.settingService.getMainCommandUrl('updateEnergyRate'),
ev => {
return of({
rateId: ev.model.id,
started_at: ev.model.started_at,
name: ev.model.name,
rate: ev.model.rate,
})
}
);
}
selectEnergyRate(id: number) {
const energyRate = this.energyRates.find(energyRate => energyRate.id === id);
if (undefined === energyRate)
return;
this.current$.next(energyRate);
}
private internalDataSource() {
if (undefined !== this.dataSource)
return;
this.dataSource = this.dataSourceBuilder()
.createDataSource();
this.dataSource.data$
.subscribe(result => {
if (null === result || undefined === result.data)
return;
let updateSelection = false;
if (this.energyRates.length === 0) {
updateSelection = true;
}
this.energyRates = result.data;
this.energyRates$.next(this.energyRates);
if (updateSelection && this.energyRates.length > 0)
this.selectEnergyRate(this.energyRates[0].id)
})
}
}

View File

@ -1,3 +0,0 @@
import {inject, Injectable} from '@angular/core';
import {SettingService} from './setting.service';
import {of} from 'rxjs';

View File

@ -15,7 +15,7 @@ export interface ISettings
clientId: string;
scopes: string;
responseType: string;
secureRoute: string[];
secureRoutes: string[];
}
}
@Injectable(
@ -41,7 +41,7 @@ export class SettingService
}
async initialize(): Promise<void> {
this.setting = await firstValueFrom(this.http.get<ISettings>('/setting.json'));
this.setting = await firstValueFrom(this.http.get<ISettings>('/settings.json'));
}
getMainCommandUrl(command: string): string {

9446
yarn.lock

File diff suppressed because it is too large Load Diff