From 7138058d0c1cd567393abe1b90fc5a7bceba3125 Mon Sep 17 00:00:00 2001 From: khalifan-kfan Date: Tue, 13 Feb 2024 12:42:06 +0300 Subject: [PATCH 01/68] add monitoring pages --- .../dashboard/src/app/app.component.ts | 4 + .../dashboard/src/app/app.routes.ts | 4 + .../modules/monitoring/monitoring.module.ts | 24 ++++++ .../modules/monitoring/monitoring.service.ts | 84 +++++++++++++++++++ .../pages/home/monitoring.page.html | 3 + .../pages/home/monitoring.page.scss | 27 ++++++ .../pages/home/monitoring.page.spec.ts | 22 +++++ .../monitoring/pages/home/monitoring.page.ts | 37 ++++++++ .../new/new-monitoring-forms.component.html | 4 + .../new/new-monitoring-forms.component.scss | 26 ++++++ .../new-monitoring-forms.component.spec.ts | 21 +++++ .../new/new-monitoring-forms.component.ts | 50 +++++++++++ .../supabase/data/monitoring_forms_rows.csv | 4 + .../20240213081336_monitoring_forms_table.sql | 14 ++++ apps/picsa-server/supabase/types/index.ts | 39 +++++++++ .../src/assets/resources/contents.json | 24 +++--- .../storage-file-picker.component.ts | 2 +- yarn.lock | 2 +- 18 files changed, 377 insertions(+), 14 deletions(-) create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.scss create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.spec.ts create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.html create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.scss create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.spec.ts create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.ts create mode 100644 apps/picsa-server/supabase/data/monitoring_forms_rows.csv create mode 100644 apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql diff --git a/apps/picsa-apps/dashboard/src/app/app.component.ts b/apps/picsa-apps/dashboard/src/app/app.component.ts index 8e795648e..29e7db337 100644 --- a/apps/picsa-apps/dashboard/src/app/app.component.ts +++ b/apps/picsa-apps/dashboard/src/app/app.component.ts @@ -46,6 +46,10 @@ export class AppComponent implements AfterViewInit { label: 'Translations', href: '/translations', }, + { + label: 'Monitoring', + href: '/monitoring', + }, ]; globalLinks: INavLink[] = [ diff --git a/apps/picsa-apps/dashboard/src/app/app.routes.ts b/apps/picsa-apps/dashboard/src/app/app.routes.ts index c49395830..88de4bfae 100644 --- a/apps/picsa-apps/dashboard/src/app/app.routes.ts +++ b/apps/picsa-apps/dashboard/src/app/app.routes.ts @@ -13,6 +13,10 @@ export const appRoutes: Route[] = [ path: 'translations', loadChildren: () => import('./modules/translations/translations.module').then((m) => m.TranslationsPageModule), }, + { + path: 'monitoring', + loadChildren: () => import('./modules/monitoring/monitoring.module').then((m) => m.MonitoringPageModule), + }, { path: '', redirectTo: 'resources', diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts new file mode 100644 index 000000000..663695554 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts @@ -0,0 +1,24 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { MonitoringPageComponent } from './pages/home/monitoring.page'; +import { NewMonitoringFormsComponent } from './pages/new/new-monitoring-forms.component'; + +@NgModule({ + declarations: [], + imports: [ + CommonModule, + RouterModule.forChild([ + { + path: '', + component: MonitoringPageComponent, + }, + { + path: ':id', + component: NewMonitoringFormsComponent, + } + ]), + ], +}) +export class MonitoringPageModule {} diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts new file mode 100644 index 000000000..0fc580f6a --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts @@ -0,0 +1,84 @@ +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 { PicsaNotificationService } from '@picsa/shared/services/core/notification.service'; +import { SupabaseService } from '@picsa/shared/services/core/supabase'; +import { IStorageEntry } from '@picsa/shared/services/core/supabase/services/supabase-storage.service'; + +export type ITranslationRow = Database['public']['Tables']['translations']['Row']; + +export interface IResourceStorageEntry extends IStorageEntry { + /** Url generated when upload to public bucket (will always be populated, even if bucket not public) */ + publicUrl: string; +} +@Injectable({ providedIn: 'root' }) +export class TranslationDashboardService extends PicsaAsyncService { + public translations: ITranslationRow[] = []; + + public get table() { + return this.supabaseService.db.table('translations'); + } + + constructor(private supabaseService: SupabaseService) { + super(); + } + + public override async init() { + await this.supabaseService.ready(); + await this.listTranslations(); + } + + public async listTranslations() { + const { data, error } = await this.supabaseService.db.table('translations').select<'*', ITranslationRow>('*'); + if (error) { + throw error; + } + this.translations = data || []; + } + // update a translation record by ID + public async updateTranslationById(id: string, updatedData: Partial): Promise { + const { data, error } = await this.supabaseService.db.table('translations').update(updatedData).eq('id', id); + if (error) { + throw error; + } + return 'Updated successfully'; + } + + // Fetch a translation record by ID + public async getTranslationById(id: string): Promise { + const { data, error } = await this.supabaseService.db.table('translations').select('*').eq('id', id).single(); + if (error) { + throw error; + } + return data; + } + + //delete function + public async deleteTranslationById(id: string): Promise { + const { error } = await this.supabaseService.db.table('translations').delete().eq('id', id); + + if (error) { + throw error; + } + return 'Deleted Successfully'; + } + + // In your TranslationDashboardService + public async addTranslation(translation: Partial): Promise { + translation.id = this.generateId(translation as ITranslationRow); + const { data, error } = await this.supabaseService.db.table('translations').insert([translation]); + + if (error) { + throw error; + } + return 'Added successfully'; + } + + /** Composite id generated by combination of row metadata and en text */ + private generateId(row: ITranslationRow) { + // Convert to lower case and remove non-alphanumeric characters so that minor text differences ignored + const textId = row.en.toLowerCase().replace(/[^a-z0-9]/gi, ''); + return `${row.tool}-${row.context || ''}-${textId}`; + } +} diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html new file mode 100644 index 000000000..aa5fbb1c0 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html @@ -0,0 +1,3 @@ +
+ This is the monitoring page +
diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.scss b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.scss new file mode 100644 index 000000000..496f3585e --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.scss @@ -0,0 +1,27 @@ +table.resources-table { + max-height: 50vh; + display: block; + overflow: auto; +} +tr.resource-row { + cursor: pointer; + &:hover { + background: whitesmoke; + } +} +th { + font-weight: bold; +} + +td { + cursor: pointer; +} + +.disallow-click{ + pointer-events: none; + cursor: not-allowed; +} +.allow-click{ + pointer-events:all; + cursor: pointer; +} \ No newline at end of file diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.spec.ts new file mode 100644 index 000000000..7381dfe44 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MonitoringPageComponent } from './monitoring.page'; + +describe('MonitoringPageComponent', () => { + let component: MonitoringPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MonitoringPageComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(MonitoringPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts new file mode 100644 index 000000000..9190a9507 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts @@ -0,0 +1,37 @@ +import { CommonModule } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { Router } from '@angular/router'; +// eslint-disable-next-line @nx/enforce-module-boundaries +import { Database } from '@picsa/server-types'; + +import { DashboardMaterialModule } from '../../../../material.module'; +import { TranslationDashboardService } from '../../monitoring.service'; + +export type ITranslationRow = Database['public']['Tables']['translations']['Row']; +@Component({ + selector: 'dashboard-translations-page', + standalone: true, + imports: [CommonModule, DashboardMaterialModule, RouterModule], + templateUrl: './monitoring.page.html', + styleUrls: ['./monitoring.page.scss'], +}) +export class MonitoringPageComponent implements OnInit { + displayedColumns: string[] = ['tool', 'context', 'en', 'mw_ny', 'zm_ny', 'ke_sw', 'tj_tg', 'created_at']; + + constructor(public service: TranslationDashboardService, private router: Router) {} + ngOnInit(): void { + this.service.ready(); + this.refreshTranslations(); + } + + goToRecord(row: ITranslationRow) { + this.router.navigate([`/translations`, row.id]); + } + + refreshTranslations() { + this.service.listTranslations().catch((error) => { + console.error('Error fetching translations:', error); + }); + } +} diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.html b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.html new file mode 100644 index 000000000..1f5363936 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.html @@ -0,0 +1,4 @@ +
+

New monitoring forms

+ +
diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.scss b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.scss new file mode 100644 index 000000000..ab42dae12 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.scss @@ -0,0 +1,26 @@ +.form-content{ + display: flex; + flex-direction: column; + gap: 1.4rem; +} +.submitButton{ + width: 7rem; + margin-bottom: 1rem; +} +.form-data{ + display: flex; + flex-direction: column; + gap: 0.5rem; + +} +input { + padding: 8px; + color: var(--color-primary); + outline-color: var(--color-primary); + max-width: 300px; + display: block; + font-size: 0.9rem; + white-space: pre-wrap; + word-wrap: break-word; + + } \ No newline at end of file diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.spec.ts new file mode 100644 index 000000000..02ea96359 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { NewMonitoringFormsComponent } from './new-monitoring-forms.component' + +describe('NewTranslationsComponent', () => { + let component: NewMonitoringFormsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [NewMonitoringFormsComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(NewMonitoringFormsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.ts new file mode 100644 index 000000000..cdb638c4b --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.ts @@ -0,0 +1,50 @@ +import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; + +// eslint-disable-next-line @nx/enforce-module-boundaries +//import type { Database } from '@picsa/server-types'; +import { DashboardMaterialModule } from '../../../../material.module'; +// import { DashboardResourcesStorageLinkComponent } from '../../components/storage-link/storage-link.component'; +import { TranslationDashboardService } from '../../monitoring.service'; + +//type ITranslationEntry = Database['public']['Tables']['translations']['Row']; + +@Component({ + selector: 'dashboard-translations-new', + standalone: true, + imports: [CommonModule, DashboardMaterialModule, FormsModule, ReactiveFormsModule], + templateUrl: './new-monitoring-forms.component.html', + styleUrls: ['./new-monitoring-forms.component.scss'], +}) +export class NewMonitoringFormsComponent { + en: string; + mw_ny: string; + ke_sw: string; + tj_tg: string; + zm_ny: string; + editActionFeedbackMessage: string; + constructor(private service: TranslationDashboardService) { + this.service.ready(); + } + submitForm() { + const data = { + en: this.en, + mw_ny: this.mw_ny, + ke_sw: this.ke_sw, + tj_tg: this.tj_tg, + zm_ny: this.zm_ny, + }; + this.service + .addTranslation(data) + .then((data) => { + if (data === 'Added successfully') { + this.editActionFeedbackMessage = 'Added successfully'; + } + }) + .catch((error) => { + console.error('Error adding translation:', error); + this.editActionFeedbackMessage = 'Failed to add a translation.'; + }); + } +} diff --git a/apps/picsa-server/supabase/data/monitoring_forms_rows.csv b/apps/picsa-server/supabase/data/monitoring_forms_rows.csv new file mode 100644 index 000000000..38d850060 --- /dev/null +++ b/apps/picsa-server/supabase/data/monitoring_forms_rows.csv @@ -0,0 +1,4 @@ +id,created_at,tittle,description,enketoDefinition,summaryFields,appCountries,enketoForm,enketoModel,cover +2,2024-02-13 07:34:03.939328+00,Extension Worker Monitoring,Malawi,"{""hash"":""md5:dc0116ade1cd0dc0a7c0f3ce02bcbddc--d97bac4e49bda9c52dee96737583f620---1"",""media"":{},""theme"":""grid"",""maxSize"":10000000,""enketoId"":""PGpldp9m"",""languageMap"":{},""externalData"":[]}","[{""field"":""date"",""label"":""Date""},{""field"":""district"",""label"":""District""},{""field"":""EPA"",""label"":""EPA""},{""field"":""section"",""label"":""Section""},{""field"":""total_farmers"",""label"":""Total Farmers""}]","[""mw"","" ""]","
\n

E-PICSA - EW Monitoring Malawi

\n \n \n \n
District\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n
","\n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <__version__/>\n \n \n \n \n ","{""icon"":""assets/svgs/monitoring-forms/ew-monitoring.svg""}" +4,2024-02-13 07:47:56.842289+00,Complex Demo Form,demo,"{""hash"":""md5:492f8458063b6e9056d5d7fa928500ce--2851db799b6357cc49f5607b9000579d---1"",""media"":{},""theme"":""grid"",""enketoId"":""poiB"",""languageMap"":{}}","[{""field"":""_id"",""label"":""ID""}]","["" ""]","
\n

The Kitchen Sink

\n \n \n
\n
\n
\n
\n
\n
\n
Was the patient\'s blood drawn?To deselect a radio button just click it again.\n
Was the patient\'s blood drawn?Same question but with a horizontal layout\n
Please choose a response:If you have a lot of response options to display, they can be formatted into columns\n
\n
I have a sufficient energy today
\n
I am able to go to work
\n
I have no problems with self-care
\n
\n
\n
I eat a balanced diet.\n
I get a lot of exercise.\n
LABEL HERE\n
\n
\n
Please choose one or more of the following:
These are organized into nicely structured columns\n
You cannot select both c and d.
How many meals did you eat today?
\n
Please choose a response:
\n
\n
Which meals did Sarah eat today?
Hint text can go here.\n
Which meals did James eat today?
HInt text can go here.\n
What meals did Simon eat today?
HInt text can go here.\n
\n
\n
\n
\n
\n
\n
\n
Ongoing?*\n
This field is required
Expected*\n
This field is required
AE Serious?*\n
This field is required
\n
\n
\n
\n
\n
\n
What is your current level of pain?\n
Please select the blue square\n
In which state will the patient be treated?\n
\n
\n
Which is the greatest contributor to the BMI of ? Other field values are displayed within the codelist labels\n
\n
\n
Select multiple widget (don\'t pick c and d together)\n
You cannot select both c and d.
\n
\n
What a creatinine test conducted?\n
Was a creatinine test conducted?\n
Please provide the subject\'s sex:\n
Did you eat dinner today?\n
\n
\n
Did you eat dinner today?\n
\n
\n
\n
\n
\n
\n
\n
\n \n
","\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n static_instance-ABCD-0\n a\n \n \n static_instance-ABCD-1\n b\n \n \n static_instance-ABCD-2\n c\n \n \n static_instance-ABCD-3\n d\n \n \n \n \n \n static_instance-YN-0\n 1\n \n \n static_instance-YN-1\n 2\n \n \n \n \n \n static_instance-GENERIC_LIST-0\n a\n \n \n static_instance-GENERIC_LIST-1\n b\n \n \n static_instance-GENERIC_LIST-2\n c\n \n \n static_instance-GENERIC_LIST-3\n d\n \n \n static_instance-GENERIC_LIST-4\n e\n \n \n static_instance-GENERIC_LIST-5\n f\n \n \n static_instance-GENERIC_LIST-6\n g\n \n \n static_instance-GENERIC_LIST-7\n h\n \n \n static_instance-GENERIC_LIST-8\n i\n \n \n static_instance-GENERIC_LIST-9\n j\n \n \n \n \n \n static_instance-FACES-0\n 0\n \n \n static_instance-FACES-1\n 1\n \n \n static_instance-FACES-2\n 2\n \n \n static_instance-FACES-3\n 3\n \n \n static_instance-FACES-4\n 4\n \n \n \n \n \n static_instance-WHUNITS-0\n 1\n \n \n static_instance-WHUNITS-1\n 2\n \n \n \n \n \n static_instance-COLORS-0\n red\n \n \n static_instance-COLORS-1\n green\n \n \n static_instance-COLORS-2\n blue\n \n \n static_instance-COLORS-3\n yellow\n \n \n \n \n \n static_instance-ACT-0\n 0\n \n \n static_instance-ACT-1\n 1\n \n \n static_instance-ACT-2\n 2\n \n \n static_instance-ACT-3\n 3\n \n \n static_instance-ACT-4\n 4\n \n \n static_instance-ACT-5\n 5\n \n \n \n \n \n static_instance-us_states-0\n AL\n \n \n static_instance-us_states-1\n AK\n \n \n static_instance-us_states-2\n AZ\n \n \n static_instance-us_states-3\n AR\n \n \n static_instance-us_states-4\n CA\n \n \n static_instance-us_states-5\n CO\n \n \n static_instance-us_states-6\n CT\n \n \n static_instance-us_states-7\n DE\n \n \n static_instance-us_states-8\n FL\n \n \n static_instance-us_states-9\n GA\n \n \n static_instance-us_states-10\n HI\n \n \n static_instance-us_states-11\n ID\n \n \n static_instance-us_states-12\n IL\n \n \n static_instance-us_states-13\n IN\n \n \n static_instance-us_states-14\n IA\n \n \n static_instance-us_states-15\n KS\n \n \n static_instance-us_states-16\n KY\n \n \n static_instance-us_states-17\n LA\n \n \n static_instance-us_states-18\n MA\n \n \n static_instance-us_states-19\n MD\n \n \n static_instance-us_states-20\n ME\n \n \n static_instance-us_states-21\n MI\n \n \n static_instance-us_states-22\n MN\n \n \n static_instance-us_states-23\n MO\n \n \n static_instance-us_states-24\n MS\n \n \n static_instance-us_states-25\n MT\n \n \n static_instance-us_states-26\n NC\n \n \n static_instance-us_states-27\n ND\n \n \n static_instance-us_states-28\n NE\n \n \n static_instance-us_states-29\n NH\n \n \n static_instance-us_states-30\n NJ\n \n \n static_instance-us_states-31\n NM\n \n \n static_instance-us_states-32\n NV\n \n \n static_instance-us_states-33\n NY\n \n \n static_instance-us_states-34\n OH\n \n \n static_instance-us_states-35\n OK\n \n \n static_instance-us_states-36\n OR\n \n \n static_instance-us_states-37\n PA\n \n \n static_instance-us_states-38\n RI\n \n \n static_instance-us_states-39\n SC\n \n \n static_instance-us_states-40\n SD\n \n \n static_instance-us_states-41\n TN\n \n \n static_instance-us_states-42\n TX\n \n \n static_instance-us_states-43\n UT\n \n \n static_instance-us_states-44\n VA\n \n \n static_instance-us_states-45\n VT\n \n \n static_instance-us_states-46\n WA\n \n \n static_instance-us_states-47\n WI\n \n \n static_instance-us_states-48\n WV\n \n \n static_instance-us_states-49\n WY\n \n \n static_instance-us_states-50\n DC\n \n \n \n \n \n static_instance-BMIRES-0\n 2\n \n \n static_instance-BMIRES-1\n 3\n \n \n \n \n \n static_instance-SEX-0\n 1\n \n \n static_instance-SEX-1\n 2\n \n \n \n \n \n static_instance-RL-0\n 1\n \n \n static_instance-RL-1\n 2\n \n \n static_instance-RL-2\n 3\n \n \n static_instance-RL-3\n 4\n \n \n static_instance-RL-4\n 5\n \n \n \n \n \n static_instance-TG-0\n 1\n \n \n static_instance-TG-1\n 2\n \n \n static_instance-TG-2\n 3\n \n \n static_instance-TG-3\n 4\n \n \n static_instance-TG-4\n 5\n \n \n \n \n \n static_instance-CASCADE1-0\n 1\n \n \n static_instance-CASCADE1-1\n 2\n \n \n static_instance-CASCADE1-2\n 3\n \n \n static_instance-CASCADE1-3\n 4\n \n \n static_instance-CASCADE1-4\n 1\n 5\n \n \n static_instance-CASCADE1-5\n 1\n 6\n \n \n \n \n \n static_instance-MEALS-0\n 1\n \n \n static_instance-MEALS-1\n 2\n \n \n static_instance-MEALS-2\n 3\n \n \n static_instance-MEALS-3\n 4\n \n \n \n \n \n static_instance-OUT-0\n 0\n \n \n static_instance-OUT-1\n 1\n \n \n static_instance-OUT-2\n 2\n \n \n static_instance-OUT-3\n 3\n \n \n static_instance-OUT-4\n 4\n \n \n \n \n \n static_instance-ACKNOWLEDGE-0\n 1\n \n \n \n \n \n static_instance-CASCADE2-0\n 1\n 1\n \n \n static_instance-CASCADE2-1\n 1\n 2\n \n \n static_instance-CASCADE2-2\n 1\n 3\n \n \n static_instance-CASCADE2-3\n 1\n 4\n \n \n static_instance-CASCADE2-4\n 2\n 5\n \n \n static_instance-CASCADE2-5\n 2\n 6\n \n \n static_instance-CASCADE2-6\n 1\n 7\n 1\n \n \n static_instance-CASCADE2-7\n 1\n 8\n 1\n \n \n \n \n \n static_instance-CASCADE3-0\n 1\n 1\n 1\n \n \n static_instance-CASCADE3-1\n 1\n 2\n 1\n \n \n static_instance-CASCADE3-2\n 1\n 3\n 1\n \n \n static_instance-CASCADE3-3\n 1\n 4\n 1\n \n \n static_instance-CASCADE3-4\n 1\n 5\n 1\n \n \n static_instance-CASCADE3-5\n 1\n 6\n 1\n \n \n static_instance-CASCADE3-6\n 1\n 7\n 2\n \n \n static_instance-CASCADE3-7\n 1\n 8\n 2\n \n \n static_instance-CASCADE3-8\n 1\n 9\n 2\n \n \n static_instance-CASCADE3-9\n 1\n 10\n 2\n \n \n static_instance-CASCADE3-10\n 2\n 11\n 7\n \n \n static_instance-CASCADE3-11\n 2\n 12\n 7\n \n \n static_instance-CASCADE3-12\n 2\n 13\n 7\n \n \n static_instance-CASCADE3-13\n 2\n 14\n 7\n \n \n static_instance-CASCADE3-14\n 2\n 15\n 7\n \n \n static_instance-CASCADE3-15\n 2\n 16\n 8\n \n \n static_instance-CASCADE3-16\n 2\n 17\n 8\n \n \n static_instance-CASCADE3-17\n 2\n 18\n 8\n \n \n static_instance-CASCADE3-18\n 19\n \n \n static_instance-CASCADE3-19\n 20\n \n \n \n \n \n static_instance-UNITS-0\n 1\n \n \n static_instance-UNITS-1\n 2\n \n \n \n \n \n static_instance-BODYSYSTEM-0\n 1\n \n \n static_instance-BODYSYSTEM-1\n 2\n \n \n static_instance-BODYSYSTEM-2\n 3\n \n \n static_instance-BODYSYSTEM-3\n 4\n \n \n \n \n \n static_instance-DATE_PRECISION-0\n 1\n \n \n static_instance-DATE_PRECISION-1\n 2\n \n \n static_instance-DATE_PRECISION-2\n 3\n \n \n \n \n \n static_instance-AGREEMENT-0\n 1\n \n \n static_instance-AGREEMENT-1\n 2\n \n \n static_instance-AGREEMENT-2\n 3\n \n \n static_instance-AGREEMENT-3\n 4\n \n \n static_instance-AGREEMENT-4\n 5\n \n \n ","{""icon"":""""}" +3,2024-02-13 07:37:01.044168+00,Extension Worker Monitoring,Zambia,"{""hash"":""md5:3166205f2f70f1e29ea86d6dbcd8b36c--d97bac4e49bda9c52dee96737583f620---1"",""media"":{},""theme"":""grid"",""maxSize"":10000000,""enketoId"":""znNPkvlJ"",""languageMap"":{},""externalData"":[]}","[{""field"":""date"",""label"":""Date""},{""field"":""district"",""label"":""District""},{""field"":""EPA"",""label"":""EPA""},{""field"":""section"",""label"":""Section""},{""field"":""total_farmers"",""label"":""Total Farmers""}]","[""zm"","" ""]","
\n

E-PICSA - EW Monitoring Zambia

\n \n \n \n
Organisation\n
\n
District\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n
","\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <__version__/>\n \n \n \n \n ","{""icon"":""assets/svgs/monitoring-forms/ew-monitoring.svg""}" \ No newline at end of file diff --git a/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql b/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql new file mode 100644 index 000000000..e96c063ad --- /dev/null +++ b/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql @@ -0,0 +1,14 @@ +create table + public.monitoring_forms ( + id bigint generated by default as identity, + created_at timestamp with time zone not null default now(), + tittle text not null, + description character varying null, + "enketoDefinition" jsonb null, + "summaryFields" jsonb[] null, + "appCountries" text[] null, + "enketoForm" text null, + "enketoModel" text null, + cover jsonb null, + constraint monitoring_forms_pkey primary key (id) + ) tablespace pg_default; \ No newline at end of file diff --git a/apps/picsa-server/supabase/types/index.ts b/apps/picsa-server/supabase/types/index.ts index c5486929a..abb5adbd4 100644 --- a/apps/picsa-server/supabase/types/index.ts +++ b/apps/picsa-server/supabase/types/index.ts @@ -134,6 +134,45 @@ export interface Database { } Relationships: [] } + monitoring_forms: { + Row: { + appCountries: string[] | null + cover: Json | null + created_at: string + description: string | null + enketoDefinition: Json | null + enketoForm: string | null + enketoModel: string | null + id: number + summaryFields: Json[] | null + tittle: string + } + Insert: { + appCountries?: string[] | null + cover?: Json | null + created_at?: string + description?: string | null + enketoDefinition?: Json | null + enketoForm?: string | null + enketoModel?: string | null + id?: number + summaryFields?: Json[] | null + tittle: string + } + Update: { + appCountries?: string[] | null + cover?: Json | null + created_at?: string + description?: string | null + enketoDefinition?: Json | null + enketoForm?: string | null + enketoModel?: string | null + id?: number + summaryFields?: Json[] | null + tittle?: string + } + Relationships: [] + } monitoring_tool_submissions: { Row: { _app_user_id: string diff --git a/apps/picsa-tools/resources-tool/src/assets/resources/contents.json b/apps/picsa-tools/resources-tool/src/assets/resources/contents.json index fe8583095..89bc4db74 100644 --- a/apps/picsa-tools/resources-tool/src/assets/resources/contents.json +++ b/apps/picsa-tools/resources-tool/src/assets/resources/contents.json @@ -44,8 +44,8 @@ "covers/facebook.svg": { "relativePath": "covers/facebook.svg", "size_kb": 1.1, - "md5Checksum": "8626bc9119bd6b8bc0fa04e8a20b6e74", - "modifiedTime": "2022-09-28T20:19:52.986Z" + "md5Checksum": "a09b2f44f6b44b5b6e29aedba9f92029", + "modifiedTime": "2023-07-10T22:11:12.359Z" }, "covers/gap.jpg": { "relativePath": "covers/gap.jpg", @@ -61,9 +61,9 @@ }, "covers/gender-equality.svg": { "relativePath": "covers/gender-equality.svg", - "size_kb": 5.5, - "md5Checksum": "0b6599c4d8b3a87b787910b18a9d8851", - "modifiedTime": "2022-11-01T20:27:18.286Z" + "size_kb": 5.4, + "md5Checksum": "95af38f37c2179d4ad0c5288cbb236ed", + "modifiedTime": "2023-07-10T22:11:12.359Z" }, "covers/gras-nelk.jpg": { "relativePath": "covers/gras-nelk.jpg", @@ -110,8 +110,8 @@ "covers/spreadsheet.svg": { "relativePath": "covers/spreadsheet.svg", "size_kb": 2.8, - "md5Checksum": "a03a4bfb0a1791fda8e232a8b032715b", - "modifiedTime": "2022-09-30T09:35:53.529Z" + "md5Checksum": "0a1bc91188bbc03fda0a7cb2bc1aa6ce", + "modifiedTime": "2023-07-10T22:11:12.360Z" }, "covers/twitter.png": { "relativePath": "covers/twitter.png", @@ -121,15 +121,15 @@ }, "covers/videos.svg": { "relativePath": "covers/videos.svg", - "size_kb": 3.5, - "md5Checksum": "9bee23e2f74d07bd0cdd27bdc286e777", - "modifiedTime": "2023-09-25T18:01:08.322Z" + "size_kb": 3.4, + "md5Checksum": "37516a0c19e20344d02960c6368dde62", + "modifiedTime": "2023-11-16T07:09:42.068Z" }, "covers/weather.svg": { "relativePath": "covers/weather.svg", "size_kb": 3.1, - "md5Checksum": "db07c7d20d9520124c251c810c5817fd", - "modifiedTime": "2022-09-28T19:22:58.064Z" + "md5Checksum": "ca87f24916d258b8b8b5dd8343d4305b", + "modifiedTime": "2023-07-10T22:11:12.360Z" }, "covers/whatsapp.svg": { "relativePath": "covers/whatsapp.svg", diff --git a/libs/shared/src/services/core/supabase/components/storage-file-picker/storage-file-picker.component.ts b/libs/shared/src/services/core/supabase/components/storage-file-picker/storage-file-picker.component.ts index 7995046d7..ab9613eed 100644 --- a/libs/shared/src/services/core/supabase/components/storage-file-picker/storage-file-picker.component.ts +++ b/libs/shared/src/services/core/supabase/components/storage-file-picker/storage-file-picker.component.ts @@ -66,7 +66,7 @@ export class SupabaseStorageFilePickerComponent { const entries = await this.storage.list(this.storageBucketName, this.storageFolderPath); this.fileEntries = entries // filter out metadata files which have filename starting with `.` - .filter(({ name }) => name && !name.split('/').pop()?.startsWith('.')) + .filter((storageEntry) => storageEntry.name && !storageEntry.name.split('/').pop()?.startsWith('.')) .sort((a, b) => { if (!b.name) return 1; if (!a.name) return -1; diff --git a/yarn.lock b/yarn.lock index 585033f0e..255c25bde 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15138,7 +15138,7 @@ __metadata: "leaflet-draw@github:enketo/Leaflet.draw#ff730785db7fcccbf2485ffcf4dffe1238a7c617": version: 1.0.4 resolution: "leaflet-draw@https://github.com/enketo/Leaflet.draw.git#commit=ff730785db7fcccbf2485ffcf4dffe1238a7c617" - checksum: b08b88994769667f11f2b6a8937656c89cea34dafd4661abab0b48b4b97f3bddbdce7b23ddfdb8d7c6335e065530e32a70e281314afa34afa134bf68597945fc + checksum: 253998170af27f886d05b245c85429767e272647221daaf8d94ff5b86f75b8cbb96cc76a8a88492243166a214bc3b66b3ae704a81f74c862f09ac6c9495f731e languageName: node linkType: hard From adf3c3ac52798a9bde360e4ed42b452c3165f25f Mon Sep 17 00:00:00 2001 From: khalifan-kfan Date: Tue, 13 Feb 2024 12:42:22 +0300 Subject: [PATCH 02/68] fix run time error --- .../storage-file-picker/storage-file-picker.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/shared/src/services/core/supabase/components/storage-file-picker/storage-file-picker.component.ts b/libs/shared/src/services/core/supabase/components/storage-file-picker/storage-file-picker.component.ts index ab9613eed..6266bb7dc 100644 --- a/libs/shared/src/services/core/supabase/components/storage-file-picker/storage-file-picker.component.ts +++ b/libs/shared/src/services/core/supabase/components/storage-file-picker/storage-file-picker.component.ts @@ -66,6 +66,7 @@ export class SupabaseStorageFilePickerComponent { const entries = await this.storage.list(this.storageBucketName, this.storageFolderPath); this.fileEntries = entries // filter out metadata files which have filename starting with `.` + // not sure why I keep getting a run time error here with a reset data base .filter((storageEntry) => storageEntry.name && !storageEntry.name.split('/').pop()?.startsWith('.')) .sort((a, b) => { if (!b.name) return 1; From 85bd7814d59225863a60458b365e9c25f9f69362 Mon Sep 17 00:00:00 2001 From: khalifan-kfan Date: Tue, 13 Feb 2024 14:21:17 +0300 Subject: [PATCH 03/68] ui display --- .../modules/monitoring/monitoring.service.ts | 63 +++++------------- .../pages/home/monitoring.page.html | 66 ++++++++++++++++++- .../pages/home/monitoring.page.scss | 14 ++-- .../monitoring/pages/home/monitoring.page.ts | 26 ++++---- .../new/new-monitoring-forms.component.ts | 24 +------ .../supabase/data/monitoring_forms_rows.csv | 2 +- .../20240213081336_monitoring_forms_table.sql | 2 +- apps/picsa-server/supabase/types/index.ts | 2 +- 8 files changed, 106 insertions(+), 93 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts index 0fc580f6a..31e11ec42 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts @@ -2,22 +2,24 @@ 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 { PicsaNotificationService } from '@picsa/shared/services/core/notification.service'; import { SupabaseService } from '@picsa/shared/services/core/supabase'; import { IStorageEntry } from '@picsa/shared/services/core/supabase/services/supabase-storage.service'; -export type ITranslationRow = Database['public']['Tables']['translations']['Row']; +export type IMonitoringFormsRow = Database['public']['Tables']['monitoring_forms']['Row']; -export interface IResourceStorageEntry extends IStorageEntry { + + +export interface IMonitoringStorageEntry extends IStorageEntry { /** Url generated when upload to public bucket (will always be populated, even if bucket not public) */ publicUrl: string; } @Injectable({ providedIn: 'root' }) -export class TranslationDashboardService extends PicsaAsyncService { - public translations: ITranslationRow[] = []; +export class MonitoringFormsDashboardService extends PicsaAsyncService { + public forms: IMonitoringFormsRow[] = []; + public TABLE_NAME = 'monitoring_forms' public get table() { - return this.supabaseService.db.table('translations'); + return this.supabaseService.db.table(this.TABLE_NAME); } constructor(private supabaseService: SupabaseService) { @@ -26,59 +28,24 @@ export class TranslationDashboardService extends PicsaAsyncService { public override async init() { await this.supabaseService.ready(); - await this.listTranslations(); + await this.listMonitoringForms(); } - public async listTranslations() { - const { data, error } = await this.supabaseService.db.table('translations').select<'*', ITranslationRow>('*'); - if (error) { - throw error; - } - this.translations = data || []; - } - // update a translation record by ID - public async updateTranslationById(id: string, updatedData: Partial): Promise { - const { data, error } = await this.supabaseService.db.table('translations').update(updatedData).eq('id', id); + public async listMonitoringForms() { + const { data, error } = await this.supabaseService.db.table(this.TABLE_NAME).select<'*', IMonitoringFormsRow>('*'); if (error) { throw error; } - return 'Updated successfully'; + this.forms = data || []; } - // Fetch a translation record by ID - public async getTranslationById(id: string): Promise { - const { data, error } = await this.supabaseService.db.table('translations').select('*').eq('id', id).single(); + // Fetch a form record by ID + public async getTranslationById(id: string): Promise { + const { data, error } = await this.supabaseService.db.table(this.TABLE_NAME).select('*').eq('id', id).single(); if (error) { throw error; } return data; } - //delete function - public async deleteTranslationById(id: string): Promise { - const { error } = await this.supabaseService.db.table('translations').delete().eq('id', id); - - if (error) { - throw error; - } - return 'Deleted Successfully'; - } - - // In your TranslationDashboardService - public async addTranslation(translation: Partial): Promise { - translation.id = this.generateId(translation as ITranslationRow); - const { data, error } = await this.supabaseService.db.table('translations').insert([translation]); - - if (error) { - throw error; - } - return 'Added successfully'; - } - - /** Composite id generated by combination of row metadata and en text */ - private generateId(row: ITranslationRow) { - // Convert to lower case and remove non-alphanumeric characters so that minor text differences ignored - const textId = row.en.toLowerCase().replace(/[^a-z0-9]/gi, ''); - return `${row.tool}-${row.context || ''}-${textId}`; - } } diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html index aa5fbb1c0..0f899a7d1 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html @@ -1,3 +1,65 @@
- This is the monitoring page -
+
+

Monitoring Froms

+ +
+ @if(service.forms){ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Title{{ element.title }}description{{ element.description }}Form Html{{ element.enketoForm }}Fields Summary + @for (item of element.summaryFields; track $index) { + {{ item['label'] }}
+ } +
App Countries{{ element.appCountries.join(',') }}Model{{ element.enketoModel }}Date{{ element.created_at | date: 'mediumDate' }}
+ } + \ No newline at end of file diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.scss b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.scss index 496f3585e..5c6e09504 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.scss +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.scss @@ -17,11 +17,17 @@ td { cursor: pointer; } -.disallow-click{ +.disallow-click { pointer-events: none; cursor: not-allowed; } -.allow-click{ - pointer-events:all; +.allow-click { + pointer-events: all; cursor: pointer; -} \ No newline at end of file +} +.long-text-rows { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 13rem; +} diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts index 9190a9507..112f6eea0 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts @@ -4,34 +4,32 @@ import { RouterModule } from '@angular/router'; import { Router } from '@angular/router'; // eslint-disable-next-line @nx/enforce-module-boundaries import { Database } from '@picsa/server-types'; +import { IDataTableOptions, PicsaDataTableComponent } from '@picsa/shared/features/data-table'; import { DashboardMaterialModule } from '../../../../material.module'; -import { TranslationDashboardService } from '../../monitoring.service'; +import { MonitoringFormsDashboardService } from '../../monitoring.service'; export type ITranslationRow = Database['public']['Tables']['translations']['Row']; @Component({ selector: 'dashboard-translations-page', standalone: true, - imports: [CommonModule, DashboardMaterialModule, RouterModule], + imports: [CommonModule, DashboardMaterialModule, RouterModule, PicsaDataTableComponent], templateUrl: './monitoring.page.html', styleUrls: ['./monitoring.page.scss'], }) export class MonitoringPageComponent implements OnInit { - displayedColumns: string[] = ['tool', 'context', 'en', 'mw_ny', 'zm_ny', 'ke_sw', 'tj_tg', 'created_at']; + displayedColumns: string[] = [ 'title', 'description', 'summaryFields', 'appCountries', 'enketoForm', 'enketoModel', 'created_at']; - constructor(public service: TranslationDashboardService, private router: Router) {} + constructor(public service: MonitoringFormsDashboardService, private router: Router) {} + public tableOptions: IDataTableOptions = { + paginatorSizes: [25, 50], + }; ngOnInit(): void { this.service.ready(); - this.refreshTranslations(); + console.log(this.service.forms) } - goToRecord(row: ITranslationRow) { - this.router.navigate([`/translations`, row.id]); - } - - refreshTranslations() { - this.service.listTranslations().catch((error) => { - console.error('Error fetching translations:', error); - }); - } + + + } diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.ts index cdb638c4b..c63ec3297 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.ts @@ -6,7 +6,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; //import type { Database } from '@picsa/server-types'; import { DashboardMaterialModule } from '../../../../material.module'; // import { DashboardResourcesStorageLinkComponent } from '../../components/storage-link/storage-link.component'; -import { TranslationDashboardService } from '../../monitoring.service'; +import { MonitoringFormsDashboardService } from '../../monitoring.service'; //type ITranslationEntry = Database['public']['Tables']['translations']['Row']; @@ -24,27 +24,7 @@ export class NewMonitoringFormsComponent { tj_tg: string; zm_ny: string; editActionFeedbackMessage: string; - constructor(private service: TranslationDashboardService) { + constructor(private service: MonitoringFormsDashboardService) { this.service.ready(); } - submitForm() { - const data = { - en: this.en, - mw_ny: this.mw_ny, - ke_sw: this.ke_sw, - tj_tg: this.tj_tg, - zm_ny: this.zm_ny, - }; - this.service - .addTranslation(data) - .then((data) => { - if (data === 'Added successfully') { - this.editActionFeedbackMessage = 'Added successfully'; - } - }) - .catch((error) => { - console.error('Error adding translation:', error); - this.editActionFeedbackMessage = 'Failed to add a translation.'; - }); - } } diff --git a/apps/picsa-server/supabase/data/monitoring_forms_rows.csv b/apps/picsa-server/supabase/data/monitoring_forms_rows.csv index 38d850060..220db8f35 100644 --- a/apps/picsa-server/supabase/data/monitoring_forms_rows.csv +++ b/apps/picsa-server/supabase/data/monitoring_forms_rows.csv @@ -1,4 +1,4 @@ -id,created_at,tittle,description,enketoDefinition,summaryFields,appCountries,enketoForm,enketoModel,cover +id,created_at,title,description,enketoDefinition,summaryFields,appCountries,enketoForm,enketoModel,cover 2,2024-02-13 07:34:03.939328+00,Extension Worker Monitoring,Malawi,"{""hash"":""md5:dc0116ade1cd0dc0a7c0f3ce02bcbddc--d97bac4e49bda9c52dee96737583f620---1"",""media"":{},""theme"":""grid"",""maxSize"":10000000,""enketoId"":""PGpldp9m"",""languageMap"":{},""externalData"":[]}","[{""field"":""date"",""label"":""Date""},{""field"":""district"",""label"":""District""},{""field"":""EPA"",""label"":""EPA""},{""field"":""section"",""label"":""Section""},{""field"":""total_farmers"",""label"":""Total Farmers""}]","[""mw"","" ""]","
\n

E-PICSA - EW Monitoring Malawi

\n \n \n \n
District\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n
","\n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <__version__/>\n \n \n \n \n ","{""icon"":""assets/svgs/monitoring-forms/ew-monitoring.svg""}" 4,2024-02-13 07:47:56.842289+00,Complex Demo Form,demo,"{""hash"":""md5:492f8458063b6e9056d5d7fa928500ce--2851db799b6357cc49f5607b9000579d---1"",""media"":{},""theme"":""grid"",""enketoId"":""poiB"",""languageMap"":{}}","[{""field"":""_id"",""label"":""ID""}]","["" ""]","
\n

The Kitchen Sink

\n \n \n
\n
\n
\n
\n
\n
\n
Was the patient\'s blood drawn?To deselect a radio button just click it again.\n
Was the patient\'s blood drawn?Same question but with a horizontal layout\n
Please choose a response:If you have a lot of response options to display, they can be formatted into columns\n
\n
I have a sufficient energy today
\n
I am able to go to work
\n
I have no problems with self-care
\n
\n
\n
I eat a balanced diet.\n
I get a lot of exercise.\n
LABEL HERE\n
\n
\n
Please choose one or more of the following:
These are organized into nicely structured columns\n
You cannot select both c and d.
How many meals did you eat today?
\n
Please choose a response:
\n
\n
Which meals did Sarah eat today?
Hint text can go here.\n
Which meals did James eat today?
HInt text can go here.\n
What meals did Simon eat today?
HInt text can go here.\n
\n
\n
\n
\n
\n
\n
\n
Ongoing?*\n
This field is required
Expected*\n
This field is required
AE Serious?*\n
This field is required
\n
\n
\n
\n
\n
\n
What is your current level of pain?\n
Please select the blue square\n
In which state will the patient be treated?\n
\n
\n
Which is the greatest contributor to the BMI of ? Other field values are displayed within the codelist labels\n
\n
\n
Select multiple widget (don\'t pick c and d together)\n
You cannot select both c and d.
\n
\n
What a creatinine test conducted?\n
Was a creatinine test conducted?\n
Please provide the subject\'s sex:\n
Did you eat dinner today?\n
\n
\n
Did you eat dinner today?\n
\n
\n
\n
\n
\n
\n
\n
\n \n
","\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n static_instance-ABCD-0\n a\n \n \n static_instance-ABCD-1\n b\n \n \n static_instance-ABCD-2\n c\n \n \n static_instance-ABCD-3\n d\n \n \n \n \n \n static_instance-YN-0\n 1\n \n \n static_instance-YN-1\n 2\n \n \n \n \n \n static_instance-GENERIC_LIST-0\n a\n \n \n static_instance-GENERIC_LIST-1\n b\n \n \n static_instance-GENERIC_LIST-2\n c\n \n \n static_instance-GENERIC_LIST-3\n d\n \n \n static_instance-GENERIC_LIST-4\n e\n \n \n static_instance-GENERIC_LIST-5\n f\n \n \n static_instance-GENERIC_LIST-6\n g\n \n \n static_instance-GENERIC_LIST-7\n h\n \n \n static_instance-GENERIC_LIST-8\n i\n \n \n static_instance-GENERIC_LIST-9\n j\n \n \n \n \n \n static_instance-FACES-0\n 0\n \n \n static_instance-FACES-1\n 1\n \n \n static_instance-FACES-2\n 2\n \n \n static_instance-FACES-3\n 3\n \n \n static_instance-FACES-4\n 4\n \n \n \n \n \n static_instance-WHUNITS-0\n 1\n \n \n static_instance-WHUNITS-1\n 2\n \n \n \n \n \n static_instance-COLORS-0\n red\n \n \n static_instance-COLORS-1\n green\n \n \n static_instance-COLORS-2\n blue\n \n \n static_instance-COLORS-3\n yellow\n \n \n \n \n \n static_instance-ACT-0\n 0\n \n \n static_instance-ACT-1\n 1\n \n \n static_instance-ACT-2\n 2\n \n \n static_instance-ACT-3\n 3\n \n \n static_instance-ACT-4\n 4\n \n \n static_instance-ACT-5\n 5\n \n \n \n \n \n static_instance-us_states-0\n AL\n \n \n static_instance-us_states-1\n AK\n \n \n static_instance-us_states-2\n AZ\n \n \n static_instance-us_states-3\n AR\n \n \n static_instance-us_states-4\n CA\n \n \n static_instance-us_states-5\n CO\n \n \n static_instance-us_states-6\n CT\n \n \n static_instance-us_states-7\n DE\n \n \n static_instance-us_states-8\n FL\n \n \n static_instance-us_states-9\n GA\n \n \n static_instance-us_states-10\n HI\n \n \n static_instance-us_states-11\n ID\n \n \n static_instance-us_states-12\n IL\n \n \n static_instance-us_states-13\n IN\n \n \n static_instance-us_states-14\n IA\n \n \n static_instance-us_states-15\n KS\n \n \n static_instance-us_states-16\n KY\n \n \n static_instance-us_states-17\n LA\n \n \n static_instance-us_states-18\n MA\n \n \n static_instance-us_states-19\n MD\n \n \n static_instance-us_states-20\n ME\n \n \n static_instance-us_states-21\n MI\n \n \n static_instance-us_states-22\n MN\n \n \n static_instance-us_states-23\n MO\n \n \n static_instance-us_states-24\n MS\n \n \n static_instance-us_states-25\n MT\n \n \n static_instance-us_states-26\n NC\n \n \n static_instance-us_states-27\n ND\n \n \n static_instance-us_states-28\n NE\n \n \n static_instance-us_states-29\n NH\n \n \n static_instance-us_states-30\n NJ\n \n \n static_instance-us_states-31\n NM\n \n \n static_instance-us_states-32\n NV\n \n \n static_instance-us_states-33\n NY\n \n \n static_instance-us_states-34\n OH\n \n \n static_instance-us_states-35\n OK\n \n \n static_instance-us_states-36\n OR\n \n \n static_instance-us_states-37\n PA\n \n \n static_instance-us_states-38\n RI\n \n \n static_instance-us_states-39\n SC\n \n \n static_instance-us_states-40\n SD\n \n \n static_instance-us_states-41\n TN\n \n \n static_instance-us_states-42\n TX\n \n \n static_instance-us_states-43\n UT\n \n \n static_instance-us_states-44\n VA\n \n \n static_instance-us_states-45\n VT\n \n \n static_instance-us_states-46\n WA\n \n \n static_instance-us_states-47\n WI\n \n \n static_instance-us_states-48\n WV\n \n \n static_instance-us_states-49\n WY\n \n \n static_instance-us_states-50\n DC\n \n \n \n \n \n static_instance-BMIRES-0\n 2\n \n \n static_instance-BMIRES-1\n 3\n \n \n \n \n \n static_instance-SEX-0\n 1\n \n \n static_instance-SEX-1\n 2\n \n \n \n \n \n static_instance-RL-0\n 1\n \n \n static_instance-RL-1\n 2\n \n \n static_instance-RL-2\n 3\n \n \n static_instance-RL-3\n 4\n \n \n static_instance-RL-4\n 5\n \n \n \n \n \n static_instance-TG-0\n 1\n \n \n static_instance-TG-1\n 2\n \n \n static_instance-TG-2\n 3\n \n \n static_instance-TG-3\n 4\n \n \n static_instance-TG-4\n 5\n \n \n \n \n \n static_instance-CASCADE1-0\n 1\n \n \n static_instance-CASCADE1-1\n 2\n \n \n static_instance-CASCADE1-2\n 3\n \n \n static_instance-CASCADE1-3\n 4\n \n \n static_instance-CASCADE1-4\n 1\n 5\n \n \n static_instance-CASCADE1-5\n 1\n 6\n \n \n \n \n \n static_instance-MEALS-0\n 1\n \n \n static_instance-MEALS-1\n 2\n \n \n static_instance-MEALS-2\n 3\n \n \n static_instance-MEALS-3\n 4\n \n \n \n \n \n static_instance-OUT-0\n 0\n \n \n static_instance-OUT-1\n 1\n \n \n static_instance-OUT-2\n 2\n \n \n static_instance-OUT-3\n 3\n \n \n static_instance-OUT-4\n 4\n \n \n \n \n \n static_instance-ACKNOWLEDGE-0\n 1\n \n \n \n \n \n static_instance-CASCADE2-0\n 1\n 1\n \n \n static_instance-CASCADE2-1\n 1\n 2\n \n \n static_instance-CASCADE2-2\n 1\n 3\n \n \n static_instance-CASCADE2-3\n 1\n 4\n \n \n static_instance-CASCADE2-4\n 2\n 5\n \n \n static_instance-CASCADE2-5\n 2\n 6\n \n \n static_instance-CASCADE2-6\n 1\n 7\n 1\n \n \n static_instance-CASCADE2-7\n 1\n 8\n 1\n \n \n \n \n \n static_instance-CASCADE3-0\n 1\n 1\n 1\n \n \n static_instance-CASCADE3-1\n 1\n 2\n 1\n \n \n static_instance-CASCADE3-2\n 1\n 3\n 1\n \n \n static_instance-CASCADE3-3\n 1\n 4\n 1\n \n \n static_instance-CASCADE3-4\n 1\n 5\n 1\n \n \n static_instance-CASCADE3-5\n 1\n 6\n 1\n \n \n static_instance-CASCADE3-6\n 1\n 7\n 2\n \n \n static_instance-CASCADE3-7\n 1\n 8\n 2\n \n \n static_instance-CASCADE3-8\n 1\n 9\n 2\n \n \n static_instance-CASCADE3-9\n 1\n 10\n 2\n \n \n static_instance-CASCADE3-10\n 2\n 11\n 7\n \n \n static_instance-CASCADE3-11\n 2\n 12\n 7\n \n \n static_instance-CASCADE3-12\n 2\n 13\n 7\n \n \n static_instance-CASCADE3-13\n 2\n 14\n 7\n \n \n static_instance-CASCADE3-14\n 2\n 15\n 7\n \n \n static_instance-CASCADE3-15\n 2\n 16\n 8\n \n \n static_instance-CASCADE3-16\n 2\n 17\n 8\n \n \n static_instance-CASCADE3-17\n 2\n 18\n 8\n \n \n static_instance-CASCADE3-18\n 19\n \n \n static_instance-CASCADE3-19\n 20\n \n \n \n \n \n static_instance-UNITS-0\n 1\n \n \n static_instance-UNITS-1\n 2\n \n \n \n \n \n static_instance-BODYSYSTEM-0\n 1\n \n \n static_instance-BODYSYSTEM-1\n 2\n \n \n static_instance-BODYSYSTEM-2\n 3\n \n \n static_instance-BODYSYSTEM-3\n 4\n \n \n \n \n \n static_instance-DATE_PRECISION-0\n 1\n \n \n static_instance-DATE_PRECISION-1\n 2\n \n \n static_instance-DATE_PRECISION-2\n 3\n \n \n \n \n \n static_instance-AGREEMENT-0\n 1\n \n \n static_instance-AGREEMENT-1\n 2\n \n \n static_instance-AGREEMENT-2\n 3\n \n \n static_instance-AGREEMENT-3\n 4\n \n \n static_instance-AGREEMENT-4\n 5\n \n \n ","{""icon"":""""}" 3,2024-02-13 07:37:01.044168+00,Extension Worker Monitoring,Zambia,"{""hash"":""md5:3166205f2f70f1e29ea86d6dbcd8b36c--d97bac4e49bda9c52dee96737583f620---1"",""media"":{},""theme"":""grid"",""maxSize"":10000000,""enketoId"":""znNPkvlJ"",""languageMap"":{},""externalData"":[]}","[{""field"":""date"",""label"":""Date""},{""field"":""district"",""label"":""District""},{""field"":""EPA"",""label"":""EPA""},{""field"":""section"",""label"":""Section""},{""field"":""total_farmers"",""label"":""Total Farmers""}]","[""zm"","" ""]","
\n

E-PICSA - EW Monitoring Zambia

\n \n \n \n
Organisation\n
\n
District\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n
","\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <__version__/>\n \n \n \n \n ","{""icon"":""assets/svgs/monitoring-forms/ew-monitoring.svg""}" \ No newline at end of file diff --git a/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql b/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql index e96c063ad..14fbe6b92 100644 --- a/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql +++ b/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql @@ -2,7 +2,7 @@ create table public.monitoring_forms ( id bigint generated by default as identity, created_at timestamp with time zone not null default now(), - tittle text not null, + title text not null, description character varying null, "enketoDefinition" jsonb null, "summaryFields" jsonb[] null, diff --git a/apps/picsa-server/supabase/types/index.ts b/apps/picsa-server/supabase/types/index.ts index abb5adbd4..09eb08733 100644 --- a/apps/picsa-server/supabase/types/index.ts +++ b/apps/picsa-server/supabase/types/index.ts @@ -145,7 +145,7 @@ export interface Database { enketoModel: string | null id: number summaryFields: Json[] | null - tittle: string + title: string } Insert: { appCountries?: string[] | null From ee24b7285519390afec08ddc15b7999e7f6bb66e Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Wed, 14 Feb 2024 22:22:10 -0800 Subject: [PATCH 04/68] refactor: example table with custom templates --- .../pages/home/monitoring.page.html | 118 ++++++++++-------- 1 file changed, 65 insertions(+), 53 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html index 0f899a7d1..09d0ba208 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html @@ -1,65 +1,77 @@
-
-

Monitoring Froms

- -
- @if(service.forms){ - - - - - - +
+

Monitoring Froms

+ +
+ @if(service.forms){ +

PICSA Table

+ + + @for (item of values; track $index) { {{ item['label'] }}
+ } +
+
- - - - - +

Legacy Table

+
Title{{ element.title }}description{{ element.description }}
+ + + + + - - + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - -
Title{{ element.title }}description{{ element.description }}Form Html{{ element.enketoForm }}Form Html{{ element.enketoForm }}Fields Summary - @for (item of element.summaryFields; track $index) { - {{ item['label'] }}
- } -
Fields Summary + @for (item of element.summaryFields; track $index) { {{ item['label'] }}
+ } +
App Countries{{ element.appCountries.join(',') }}App Countries{{ element.appCountries.join(',') }}Model{{ element.enketoModel }}Model{{ element.enketoModel }}Date{{ element.created_at | date: 'mediumDate' }}Date{{ element.created_at | date: 'mediumDate' }}
- } -
\ No newline at end of file + + + + + } + From ec3467e1089ddc7aaceac70c058bb90a56a9c499 Mon Sep 17 00:00:00 2001 From: khalifan-kfan Date: Tue, 13 Feb 2024 14:33:25 +0300 Subject: [PATCH 05/68] attempt to add custom table --- .../src/app/modules/monitoring/pages/home/monitoring.page.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts index 112f6eea0..d7d59d69e 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts @@ -19,7 +19,6 @@ export type ITranslationRow = Database['public']['Tables']['translations']['Row' }) export class MonitoringPageComponent implements OnInit { displayedColumns: string[] = [ 'title', 'description', 'summaryFields', 'appCountries', 'enketoForm', 'enketoModel', 'created_at']; - constructor(public service: MonitoringFormsDashboardService, private router: Router) {} public tableOptions: IDataTableOptions = { paginatorSizes: [25, 50], @@ -28,8 +27,4 @@ export class MonitoringPageComponent implements OnInit { this.service.ready(); console.log(this.service.forms) } - - - - } From cc31894568108a9b302660f8c82b927de6e803e3 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 8 Feb 2024 20:03:05 -0800 Subject: [PATCH 06/68] feat: add dashboard nav accordion --- .../dashboard/src/app/app.component.html | 29 +++++++++++++++++-- .../dashboard/src/app/app.component.ts | 13 ++++++++- .../dashboard/src/app/material.module.ts | 4 ++- .../climate-data/climate-data.module.ts | 7 ++++- 4 files changed, 48 insertions(+), 5 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/app.component.html b/apps/picsa-apps/dashboard/src/app/app.component.html index 64c995e17..257f24f67 100644 --- a/apps/picsa-apps/dashboard/src/app/app.component.html +++ b/apps/picsa-apps/dashboard/src/app/app.component.html @@ -26,11 +26,36 @@ - @for (link of navLinks; track link.href) { + @for (link of navLinks; track link.href) { @if(link.children){ + + + + + {{ link.label }} + + + @for(child of link.children || []; track $index){ + + {{ child.label }} + } + + } @else { + {{ link.label }} - } + } }
Global Admin
diff --git a/apps/picsa-apps/dashboard/src/app/app.component.ts b/apps/picsa-apps/dashboard/src/app/app.component.ts index 29e7db337..e6cf70ca4 100644 --- a/apps/picsa-apps/dashboard/src/app/app.component.ts +++ b/apps/picsa-apps/dashboard/src/app/app.component.ts @@ -8,6 +8,7 @@ import { DashboardMaterialModule } from './material.module'; interface INavLink { label: string; href: string; + children?: INavLink[]; isActive?: boolean; } @@ -31,8 +32,18 @@ export class AppComponent implements AfterViewInit { href: '/resources', }, { - label: 'Climate Data', + label: 'Climate', href: '/climate-data', + children: [ + { + label: 'Station Data', + href: '/station', + }, + { + label: 'Forecasts', + href: '/forecasts', + }, + ], }, // { // label: 'Crop Information', diff --git a/apps/picsa-apps/dashboard/src/app/material.module.ts b/apps/picsa-apps/dashboard/src/app/material.module.ts index 8a3c8b562..80c145a1f 100644 --- a/apps/picsa-apps/dashboard/src/app/material.module.ts +++ b/apps/picsa-apps/dashboard/src/app/material.module.ts @@ -1,6 +1,7 @@ import { NgModule } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatChipsModule } from '@angular/material/chips'; +import { MatExpansionModule } from '@angular/material/expansion'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; @@ -15,6 +16,7 @@ import { MatToolbarModule } from '@angular/material/toolbar'; const matModules = [ MatButtonModule, MatChipsModule, + MatExpansionModule, MatFormFieldModule, MatIconModule, MatInputModule, @@ -24,7 +26,7 @@ const matModules = [ MatStepperModule, MatTableModule, MatTabsModule, - MatToolbarModule + MatToolbarModule, ]; @NgModule({ diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data.module.ts b/apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data.module.ts index 9b5eda51d..6540d6407 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data.module.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data.module.ts @@ -12,10 +12,15 @@ import { StationPageComponent } from './pages/station/station-page.component'; RouterModule.forChild([ { path: '', + redirectTo: 'station', + pathMatch: 'full', + }, + { + path: 'station', component: ClimateDataHomeComponent, }, { - path: ':stationId', + path: 'station/:stationId', component: StationPageComponent, }, ]), From c72c9ebde72cb0c69e1c00bef4867e266ad0cfd5 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 8 Feb 2024 20:03:48 -0800 Subject: [PATCH 07/68] chore: code tidying --- .../station/components/rainfall-summary/rainfall-summary.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/components/rainfall-summary/rainfall-summary.ts b/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/components/rainfall-summary/rainfall-summary.ts index 9fe7655b5..e28923b66 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/components/rainfall-summary/rainfall-summary.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/components/rainfall-summary/rainfall-summary.ts @@ -56,14 +56,14 @@ export class RainfallSummaryComponent implements AfterViewInit { public async refreshData() { const { station_id, country_code } = this.service.activeStation; - const { response, data, error } = await this.api.useMeta('rainfallSummary').POST('/v1/annual_rainfall_summaries/', { + const { data, error } = await this.api.useMeta('rainfallSummary').POST('/v1/annual_rainfall_summaries/', { body: { country: `${country_code}` as any, station_id: `${station_id}`, summaries: ['annual_rain', 'start_rains', 'end_rains', 'end_season', 'seasonal_rain', 'seasonal_length'], }, }); - console.log('rainfallSummary', { response, data, error }); + if (error) throw error; this.loadData(data as any); // TODO - generalise way to persist db updates from api queries const dbRes = await this.supabase.db.table('climate_products').upsert({ From 0324fde89c238937d690aa067f742613f56f6784 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 8 Feb 2024 20:04:33 -0800 Subject: [PATCH 08/68] style: dashboard climate api status spinner --- .../pages/home/climate-data-home.component.html | 10 ++++++++-- .../pages/home/climate-data-home.component.scss | 13 +++++++++++++ .../pages/home/climate-data-home.component.ts | 3 ++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.html b/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.html index 39cc37626..92c61a7be 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.html +++ b/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.html @@ -3,8 +3,14 @@

Climate Data

@if(api.meta.serverStatus; as meta){
- Server Status - {{ meta.rawResponse?.status }} + Server Status + + @if(meta.rawResponse; as response){ + {{ response.status }} + } @else { + + } +
} diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.scss b/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.scss index c620acfc0..69317ae3d 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.scss +++ b/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.scss @@ -1,3 +1,8 @@ +.server-status { + display: flex; + gap: 8px; + align-items: center; +} .status-code { color: white; padding: 8px; @@ -6,6 +11,14 @@ &[data-status='200'] { background: green; } + width: 32px; + height: 24px; + display: flex; + justify-content: center; + align-items: center; +} +mat-progress-spinner.mat-white { + --mdc-circular-progress-active-indicator-color: white; } table.station-table { diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.ts b/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.ts index 094aa4b0c..77f87a464 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.ts @@ -1,5 +1,6 @@ import { CommonModule } from '@angular/common'; import { Component, OnInit } from '@angular/core'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatTableModule } from '@angular/material/table'; import { RouterModule } from '@angular/router'; import { IMapMarker, PicsaMapComponent } from '@picsa/shared/features/map/map'; @@ -10,7 +11,7 @@ import { ClimateDataApiService } from '../../climate-data-api.service'; @Component({ selector: 'dashboard-climate-data-home', standalone: true, - imports: [CommonModule, MatTableModule, RouterModule, PicsaMapComponent], + imports: [CommonModule, MatTableModule, RouterModule, PicsaMapComponent, MatProgressSpinnerModule], templateUrl: './climate-data-home.component.html', styleUrls: ['./climate-data-home.component.scss'], }) From f65eeccda9f385c763c4a2d61859b2ed58adec12 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 8 Feb 2024 20:06:21 -0800 Subject: [PATCH 09/68] feat: integrate climate station list api --- .../climate-data/climate-data.service.ts | 25 ++- .../app/modules/climate-data/types/api.d.ts | 166 +++++++++++++++--- 2 files changed, 162 insertions(+), 29 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data.service.ts b/apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data.service.ts index e71e8c69e..bf3a75d09 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data.service.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data.service.ts @@ -37,8 +37,9 @@ export class ClimateDataDashboardService extends PicsaAsyncService { public override async init() { await this.supabaseService.ready(); - await this.checkStatus(); await this.listStations(); + // Initialise other services without await to allow parallel requests + this.checkApiStatus(); this.subscribeToRouteChanges(); } @@ -54,30 +55,36 @@ export class ClimateDataDashboardService extends PicsaAsyncService { private subscribeToRouteChanges() { // Use merged router as service cannot access route params directly like component - ngRouterMergedSnapshot$(this.router).subscribe(({ params }) => { + ngRouterMergedSnapshot$(this.router).subscribe(async ({ params }) => { if (params.stationId) { + await this.ready(); this.setActiveStation(parseInt(params.stationId)); } }); } - private async checkStatus() { + private async checkApiStatus() { await this.api.useMeta('serverStatus').GET('/v1/status/'); } - private async listStations() { + private async listStations(allowRefresh = true) { // 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; } - if (data.length === 0) { - this.notificationService.showUserNotification({ - matIcon: 'warning', - message: 'climate_stations_rows must be imported into database for this feature to work', - }); + if (data.length === 0 && allowRefresh) { + await this.refreshStationList(); + return this.listStations(false); } this.stations = data || []; } + private async refreshStationList() { + const { data, error } = await this.api.useMeta('station').GET('/v1/station/'); + if (error) throw error; + // TODO - general mapping doc + const stations: IStationRow[] = data; + await this.supabaseService.db.table('climate_stations').upsert(stations); + } } diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/types/api.d.ts b/apps/picsa-apps/dashboard/src/app/modules/climate-data/types/api.d.ts index 52a0bb695..ba4357a5b 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate-data/types/api.d.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate-data/types/api.d.ts @@ -32,19 +32,29 @@ export interface paths { /** Get Season Start Probabilities */ post: operations["get_season_start_probabilities_v1_season_start_probabilities__post"]; }; + "/v1/extremes_summaries/": { + /** Get Extremes Summaries */ + post: operations["get_extremes_summaries_v1_extremes_summaries__post"]; + }; "/v1/station/": { - /** - * Read Stations - * @description Retrieve stations. - */ + /** Read Stations */ get: operations["read_stations_v1_station__get"]; }; - "/v1/station/{id}": { - /** - * Read Station - * @description Get station by ID. - */ - get: operations["read_station_v1_station__id__get"]; + "/v1/station/{country}": { + /** Read Stations */ + get: operations["read_stations_v1_station__country__get"]; + }; + "/v1/station/{country}/{station_id}": { + /** Read Stations */ + get: operations["read_stations_v1_station__country___station_id__get"]; + }; + "/v1/forecasts/": { + /** Get Forecasts */ + get: operations["get_forecasts_v1_forecasts__get"]; + }; + "/v1/forecasts/{country}/{file_name}": { + /** Get Forecasts */ + get: operations["get_forecasts_v1_forecasts__country___file_name__get"]; }; } @@ -122,6 +132,29 @@ export interface components { /** Start Before Season */ start_before_season?: boolean; }; + /** ExtremesSummariesParameters */ + ExtremesSummariesParameters: { + /** + * Country + * @default zm + * @enum {string} + */ + country?: "zm" | "mw"; + /** + * Station Id + * @default test_1 + */ + station_id?: string; + /** + * Summaries + * @default [ + * "extremes_rain", + * "extremes_tmin", + * "extremes_tmax" + * ] + */ + summaries?: ("extremes_rain" | "extremes_tmin" | "extremes_tmax")[]; + }; /** HTTPValidationError */ HTTPValidationError: { /** Detail */ @@ -165,6 +198,26 @@ export interface components { /** Start Dates */ start_dates?: number[]; }; + /** Station */ + Station: { + /** + * Country Code + * @enum {string} + */ + country_code: "zm" | "mw"; + /** District */ + district: string; + /** Elevation */ + elevation: number; + /** Latitude */ + latitude: number; + /** Longitude */ + longitude: number; + /** Station Id */ + station_id: number; + /** Station Name */ + station_name: string; + }; /** ValidationError */ ValidationError: { /** Location */ @@ -312,11 +365,46 @@ export interface operations { }; }; }; - /** - * Read Stations - * @description Retrieve stations. - */ + /** Get Extremes Summaries */ + get_extremes_summaries_v1_extremes_summaries__post: { + requestBody: { + content: { + "application/json": components["schemas"]["ExtremesSummariesParameters"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + /** Read Stations */ read_stations_v1_station__get: { + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": components["schemas"]["Station"][]; + }; + }; + }; + }; + /** Read Stations */ + read_stations_v1_station__country__get: { + parameters: { + path: { + country: "zm" | "mw"; + }; + }; responses: { /** @description Successful Response */ 200: { @@ -324,16 +412,54 @@ export interface operations { "application/json": unknown; }; }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; }; }; - /** - * Read Station - * @description Get station by ID. - */ - read_station_v1_station__id__get: { + /** Read Stations */ + read_stations_v1_station__country___station_id__get: { + parameters: { + path: { + country: "zm" | "mw"; + station_id: string; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": unknown; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + /** Get Forecasts */ + get_forecasts_v1_forecasts__get: { + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": unknown; + }; + }; + }; + }; + /** Get Forecasts */ + get_forecasts_v1_forecasts__country___file_name__get: { parameters: { path: { - id: number; + country: "zm" | "mw"; + file_name: string; }; }; responses: { From 5875e5aa05b16cb216e5a230182536f60f19cff4 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 8 Feb 2024 20:16:17 -0800 Subject: [PATCH 10/68] chore: station-data rename --- .../modules/{climate-data => climate}/climate-data-api.service.ts | 0 .../app/modules/{climate-data => climate}/climate-data.module.ts | 0 .../app/modules/{climate-data => climate}/climate-data.service.ts | 0 .../pages/home/climate-data-home.component.html | 0 .../pages/home/climate-data-home.component.scss | 0 .../pages/home/climate-data-home.component.spec.ts | 0 .../pages/home/climate-data-home.component.ts | 0 .../station/components/rainfall-summary/rainfall-summary.html | 0 .../station/components/rainfall-summary/rainfall-summary.scss | 0 .../station/components/rainfall-summary/rainfall-summary.spec.ts | 0 .../pages/station/components/rainfall-summary/rainfall-summary.ts | 0 .../pages/station/station-page.component.html | 0 .../pages/station/station-page.component.scss | 0 .../pages/station/station-page.component.spec.ts | 0 .../pages/station/station-page.component.ts | 0 .../src/app/modules/{climate-data => climate}/types/README.md | 0 .../src/app/modules/{climate-data => climate}/types/api.d.ts | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/climate-data-api.service.ts (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/climate-data.module.ts (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/climate-data.service.ts (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/pages/home/climate-data-home.component.html (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/pages/home/climate-data-home.component.scss (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/pages/home/climate-data-home.component.spec.ts (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/pages/home/climate-data-home.component.ts (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/pages/station/components/rainfall-summary/rainfall-summary.html (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/pages/station/components/rainfall-summary/rainfall-summary.scss (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/pages/station/components/rainfall-summary/rainfall-summary.spec.ts (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/pages/station/components/rainfall-summary/rainfall-summary.ts (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/pages/station/station-page.component.html (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/pages/station/station-page.component.scss (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/pages/station/station-page.component.spec.ts (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/pages/station/station-page.component.ts (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/types/README.md (100%) rename apps/picsa-apps/dashboard/src/app/modules/{climate-data => climate}/types/api.d.ts (100%) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data-api.service.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate-data-api.service.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data-api.service.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/climate-data-api.service.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data.module.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate-data.module.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data.module.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/climate-data.module.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data.service.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate-data.service.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/climate-data.service.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/climate-data.service.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.html b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.html similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.html rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.html diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.scss b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.scss similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.scss rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.scss diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.spec.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.spec.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.spec.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/home/climate-data-home.component.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/components/rainfall-summary/rainfall-summary.html b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.html similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/components/rainfall-summary/rainfall-summary.html rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.html diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/components/rainfall-summary/rainfall-summary.scss b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.scss similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/components/rainfall-summary/rainfall-summary.scss rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.scss diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/components/rainfall-summary/rainfall-summary.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.spec.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/components/rainfall-summary/rainfall-summary.spec.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.spec.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/components/rainfall-summary/rainfall-summary.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/components/rainfall-summary/rainfall-summary.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/station-page.component.html b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.html similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/station-page.component.html rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.html diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/station-page.component.scss b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.scss similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/station-page.component.scss rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.scss diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/station-page.component.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.spec.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/station-page.component.spec.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.spec.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/station-page.component.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/pages/station/station-page.component.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/types/README.md b/apps/picsa-apps/dashboard/src/app/modules/climate/types/README.md similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/types/README.md rename to apps/picsa-apps/dashboard/src/app/modules/climate/types/README.md diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate-data/types/api.d.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/types/api.d.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate-data/types/api.d.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/types/api.d.ts From 5b80f05174376b75e14fae3b0980fbb7791918a1 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 8 Feb 2024 20:17:25 -0800 Subject: [PATCH 11/68] chore: service rename --- .../{climate-data-api.service.ts => climate-api.service.ts} | 0 .../modules/climate/{climate-data.module.ts => climate.module.ts} | 0 .../climate/{climate-data.service.ts => climate.service.ts} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename apps/picsa-apps/dashboard/src/app/modules/climate/{climate-data-api.service.ts => climate-api.service.ts} (100%) rename apps/picsa-apps/dashboard/src/app/modules/climate/{climate-data.module.ts => climate.module.ts} (100%) rename apps/picsa-apps/dashboard/src/app/modules/climate/{climate-data.service.ts => climate.service.ts} (100%) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/climate-data-api.service.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/climate-data-api.service.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/climate-data.module.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/climate-data.module.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/climate-data.service.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.service.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/climate-data.service.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/climate.service.ts From 35ece419318088881a249e485e003fc1cd85450a Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 8 Feb 2024 20:20:23 -0800 Subject: [PATCH 12/68] chore: code tidying --- apps/picsa-apps/dashboard/src/app/app.component.ts | 2 +- apps/picsa-apps/dashboard/src/app/app.routes.ts | 4 ++-- .../src/app/modules/climate/climate-api.service.ts | 2 +- .../dashboard/src/app/modules/climate/climate.module.ts | 2 +- .../dashboard/src/app/modules/climate/climate.service.ts | 6 +++--- .../climate/pages/home/climate-data-home.component.ts | 6 +++--- .../components/rainfall-summary/rainfall-summary.ts | 8 ++++---- .../climate/pages/station/station-page.component.ts | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/app.component.ts b/apps/picsa-apps/dashboard/src/app/app.component.ts index e6cf70ca4..aa90584cd 100644 --- a/apps/picsa-apps/dashboard/src/app/app.component.ts +++ b/apps/picsa-apps/dashboard/src/app/app.component.ts @@ -33,7 +33,7 @@ export class AppComponent implements AfterViewInit { }, { label: 'Climate', - href: '/climate-data', + href: '/climate', children: [ { label: 'Station Data', diff --git a/apps/picsa-apps/dashboard/src/app/app.routes.ts b/apps/picsa-apps/dashboard/src/app/app.routes.ts index 88de4bfae..bac064eb9 100644 --- a/apps/picsa-apps/dashboard/src/app/app.routes.ts +++ b/apps/picsa-apps/dashboard/src/app/app.routes.ts @@ -6,8 +6,8 @@ export const appRoutes: Route[] = [ 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: 'climate', + loadChildren: () => import('./modules/climate/climate.module').then((m) => m.ClimateModule), }, { path: 'translations', diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts index 414381324..096579cf5 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts @@ -35,7 +35,7 @@ interface IMetaEntry{ * ``` * */ @Injectable({ providedIn: 'root' }) -export class ClimateDataApiService { +export class ClimateApiService { /** Request additional meta by id */ public meta:Record={} diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts index 6540d6407..72914f0b7 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts @@ -26,4 +26,4 @@ import { StationPageComponent } from './pages/station/station-page.component'; ]), ], }) -export class ClimateDataModule {} +export class ClimateModule {} diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/climate.service.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.service.ts index bf3a75d09..56e953827 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/climate.service.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.service.ts @@ -8,7 +8,7 @@ import { SupabaseService } from '@picsa/shared/services/core/supabase'; import { IStorageEntry } from '@picsa/shared/services/core/supabase/services/supabase-storage.service'; import { ngRouterMergedSnapshot$ } from '@picsa/utils/angular'; -import { ClimateDataApiService } from './climate-data-api.service'; +import { ClimateApiService } from './climate-api.service'; export type IStationRow = Database['public']['Tables']['climate_stations']['Row']; @@ -20,14 +20,14 @@ export interface IResourceStorageEntry extends IStorageEntry { export type IResourceEntry = Database['public']['Tables']['resources']['Row']; @Injectable({ providedIn: 'root' }) -export class ClimateDataDashboardService extends PicsaAsyncService { +export class ClimateService extends PicsaAsyncService { public apiStatus: number; public stations: IStationRow[] = []; public activeStation: IStationRow; constructor( private supabaseService: SupabaseService, - private api: ClimateDataApiService, + private api: ClimateApiService, private notificationService: PicsaNotificationService, private router: Router ) { diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.ts index 77f87a464..d084a296d 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.ts @@ -5,8 +5,8 @@ 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'; -import { ClimateDataApiService } from '../../climate-data-api.service'; +import { ClimateService, IStationRow } from '../../climate.service'; +import { ClimateApiService } from '../../climate-api.service'; @Component({ selector: 'dashboard-climate-data-home', @@ -20,7 +20,7 @@ export class ClimateDataHomeComponent implements OnInit { public mapMarkers: IMapMarker[]; - constructor(public service: ClimateDataDashboardService, public api: ClimateDataApiService) {} + constructor(public service: ClimateService, public api: ClimateApiService) {} async ngOnInit() { await this.service.ready(); diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.ts index e28923b66..a04eefe77 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.ts @@ -6,8 +6,8 @@ import { MatTabsModule } from '@angular/material/tabs'; import { IDataTableOptions, PicsaDataTableComponent } from '@picsa/shared/features/data-table'; import { SupabaseService } from '@picsa/shared/services/core/supabase'; -import { ClimateDataDashboardService } from '../../../../climate-data.service'; -import { ClimateDataApiService } from '../../../../climate-data-api.service'; +import { ClimateService } from '../../../../climate.service'; +import { ClimateApiService } from '../../../../climate-api.service'; interface IRainfallSummary { data: any[]; @@ -25,8 +25,8 @@ interface IRainfallSummary { export class RainfallSummaryComponent implements AfterViewInit { public summary: IRainfallSummary = { data: [], metadata: {} }; constructor( - public api: ClimateDataApiService, - private service: ClimateDataDashboardService, + public api: ClimateApiService, + private service: ClimateService, private cdr: ChangeDetectorRef, private supabase: SupabaseService ) {} diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.ts index 85d6a6439..c1d399d24 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.ts @@ -2,7 +2,7 @@ import { CommonModule } from '@angular/common'; import { Component, OnInit } from '@angular/core'; import { MatProgressBarModule } from '@angular/material/progress-bar'; -import { ClimateDataDashboardService } from '../../climate-data.service'; +import { ClimateService } from '../../climate.service'; import { RainfallSummaryComponent } from './components/rainfall-summary/rainfall-summary'; @Component({ @@ -24,7 +24,7 @@ export class StationPageComponent implements OnInit { }; } - constructor(private service: ClimateDataDashboardService) {} + constructor(private service: ClimateService) {} async ngOnInit() { await this.service.ready(); From 7996c41870568493807da168b5219d1272a15fc1 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 8 Feb 2024 20:22:44 -0800 Subject: [PATCH 13/68] chore: rename station-details page --- .../components/rainfall-summary/rainfall-summary.html | 0 .../components/rainfall-summary/rainfall-summary.scss | 0 .../components/rainfall-summary/rainfall-summary.spec.ts | 0 .../components/rainfall-summary/rainfall-summary.ts | 0 .../{station => station-details}/station-page.component.html | 0 .../{station => station-details}/station-page.component.scss | 0 .../{station => station-details}/station-page.component.spec.ts | 0 .../pages/{station => station-details}/station-page.component.ts | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/{station => station-details}/components/rainfall-summary/rainfall-summary.html (100%) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/{station => station-details}/components/rainfall-summary/rainfall-summary.scss (100%) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/{station => station-details}/components/rainfall-summary/rainfall-summary.spec.ts (100%) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/{station => station-details}/components/rainfall-summary/rainfall-summary.ts (100%) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/{station => station-details}/station-page.component.html (100%) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/{station => station-details}/station-page.component.scss (100%) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/{station => station-details}/station-page.component.spec.ts (100%) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/{station => station-details}/station-page.component.ts (100%) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.html b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.html similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.html rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.html diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.scss b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.scss similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.scss rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.scss diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.spec.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.spec.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.spec.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/components/rainfall-summary/rainfall-summary.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.html b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.html similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.html rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.html diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.scss b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.scss similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.scss rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.scss diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.spec.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.spec.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.spec.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station-page.component.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.ts From e9721aebebb2ca5f3bb800a61a07837f479955f7 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 8 Feb 2024 20:25:21 -0800 Subject: [PATCH 14/68] refactor: station details page --- .../src/app/modules/climate/climate.module.ts | 4 ++-- .../app/modules/climate/climate.service.ts | 2 +- ...nt.html => station-details.component.html} | 0 ...nt.scss => station-details.component.scss} | 0 .../station-details.component.spec.ts | 21 +++++++++++++++++++ ...ponent.ts => station-details.component.ts} | 8 +++---- .../station-page.component.spec.ts | 21 ------------------- 7 files changed, 28 insertions(+), 28 deletions(-) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/{station-page.component.html => station-details.component.html} (100%) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/{station-page.component.scss => station-details.component.scss} (100%) create mode 100644 apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-details.component.spec.ts rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/{station-page.component.ts => station-details.component.ts} (78%) delete mode 100644 apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.spec.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts index 72914f0b7..522a1eb3b 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts @@ -3,7 +3,7 @@ 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'; +import { StationDetailsPageComponent } from './pages/station-details/station-details.component'; @NgModule({ declarations: [], @@ -21,7 +21,7 @@ import { StationPageComponent } from './pages/station/station-page.component'; }, { path: 'station/:stationId', - component: StationPageComponent, + component: StationDetailsPageComponent, }, ]), ], diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/climate.service.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.service.ts index 56e953827..ab39ba18d 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/climate.service.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.service.ts @@ -39,7 +39,7 @@ export class ClimateService extends PicsaAsyncService { await this.supabaseService.ready(); await this.listStations(); // Initialise other services without await to allow parallel requests - this.checkApiStatus(); + // this.checkApiStatus(); this.subscribeToRouteChanges(); } diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.html b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-details.component.html similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.html rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-details.component.html diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.scss b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-details.component.scss similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.scss rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-details.component.scss diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-details.component.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-details.component.spec.ts new file mode 100644 index 000000000..59311ffc2 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-details.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { StationDetailsPageComponent } from './station-details.component'; + +describe('StationDetailsPageComponent', () => { + let component: StationDetailsPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [StationDetailsPageComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(StationDetailsPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-details.component.ts similarity index 78% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-details.component.ts index c1d399d24..6fc50d3c5 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-details.component.ts @@ -6,13 +6,13 @@ import { ClimateService } from '../../climate.service'; import { RainfallSummaryComponent } from './components/rainfall-summary/rainfall-summary'; @Component({ - selector: 'dashboard-station-page', + selector: 'dashboard-station-details', standalone: true, imports: [CommonModule, MatProgressBarModule, RainfallSummaryComponent], - templateUrl: './station-page.component.html', - styleUrls: ['./station-page.component.scss'], + templateUrl: './station-details.component.html', + styleUrls: ['./station-details.component.scss'], }) -export class StationPageComponent implements OnInit { +export class StationDetailsPageComponent implements OnInit { public get station() { return this.service.activeStation; } diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.spec.ts deleted file mode 100644 index dcdb50ac9..000000000 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/station-page.component.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { StationPageComponent } from './station-page.component'; - -describe('StationPageComponent', () => { - let component: StationPageComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [StationPageComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(StationPageComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); From 6c89be9c45a67aaa95d4feb6abe8deba7d87f197 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 8 Feb 2024 20:25:57 -0800 Subject: [PATCH 15/68] chore: rename station home --- .../pages/{home => station}/climate-data-home.component.html | 0 .../pages/{home => station}/climate-data-home.component.scss | 0 .../pages/{home => station}/climate-data-home.component.spec.ts | 0 .../pages/{home => station}/climate-data-home.component.ts | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/{home => station}/climate-data-home.component.html (100%) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/{home => station}/climate-data-home.component.scss (100%) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/{home => station}/climate-data-home.component.spec.ts (100%) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/{home => station}/climate-data-home.component.ts (100%) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.html b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.html similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.html rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.html diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.scss b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.scss similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.scss rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.scss diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.spec.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.spec.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.spec.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.ts similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/home/climate-data-home.component.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.ts From 02745a1718d9fae5d05cc9509dbcb53e74bb932b Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 8 Feb 2024 20:27:56 -0800 Subject: [PATCH 16/68] chore: code tidying --- .../src/app/modules/climate/climate.module.ts | 4 ++-- .../climate-data-home.component.spec.ts | 22 ------------------- ....component.html => station.component.html} | 0 ....component.scss => station.component.scss} | 0 .../pages/station/station.component.spec.ts | 22 +++++++++++++++++++ ...home.component.ts => station.component.ts} | 8 +++---- 6 files changed, 28 insertions(+), 28 deletions(-) delete mode 100644 apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.spec.ts rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/{climate-data-home.component.html => station.component.html} (100%) rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/{climate-data-home.component.scss => station.component.scss} (100%) create mode 100644 apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.spec.ts rename apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/{climate-data-home.component.ts => station.component.ts} (83%) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts index 522a1eb3b..98223b72e 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts @@ -2,7 +2,7 @@ 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 { ClimateStationPageComponent } from './pages/station/station.component'; import { StationDetailsPageComponent } from './pages/station-details/station-details.component'; @NgModule({ @@ -17,7 +17,7 @@ import { StationDetailsPageComponent } from './pages/station-details/station-det }, { path: 'station', - component: ClimateDataHomeComponent, + component: ClimateStationPageComponent, }, { path: 'station/:stationId', diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.spec.ts deleted file mode 100644 index d643e4000..000000000 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ClimateDataHomeComponent } from './climate-data-home.component'; - -describe('ClimateDataHomeComponent', () => { - let component: ClimateDataHomeComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ClimateDataHomeComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(ClimateDataHomeComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.html b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.html similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.html rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.html diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.scss b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.scss similarity index 100% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.scss rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.scss diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.spec.ts new file mode 100644 index 000000000..c4e61cbcc --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ClimateStationPageComponent } from './station.component'; + +describe('ClimateStationPageComponent', () => { + let component: ClimateStationPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ClimateStationPageComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ClimateStationPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.ts similarity index 83% rename from apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.ts rename to apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.ts index d084a296d..114bfc419 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/climate-data-home.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.ts @@ -9,13 +9,13 @@ import { ClimateService, IStationRow } from '../../climate.service'; import { ClimateApiService } from '../../climate-api.service'; @Component({ - selector: 'dashboard-climate-data-home', + selector: 'dashboard-climate-station-page', standalone: true, imports: [CommonModule, MatTableModule, RouterModule, PicsaMapComponent, MatProgressSpinnerModule], - templateUrl: './climate-data-home.component.html', - styleUrls: ['./climate-data-home.component.scss'], + templateUrl: './station.component.html', + styleUrls: ['./station.component.scss'], }) -export class ClimateDataHomeComponent implements OnInit { +export class ClimateStationPageComponent implements OnInit { public displayedColumns: (keyof IStationRow)[] = ['station_id', 'station_name']; public mapMarkers: IMapMarker[]; From 58ff7a67e574103d8290538f9648d163a9838be0 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 8 Feb 2024 22:18:56 -0800 Subject: [PATCH 17/68] feat: forecast page placeholder --- .../dashboard/src/app/app.component.html | 16 +++++++------- .../dashboard/src/app/app.component.ts | 7 +------ .../src/app/modules/climate/climate.module.ts | 5 +++++ .../pages/forecast/forecast.component.html | 1 + .../pages/forecast/forecast.component.scss | 0 .../pages/forecast/forecast.component.spec.ts | 21 +++++++++++++++++++ .../pages/forecast/forecast.component.ts | 12 +++++++++++ .../rainfall-summary/rainfall-summary.ts | 1 + 8 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.html create mode 100644 apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.scss create mode 100644 apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.spec.ts create mode 100644 apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.ts diff --git a/apps/picsa-apps/dashboard/src/app/app.component.html b/apps/picsa-apps/dashboard/src/app/app.component.html index 257f24f67..c9bca6e1d 100644 --- a/apps/picsa-apps/dashboard/src/app/app.component.html +++ b/apps/picsa-apps/dashboard/src/app/app.component.html @@ -28,14 +28,14 @@ @for (link of navLinks; track link.href) { @if(link.children){ - - + + {{ link.label }} diff --git a/apps/picsa-apps/dashboard/src/app/app.component.ts b/apps/picsa-apps/dashboard/src/app/app.component.ts index aa90584cd..69916e573 100644 --- a/apps/picsa-apps/dashboard/src/app/app.component.ts +++ b/apps/picsa-apps/dashboard/src/app/app.component.ts @@ -9,7 +9,6 @@ interface INavLink { label: string; href: string; children?: INavLink[]; - isActive?: boolean; } @Component({ @@ -23,10 +22,6 @@ export class AppComponent implements AfterViewInit { title = 'picsa-apps-dashboard'; navLinks: INavLink[] = [ - // { - // label: 'Home', - // href: '', - // }, { label: 'Resources', href: '/resources', @@ -41,7 +36,7 @@ export class AppComponent implements AfterViewInit { }, { label: 'Forecasts', - href: '/forecasts', + href: '/forecast', }, ], }, diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts index 98223b72e..8430cb47f 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.module.ts @@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; +import { ClimateForecastPageComponent } from './pages/forecast/forecast.component'; import { ClimateStationPageComponent } from './pages/station/station.component'; import { StationDetailsPageComponent } from './pages/station-details/station-details.component'; @@ -19,6 +20,10 @@ import { StationDetailsPageComponent } from './pages/station-details/station-det path: 'station', component: ClimateStationPageComponent, }, + { + path: 'forecast', + component: ClimateForecastPageComponent, + }, { path: 'station/:stationId', component: StationDetailsPageComponent, diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.html b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.html new file mode 100644 index 000000000..0dd7d8241 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.html @@ -0,0 +1 @@ +
diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.scss b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.spec.ts new file mode 100644 index 000000000..603a95581 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ForecastComponent } from './forecast.component'; + +describe('ForecastComponent', () => { + let component: ForecastComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ForecastComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ForecastComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.ts new file mode 100644 index 000000000..5e8315259 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.ts @@ -0,0 +1,12 @@ +import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +@Component({ + selector: 'dashboard-climate-forecast', + standalone: true, + imports: [CommonModule, RouterModule], + templateUrl: './forecast.component.html', + styleUrls: ['./forecast.component.scss'], +}) +export class ClimateForecastPageComponent {} diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.ts index a04eefe77..65a15e3b1 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.ts @@ -63,6 +63,7 @@ export class RainfallSummaryComponent implements AfterViewInit { summaries: ['annual_rain', 'start_rains', 'end_rains', 'end_season', 'seasonal_rain', 'seasonal_length'], }, }); + this.cdr.markForCheck(); if (error) throw error; this.loadData(data as any); // TODO - generalise way to persist db updates from api queries From edb1d2b489a4aa75c3432aeffd4f34472cd183e5 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Fri, 9 Feb 2024 12:23:06 -0800 Subject: [PATCH 18/68] feat: add climate forecasts db endpoint --- .../20240209065921_climate_forecasts_create.sql | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 apps/picsa-server/supabase/migrations/20240209065921_climate_forecasts_create.sql diff --git a/apps/picsa-server/supabase/migrations/20240209065921_climate_forecasts_create.sql b/apps/picsa-server/supabase/migrations/20240209065921_climate_forecasts_create.sql new file mode 100644 index 000000000..c210a201f --- /dev/null +++ b/apps/picsa-server/supabase/migrations/20240209065921_climate_forecasts_create.sql @@ -0,0 +1,13 @@ +create table + public.climate_forecasts ( + id character varying not null, + district text null, + filename text not null, + language text not null, + type text null, + date_modified timestamp with time zone not null, + country character varying null, + storage_file uuid null, + constraint climate_forecasts_pkey primary key (id), + constraint climate_forecasts_storage_file_fkey foreign key (storage_file) references storage.objects (id) on update cascade on delete cascade + ) tablespace pg_default; \ No newline at end of file From cac1acdfff8a09baed764c976e7c68280515fb7b Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Fri, 9 Feb 2024 12:32:39 -0800 Subject: [PATCH 19/68] chore: update type definitions --- .../src/app/modules/climate/types/README.md | 2 +- .../src/app/modules/climate/types/api.d.ts | 94 ++++++++++++++++--- .../src/app/modules/climate/types/db.d.ts | 3 + .../src/app/modules/climate/types/index.ts | 2 + apps/picsa-server/supabase/types/index.ts | 58 ++++++++++-- 5 files changed, 136 insertions(+), 23 deletions(-) create mode 100644 apps/picsa-apps/dashboard/src/app/modules/climate/types/db.d.ts create mode 100644 apps/picsa-apps/dashboard/src/app/modules/climate/types/index.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/types/README.md b/apps/picsa-apps/dashboard/src/app/modules/climate/types/README.md index 4e86aec40..af81a2f9a 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/types/README.md +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/types/README.md @@ -5,5 +5,5 @@ The climate data api runs on a remote server and exports endpoint definitions us 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" +npx openapi-typescript "https://api.epicsa.idems.international/openapi.json" -o "apps\picsa-apps\dashboard\src\app\modules\climate\types\api.d.ts" ``` diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/types/api.d.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/types/api.d.ts index ba4357a5b..e2f1001ab 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/types/api.d.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/types/api.d.ts @@ -8,7 +8,7 @@ export interface paths { "/v1/status/": { /** * Get Status - * @description Check server up and authorized to access data + * @description Check server up */ get: operations["get_status_v1_status__get"]; }; @@ -49,12 +49,25 @@ export interface paths { get: operations["read_stations_v1_station__country___station_id__get"]; }; "/v1/forecasts/": { - /** Get Forecasts */ - get: operations["get_forecasts_v1_forecasts__get"]; + /** + * List Endpoints + * @description List available forecast endpoints + */ + get: operations["list_endpoints_v1_forecasts__get"]; }; - "/v1/forecasts/{country}/{file_name}": { - /** Get Forecasts */ - get: operations["get_forecasts_v1_forecasts__country___file_name__get"]; + "/v1/forecasts/{country_code}": { + /** + * List Forecasts + * @description Get available forecasts for country + */ + get: operations["list_forecasts_v1_forecasts__country_code__get"]; + }; + "/v1/forecasts/{country_code}/{file_name}": { + /** + * Get Forecast + * @description Get forecast file + */ + get: operations["get_forecast_v1_forecasts__country_code___file_name__get"]; }; } @@ -155,6 +168,30 @@ export interface components { */ summaries?: ("extremes_rain" | "extremes_tmin" | "extremes_tmax")[]; }; + /** Forecast */ + Forecast: { + /** + * Date Modified + * Format: date-time + */ + date_modified: string; + /** District */ + district?: string; + /** Filename */ + filename: string; + /** Id */ + id: string; + /** + * Language Code + * @enum {string} + */ + language_code?: "en" | "ny"; + /** + * Type + * @enum {string} + */ + type?: "downscale_forecast" | "annual_forecast"; + }; /** HTTPValidationError */ HTTPValidationError: { /** Detail */ @@ -243,7 +280,7 @@ export interface operations { /** * Get Status - * @description Check server up and authorized to access data + * @description Check server up */ get_status_v1_status__get: { responses: { @@ -443,22 +480,53 @@ export interface operations { }; }; }; - /** Get Forecasts */ - get_forecasts_v1_forecasts__get: { + /** + * List Endpoints + * @description List available forecast endpoints + */ + list_endpoints_v1_forecasts__get: { responses: { /** @description Successful Response */ 200: { content: { - "application/json": unknown; + "application/json": string[]; }; }; }; }; - /** Get Forecasts */ - get_forecasts_v1_forecasts__country___file_name__get: { + /** + * List Forecasts + * @description Get available forecasts for country + */ + list_forecasts_v1_forecasts__country_code__get: { parameters: { path: { - country: "zm" | "mw"; + country_code: "zm" | "mw"; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + content: { + "application/json": components["schemas"]["Forecast"][]; + }; + }; + /** @description Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + /** + * Get Forecast + * @description Get forecast file + */ + get_forecast_v1_forecasts__country_code___file_name__get: { + parameters: { + path: { + country_code: "zm" | "mw"; file_name: string; }; }; diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/types/db.d.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/types/db.d.ts new file mode 100644 index 000000000..f20e33626 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/types/db.d.ts @@ -0,0 +1,3 @@ +import { Database } from '@picsa/server-types'; + +export type IForecastRow = Database['public']['Tables']['climate_forecasts']['Row']; diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/types/index.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/types/index.ts new file mode 100644 index 000000000..62dd5da1c --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/types/index.ts @@ -0,0 +1,2 @@ +export * from './api'; +export * from './db'; diff --git a/apps/picsa-server/supabase/types/index.ts b/apps/picsa-server/supabase/types/index.ts index 09eb08733..629c86e22 100644 --- a/apps/picsa-server/supabase/types/index.ts +++ b/apps/picsa-server/supabase/types/index.ts @@ -34,26 +34,69 @@ export interface Database { } public: { Tables: { + climate_forecasts: { + Row: { + country: string | null + date_modified: string + district: string | null + filename: string + id: string + language: string + storage_file: string | null + type: string | null + } + Insert: { + country?: string | null + date_modified: string + district?: string | null + filename: string + id: string + language: string + storage_file?: string | null + type?: string | null + } + Update: { + country?: string | null + date_modified?: string + district?: string | null + filename?: string + id?: string + language?: string + storage_file?: string | null + type?: string | null + } + Relationships: [ + { + foreignKeyName: "climate_forecasts_storage_file_fkey" + columns: ["storage_file"] + referencedRelation: "objects" + referencedColumns: ["id"] + }, + { + foreignKeyName: "climate_forecasts_storage_file_fkey" + columns: ["storage_file"] + referencedRelation: "storage_objects" + referencedColumns: ["id"] + } + ] + } climate_products: { Row: { created_at: string data: Json - id: number - station_id: number | null + station_id: number type: string } Insert: { created_at?: string data: Json - id?: number - station_id?: number | null + station_id: number type: string } Update: { created_at?: string data?: Json - id?: number - station_id?: number | null + station_id?: number type?: string } Relationships: [ @@ -314,7 +357,6 @@ export interface Database { metadata: Json | null name: string | null owner: string | null - owner_id: string | null path_tokens: string[] | null updated_at: string | null version: string | null @@ -327,7 +369,6 @@ export interface Database { metadata?: Json | null name?: string | null owner?: string | null - owner_id?: string | null path_tokens?: string[] | null updated_at?: string | null version?: string | null @@ -340,7 +381,6 @@ export interface Database { metadata?: Json | null name?: string | null owner?: string | null - owner_id?: string | null path_tokens?: string[] | null updated_at?: string | null version?: string | null From 78ca1ee0e139dba66078ebf58dce7d0aa244163a Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Fri, 9 Feb 2024 12:37:22 -0800 Subject: [PATCH 20/68] chore: code tidying --- .../src/app/modules/climate/climate-api.service.ts | 2 -- .../dashboard/src/app/modules/climate/climate.service.ts | 7 +------ .../modules/climate/pages/station/station.component.ts | 3 ++- .../dashboard/src/app/modules/climate/types/db.d.ts | 7 ++++++- .../resources/pages/create/resource-create.component.ts | 9 +++------ .../src/app/modules/resources/resources.service.ts | 8 ++++---- 6 files changed, 16 insertions(+), 20 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts index 096579cf5..34d9d65d0 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts @@ -12,8 +12,6 @@ type ICallbackClient = (id:string)=>ReturnType> /** Type-safe http client with added support for callbacks */ type IClient = ReturnType> & {useMeta:ICallbackClient} - - interface IMetaEntry{ status:'pending' | 'success' | 'error' | 'unknown', rawResponse?:Response, diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/climate.service.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.service.ts index ab39ba18d..5c787ec56 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/climate.service.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/climate.service.ts @@ -1,7 +1,5 @@ import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; -// eslint-disable-next-line @nx/enforce-module-boundaries -import { Database } from '@picsa/server-types'; import { PicsaAsyncService } from '@picsa/shared/services/asyncService.service'; import { PicsaNotificationService } from '@picsa/shared/services/core/notification.service'; import { SupabaseService } from '@picsa/shared/services/core/supabase'; @@ -9,16 +7,13 @@ import { IStorageEntry } from '@picsa/shared/services/core/supabase/services/sup import { ngRouterMergedSnapshot$ } from '@picsa/utils/angular'; import { ClimateApiService } from './climate-api.service'; - -export type IStationRow = Database['public']['Tables']['climate_stations']['Row']; +import { IStationRow } from './types'; 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 ClimateService extends PicsaAsyncService { public apiStatus: number; diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.ts index 114bfc419..842c053f9 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.ts @@ -5,8 +5,9 @@ import { MatTableModule } from '@angular/material/table'; import { RouterModule } from '@angular/router'; import { IMapMarker, PicsaMapComponent } from '@picsa/shared/features/map/map'; -import { ClimateService, IStationRow } from '../../climate.service'; +import { ClimateService } from '../../climate.service'; import { ClimateApiService } from '../../climate-api.service'; +import { IStationRow } from '../../types'; @Component({ selector: 'dashboard-climate-station-page', diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/types/db.d.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/types/db.d.ts index f20e33626..4dc76d5a0 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/types/db.d.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/types/db.d.ts @@ -1,3 +1,8 @@ -import { Database } from '@picsa/server-types'; +// eslint-disable-next-line @nx/enforce-module-boundaries +import type { Database } from '@picsa/server-types'; export type IForecastRow = Database['public']['Tables']['climate_forecasts']['Row']; + +export type IResourceRow = Database['public']['Tables']['resources']['Row']; + +export type IStationRow = Database['public']['Tables']['climate_stations']['Row']; diff --git a/apps/picsa-apps/dashboard/src/app/modules/resources/pages/create/resource-create.component.ts b/apps/picsa-apps/dashboard/src/app/modules/resources/pages/create/resource-create.component.ts index 2d38af24c..268f43a4c 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/resources/pages/create/resource-create.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/resources/pages/create/resource-create.component.ts @@ -3,8 +3,6 @@ import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; import { PICSAFormValidators } from '@picsa/forms'; -// eslint-disable-next-line @nx/enforce-module-boundaries -import type { Database } from '@picsa/server-types'; import { IUploadResult, SupabaseStoragePickerDirective, @@ -13,11 +11,10 @@ import { import { IStorageEntry } from '@picsa/shared/services/core/supabase/services/supabase-storage.service'; import { DashboardMaterialModule } from '../../../../material.module'; +import { IResourceRow } from '../../../climate/types'; import { DashboardResourcesStorageLinkComponent } from '../../components/storage-link/storage-link.component'; import { ResourcesDashboardService } from '../../resources.service'; -type IResourceEntry = Database['public']['Tables']['resources']['Row']; - @Component({ selector: 'dashboard-resource-create', standalone: true, @@ -67,7 +64,7 @@ export class ResourceCreateComponent implements OnInit { await this.service.ready(); const { id } = this.route.snapshot.params; if (id) { - const { data } = await this.service.table.select<'*', IResourceEntry>('*').eq('id', id); + const { data } = await this.service.table.select<'*', IResourceRow>('*').eq('id', id); const resource = data?.[0]; if (resource) { this.populateResource(resource); @@ -86,7 +83,7 @@ export class ResourceCreateComponent implements OnInit { console.log({ data, error }); } - private populateResource(resource: IResourceEntry) { + private populateResource(resource: IResourceRow) { this.resourceType = resource.type as any; console.log('populate resource', resource); switch (resource.type) { diff --git a/apps/picsa-apps/dashboard/src/app/modules/resources/resources.service.ts b/apps/picsa-apps/dashboard/src/app/modules/resources/resources.service.ts index 0638c4133..438711585 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/resources/resources.service.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/resources/resources.service.ts @@ -7,18 +7,18 @@ import { SupabaseService } from '@picsa/shared/services/core/supabase'; import { IStorageEntry } from '@picsa/shared/services/core/supabase/services/supabase-storage.service'; import { arrayToHashmap } from '@picsa/utils'; +import { IResourceRow } from '../climate/types'; + 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 ResourcesDashboardService extends PicsaAsyncService { private storageFiles: IResourceStorageEntry[] = []; public storageFilesHashmap: Record = {}; - public readonly resources = signal([]); + public readonly resources = signal([]); public get table() { return this.supabaseService.db.table('resources'); @@ -104,7 +104,7 @@ export class ResourcesDashboardService extends PicsaAsyncService { } private async listResources() { - const { data, error } = await this.supabaseService.db.table('resources').select<'*', IResourceEntry>('*'); + const { data, error } = await this.supabaseService.db.table('resources').select<'*', IResourceRow>('*'); if (error) { throw error; } From f57d21320187daadcf7809af4610958f9d2986a2 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Fri, 9 Feb 2024 14:24:18 -0800 Subject: [PATCH 21/68] chore: update type definitions --- .../dashboard/src/app/modules/climate/types/db.d.ts | 4 ++++ .../20240209065921_climate_forecasts_create.sql | 4 ++-- apps/picsa-server/supabase/types/index.ts | 12 ++++++------ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/types/db.d.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/types/db.d.ts index 4dc76d5a0..9f2783bc0 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/types/db.d.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/types/db.d.ts @@ -1,7 +1,11 @@ // eslint-disable-next-line @nx/enforce-module-boundaries import type { Database } from '@picsa/server-types'; +export type IClimateProductRow = Database['public']['Tables']['climate_products']['Row']; +export type IClimateProductInsert = Database['public']['Tables']['climate_products']['Insert']; + export type IForecastRow = Database['public']['Tables']['climate_forecasts']['Row']; +export type IForecastInsert = Database['public']['Tables']['climate_forecasts']['Insert']; export type IResourceRow = Database['public']['Tables']['resources']['Row']; diff --git a/apps/picsa-server/supabase/migrations/20240209065921_climate_forecasts_create.sql b/apps/picsa-server/supabase/migrations/20240209065921_climate_forecasts_create.sql index c210a201f..cfbd90036 100644 --- a/apps/picsa-server/supabase/migrations/20240209065921_climate_forecasts_create.sql +++ b/apps/picsa-server/supabase/migrations/20240209065921_climate_forecasts_create.sql @@ -3,10 +3,10 @@ create table id character varying not null, district text null, filename text not null, - language text not null, + language_code text null, type text null, date_modified timestamp with time zone not null, - country character varying null, + country_code character varying null, storage_file uuid null, constraint climate_forecasts_pkey primary key (id), constraint climate_forecasts_storage_file_fkey foreign key (storage_file) references storage.objects (id) on update cascade on delete cascade diff --git a/apps/picsa-server/supabase/types/index.ts b/apps/picsa-server/supabase/types/index.ts index 629c86e22..fdb2283ac 100644 --- a/apps/picsa-server/supabase/types/index.ts +++ b/apps/picsa-server/supabase/types/index.ts @@ -36,32 +36,32 @@ export interface Database { Tables: { climate_forecasts: { Row: { - country: string | null + country_code: string | null date_modified: string district: string | null filename: string id: string - language: string + language_code: string | null storage_file: string | null type: string | null } Insert: { - country?: string | null + country_code?: string | null date_modified: string district?: string | null filename: string id: string - language: string + language_code?: string | null storage_file?: string | null type?: string | null } Update: { - country?: string | null + country_code?: string | null date_modified?: string district?: string | null filename?: string id?: string - language?: string + language_code?: string | null storage_file?: string | null type?: string | null } From 9f303b584ff44cfcd599e7671b1af9124212eb90 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Tue, 13 Feb 2024 22:06:26 -0800 Subject: [PATCH 22/68] feat: api status component --- .../components/api-status/api-status.html | 6 ++ .../components/api-status/api-status.scss | 21 ++++ .../components/api-status/api-status.spec.ts | 21 ++++ .../components/api-status/api-status.ts | 96 +++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.html create mode 100644 apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.scss create mode 100644 apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.spec.ts create mode 100644 apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.html b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.html new file mode 100644 index 000000000..5455221e7 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.html @@ -0,0 +1,6 @@ + + + diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.scss b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.scss new file mode 100644 index 000000000..50e062446 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.scss @@ -0,0 +1,21 @@ +mat-icon.spin { + animation: spin 2s linear infinite; +} + +@-webkit-keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + } +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.spec.ts new file mode 100644 index 000000000..31e012139 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DashboardClimateApiStatusComponent } from './api-status'; + +describe('DashboardClimateApiStatusComponent', () => { + let component: DashboardClimateApiStatusComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DashboardClimateApiStatusComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(DashboardClimateApiStatusComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.ts new file mode 100644 index 000000000..e84c4260a --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.ts @@ -0,0 +1,96 @@ +import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + EventEmitter, + Input, + OnDestroy, + Output, +} from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { PicsaNotificationService } from '@picsa/shared/services/core/notification.service'; +import { Subject, Subscription, takeUntil } from 'rxjs'; + +import { ClimateService } from '../../climate.service'; +import { ClimateApiService } from '../../climate-api.service'; + +export type IStatus = 'pending' | 'success' | 'error' | 'unknown'; + +/** + * Component used to display status of ongoing API requests + * ``` + * + * ``` + */ +@Component({ + selector: 'dashboard-climate-api-status', + standalone: true, + imports: [CommonModule, MatButtonModule, MatIconModule], + templateUrl: './api-status.html', + styleUrls: ['./api-status.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DashboardClimateApiStatusComponent implements OnDestroy { + public status: IStatus = 'pending'; + public code?: number; + + private componentDestroyed$ = new Subject(); + private subscription: Subscription; + + @Output() handleRefresh = new EventEmitter(); + + constructor( + public api: ClimateApiService, + public service: ClimateService, + private notificationService: PicsaNotificationService, + private cdr: ChangeDetectorRef + ) {} + + /** Unique id of API request to monitor for status updates */ + @Input() set clientId(id: string) { + // clear any previous subscription + if (this.subscription) this.subscription.unsubscribe(); + // subscribe to any requests sent via client and update UI accordingly + const client = this.api.getObservableClient(id); + this.subscription = client.$.pipe(takeUntil(this.componentDestroyed$)).subscribe((response) => { + this.status = this.getCallbackStatus(response?.status); + this.code = response?.status; + this.cdr.markForCheck(); + if (response && this.status === 'error') { + this.showCustomFetchErrorMessage(id, response); + } + }); + } + ngOnDestroy(): void { + this.componentDestroyed$.next(true); + this.componentDestroyed$.complete(); + } + + private getCallbackStatus(statusCode?: number): IStatus { + if (!statusCode) return 'unknown'; + if (100 <= statusCode && statusCode <= 199) return 'pending'; + if (200 <= statusCode && statusCode <= 299) return 'success'; + if (400 <= statusCode && statusCode <= 599) return 'error'; + return 'unknown'; + } + + /** 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'`, + }); + } + } +} From e58ff038a0994a6191625d45a9ad0a6fcb3044d3 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Tue, 13 Feb 2024 22:09:07 -0800 Subject: [PATCH 23/68] refactor: climate api service client --- .../modules/climate/climate-api.service.ts | 96 +++++++------------ 1 file changed, 36 insertions(+), 60 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts index 34d9d65d0..c9f50214d 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts @@ -1,32 +1,27 @@ import { Injectable } from '@angular/core'; -import { PicsaNotificationService } from '@picsa/shared/services/core/notification.service'; import createClient from 'openapi-fetch'; +import { BehaviorSubject, Subject } from 'rxjs'; 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> - /** Type-safe http client with added support for callbacks */ -type IClient = ReturnType> & {useMeta:ICallbackClient} - -interface IMetaEntry{ - status:'pending' | 'success' | 'error' | 'unknown', - rawResponse?:Response, -} - +export type IApiClient = ReturnType> & { $:Subject} /** * 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 + * Use observable client that can also be accessed by api-status component * ```ts - * const {response, data, error} = await api.useMeta('myRequestId').POST(...) + * await api.getObservableClient('myRequestId').POST(...) * ``` + * ```html + * + * ``` + * * Use default client without additional callbacks * ```ts * const {response, data, error} = await api.client.POST(...) @@ -34,64 +29,45 @@ interface IMetaEntry{ * */ @Injectable({ providedIn: 'root' }) export class ClimateApiService { - - /** Request additional meta by id */ - public meta:Record={} /** Http client with type-definitions for API endpoints */ - public client:IClient + public client:IApiClient - constructor(private notificationService:PicsaNotificationService) { - const client = createClient({ baseUrl: API_ENDPOINT,mode:'cors' }); - this.client = {...client,useMeta:()=>{ - return client - }} - } + /** List of custom clients generated to be shared across observers */ + private observableClients:Record = {} + constructor() { + this.client = this.createObservableClient('_default') + } - /** - * 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({ baseUrl: API_ENDPOINT,mode:'cors',fetch:customFetch }); - return customClient + /** + * Retrive an instance of the api client with a specific ID so that request updates can be subscribed to + * by any other services or components accessing via the same id. Creates new client if not existing + */ + public getObservableClient(clientId?:string):IApiClient{ + if(clientId){ + return this.observableClients[clientId] || this.createObservableClient(clientId) + } + return this.client } - /** Create a custom implementation of fetch client to handle status updates and notifications */ - private createCustomFetchClient(id:string){ - return async (...args:Parameters)=>{ - this.meta[id]={status:'pending'} + private createObservableClient(clientId:string){ + const $ = new BehaviorSubject(undefined) + const customFetch = async (...args:Parameters)=>{ + // send a custom response with 102 status code to inform that request has been sent but is pending + $.next({status:102} as Response) 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) - } + $.next(response) return response } + const baseClient = createClient({ baseUrl: API_ENDPOINT,mode:'cors',fetch:customFetch }); + const client:IApiClient ={...baseClient, $} + this.observableClients[clientId] = client + return client } - /** 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' - } + + + } From 20ebcbe9b25a3b131968980d59288eb15895fa7a Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Tue, 13 Feb 2024 23:12:55 -0800 Subject: [PATCH 24/68] style: api status component --- .../components/api-status/api-status.html | 15 ++++-- .../components/api-status/api-status.scss | 50 +++++++++++++++++++ 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.html b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.html index 5455221e7..f0a206e9b 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.html +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.html @@ -1,6 +1,11 @@ - - +
+ {{code}} + @if(options.labels?.ready; as readyLabel){ +
{{readyLabel}}
+ } @if(options.events?.refresh; as refreshEvent){ + + } +
diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.scss b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.scss index 50e062446..718cb6020 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.scss +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.scss @@ -1,3 +1,53 @@ +.status-container { + display: flex; + align-items: center; + justify-content: center; +} + +.status-label, +button.status-refresh-button, +.status-code { + --border-color: var(--color-light); + height: 30px; + padding: 8px; + display: flex; + justify-content: center; + align-items: center; + border-top: 1px solid var(--border-color); + border-bottom: 1px solid var(--border-color); + border-radius: 0; +} + +.status-code { + width: 24px; + color: white; + background: var(--color-light); + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + &[data-status='success'] { + --border-color: hsl(120, 25%, 50%); + background: hsl(120, 25%, 50%); + } + &[data-status='error'] { + --border-color: hsl(0, 60%, 65%); + background: hsl(0, 60%, 65%); + } +} +.status-label { + border-right: 1px solid var(--border-color); +} + +button.status-refresh-button { + --mdc-icon-button-state-layer-size: 40px; + border-right: 1px solid var(--border-color); + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +// mat-progress-spinner.mat-white { +// --mdc-circular-progress-active-indicator-color: white; +// } + mat-icon.spin { animation: spin 2s linear infinite; } From 84cb2680bb17fb03470ecfdaaadaf65aa4740ed2 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Tue, 13 Feb 2024 23:13:26 -0800 Subject: [PATCH 25/68] chore: code tidying --- .../components/api-status/api-status.ts | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.ts index e84c4260a..bb374fafe 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.ts @@ -1,13 +1,5 @@ import { CommonModule } from '@angular/common'; -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - EventEmitter, - Input, - OnDestroy, - Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { PicsaNotificationService } from '@picsa/shared/services/core/notification.service'; @@ -16,7 +8,17 @@ import { Subject, Subscription, takeUntil } from 'rxjs'; import { ClimateService } from '../../climate.service'; import { ClimateApiService } from '../../climate-api.service'; -export type IStatus = 'pending' | 'success' | 'error' | 'unknown'; +export type IStatus = 'pending' | 'success' | 'error' | 'ready'; + +export interface IApiStatusOptions { + labels?: { + ready?: string; + error?: string; + }; + events?: { + refresh?: () => void; + }; +} /** * Component used to display status of ongoing API requests @@ -39,8 +41,6 @@ export class DashboardClimateApiStatusComponent implements OnDestroy { private componentDestroyed$ = new Subject(); private subscription: Subscription; - @Output() handleRefresh = new EventEmitter(); - constructor( public api: ClimateApiService, public service: ClimateService, @@ -48,6 +48,8 @@ export class DashboardClimateApiStatusComponent implements OnDestroy { private cdr: ChangeDetectorRef ) {} + @Input() options: IApiStatusOptions = {}; + /** Unique id of API request to monitor for status updates */ @Input() set clientId(id: string) { // clear any previous subscription @@ -56,7 +58,10 @@ export class DashboardClimateApiStatusComponent implements OnDestroy { const client = this.api.getObservableClient(id); this.subscription = client.$.pipe(takeUntil(this.componentDestroyed$)).subscribe((response) => { this.status = this.getCallbackStatus(response?.status); - this.code = response?.status; + // only assign success and error codes + if (this.status === 'error' || this.status === 'success') { + this.code = response?.status; + } this.cdr.markForCheck(); if (response && this.status === 'error') { this.showCustomFetchErrorMessage(id, response); @@ -69,11 +74,11 @@ export class DashboardClimateApiStatusComponent implements OnDestroy { } private getCallbackStatus(statusCode?: number): IStatus { - if (!statusCode) return 'unknown'; + if (!statusCode) return 'ready'; if (100 <= statusCode && statusCode <= 199) return 'pending'; if (200 <= statusCode && statusCode <= 299) return 'success'; if (400 <= statusCode && statusCode <= 599) return 'error'; - return 'unknown'; + return 'ready'; } /** Show error message when using custom fetch with callbacks */ From 82f1f5fcf8011b16bbd7f408311ec068f893b952 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Tue, 13 Feb 2024 23:15:59 -0800 Subject: [PATCH 26/68] chore: code tidying --- .../rainfall-summary/rainfall-summary.html | 5 +--- .../rainfall-summary/rainfall-summary.scss | 22 ------------------ .../pages/station/station.component.html | 13 +---------- .../pages/station/station.component.scss | 23 ------------------- .../pages/station/station.component.ts | 20 +++++++++++++++- 5 files changed, 21 insertions(+), 62 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.html b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.html index 613ca95f3..0465b6405 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.html +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.html @@ -1,9 +1,6 @@

Rainfall Summary

- +
diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.scss b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.scss index 58125726c..5d4e87f30 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.scss +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station-details/components/rainfall-summary/rainfall-summary.scss @@ -1,25 +1,3 @@ :host { display: block; } - -mat-icon.spin { - animation: spin 2s linear infinite; -} - -@-webkit-keyframes spin { - 0% { - -webkit-transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - } -} - -@keyframes spin { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } -} diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.html b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.html index 92c61a7be..03e9679de 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.html +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.html @@ -1,18 +1,7 @@

Climate Data

- @if(api.meta.serverStatus; as meta){ -
- Server Status - - @if(meta.rawResponse; as response){ - {{ response.status }} - } @else { - - } - -
- } +

Stations

diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.scss b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.scss index 69317ae3d..bfe89e0d1 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.scss +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.scss @@ -1,26 +1,3 @@ -.server-status { - display: flex; - gap: 8px; - align-items: center; -} -.status-code { - color: white; - padding: 8px; - border-radius: 4px; - background: gray; - &[data-status='200'] { - background: green; - } - width: 32px; - height: 24px; - display: flex; - justify-content: center; - align-items: center; -} -mat-progress-spinner.mat-white { - --mdc-circular-progress-active-indicator-color: white; -} - table.station-table { max-height: 50vh; display: block; diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.ts index 842c053f9..743843c43 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/station/station.component.ts @@ -7,12 +7,20 @@ import { IMapMarker, PicsaMapComponent } from '@picsa/shared/features/map/map'; import { ClimateService } from '../../climate.service'; import { ClimateApiService } from '../../climate-api.service'; +import { DashboardClimateApiStatusComponent, IApiStatusOptions } from '../../components/api-status/api-status'; import { IStationRow } from '../../types'; @Component({ selector: 'dashboard-climate-station-page', standalone: true, - imports: [CommonModule, MatTableModule, RouterModule, PicsaMapComponent, MatProgressSpinnerModule], + imports: [ + CommonModule, + DashboardClimateApiStatusComponent, + MatTableModule, + RouterModule, + PicsaMapComponent, + MatProgressSpinnerModule, + ], templateUrl: './station.component.html', styleUrls: ['./station.component.scss'], }) @@ -21,6 +29,11 @@ export class ClimateStationPageComponent implements OnInit { public mapMarkers: IMapMarker[]; + public apiStatusOptions: IApiStatusOptions = { + events: { refresh: () => this.getApiStatus() }, + labels: { ready: 'Server Status' }, + }; + constructor(public service: ClimateService, public api: ClimateApiService) {} async ngOnInit() { @@ -29,5 +42,10 @@ export class ClimateStationPageComponent implements OnInit { latlng: [m.latitude as number, m.longitude as number], number: m.station_id, })); + this.getApiStatus(); + } + + public getApiStatus() { + this.api.getObservableClient('serverStatus').GET('/v1/status/'); } } From 8b6ea0e0a5d8e7c9824d64e3dae892c14ef10a11 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Wed, 14 Feb 2024 08:33:58 -0800 Subject: [PATCH 27/68] feat: wip forecast component --- .../components/api-status/api-status.html | 3 +- .../components/api-status/api-status.scss | 24 ++++----- .../components/api-status/api-status.ts | 15 ++++-- .../pages/forecast/forecast.component.html | 10 +++- .../pages/forecast/forecast.component.ts | 52 +++++++++++++++++-- 5 files changed, 82 insertions(+), 22 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.html b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.html index f0a206e9b..468845a64 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.html +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.html @@ -1,7 +1,8 @@
+ @if(options.showStatusCode){ {{code}} - @if(options.labels?.ready; as readyLabel){ + } @if(options.labels?.ready; as readyLabel){
{{readyLabel}}
} @if(options.events?.refresh; as refreshEvent){ +
diff --git a/apps/picsa-apps/dashboard/src/styles.scss b/apps/picsa-apps/dashboard/src/styles.scss index 0029378bb..a06c718d9 100644 --- a/apps/picsa-apps/dashboard/src/styles.scss +++ b/apps/picsa-apps/dashboard/src/styles.scss @@ -12,13 +12,13 @@ Made available from project.json }, ``` */ - -@import 'themes'; -@import 'variables'; +@import 'animations'; @import 'fonts'; @import 'layout'; -@import 'typography'; @import 'overrides'; +@import 'themes'; +@import 'typography'; +@import 'variables'; mat-sidenav-content > * { display: contents; diff --git a/libs/theme/src/_animations.scss b/libs/theme/src/_animations.scss new file mode 100644 index 000000000..bca415c58 --- /dev/null +++ b/libs/theme/src/_animations.scss @@ -0,0 +1,22 @@ +// Provide perpetual spin animation to any mat-icon +mat-icon.spin { + animation: spin 2s linear infinite; +} + +@-webkit-keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + } +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/libs/theme/src/_index.scss b/libs/theme/src/_index.scss index 0fde0dd35..0689ecb7f 100644 --- a/libs/theme/src/_index.scss +++ b/libs/theme/src/_index.scss @@ -3,9 +3,10 @@ */ // shared variables -@import 'themes'; -@import 'variables'; +@import 'animations'; @import 'fonts'; @import 'layout'; -@import 'typography'; @import 'overrides'; +@import 'themes'; +@import 'typography'; +@import 'variables'; From 1c774ea41c99a6a48fd571584d3cca4691138a0a Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 15 Feb 2024 15:14:37 -0800 Subject: [PATCH 35/68] feat: storage putFile and blob response handling --- .../modules/climate/climate-api.service.ts | 18 ++++++++-- .../components/api-status/api-status.ts | 35 ++++++++++++------- .../services/supabase-storage.service.ts | 16 ++++++++- 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts index c9f50214d..24cc3a130 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/climate-api.service.ts @@ -56,9 +56,21 @@ export class ClimateApiService { const customFetch = async (...args:Parameters)=>{ // send a custom response with 102 status code to inform that request has been sent but is pending $.next({status:102} as Response) - const response = await window.fetch(...args); - $.next(response) - return response + try { + const response = await window.fetch(...args); + $.next(response) + return response + } catch (error:any) { + // Likely internal server error thrown + console.error(args) + console.error(error) + const message = error.message + const blob = new Blob([JSON.stringify({message}, null, 2)], {type : 'application/json'}); + const errorRes = new Response(blob,{status:500,statusText:message}) + $.next(errorRes) + return errorRes + } + } const baseClient = createClient({ baseUrl: API_ENDPOINT,mode:'cors',fetch:customFetch }); const client:IApiClient ={...baseClient, $} diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.ts index e486ca305..2ed4fd973 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/components/api-status/api-status.ts @@ -72,8 +72,8 @@ export class DashboardClimateApiStatusComponent implements OnInit, OnDestroy { } // console log response body (debug purposes) if (response && this.status === 'success') { - const resJson = await this.getResponseBodyJson(response); - console.log(`[API] ${id}`, resJson); + const body = await this.parseResponseBody(response); + console.log(`[API] ${id}`, body); } }); } @@ -95,21 +95,32 @@ export class DashboardClimateApiStatusComponent implements OnInit, OnDestroy { /** Show error message when using custom fetch with callbacks */ private async showCustomFetchErrorMessage(id: string, response: Response) { - const resJson = await this.getResponseBodyJson(response); - const errorText = resJson.detail || 'failed, see console logs for details'; + const body = await this.parseResponseBody(response); + const errorText = body.detail || 'failed, see console logs for details'; console.error(response); this.notificationService.showUserNotification({ matIcon: 'error', message: `[${id}] ${errorText}` }); } - private async getResponseBodyJson(response: Response) { - // clone body so that open-api can still consume when constructing full fetch response + /** + * Parse response body and format as JSON, nesting blob and text content types as custom properties + * in cases where response is not json type + */ + private async parseResponseBody(response: Response): Promise> { + // if (!response.bodyUsed) return {}; const clone = response.clone(); - try { - const json = await clone.json(); - return json; - } catch (error) { - console.error('Response body parse error', error); - return {}; + const contentType = response.headers.get('content-type'); + switch (contentType) { + case 'application/json': + return clone.json(); + case 'application/pdf': { + const blob = await clone.blob(); + return { blob }; + } + default: { + console.warn('No parser for response content', contentType); + const text = await clone.text(); + return { text }; + } } } } diff --git a/libs/shared/src/services/core/supabase/services/supabase-storage.service.ts b/libs/shared/src/services/core/supabase/services/supabase-storage.service.ts index 6290aeed8..aecc75557 100644 --- a/libs/shared/src/services/core/supabase/services/supabase-storage.service.ts +++ b/libs/shared/src/services/core/supabase/services/supabase-storage.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; // eslint-disable-next-line @nx/enforce-module-boundaries import { Database } from '@picsa/server-types'; -import { FileObject } from '@supabase/storage-js'; +import { FileObject, FileOptions } from '@supabase/storage-js'; import { SupabaseClient } from '@supabase/supabase-js'; import { PicsaNotificationService } from '../../notification.service'; @@ -81,6 +81,20 @@ export class SupabaseStorageService { return data?.[0] || null; } + public async putFile( + options: { bucketId: string; filename: string; fileBlob: Blob; folderPath?: string }, + fileOptions: FileOptions = { upsert: false } + ) { + const defaults = { folderPath: '' }; + const { bucketId, fileBlob, filename, folderPath } = { ...defaults, ...options }; + const filePath = folderPath ? `${folderPath}/${filename}` : `${filename}`; + const { data, error } = await this.storage.from(bucketId).upload(filePath, fileBlob, fileOptions); + if (error) { + throw error; + } + return data?.[0] || null; + } + /** Return the link to a file in a public bucket */ public getPublicLink(bucketId: string, objectPath: string) { return this.storage.from(bucketId).getPublicUrl(objectPath).data.publicUrl; From de465486de13b981203aecafe685410ef02caa9b Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 15 Feb 2024 15:15:07 -0800 Subject: [PATCH 36/68] feat: data-table context row and column --- .../src/features/data-table/data-table.component.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/shared/src/features/data-table/data-table.component.html b/libs/shared/src/features/data-table/data-table.component.html index 19cf6b8f5..cee4c71eb 100644 --- a/libs/shared/src/features/data-table/data-table.component.html +++ b/libs/shared/src/features/data-table/data-table.component.html @@ -17,20 +17,20 @@ - @for(column of tableOptions.displayColumns; track column){ + @for(column of tableOptions.displayColumns; track column; let index=$index){ - From 69bd033279a8103f0160f444f46e96efb4794efc Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 15 Feb 2024 15:15:24 -0800 Subject: [PATCH 37/68] feat: forecast dashboard download and open --- .../pages/forecast/forecast.component.ts | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.ts b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.ts index 5cdb2d468..a2b005ae2 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/climate/pages/forecast/forecast.component.ts @@ -4,14 +4,31 @@ import { RouterModule } from '@angular/router'; import { IDataTableOptions, PicsaDataTableComponent } from '@picsa/shared/features'; import { SupabaseService } from '@picsa/shared/services/core/supabase'; +import { DashboardMaterialModule } from '../../../../material.module'; import { ClimateService } from '../../climate.service'; import { DashboardClimateApiStatusComponent, IApiStatusOptions } from '../../components/api-status/api-status'; import { IForecastRow } from '../../types'; +const DISPLAY_COLUMNS: (keyof IForecastRow)[] = [ + 'country_code', + 'district', + 'type', + 'language_code', + 'filename', + 'date_modified', + 'storage_file', +]; + @Component({ selector: 'dashboard-climate-forecast', standalone: true, - imports: [CommonModule, DashboardClimateApiStatusComponent, RouterModule, PicsaDataTableComponent], + imports: [ + CommonModule, + DashboardClimateApiStatusComponent, + RouterModule, + PicsaDataTableComponent, + DashboardMaterialModule, + ], templateUrl: './forecast.component.html', styleUrls: ['./forecast.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, @@ -20,13 +37,15 @@ export class ClimateForecastPageComponent implements OnInit { public forecastData: IForecastRow[] = []; public tableOptions: IDataTableOptions = { - displayColumns: ['country', 'district', 'type', 'language', 'date_modified'], + displayColumns: DISPLAY_COLUMNS, }; public apiStatusOptions: IApiStatusOptions = { events: { refresh: () => this.refreshData() }, showStatusCode: false, }; + public activeDownloads: Record = {}; + constructor(private service: ClimateService, private supabase: SupabaseService, private cdr: ChangeDetectorRef) {} private get db() { @@ -46,6 +65,26 @@ export class ClimateForecastPageComponent implements OnInit { await this.refreshData(); } } + + public async handleStorageClick(row: IForecastRow) { + // handle download if storage file doesn't exist or hasn't been downloaded + if (!row.storage_file && this.activeDownloads[row.filename] !== 'complete') { + await this.downloadStorageFile(row); + } + // handle open + const storagePath = `climate/forecasts/${row.filename}`; + const publicLink = this.supabase.storage.getPublicLink('mw', storagePath); + open(publicLink, '_blank'); + } + + private async downloadStorageFile(row: IForecastRow) { + this.activeDownloads[row.filename] = 'pending'; + this.cdr.markForCheck(); + await this.service.loadFromAPI.forecast_file(row); + this.activeDownloads[row.filename] = 'complete'; + this.cdr.markForCheck(); + } + private loadForecastData(data: any[] = []) { this.forecastData = data; this.cdr.detectChanges(); From abb39a259c3193dc4e10cfcf17a77cf4966339cc Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Mon, 19 Feb 2024 21:36:02 -0800 Subject: [PATCH 38/68] chore: disable dashboard deploy action from main --- .github/workflows/web-release-dashboard.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/web-release-dashboard.yml b/.github/workflows/web-release-dashboard.yml index fc977addd..963d98fb0 100644 --- a/.github/workflows/web-release-dashboard.yml +++ b/.github/workflows/web-release-dashboard.yml @@ -18,11 +18,14 @@ env: VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} on: workflow_dispatch: - push: - branches: - - main - paths: - - 'apps/picsa-apps/dashboard/**' + + # (avoid automated push as will often need manual migration apply) + + # push: + # branches: + # - main + # paths: + # - 'apps/picsa-apps/dashboard/**' jobs: web_release_dashboard: From a97e32d970d0f1e14493982926f57ec956e1fbab Mon Sep 17 00:00:00 2001 From: Pamela Date: Tue, 20 Feb 2024 09:08:28 +0300 Subject: [PATCH 39/68] link search route --- .../src/app/pages/search/search.component.ts | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts b/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts index 24527ae98..083b3b6a3 100644 --- a/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts +++ b/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts @@ -7,6 +7,7 @@ import Fuse, { FuseResult, IFuseOptions } from 'fuse.js'; import { ResourcesComponentsModule } from '../../components/components.module'; import { IResourceBase, IResourceCollection, IResourceFile, IResourceLink } from '../../schemas'; import { ResourcesToolService } from '../../services/resources-tool.service'; +import { ActivatedRoute, Router, Params } from '@angular/router'; interface ISearchResultsByType { collection: IResourceCollection[]; @@ -46,17 +47,35 @@ export class ResourceSearchComponent implements OnInit { /** Store total number of results across types */ public totalResults?: number; - constructor(private service: ResourcesToolService, private cdr: ChangeDetectorRef) {} + constructor( + private service: ResourcesToolService, + private cdr: ChangeDetectorRef, + private router: Router, + private route: ActivatedRoute) {} - async ngOnInit() { - await this.service.ready(); - const fileDocs = await this.service.dbFiles.find().exec(); - const linkDocs = await this.service.dbLinks.find().exec(); - const collectionDocs = await this.service.dbCollections.find().exec(); - const allResources = [...fileDocs, ...linkDocs, ...collectionDocs].map((doc) => doc._data); - // TODO - add support for translations - this.fuse = new Fuse(allResources, this.fuseOptions); - } + async ngOnInit() { + await this.initializeServiceData(); + this.subscribeToQueryParams(); + } + + private async initializeServiceData() { + await this.service.ready(); + const fileDocs = await this.service.dbFiles.find().exec(); + const linkDocs = await this.service.dbLinks.find().exec(); + const collectionDocs = await this.service.dbCollections.find().exec(); + const allResources = [...fileDocs, ...linkDocs, ...collectionDocs].map((doc) => doc._data); + // TODO - add support for translations + this.fuse = new Fuse(allResources, this.fuseOptions); + } + + private subscribeToQueryParams() { + this.route.queryParams.subscribe((params: Params) => { + if (params.searchText) { + this.query = params.searchText; + this.onSearchInputChange() + } + }); + } onSearchInputChange() { // Only display search results if user has typed more than 2 characters @@ -64,13 +83,17 @@ export class ResourceSearchComponent implements OnInit { const searchResults = this.fuse.search(this.query); this.setSearchResultsByType(searchResults); this.totalResults = searchResults.length; + this.updateRoute(); } else { this.searchResults = { collection: [], file: [], link: [] }; this.totalResults = undefined; + this.updateRoute(true); } this.cdr.markForCheck(); + } + private setSearchResultsByType(results: FuseResult[]) { const searchResults: ISearchResultsByType = { collection: [], file: [], link: [] }; for (const result of results) { @@ -86,4 +109,15 @@ export class ResourceSearchComponent implements OnInit { } this.searchResults = searchResults; } + + private updateRoute(removeParam: boolean = false) { + const queryParams = removeParam ? {} : { searchText: this.query }; + this.router.navigate([], { relativeTo: this.route, queryParams }); + } + + goSearch() { + this.router.navigate(['/search'], { + queryParams: { searchText: this.query } + }); + } } From b69cf7b3bceec136370d16bb46b03ecac5a2bb6f Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 22 Feb 2024 14:37:11 -0800 Subject: [PATCH 40/68] chore: fix precommit lint --- .husky/pre-commit | 2 +- .../src/app/pages/search/search.component.ts | 59 +++++++++---------- package.json | 4 +- yarn.lock | 2 +- 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index d24fdfc60..5a182ef10 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,4 @@ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" -npx lint-staged +yarn lint-staged diff --git a/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts b/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts index 083b3b6a3..8a1ddb4fc 100644 --- a/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts +++ b/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts @@ -1,13 +1,13 @@ import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; +import { ActivatedRoute, Params, Router } from '@angular/router'; import { PicsaTranslateModule } from '@picsa/shared/modules'; import Fuse, { FuseResult, IFuseOptions } from 'fuse.js'; import { ResourcesComponentsModule } from '../../components/components.module'; import { IResourceBase, IResourceCollection, IResourceFile, IResourceLink } from '../../schemas'; import { ResourcesToolService } from '../../services/resources-tool.service'; -import { ActivatedRoute, Router, Params } from '@angular/router'; interface ISearchResultsByType { collection: IResourceCollection[]; @@ -48,34 +48,35 @@ export class ResourceSearchComponent implements OnInit { public totalResults?: number; constructor( - private service: ResourcesToolService, + private service: ResourcesToolService, private cdr: ChangeDetectorRef, private router: Router, - private route: ActivatedRoute) {} + private route: ActivatedRoute + ) {} - async ngOnInit() { - await this.initializeServiceData(); - this.subscribeToQueryParams(); - } - - private async initializeServiceData() { - await this.service.ready(); - const fileDocs = await this.service.dbFiles.find().exec(); - const linkDocs = await this.service.dbLinks.find().exec(); - const collectionDocs = await this.service.dbCollections.find().exec(); - const allResources = [...fileDocs, ...linkDocs, ...collectionDocs].map((doc) => doc._data); - // TODO - add support for translations - this.fuse = new Fuse(allResources, this.fuseOptions); - } - - private subscribeToQueryParams() { - this.route.queryParams.subscribe((params: Params) => { - if (params.searchText) { - this.query = params.searchText; - this.onSearchInputChange() - } - }); - } + async ngOnInit() { + await this.initializeServiceData(); + this.subscribeToQueryParams(); + } + + private async initializeServiceData() { + await this.service.ready(); + const fileDocs = await this.service.dbFiles.find().exec(); + const linkDocs = await this.service.dbLinks.find().exec(); + const collectionDocs = await this.service.dbCollections.find().exec(); + const allResources = [...fileDocs, ...linkDocs, ...collectionDocs].map((doc) => doc._data); + // TODO - add support for translations + this.fuse = new Fuse(allResources, this.fuseOptions); + } + + private subscribeToQueryParams() { + this.route.queryParams.subscribe((params: Params) => { + if (params.searchText) { + this.query = params.searchText; + this.onSearchInputChange(); + } + }); + } onSearchInputChange() { // Only display search results if user has typed more than 2 characters @@ -90,10 +91,8 @@ export class ResourceSearchComponent implements OnInit { this.updateRoute(true); } this.cdr.markForCheck(); - } - private setSearchResultsByType(results: FuseResult[]) { const searchResults: ISearchResultsByType = { collection: [], file: [], link: [] }; for (const result of results) { @@ -116,8 +115,8 @@ export class ResourceSearchComponent implements OnInit { } goSearch() { - this.router.navigate(['/search'], { - queryParams: { searchText: this.query } + this.router.navigate(['/search'], { + queryParams: { searchText: this.query }, }); } } diff --git a/package.json b/package.json index 0bcc3777f..a1786e534 100644 --- a/package.json +++ b/package.json @@ -191,7 +191,7 @@ }, "packageManager": "yarn@3.6.3", "lint-staged": { - "*.js": "eslint --cache --fix", - "*.{js,css,md}": "prettier --write" + "*.ts": "eslint --cache --fix", + "*.{ts,scss}": "prettier --write" } } diff --git a/yarn.lock b/yarn.lock index 255c25bde..585033f0e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15138,7 +15138,7 @@ __metadata: "leaflet-draw@github:enketo/Leaflet.draw#ff730785db7fcccbf2485ffcf4dffe1238a7c617": version: 1.0.4 resolution: "leaflet-draw@https://github.com/enketo/Leaflet.draw.git#commit=ff730785db7fcccbf2485ffcf4dffe1238a7c617" - checksum: 253998170af27f886d05b245c85429767e272647221daaf8d94ff5b86f75b8cbb96cc76a8a88492243166a214bc3b66b3ae704a81f74c862f09ac6c9495f731e + checksum: b08b88994769667f11f2b6a8937656c89cea34dafd4661abab0b48b4b97f3bddbdce7b23ddfdb8d7c6335e065530e32a70e281314afa34afa134bf68597945fc languageName: node linkType: hard From 3a350614ea2be713e723f25ee16579d67159d2d0 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 22 Feb 2024 14:38:51 -0800 Subject: [PATCH 41/68] chore: clean up subscriptions --- .../src/app/pages/search/search.component.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts b/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts index 8a1ddb4fc..02610b2af 100644 --- a/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts +++ b/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts @@ -1,9 +1,10 @@ import { CommonModule } from '@angular/common'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { PicsaTranslateModule } from '@picsa/shared/modules'; import Fuse, { FuseResult, IFuseOptions } from 'fuse.js'; +import { Subject, takeUntil } from 'rxjs'; import { ResourcesComponentsModule } from '../../components/components.module'; import { IResourceBase, IResourceCollection, IResourceFile, IResourceLink } from '../../schemas'; @@ -23,7 +24,7 @@ interface ISearchResultsByType { styleUrls: ['./search.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ResourceSearchComponent implements OnInit { +export class ResourceSearchComponent implements OnInit, OnDestroy { query: string = ''; // https://www.fusejs.io/api/options.html @@ -44,6 +45,8 @@ export class ResourceSearchComponent implements OnInit { searchResults: ISearchResultsByType = { collection: [], file: [], link: [] }; + private componentDestroyed$ = new Subject(); + /** Store total number of results across types */ public totalResults?: number; @@ -58,6 +61,9 @@ export class ResourceSearchComponent implements OnInit { await this.initializeServiceData(); this.subscribeToQueryParams(); } + async ngOnDestroy() { + this.componentDestroyed$.next(true); + } private async initializeServiceData() { await this.service.ready(); @@ -70,7 +76,7 @@ export class ResourceSearchComponent implements OnInit { } private subscribeToQueryParams() { - this.route.queryParams.subscribe((params: Params) => { + this.route.queryParams.pipe(takeUntil(this.componentDestroyed$)).subscribe((params: Params) => { if (params.searchText) { this.query = params.searchText; this.onSearchInputChange(); From 60b6aee5cd5a9b132ee51499dac29a68d3281c0f Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 22 Feb 2024 14:45:16 -0800 Subject: [PATCH 42/68] chore: code tidying --- .../src/app/pages/search/search.component.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts b/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts index 02610b2af..c71278aaf 100644 --- a/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts +++ b/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts @@ -90,11 +90,11 @@ export class ResourceSearchComponent implements OnInit, OnDestroy { const searchResults = this.fuse.search(this.query); this.setSearchResultsByType(searchResults); this.totalResults = searchResults.length; - this.updateRoute(); + this.setRouteSearchParam(this.query); } else { this.searchResults = { collection: [], file: [], link: [] }; this.totalResults = undefined; - this.updateRoute(true); + this.setRouteSearchParam(undefined); } this.cdr.markForCheck(); } @@ -115,14 +115,9 @@ export class ResourceSearchComponent implements OnInit, OnDestroy { this.searchResults = searchResults; } - private updateRoute(removeParam: boolean = false) { - const queryParams = removeParam ? {} : { searchText: this.query }; + /** Update route param to match search text. Passing undefined will remove the param */ + private setRouteSearchParam(searchText?: string) { + const queryParams = { searchText }; this.router.navigate([], { relativeTo: this.route, queryParams }); } - - goSearch() { - this.router.navigate(['/search'], { - queryParams: { searchText: this.query }, - }); - } } From 386968a1d3a0ee9f43950e1d2b1936c8b347b348 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 22 Feb 2024 14:58:20 -0800 Subject: [PATCH 43/68] fix: resources search nav --- .../resources-tool/src/app/pages/search/search.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts b/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts index c71278aaf..4649dbdc0 100644 --- a/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts +++ b/apps/picsa-tools/resources-tool/src/app/pages/search/search.component.ts @@ -118,6 +118,6 @@ export class ResourceSearchComponent implements OnInit, OnDestroy { /** Update route param to match search text. Passing undefined will remove the param */ private setRouteSearchParam(searchText?: string) { const queryParams = { searchText }; - this.router.navigate([], { relativeTo: this.route, queryParams }); + this.router.navigate([], { relativeTo: this.route, queryParams, replaceUrl: true }); } } From 8fa34027019bbe0c415307f138621072dff583fe Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Tue, 20 Feb 2024 16:32:40 +0300 Subject: [PATCH 44/68] launching app on mobile device --- .../app/src/main/assets/capacitor.config.json | 1 + .../extension-app-native/capacitor.config.ts | 2 +- .../video-player/video-player.component.ts | 22 ++++++++++++++++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/apps/picsa-apps/extension-app-native/android/app/src/main/assets/capacitor.config.json b/apps/picsa-apps/extension-app-native/android/app/src/main/assets/capacitor.config.json index aca61dd95..d0daf3f88 100644 --- a/apps/picsa-apps/extension-app-native/android/app/src/main/assets/capacitor.config.json +++ b/apps/picsa-apps/extension-app-native/android/app/src/main/assets/capacitor.config.json @@ -24,6 +24,7 @@ "@capacitor-firebase/performance" ], "server": { + "url": "http://192.168.0.107:4200", "androidScheme": "http", "cleartext": true } diff --git a/apps/picsa-apps/extension-app-native/capacitor.config.ts b/apps/picsa-apps/extension-app-native/capacitor.config.ts index 60f8691f9..b4e4c21f2 100644 --- a/apps/picsa-apps/extension-app-native/capacitor.config.ts +++ b/apps/picsa-apps/extension-app-native/capacitor.config.ts @@ -44,7 +44,7 @@ const config: CapacitorConfig = { * See notes at https://docs.picsa.app/advanced/testing **/ - // url: 'http://192.168.50.67:4200', + // url: 'http://192.168.0.107:4200', androidScheme: 'http', /** diff --git a/libs/shared/src/features/video-player/video-player.component.ts b/libs/shared/src/features/video-player/video-player.component.ts index 024bf5f7a..e7804f500 100644 --- a/libs/shared/src/features/video-player/video-player.component.ts +++ b/libs/shared/src/features/video-player/video-player.component.ts @@ -50,6 +50,8 @@ export class VideoPlayerComponent implements OnDestroy { /** Track any created object urls to dispose on destroy */ private objectUrl: string; + private pauseTime: number = 0; + constructor(private elementRef: ElementRef) {} async ngOnDestroy() { @@ -67,10 +69,17 @@ export class VideoPlayerComponent implements OnDestroy { public async playVideo() { // Remove thumbnail from future playback this.thumbnail = undefined; + if (Capacitor.isNativePlatform()) { + await this.videoPlayer.stopAllPlayers(); + this.initialised = false; + } + // Initialise player any time playback triggered in case url updated (e.g. downloaded after init) await this.initPlayer(); - await this.videoPlayer.stopAllPlayers(); await this.videoPlayer.play({ playerId: this.playerId }); + if (this.pauseTime > 0) { + await this.videoPlayer.setCurrentTime({ playerId: this.playerId, seektime: this.pauseTime }); + } } private async initPlayer() { @@ -149,8 +158,12 @@ export class VideoPlayerComponent implements OnDestroy { this.showPlayButton = false; } } - private async handlePlayerPause() { - this.showPlayButton = true; + + private handlePlayerPause(e: { fromPlayerId: string; currentTime: number }) { + if (e.fromPlayerId === this.playerId) { + this.pauseTime = e.currentTime; + this.showPlayButton = true; + } } private handlePlayerEnded() { @@ -158,6 +171,9 @@ export class VideoPlayerComponent implements OnDestroy { } private handlePlayerExit() { this.showPlayButton = true; + if (Capacitor.isNativePlatform()) { + this.initialised = false; + } } } From 6c4fd2dbad1ddf1061fc9e1ff3e65aff45868ea6 Mon Sep 17 00:00:00 2001 From: Ochieng Paul Date: Tue, 20 Feb 2024 19:29:25 +0300 Subject: [PATCH 45/68] worked on the resume issue --- .../video-player/video-player.component.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/libs/shared/src/features/video-player/video-player.component.ts b/libs/shared/src/features/video-player/video-player.component.ts index e7804f500..a6b20ee82 100644 --- a/libs/shared/src/features/video-player/video-player.component.ts +++ b/libs/shared/src/features/video-player/video-player.component.ts @@ -70,16 +70,20 @@ export class VideoPlayerComponent implements OnDestroy { // Remove thumbnail from future playback this.thumbnail = undefined; if (Capacitor.isNativePlatform()) { + const storedPauseTime = this.pauseTime; await this.videoPlayer.stopAllPlayers(); this.initialised = false; + this.pauseTime = storedPauseTime; } - // Initialise player any time playback triggered in case url updated (e.g. downloaded after init) await this.initPlayer(); - await this.videoPlayer.play({ playerId: this.playerId }); - if (this.pauseTime > 0) { - await this.videoPlayer.setCurrentTime({ playerId: this.playerId, seektime: this.pauseTime }); - } + this.videoPlayer.play({ playerId: this.playerId }).then(() => { + if (this.pauseTime > 0) { + setTimeout(() => { + this.videoPlayer.setCurrentTime({ playerId: this.playerId, seektime: this.pauseTime }); + }, 500); + } + }); } private async initPlayer() { @@ -169,11 +173,9 @@ export class VideoPlayerComponent implements OnDestroy { private handlePlayerEnded() { this.showPlayButton = true; } - private handlePlayerExit() { + private handlePlayerExit(e: { currentTime: number }) { this.showPlayButton = true; - if (Capacitor.isNativePlatform()) { - this.initialised = false; - } + this.pauseTime = e.currentTime; } } From 9366a83e325b0e949d0d7574f6a398522882fd35 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 22 Feb 2024 21:08:44 -0800 Subject: [PATCH 46/68] fix: video player screen orientation --- .../android/app/capacitor.build.gradle | 1 + .../app/src/main/assets/capacitor.config.json | 4 ++-- .../app/src/main/assets/capacitor.plugins.json | 4 ++++ .../android/capacitor.settings.gradle | 3 +++ .../extension-app-native/capacitor.config.ts | 3 ++- .../video-player/video-player.component.ts | 7 ++++++- package.json | 3 ++- yarn.lock | 14 ++++++++++++-- 8 files changed, 32 insertions(+), 7 deletions(-) diff --git a/apps/picsa-apps/extension-app-native/android/app/capacitor.build.gradle b/apps/picsa-apps/extension-app-native/android/app/capacitor.build.gradle index c3748cdbb..fb3785a60 100644 --- a/apps/picsa-apps/extension-app-native/android/app/capacitor.build.gradle +++ b/apps/picsa-apps/extension-app-native/android/app/capacitor.build.gradle @@ -19,6 +19,7 @@ dependencies { implementation project(':capacitor-community-firebase-analytics') implementation project(':capacitor-community-firebase-crashlytics') implementation project(':capacitor-firebase-performance') + implementation project(':capacitor-screen-orientation') implementation "androidx.webkit:webkit:1.4.0" implementation "androidx.legacy:legacy-support-v4:1.0.0" } diff --git a/apps/picsa-apps/extension-app-native/android/app/src/main/assets/capacitor.config.json b/apps/picsa-apps/extension-app-native/android/app/src/main/assets/capacitor.config.json index d0daf3f88..833fdb3c0 100644 --- a/apps/picsa-apps/extension-app-native/android/app/src/main/assets/capacitor.config.json +++ b/apps/picsa-apps/extension-app-native/android/app/src/main/assets/capacitor.config.json @@ -21,10 +21,10 @@ "capacitor-video-player", "@capacitor-community/firebase-analytics", "@capacitor-community/firebase-crashlytics", - "@capacitor-firebase/performance" + "@capacitor-firebase/performance", + "@capacitor/screen-orientation" ], "server": { - "url": "http://192.168.0.107:4200", "androidScheme": "http", "cleartext": true } diff --git a/apps/picsa-apps/extension-app-native/android/app/src/main/assets/capacitor.plugins.json b/apps/picsa-apps/extension-app-native/android/app/src/main/assets/capacitor.plugins.json index 8b1ef0188..bb0d087b5 100644 --- a/apps/picsa-apps/extension-app-native/android/app/src/main/assets/capacitor.plugins.json +++ b/apps/picsa-apps/extension-app-native/android/app/src/main/assets/capacitor.plugins.json @@ -38,5 +38,9 @@ { "pkg": "@capacitor-firebase/performance", "classpath": "io.capawesome.capacitorjs.plugins.firebase.performance.FirebasePerformancePlugin" + }, + { + "pkg": "@capacitor/screen-orientation", + "classpath": "com.capacitorjs.plugins.screenorientation.ScreenOrientationPlugin" } ] diff --git a/apps/picsa-apps/extension-app-native/android/capacitor.settings.gradle b/apps/picsa-apps/extension-app-native/android/capacitor.settings.gradle index 77c168a17..ad0f47c86 100644 --- a/apps/picsa-apps/extension-app-native/android/capacitor.settings.gradle +++ b/apps/picsa-apps/extension-app-native/android/capacitor.settings.gradle @@ -31,3 +31,6 @@ project(':capacitor-community-firebase-crashlytics').projectDir = new File('../. include ':capacitor-firebase-performance' project(':capacitor-firebase-performance').projectDir = new File('../../../../node_modules/@capacitor-firebase/performance/android') + +include ':capacitor-screen-orientation' +project(':capacitor-screen-orientation').projectDir = new File('../../../../node_modules/@capacitor/screen-orientation/android') diff --git a/apps/picsa-apps/extension-app-native/capacitor.config.ts b/apps/picsa-apps/extension-app-native/capacitor.config.ts index b4e4c21f2..af4c5e7c4 100644 --- a/apps/picsa-apps/extension-app-native/capacitor.config.ts +++ b/apps/picsa-apps/extension-app-native/capacitor.config.ts @@ -29,6 +29,7 @@ const config: CapacitorConfig = { '@capacitor-community/firebase-analytics', '@capacitor-community/firebase-crashlytics', '@capacitor-firebase/performance', + '@capacitor/screen-orientation', ], // Enable app to use native http for requests (bypass cors) // https://capacitorjs.com/docs/apis/http @@ -44,7 +45,7 @@ const config: CapacitorConfig = { * See notes at https://docs.picsa.app/advanced/testing **/ - // url: 'http://192.168.0.107:4200', + // url: 'http://192.168.50.67:4200', androidScheme: 'http', /** diff --git a/libs/shared/src/features/video-player/video-player.component.ts b/libs/shared/src/features/video-player/video-player.component.ts index a6b20ee82..5e97225eb 100644 --- a/libs/shared/src/features/video-player/video-player.component.ts +++ b/libs/shared/src/features/video-player/video-player.component.ts @@ -1,5 +1,6 @@ import { Component, ElementRef, HostBinding, Input, OnDestroy } from '@angular/core'; import { Capacitor } from '@capacitor/core'; +import { ScreenOrientation } from '@capacitor/screen-orientation'; import { CapacitorVideoPlayer, CapacitorVideoPlayerPlugin, capVideoPlayerOptions } from 'capacitor-video-player'; // Fix listeners missing from type @@ -173,9 +174,13 @@ export class VideoPlayerComponent implements OnDestroy { private handlePlayerEnded() { this.showPlayButton = true; } - private handlePlayerExit(e: { currentTime: number }) { + private async handlePlayerExit(e: { currentTime: number }) { this.showPlayButton = true; this.pauseTime = e.currentTime; + // Ensure player does not stay stuck in landscape mode + if (Capacitor.isNativePlatform()) { + await ScreenOrientation.unlock(); + } } } diff --git a/package.json b/package.json index a1786e534..6c6c65619 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@capacitor/device": "^5.0.6", "@capacitor/filesystem": "^5.1.4", "@capacitor/network": "^5.0.6", + "@capacitor/screen-orientation": "^5.0.7", "@ngx-translate/core": "~15.0.0", "@ngx-translate/http-loader": "~8.0.0", "@nx/angular": "17.1.2", @@ -75,7 +76,7 @@ "@uppy/tus": "^3.4.0", "c3": "^0.7.20", "capacitor-blob-writer": "^1.1.14", - "capacitor-video-player": "^5.0.3", + "capacitor-video-player": "^5.5.1", "cordova-plugin-codeplay-share-own-apk": "0.0.7", "cordova-plugin-file": "^8.0.1", "cordova-plugin-file-opener2": "^4.0.0", diff --git a/yarn.lock b/yarn.lock index 585033f0e..115872e01 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2336,6 +2336,15 @@ __metadata: languageName: node linkType: hard +"@capacitor/screen-orientation@npm:^5.0.7": + version: 5.0.7 + resolution: "@capacitor/screen-orientation@npm:5.0.7" + peerDependencies: + "@capacitor/core": ^5.0.0 + checksum: 0ae635df7c4c2cea494d3a746ec257327568b66210b8acf16e8e4f6ce696c8eeca5fdb465d6cd756a2f2e4469cb918bab6b74b4abfeffad8cc6946f2ab15c248 + languageName: node + linkType: hard + "@colors/colors@npm:1.5.0": version: 1.5.0 resolution: "@colors/colors@npm:1.5.0" @@ -9428,7 +9437,7 @@ __metadata: languageName: node linkType: hard -"capacitor-video-player@npm:^5.0.3": +"capacitor-video-player@npm:^5.5.1": version: 5.5.1 resolution: "capacitor-video-player@npm:5.5.1" peerDependencies: @@ -17527,6 +17536,7 @@ __metadata: "@capacitor/device": ^5.0.6 "@capacitor/filesystem": ^5.1.4 "@capacitor/network": ^5.0.6 + "@capacitor/screen-orientation": ^5.0.7 "@ngx-translate/core": ~15.0.0 "@ngx-translate/http-loader": ~8.0.0 "@nx/angular": 17.1.2 @@ -17570,7 +17580,7 @@ __metadata: autoprefixer: ^10.4.0 c3: ^0.7.20 capacitor-blob-writer: ^1.1.14 - capacitor-video-player: ^5.0.3 + capacitor-video-player: ^5.5.1 cordova-plugin-codeplay-share-own-apk: 0.0.7 cordova-plugin-file: ^8.0.1 cordova-plugin-file-opener2: ^4.0.0 From e2a039f3484efeed3cd8477038ca4192f8ab5f3b Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 22 Feb 2024 21:34:24 -0800 Subject: [PATCH 47/68] chore: code tidying --- .../src/features/video-player/video-player.component.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libs/shared/src/features/video-player/video-player.component.ts b/libs/shared/src/features/video-player/video-player.component.ts index 5e97225eb..e1bc806e7 100644 --- a/libs/shared/src/features/video-player/video-player.component.ts +++ b/libs/shared/src/features/video-player/video-player.component.ts @@ -70,11 +70,9 @@ export class VideoPlayerComponent implements OnDestroy { public async playVideo() { // Remove thumbnail from future playback this.thumbnail = undefined; + await this.videoPlayer.stopAllPlayers(); if (Capacitor.isNativePlatform()) { - const storedPauseTime = this.pauseTime; - await this.videoPlayer.stopAllPlayers(); this.initialised = false; - this.pauseTime = storedPauseTime; } // Initialise player any time playback triggered in case url updated (e.g. downloaded after init) await this.initPlayer(); From f6f7b12467752dd74c95cc618a7b51d6b84e73a2 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Mon, 26 Feb 2024 17:58:58 -0800 Subject: [PATCH 48/68] chore: rename pr label title action --- .../workflows/{gh-pr-labeler.yml => gh-pr-label-title.yml} | 6 ------ 1 file changed, 6 deletions(-) rename .github/workflows/{gh-pr-labeler.yml => gh-pr-label-title.yml} (86%) diff --git a/.github/workflows/gh-pr-labeler.yml b/.github/workflows/gh-pr-label-title.yml similarity index 86% rename from .github/workflows/gh-pr-labeler.yml rename to .github/workflows/gh-pr-label-title.yml index e1d2c50aa..dedba8aff 100644 --- a/.github/workflows/gh-pr-labeler.yml +++ b/.github/workflows/gh-pr-label-title.yml @@ -5,7 +5,6 @@ on: types: - opened - edited - - synchronize jobs: label: runs-on: ubuntu-latest @@ -38,8 +37,3 @@ jobs: } # Do not ignore any labels (default ignores chore:) ignored_types: '[]' - # Assign labels based on files modified - - name: Assign PR path Labels - uses: actions/labeler@v4 - with: - configuration-path: .github/pr-labeler.config.yml From 908c73b6050dc8d5ef696c92456db62f57e39035 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Mon, 26 Feb 2024 17:59:20 -0800 Subject: [PATCH 49/68] chore: rename label path action --- .github/workflows/gh-pr-label-paths.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/gh-pr-label-paths.yml diff --git a/.github/workflows/gh-pr-label-paths.yml b/.github/workflows/gh-pr-label-paths.yml new file mode 100644 index 000000000..6cc5693d9 --- /dev/null +++ b/.github/workflows/gh-pr-label-paths.yml @@ -0,0 +1,21 @@ +name: Github PR Labeler +on: + # TODO - replace with pull_request_target when working in master + pull_request: + # https://frontside.com/blog/2020-05-26-github-actions-pull_request/ + types: + - opened + - edited + - synchronize +jobs: + label: + runs-on: ubuntu-latest + steps: + # Checkout repo to access local templates (if updated) + - name: checkout + uses: actions/checkout@main + # Assign labels based on files modified + - name: Assign PR path Labels + uses: actions/labeler@v4 + with: + configuration-path: .github/pr-labeler.config.yml From 0da961355f280c40ffe99c51dbcce3bffc6c5195 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Mon, 26 Feb 2024 21:16:19 -0800 Subject: [PATCH 50/68] feat: add autolabel disable --- .github/labels.yml | 1 + .github/workflows/gh-pr-label-paths.yml | 17 +++++++++-------- .github/workflows/gh-pr-label-title.yml | 7 +++++-- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.github/labels.yml b/.github/labels.yml index 05d4af331..313139281 100644 --- a/.github/labels.yml +++ b/.github/labels.yml @@ -11,6 +11,7 @@ { 'color': 'e4e669', 'description': "This doesn't seem right", 'name': 'invalid' }, { 'color': 'd876e3', 'description': 'Further information is requested', 'name': 'question' }, { 'color': 'ffffff', 'description': 'This will not be worked on', 'name': 'wontfix' }, + { 'color': 'ffffff', 'description': 'Disable PR autolabel actions', 'name': 'no-autolable' }, { 'color': '0366d6', 'description': 'Pull requests that update a dependency file', 'name': 'dependencies' }, { 'color': 'CF3458', 'description': '', 'name': 'Feedback Discussion' }, { 'color': 'ffffff', 'description': '', 'name': 'Test - Preview' }, diff --git a/.github/workflows/gh-pr-label-paths.yml b/.github/workflows/gh-pr-label-paths.yml index 6cc5693d9..be1ae2938 100644 --- a/.github/workflows/gh-pr-label-paths.yml +++ b/.github/workflows/gh-pr-label-paths.yml @@ -1,21 +1,22 @@ -name: Github PR Labeler +# Automatically apply labels to PR based on filepaths of modified files +name: Github PR Label Paths on: - # TODO - replace with pull_request_target when working in master + # TODO - replace with pull_request_target when working in main pull_request: - # https://frontside.com/blog/2020-05-26-github-actions-pull_request/ - types: - - opened - - edited - - synchronize jobs: + if: ${{ !contains(github.event.pull_request.labels.*.name , 'no-autolabel')}} label: + permissions: + contents: read + pull-requests: write runs-on: ubuntu-latest steps: # Checkout repo to access local templates (if updated) - name: checkout uses: actions/checkout@main # Assign labels based on files modified + # https://github.com/actions/labeler - name: Assign PR path Labels - uses: actions/labeler@v4 + uses: actions/labeler@v5 with: configuration-path: .github/pr-labeler.config.yml diff --git a/.github/workflows/gh-pr-label-title.yml b/.github/workflows/gh-pr-label-title.yml index dedba8aff..624cb1d54 100644 --- a/.github/workflows/gh-pr-label-title.yml +++ b/.github/workflows/gh-pr-label-title.yml @@ -1,12 +1,15 @@ -name: Github PR Labeler +# Automatically apply labels to PR based on title (if written in semantic format) +name: Github PR Label Title on: - # TODO - replace with pull_request_target when working in master + # TODO - replace with pull_request_target when working in main pull_request: + # https://frontside.com/blog/2020-05-26-github-actions-pull_request/ types: - opened - edited jobs: label: + if: ${{ !contains(github.event.pull_request.labels.*.name , 'no-autolabel')}} runs-on: ubuntu-latest steps: # Checkout repo to access local templates (if updated) From 62b19d78749cd30c8ac08e4338757b5e6cd80b19 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Mon, 26 Feb 2024 21:18:53 -0800 Subject: [PATCH 51/68] chore: update label prefixes --- .github/workflows/gh-pr-label-title.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gh-pr-label-title.yml b/.github/workflows/gh-pr-label-title.yml index 624cb1d54..31a387aa2 100644 --- a/.github/workflows/gh-pr-label-title.yml +++ b/.github/workflows/gh-pr-label-title.yml @@ -36,7 +36,13 @@ jobs: "docs": "documentation", "feat": "feature", "fix": "fix", - "refactor": "maintenance" + "refactor": "maintenance", + "Breaking": "breaking", + "Chore": "maintenance", + "Docs": "documentation", + "Feat": "feature", + "Fix": "fix", + "Refactor": "maintenance" } # Do not ignore any labels (default ignores chore:) ignored_types: '[]' From 5b28a542e8b1ac34896a03512187dc1aab13d844 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Mon, 26 Feb 2024 21:26:22 -0800 Subject: [PATCH 52/68] chore: code tidying --- .github/workflows/gh-pr-label-paths.yml | 4 +++- apps/picsa-apps/extension-app-native/.env.local | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 apps/picsa-apps/extension-app-native/.env.local diff --git a/.github/workflows/gh-pr-label-paths.yml b/.github/workflows/gh-pr-label-paths.yml index be1ae2938..715d19a7b 100644 --- a/.github/workflows/gh-pr-label-paths.yml +++ b/.github/workflows/gh-pr-label-paths.yml @@ -1,8 +1,10 @@ # Automatically apply labels to PR based on filepaths of modified files name: Github PR Label Paths +# TODO - replace with pull_request_target when working in main on: - # TODO - replace with pull_request_target when working in main pull_request: + branches: + - main jobs: if: ${{ !contains(github.event.pull_request.labels.*.name , 'no-autolabel')}} label: diff --git a/apps/picsa-apps/extension-app-native/.env.local b/apps/picsa-apps/extension-app-native/.env.local new file mode 100644 index 000000000..2728ac94c --- /dev/null +++ b/apps/picsa-apps/extension-app-native/.env.local @@ -0,0 +1 @@ +SERVER_URL=http://192.168.50.67:4200 From a25b80fa23506cf0351d24961582637f6d057067 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Mon, 26 Feb 2024 21:28:56 -0800 Subject: [PATCH 53/68] chore: update action target --- .github/workflows/gh-pr-label-paths.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/gh-pr-label-paths.yml b/.github/workflows/gh-pr-label-paths.yml index 715d19a7b..cf7188ff6 100644 --- a/.github/workflows/gh-pr-label-paths.yml +++ b/.github/workflows/gh-pr-label-paths.yml @@ -2,9 +2,7 @@ name: Github PR Label Paths # TODO - replace with pull_request_target when working in main on: - pull_request: - branches: - - main + pull_request_target: jobs: if: ${{ !contains(github.event.pull_request.labels.*.name , 'no-autolabel')}} label: From 38f1bbaa5b5bf073e262998913196c1040793320 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Mon, 26 Feb 2024 21:31:02 -0800 Subject: [PATCH 54/68] chore: ci --- .github/workflows/gh-pr-label-paths.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pr-label-paths.yml b/.github/workflows/gh-pr-label-paths.yml index cf7188ff6..34664b38f 100644 --- a/.github/workflows/gh-pr-label-paths.yml +++ b/.github/workflows/gh-pr-label-paths.yml @@ -2,7 +2,7 @@ name: Github PR Label Paths # TODO - replace with pull_request_target when working in main on: - pull_request_target: + - pull_request_target jobs: if: ${{ !contains(github.event.pull_request.labels.*.name , 'no-autolabel')}} label: From 55f8dde454f50224e4dbb81a22ba7d1d8870218d Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Mon, 26 Feb 2024 21:32:15 -0800 Subject: [PATCH 55/68] chore: ci test --- .github/workflows/gh-pr-label-paths.yml | 4 +++- .github/workflows/gh-pr-label-title.yml | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gh-pr-label-paths.yml b/.github/workflows/gh-pr-label-paths.yml index 34664b38f..82f410d31 100644 --- a/.github/workflows/gh-pr-label-paths.yml +++ b/.github/workflows/gh-pr-label-paths.yml @@ -1,7 +1,9 @@ # Automatically apply labels to PR based on filepaths of modified files name: Github PR Label Paths -# TODO - replace with pull_request_target when working in main on: + push: + branches: + - chore/pr-label-semantic - pull_request_target jobs: if: ${{ !contains(github.event.pull_request.labels.*.name , 'no-autolabel')}} diff --git a/.github/workflows/gh-pr-label-title.yml b/.github/workflows/gh-pr-label-title.yml index 31a387aa2..00dac5275 100644 --- a/.github/workflows/gh-pr-label-title.yml +++ b/.github/workflows/gh-pr-label-title.yml @@ -1,7 +1,6 @@ # Automatically apply labels to PR based on title (if written in semantic format) name: Github PR Label Title on: - # TODO - replace with pull_request_target when working in main pull_request: # https://frontside.com/blog/2020-05-26-github-actions-pull_request/ types: From 4c1fe22efa7be30d3a1655cc8c7da39f141bf2a9 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Mon, 26 Feb 2024 21:32:29 -0800 Subject: [PATCH 56/68] chore: ci --- .github/workflows/gh-pr-label-paths.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pr-label-paths.yml b/.github/workflows/gh-pr-label-paths.yml index 82f410d31..2e45a58bf 100644 --- a/.github/workflows/gh-pr-label-paths.yml +++ b/.github/workflows/gh-pr-label-paths.yml @@ -4,7 +4,7 @@ on: push: branches: - chore/pr-label-semantic - - pull_request_target + pull_request_target: jobs: if: ${{ !contains(github.event.pull_request.labels.*.name , 'no-autolabel')}} label: From ded02422923db75fdf5c12c467385a2b3ceb9fc7 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Mon, 26 Feb 2024 21:33:46 -0800 Subject: [PATCH 57/68] chore: code tidying --- .github/workflows/gh-pr-label-paths.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/gh-pr-label-paths.yml b/.github/workflows/gh-pr-label-paths.yml index 2e45a58bf..be54e6324 100644 --- a/.github/workflows/gh-pr-label-paths.yml +++ b/.github/workflows/gh-pr-label-paths.yml @@ -1,13 +1,10 @@ # Automatically apply labels to PR based on filepaths of modified files name: Github PR Label Paths on: - push: - branches: - - chore/pr-label-semantic - pull_request_target: + pull_request: jobs: - if: ${{ !contains(github.event.pull_request.labels.*.name , 'no-autolabel')}} label: + if: ${{ !contains(github.event.pull_request.labels.*.name , 'no-autolabel')}} permissions: contents: read pull-requests: write From e5eefbc497a5d5d45e186fa8bb845c49afc8dd82 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Mon, 26 Feb 2024 21:39:16 -0800 Subject: [PATCH 58/68] chore: update labeller config --- .github/pr-labeler.config.yml | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/.github/pr-labeler.config.yml b/.github/pr-labeler.config.yml index 73e8b5a98..3bcd350a4 100644 --- a/.github/pr-labeler.config.yml +++ b/.github/pr-labeler.config.yml @@ -1,25 +1,36 @@ # Apps 'App: Dashboard': - - apps/picsa-apps/dashboard/**/* + - changed-files: + - any-glob-to-any-file: 'apps/picsa-apps/dashboard/**/*' 'App: Server': - - apps/picsa-apps/server/**/* + - changed-files: + - any-glob-to-any-file: 'apps/picsa-apps/server/**/*' # Tools 'Tool: Budget': - - apps/picsa-tools/budget-tool/**/* + - changed-files: + - any-glob-to-any-file: 'apps/picsa-tools/budget-tool/**/*' 'Tool: Climate': - - apps/picsa-tools/climate-tool/**/* + - changed-files: + - any-glob-to-any-file: 'apps/picsa-tools/climate-tool/**/*' 'Tool: Crop Probability': - - apps/picsa-tools/crop-probability-tool/**/* + - changed-files: + - any-glob-to-any-file: 'apps/picsa-tools/crop-probability-tool/**/*' 'Tool: Farmer Activity': - - apps/picsa-tools/farmer-activity/**/* + - changed-files: + - any-glob-to-any-file: 'apps/picsa-tools/farmer-activity/**/*' 'Tool: Manual': - - apps/picsa-tools/manual-tool/**/* + - changed-files: + - any-glob-to-any-file: 'apps/picsa-tools/manual-tool/**/*' 'Tool: Monitoring': - - apps/picsa-tools/monitoring-tool/**/* + - changed-files: + - any-glob-to-any-file: 'apps/picsa-tools/monitoring-tool/**/*' 'Tool: Option': - - apps/picsa-tools/option-tool/**/* + - changed-files: + - any-glob-to-any-file: 'apps/picsa-tools/option-tool/**/*' 'Tool: Resources': - - apps/picsa-tools/resources-tool/**/* + - changed-files: + - any-glob-to-any-file: 'apps/picsa-tools/resources-tool/**/*' 'Tool: Seasonal Calendar': - - apps/picsa-tools/seasonal-calendar-tool/**/* + - changed-files: + - any-glob-to-any-file: 'apps/picsa-tools/seasonal-calendar-tool/**/*' From f421e038256b48f29a4d312299c261fbf074d99a Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Mon, 26 Feb 2024 21:40:23 -0800 Subject: [PATCH 59/68] chore: add sync-labels --- .github/workflows/gh-pr-label-paths.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/gh-pr-label-paths.yml b/.github/workflows/gh-pr-label-paths.yml index be54e6324..77f47c6b8 100644 --- a/.github/workflows/gh-pr-label-paths.yml +++ b/.github/workflows/gh-pr-label-paths.yml @@ -19,3 +19,4 @@ jobs: uses: actions/labeler@v5 with: configuration-path: .github/pr-labeler.config.yml + sync-labels: true From c9195d61d6765b73d8e1acb89ffa7eed73cd0538 Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Mon, 26 Feb 2024 21:41:36 -0800 Subject: [PATCH 60/68] chore: ci --- .github/workflows/gh-pr-label-paths.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh-pr-label-paths.yml b/.github/workflows/gh-pr-label-paths.yml index 77f47c6b8..89aef59c2 100644 --- a/.github/workflows/gh-pr-label-paths.yml +++ b/.github/workflows/gh-pr-label-paths.yml @@ -19,4 +19,4 @@ jobs: uses: actions/labeler@v5 with: configuration-path: .github/pr-labeler.config.yml - sync-labels: true + sync-labels: false From 9eb07d3d78f3457dbde5cb8f9877e9cf2328c175 Mon Sep 17 00:00:00 2001 From: khalifan-kfan Date: Tue, 27 Feb 2024 13:15:09 +0300 Subject: [PATCH 61/68] fix database types --- .../20240213081336_monitoring_forms_table.sql | 10 ++--- apps/picsa-server/supabase/types/index.ts | 37 ++++++++++--------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql b/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql index 14fbe6b92..1d618195b 100644 --- a/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql +++ b/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql @@ -4,11 +4,11 @@ create table created_at timestamp with time zone not null default now(), title text not null, description character varying null, - "enketoDefinition" jsonb null, - "summaryFields" jsonb[] null, - "appCountries" text[] null, - "enketoForm" text null, - "enketoModel" text null, + enketo_definition jsonb null, + summary_fields jsonb[] null, + app_countries text[] null, + enketo_form text null, + enketo_model text null, cover jsonb null, constraint monitoring_forms_pkey primary key (id) ) tablespace pg_default; \ No newline at end of file diff --git a/apps/picsa-server/supabase/types/index.ts b/apps/picsa-server/supabase/types/index.ts index fdb2283ac..94fe37fa7 100644 --- a/apps/picsa-server/supabase/types/index.ts +++ b/apps/picsa-server/supabase/types/index.ts @@ -179,40 +179,40 @@ export interface Database { } monitoring_forms: { Row: { - appCountries: string[] | null + app_countries: string[] | null cover: Json | null created_at: string description: string | null - enketoDefinition: Json | null - enketoForm: string | null - enketoModel: string | null + enketo_definition: Json | null + enketo_form: string | null + enketo_model: string | null id: number - summaryFields: Json[] | null + summary_fields: Json[] | null title: string } Insert: { - appCountries?: string[] | null + app_countries?: string[] | null cover?: Json | null created_at?: string description?: string | null - enketoDefinition?: Json | null - enketoForm?: string | null - enketoModel?: string | null + enketo_definition?: Json | null + enketo_form?: string | null + enketo_model?: string | null id?: number - summaryFields?: Json[] | null - tittle: string + summary_fields?: Json[] | null + title: string } Update: { - appCountries?: string[] | null + app_countries?: string[] | null cover?: Json | null created_at?: string description?: string | null - enketoDefinition?: Json | null - enketoForm?: string | null - enketoModel?: string | null + enketo_definition?: Json | null + enketo_form?: string | null + enketo_model?: string | null id?: number - summaryFields?: Json[] | null - tittle?: string + summary_fields?: Json[] | null + title?: string } Relationships: [] } @@ -357,6 +357,7 @@ export interface Database { metadata: Json | null name: string | null owner: string | null + owner_id: string | null path_tokens: string[] | null updated_at: string | null version: string | null @@ -369,6 +370,7 @@ export interface Database { metadata?: Json | null name?: string | null owner?: string | null + owner_id?: string | null path_tokens?: string[] | null updated_at?: string | null version?: string | null @@ -381,6 +383,7 @@ export interface Database { metadata?: Json | null name?: string | null owner?: string | null + owner_id?: string | null path_tokens?: string[] | null updated_at?: string | null version?: string | null From 5aa980c7b3df3b63876bf95d27e2dd2248091a5a Mon Sep 17 00:00:00 2001 From: khalifan-kfan Date: Tue, 27 Feb 2024 16:47:53 +0300 Subject: [PATCH 62/68] ch: allow for adding of custom data --- .../pages/home/monitoring.page.html | 49 +++++++++---------- .../pages/home/monitoring.page.scss | 8 +-- .../monitoring/pages/home/monitoring.page.ts | 3 +- yarn.lock | 2 +- 4 files changed, 29 insertions(+), 33 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html index 09d0ba208..07f6e7321 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html @@ -7,71 +7,68 @@

Monitoring Froms

PICSA Table

@for (item of values; track $index) { {{ item['label'] }}
}
+ +
+ {{values}} +
+
+ +
+ {{values}} +
+
-

Legacy Table

+
- - - - - - - + + - + - - + - - + - + - - + - + - - + -
{{ column | formatValue: tableOptions.formatHeader }} + @if(valueTemplates[column]){ } @else { - {{ el[column] }} + {{ row[column] }} } Title {{ element.title }} description {{ element.description }} Form Html{{ element.enketoForm }}{{ element.enketo_form }} Fields Summary - @for (item of element.summaryFields; track $index) { {{ item['label'] }}
+ @for (item of element.summary_fields; track $index) { {{ item['label'] }}
}
App Countries{{ element.appCountries.join(',') }}{{ element.app_countries.join(',') }} Model{{ element.enketoModel }}{{ element.enketo_model }} Date {{ element.created_at | date: 'mediumDate' }}
+ --> }
diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.scss b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.scss index 5c6e09504..69db49c2a 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.scss +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.scss @@ -26,8 +26,8 @@ td { cursor: pointer; } .long-text-rows { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 13rem; + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; + max-width: 13rem !important; } diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts index d7d59d69e..959376361 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts @@ -9,7 +9,6 @@ import { IDataTableOptions, PicsaDataTableComponent } from '@picsa/shared/featur import { DashboardMaterialModule } from '../../../../material.module'; import { MonitoringFormsDashboardService } from '../../monitoring.service'; -export type ITranslationRow = Database['public']['Tables']['translations']['Row']; @Component({ selector: 'dashboard-translations-page', standalone: true, @@ -18,7 +17,7 @@ export type ITranslationRow = Database['public']['Tables']['translations']['Row' styleUrls: ['./monitoring.page.scss'], }) export class MonitoringPageComponent implements OnInit { - displayedColumns: string[] = [ 'title', 'description', 'summaryFields', 'appCountries', 'enketoForm', 'enketoModel', 'created_at']; + displayedColumns: string[] = [ 'title', 'description', 'summary_fields', 'app_countries', 'enketo_form', 'enketo_model', 'created_at']; constructor(public service: MonitoringFormsDashboardService, private router: Router) {} public tableOptions: IDataTableOptions = { paginatorSizes: [25, 50], diff --git a/yarn.lock b/yarn.lock index 115872e01..c085187ee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15147,7 +15147,7 @@ __metadata: "leaflet-draw@github:enketo/Leaflet.draw#ff730785db7fcccbf2485ffcf4dffe1238a7c617": version: 1.0.4 resolution: "leaflet-draw@https://github.com/enketo/Leaflet.draw.git#commit=ff730785db7fcccbf2485ffcf4dffe1238a7c617" - checksum: b08b88994769667f11f2b6a8937656c89cea34dafd4661abab0b48b4b97f3bddbdce7b23ddfdb8d7c6335e065530e32a70e281314afa34afa134bf68597945fc + checksum: 253998170af27f886d05b245c85429767e272647221daaf8d94ff5b86f75b8cbb96cc76a8a88492243166a214bc3b66b3ae704a81f74c862f09ac6c9495f731e languageName: node linkType: hard From a82d8bbebecdbeff95f1792814369a3dfa450d24 Mon Sep 17 00:00:00 2001 From: khalifan-kfan Date: Tue, 27 Feb 2024 17:01:17 +0300 Subject: [PATCH 63/68] update data form --- apps/picsa-server/supabase/data/monitoring_forms_rows.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/picsa-server/supabase/data/monitoring_forms_rows.csv b/apps/picsa-server/supabase/data/monitoring_forms_rows.csv index 220db8f35..3949dbb71 100644 --- a/apps/picsa-server/supabase/data/monitoring_forms_rows.csv +++ b/apps/picsa-server/supabase/data/monitoring_forms_rows.csv @@ -1,4 +1,4 @@ -id,created_at,title,description,enketoDefinition,summaryFields,appCountries,enketoForm,enketoModel,cover +id,created_at,title,description,enketo_definition,summary_fields,app_countries,enketo_form,enketo_model,cover 2,2024-02-13 07:34:03.939328+00,Extension Worker Monitoring,Malawi,"{""hash"":""md5:dc0116ade1cd0dc0a7c0f3ce02bcbddc--d97bac4e49bda9c52dee96737583f620---1"",""media"":{},""theme"":""grid"",""maxSize"":10000000,""enketoId"":""PGpldp9m"",""languageMap"":{},""externalData"":[]}","[{""field"":""date"",""label"":""Date""},{""field"":""district"",""label"":""District""},{""field"":""EPA"",""label"":""EPA""},{""field"":""section"",""label"":""Section""},{""field"":""total_farmers"",""label"":""Total Farmers""}]","[""mw"","" ""]","
\n

E-PICSA - EW Monitoring Malawi

\n \n \n \n
District\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n
","\n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <__version__/>\n \n \n \n \n ","{""icon"":""assets/svgs/monitoring-forms/ew-monitoring.svg""}" 4,2024-02-13 07:47:56.842289+00,Complex Demo Form,demo,"{""hash"":""md5:492f8458063b6e9056d5d7fa928500ce--2851db799b6357cc49f5607b9000579d---1"",""media"":{},""theme"":""grid"",""enketoId"":""poiB"",""languageMap"":{}}","[{""field"":""_id"",""label"":""ID""}]","["" ""]","
\n

The Kitchen Sink

\n \n \n
\n
\n
\n
\n
\n
\n
Was the patient\'s blood drawn?To deselect a radio button just click it again.\n
Was the patient\'s blood drawn?Same question but with a horizontal layout\n
Please choose a response:If you have a lot of response options to display, they can be formatted into columns\n
\n
I have a sufficient energy today
\n
I am able to go to work
\n
I have no problems with self-care
\n
\n
\n
I eat a balanced diet.\n
I get a lot of exercise.\n
LABEL HERE\n
\n
\n
Please choose one or more of the following:
These are organized into nicely structured columns\n
You cannot select both c and d.
How many meals did you eat today?
\n
Please choose a response:
\n
\n
Which meals did Sarah eat today?
Hint text can go here.\n
Which meals did James eat today?
HInt text can go here.\n
What meals did Simon eat today?
HInt text can go here.\n
\n
\n
\n
\n
\n
\n
\n
Ongoing?*\n
This field is required
Expected*\n
This field is required
AE Serious?*\n
This field is required
\n
\n
\n
\n
\n
\n
What is your current level of pain?\n
Please select the blue square\n
In which state will the patient be treated?\n
\n
\n
Which is the greatest contributor to the BMI of ? Other field values are displayed within the codelist labels\n
\n
\n
Select multiple widget (don\'t pick c and d together)\n
You cannot select both c and d.
\n
\n
What a creatinine test conducted?\n
Was a creatinine test conducted?\n
Please provide the subject\'s sex:\n
Did you eat dinner today?\n
\n
\n
Did you eat dinner today?\n
\n
\n
\n
\n
\n
\n
\n
\n \n
","\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n static_instance-ABCD-0\n a\n \n \n static_instance-ABCD-1\n b\n \n \n static_instance-ABCD-2\n c\n \n \n static_instance-ABCD-3\n d\n \n \n \n \n \n static_instance-YN-0\n 1\n \n \n static_instance-YN-1\n 2\n \n \n \n \n \n static_instance-GENERIC_LIST-0\n a\n \n \n static_instance-GENERIC_LIST-1\n b\n \n \n static_instance-GENERIC_LIST-2\n c\n \n \n static_instance-GENERIC_LIST-3\n d\n \n \n static_instance-GENERIC_LIST-4\n e\n \n \n static_instance-GENERIC_LIST-5\n f\n \n \n static_instance-GENERIC_LIST-6\n g\n \n \n static_instance-GENERIC_LIST-7\n h\n \n \n static_instance-GENERIC_LIST-8\n i\n \n \n static_instance-GENERIC_LIST-9\n j\n \n \n \n \n \n static_instance-FACES-0\n 0\n \n \n static_instance-FACES-1\n 1\n \n \n static_instance-FACES-2\n 2\n \n \n static_instance-FACES-3\n 3\n \n \n static_instance-FACES-4\n 4\n \n \n \n \n \n static_instance-WHUNITS-0\n 1\n \n \n static_instance-WHUNITS-1\n 2\n \n \n \n \n \n static_instance-COLORS-0\n red\n \n \n static_instance-COLORS-1\n green\n \n \n static_instance-COLORS-2\n blue\n \n \n static_instance-COLORS-3\n yellow\n \n \n \n \n \n static_instance-ACT-0\n 0\n \n \n static_instance-ACT-1\n 1\n \n \n static_instance-ACT-2\n 2\n \n \n static_instance-ACT-3\n 3\n \n \n static_instance-ACT-4\n 4\n \n \n static_instance-ACT-5\n 5\n \n \n \n \n \n static_instance-us_states-0\n AL\n \n \n static_instance-us_states-1\n AK\n \n \n static_instance-us_states-2\n AZ\n \n \n static_instance-us_states-3\n AR\n \n \n static_instance-us_states-4\n CA\n \n \n static_instance-us_states-5\n CO\n \n \n static_instance-us_states-6\n CT\n \n \n static_instance-us_states-7\n DE\n \n \n static_instance-us_states-8\n FL\n \n \n static_instance-us_states-9\n GA\n \n \n static_instance-us_states-10\n HI\n \n \n static_instance-us_states-11\n ID\n \n \n static_instance-us_states-12\n IL\n \n \n static_instance-us_states-13\n IN\n \n \n static_instance-us_states-14\n IA\n \n \n static_instance-us_states-15\n KS\n \n \n static_instance-us_states-16\n KY\n \n \n static_instance-us_states-17\n LA\n \n \n static_instance-us_states-18\n MA\n \n \n static_instance-us_states-19\n MD\n \n \n static_instance-us_states-20\n ME\n \n \n static_instance-us_states-21\n MI\n \n \n static_instance-us_states-22\n MN\n \n \n static_instance-us_states-23\n MO\n \n \n static_instance-us_states-24\n MS\n \n \n static_instance-us_states-25\n MT\n \n \n static_instance-us_states-26\n NC\n \n \n static_instance-us_states-27\n ND\n \n \n static_instance-us_states-28\n NE\n \n \n static_instance-us_states-29\n NH\n \n \n static_instance-us_states-30\n NJ\n \n \n static_instance-us_states-31\n NM\n \n \n static_instance-us_states-32\n NV\n \n \n static_instance-us_states-33\n NY\n \n \n static_instance-us_states-34\n OH\n \n \n static_instance-us_states-35\n OK\n \n \n static_instance-us_states-36\n OR\n \n \n static_instance-us_states-37\n PA\n \n \n static_instance-us_states-38\n RI\n \n \n static_instance-us_states-39\n SC\n \n \n static_instance-us_states-40\n SD\n \n \n static_instance-us_states-41\n TN\n \n \n static_instance-us_states-42\n TX\n \n \n static_instance-us_states-43\n UT\n \n \n static_instance-us_states-44\n VA\n \n \n static_instance-us_states-45\n VT\n \n \n static_instance-us_states-46\n WA\n \n \n static_instance-us_states-47\n WI\n \n \n static_instance-us_states-48\n WV\n \n \n static_instance-us_states-49\n WY\n \n \n static_instance-us_states-50\n DC\n \n \n \n \n \n static_instance-BMIRES-0\n 2\n \n \n static_instance-BMIRES-1\n 3\n \n \n \n \n \n static_instance-SEX-0\n 1\n \n \n static_instance-SEX-1\n 2\n \n \n \n \n \n static_instance-RL-0\n 1\n \n \n static_instance-RL-1\n 2\n \n \n static_instance-RL-2\n 3\n \n \n static_instance-RL-3\n 4\n \n \n static_instance-RL-4\n 5\n \n \n \n \n \n static_instance-TG-0\n 1\n \n \n static_instance-TG-1\n 2\n \n \n static_instance-TG-2\n 3\n \n \n static_instance-TG-3\n 4\n \n \n static_instance-TG-4\n 5\n \n \n \n \n \n static_instance-CASCADE1-0\n 1\n \n \n static_instance-CASCADE1-1\n 2\n \n \n static_instance-CASCADE1-2\n 3\n \n \n static_instance-CASCADE1-3\n 4\n \n \n static_instance-CASCADE1-4\n 1\n 5\n \n \n static_instance-CASCADE1-5\n 1\n 6\n \n \n \n \n \n static_instance-MEALS-0\n 1\n \n \n static_instance-MEALS-1\n 2\n \n \n static_instance-MEALS-2\n 3\n \n \n static_instance-MEALS-3\n 4\n \n \n \n \n \n static_instance-OUT-0\n 0\n \n \n static_instance-OUT-1\n 1\n \n \n static_instance-OUT-2\n 2\n \n \n static_instance-OUT-3\n 3\n \n \n static_instance-OUT-4\n 4\n \n \n \n \n \n static_instance-ACKNOWLEDGE-0\n 1\n \n \n \n \n \n static_instance-CASCADE2-0\n 1\n 1\n \n \n static_instance-CASCADE2-1\n 1\n 2\n \n \n static_instance-CASCADE2-2\n 1\n 3\n \n \n static_instance-CASCADE2-3\n 1\n 4\n \n \n static_instance-CASCADE2-4\n 2\n 5\n \n \n static_instance-CASCADE2-5\n 2\n 6\n \n \n static_instance-CASCADE2-6\n 1\n 7\n 1\n \n \n static_instance-CASCADE2-7\n 1\n 8\n 1\n \n \n \n \n \n static_instance-CASCADE3-0\n 1\n 1\n 1\n \n \n static_instance-CASCADE3-1\n 1\n 2\n 1\n \n \n static_instance-CASCADE3-2\n 1\n 3\n 1\n \n \n static_instance-CASCADE3-3\n 1\n 4\n 1\n \n \n static_instance-CASCADE3-4\n 1\n 5\n 1\n \n \n static_instance-CASCADE3-5\n 1\n 6\n 1\n \n \n static_instance-CASCADE3-6\n 1\n 7\n 2\n \n \n static_instance-CASCADE3-7\n 1\n 8\n 2\n \n \n static_instance-CASCADE3-8\n 1\n 9\n 2\n \n \n static_instance-CASCADE3-9\n 1\n 10\n 2\n \n \n static_instance-CASCADE3-10\n 2\n 11\n 7\n \n \n static_instance-CASCADE3-11\n 2\n 12\n 7\n \n \n static_instance-CASCADE3-12\n 2\n 13\n 7\n \n \n static_instance-CASCADE3-13\n 2\n 14\n 7\n \n \n static_instance-CASCADE3-14\n 2\n 15\n 7\n \n \n static_instance-CASCADE3-15\n 2\n 16\n 8\n \n \n static_instance-CASCADE3-16\n 2\n 17\n 8\n \n \n static_instance-CASCADE3-17\n 2\n 18\n 8\n \n \n static_instance-CASCADE3-18\n 19\n \n \n static_instance-CASCADE3-19\n 20\n \n \n \n \n \n static_instance-UNITS-0\n 1\n \n \n static_instance-UNITS-1\n 2\n \n \n \n \n \n static_instance-BODYSYSTEM-0\n 1\n \n \n static_instance-BODYSYSTEM-1\n 2\n \n \n static_instance-BODYSYSTEM-2\n 3\n \n \n static_instance-BODYSYSTEM-3\n 4\n \n \n \n \n \n static_instance-DATE_PRECISION-0\n 1\n \n \n static_instance-DATE_PRECISION-1\n 2\n \n \n static_instance-DATE_PRECISION-2\n 3\n \n \n \n \n \n static_instance-AGREEMENT-0\n 1\n \n \n static_instance-AGREEMENT-1\n 2\n \n \n static_instance-AGREEMENT-2\n 3\n \n \n static_instance-AGREEMENT-3\n 4\n \n \n static_instance-AGREEMENT-4\n 5\n \n \n ","{""icon"":""""}" 3,2024-02-13 07:37:01.044168+00,Extension Worker Monitoring,Zambia,"{""hash"":""md5:3166205f2f70f1e29ea86d6dbcd8b36c--d97bac4e49bda9c52dee96737583f620---1"",""media"":{},""theme"":""grid"",""maxSize"":10000000,""enketoId"":""znNPkvlJ"",""languageMap"":{},""externalData"":[]}","[{""field"":""date"",""label"":""Date""},{""field"":""district"",""label"":""District""},{""field"":""EPA"",""label"":""EPA""},{""field"":""section"",""label"":""Section""},{""field"":""total_farmers"",""label"":""Total Farmers""}]","[""zm"","" ""]","
\n

E-PICSA - EW Monitoring Zambia

\n \n \n \n
Organisation\n
\n
District\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n
","\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <__version__/>\n \n \n \n \n ","{""icon"":""assets/svgs/monitoring-forms/ew-monitoring.svg""}" \ No newline at end of file From a80ab2319c9213a10432037be9dc3aaae16bc1d3 Mon Sep 17 00:00:00 2001 From: khalifan-kfan Date: Tue, 27 Feb 2024 23:19:51 +0300 Subject: [PATCH 64/68] feat: add view of a single form --- .../modules/monitoring/monitoring.module.ts | 4 +- .../modules/monitoring/monitoring.service.ts | 2 +- .../pages/home/monitoring.page.html | 12 ++-- .../pages/home/monitoring.page.scss | 8 +-- .../monitoring/pages/home/monitoring.page.ts | 14 ++++- .../new/new-monitoring-forms.component.html | 4 -- .../new/new-monitoring-forms.component.scss | 26 -------- .../new-monitoring-forms.component.spec.ts | 21 ------- .../new/new-monitoring-forms.component.ts | 30 --------- .../view/view-monitoring-forms.component.html | 62 +++++++++++++++++++ .../view/view-monitoring-forms.component.scss | 24 +++++++ .../view-monitoring-forms.component.spec.ts | 21 +++++++ .../view/view-monitoring-forms.component.ts | 39 ++++++++++++ package.json | 1 + yarn.lock | 10 +++ 15 files changed, 182 insertions(+), 96 deletions(-) delete mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.html delete mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.scss delete mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.spec.ts delete mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/new/new-monitoring-forms.component.ts create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.html create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.scss create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.spec.ts create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts index 663695554..6da4dbc6a 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts @@ -3,7 +3,7 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { MonitoringPageComponent } from './pages/home/monitoring.page'; -import { NewMonitoringFormsComponent } from './pages/new/new-monitoring-forms.component'; +import { ViewMonitoringFormsComponent } from './pages/view/view-monitoring-forms.component'; @NgModule({ declarations: [], @@ -16,7 +16,7 @@ import { NewMonitoringFormsComponent } from './pages/new/new-monitoring-forms.co }, { path: ':id', - component: NewMonitoringFormsComponent, + component: ViewMonitoringFormsComponent, } ]), ], diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts index 31e11ec42..11d23ced2 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts @@ -40,7 +40,7 @@ export class MonitoringFormsDashboardService extends PicsaAsyncService { } // Fetch a form record by ID - public async getTranslationById(id: string): Promise { + public async getFormById(id: string): Promise { const { data, error } = await this.supabaseService.db.table(this.TABLE_NAME).select('*').eq('id', id).single(); if (error) { throw error; diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html index 07f6e7321..f10d9bfac 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html @@ -7,23 +7,25 @@

Monitoring Froms

PICSA Table

- @for (item of values; track $index) { {{ item['label'] }}
- } + @for (item of values; track $index) { {{ item['label'] }}
}
{{values}}
- +
{{values}}
+ + {{ value | date: 'mediumDate' }} +
+
+ {{form.enketo_form}} +
+
+
+ +
+ {{form.enketo_model}} +
+
+ + + + + } @else if(dataLoadError) { +
{{ dataLoadError }}
+ } @else { +
Loading...
+ } + \ No newline at end of file diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.scss b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.scss new file mode 100644 index 000000000..5e710ebdf --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.scss @@ -0,0 +1,24 @@ +.form-content{ + display: flex; + flex-direction: column; + gap: 1.4rem; +} +.submitButton{ + width: 7rem; + margin-bottom: 1rem; +} +.form-data{ + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.data-container{ + margin-left: 2rem; + max-height: 25rem; + overflow-y: auto; +} +label{ + font-weight: 700; + +} \ No newline at end of file diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.spec.ts new file mode 100644 index 000000000..d7ffd87fb --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ViewMonitoringFormsComponent } from './view-monitoring-forms.component' + +describe('ViewMonitoringFormsComponent', () => { + let component: ViewMonitoringFormsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ViewMonitoringFormsComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ViewMonitoringFormsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts new file mode 100644 index 000000000..f3d9e400b --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts @@ -0,0 +1,39 @@ +import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +// eslint-disable-next-line @nx/enforce-module-boundaries +import type { Database } from '@picsa/server-types'; +import { NgxJsonViewerModule } from 'ngx-json-viewer'; + +import { DashboardMaterialModule } from '../../../../material.module'; +import { MonitoringFormsDashboardService } from '../../monitoring.service'; + +export type IMonitoringFormsRow = Database['public']['Tables']['monitoring_forms']['Row']; + +@Component({ + selector: 'dashboard-monitoring-view', + standalone: true, + imports: [CommonModule, DashboardMaterialModule, FormsModule, ReactiveFormsModule,NgxJsonViewerModule], + templateUrl: './view-monitoring-forms.component.html', + styleUrls: ['./view-monitoring-forms.component.scss'], +}) +export class ViewMonitoringFormsComponent { + public form: IMonitoringFormsRow; + dataLoadError: string; + constructor(private service: MonitoringFormsDashboardService, private route: ActivatedRoute, private router: Router) { + this.service.ready(); + this.route.params.subscribe((params) => { + const id = params['id']; + this.service + .getFormById(id) + .then((data) => { + this.form = data; + }) + .catch((error) => { + console.error('Error fetching translation:', error); + this.dataLoadError = 'Failed to fetch translation.'; + }); + }); + } +} diff --git a/package.json b/package.json index 6c6c65619..b3f739a19 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,7 @@ "mobx": "^6.7.0", "mobx-angular": "^4.7.1", "ngx-extended-pdf-viewer": "^18.1.7", + "ngx-json-viewer": "^3.2.1", "ngx-lottie": "^10.0.0", "openapi-fetch": "^0.8.2", "papaparse": "^5.3.2", diff --git a/yarn.lock b/yarn.lock index c085187ee..121d09274 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16400,6 +16400,15 @@ __metadata: languageName: node linkType: hard +"ngx-json-viewer@npm:^3.2.1": + version: 3.2.1 + resolution: "ngx-json-viewer@npm:3.2.1" + dependencies: + tslib: ^2.3.0 + checksum: 6eec613c4bdf7550ed934781e8ded691cbe9b9599ec9bb10564d3a42ea94df663555a1e5063d5a66124bc6e7c1dc265645d79b3cdf7a8a11dbad37baabbbfc21 + languageName: node + linkType: hard + "ngx-lottie@npm:^10.0.0": version: 10.0.0 resolution: "ngx-lottie@npm:10.0.0" @@ -17624,6 +17633,7 @@ __metadata: mobx-angular: ^4.7.1 ng-packagr: 17.0.1 ngx-extended-pdf-viewer: ^18.1.7 + ngx-json-viewer: ^3.2.1 ngx-lottie: ^10.0.0 nx: 17.1.2 openapi-fetch: ^0.8.2 From cc6fbadc2546f74212f4bd683c7e9fbddc97abd7 Mon Sep 17 00:00:00 2001 From: khalifan-kfan Date: Wed, 28 Feb 2024 10:07:50 +0300 Subject: [PATCH 65/68] feat: display submissions tables --- .../modules/monitoring/monitoring.module.ts | 5 ++ .../modules/monitoring/monitoring.service.ts | 9 +++ .../form-submissions.component.html | 19 ++++++ .../form-submissions.component.scss | 5 ++ .../form-submissions.component.spec.ts | 21 +++++++ .../form-submissions.component.ts | 62 +++++++++++++++++++ .../view/view-monitoring-forms.component.html | 11 ++-- .../view/view-monitoring-forms.component.ts | 9 ++- .../supabase/data/monitoring_forms_rows.csv | 8 +-- .../20240213081336_monitoring_forms_table.sql | 1 + apps/picsa-server/supabase/types/index.ts | 3 + 11 files changed, 142 insertions(+), 11 deletions(-) create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.html create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.scss create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.spec.ts create mode 100644 apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.ts diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts index 6da4dbc6a..bec15d9bc 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts @@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; +import { FormSubmissionsComponent } from './pages/form-submissions/form-submissions.component'; import { MonitoringPageComponent } from './pages/home/monitoring.page'; import { ViewMonitoringFormsComponent } from './pages/view/view-monitoring-forms.component'; @@ -17,6 +18,10 @@ import { ViewMonitoringFormsComponent } from './pages/view/view-monitoring-forms { path: ':id', component: ViewMonitoringFormsComponent, + }, + { + path: ':id/submissions/:slug', + component: FormSubmissionsComponent, } ]), ], diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts index 11d23ced2..33de405e3 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts @@ -17,6 +17,7 @@ export interface IMonitoringStorageEntry extends IStorageEntry { export class MonitoringFormsDashboardService extends PicsaAsyncService { public forms: IMonitoringFormsRow[] = []; public TABLE_NAME = 'monitoring_forms' + public SUBMISSIONS_TABLE_NAME = 'monitoring_tool_submissions' public get table() { return this.supabaseService.db.table(this.TABLE_NAME); @@ -48,4 +49,12 @@ export class MonitoringFormsDashboardService extends PicsaAsyncService { return data; } + public async getSubmissionsByLocalFormId(id: string){ + const { data, error } = await this.supabaseService.db.table(this.SUBMISSIONS_TABLE_NAME).select('*').eq('formId', id); + if (error) { + throw error; + } + return {data, error}; + } + } diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.html b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.html new file mode 100644 index 000000000..fddb76254 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.html @@ -0,0 +1,19 @@ +
+
+

Form Submissions

+
+ @if(submissions){ + + } + @else if(dataLoadError){ +
{{ dataLoadError }}
+ } + @else { +
Loading...
+ } + +
\ No newline at end of file diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.scss b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.scss new file mode 100644 index 000000000..757634646 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.scss @@ -0,0 +1,5 @@ +.form-content{ + display: flex; + flex-direction: column; + gap: 1.4rem; +} diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.spec.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.spec.ts new file mode 100644 index 000000000..010a3cb44 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormSubmissionsComponent } from './form-submissions.component' + +describe('FormSubmissionsComponent', () => { + let component: FormSubmissionsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormSubmissionsComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(FormSubmissionsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.ts new file mode 100644 index 000000000..c4baeb263 --- /dev/null +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.ts @@ -0,0 +1,62 @@ +import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +// eslint-disable-next-line @nx/enforce-module-boundaries +import type { Database } from '@picsa/server-types'; +import { IDataTableOptions, PicsaDataTableComponent } from '@picsa/shared/features/data-table'; +import { NgxJsonViewerModule } from 'ngx-json-viewer'; + +import { DashboardMaterialModule } from '../../../../material.module'; +import { MonitoringFormsDashboardService } from '../../monitoring.service'; + +export type IMonitoringSubmissionsRow = Database['public']['Tables']['monitoring_tool_submissions']['Row']; + +@Component({ + selector: 'dashboard-form-submissions', + standalone: true, + imports: [CommonModule, DashboardMaterialModule, FormsModule, ReactiveFormsModule,NgxJsonViewerModule,PicsaDataTableComponent], + templateUrl: './form-submissions.component.html', + styleUrls: ['./form-submissions.component.scss'], +}) +export class FormSubmissionsComponent { + public submissions: any[]; + dataLoadError: string; + displayedColumns: string[]; + + constructor(private service: MonitoringFormsDashboardService, private route: ActivatedRoute, private router: Router) { + this.service.ready(); + this.route.params.subscribe((params) => { + const id = params['slug']; + service.forms.forEach((form)=>{ + if(form.device_form_id === id && form.summary_fields){ + this.displayedColumns = form.summary_fields.map((field) => { + if(field && field['field']) { + return field['field']; + } + return + }); + } + }) + this.fetchSubmissions(id); + }); + } + public tableOptions: IDataTableOptions = { + paginatorSizes: [25, 50] + }; + + async fetchSubmissions(id:string){ + const {data,error} = await this.service.getSubmissionsByLocalFormId(id); + if(error){ + this.dataLoadError = 'Failed to fetch submitions'; + }else{ + this.submissions = data.map((submission) => { + if(submission.json) { + return submission.json + } + return + }); + } + } + +} diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.html b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.html index 76d808826..3ef64cc73 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.html +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.html @@ -1,10 +1,15 @@
-

View Monitoring Form

+
+

Monitoring Form View

+ @if(form && form.device_form_id){ + + } +
@if(form){
-
{{ form.created_at | date: 'mediumDate' }}
+
{{ form.created_at | date: 'mediumDate' }}
@@ -51,8 +56,6 @@

View Monitoring Form

- - } @else if(dataLoadError) {
{{ dataLoadError }}
diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts index f3d9e400b..54888e3ba 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts @@ -14,7 +14,7 @@ export type IMonitoringFormsRow = Database['public']['Tables']['monitoring_forms @Component({ selector: 'dashboard-monitoring-view', standalone: true, - imports: [CommonModule, DashboardMaterialModule, FormsModule, ReactiveFormsModule,NgxJsonViewerModule], + imports: [CommonModule, DashboardMaterialModule, FormsModule, ReactiveFormsModule, NgxJsonViewerModule], templateUrl: './view-monitoring-forms.component.html', styleUrls: ['./view-monitoring-forms.component.scss'], }) @@ -31,9 +31,12 @@ export class ViewMonitoringFormsComponent { this.form = data; }) .catch((error) => { - console.error('Error fetching translation:', error); - this.dataLoadError = 'Failed to fetch translation.'; + console.error('Error fetching Form:', error); + this.dataLoadError = 'Failed to fetch Form.'; }); }); } + openSubmissions = async (formId:string) => { + this.router.navigate([`/monitoring/${this.form.id}/submissions`, formId]); + }; } diff --git a/apps/picsa-server/supabase/data/monitoring_forms_rows.csv b/apps/picsa-server/supabase/data/monitoring_forms_rows.csv index 3949dbb71..8ada1b8ef 100644 --- a/apps/picsa-server/supabase/data/monitoring_forms_rows.csv +++ b/apps/picsa-server/supabase/data/monitoring_forms_rows.csv @@ -1,4 +1,4 @@ -id,created_at,title,description,enketo_definition,summary_fields,app_countries,enketo_form,enketo_model,cover -2,2024-02-13 07:34:03.939328+00,Extension Worker Monitoring,Malawi,"{""hash"":""md5:dc0116ade1cd0dc0a7c0f3ce02bcbddc--d97bac4e49bda9c52dee96737583f620---1"",""media"":{},""theme"":""grid"",""maxSize"":10000000,""enketoId"":""PGpldp9m"",""languageMap"":{},""externalData"":[]}","[{""field"":""date"",""label"":""Date""},{""field"":""district"",""label"":""District""},{""field"":""EPA"",""label"":""EPA""},{""field"":""section"",""label"":""Section""},{""field"":""total_farmers"",""label"":""Total Farmers""}]","[""mw"","" ""]","
\n

E-PICSA - EW Monitoring Malawi

\n \n \n \n
District\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n
","\n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <__version__/>\n \n \n \n \n ","{""icon"":""assets/svgs/monitoring-forms/ew-monitoring.svg""}" -4,2024-02-13 07:47:56.842289+00,Complex Demo Form,demo,"{""hash"":""md5:492f8458063b6e9056d5d7fa928500ce--2851db799b6357cc49f5607b9000579d---1"",""media"":{},""theme"":""grid"",""enketoId"":""poiB"",""languageMap"":{}}","[{""field"":""_id"",""label"":""ID""}]","["" ""]","
\n

The Kitchen Sink

\n \n \n
\n
\n
\n
\n
\n
\n
Was the patient\'s blood drawn?To deselect a radio button just click it again.\n
Was the patient\'s blood drawn?Same question but with a horizontal layout\n
Please choose a response:If you have a lot of response options to display, they can be formatted into columns\n
\n
I have a sufficient energy today
\n
I am able to go to work
\n
I have no problems with self-care
\n
\n
\n
I eat a balanced diet.\n
I get a lot of exercise.\n
LABEL HERE\n
\n
\n
Please choose one or more of the following:
These are organized into nicely structured columns\n
You cannot select both c and d.
How many meals did you eat today?
\n
Please choose a response:
\n
\n
Which meals did Sarah eat today?
Hint text can go here.\n
Which meals did James eat today?
HInt text can go here.\n
What meals did Simon eat today?
HInt text can go here.\n
\n
\n
\n
\n
\n
\n
\n
Ongoing?*\n
This field is required
Expected*\n
This field is required
AE Serious?*\n
This field is required
\n
\n
\n
\n
\n
\n
What is your current level of pain?\n
Please select the blue square\n
In which state will the patient be treated?\n
\n
\n
Which is the greatest contributor to the BMI of ? Other field values are displayed within the codelist labels\n
\n
\n
Select multiple widget (don\'t pick c and d together)\n
You cannot select both c and d.
\n
\n
What a creatinine test conducted?\n
Was a creatinine test conducted?\n
Please provide the subject\'s sex:\n
Did you eat dinner today?\n
\n
\n
Did you eat dinner today?\n
\n
\n
\n
\n
\n
\n
\n
\n \n
","\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n static_instance-ABCD-0\n a\n \n \n static_instance-ABCD-1\n b\n \n \n static_instance-ABCD-2\n c\n \n \n static_instance-ABCD-3\n d\n \n \n \n \n \n static_instance-YN-0\n 1\n \n \n static_instance-YN-1\n 2\n \n \n \n \n \n static_instance-GENERIC_LIST-0\n a\n \n \n static_instance-GENERIC_LIST-1\n b\n \n \n static_instance-GENERIC_LIST-2\n c\n \n \n static_instance-GENERIC_LIST-3\n d\n \n \n static_instance-GENERIC_LIST-4\n e\n \n \n static_instance-GENERIC_LIST-5\n f\n \n \n static_instance-GENERIC_LIST-6\n g\n \n \n static_instance-GENERIC_LIST-7\n h\n \n \n static_instance-GENERIC_LIST-8\n i\n \n \n static_instance-GENERIC_LIST-9\n j\n \n \n \n \n \n static_instance-FACES-0\n 0\n \n \n static_instance-FACES-1\n 1\n \n \n static_instance-FACES-2\n 2\n \n \n static_instance-FACES-3\n 3\n \n \n static_instance-FACES-4\n 4\n \n \n \n \n \n static_instance-WHUNITS-0\n 1\n \n \n static_instance-WHUNITS-1\n 2\n \n \n \n \n \n static_instance-COLORS-0\n red\n \n \n static_instance-COLORS-1\n green\n \n \n static_instance-COLORS-2\n blue\n \n \n static_instance-COLORS-3\n yellow\n \n \n \n \n \n static_instance-ACT-0\n 0\n \n \n static_instance-ACT-1\n 1\n \n \n static_instance-ACT-2\n 2\n \n \n static_instance-ACT-3\n 3\n \n \n static_instance-ACT-4\n 4\n \n \n static_instance-ACT-5\n 5\n \n \n \n \n \n static_instance-us_states-0\n AL\n \n \n static_instance-us_states-1\n AK\n \n \n static_instance-us_states-2\n AZ\n \n \n static_instance-us_states-3\n AR\n \n \n static_instance-us_states-4\n CA\n \n \n static_instance-us_states-5\n CO\n \n \n static_instance-us_states-6\n CT\n \n \n static_instance-us_states-7\n DE\n \n \n static_instance-us_states-8\n FL\n \n \n static_instance-us_states-9\n GA\n \n \n static_instance-us_states-10\n HI\n \n \n static_instance-us_states-11\n ID\n \n \n static_instance-us_states-12\n IL\n \n \n static_instance-us_states-13\n IN\n \n \n static_instance-us_states-14\n IA\n \n \n static_instance-us_states-15\n KS\n \n \n static_instance-us_states-16\n KY\n \n \n static_instance-us_states-17\n LA\n \n \n static_instance-us_states-18\n MA\n \n \n static_instance-us_states-19\n MD\n \n \n static_instance-us_states-20\n ME\n \n \n static_instance-us_states-21\n MI\n \n \n static_instance-us_states-22\n MN\n \n \n static_instance-us_states-23\n MO\n \n \n static_instance-us_states-24\n MS\n \n \n static_instance-us_states-25\n MT\n \n \n static_instance-us_states-26\n NC\n \n \n static_instance-us_states-27\n ND\n \n \n static_instance-us_states-28\n NE\n \n \n static_instance-us_states-29\n NH\n \n \n static_instance-us_states-30\n NJ\n \n \n static_instance-us_states-31\n NM\n \n \n static_instance-us_states-32\n NV\n \n \n static_instance-us_states-33\n NY\n \n \n static_instance-us_states-34\n OH\n \n \n static_instance-us_states-35\n OK\n \n \n static_instance-us_states-36\n OR\n \n \n static_instance-us_states-37\n PA\n \n \n static_instance-us_states-38\n RI\n \n \n static_instance-us_states-39\n SC\n \n \n static_instance-us_states-40\n SD\n \n \n static_instance-us_states-41\n TN\n \n \n static_instance-us_states-42\n TX\n \n \n static_instance-us_states-43\n UT\n \n \n static_instance-us_states-44\n VA\n \n \n static_instance-us_states-45\n VT\n \n \n static_instance-us_states-46\n WA\n \n \n static_instance-us_states-47\n WI\n \n \n static_instance-us_states-48\n WV\n \n \n static_instance-us_states-49\n WY\n \n \n static_instance-us_states-50\n DC\n \n \n \n \n \n static_instance-BMIRES-0\n 2\n \n \n static_instance-BMIRES-1\n 3\n \n \n \n \n \n static_instance-SEX-0\n 1\n \n \n static_instance-SEX-1\n 2\n \n \n \n \n \n static_instance-RL-0\n 1\n \n \n static_instance-RL-1\n 2\n \n \n static_instance-RL-2\n 3\n \n \n static_instance-RL-3\n 4\n \n \n static_instance-RL-4\n 5\n \n \n \n \n \n static_instance-TG-0\n 1\n \n \n static_instance-TG-1\n 2\n \n \n static_instance-TG-2\n 3\n \n \n static_instance-TG-3\n 4\n \n \n static_instance-TG-4\n 5\n \n \n \n \n \n static_instance-CASCADE1-0\n 1\n \n \n static_instance-CASCADE1-1\n 2\n \n \n static_instance-CASCADE1-2\n 3\n \n \n static_instance-CASCADE1-3\n 4\n \n \n static_instance-CASCADE1-4\n 1\n 5\n \n \n static_instance-CASCADE1-5\n 1\n 6\n \n \n \n \n \n static_instance-MEALS-0\n 1\n \n \n static_instance-MEALS-1\n 2\n \n \n static_instance-MEALS-2\n 3\n \n \n static_instance-MEALS-3\n 4\n \n \n \n \n \n static_instance-OUT-0\n 0\n \n \n static_instance-OUT-1\n 1\n \n \n static_instance-OUT-2\n 2\n \n \n static_instance-OUT-3\n 3\n \n \n static_instance-OUT-4\n 4\n \n \n \n \n \n static_instance-ACKNOWLEDGE-0\n 1\n \n \n \n \n \n static_instance-CASCADE2-0\n 1\n 1\n \n \n static_instance-CASCADE2-1\n 1\n 2\n \n \n static_instance-CASCADE2-2\n 1\n 3\n \n \n static_instance-CASCADE2-3\n 1\n 4\n \n \n static_instance-CASCADE2-4\n 2\n 5\n \n \n static_instance-CASCADE2-5\n 2\n 6\n \n \n static_instance-CASCADE2-6\n 1\n 7\n 1\n \n \n static_instance-CASCADE2-7\n 1\n 8\n 1\n \n \n \n \n \n static_instance-CASCADE3-0\n 1\n 1\n 1\n \n \n static_instance-CASCADE3-1\n 1\n 2\n 1\n \n \n static_instance-CASCADE3-2\n 1\n 3\n 1\n \n \n static_instance-CASCADE3-3\n 1\n 4\n 1\n \n \n static_instance-CASCADE3-4\n 1\n 5\n 1\n \n \n static_instance-CASCADE3-5\n 1\n 6\n 1\n \n \n static_instance-CASCADE3-6\n 1\n 7\n 2\n \n \n static_instance-CASCADE3-7\n 1\n 8\n 2\n \n \n static_instance-CASCADE3-8\n 1\n 9\n 2\n \n \n static_instance-CASCADE3-9\n 1\n 10\n 2\n \n \n static_instance-CASCADE3-10\n 2\n 11\n 7\n \n \n static_instance-CASCADE3-11\n 2\n 12\n 7\n \n \n static_instance-CASCADE3-12\n 2\n 13\n 7\n \n \n static_instance-CASCADE3-13\n 2\n 14\n 7\n \n \n static_instance-CASCADE3-14\n 2\n 15\n 7\n \n \n static_instance-CASCADE3-15\n 2\n 16\n 8\n \n \n static_instance-CASCADE3-16\n 2\n 17\n 8\n \n \n static_instance-CASCADE3-17\n 2\n 18\n 8\n \n \n static_instance-CASCADE3-18\n 19\n \n \n static_instance-CASCADE3-19\n 20\n \n \n \n \n \n static_instance-UNITS-0\n 1\n \n \n static_instance-UNITS-1\n 2\n \n \n \n \n \n static_instance-BODYSYSTEM-0\n 1\n \n \n static_instance-BODYSYSTEM-1\n 2\n \n \n static_instance-BODYSYSTEM-2\n 3\n \n \n static_instance-BODYSYSTEM-3\n 4\n \n \n \n \n \n static_instance-DATE_PRECISION-0\n 1\n \n \n static_instance-DATE_PRECISION-1\n 2\n \n \n static_instance-DATE_PRECISION-2\n 3\n \n \n \n \n \n static_instance-AGREEMENT-0\n 1\n \n \n static_instance-AGREEMENT-1\n 2\n \n \n static_instance-AGREEMENT-2\n 3\n \n \n static_instance-AGREEMENT-3\n 4\n \n \n static_instance-AGREEMENT-4\n 5\n \n \n ","{""icon"":""""}" -3,2024-02-13 07:37:01.044168+00,Extension Worker Monitoring,Zambia,"{""hash"":""md5:3166205f2f70f1e29ea86d6dbcd8b36c--d97bac4e49bda9c52dee96737583f620---1"",""media"":{},""theme"":""grid"",""maxSize"":10000000,""enketoId"":""znNPkvlJ"",""languageMap"":{},""externalData"":[]}","[{""field"":""date"",""label"":""Date""},{""field"":""district"",""label"":""District""},{""field"":""EPA"",""label"":""EPA""},{""field"":""section"",""label"":""Section""},{""field"":""total_farmers"",""label"":""Total Farmers""}]","[""zm"","" ""]","
\n

E-PICSA - EW Monitoring Zambia

\n \n \n \n
Organisation\n
\n
District\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n
","\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <__version__/>\n \n \n \n \n ","{""icon"":""assets/svgs/monitoring-forms/ew-monitoring.svg""}" \ No newline at end of file +id,created_at,title,description,enketo_definition,summary_fields,app_countries,enketo_form,enketo_model,cover,device_form_id +2,2024-02-13 07:34:03.939328+00,Extension Worker Monitoring,Malawi,"{""hash"":""md5:dc0116ade1cd0dc0a7c0f3ce02bcbddc--d97bac4e49bda9c52dee96737583f620---1"",""media"":{},""theme"":""grid"",""maxSize"":10000000,""enketoId"":""PGpldp9m"",""languageMap"":{},""externalData"":[]}","[{""field"":""date"",""label"":""Date""},{""field"":""district"",""label"":""District""},{""field"":""EPA"",""label"":""EPA""},{""field"":""section"",""label"":""Section""},{""field"":""total_farmers"",""label"":""Total Farmers""}]","[""mw"","" ""]","
\n

E-PICSA - EW Monitoring Malawi

\n \n \n \n
District\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n
","\n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <__version__/>\n \n \n \n \n ","{""icon"":""assets/svgs/monitoring-forms/ew-monitoring.svg""}",ew_monitoring +3,2024-02-13 07:37:01.044168+00,Extension Worker Monitoring,Zambia,"{""hash"":""md5:3166205f2f70f1e29ea86d6dbcd8b36c--d97bac4e49bda9c52dee96737583f620---1"",""media"":{},""theme"":""grid"",""maxSize"":10000000,""enketoId"":""znNPkvlJ"",""languageMap"":{},""externalData"":[]}","[{""field"":""date"",""label"":""Date""},{""field"":""district"",""label"":""District""},{""field"":""EPA"",""label"":""EPA""},{""field"":""section"",""label"":""Section""},{""field"":""total_farmers"",""label"":""Total Farmers""}]","[""zm"","" ""]","
\n

E-PICSA - EW Monitoring Zambia

\n \n \n \n
Organisation\n
\n
District\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n
","\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <__version__/>\n \n \n \n \n ","{""icon"":""assets/svgs/monitoring-forms/ew-monitoring.svg""}",ew_monitoring_zm +4,2024-02-13 07:47:56.842289+00,Complex Demo Form,demo,"{""hash"":""md5:492f8458063b6e9056d5d7fa928500ce--2851db799b6357cc49f5607b9000579d---1"",""media"":{},""theme"":""grid"",""enketoId"":""poiB"",""languageMap"":{}}","[{""field"":""_id"",""label"":""ID""}]","["" ""]","
\n

The Kitchen Sink

\n \n \n
\n
\n
\n
\n
\n
\n
Was the patient\'s blood drawn?To deselect a radio button just click it again.\n
Was the patient\'s blood drawn?Same question but with a horizontal layout\n
Please choose a response:If you have a lot of response options to display, they can be formatted into columns\n
\n
I have a sufficient energy today
\n
I am able to go to work
\n
I have no problems with self-care
\n
\n
\n
I eat a balanced diet.\n
I get a lot of exercise.\n
LABEL HERE\n
\n
\n
Please choose one or more of the following:
These are organized into nicely structured columns\n
You cannot select both c and d.
How many meals did you eat today?
\n
Please choose a response:
\n
\n
Which meals did Sarah eat today?
Hint text can go here.\n
Which meals did James eat today?
HInt text can go here.\n
What meals did Simon eat today?
HInt text can go here.\n
\n
\n
\n
\n
\n
\n
\n
Ongoing?*\n
This field is required
Expected*\n
This field is required
AE Serious?*\n
This field is required
\n
\n
\n
\n
\n
\n
What is your current level of pain?\n
Please select the blue square\n
In which state will the patient be treated?\n
\n
\n
Which is the greatest contributor to the BMI of ? Other field values are displayed within the codelist labels\n
\n
\n
Select multiple widget (don\'t pick c and d together)\n
You cannot select both c and d.
\n
\n
What a creatinine test conducted?\n
Was a creatinine test conducted?\n
Please provide the subject\'s sex:\n
Did you eat dinner today?\n
\n
\n
Did you eat dinner today?\n
\n
\n
\n
\n
\n
\n
\n
\n \n
","\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n static_instance-ABCD-0\n a\n \n \n static_instance-ABCD-1\n b\n \n \n static_instance-ABCD-2\n c\n \n \n static_instance-ABCD-3\n d\n \n \n \n \n \n static_instance-YN-0\n 1\n \n \n static_instance-YN-1\n 2\n \n \n \n \n \n static_instance-GENERIC_LIST-0\n a\n \n \n static_instance-GENERIC_LIST-1\n b\n \n \n static_instance-GENERIC_LIST-2\n c\n \n \n static_instance-GENERIC_LIST-3\n d\n \n \n static_instance-GENERIC_LIST-4\n e\n \n \n static_instance-GENERIC_LIST-5\n f\n \n \n static_instance-GENERIC_LIST-6\n g\n \n \n static_instance-GENERIC_LIST-7\n h\n \n \n static_instance-GENERIC_LIST-8\n i\n \n \n static_instance-GENERIC_LIST-9\n j\n \n \n \n \n \n static_instance-FACES-0\n 0\n \n \n static_instance-FACES-1\n 1\n \n \n static_instance-FACES-2\n 2\n \n \n static_instance-FACES-3\n 3\n \n \n static_instance-FACES-4\n 4\n \n \n \n \n \n static_instance-WHUNITS-0\n 1\n \n \n static_instance-WHUNITS-1\n 2\n \n \n \n \n \n static_instance-COLORS-0\n red\n \n \n static_instance-COLORS-1\n green\n \n \n static_instance-COLORS-2\n blue\n \n \n static_instance-COLORS-3\n yellow\n \n \n \n \n \n static_instance-ACT-0\n 0\n \n \n static_instance-ACT-1\n 1\n \n \n static_instance-ACT-2\n 2\n \n \n static_instance-ACT-3\n 3\n \n \n static_instance-ACT-4\n 4\n \n \n static_instance-ACT-5\n 5\n \n \n \n \n \n static_instance-us_states-0\n AL\n \n \n static_instance-us_states-1\n AK\n \n \n static_instance-us_states-2\n AZ\n \n \n static_instance-us_states-3\n AR\n \n \n static_instance-us_states-4\n CA\n \n \n static_instance-us_states-5\n CO\n \n \n static_instance-us_states-6\n CT\n \n \n static_instance-us_states-7\n DE\n \n \n static_instance-us_states-8\n FL\n \n \n static_instance-us_states-9\n GA\n \n \n static_instance-us_states-10\n HI\n \n \n static_instance-us_states-11\n ID\n \n \n static_instance-us_states-12\n IL\n \n \n static_instance-us_states-13\n IN\n \n \n static_instance-us_states-14\n IA\n \n \n static_instance-us_states-15\n KS\n \n \n static_instance-us_states-16\n KY\n \n \n static_instance-us_states-17\n LA\n \n \n static_instance-us_states-18\n MA\n \n \n static_instance-us_states-19\n MD\n \n \n static_instance-us_states-20\n ME\n \n \n static_instance-us_states-21\n MI\n \n \n static_instance-us_states-22\n MN\n \n \n static_instance-us_states-23\n MO\n \n \n static_instance-us_states-24\n MS\n \n \n static_instance-us_states-25\n MT\n \n \n static_instance-us_states-26\n NC\n \n \n static_instance-us_states-27\n ND\n \n \n static_instance-us_states-28\n NE\n \n \n static_instance-us_states-29\n NH\n \n \n static_instance-us_states-30\n NJ\n \n \n static_instance-us_states-31\n NM\n \n \n static_instance-us_states-32\n NV\n \n \n static_instance-us_states-33\n NY\n \n \n static_instance-us_states-34\n OH\n \n \n static_instance-us_states-35\n OK\n \n \n static_instance-us_states-36\n OR\n \n \n static_instance-us_states-37\n PA\n \n \n static_instance-us_states-38\n RI\n \n \n static_instance-us_states-39\n SC\n \n \n static_instance-us_states-40\n SD\n \n \n static_instance-us_states-41\n TN\n \n \n static_instance-us_states-42\n TX\n \n \n static_instance-us_states-43\n UT\n \n \n static_instance-us_states-44\n VA\n \n \n static_instance-us_states-45\n VT\n \n \n static_instance-us_states-46\n WA\n \n \n static_instance-us_states-47\n WI\n \n \n static_instance-us_states-48\n WV\n \n \n static_instance-us_states-49\n WY\n \n \n static_instance-us_states-50\n DC\n \n \n \n \n \n static_instance-BMIRES-0\n 2\n \n \n static_instance-BMIRES-1\n 3\n \n \n \n \n \n static_instance-SEX-0\n 1\n \n \n static_instance-SEX-1\n 2\n \n \n \n \n \n static_instance-RL-0\n 1\n \n \n static_instance-RL-1\n 2\n \n \n static_instance-RL-2\n 3\n \n \n static_instance-RL-3\n 4\n \n \n static_instance-RL-4\n 5\n \n \n \n \n \n static_instance-TG-0\n 1\n \n \n static_instance-TG-1\n 2\n \n \n static_instance-TG-2\n 3\n \n \n static_instance-TG-3\n 4\n \n \n static_instance-TG-4\n 5\n \n \n \n \n \n static_instance-CASCADE1-0\n 1\n \n \n static_instance-CASCADE1-1\n 2\n \n \n static_instance-CASCADE1-2\n 3\n \n \n static_instance-CASCADE1-3\n 4\n \n \n static_instance-CASCADE1-4\n 1\n 5\n \n \n static_instance-CASCADE1-5\n 1\n 6\n \n \n \n \n \n static_instance-MEALS-0\n 1\n \n \n static_instance-MEALS-1\n 2\n \n \n static_instance-MEALS-2\n 3\n \n \n static_instance-MEALS-3\n 4\n \n \n \n \n \n static_instance-OUT-0\n 0\n \n \n static_instance-OUT-1\n 1\n \n \n static_instance-OUT-2\n 2\n \n \n static_instance-OUT-3\n 3\n \n \n static_instance-OUT-4\n 4\n \n \n \n \n \n static_instance-ACKNOWLEDGE-0\n 1\n \n \n \n \n \n static_instance-CASCADE2-0\n 1\n 1\n \n \n static_instance-CASCADE2-1\n 1\n 2\n \n \n static_instance-CASCADE2-2\n 1\n 3\n \n \n static_instance-CASCADE2-3\n 1\n 4\n \n \n static_instance-CASCADE2-4\n 2\n 5\n \n \n static_instance-CASCADE2-5\n 2\n 6\n \n \n static_instance-CASCADE2-6\n 1\n 7\n 1\n \n \n static_instance-CASCADE2-7\n 1\n 8\n 1\n \n \n \n \n \n static_instance-CASCADE3-0\n 1\n 1\n 1\n \n \n static_instance-CASCADE3-1\n 1\n 2\n 1\n \n \n static_instance-CASCADE3-2\n 1\n 3\n 1\n \n \n static_instance-CASCADE3-3\n 1\n 4\n 1\n \n \n static_instance-CASCADE3-4\n 1\n 5\n 1\n \n \n static_instance-CASCADE3-5\n 1\n 6\n 1\n \n \n static_instance-CASCADE3-6\n 1\n 7\n 2\n \n \n static_instance-CASCADE3-7\n 1\n 8\n 2\n \n \n static_instance-CASCADE3-8\n 1\n 9\n 2\n \n \n static_instance-CASCADE3-9\n 1\n 10\n 2\n \n \n static_instance-CASCADE3-10\n 2\n 11\n 7\n \n \n static_instance-CASCADE3-11\n 2\n 12\n 7\n \n \n static_instance-CASCADE3-12\n 2\n 13\n 7\n \n \n static_instance-CASCADE3-13\n 2\n 14\n 7\n \n \n static_instance-CASCADE3-14\n 2\n 15\n 7\n \n \n static_instance-CASCADE3-15\n 2\n 16\n 8\n \n \n static_instance-CASCADE3-16\n 2\n 17\n 8\n \n \n static_instance-CASCADE3-17\n 2\n 18\n 8\n \n \n static_instance-CASCADE3-18\n 19\n \n \n static_instance-CASCADE3-19\n 20\n \n \n \n \n \n static_instance-UNITS-0\n 1\n \n \n static_instance-UNITS-1\n 2\n \n \n \n \n \n static_instance-BODYSYSTEM-0\n 1\n \n \n static_instance-BODYSYSTEM-1\n 2\n \n \n static_instance-BODYSYSTEM-2\n 3\n \n \n static_instance-BODYSYSTEM-3\n 4\n \n \n \n \n \n static_instance-DATE_PRECISION-0\n 1\n \n \n static_instance-DATE_PRECISION-1\n 2\n \n \n static_instance-DATE_PRECISION-2\n 3\n \n \n \n \n \n static_instance-AGREEMENT-0\n 1\n \n \n static_instance-AGREEMENT-1\n 2\n \n \n static_instance-AGREEMENT-2\n 3\n \n \n static_instance-AGREEMENT-3\n 4\n \n \n static_instance-AGREEMENT-4\n 5\n \n \n ","{""icon"":""""}",demo_kitchen \ No newline at end of file diff --git a/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql b/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql index 1d618195b..b7b40c357 100644 --- a/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql +++ b/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql @@ -10,5 +10,6 @@ create table enketo_form text null, enketo_model text null, cover jsonb null, + device_form_id character varying null, constraint monitoring_forms_pkey primary key (id) ) tablespace pg_default; \ No newline at end of file diff --git a/apps/picsa-server/supabase/types/index.ts b/apps/picsa-server/supabase/types/index.ts index 94fe37fa7..5ac89c9d5 100644 --- a/apps/picsa-server/supabase/types/index.ts +++ b/apps/picsa-server/supabase/types/index.ts @@ -183,6 +183,7 @@ export interface Database { cover: Json | null created_at: string description: string | null + device_form_id: string | null enketo_definition: Json | null enketo_form: string | null enketo_model: string | null @@ -195,6 +196,7 @@ export interface Database { cover?: Json | null created_at?: string description?: string | null + device_form_id?: string | null enketo_definition?: Json | null enketo_form?: string | null enketo_model?: string | null @@ -207,6 +209,7 @@ export interface Database { cover?: Json | null created_at?: string description?: string | null + device_form_id?: string | null enketo_definition?: Json | null enketo_form?: string | null enketo_model?: string | null From 516b17620a61636d5052f1d15a8b13d60a814220 Mon Sep 17 00:00:00 2001 From: khalifan-kfan Date: Wed, 28 Feb 2024 10:23:28 +0300 Subject: [PATCH 66/68] feat: fix navigation --- apps/picsa-apps/dashboard/src/app/app.routes.ts | 2 +- apps/picsa-apps/dashboard/src/app/data/navLinks.ts | 8 ++++---- .../app/modules/monitoring/pages/home/monitoring.page.ts | 2 +- .../pages/view/view-monitoring-forms.component.ts | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/picsa-apps/dashboard/src/app/app.routes.ts b/apps/picsa-apps/dashboard/src/app/app.routes.ts index ec407e8ba..aa343a224 100644 --- a/apps/picsa-apps/dashboard/src/app/app.routes.ts +++ b/apps/picsa-apps/dashboard/src/app/app.routes.ts @@ -21,7 +21,7 @@ export const appRoutes: Route[] = [ }, // unmatched routes fallback to home { - path: 'monitoring', + path: 'monitoring-forms', loadChildren: () => import('./modules/monitoring/monitoring.module').then((m) => m.MonitoringPageModule), }, { diff --git a/apps/picsa-apps/dashboard/src/app/data/navLinks.ts b/apps/picsa-apps/dashboard/src/app/data/navLinks.ts index d534a3428..76b5e467d 100644 --- a/apps/picsa-apps/dashboard/src/app/data/navLinks.ts +++ b/apps/picsa-apps/dashboard/src/app/data/navLinks.ts @@ -35,10 +35,10 @@ export const DASHBOARD_NAV_LINKS = [ // label: 'Crop Information', // href: '/crop-information', // }, - // { - // label: 'Monitoring Forms', - // href: '/monitoring-forms', - // }, + { + label: 'Monitoring Forms', + href: '/monitoring-forms', + }, { label: 'Translations', href: '/translations', diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts index bb99f6145..200c8dfc3 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts @@ -31,7 +31,7 @@ export class MonitoringPageComponent implements OnInit { } //this was returning undefined for "this.router" before I made it an arrow funtion, any idea why? onRowClick = (row: IMonitoringFormsRow) => { - this.router.navigate([`/monitoring`, row.id]); + this.router.navigate([`/monitoring-forms`, row.id]); } } diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts index 54888e3ba..6b16ac7d2 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts @@ -37,6 +37,6 @@ export class ViewMonitoringFormsComponent { }); } openSubmissions = async (formId:string) => { - this.router.navigate([`/monitoring/${this.form.id}/submissions`, formId]); + this.router.navigate([`/monitoring-forms/${this.form.id}/submissions`, formId]); }; } From 48cb828b5ab5122d7da3e4d81cea6520be385ea0 Mon Sep 17 00:00:00 2001 From: khalifan-kfan Date: Wed, 28 Feb 2024 10:35:11 +0300 Subject: [PATCH 67/68] update code --- apps/picsa-apps/dashboard/src/app/app.routes.ts | 6 +----- apps/picsa-apps/dashboard/src/app/data/navLinks.ts | 1 + .../{monitoring.module.ts => monitoring-forms.module.ts} | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) rename apps/picsa-apps/dashboard/src/app/modules/monitoring/{monitoring.module.ts => monitoring-forms.module.ts} (94%) diff --git a/apps/picsa-apps/dashboard/src/app/app.routes.ts b/apps/picsa-apps/dashboard/src/app/app.routes.ts index aa343a224..7d55c6f46 100644 --- a/apps/picsa-apps/dashboard/src/app/app.routes.ts +++ b/apps/picsa-apps/dashboard/src/app/app.routes.ts @@ -22,11 +22,7 @@ export const appRoutes: Route[] = [ // unmatched routes fallback to home { path: 'monitoring-forms', - loadChildren: () => import('./modules/monitoring/monitoring.module').then((m) => m.MonitoringPageModule), - }, - { - path: '', - redirectTo: 'resources', + loadChildren: () => import('./modules/monitoring/monitoring-forms.module').then((m) => m.MonitoringFormsPageModule), }, { path: '**', diff --git a/apps/picsa-apps/dashboard/src/app/data/navLinks.ts b/apps/picsa-apps/dashboard/src/app/data/navLinks.ts index 76b5e467d..70f1039c2 100644 --- a/apps/picsa-apps/dashboard/src/app/data/navLinks.ts +++ b/apps/picsa-apps/dashboard/src/app/data/navLinks.ts @@ -38,6 +38,7 @@ export const DASHBOARD_NAV_LINKS = [ { label: 'Monitoring Forms', href: '/monitoring-forms', + matIcon: 'library_books', }, { label: 'Translations', diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring-forms.module.ts similarity index 94% rename from apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts rename to apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring-forms.module.ts index bec15d9bc..60f7dc7c0 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.module.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring-forms.module.ts @@ -26,4 +26,4 @@ import { ViewMonitoringFormsComponent } from './pages/view/view-monitoring-forms ]), ], }) -export class MonitoringPageModule {} +export class MonitoringFormsPageModule {} From b5c44f1b0d7f7627e8e5be4c136fb4f0f37fb71a Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Thu, 29 Feb 2024 17:10:59 -0800 Subject: [PATCH 68/68] chore: review tidying --- .../dashboard/src/app/app.routes.ts | 2 +- .../dashboard/src/app/data/navLinks.ts | 4 +- .../monitoring/monitoring-forms.module.ts | 4 +- .../modules/monitoring/monitoring.service.ts | 16 ++--- .../form-submissions.component.html | 27 ++++---- .../form-submissions.component.ts | 52 ++++++++------- .../pages/home/monitoring.page.html | 63 ++---------------- .../monitoring/pages/home/monitoring.page.ts | 21 +++--- .../view/view-monitoring-forms.component.html | 46 ++++++------- .../view/view-monitoring-forms.component.ts | 18 +++-- apps/picsa-server/supabase/data/README.md | 15 +++++ .../supabase/data/monitoring_forms_rows.csv | 8 +-- .../data/monitoring_tool_submissions_rows.csv | 4 ++ .../global/monitoring/forms/widgets.xlsx | Bin 0 -> 20895 bytes .../20240213081336_monitoring_forms_table.sql | 15 ----- .../20240229081336_monitoring_forms_table.sql | 16 +++++ apps/picsa-server/supabase/types/index.ts | 36 ++++++---- .../storage-file-picker.component.ts | 3 +- 18 files changed, 161 insertions(+), 189 deletions(-) create mode 100644 apps/picsa-server/supabase/data/README.md create mode 100644 apps/picsa-server/supabase/data/monitoring_tool_submissions_rows.csv create mode 100644 apps/picsa-server/supabase/data/storage/global/monitoring/forms/widgets.xlsx delete mode 100644 apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql create mode 100644 apps/picsa-server/supabase/migrations/20240229081336_monitoring_forms_table.sql diff --git a/apps/picsa-apps/dashboard/src/app/app.routes.ts b/apps/picsa-apps/dashboard/src/app/app.routes.ts index 7abae8b5b..ce64d130f 100644 --- a/apps/picsa-apps/dashboard/src/app/app.routes.ts +++ b/apps/picsa-apps/dashboard/src/app/app.routes.ts @@ -26,7 +26,7 @@ export const appRoutes: Route[] = [ // unmatched routes fallback to home { - path: 'monitoring-forms', + path: 'monitoring', loadChildren: () => import('./modules/monitoring/monitoring-forms.module').then((m) => m.MonitoringFormsPageModule), }, { diff --git a/apps/picsa-apps/dashboard/src/app/data/navLinks.ts b/apps/picsa-apps/dashboard/src/app/data/navLinks.ts index 70f1039c2..c0f37a4b7 100644 --- a/apps/picsa-apps/dashboard/src/app/data/navLinks.ts +++ b/apps/picsa-apps/dashboard/src/app/data/navLinks.ts @@ -37,8 +37,8 @@ export const DASHBOARD_NAV_LINKS = [ // }, { label: 'Monitoring Forms', - href: '/monitoring-forms', - matIcon: 'library_books', + href: '/monitoring', + matIcon: 'poll', }, { label: 'Translations', diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring-forms.module.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring-forms.module.ts index 60f7dc7c0..3b7c1090f 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring-forms.module.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring-forms.module.ts @@ -20,9 +20,9 @@ import { ViewMonitoringFormsComponent } from './pages/view/view-monitoring-forms component: ViewMonitoringFormsComponent, }, { - path: ':id/submissions/:slug', + path: ':id/submissions', component: FormSubmissionsComponent, - } + }, ]), ], }) diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts index 33de405e3..1e3d9daca 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/monitoring.service.ts @@ -7,8 +7,6 @@ import { IStorageEntry } from '@picsa/shared/services/core/supabase/services/sup export type IMonitoringFormsRow = Database['public']['Tables']['monitoring_forms']['Row']; - - export interface IMonitoringStorageEntry extends IStorageEntry { /** Url generated when upload to public bucket (will always be populated, even if bucket not public) */ publicUrl: string; @@ -16,8 +14,8 @@ export interface IMonitoringStorageEntry extends IStorageEntry { @Injectable({ providedIn: 'root' }) export class MonitoringFormsDashboardService extends PicsaAsyncService { public forms: IMonitoringFormsRow[] = []; - public TABLE_NAME = 'monitoring_forms' - public SUBMISSIONS_TABLE_NAME = 'monitoring_tool_submissions' + public TABLE_NAME = 'monitoring_forms'; + public SUBMISSIONS_TABLE_NAME = 'monitoring_tool_submissions'; public get table() { return this.supabaseService.db.table(this.TABLE_NAME); @@ -49,12 +47,14 @@ export class MonitoringFormsDashboardService extends PicsaAsyncService { return data; } - public async getSubmissionsByLocalFormId(id: string){ - const { data, error } = await this.supabaseService.db.table(this.SUBMISSIONS_TABLE_NAME).select('*').eq('formId', id); + public async getSubmissions(formId: string) { + const { data, error } = await this.supabaseService.db + .table(this.SUBMISSIONS_TABLE_NAME) + .select('*') + .eq('formId', formId); if (error) { throw error; } - return {data, error}; + return { data, error }; } - } diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.html b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.html index fddb76254..a0c9d4adf 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.html +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.html @@ -1,19 +1,18 @@
-

Form Submissions

+

+ @if(form){ + {{ form.title }} + } +

@if(submissions){ - +

Submissions

+ + + } @else if(dataLoadError){ +
{{ dataLoadError }}
+ } @else { +
Loading...
} - @else if(dataLoadError){ -
{{ dataLoadError }}
- } - @else { -
Loading...
- } - -
\ No newline at end of file + diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.ts index c4baeb263..6397cd998 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/form-submissions/form-submissions.component.ts @@ -8,55 +8,57 @@ import { IDataTableOptions, PicsaDataTableComponent } from '@picsa/shared/featur import { NgxJsonViewerModule } from 'ngx-json-viewer'; import { DashboardMaterialModule } from '../../../../material.module'; -import { MonitoringFormsDashboardService } from '../../monitoring.service'; +import { IMonitoringFormsRow, MonitoringFormsDashboardService } from '../../monitoring.service'; export type IMonitoringSubmissionsRow = Database['public']['Tables']['monitoring_tool_submissions']['Row']; @Component({ selector: 'dashboard-form-submissions', standalone: true, - imports: [CommonModule, DashboardMaterialModule, FormsModule, ReactiveFormsModule,NgxJsonViewerModule,PicsaDataTableComponent], + imports: [ + CommonModule, + DashboardMaterialModule, + FormsModule, + ReactiveFormsModule, + NgxJsonViewerModule, + PicsaDataTableComponent, + ], templateUrl: './form-submissions.component.html', styleUrls: ['./form-submissions.component.scss'], }) export class FormSubmissionsComponent { + public form: IMonitoringFormsRow | null = null; public submissions: any[]; dataLoadError: string; displayedColumns: string[]; constructor(private service: MonitoringFormsDashboardService, private route: ActivatedRoute, private router: Router) { this.service.ready(); - this.route.params.subscribe((params) => { - const id = params['slug']; - service.forms.forEach((form)=>{ - if(form.device_form_id === id && form.summary_fields){ - this.displayedColumns = form.summary_fields.map((field) => { - if(field && field['field']) { - return field['field']; - } - return - }); - } - }) - this.fetchSubmissions(id); + this.route.params.subscribe(async (params) => { + const id = params['id']; + const form = await service.getFormById(id); + this.form = form; + if (form && form.summary_fields) { + this.displayedColumns = form.summary_fields.map((entry) => entry?.['field']); + } + await this.fetchSubmissions(id); }); } public tableOptions: IDataTableOptions = { - paginatorSizes: [25, 50] + paginatorSizes: [25, 50], }; - async fetchSubmissions(id:string){ - const {data,error} = await this.service.getSubmissionsByLocalFormId(id); - if(error){ + async fetchSubmissions(formId: string) { + const { data, error } = await this.service.getSubmissions(formId); + if (error) { this.dataLoadError = 'Failed to fetch submitions'; - }else{ - this.submissions = data.map((submission) => { - if(submission.json) { - return submission.json + } else { + this.submissions = data.map((submission) => { + if (submission.json) { + return submission.json; } - return + return; }); } } - } diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html index f10d9bfac..0015901c2 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.html @@ -1,10 +1,8 @@
-

Monitoring Froms

- +

Monitoring Forms

@if(service.forms){ -

PICSA Table

PICSA Table @for (item of values; track $index) { {{ item['label'] }}
} -
- {{values}} -
+
{{values}}
- -
- {{values}} -
-
- - {{ value | date: 'mediumDate' }} + +
{{values}}
+ {{ value | date: 'mediumDate' }}
- - }
diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts index 200c8dfc3..dbbed9d0a 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/home/monitoring.page.ts @@ -1,6 +1,6 @@ import { CommonModule } from '@angular/common'; import { Component, OnInit } from '@angular/core'; -import { RouterModule } from '@angular/router'; +import { ActivatedRoute, RouterModule } from '@angular/router'; import { Router } from '@angular/router'; // eslint-disable-next-line @nx/enforce-module-boundaries import { Database } from '@picsa/server-types'; @@ -10,7 +10,6 @@ import { DashboardMaterialModule } from '../../../../material.module'; import { MonitoringFormsDashboardService } from '../../monitoring.service'; export type IMonitoringFormsRow = Database['public']['Tables']['monitoring_forms']['Row']; - @Component({ selector: 'dashboard-monitoring-page', standalone: true, @@ -19,19 +18,25 @@ export type IMonitoringFormsRow = Database['public']['Tables']['monitoring_forms styleUrls: ['./monitoring.page.scss'], }) export class MonitoringPageComponent implements OnInit { - displayedColumns: string[] = [ 'title', 'description', 'summary_fields', 'app_countries', 'enketo_form', 'enketo_model', 'created_at']; + displayedColumns: (keyof IMonitoringFormsRow)[] = [ + 'title', + 'description', + 'summary_fields', + 'enketo_form', + 'enketo_model', + 'created_at', + ]; - constructor(public service: MonitoringFormsDashboardService, private router: Router) {} + constructor(public service: MonitoringFormsDashboardService, private router: Router, private route: ActivatedRoute) {} public tableOptions: IDataTableOptions = { - paginatorSizes: [25, 50] + paginatorSizes: [25, 50], }; ngOnInit(): void { this.service.ready(); } //this was returning undefined for "this.router" before I made it an arrow funtion, any idea why? onRowClick = (row: IMonitoringFormsRow) => { - this.router.navigate([`/monitoring-forms`, row.id]); - } - + this.router.navigate([row.id], { relativeTo: this.route }); + }; } diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.html b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.html index 3ef64cc73..00e7f4b91 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.html +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.html @@ -1,8 +1,8 @@

Monitoring Form View

- @if(form && form.device_form_id){ - + @if(form){ + }
@if(form){ @@ -13,53 +13,45 @@

Monitoring Form View

-
{{ form.title}}
+
{{ form.title }}
-
{{ form.description}}
-
-
- -
{{form.app_countries?.join(',')}}
+
{{ form.description }}
- @if(form.summary_fields && form.summary_fields.length > 0){ - @for (field of form.summary_fields; track $index) { - @if( field){ - {{ field['label'] }}: {{ field['field'] }} - } - } - } -
-
- -
- + @if(form.summary_fields && form.summary_fields.length > 0){ @for (field of form.summary_fields; track $index) { + @if( field){ + {{ field['label'] }}: {{ field['field'] }} + } } } +
+
+ +
+ +
-
-
- {{form.enketo_form}} +
+ {{ form.enketo_form }}
-
- {{form.enketo_model}} +
+ {{ form.enketo_model }}
- } @else if(dataLoadError) {
{{ dataLoadError }}
} @else {
Loading...
} -
\ No newline at end of file +
diff --git a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts index 6b16ac7d2..838bf2fd3 100644 --- a/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts +++ b/apps/picsa-apps/dashboard/src/app/modules/monitoring/pages/view/view-monitoring-forms.component.ts @@ -1,7 +1,7 @@ import { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterModule } from '@angular/router'; // eslint-disable-next-line @nx/enforce-module-boundaries import type { Database } from '@picsa/server-types'; import { NgxJsonViewerModule } from 'ngx-json-viewer'; @@ -14,16 +14,17 @@ export type IMonitoringFormsRow = Database['public']['Tables']['monitoring_forms @Component({ selector: 'dashboard-monitoring-view', standalone: true, - imports: [CommonModule, DashboardMaterialModule, FormsModule, ReactiveFormsModule, NgxJsonViewerModule], + imports: [CommonModule, DashboardMaterialModule, FormsModule, ReactiveFormsModule, RouterModule, NgxJsonViewerModule], templateUrl: './view-monitoring-forms.component.html', styleUrls: ['./view-monitoring-forms.component.scss'], }) -export class ViewMonitoringFormsComponent { +export class ViewMonitoringFormsComponent implements OnInit { public form: IMonitoringFormsRow; dataLoadError: string; - constructor(private service: MonitoringFormsDashboardService, private route: ActivatedRoute, private router: Router) { - this.service.ready(); - this.route.params.subscribe((params) => { + constructor(private service: MonitoringFormsDashboardService, private route: ActivatedRoute) {} + async ngOnInit() { + await this.service.ready(); + this.route.params.subscribe(async (params) => { const id = params['id']; this.service .getFormById(id) @@ -36,7 +37,4 @@ export class ViewMonitoringFormsComponent { }); }); } - openSubmissions = async (formId:string) => { - this.router.navigate([`/monitoring-forms/${this.form.id}/submissions`, formId]); - }; } diff --git a/apps/picsa-server/supabase/data/README.md b/apps/picsa-server/supabase/data/README.md new file mode 100644 index 000000000..3f1967882 --- /dev/null +++ b/apps/picsa-server/supabase/data/README.md @@ -0,0 +1,15 @@ +# Server Data + +(TODO - move to docs page) + +Files in this folder will be used to seed database when running locally + +## Storage + +All files in the `storage` folder will be uploaded to the server, along with any files in the `shared-assets` directory. + +The storage folder should primarily be used for files which are used on the server but not in the app (e.g. monitoring form xlsx versions) + +## Database + +All `.csv` files will be imported into corresponding database tables diff --git a/apps/picsa-server/supabase/data/monitoring_forms_rows.csv b/apps/picsa-server/supabase/data/monitoring_forms_rows.csv index 8ada1b8ef..ee1245553 100644 --- a/apps/picsa-server/supabase/data/monitoring_forms_rows.csv +++ b/apps/picsa-server/supabase/data/monitoring_forms_rows.csv @@ -1,4 +1,4 @@ -id,created_at,title,description,enketo_definition,summary_fields,app_countries,enketo_form,enketo_model,cover,device_form_id -2,2024-02-13 07:34:03.939328+00,Extension Worker Monitoring,Malawi,"{""hash"":""md5:dc0116ade1cd0dc0a7c0f3ce02bcbddc--d97bac4e49bda9c52dee96737583f620---1"",""media"":{},""theme"":""grid"",""maxSize"":10000000,""enketoId"":""PGpldp9m"",""languageMap"":{},""externalData"":[]}","[{""field"":""date"",""label"":""Date""},{""field"":""district"",""label"":""District""},{""field"":""EPA"",""label"":""EPA""},{""field"":""section"",""label"":""Section""},{""field"":""total_farmers"",""label"":""Total Farmers""}]","[""mw"","" ""]","
\n

E-PICSA - EW Monitoring Malawi

\n \n \n \n
District\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n
","\n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <__version__/>\n \n \n \n \n ","{""icon"":""assets/svgs/monitoring-forms/ew-monitoring.svg""}",ew_monitoring -3,2024-02-13 07:37:01.044168+00,Extension Worker Monitoring,Zambia,"{""hash"":""md5:3166205f2f70f1e29ea86d6dbcd8b36c--d97bac4e49bda9c52dee96737583f620---1"",""media"":{},""theme"":""grid"",""maxSize"":10000000,""enketoId"":""znNPkvlJ"",""languageMap"":{},""externalData"":[]}","[{""field"":""date"",""label"":""Date""},{""field"":""district"",""label"":""District""},{""field"":""EPA"",""label"":""EPA""},{""field"":""section"",""label"":""Section""},{""field"":""total_farmers"",""label"":""Total Farmers""}]","[""zm"","" ""]","
\n

E-PICSA - EW Monitoring Zambia

\n \n \n \n
Organisation\n
\n
District\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n
","\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <__version__/>\n \n \n \n \n ","{""icon"":""assets/svgs/monitoring-forms/ew-monitoring.svg""}",ew_monitoring_zm -4,2024-02-13 07:47:56.842289+00,Complex Demo Form,demo,"{""hash"":""md5:492f8458063b6e9056d5d7fa928500ce--2851db799b6357cc49f5607b9000579d---1"",""media"":{},""theme"":""grid"",""enketoId"":""poiB"",""languageMap"":{}}","[{""field"":""_id"",""label"":""ID""}]","["" ""]","
\n

The Kitchen Sink

\n \n \n
\n
\n
\n
\n
\n
\n
Was the patient\'s blood drawn?To deselect a radio button just click it again.\n
Was the patient\'s blood drawn?Same question but with a horizontal layout\n
Please choose a response:If you have a lot of response options to display, they can be formatted into columns\n
\n
I have a sufficient energy today
\n
I am able to go to work
\n
I have no problems with self-care
\n
\n
\n
I eat a balanced diet.\n
I get a lot of exercise.\n
LABEL HERE\n
\n
\n
Please choose one or more of the following:
These are organized into nicely structured columns\n
You cannot select both c and d.
How many meals did you eat today?
\n
Please choose a response:
\n
\n
Which meals did Sarah eat today?
Hint text can go here.\n
Which meals did James eat today?
HInt text can go here.\n
What meals did Simon eat today?
HInt text can go here.\n
\n
\n
\n
\n
\n
\n
\n
Ongoing?*\n
This field is required
Expected*\n
This field is required
AE Serious?*\n
This field is required
\n
\n
\n
\n
\n
\n
What is your current level of pain?\n
Please select the blue square\n
In which state will the patient be treated?\n
\n
\n
Which is the greatest contributor to the BMI of ? Other field values are displayed within the codelist labels\n
\n
\n
Select multiple widget (don\'t pick c and d together)\n
You cannot select both c and d.
\n
\n
What a creatinine test conducted?\n
Was a creatinine test conducted?\n
Please provide the subject\'s sex:\n
Did you eat dinner today?\n
\n
\n
Did you eat dinner today?\n
\n
\n
\n
\n
\n
\n
\n
\n \n
","\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n static_instance-ABCD-0\n a\n \n \n static_instance-ABCD-1\n b\n \n \n static_instance-ABCD-2\n c\n \n \n static_instance-ABCD-3\n d\n \n \n \n \n \n static_instance-YN-0\n 1\n \n \n static_instance-YN-1\n 2\n \n \n \n \n \n static_instance-GENERIC_LIST-0\n a\n \n \n static_instance-GENERIC_LIST-1\n b\n \n \n static_instance-GENERIC_LIST-2\n c\n \n \n static_instance-GENERIC_LIST-3\n d\n \n \n static_instance-GENERIC_LIST-4\n e\n \n \n static_instance-GENERIC_LIST-5\n f\n \n \n static_instance-GENERIC_LIST-6\n g\n \n \n static_instance-GENERIC_LIST-7\n h\n \n \n static_instance-GENERIC_LIST-8\n i\n \n \n static_instance-GENERIC_LIST-9\n j\n \n \n \n \n \n static_instance-FACES-0\n 0\n \n \n static_instance-FACES-1\n 1\n \n \n static_instance-FACES-2\n 2\n \n \n static_instance-FACES-3\n 3\n \n \n static_instance-FACES-4\n 4\n \n \n \n \n \n static_instance-WHUNITS-0\n 1\n \n \n static_instance-WHUNITS-1\n 2\n \n \n \n \n \n static_instance-COLORS-0\n red\n \n \n static_instance-COLORS-1\n green\n \n \n static_instance-COLORS-2\n blue\n \n \n static_instance-COLORS-3\n yellow\n \n \n \n \n \n static_instance-ACT-0\n 0\n \n \n static_instance-ACT-1\n 1\n \n \n static_instance-ACT-2\n 2\n \n \n static_instance-ACT-3\n 3\n \n \n static_instance-ACT-4\n 4\n \n \n static_instance-ACT-5\n 5\n \n \n \n \n \n static_instance-us_states-0\n AL\n \n \n static_instance-us_states-1\n AK\n \n \n static_instance-us_states-2\n AZ\n \n \n static_instance-us_states-3\n AR\n \n \n static_instance-us_states-4\n CA\n \n \n static_instance-us_states-5\n CO\n \n \n static_instance-us_states-6\n CT\n \n \n static_instance-us_states-7\n DE\n \n \n static_instance-us_states-8\n FL\n \n \n static_instance-us_states-9\n GA\n \n \n static_instance-us_states-10\n HI\n \n \n static_instance-us_states-11\n ID\n \n \n static_instance-us_states-12\n IL\n \n \n static_instance-us_states-13\n IN\n \n \n static_instance-us_states-14\n IA\n \n \n static_instance-us_states-15\n KS\n \n \n static_instance-us_states-16\n KY\n \n \n static_instance-us_states-17\n LA\n \n \n static_instance-us_states-18\n MA\n \n \n static_instance-us_states-19\n MD\n \n \n static_instance-us_states-20\n ME\n \n \n static_instance-us_states-21\n MI\n \n \n static_instance-us_states-22\n MN\n \n \n static_instance-us_states-23\n MO\n \n \n static_instance-us_states-24\n MS\n \n \n static_instance-us_states-25\n MT\n \n \n static_instance-us_states-26\n NC\n \n \n static_instance-us_states-27\n ND\n \n \n static_instance-us_states-28\n NE\n \n \n static_instance-us_states-29\n NH\n \n \n static_instance-us_states-30\n NJ\n \n \n static_instance-us_states-31\n NM\n \n \n static_instance-us_states-32\n NV\n \n \n static_instance-us_states-33\n NY\n \n \n static_instance-us_states-34\n OH\n \n \n static_instance-us_states-35\n OK\n \n \n static_instance-us_states-36\n OR\n \n \n static_instance-us_states-37\n PA\n \n \n static_instance-us_states-38\n RI\n \n \n static_instance-us_states-39\n SC\n \n \n static_instance-us_states-40\n SD\n \n \n static_instance-us_states-41\n TN\n \n \n static_instance-us_states-42\n TX\n \n \n static_instance-us_states-43\n UT\n \n \n static_instance-us_states-44\n VA\n \n \n static_instance-us_states-45\n VT\n \n \n static_instance-us_states-46\n WA\n \n \n static_instance-us_states-47\n WI\n \n \n static_instance-us_states-48\n WV\n \n \n static_instance-us_states-49\n WY\n \n \n static_instance-us_states-50\n DC\n \n \n \n \n \n static_instance-BMIRES-0\n 2\n \n \n static_instance-BMIRES-1\n 3\n \n \n \n \n \n static_instance-SEX-0\n 1\n \n \n static_instance-SEX-1\n 2\n \n \n \n \n \n static_instance-RL-0\n 1\n \n \n static_instance-RL-1\n 2\n \n \n static_instance-RL-2\n 3\n \n \n static_instance-RL-3\n 4\n \n \n static_instance-RL-4\n 5\n \n \n \n \n \n static_instance-TG-0\n 1\n \n \n static_instance-TG-1\n 2\n \n \n static_instance-TG-2\n 3\n \n \n static_instance-TG-3\n 4\n \n \n static_instance-TG-4\n 5\n \n \n \n \n \n static_instance-CASCADE1-0\n 1\n \n \n static_instance-CASCADE1-1\n 2\n \n \n static_instance-CASCADE1-2\n 3\n \n \n static_instance-CASCADE1-3\n 4\n \n \n static_instance-CASCADE1-4\n 1\n 5\n \n \n static_instance-CASCADE1-5\n 1\n 6\n \n \n \n \n \n static_instance-MEALS-0\n 1\n \n \n static_instance-MEALS-1\n 2\n \n \n static_instance-MEALS-2\n 3\n \n \n static_instance-MEALS-3\n 4\n \n \n \n \n \n static_instance-OUT-0\n 0\n \n \n static_instance-OUT-1\n 1\n \n \n static_instance-OUT-2\n 2\n \n \n static_instance-OUT-3\n 3\n \n \n static_instance-OUT-4\n 4\n \n \n \n \n \n static_instance-ACKNOWLEDGE-0\n 1\n \n \n \n \n \n static_instance-CASCADE2-0\n 1\n 1\n \n \n static_instance-CASCADE2-1\n 1\n 2\n \n \n static_instance-CASCADE2-2\n 1\n 3\n \n \n static_instance-CASCADE2-3\n 1\n 4\n \n \n static_instance-CASCADE2-4\n 2\n 5\n \n \n static_instance-CASCADE2-5\n 2\n 6\n \n \n static_instance-CASCADE2-6\n 1\n 7\n 1\n \n \n static_instance-CASCADE2-7\n 1\n 8\n 1\n \n \n \n \n \n static_instance-CASCADE3-0\n 1\n 1\n 1\n \n \n static_instance-CASCADE3-1\n 1\n 2\n 1\n \n \n static_instance-CASCADE3-2\n 1\n 3\n 1\n \n \n static_instance-CASCADE3-3\n 1\n 4\n 1\n \n \n static_instance-CASCADE3-4\n 1\n 5\n 1\n \n \n static_instance-CASCADE3-5\n 1\n 6\n 1\n \n \n static_instance-CASCADE3-6\n 1\n 7\n 2\n \n \n static_instance-CASCADE3-7\n 1\n 8\n 2\n \n \n static_instance-CASCADE3-8\n 1\n 9\n 2\n \n \n static_instance-CASCADE3-9\n 1\n 10\n 2\n \n \n static_instance-CASCADE3-10\n 2\n 11\n 7\n \n \n static_instance-CASCADE3-11\n 2\n 12\n 7\n \n \n static_instance-CASCADE3-12\n 2\n 13\n 7\n \n \n static_instance-CASCADE3-13\n 2\n 14\n 7\n \n \n static_instance-CASCADE3-14\n 2\n 15\n 7\n \n \n static_instance-CASCADE3-15\n 2\n 16\n 8\n \n \n static_instance-CASCADE3-16\n 2\n 17\n 8\n \n \n static_instance-CASCADE3-17\n 2\n 18\n 8\n \n \n static_instance-CASCADE3-18\n 19\n \n \n static_instance-CASCADE3-19\n 20\n \n \n \n \n \n static_instance-UNITS-0\n 1\n \n \n static_instance-UNITS-1\n 2\n \n \n \n \n \n static_instance-BODYSYSTEM-0\n 1\n \n \n static_instance-BODYSYSTEM-1\n 2\n \n \n static_instance-BODYSYSTEM-2\n 3\n \n \n static_instance-BODYSYSTEM-3\n 4\n \n \n \n \n \n static_instance-DATE_PRECISION-0\n 1\n \n \n static_instance-DATE_PRECISION-1\n 2\n \n \n static_instance-DATE_PRECISION-2\n 3\n \n \n \n \n \n static_instance-AGREEMENT-0\n 1\n \n \n static_instance-AGREEMENT-1\n 2\n \n \n static_instance-AGREEMENT-2\n 3\n \n \n static_instance-AGREEMENT-3\n 4\n \n \n static_instance-AGREEMENT-4\n 5\n \n \n ","{""icon"":""""}",demo_kitchen \ No newline at end of file +created_at,title,description,enketo_definition,summary_fields,enketo_form,enketo_model,cover_image,id,form_xlsx +2024-02-13 07:34:03.939328+00,Extension Worker Monitoring,Malawi,"{""hash"":""md5:dc0116ade1cd0dc0a7c0f3ce02bcbddc--d97bac4e49bda9c52dee96737583f620---1"",""media"":{},""theme"":""grid"",""maxSize"":10000000,""enketoId"":""PGpldp9m"",""languageMap"":{},""externalData"":[]}","[{""field"":""date"",""label"":""Date""},{""field"":""district"",""label"":""District""},{""field"":""EPA"",""label"":""EPA""},{""field"":""section"",""label"":""Section""},{""field"":""total_farmers"",""label"":""Total Farmers""}]","
\n

E-PICSA - EW Monitoring Malawi

\n \n \n \n
District\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n
","\n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <__version__/>\n \n \n \n \n ",global/monitoring/cover_images/ew-monitoring.svg,ew_monitoring, +2024-02-13 07:37:01.044168+00,Extension Worker Monitoring,Zambia,"{""hash"":""md5:3166205f2f70f1e29ea86d6dbcd8b36c--d97bac4e49bda9c52dee96737583f620---1"",""media"":{},""theme"":""grid"",""maxSize"":10000000,""enketoId"":""znNPkvlJ"",""languageMap"":{},""externalData"":[]}","[{""field"":""date"",""label"":""Date""},{""field"":""district"",""label"":""District""},{""field"":""EPA"",""label"":""EPA""},{""field"":""section"",""label"":""Section""},{""field"":""total_farmers"",""label"":""Total Farmers""}]","
\n

E-PICSA - EW Monitoring Zambia

\n \n \n \n
Organisation\n
\n
District\n
\n \n \n \n
\n
\n
\n
\n
\n
\n \n \n \n \n \n
","\n \n \n \n \n \n \n \n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n <__version__/>\n \n \n \n \n ",global/monitoring/cover_images/ew-monitoring.svg,ew_monitoring_zm, +2024-02-13 07:47:56.842289+00,Complex Demo Form,demo,"{""hash"":""md5:492f8458063b6e9056d5d7fa928500ce--2851db799b6357cc49f5607b9000579d---1"",""media"":{},""theme"":""grid"",""enketoId"":""poiB"",""languageMap"":{}}","[{""field"":""_id"",""label"":""ID""}]","
\n

The Kitchen Sink

\n \n \n
\n
\n
\n
\n
\n
\n
Was the patient\'s blood drawn?To deselect a radio button just click it again.\n
Was the patient\'s blood drawn?Same question but with a horizontal layout\n
Please choose a response:If you have a lot of response options to display, they can be formatted into columns\n
\n
I have a sufficient energy today
\n
I am able to go to work
\n
I have no problems with self-care
\n
\n
\n
I eat a balanced diet.\n
I get a lot of exercise.\n
LABEL HERE\n
\n
\n
Please choose one or more of the following:
These are organized into nicely structured columns\n
You cannot select both c and d.
How many meals did you eat today?
\n
Please choose a response:
\n
\n
Which meals did Sarah eat today?
Hint text can go here.\n
Which meals did James eat today?
HInt text can go here.\n
What meals did Simon eat today?
HInt text can go here.\n
\n
\n
\n
\n
\n
\n
\n
Ongoing?*\n
This field is required
Expected*\n
This field is required
AE Serious?*\n
This field is required
\n
\n
\n
\n
\n
\n
What is your current level of pain?\n
Please select the blue square\n
In which state will the patient be treated?\n
\n
\n
Which is the greatest contributor to the BMI of ? Other field values are displayed within the codelist labels\n
\n
\n
Select multiple widget (don\'t pick c and d together)\n
You cannot select both c and d.
\n
\n
What a creatinine test conducted?\n
Was a creatinine test conducted?\n
Please provide the subject\'s sex:\n
Did you eat dinner today?\n
\n
\n
Did you eat dinner today?\n
\n
\n
\n
\n
\n
\n
\n
\n \n
","\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n static_instance-ABCD-0\n a\n \n \n static_instance-ABCD-1\n b\n \n \n static_instance-ABCD-2\n c\n \n \n static_instance-ABCD-3\n d\n \n \n \n \n \n static_instance-YN-0\n 1\n \n \n static_instance-YN-1\n 2\n \n \n \n \n \n static_instance-GENERIC_LIST-0\n a\n \n \n static_instance-GENERIC_LIST-1\n b\n \n \n static_instance-GENERIC_LIST-2\n c\n \n \n static_instance-GENERIC_LIST-3\n d\n \n \n static_instance-GENERIC_LIST-4\n e\n \n \n static_instance-GENERIC_LIST-5\n f\n \n \n static_instance-GENERIC_LIST-6\n g\n \n \n static_instance-GENERIC_LIST-7\n h\n \n \n static_instance-GENERIC_LIST-8\n i\n \n \n static_instance-GENERIC_LIST-9\n j\n \n \n \n \n \n static_instance-FACES-0\n 0\n \n \n static_instance-FACES-1\n 1\n \n \n static_instance-FACES-2\n 2\n \n \n static_instance-FACES-3\n 3\n \n \n static_instance-FACES-4\n 4\n \n \n \n \n \n static_instance-WHUNITS-0\n 1\n \n \n static_instance-WHUNITS-1\n 2\n \n \n \n \n \n static_instance-COLORS-0\n red\n \n \n static_instance-COLORS-1\n green\n \n \n static_instance-COLORS-2\n blue\n \n \n static_instance-COLORS-3\n yellow\n \n \n \n \n \n static_instance-ACT-0\n 0\n \n \n static_instance-ACT-1\n 1\n \n \n static_instance-ACT-2\n 2\n \n \n static_instance-ACT-3\n 3\n \n \n static_instance-ACT-4\n 4\n \n \n static_instance-ACT-5\n 5\n \n \n \n \n \n static_instance-us_states-0\n AL\n \n \n static_instance-us_states-1\n AK\n \n \n static_instance-us_states-2\n AZ\n \n \n static_instance-us_states-3\n AR\n \n \n static_instance-us_states-4\n CA\n \n \n static_instance-us_states-5\n CO\n \n \n static_instance-us_states-6\n CT\n \n \n static_instance-us_states-7\n DE\n \n \n static_instance-us_states-8\n FL\n \n \n static_instance-us_states-9\n GA\n \n \n static_instance-us_states-10\n HI\n \n \n static_instance-us_states-11\n ID\n \n \n static_instance-us_states-12\n IL\n \n \n static_instance-us_states-13\n IN\n \n \n static_instance-us_states-14\n IA\n \n \n static_instance-us_states-15\n KS\n \n \n static_instance-us_states-16\n KY\n \n \n static_instance-us_states-17\n LA\n \n \n static_instance-us_states-18\n MA\n \n \n static_instance-us_states-19\n MD\n \n \n static_instance-us_states-20\n ME\n \n \n static_instance-us_states-21\n MI\n \n \n static_instance-us_states-22\n MN\n \n \n static_instance-us_states-23\n MO\n \n \n static_instance-us_states-24\n MS\n \n \n static_instance-us_states-25\n MT\n \n \n static_instance-us_states-26\n NC\n \n \n static_instance-us_states-27\n ND\n \n \n static_instance-us_states-28\n NE\n \n \n static_instance-us_states-29\n NH\n \n \n static_instance-us_states-30\n NJ\n \n \n static_instance-us_states-31\n NM\n \n \n static_instance-us_states-32\n NV\n \n \n static_instance-us_states-33\n NY\n \n \n static_instance-us_states-34\n OH\n \n \n static_instance-us_states-35\n OK\n \n \n static_instance-us_states-36\n OR\n \n \n static_instance-us_states-37\n PA\n \n \n static_instance-us_states-38\n RI\n \n \n static_instance-us_states-39\n SC\n \n \n static_instance-us_states-40\n SD\n \n \n static_instance-us_states-41\n TN\n \n \n static_instance-us_states-42\n TX\n \n \n static_instance-us_states-43\n UT\n \n \n static_instance-us_states-44\n VA\n \n \n static_instance-us_states-45\n VT\n \n \n static_instance-us_states-46\n WA\n \n \n static_instance-us_states-47\n WI\n \n \n static_instance-us_states-48\n WV\n \n \n static_instance-us_states-49\n WY\n \n \n static_instance-us_states-50\n DC\n \n \n \n \n \n static_instance-BMIRES-0\n 2\n \n \n static_instance-BMIRES-1\n 3\n \n \n \n \n \n static_instance-SEX-0\n 1\n \n \n static_instance-SEX-1\n 2\n \n \n \n \n \n static_instance-RL-0\n 1\n \n \n static_instance-RL-1\n 2\n \n \n static_instance-RL-2\n 3\n \n \n static_instance-RL-3\n 4\n \n \n static_instance-RL-4\n 5\n \n \n \n \n \n static_instance-TG-0\n 1\n \n \n static_instance-TG-1\n 2\n \n \n static_instance-TG-2\n 3\n \n \n static_instance-TG-3\n 4\n \n \n static_instance-TG-4\n 5\n \n \n \n \n \n static_instance-CASCADE1-0\n 1\n \n \n static_instance-CASCADE1-1\n 2\n \n \n static_instance-CASCADE1-2\n 3\n \n \n static_instance-CASCADE1-3\n 4\n \n \n static_instance-CASCADE1-4\n 1\n 5\n \n \n static_instance-CASCADE1-5\n 1\n 6\n \n \n \n \n \n static_instance-MEALS-0\n 1\n \n \n static_instance-MEALS-1\n 2\n \n \n static_instance-MEALS-2\n 3\n \n \n static_instance-MEALS-3\n 4\n \n \n \n \n \n static_instance-OUT-0\n 0\n \n \n static_instance-OUT-1\n 1\n \n \n static_instance-OUT-2\n 2\n \n \n static_instance-OUT-3\n 3\n \n \n static_instance-OUT-4\n 4\n \n \n \n \n \n static_instance-ACKNOWLEDGE-0\n 1\n \n \n \n \n \n static_instance-CASCADE2-0\n 1\n 1\n \n \n static_instance-CASCADE2-1\n 1\n 2\n \n \n static_instance-CASCADE2-2\n 1\n 3\n \n \n static_instance-CASCADE2-3\n 1\n 4\n \n \n static_instance-CASCADE2-4\n 2\n 5\n \n \n static_instance-CASCADE2-5\n 2\n 6\n \n \n static_instance-CASCADE2-6\n 1\n 7\n 1\n \n \n static_instance-CASCADE2-7\n 1\n 8\n 1\n \n \n \n \n \n static_instance-CASCADE3-0\n 1\n 1\n 1\n \n \n static_instance-CASCADE3-1\n 1\n 2\n 1\n \n \n static_instance-CASCADE3-2\n 1\n 3\n 1\n \n \n static_instance-CASCADE3-3\n 1\n 4\n 1\n \n \n static_instance-CASCADE3-4\n 1\n 5\n 1\n \n \n static_instance-CASCADE3-5\n 1\n 6\n 1\n \n \n static_instance-CASCADE3-6\n 1\n 7\n 2\n \n \n static_instance-CASCADE3-7\n 1\n 8\n 2\n \n \n static_instance-CASCADE3-8\n 1\n 9\n 2\n \n \n static_instance-CASCADE3-9\n 1\n 10\n 2\n \n \n static_instance-CASCADE3-10\n 2\n 11\n 7\n \n \n static_instance-CASCADE3-11\n 2\n 12\n 7\n \n \n static_instance-CASCADE3-12\n 2\n 13\n 7\n \n \n static_instance-CASCADE3-13\n 2\n 14\n 7\n \n \n static_instance-CASCADE3-14\n 2\n 15\n 7\n \n \n static_instance-CASCADE3-15\n 2\n 16\n 8\n \n \n static_instance-CASCADE3-16\n 2\n 17\n 8\n \n \n static_instance-CASCADE3-17\n 2\n 18\n 8\n \n \n static_instance-CASCADE3-18\n 19\n \n \n static_instance-CASCADE3-19\n 20\n \n \n \n \n \n static_instance-UNITS-0\n 1\n \n \n static_instance-UNITS-1\n 2\n \n \n \n \n \n static_instance-BODYSYSTEM-0\n 1\n \n \n static_instance-BODYSYSTEM-1\n 2\n \n \n static_instance-BODYSYSTEM-2\n 3\n \n \n static_instance-BODYSYSTEM-3\n 4\n \n \n \n \n \n static_instance-DATE_PRECISION-0\n 1\n \n \n static_instance-DATE_PRECISION-1\n 2\n \n \n static_instance-DATE_PRECISION-2\n 3\n \n \n \n \n \n static_instance-AGREEMENT-0\n 1\n \n \n static_instance-AGREEMENT-1\n 2\n \n \n static_instance-AGREEMENT-2\n 3\n \n \n static_instance-AGREEMENT-3\n 4\n \n \n static_instance-AGREEMENT-4\n 5\n \n \n ",global/monitoring/cover_images/ew-monitoring.svg,demo_kitchen,global/monitoring/forms/kitchen_sink_demo_form.xls \ No newline at end of file diff --git a/apps/picsa-server/supabase/data/monitoring_tool_submissions_rows.csv b/apps/picsa-server/supabase/data/monitoring_tool_submissions_rows.csv new file mode 100644 index 000000000..d444f7ef6 --- /dev/null +++ b/apps/picsa-server/supabase/data/monitoring_tool_submissions_rows.csv @@ -0,0 +1,4 @@ +_id,_created,_modified,formId,enketoEntry,json,_app_user_id,_attachments,_deleted +719FTTS7o1TcWsKSQP4I,2024-03-01 00:49:11.981+00,2024-03-01 00:49:17.304+00,demo_kitchen,"{""xml"":""\n \n 0b169b0feeda4af2a8b0830239427e8f\n \n \n \n \n \n \n test\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n NaN\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n 2024-02-29\n \n NaN\n NaN\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n 1\n 1\n 1\n \n \n 0\n 0\n \n 0\n 0\n \n 0\n 0\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n weight not entered\n \n height not entered\n \n 0\n 0\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n 3\n C\n \n \n \n uuid:0d3b1e52-3490-41f8-a48a-dcbfd58ed258\n \n "",""draft"":false,""nodes"":[""text-single""]}","{""meta"":{""instanceID"":""uuid:0d3b1e52-3490-41f8-a48a-dcbfd58ed258""},""text"":{""text-multi"":"""",""text-single"":""test"",""overview-text"":""""},""formhub"":{""uuid"":""0b169b0feeda4af2a8b0830239427e8f""},""page-vas"":{""eq5d"":"""",""vas-vert"":"""",""vas_eq5d"":"""",""vas-horiz"":"""",""overview-VAS"":"""",""vas_distress"":"""",""vas-vert-no-ticks"":"""",""vas-vertical-no-ticks"":"""",""vas-horizontal-no-ticks"":"""",""vas-vertical-with-ticks"":"""",""vas-horizontal-with-ticks"":""""},""page-dates"":{""bday"":"""",""fancy"":"""",""today"":""2024-02-29"",""date-y"":"""",""date-ym"":"""",""age_days"":""NaN"",""date-ymd"":"""",""your_age"":"""",""age_years"":""NaN"",""date_note"":"""",""date-types"":"""",""date_calcs"":"""",""date-y-dynamic"":"""",""date_precision"":"""",""note_datecalcs"":"""",""date-ym-dynamic"":"""",""date-ymd-dynamic"":""""},""page-images"":{""draw"":"""",""colors"":"""",""upload"":"""",""us_map"":"""",""image_map"":"""",""signature"":"""",""ecg_normal"":"""",""pain_level"":"""",""embeded_image"":"""",""image_map_note"":"""",""images-overview"":"""",""images-selectors"":""""},""page-piping"":{""bmi"":0,""bmi_ro"":"""",""height"":"""",""weight"":"""",""bmi_rad"":"""",""bmi_calc"":0,""wh_units"":"""",""piping_note"":"""",""height_unit_des"":""height not entered"",""piping-overview"":"""",""weight_unit_des"":""weight not entered""},""page-styles"":{""links"":"""",""line-breaks"":"""",""grid-screenshot"":"""",""styles-overview"":"""",""themes-overview"":"""",""pages-screenshot"":""""},""page-numbers"":{""calc"":"""",""decimal"":"""",""integer"":"""",""overview-numbers"":""""},""page-welcome"":{""overview"":""""},""page-repeating"":{""ae_rg"":{""ACT"":"""",""ONG"":"""",""OUT"":"""",""RL1"":"""",""SAE"":"""",""TG1"":"""",""AEBL"":"""",""EXP1"":"""",""STOP"":"""",""START"":"""",""SAE_BL"":"""",""AE_NOTE"":"""",""SAE_UPLOAD"":""""},""VITALS_RG"":{""TIME"":""00:15"",""PULSE"":"""",""RG_NUM"":1,""bp_dia"":"""",""bp_sys"":"""",""PULSE_val"":0,""ROW_NUMBER"":1,""bp_dia_val"":0,""bp_sys_val"":0,""PULSE_count"":0,""RG_NUM_INIT"":1,""bp_dia_count"":0,""bp_sys_count"":0},""pules_mean"":"""",""bp_dia_mean"":"""",""bp_sys_mean"":"""",""pre-popluate-rg"":"""",""overview-repeating"":""""},""page-show-hide"":{""DINNER"":{""ITEM"":"""",""SERVINGS"":""""},""SH-SEX"":"""",""DINNER2"":{""ITEM2"":"""",""SERVINGS2"":"""",""ADDL-SERVINGS"":""""},""SH-CREAT"":"""",""SH-MULTI"":"""",""SH-CREAT2"":"""",""SH-DINNER"":"""",""SH-SINGLE"":"""",""SH-DINNER2"":"""",""SH-FOODLOG"":"""",""SH-FOODLOG2"":"""",""SH-INSIDE_RG"":"""",""SH-REPEATING"":"""",""SH-CREAT-VALUE"":"""",""SH-CREAT-VALUE2"":"""",""show-hide-overview"":""""},""page-constraints"":{""page-constraint"":"""",""constraints_date"":"""",""constraints_regex"":"""",""constraints_no_c_d"":"""",""constraints_integer"":"""",""constraints-overview"":"""",""constraints_even_odd"":"""",""constraints_integer2"":""""},""page-calculations"":{""rndm"":3,""cohort"":""C"",""cohort_note"":"""",""calc-overview"":"""",""calc-functions"":"""",""calc-operators"":""""},""page-multi-selects"":{""MEALS"":"""",""SM_COLUMNS"":"""",""ML_DROPDOWN"":"""",""SM_TABLE_LIST"":{""JAMES"":"""",""SARAH"":"""",""SIMON"":"""",""reserved_name_for_field_list_labels_56"":""""},""select_multiple"":"""",""overview-multi-selects"":""""},""page-single-selects"":{""RADIO"":"""",""cascade1"":"""",""cascade2"":"""",""cascade3"":"""",""codelist"":"""",""SS_COLUMNS"":"""",""bodysystem"":"""",""acknowledge"":"""",""likert-calc"":""NaN"",""likert-diet"":"""",""Autocomplete"":"""",""three_column"":{""cascade1auto"":"""",""cascade2auto"":"""",""cascade3auto"":""""},""SS_TABLE_LIST"":{""WORK"":"""",""ENERGY"":"""",""SELF-CARE"":"""",""reserved_name_for_field_list_labels_22"":""""},""picklist-units"":"""",""likert-exercise"":"""",""likert-overview"":"""",""RADIO_HORIZ_COMP"":"""",""acknowledge_note"":"""",""if-other-specify"":"""",""likert-score-note"":"""",""other-body-system"":"""",""Cascading_picklist"":"""",""Single_Selects_Overview"":""""}}",__default__,{},false +N14gINnKchHSoISjBNws,2024-03-01 00:49:32.479+00,2024-03-01 00:50:05.729+00,ew_monitoring,"{""xml"":""\n \n 6047e7f1180a493eb78272460fd38317\n \n 2024-02-29T16:49:32.493-08:00\n 2024-02-29T16:49:32.494-08:00\n 2024-02-01\n kasungu\n Example EPA\n
\n Dev Testing\n \n \n \n \n \n \n \n 5\n 7\n \n \n \n 12\n 4\n \n 28\n \n \n \n \n <__version__>vJ2qCqJhqh4ZuRkftq5gzE\n \n uuid:d113bfbe-38e6-4d74-bad1-faac545f8b54\n \n "",""draft"":false,""nodes"":[""farmers_male_36orover""]}","{""EPA"":""Example EPA"",""end"":""2024-02-29T16:49:32.494-08:00"",""date"":""2024-02-01"",""meta"":{""instanceID"":""uuid:d113bfbe-38e6-4d74-bad1-faac545f8b54""},""name"":""Dev Testing"",""start"":""2024-02-29T16:49:32.493-08:00"",""formhub"":{""uuid"":""6047e7f1180a493eb78272460fd38317""},""section"":"""",""district"":""kasungu"",""activities"":"""",""__version__"":""vJ2qCqJhqh4ZuRkftq5gzE"",""observations"":"""",""total_farmers"":28,""share_community"":"""",""farmers_present_35"":{""farmers_male_35orunder"":7,""farmers_female_35orunder"":5,""farmers_present_f_1_note"":""""},""farmers_present_36"":{""farmers_male_36orover"":4,""farmers_female_36orover"":12,""farmers_present_m_1_note"":""""},""farmers_present_header"":{""farmers_present_header_o36"":"""",""farmers_present_header_u35"":"""",""farmers_present_header_note"":""""},""Total_Farmers_total_farmers"":""""}",__default__,{},false +kRaPWQ17x22TW0H6AcSq,2024-03-01 00:54:13.279+00,2024-03-01 00:54:53.081+00,ew_monitoring,"{""xml"":""\n \n 6047e7f1180a493eb78272460fd38317\n \n 2024-02-29T16:54:13.315-08:00\n 2024-02-29T16:54:13.315-08:00\n 2024-02-17\n nkhotakota\n Another EPA\n
Demo Section
\n Dev Demo User\n \n \n \n \n \n \n \n 23\n 5\n \n \n \n 7\n 19\n \n 54\n \n Example activities\n \n \n <__version__>vJ2qCqJhqh4ZuRkftq5gzE\n \n uuid:0a8a1c3f-e93f-47cd-952e-134eb79d4fbe\n \n
"",""draft"":false,""nodes"":[""activities""]}","{""EPA"":""Another EPA"",""end"":""2024-02-29T16:54:13.315-08:00"",""date"":""2024-02-17"",""meta"":{""instanceID"":""uuid:0a8a1c3f-e93f-47cd-952e-134eb79d4fbe""},""name"":""Dev Demo User"",""start"":""2024-02-29T16:54:13.315-08:00"",""formhub"":{""uuid"":""6047e7f1180a493eb78272460fd38317""},""section"":""Demo Section"",""district"":""nkhotakota"",""activities"":""Example activities"",""__version__"":""vJ2qCqJhqh4ZuRkftq5gzE"",""observations"":"""",""total_farmers"":54,""share_community"":"""",""farmers_present_35"":{""farmers_male_35orunder"":5,""farmers_female_35orunder"":23,""farmers_present_f_1_note"":""""},""farmers_present_36"":{""farmers_male_36orover"":19,""farmers_female_36orover"":7,""farmers_present_m_1_note"":""""},""farmers_present_header"":{""farmers_present_header_o36"":"""",""farmers_present_header_u35"":"""",""farmers_present_header_note"":""""},""Total_Farmers_total_farmers"":""""}",__default__,{},false \ No newline at end of file diff --git a/apps/picsa-server/supabase/data/storage/global/monitoring/forms/widgets.xlsx b/apps/picsa-server/supabase/data/storage/global/monitoring/forms/widgets.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..904ad182a4466adfbec5f10ad5db80a78b41c00a GIT binary patch literal 20895 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 Sy%-n-fp8XZM*h7Ihz9_6I&_Bs literal 0 HcmV?d00001 diff --git a/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql b/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql deleted file mode 100644 index b7b40c357..000000000 --- a/apps/picsa-server/supabase/migrations/20240213081336_monitoring_forms_table.sql +++ /dev/null @@ -1,15 +0,0 @@ -create table - public.monitoring_forms ( - id bigint generated by default as identity, - created_at timestamp with time zone not null default now(), - title text not null, - description character varying null, - enketo_definition jsonb null, - summary_fields jsonb[] null, - app_countries text[] null, - enketo_form text null, - enketo_model text null, - cover jsonb null, - device_form_id character varying null, - constraint monitoring_forms_pkey primary key (id) - ) tablespace pg_default; \ No newline at end of file diff --git a/apps/picsa-server/supabase/migrations/20240229081336_monitoring_forms_table.sql b/apps/picsa-server/supabase/migrations/20240229081336_monitoring_forms_table.sql new file mode 100644 index 000000000..0a7266787 --- /dev/null +++ b/apps/picsa-server/supabase/migrations/20240229081336_monitoring_forms_table.sql @@ -0,0 +1,16 @@ +create table + public.monitoring_forms ( + created_at timestamp with time zone not null default now(), + title text not null, + description character varying null, + enketo_definition jsonb null, + summary_fields jsonb[] null, + enketo_form text null, + enketo_model text null, + cover_image text null, + id text not null, + form_xlsx text null, + constraint monitoring_forms_pkey primary key (id), + constraint monitoring_forms_cover_image_fkey foreign key (cover_image) references storage.objects (path) on delete set null, + constraint monitoring_forms_form_xlsx_fkey foreign key (form_xlsx) references storage.objects (path) on update cascade on delete set null + ) tablespace pg_default; \ No newline at end of file diff --git a/apps/picsa-server/supabase/types/index.ts b/apps/picsa-server/supabase/types/index.ts index 8ba54c238..e72c757df 100644 --- a/apps/picsa-server/supabase/types/index.ts +++ b/apps/picsa-server/supabase/types/index.ts @@ -213,45 +213,55 @@ export interface Database { }; monitoring_forms: { Row: { - app_countries: string[] | null; - cover: Json | null; + cover_image: string | null; created_at: string; description: string | null; - device_form_id: string | null; enketo_definition: Json | null; enketo_form: string | null; enketo_model: string | null; - id: number; + form_xlsx: string | null; + id: string; summary_fields: Json[] | null; title: string; }; Insert: { - app_countries?: string[] | null; - cover?: Json | null; + cover_image?: string | null; created_at?: string; description?: string | null; - device_form_id?: string | null; enketo_definition?: Json | null; enketo_form?: string | null; enketo_model?: string | null; - id?: number; + form_xlsx?: string | null; + id: string; summary_fields?: Json[] | null; title: string; }; Update: { - app_countries?: string[] | null; - cover?: Json | null; + cover_image?: string | null; created_at?: string; description?: string | null; - device_form_id?: string | null; enketo_definition?: Json | null; enketo_form?: string | null; enketo_model?: string | null; - id?: number; + form_xlsx?: string | null; + id?: string; summary_fields?: Json[] | null; title?: string; }; - Relationships: []; + Relationships: [ + { + foreignKeyName: 'monitoring_forms_cover_image_fkey'; + columns: ['cover_image']; + referencedRelation: 'objects'; + referencedColumns: ['path']; + }, + { + foreignKeyName: 'monitoring_forms_form_xlsx_fkey'; + columns: ['form_xlsx']; + referencedRelation: 'objects'; + referencedColumns: ['path']; + } + ]; }; monitoring_tool_submissions: { Row: { diff --git a/libs/shared/src/services/core/supabase/components/storage-file-picker/storage-file-picker.component.ts b/libs/shared/src/services/core/supabase/components/storage-file-picker/storage-file-picker.component.ts index 6266bb7dc..4f709936b 100644 --- a/libs/shared/src/services/core/supabase/components/storage-file-picker/storage-file-picker.component.ts +++ b/libs/shared/src/services/core/supabase/components/storage-file-picker/storage-file-picker.component.ts @@ -66,8 +66,7 @@ export class SupabaseStorageFilePickerComponent { const entries = await this.storage.list(this.storageBucketName, this.storageFolderPath); this.fileEntries = entries // filter out metadata files which have filename starting with `.` - // not sure why I keep getting a run time error here with a reset data base - .filter((storageEntry) => storageEntry.name && !storageEntry.name.split('/').pop()?.startsWith('.')) + .filter((entry) => entry?.name && !entry.name.split('/').pop()?.startsWith('.')) .sort((a, b) => { if (!b.name) return 1; if (!a.name) return -1;