Skip to content

Commit

Permalink
Merge branch 'main' into ft-calendar-transalation
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismclarke authored Jan 17, 2024
2 parents 94b262e + 2f726c4 commit b219d73
Show file tree
Hide file tree
Showing 267 changed files with 3,153 additions and 1,268 deletions.
84 changes: 84 additions & 0 deletions .github/workflows/web-release-dashboard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Deploy dashboard to vercel hosting
# https://vercel.com/guides/how-can-i-use-github-actions-with-vercel

# Required Secrets
# - VERCEL_TOKEN
# - VERCEL_PROJECT_ID
# - VERCEL_ORG_ID

name: Web Release Dashboard

# Only keep one active build per ref (e.g. pr branch, push branch, triggering workflow ref)
concurrency:
group: web-release-dashboard-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
on:
workflow_dispatch:
push:
branches:
- main
paths:
- 'apps/picsa-apps/dashboard/**'

jobs:
web_release_dashboard:
runs-on: ubuntu-latest
environment: dashboard
steps:
- uses: actions/checkout@v3
with:
lfs: true
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18.x
#############################################################################
# Node Modules
# Manually restore any previous cache to speed install
# As immutable install will not change cache only save new cache if not hit
# Uses fine-grained methods from https://github.com/actions/cache
#############################################################################
- uses: actions/cache/restore@v3
id: cache
with:
path: ./.yarn/cache
key: ${{ runner.os }}-node-modules-yarn-v1-${{ hashFiles('yarn.lock') }}
restore-keys: |
${{ runner.os }}-node-modules-yarn-v1-
- name: Install node modules
run: yarn install --immutable && npm i -g vercel

- uses: actions/cache/save@v3
if: steps.cache.outputs.cache-hit != 'true'
with:
path: ./.yarn/cache
key: ${{ runner.os }}-node-modules-yarn-v1-${{ hashFiles('yarn.lock') }}

- name: Pull Vercel Environment Information
run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}

# Required build script defined in dashboard project `vercel.json`
- name: Build
run: npx vercel --local-config apps/picsa-apps/dashboard/vercel.json build --prod --token=${{ secrets.VERCEL_TOKEN }}

- name: Deploy
run: npx vercel --local-config apps/picsa-apps/dashboard/vercel.json deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}

# TODO - run supabase db migrations (if required)
# TODO - handle staging/preview deploy (if required)

# NOTE - could also use deployment action to populate additonal metadata (could use cli)
# https://github.com/marketplace/actions/vercel-action

# - uses: amondnet/vercel-action@v20
# with:
# vercel-token: ${{ secrets.VERCEL_TOKEN }}
# github-token: ${{ secrets.GITHUB_TOKEN }}
# vercel-args: '--local-config=apps/picsa-apps/dashboard/vercel.json --prebuilt --prod'
# vercel-org-id: ${{ secrets.VERCEL_ORG_ID}}
# vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID}}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,5 @@ apps/extension-toolkit/www_sourcemaps
.next/

