mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 04:01:58 +00:00
implement textarea config value
This commit is contained in:
committed by
Aiden McClelland
parent
c7438c4aff
commit
5675fc51a0
@@ -286,7 +286,6 @@ const CifsSpec: InputSpec = {
|
||||
nullable: false,
|
||||
masked: false,
|
||||
default: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
path: {
|
||||
@@ -299,7 +298,6 @@ const CifsSpec: InputSpec = {
|
||||
nullable: false,
|
||||
masked: false,
|
||||
default: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
username: {
|
||||
@@ -312,7 +310,6 @@ const CifsSpec: InputSpec = {
|
||||
nullable: false,
|
||||
masked: false,
|
||||
default: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
password: {
|
||||
@@ -325,7 +322,6 @@ const CifsSpec: InputSpec = {
|
||||
nullable: true,
|
||||
masked: true,
|
||||
default: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
></form-label>
|
||||
<ion-item [color]="(theme$ | async) === 'Light' ? 'light' : 'dark'">
|
||||
<ion-textarea
|
||||
*ngIf="spec.type === 'string' && spec.textarea; else notTextArea"
|
||||
*ngIf="spec.type === 'textarea'; else notTextArea"
|
||||
formWarning
|
||||
#warning="formWarning"
|
||||
[placeholder]="spec.placeholder"
|
||||
|
||||
@@ -10,7 +10,7 @@ import { THEME } from '@start9labs/shared'
|
||||
})
|
||||
export class FormInputComponent {
|
||||
@Input() name!: string
|
||||
@Input() spec!: ValueSpecOf<'string' | 'number'>
|
||||
@Input() spec!: ValueSpecOf<'string' | 'textarea' | 'number'>
|
||||
@Input() control!: FormControl
|
||||
|
||||
@Output() onInputChange = new EventEmitter<void>()
|
||||
|
||||
@@ -9,7 +9,11 @@
|
||||
></form-file>
|
||||
<!-- string or number -->
|
||||
<form-input
|
||||
*ngIf="spec.type === 'string' || spec.type === 'number'"
|
||||
*ngIf="
|
||||
spec.type === 'string' ||
|
||||
spec.type === 'textarea' ||
|
||||
spec.type === 'number'
|
||||
"
|
||||
[spec]="spec"
|
||||
[name]="entry.key"
|
||||
[control]="$any(entry.value)"
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item-divider>
|
||||
<p class="error-message">
|
||||
<span *ngIf="unionControl?.errors as errors">
|
||||
{{ errors | getError }}
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<tui-elastic-container [id]="objectId | toElementId : 'union'" class="indent">
|
||||
<form-object
|
||||
|
||||
@@ -22,3 +22,9 @@ ion-item-divider {
|
||||
.indent {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
margin-top: 2px;
|
||||
font-size: small;
|
||||
color: var(--ion-color-danger);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
||||
import { UntypedFormGroup } from '@angular/forms'
|
||||
import { AbstractControl, UntypedFormGroup } from '@angular/forms'
|
||||
import { v4 } from 'uuid'
|
||||
import { FormService } from 'src/app/services/form.service'
|
||||
import {
|
||||
@@ -22,16 +22,20 @@ export class FormUnionComponent {
|
||||
@Input() current?: Record<string, any>
|
||||
@Input() original?: Record<string, any>
|
||||
|
||||
get unionControl(): AbstractControl | null {
|
||||
return this.formGroup.get(unionSelectKey)
|
||||
}
|
||||
|
||||
get selectedVariant(): string {
|
||||
return this.formGroup.get(unionSelectKey)?.value
|
||||
return this.unionControl?.value || ''
|
||||
}
|
||||
|
||||
get variantName(): string {
|
||||
return this.spec.variants[this.selectedVariant].name
|
||||
return this.spec.variants[this.selectedVariant]?.name || ''
|
||||
}
|
||||
|
||||
get variantSpec(): InputSpec {
|
||||
return this.spec.variants[this.selectedVariant].spec
|
||||
return this.spec.variants[this.selectedVariant]?.spec || {}
|
||||
}
|
||||
|
||||
get hasNewOptions(): boolean {
|
||||
|
||||
@@ -279,7 +279,6 @@ function getMarketplaceValueSpec(): ValueSpecObject {
|
||||
patternDescription: 'Must be a valid URL',
|
||||
placeholder: 'e.g. https://example.org',
|
||||
default: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -229,7 +229,6 @@ const SAMPLE_CONFIG: InputSpec = {
|
||||
pattern: '^[a-zA-Z0-9! _]+$',
|
||||
patternDescription: 'Must be alphanumeric (may contain underscore).',
|
||||
default: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
'sample-number': {
|
||||
|
||||
@@ -30,7 +30,6 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
pattern: '^([a-z][a-z0-9]*)(-[a-z0-9]+)*$',
|
||||
patternDescription: 'Must be kebab case',
|
||||
default: basicInfo?.id || '',
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
title: {
|
||||
@@ -43,7 +42,6 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
default: basicInfo ? basicInfo.title : devData.name,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
'service-version-number': {
|
||||
@@ -57,7 +55,6 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
pattern: '^([0-9]+).([0-9]+).([0-9]+).([0-9]+)$',
|
||||
patternDescription: 'Must be valid Emver version',
|
||||
default: basicInfo?.['service-version-number'] || '',
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
description: {
|
||||
@@ -74,23 +71,17 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
placeholder: null,
|
||||
nullable: false,
|
||||
masked: false,
|
||||
textarea: true,
|
||||
default: basicInfo?.description?.short || '',
|
||||
pattern: '^.{1,320}$',
|
||||
patternDescription: 'Must be shorter than 320 characters',
|
||||
warning: null,
|
||||
},
|
||||
long: {
|
||||
type: 'string',
|
||||
type: 'textarea',
|
||||
name: 'Long Description',
|
||||
description: `This description will display with additional details in the service's individual marketplace page`,
|
||||
placeholder: null,
|
||||
nullable: false,
|
||||
masked: false,
|
||||
textarea: true,
|
||||
default: basicInfo?.description?.long || '',
|
||||
pattern: '^.{1,5000}$',
|
||||
patternDescription: 'Must be shorter than 5000 characters',
|
||||
warning: null,
|
||||
},
|
||||
},
|
||||
@@ -105,7 +96,6 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
masked: false,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: true,
|
||||
default: basicInfo?.['release-notes'] || '',
|
||||
warning: null,
|
||||
},
|
||||
@@ -139,7 +129,6 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
nullable: false,
|
||||
masked: false,
|
||||
default: basicInfo?.['wrapper-repo'] || '',
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
'upstream-repo': {
|
||||
@@ -152,7 +141,6 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
nullable: true,
|
||||
masked: false,
|
||||
default: basicInfo?.['upstream-repo'] || '',
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
'support-site': {
|
||||
@@ -165,7 +153,6 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
nullable: true,
|
||||
masked: false,
|
||||
default: basicInfo?.['support-site'] || '',
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
'marketing-site': {
|
||||
@@ -178,7 +165,6 @@ export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
|
||||
nullable: true,
|
||||
masked: false,
|
||||
default: basicInfo?.['marketing-site'] || '',
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -365,7 +365,6 @@ function getWifiValueSpec(
|
||||
nullable: false,
|
||||
masked: false,
|
||||
default: ssid || null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
password: {
|
||||
@@ -378,7 +377,6 @@ function getWifiValueSpec(
|
||||
pattern: '^.{8,}$',
|
||||
patternDescription: 'Must be longer than 8 characters',
|
||||
default: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
Manifest,
|
||||
} from '@start9labs/marketplace'
|
||||
import { Log } from '@start9labs/shared'
|
||||
import { unionSelectKey } from 'start-sdk/lib/config/config-types'
|
||||
|
||||
export module Mock {
|
||||
export const ServerUpdated: ServerStatusInfo = {
|
||||
@@ -718,7 +719,7 @@ export module Mock {
|
||||
description:
|
||||
'<p>The Bitcoin Core node to connect to over the peer-to-peer (P2P) interface:</p><ul><li><strong>Bitcoin Core</strong>: The Bitcoin Core service installed on this device</li><li><strong>External Node</strong>: A Bitcoin node running on a different device</li></ul>',
|
||||
warning: null,
|
||||
default: 'internal',
|
||||
default: null,
|
||||
nullable: false,
|
||||
variants: {
|
||||
internal: { name: 'Internal', spec: {} },
|
||||
@@ -734,7 +735,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -781,7 +781,6 @@ export module Mock {
|
||||
pattern: '^[a-zA-Z]+$',
|
||||
patternDescription: 'must contain only letters.',
|
||||
masked: false,
|
||||
textarea: false,
|
||||
},
|
||||
rpcuser: {
|
||||
name: 'RPC Username',
|
||||
@@ -794,7 +793,6 @@ export module Mock {
|
||||
pattern: '^[a-zA-Z]+$',
|
||||
patternDescription: 'must contain only letters.',
|
||||
masked: false,
|
||||
textarea: false,
|
||||
},
|
||||
rpcpass: {
|
||||
name: 'RPC User Password',
|
||||
@@ -810,7 +808,6 @@ export module Mock {
|
||||
masked: true,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
},
|
||||
rpcpass2: {
|
||||
name: 'RPC User Password',
|
||||
@@ -826,12 +823,19 @@ export module Mock {
|
||||
masked: true,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
bio: {
|
||||
name: 'Bio',
|
||||
type: 'textarea',
|
||||
description: 'Your personal bio',
|
||||
placeholder: 'Tell the world about yourself',
|
||||
warning: null,
|
||||
nullable: true,
|
||||
},
|
||||
testnet: {
|
||||
name: 'Testnet',
|
||||
type: 'boolean',
|
||||
@@ -882,7 +886,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -899,7 +902,6 @@ export module Mock {
|
||||
patternDescription: 'must contain only letters.',
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
age: {
|
||||
@@ -994,7 +996,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1007,7 +1008,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1038,7 +1038,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
rulemakerip: {
|
||||
@@ -1052,7 +1051,6 @@ export module Mock {
|
||||
patternDescription: 'may only contain numbers and periods',
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
},
|
||||
@@ -1068,7 +1066,6 @@ export module Mock {
|
||||
patternDescription: 'must contain only letters.',
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
rpcpass: {
|
||||
@@ -1084,7 +1081,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
},
|
||||
@@ -1116,7 +1112,6 @@ export module Mock {
|
||||
pattern: '^[a-zA-Z]+$',
|
||||
patternDescription: 'Must contain only letters.',
|
||||
placeholder: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1129,7 +1124,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1145,7 +1139,6 @@ export module Mock {
|
||||
patternDescription: 'anything',
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
'private-domain': {
|
||||
@@ -1157,7 +1150,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1188,7 +1180,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1253,7 +1244,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1266,7 +1256,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1279,7 +1268,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1308,7 +1296,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
lawagency: {
|
||||
@@ -1323,7 +1310,6 @@ export module Mock {
|
||||
'may only contain numbers and periods',
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
},
|
||||
@@ -1338,7 +1324,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
@@ -1369,7 +1354,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
rulemakerip: {
|
||||
@@ -1383,7 +1367,6 @@ export module Mock {
|
||||
patternDescription: 'may only contain numbers and periods',
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
},
|
||||
@@ -1399,7 +1382,6 @@ export module Mock {
|
||||
patternDescription: 'must contain only letters.',
|
||||
masked: false,
|
||||
placeholder: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
rpcpass: {
|
||||
@@ -1415,7 +1397,6 @@ export module Mock {
|
||||
placeholder: null,
|
||||
pattern: null,
|
||||
patternDescription: null,
|
||||
textarea: false,
|
||||
warning: null,
|
||||
},
|
||||
},
|
||||
@@ -1455,7 +1436,7 @@ export module Mock {
|
||||
rulemakers: [],
|
||||
},
|
||||
'bitcoin-node': {
|
||||
type: 'internal',
|
||||
[unionSelectKey]: 'internal',
|
||||
},
|
||||
port: 20,
|
||||
rpcallowip: undefined,
|
||||
@@ -1524,7 +1505,6 @@ export module Mock {
|
||||
masked: false,
|
||||
pattern: '^[a-zA-Z]+$',
|
||||
patternDescription: 'Must contain only letters.',
|
||||
textarea: false,
|
||||
warning: null,
|
||||
default: null,
|
||||
},
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
ValueSpecString,
|
||||
ValueSpecUnion,
|
||||
unionSelectKey,
|
||||
ValueSpecTextarea,
|
||||
} from 'start-sdk/lib/config/config-types'
|
||||
import { getDefaultString, Range } from '../util/config-utilities'
|
||||
const Mustache = require('mustache')
|
||||
@@ -48,7 +49,7 @@ export class FormService {
|
||||
): UntypedFormGroup {
|
||||
const { name, description, warning, variants, nullable } = spec
|
||||
|
||||
const enumSpec: ValueSpecSelect = {
|
||||
const selectSpec: ValueSpecSelect = {
|
||||
type: 'select',
|
||||
name,
|
||||
description,
|
||||
@@ -67,7 +68,7 @@ export class FormService {
|
||||
const selectedSpec = selection ? variants[selection].spec : {}
|
||||
|
||||
return this.getFormGroup({
|
||||
['selectedVariant']: enumSpec,
|
||||
[unionSelectKey]: selectSpec,
|
||||
...selectedSpec,
|
||||
})
|
||||
}
|
||||
@@ -111,6 +112,9 @@ export class FormService {
|
||||
value = spec.default ? getDefaultString(spec.default) : null
|
||||
}
|
||||
return this.formBuilder.control(value, stringValidators(spec))
|
||||
case 'textarea':
|
||||
value = currentValue || null
|
||||
return this.formBuilder.control(value, textareaValidators(spec))
|
||||
case 'number':
|
||||
if (currentValue !== undefined) {
|
||||
value = currentValue
|
||||
@@ -141,9 +145,11 @@ export class FormService {
|
||||
isValid ? currentSelection : spec.default,
|
||||
)
|
||||
case 'boolean':
|
||||
case 'select':
|
||||
value = currentValue === undefined ? spec.default : currentValue
|
||||
return this.formBuilder.control(value)
|
||||
case 'select':
|
||||
value = currentValue === undefined ? spec.default : currentValue
|
||||
return this.formBuilder.control(value, selectValidators(spec))
|
||||
case 'multiselect':
|
||||
value = currentValue === undefined ? spec.default : currentValue
|
||||
return this.formBuilder.control(value, multiselectValidators(spec))
|
||||
@@ -177,6 +183,16 @@ function stringValidators(
|
||||
return validators
|
||||
}
|
||||
|
||||
function textareaValidators(spec: ValueSpecTextarea): ValidatorFn[] {
|
||||
const validators: ValidatorFn[] = []
|
||||
|
||||
if (!spec.nullable) {
|
||||
validators.push(Validators.required)
|
||||
}
|
||||
|
||||
return validators
|
||||
}
|
||||
|
||||
function numberValidators(
|
||||
spec: ValueSpecNumber | ListValueSpecNumber,
|
||||
): ValidatorFn[] {
|
||||
@@ -197,6 +213,16 @@ function numberValidators(
|
||||
return validators
|
||||
}
|
||||
|
||||
function selectValidators(spec: ValueSpecSelect): ValidatorFn[] {
|
||||
const validators: ValidatorFn[] = []
|
||||
|
||||
if (!spec.nullable) {
|
||||
validators.push(Validators.required)
|
||||
}
|
||||
|
||||
return validators
|
||||
}
|
||||
|
||||
function multiselectValidators(spec: ValueSpecMultiselect): ValidatorFn[] {
|
||||
const validators: ValidatorFn[] = []
|
||||
validators.push(listInRange(spec.range))
|
||||
@@ -327,6 +353,7 @@ function listItemEquals(spec: ValueSpecList, val1: any, val2: any): boolean {
|
||||
function itemEquals(spec: ValueSpec, val1: any, val2: any): boolean {
|
||||
switch (spec.type) {
|
||||
case 'string':
|
||||
case 'textarea':
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
case 'select':
|
||||
@@ -513,7 +540,7 @@ export function convertValuesRecursive(
|
||||
control.setValue(
|
||||
control.value || control.value === 0 ? Number(control.value) : null,
|
||||
)
|
||||
} else if (valueSpec.type === 'string') {
|
||||
} else if (valueSpec.type === 'string' || valueSpec.type === 'textarea') {
|
||||
if (!control.value) control.setValue(null)
|
||||
} else if (valueSpec.type === 'object') {
|
||||
convertValuesRecursive(valueSpec.spec, group.get(key) as UntypedFormGroup)
|
||||
|
||||
Reference in New Issue
Block a user