-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #210 from e-picsa/feat/dashboard-climate-admin
Feat(dashboard): climate data page
- Loading branch information
Showing
28 changed files
with
863 additions
and
123 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data-api.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Injectable } from '@angular/core'; | ||
import createClient from 'openapi-fetch'; | ||
|
||
import { paths } from './types/api'; | ||
|
||
const API_ENDPOINT = 'https://api.epicsa.idems.international'; | ||
|
||
/** Service to interact with external PICSA Climate API */ | ||
@Injectable({ providedIn: 'root' }) | ||
export class ClimateDataApiService { | ||
|
||
/** Http client with type-definitions for API endpoints */ | ||
public client:ReturnType<typeof createClient<paths>> | ||
|
||
constructor() { | ||
this.client = createClient<paths>({ baseUrl: API_ENDPOINT,mode:'cors' }); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data.module.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { CommonModule } from '@angular/common'; | ||
import { NgModule } from '@angular/core'; | ||
import { RouterModule } from '@angular/router'; | ||
|
||
import { ClimateDataHomeComponent } from './pages/home/climate-data-home.component'; | ||
import { StationPageComponent } from './pages/station/station-page.component'; | ||
|
||
@NgModule({ | ||
declarations: [], | ||
imports: [ | ||
CommonModule, | ||
RouterModule.forChild([ | ||
{ | ||
path: '', | ||
component: ClimateDataHomeComponent, | ||
}, | ||
{ | ||
path: ':stationId', | ||
component: StationPageComponent, | ||
}, | ||
]), | ||
], | ||
}) | ||
export class ClimateDataModule {} |
49 changes: 49 additions & 0 deletions
49
apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data.service.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { Injectable } from '@angular/core'; | ||
// eslint-disable-next-line @nx/enforce-module-boundaries | ||
import { Database } from '@picsa/server-types'; | ||
import { PicsaAsyncService } from '@picsa/shared/services/asyncService.service'; | ||
import { SupabaseService } from '@picsa/shared/services/core/supabase'; | ||
import { IStorageEntry } from '@picsa/shared/services/core/supabase/services/supabase-storage.service'; | ||
|
||
import { ClimateDataApiService } from './climate-data-api.service'; | ||
|
||
export type IStationRow = Database['public']['Tables']['climate_stations']['Row']; | ||
|
||
export interface IResourceStorageEntry extends IStorageEntry { | ||
/** Url generated when upload to public bucket (will always be populated, even if bucket not public) */ | ||
publicUrl: string; | ||
} | ||
|
||
export type IResourceEntry = Database['public']['Tables']['resources']['Row']; | ||
|
||
@Injectable({ providedIn: 'root' }) | ||
export class ClimateDataDashboardService extends PicsaAsyncService { | ||
public apiStatus: number; | ||
public stations: IStationRow[] = []; | ||
|
||
constructor(private supabaseService: SupabaseService, private api: ClimateDataApiService) { | ||
super(); | ||
} | ||
|
||
public override async init() { | ||
await this.supabaseService.ready(); | ||
await this.checkStatus(); | ||
await this.listStations(); | ||
} | ||
|
||
private async checkStatus() { | ||
const { client } = this.api; | ||
const { response } = await client.GET('/v1/status/'); | ||
this.apiStatus = response.status; | ||
} | ||
|
||
private async listStations() { | ||
// HACK - endpoint not operational | ||
// TODO - when running should refresh from server as cron task | ||
const { data, error } = await this.supabaseService.db.table('climate_stations').select<'*', IStationRow>('*'); | ||
if (error) { | ||
throw error; | ||
} | ||
this.stations = data || []; | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
...a-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<div class="page-content"> | ||
<div style="display: flex; align-items: center"> | ||
<h2 style="flex: 1">Climate Data</h2> | ||
@if(service.apiStatus; as status){ | ||
<div class="server-status"> | ||
Server Status <span class="status-code" [attr.data-status]="status">{{ status }}</span> | ||
</div> | ||
} | ||
</div> | ||
<h3>Stations</h3> | ||
<div style="display: flex; gap: 1rem"> | ||
<div style="flex: 1"> | ||
<table mat-table class="stations-table" [dataSource]="service.stations" style="width: 200px"> | ||
<ng-container matColumnDef="station_id"> | ||
<th mat-header-cell *matHeaderCellDef>station_id</th> | ||
<td mat-cell *matCellDef="let station">{{ station.station_id }}</td> | ||
</ng-container> | ||
<ng-container matColumnDef="station_name"> | ||
<th mat-header-cell *matHeaderCellDef>station_name</th> | ||
<td mat-cell *matCellDef="let station">{{ station.station_name }}</td> | ||
</ng-container> | ||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | ||
<tr | ||
mat-row | ||
class="station-row" | ||
[routerLink]="row.station_id" | ||
*matRowDef="let row; columns: displayedColumns" | ||
></tr> | ||
</table> | ||
</div> | ||
<picsa-map style="height: 500px; width: 500px" [markers]="mapMarkers"></picsa-map> | ||
</div> | ||
</div> |
24 changes: 24 additions & 0 deletions
24
...a-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
.status-code { | ||
color: white; | ||
padding: 8px; | ||
border-radius: 4px; | ||
background: gray; | ||
&[data-status='200'] { | ||
background: green; | ||
} | ||
} | ||
|
||
table.station-table { | ||
max-height: 50vh; | ||
display: block; | ||
overflow: auto; | ||
} | ||
tr.station-row { | ||
cursor: pointer; | ||
&:hover { | ||
background: whitesmoke; | ||
} | ||
} | ||
th { | ||
font-weight: bold; | ||
} |
22 changes: 22 additions & 0 deletions
22
...pps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
|
||
import { ClimateDataHomeComponent } from './climate-data-home.component'; | ||
|
||
describe('ClimateDataHomeComponent', () => { | ||
let component: ClimateDataHomeComponent; | ||
let fixture: ComponentFixture<ClimateDataHomeComponent>; | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
imports: [ClimateDataHomeComponent], | ||
}).compileComponents(); | ||
|
||
fixture = TestBed.createComponent(ClimateDataHomeComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
30 changes: 30 additions & 0 deletions
30
...csa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { CommonModule } from '@angular/common'; | ||
import { Component, OnInit } from '@angular/core'; | ||
import { MatTableModule } from '@angular/material/table'; | ||
import { RouterModule } from '@angular/router'; | ||
import { IMapMarker, PicsaMapComponent } from '@picsa/shared/features/map/map'; | ||
|
||
import { ClimateDataDashboardService, IStationRow } from '../../climate-data.service'; | ||
|
||
@Component({ | ||
selector: 'dashboard-climate-data-home', | ||
standalone: true, | ||
imports: [CommonModule, MatTableModule, RouterModule, PicsaMapComponent], | ||
templateUrl: './climate-data-home.component.html', | ||
styleUrls: ['./climate-data-home.component.scss'], | ||
}) | ||
export class ClimateDataHomeComponent implements OnInit { | ||
public displayedColumns: (keyof IStationRow)[] = ['station_id', 'station_name']; | ||
|
||
public mapMarkers: IMapMarker[]; | ||
|
||
constructor(public service: ClimateDataDashboardService) {} | ||
|
||
async ngOnInit() { | ||
await this.service.ready(); | ||
this.mapMarkers = this.service.stations.map((m) => ({ | ||
latlng: [m.latitude as number, m.longitude as number], | ||
number: m.station_id, | ||
})); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
...csa-apps/dashboard/src/app/modules/climate-data/pages/station/station-page.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<div class="page-content"> | ||
@if(station){ | ||
<h2>{{ station.station_name }}</h2> | ||
<table> | ||
<tr> | ||
@for(key of stationSummary.keys; track $index){ | ||
<th>{{ key }}</th> | ||
} | ||
</tr> | ||
@for(value of stationSummary.values; track $index){ | ||
<td>{{ value }}</td> | ||
} | ||
</table> | ||
} | ||
</div> |
12 changes: 12 additions & 0 deletions
12
...csa-apps/dashboard/src/app/modules/climate-data/pages/station/station-page.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
table { | ||
font-family: arial, sans-serif; | ||
border-collapse: collapse; | ||
width: 100%; | ||
} | ||
|
||
td, | ||
th { | ||
border: 1px solid #dddddd; | ||
text-align: left; | ||
padding: 8px; | ||
} |
21 changes: 21 additions & 0 deletions
21
...-apps/dashboard/src/app/modules/climate-data/pages/station/station-page.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
import { StationPageComponent } from './station-page.component'; | ||
|
||
describe('StationPageComponent', () => { | ||
let component: StationPageComponent; | ||
let fixture: ComponentFixture<StationPageComponent>; | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
imports: [StationPageComponent], | ||
}).compileComponents(); | ||
|
||
fixture = TestBed.createComponent(StationPageComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
39 changes: 39 additions & 0 deletions
39
...picsa-apps/dashboard/src/app/modules/climate-data/pages/station/station-page.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { CommonModule } from '@angular/common'; | ||
import { Component, OnInit } from '@angular/core'; | ||
import { ActivatedRoute } from '@angular/router'; | ||
import { PicsaNotificationService } from '@picsa/shared/services/core/notification.service'; | ||
|
||
import { ClimateDataDashboardService, IStationRow } from '../../climate-data.service'; | ||
|
||
@Component({ | ||
selector: 'dashboard-station-page', | ||
standalone: true, | ||
imports: [CommonModule], | ||
templateUrl: './station-page.component.html', | ||
styleUrls: ['./station-page.component.scss'], | ||
}) | ||
export class StationPageComponent implements OnInit { | ||
public station: IStationRow | undefined; | ||
|
||
public get stationSummary() { | ||
return { | ||
keys: Object.keys(this.station || {}), | ||
values: Object.values(this.station || {}), | ||
}; | ||
} | ||
|
||
constructor( | ||
private service: ClimateDataDashboardService, | ||
private route: ActivatedRoute, | ||
private notificationService: PicsaNotificationService | ||
) {} | ||
|
||
async ngOnInit() { | ||
await this.service.ready(); | ||
const { stationId } = this.route.snapshot.params; | ||
this.station = this.service.stations.find((station) => station.station_id === parseInt(stationId)); | ||
if (!this.station) { | ||
this.notificationService.showUserNotification({ matIcon: 'error', message: `Station data not found` }); | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
apps/picsa-apps/dashboard/src/app/modules/climate-data/types/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Climate Data Types | ||
|
||
The climate data api runs on a remote server and exports endpoint definitions using the OpenAPI spec. | ||
|
||
These definitions can be converted into typescript types on-demand using the command: | ||
|
||
```sh | ||
npx openapi-typescript "https://api.epicsa.idems.international/openapi.json" -o "apps\picsa-apps\dashboard\src\app\modules\climate-data\types\api.d.ts" | ||
``` |
Oops, something went wrong.