diff --git a/frontend/projects/ui/src/app/modals/generic-form/generic-form.page.html b/frontend/projects/ui/src/app/modals/generic-form/generic-form.page.html index 706c8487d..3ff3e4aae 100644 --- a/frontend/projects/ui/src/app/modals/generic-form/generic-form.page.html +++ b/frontend/projects/ui/src/app/modals/generic-form/generic-form.page.html @@ -24,7 +24,7 @@ diff --git a/frontend/projects/ui/src/app/modals/generic-form/generic-form.page.ts b/frontend/projects/ui/src/app/modals/generic-form/generic-form.page.ts index 6fac73d4c..6039ab721 100644 --- a/frontend/projects/ui/src/app/modals/generic-form/generic-form.page.ts +++ b/frontend/projects/ui/src/app/modals/generic-form/generic-form.page.ts @@ -22,7 +22,7 @@ export class GenericFormPage { @Input() title!: string @Input() spec!: InputSpec @Input() buttons!: ActionButton[] - @Input() initialValue: object = {} + @Input() initialValue: Record = {} submitBtn!: ActionButton formGroup!: UntypedFormGroup @@ -34,10 +34,7 @@ export class GenericFormPage { ngOnInit() { this.formGroup = this.formService.createForm(this.spec, this.initialValue) - this.submitBtn = this.buttons.find(btn => btn.isSubmit) || { - text: '', - handler: () => Promise.resolve(true), - } + this.submitBtn = this.buttons.find(btn => btn.isSubmit)! // @TODO this really needs to be redesigned. No way to enforce this with types. } async dismiss(): Promise { @@ -56,6 +53,15 @@ export class GenericFormPage { // @TODO make this more like generic input component dismissal const success = await handler(this.formGroup.value) - if (success !== false) this.modalCtrl.dismiss() + if (success === true) this.modalCtrl.dismiss() } } + +export interface GenericFormOptions { + // required + title: string + spec: InputSpec + buttons: ActionButton[] + // optional + initialValue?: Record +} diff --git a/frontend/projects/ui/src/app/modals/generic-input/generic-input.component.ts b/frontend/projects/ui/src/app/modals/generic-input/generic-input.component.ts index 59ebb8c3d..094887a14 100644 --- a/frontend/projects/ui/src/app/modals/generic-input/generic-input.component.ts +++ b/frontend/projects/ui/src/app/modals/generic-input/generic-input.component.ts @@ -92,5 +92,5 @@ export interface GenericInputOptions { placeholder?: string nullable?: boolean useMask?: boolean - initialValue?: string + initialValue?: string | null } diff --git a/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.ts b/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.ts index ec58e6c95..b9ee3819c 100644 --- a/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.ts +++ b/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.ts @@ -15,7 +15,10 @@ import { ErrorToastService, sameUrl, toUrl } from '@start9labs/shared' import { AbstractMarketplaceService } from '@start9labs/marketplace' import { ApiService } from 'src/app/services/api/embassy-api.service' import { ValueSpecObject } from 'start-sdk/types/config-types' -import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page' +import { + GenericFormPage, + GenericFormOptions, +} from 'src/app/modals/generic-form/generic-form.page' import { PatchDB } from 'patch-db-client' import { DataModel, UIStore } from 'src/app/services/patch-db/data-model' import { MarketplaceService } from 'src/app/services/marketplace.service' @@ -65,27 +68,27 @@ export class MarketplaceSettingsPage { async presentModalAdd() { const { name, spec } = getMarketplaceValueSpec() + + const options: GenericFormOptions = { + title: name, + spec, + buttons: [ + { + text: 'Save for Later', + handler: async (value: { url: string }) => this.saveOnly(value.url), + }, + { + text: 'Save and Connect', + handler: async (value: { url: string }) => + this.saveAndConnect(value.url), + isSubmit: true, + }, + ], + } + const modal = await this.modalCtrl.create({ component: GenericFormPage, - componentProps: { - title: name, - spec, - buttons: [ - { - text: 'Save for Later', - handler: (value: { url: string }) => { - this.saveOnly(value.url) - }, - }, - { - text: 'Save and Connect', - handler: (value: { url: string }) => { - this.saveAndConnect(value.url) - }, - isSubmit: true, - }, - ], - }, + componentProps: options, cssClass: 'alertlike-modal', }) @@ -166,28 +169,32 @@ export class MarketplaceSettingsPage { } } - private async saveOnly(rawUrl: string): Promise { + private async saveOnly(rawUrl: string): Promise { const loader = await this.loadingCtrl.create() try { const url = new URL(rawUrl).toString() await this.validateAndSave(url, loader) + return true } catch (e: any) { this.errToast.present(e) + return false } finally { loader.dismiss() } } - private async saveAndConnect(rawUrl: string): Promise { + private async saveAndConnect(rawUrl: string): Promise { const loader = await this.loadingCtrl.create() try { const url = new URL(rawUrl).toString() await this.validateAndSave(url, loader) await this.connect(url, loader) + return true } catch (e: any) { this.errToast.present(e) + return false } finally { loader.dismiss() this.dismiss() diff --git a/frontend/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts b/frontend/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts index aed071eed..085e1ed99 100644 --- a/frontend/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts +++ b/frontend/projects/ui/src/app/pages/apps-routes/app-actions/app-actions.page.ts @@ -20,7 +20,10 @@ import { PackageDataEntry, PackageState, } from 'src/app/services/patch-db/data-model' -import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page' +import { + GenericFormPage, + GenericFormOptions, +} from 'src/app/modals/generic-form/generic-form.page' import { isEmptyObject, ErrorToastService, @@ -64,22 +67,21 @@ export class AppActionsPage { }) await alert.present() } else { - if (!isEmptyObject(action['input-spec'] || {})) { + if (action['input-spec'] && !isEmptyObject(action['input-spec'])) { + const options: GenericFormOptions = { + title: action.name, + spec: action['input-spec'], + buttons: [ + { + text: 'Execute', + handler: (value: any) => this.executeAction(action.id, value), + isSubmit: true, + }, + ], + } const modal = await this.modalCtrl.create({ component: GenericFormPage, - componentProps: { - title: action.name, - spec: action['input-spec'], - buttons: [ - { - text: 'Execute', - handler: (value: any) => { - return this.executeAction(action.id, value) - }, - isSubmit: true, - }, - ], - }, + componentProps: options, }) await modal.present() } else { diff --git a/frontend/projects/ui/src/app/pages/developer-routes/dev-config/dev-config.page.ts b/frontend/projects/ui/src/app/pages/developer-routes/dev-config/dev-config.page.ts index 6dc4c7e13..bce4fd6bd 100644 --- a/frontend/projects/ui/src/app/pages/developer-routes/dev-config/dev-config.page.ts +++ b/frontend/projects/ui/src/app/pages/developer-routes/dev-config/dev-config.page.ts @@ -7,7 +7,10 @@ import { filter, take } from 'rxjs/operators' import { ApiService } from 'src/app/services/api/embassy-api.service' import { PatchDB } from 'patch-db-client' import { getProjectId } from 'src/app/util/get-project-id' -import { GenericFormPage } from '../../../modals/generic-form/generic-form.page' +import { + GenericFormPage, + GenericFormOptions, +} from '../../../modals/generic-form/generic-form.page' import { DataModel } from 'src/app/services/patch-db/data-model' @Component({ @@ -46,21 +49,21 @@ export class DevConfigPage { this.errToast.present(e) } + const options: GenericFormOptions = { + title: 'Config Sample', + spec: JSON.parse(JSON.stringify(doc, null, 2)), + buttons: [ + { + text: 'OK', + handler: async () => true, + isSubmit: true, + }, + ], + } + const modal = await this.modalCtrl.create({ component: GenericFormPage, - componentProps: { - title: 'Config Sample', - spec: JSON.parse(JSON.stringify(doc, null, 2)), - buttons: [ - { - text: 'OK', - handler: () => { - return - }, - isSubmit: true, - }, - ], - }, + componentProps: options, }) await modal.present() } diff --git a/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.page.ts b/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.page.ts index 3ae2b394c..836d67425 100644 --- a/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.page.ts +++ b/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.page.ts @@ -1,7 +1,10 @@ import { ChangeDetectionStrategy, Component } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { LoadingController, ModalController } from '@ionic/angular' -import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page' +import { + GenericFormPage, + GenericFormOptions, +} from 'src/app/modals/generic-form/generic-form.page' import { BasicInfo, getBasicInfoSpec } from './form-info' import { PatchDB } from 'patch-db-client' import { ApiService } from 'src/app/services/api/embassy-api.service' @@ -29,26 +32,27 @@ export class DeveloperMenuPage { ) {} async openBasicInfoModal(data: DevProjectData) { + const options: GenericFormOptions = { + title: 'Basic Info', + spec: getBasicInfoSpec(data), + buttons: [ + { + text: 'Save', + handler: async (basicInfo: BasicInfo) => + this.saveBasicInfo(basicInfo), + isSubmit: true, + }, + ], + } + const modal = await this.modalCtrl.create({ component: GenericFormPage, - componentProps: { - title: 'Basic Info', - spec: getBasicInfoSpec(data), - buttons: [ - { - text: 'Save', - handler: (basicInfo: BasicInfo) => { - this.saveBasicInfo(basicInfo) - }, - isSubmit: true, - }, - ], - }, + componentProps: options, }) await modal.present() } - async saveBasicInfo(basicInfo: BasicInfo) { + async saveBasicInfo(basicInfo: BasicInfo): Promise { const loader = await this.loadingCtrl.create({ message: 'Saving...', }) @@ -59,8 +63,10 @@ export class DeveloperMenuPage { ['dev', this.projectId, 'basic-info'], basicInfo, ) + return true } catch (e: any) { this.errToast.present(e) + return false } finally { loader.dismiss() } diff --git a/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts b/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts index 8b8ea4eaf..ea782b0c8 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts +++ b/frontend/projects/ui/src/app/pages/server-routes/server-show/server-show.page.ts @@ -57,7 +57,25 @@ export class ServerShowPage { @Inject(DOCUMENT) private readonly document: Document, ) {} - async setBrowserTab(): Promise { + async launchHttps() { + const { 'lan-address': lanAddress } = await getServerInfo(this.patch) + window.open(lanAddress) + } + + addClick(title: string) { + switch (title) { + case 'Manage': + this.addManageClick() + break + case 'Power': + this.addPowerClick() + break + default: + return + } + } + + private async setBrowserTab(): Promise { const chosenName = await firstValueFrom(this.patch.watch$('ui', 'name')) const options: GenericInputOptions = { @@ -82,14 +100,14 @@ export class ServerShowPage { await modal.present() } - async updateEos(): Promise { + private async updateEos(): Promise { const modal = await this.modalCtrl.create({ component: OSUpdatePage, }) modal.present() } - async presentAlertLogout() { + private async presentAlertLogout() { const alert = await this.alertCtrl.create({ header: 'Confirm', message: 'Are you sure you want to log out?', @@ -109,7 +127,7 @@ export class ServerShowPage { await alert.present() } - async presentAlertRestart() { + private async presentAlertRestart() { const alert = await this.alertCtrl.create({ header: 'Restart', message: @@ -131,7 +149,7 @@ export class ServerShowPage { await alert.present() } - async presentAlertShutdown() { + private async presentAlertShutdown() { const alert = await this.alertCtrl.create({ header: 'Warning', message: @@ -154,7 +172,7 @@ export class ServerShowPage { await alert.present() } - async presentAlertSystemRebuild() { + private async presentAlertSystemRebuild() { const localPkgs = await getAllPackages(this.patch) const minutes = Object.keys(localPkgs).length * 2 const alert = await this.alertCtrl.create({ @@ -178,7 +196,7 @@ export class ServerShowPage { await alert.present() } - async presentAlertRepairDisk() { + private async presentAlertRepairDisk() { const alert = await this.alertCtrl.create({ header: 'Warning', message: `

This action should only be executed if directed by a Start9 support specialist. We recommend backing up your device before preforming this action.

If anything happens to the device during the reboot, such as losing power or unplugging the drive, the filesystem will be in an unrecoverable state. Please proceed with caution.

`, @@ -206,24 +224,6 @@ export class ServerShowPage { await alert.present() } - async launchHttps() { - const { 'lan-address': lanAddress } = await getServerInfo(this.patch) - window.open(lanAddress) - } - - addClick(title: string) { - switch (title) { - case 'Manage': - this.addManageClick() - break - case 'Power': - this.addPowerClick() - break - default: - return - } - } - private async setName(value: string | null): Promise { const loader = await this.loadingCtrl.create({ message: 'Saving...', diff --git a/frontend/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts b/frontend/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts index aba3c3af5..b6e1b966f 100644 --- a/frontend/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts +++ b/frontend/projects/ui/src/app/pages/server-routes/wifi/wifi.page.ts @@ -12,7 +12,10 @@ import { ActionSheetButton } from '@ionic/core' import { ValueSpecObject } from 'start-sdk/types/config-types' import { RR } from 'src/app/services/api/api.types' import { pauseFor, ErrorToastService } from '@start9labs/shared' -import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page' +import { + GenericFormPage, + GenericFormOptions, +} from 'src/app/modals/generic-form/generic-form.page' import { ConfigService } from 'src/app/services/config.service' @Component({ @@ -98,9 +101,7 @@ export class WifiPage { }, { text: 'Save', - handler: async (country: string) => { - this.setCountry(country) - }, + handler: (country: string) => this.setCountry(country), }, ], cssClass: 'enter-click select-warning', @@ -110,27 +111,28 @@ export class WifiPage { async presentModalAdd(ssid?: string, needsPW: boolean = true) { const wifiSpec = getWifiValueSpec(ssid, needsPW) + + const options: GenericFormOptions = { + title: wifiSpec.name, + spec: wifiSpec.spec, + buttons: [ + { + text: 'Save for Later', + handler: async (value: { ssid: string; password: string }) => + this.save(value.ssid, value.password), + }, + { + text: 'Save and Connect', + handler: async (value: { ssid: string; password: string }) => + this.saveAndConnect(value.ssid, value.password), + isSubmit: true, + }, + ], + } + const modal = await this.modalCtrl.create({ component: GenericFormPage, - componentProps: { - title: wifiSpec.name, - spec: wifiSpec.spec, - buttons: [ - { - text: 'Save for Later', - handler: async (value: { ssid: string; password: string }) => { - await this.save(value.ssid, value.password) - }, - }, - { - text: 'Save and Connect', - handler: async (value: { ssid: string; password: string }) => { - await this.saveAndConnect(value.ssid, value.password) - }, - isSubmit: true, - }, - ], - }, + componentProps: options, }) await modal.present() @@ -292,7 +294,7 @@ export class WifiPage { } } - private async save(ssid: string, password: string): Promise { + private async save(ssid: string, password: string): Promise { const loader = await this.loadingCtrl.create({ message: 'Saving...', }) @@ -306,14 +308,19 @@ export class WifiPage { connect: false, }) await this.getWifi() + return true } catch (e: any) { this.errToast.present(e) + return false } finally { loader.dismiss() } } - private async saveAndConnect(ssid: string, password: string): Promise { + private async saveAndConnect( + ssid: string, + password: string, + ): Promise { const loader = await this.loadingCtrl.create({ message: 'Connecting. This could take a while...', }) @@ -326,10 +333,11 @@ export class WifiPage { priority: 0, connect: true, }) - await this.confirmWifi(ssid, true) + return true } catch (e: any) { this.errToast.present(e) + return false } finally { loader.dismiss() }