generic form options

This commit is contained in:
Matt Hill
2023-03-22 14:58:46 -06:00
committed by Aiden McClelland
parent 13cda80ee6
commit 234258a077
9 changed files with 157 additions and 125 deletions

View File

@@ -24,7 +24,7 @@
<ion-toolbar class="footer">
<ion-buttons slot="end">
<ion-button
class="ion-padding-end"
class="ion-padding-end enter-click"
*ngFor="let button of buttons"
(click)="handleClick(button.handler)"
>

View File

@@ -22,7 +22,7 @@ export class GenericFormPage {
@Input() title!: string
@Input() spec!: InputSpec
@Input() buttons!: ActionButton[]
@Input() initialValue: object = {}
@Input() initialValue: Record<string, any> = {}
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<void> {
@@ -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<string, any>
}

View File

@@ -92,5 +92,5 @@ export interface GenericInputOptions {
placeholder?: string
nullable?: boolean
useMask?: boolean
initialValue?: string
initialValue?: string | null
}

View File

@@ -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<void> {
private async saveOnly(rawUrl: string): Promise<boolean> {
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<void> {
private async saveAndConnect(rawUrl: string): Promise<boolean> {
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()

View File

@@ -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 {

View File

@@ -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()
}

View File

@@ -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<boolean> {
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()
}

View File

@@ -57,7 +57,25 @@ export class ServerShowPage {
@Inject(DOCUMENT) private readonly document: Document,
) {}
async setBrowserTab(): Promise<void> {
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<void> {
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<void> {
private async updateEos(): Promise<void> {
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: `<p>This action should only be executed if directed by a Start9 support specialist. We recommend backing up your device before preforming this action.</p><p>If anything happens to the device during the reboot, such as losing power or unplugging the drive, the filesystem <i>will</i> be in an unrecoverable state. Please proceed with caution.</p>`,
@@ -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<void> {
const loader = await this.loadingCtrl.create({
message: 'Saving...',

View File

@@ -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<void> {
private async save(ssid: string, password: string): Promise<boolean> {
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<void> {
private async saveAndConnect(
ssid: string,
password: string,
): Promise<boolean> {
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()
}