.eslintcache
.nx/cache
.nx/cache
.vercel
1 change: 1 addition & 0 deletions .nxignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
apps/_deprecated/**
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
10 changes: 8 additions & 2 deletions apps/picsa-apps/dashboard/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"tsConfig": "apps/picsa-apps/dashboard/tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": ["apps/picsa-apps/dashboard/src/favicon.ico", "apps/picsa-apps/dashboard/src/assets"],
"styles": ["apps/picsa-apps/dashboard/src/styles.scss"],
"styles": ["apps/picsa-apps/dashboard/src/styles.scss", "node_modules/leaflet/dist/leaflet.css"],
"stylePreprocessorOptions": {
"includePaths": ["libs/theme/src"]
},
Expand All @@ -30,14 +30,20 @@
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
"maximumError": "2mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "libs/environments/src/environment.ts",
"with": "libs/environments/src/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
Expand Down
14 changes: 2 additions & 12 deletions apps/picsa-apps/dashboard/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,15 @@
<mat-sidenav #sidenav mode="side" opened [fixedInViewport]="true" fixedTopGap="64">
<mat-nav-list>
@for (link of navLinks; track link.href) {
<a
mat-list-item
[routerLink]="link.href"
routerLinkActive="mdc-list-item--activated active-link"
[routerLinkActiveOptions]="{ exact: true }"
>
<a mat-list-item [routerLink]="link.href" routerLinkActive="mdc-list-item--activated active-link">
{{ link.label }}
</a>
}
<mat-divider style="margin-top: auto"></mat-divider>
<div mat-subheader>Global Admin</div>
<mat-divider></mat-divider>
@for (link of globalLinks; track link.href) {
<a
mat-list-item
[routerLink]="link.href"
routerLinkActive="mdc-list-item--activated active-link"
[routerLinkActiveOptions]="{ exact: true }"
>
<a mat-list-item [routerLink]="link.href" routerLinkActive="mdc-list-item--activated active-link">
{{ link.label }}
</a>
}
Expand Down
24 changes: 12 additions & 12 deletions apps/picsa-apps/dashboard/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,18 @@ export class AppComponent implements AfterViewInit {
title = 'picsa-apps-dashboard';

navLinks: INavLink[] = [
{
label: 'Home',
href: '/',
},
// {
// label: 'Home',
// href: '',
// },
{
label: 'Resources',
href: '/resources',
},
// {
// label: 'Climate Data',
// href: '/climate-data',
// },
{
label: 'Climate Data',
href: '/climate-data',
},
// {
// label: 'Crop Information',
// href: '/crop-information',
Expand All @@ -42,10 +42,10 @@ export class AppComponent implements AfterViewInit {
// label: 'Monitoring Forms',
// href: '/monitoring-forms',
// },
// {
// label: 'Translations',
// href: '/translations',
// },
{
label: 'Translations',
href: '/translations',
},
];

globalLinks: INavLink[] = [
Expand Down
13 changes: 13 additions & 0 deletions apps/picsa-apps/dashboard/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,17 @@ export const appRoutes: Route[] = [
path: 'resources',
loadChildren: () => import('./modules/resources/resources.module').then((m) => m.ResourcesPageModule),
},
{
path: 'climate-data',
loadChildren: () => import('./modules/climate-data/climate-data.module').then((m) => m.ClimateDataModule),
},
{
path: 'translations',
loadChildren: () => import('./modules/translations/translations.module').then((m) => m.TranslationsPageModule),
},
{
path: '',
redirectTo: 'resources',
pathMatch: 'full',
},
];
2 changes: 1 addition & 1 deletion apps/picsa-apps/dashboard/src/app/material.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const matModules = [
MatStepperModule,
MatTableModule,
MatTabsModule,
MatToolbarModule,
MatToolbarModule
];

@NgModule({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Injectable } from '@angular/core';
import { PicsaNotificationService } from '@picsa/shared/services/core/notification.service';
import createClient from 'openapi-fetch';

import { paths } from './types/api';

const API_ENDPOINT = 'https://api.epicsa.idems.international';

/** Custom client which tracks responses by callback id */
type ICallbackClient = (id:string)=>ReturnType<typeof createClient<paths>>

/** Type-safe http client with added support for callbacks */
type IClient = ReturnType<typeof createClient<paths>> & {useMeta:ICallbackClient}



interface IMetaEntry{
status:'pending' | 'success' | 'error' | 'unknown',
rawResponse?:Response,
}


/**
* Service to interact with external PICSA Climate API
* All methods are exposed through a type-safe `client` property, or can additionally use
* a custom client that includes status notification updates via the `useMeta` method
* @example
* Use custom callback that will show user notifications on error and record to service
* ```ts
* const {response, data, error} = await api.useMeta('myRequestId').POST(...)
* ```
* Use default client without additional callbacks
* ```ts
* const {response, data, error} = await api.client.POST(...)
* ```
* */
@Injectable({ providedIn: 'root' })
export class ClimateDataApiService {

/** Request additional meta by id */
public meta:Record<string ,IMetaEntry>={}

/** Http client with type-definitions for API endpoints */
public client:IClient

constructor(private notificationService:PicsaNotificationService) {
const client = createClient<paths>({ baseUrl: API_ENDPOINT,mode:'cors' });
this.client = {...client,useMeta:()=>{
return client
}}
}


/**
* Provide an id which which will be updated alongside requests.
* The cache will also include interceptors to provide user notification on error
**/
public useMeta(id:string){
const customFetch = this.createCustomFetchClient(id)
const customClient = createClient<paths>({ baseUrl: API_ENDPOINT,mode:'cors',fetch:customFetch });
return customClient
}

/** Create a custom implementation of fetch client to handle status updates and notifications */
private createCustomFetchClient(id:string){
return async (...args:Parameters<typeof window['fetch']>)=>{
this.meta[id]={status:'pending'}
const response = await window.fetch(...args);
this.meta[id].status = this.getCallbackStatus(response.status)
this.meta[id].rawResponse = response
if(this.meta[id].status ==='error' ){
await this.showCustomFetchErrorMessage(id,response)
}
return response
}
}

/** Show error message when using custom fetch with callbacks */
private async showCustomFetchErrorMessage(id:string,response:Response){
// clone body so that open-api can still consume when constructing full fetch response
const clone = response.clone()
try {
const json = await clone.json()
const errorText = json.detail || 'failed, see console logs for details'
this.notificationService.showUserNotification({matIcon:'error',message:`[${id}] ${errorText}`})
} catch (error) {
console.error(error)
console.error('Fetch Error',error)
this.notificationService.showUserNotification({matIcon:'error',message:`[${id}] 'failed, see console logs for details'`})
}
}

private getCallbackStatus(statusCode:number):IMetaEntry['status']{
if(200 <= statusCode && statusCode <=299) return 'success'
if(400 <= statusCode && statusCode <=499) return 'error'
if(500 <= statusCode && statusCode <=599) return 'error'
return 'unknown'
}
}
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 {}
Loading

0 comments on commit b219d73

Please sign in to comment.