mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
files for config
This commit is contained in:
committed by
Aiden McClelland
parent
e76ccba2f7
commit
dacd5d3e6b
10
frontend/package-lock.json
generated
10
frontend/package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "embassy-os",
|
"name": "embassy-os",
|
||||||
"version": "0.3.4",
|
"version": "0.3.4.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
"patch-db-client": "file: ../../../patch-db/client",
|
"patch-db-client": "file: ../../../patch-db/client",
|
||||||
"pbkdf2": "^3.1.2",
|
"pbkdf2": "^3.1.2",
|
||||||
"rxjs": "^7.5.6",
|
"rxjs": "^7.5.6",
|
||||||
"start-sdk": "^0.4.0-lib0.alpha6",
|
"start-sdk": "^0.4.0-lib0.alpha8",
|
||||||
"swiper": "^8.2.4",
|
"swiper": "^8.2.4",
|
||||||
"ts-matches": "^5.2.1",
|
"ts-matches": "^5.2.1",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
@@ -13771,9 +13771,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/start-sdk": {
|
"node_modules/start-sdk": {
|
||||||
"version": "0.4.0-lib0.alpha6",
|
"version": "0.4.0-lib0.alpha8",
|
||||||
"resolved": "https://registry.npmjs.org/start-sdk/-/start-sdk-0.4.0-lib0.alpha6.tgz",
|
"resolved": "https://registry.npmjs.org/start-sdk/-/start-sdk-0.4.0-lib0.alpha8.tgz",
|
||||||
"integrity": "sha512-DshqK1GFbxojoRK8/RjPm0+xmNZ0Ac41JhhwhqvpSIdbk7l4vkLm38KIOVJ9nAFw592Ob9vu1NHKySuYOXgQ8Q==",
|
"integrity": "sha512-qErlv8ikV8nYqyCxxSN856dUwddGK5OOwTXk62IiJPxY3si03P1NQ3MnFch6Vx3NXiOmKeNNqw4/bj26TdUWRA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iarna/toml": "^2.2.5",
|
"@iarna/toml": "^2.2.5",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
|||||||
@@ -74,7 +74,7 @@
|
|||||||
"patch-db-client": "file: ../../../patch-db/client",
|
"patch-db-client": "file: ../../../patch-db/client",
|
||||||
"pbkdf2": "^3.1.2",
|
"pbkdf2": "^3.1.2",
|
||||||
"rxjs": "^7.5.6",
|
"rxjs": "^7.5.6",
|
||||||
"start-sdk": "^0.4.0-lib0.alpha6",
|
"start-sdk": "^0.4.0-lib0.alpha8",
|
||||||
"swiper": "^8.2.4",
|
"swiper": "^8.2.4",
|
||||||
"ts-matches": "^5.2.1",
|
"ts-matches": "^5.2.1",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export class FormLabelComponent {
|
|||||||
name: string
|
name: string
|
||||||
new: boolean
|
new: boolean
|
||||||
edited: boolean
|
edited: boolean
|
||||||
description?: string
|
description: string | null
|
||||||
required?: boolean
|
required?: boolean
|
||||||
newOptions?: boolean
|
newOptions?: boolean
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ export class FormLabelComponent {
|
|||||||
async presentAlertDescription() {
|
async presentAlertDescription() {
|
||||||
const alert = await this.alertCtrl.create({
|
const alert = await this.alertCtrl.create({
|
||||||
header: this.data.name,
|
header: this.data.name,
|
||||||
message: this.data.description,
|
message: this.data.description!,
|
||||||
buttons: [
|
buttons: [
|
||||||
{
|
{
|
||||||
text: 'OK',
|
text: 'OK',
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<ion-item-group [formGroup]="formGroup">
|
<ion-item-group [formGroup]="formGroup">
|
||||||
<div *ngFor="let entry of formGroup.controls | keyvalue: asIsOrder">
|
<div *ngFor="let entry of formGroup.controls | keyvalue : asIsOrder">
|
||||||
<div *ngIf="objectSpec[entry.key] as spec">
|
<div *ngIf="objectSpec[entry.key] as spec">
|
||||||
<!-- string or number -->
|
<!-- string or number -->
|
||||||
<ng-container *ngIf="spec.type === 'string' || spec.type === 'number'">
|
<ng-container *ngIf="spec.type === 'string' || spec.type === 'number'">
|
||||||
<!-- label -->
|
<!-- label -->
|
||||||
<h4 class="input-label">
|
<p class="input-label">
|
||||||
<form-label
|
<form-label
|
||||||
[data]="{
|
[data]="{
|
||||||
name: spec.name,
|
name: spec.name,
|
||||||
@@ -14,11 +14,11 @@
|
|||||||
required: !spec.nullable
|
required: !spec.nullable
|
||||||
}"
|
}"
|
||||||
></form-label>
|
></form-label>
|
||||||
</h4>
|
</p>
|
||||||
<ion-item [color]="(theme$ | async) === 'Light' ? 'light' : 'dark'">
|
<ion-item [color]="(theme$ | async) === 'Light' ? 'light' : 'dark'">
|
||||||
<ion-textarea
|
<ion-textarea
|
||||||
*ngIf="spec.type === 'string' && spec.textarea; else notTextArea"
|
*ngIf="spec.type === 'string' && spec.textarea; else notTextArea"
|
||||||
[placeholder]="spec.placeholder || 'Enter ' + spec.name"
|
[placeholder]="spec.placeholder"
|
||||||
[formControlName]="entry.key"
|
[formControlName]="entry.key"
|
||||||
(ionFocus)="presentAlertChangeWarning(entry.key, spec)"
|
(ionFocus)="presentAlertChangeWarning(entry.key, spec)"
|
||||||
(ionChange)="handleInputChange()"
|
(ionChange)="handleInputChange()"
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
!unmasked[entry.key]
|
!unmasked[entry.key]
|
||||||
"
|
"
|
||||||
[inputmode]="spec.type === 'number' ? 'tel' : 'text'"
|
[inputmode]="spec.type === 'number' ? 'tel' : 'text'"
|
||||||
[placeholder]="spec.placeholder || 'Enter ' + spec.name"
|
[placeholder]="spec.placeholder"
|
||||||
[formControlName]="entry.key"
|
[formControlName]="entry.key"
|
||||||
(ionFocus)="presentAlertChangeWarning(entry.key, spec)"
|
(ionFocus)="presentAlertChangeWarning(entry.key, spec)"
|
||||||
(ionChange)="handleInputChange()"
|
(ionChange)="handleInputChange()"
|
||||||
@@ -63,20 +63,24 @@
|
|||||||
</ion-note>
|
</ion-note>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<p class="error-message">
|
<p class="error-message">
|
||||||
<span *ngIf="(formGroup | getControl: entry.key).errors as errors">
|
<span *ngIf="(formGroup | getControl : entry.key).errors as errors">
|
||||||
{{ errors | getError: $any(spec)['pattern-description'] }}
|
{{ errors | getError : $any(spec)['pattern-description'] }}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<!-- boolean or enum -->
|
<!-- boolean, enum, or file -->
|
||||||
<ion-item
|
<ion-item
|
||||||
*ngIf="spec.type === 'boolean' || spec.type === 'enum'"
|
*ngIf="
|
||||||
|
spec.type === 'boolean' ||
|
||||||
|
spec.type === 'enum' ||
|
||||||
|
spec.type === 'file'
|
||||||
|
"
|
||||||
style="--padding-start: 0"
|
style="--padding-start: 0"
|
||||||
>
|
>
|
||||||
<ion-button
|
<ion-button
|
||||||
*ngIf="spec.description"
|
*ngIf="spec.description as desc"
|
||||||
fill="clear"
|
fill="clear"
|
||||||
(click)="presentAlertBoolEnumDescription($event, spec)"
|
(click.stop)="presentAlertDescription(spec.name, desc)"
|
||||||
style="--padding-start: 0"
|
style="--padding-start: 0"
|
||||||
>
|
>
|
||||||
<ion-icon
|
<ion-icon
|
||||||
@@ -126,6 +130,35 @@
|
|||||||
{{ spec['value-names'][option] }}
|
{{ spec['value-names'][option] }}
|
||||||
</ion-select-option>
|
</ion-select-option>
|
||||||
</ion-select>
|
</ion-select>
|
||||||
|
|
||||||
|
<div
|
||||||
|
*ngIf="spec.type === 'file' && formGroup.get(entry.key) as control"
|
||||||
|
slot="end"
|
||||||
|
>
|
||||||
|
<ion-button
|
||||||
|
*ngIf="!control.value; else hasFile"
|
||||||
|
strong
|
||||||
|
color="dark"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
<label for="upload-file">Browse...</label>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
[accept]="spec.extensions.join(',')"
|
||||||
|
style="position: absolute; opacity: 0; height: 100%"
|
||||||
|
id="upload-file"
|
||||||
|
(change)="handleFileInput($event, control)"
|
||||||
|
/>
|
||||||
|
</ion-button>
|
||||||
|
<ng-template #hasFile>
|
||||||
|
<div class="inline">
|
||||||
|
<p class="ion-padding-end">{{ control.value.name }}</p>
|
||||||
|
<div style="cursor: pointer" (click)="clearFile(control)">
|
||||||
|
<ion-icon name="close"></ion-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<!-- object -->
|
<!-- object -->
|
||||||
<ng-container *ngIf="spec.type === 'object'">
|
<ng-container *ngIf="spec.type === 'object'">
|
||||||
@@ -159,7 +192,7 @@
|
|||||||
<!-- body -->
|
<!-- body -->
|
||||||
<tui-expand
|
<tui-expand
|
||||||
[expanded]="objectDisplay[entry.key].expanded"
|
[expanded]="objectDisplay[entry.key].expanded"
|
||||||
[id]="objectId | toElementId: entry.key"
|
[id]="objectId | toElementId : entry.key"
|
||||||
>
|
>
|
||||||
<div class="nested-wrapper">
|
<div class="nested-wrapper">
|
||||||
<form-object
|
<form-object
|
||||||
@@ -209,7 +242,7 @@
|
|||||||
</ion-button>
|
</ion-button>
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
<p class="error-message" style="margin-bottom: 8px">
|
<p class="error-message" style="margin-bottom: 8px">
|
||||||
<span *ngIf="(formGroup | getControl: entry.key).errors as errors">
|
<span *ngIf="(formGroup | getControl : entry.key).errors as errors">
|
||||||
{{ errors | getError }}
|
{{ errors | getError }}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
@@ -257,7 +290,7 @@
|
|||||||
<tui-expand
|
<tui-expand
|
||||||
style="padding-left: 24px"
|
style="padding-left: 24px"
|
||||||
[expanded]="objectListDisplay[entry.key][i].expanded"
|
[expanded]="objectListDisplay[entry.key][i].expanded"
|
||||||
[id]="objectId | toElementId: entry.key:i"
|
[id]="objectId | toElementId : entry.key : i"
|
||||||
>
|
>
|
||||||
<form-object
|
<form-object
|
||||||
*ngIf="spec.subtype === 'object'"
|
*ngIf="spec.subtype === 'object'"
|
||||||
@@ -294,7 +327,7 @@
|
|||||||
<!-- string or number -->
|
<!-- string or number -->
|
||||||
<div
|
<div
|
||||||
*ngIf="spec.subtype === 'string' || spec.subtype === 'number'"
|
*ngIf="spec.subtype === 'string' || spec.subtype === 'number'"
|
||||||
[id]="objectId | toElementId: entry.key:i"
|
[id]="objectId | toElementId : entry.key : i"
|
||||||
>
|
>
|
||||||
<ion-item
|
<ion-item
|
||||||
[color]="(theme$ | async) === 'Light' ? 'light' : 'dark'"
|
[color]="(theme$ | async) === 'Light' ? 'light' : 'dark'"
|
||||||
@@ -320,10 +353,10 @@
|
|||||||
<p class="error-message">
|
<p class="error-message">
|
||||||
<span
|
<span
|
||||||
*ngIf="
|
*ngIf="
|
||||||
(formGroup | getControl: entry.key:i).errors as errors
|
(formGroup | getControl : entry.key : i).errors as errors
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
{{ errors | getError: $any(spec)['pattern-description'] }}
|
{{ errors | getError : $any(spec)['pattern-description'] }}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -356,14 +389,14 @@
|
|||||||
(click)="presentModalEnumList(entry.key, $any(spec), formArr.value)"
|
(click)="presentModalEnumList(entry.key, $any(spec), formArr.value)"
|
||||||
>
|
>
|
||||||
<ion-label style="white-space: nowrap !important">
|
<ion-label style="white-space: nowrap !important">
|
||||||
<h2>{{ formArr.value | toEnumListDisplay: $any(spec.spec) }}</h2>
|
<h2>{{ formArr.value | toEnumListDisplay : $any(spec.spec) }}</h2>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-button slot="end" fill="clear" color="light">
|
<ion-button slot="end" fill="clear" color="light">
|
||||||
<ion-icon slot="icon-only" name="chevron-down"></ion-icon>
|
<ion-icon slot="icon-only" name="chevron-down"></ion-icon>
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<p class="error-message">
|
<p class="error-message">
|
||||||
<span *ngIf="(formGroup | getControl: entry.key).errors as errors">
|
<span *ngIf="(formGroup | getControl : entry.key).errors as errors">
|
||||||
{{ errors | getError }}
|
{{ errors | getError }}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -7,14 +7,18 @@ import {
|
|||||||
inject,
|
inject,
|
||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
} from '@angular/core'
|
} from '@angular/core'
|
||||||
import { FormArray, UntypedFormArray, UntypedFormGroup } from '@angular/forms'
|
import {
|
||||||
|
AbstractControl,
|
||||||
|
FormArray,
|
||||||
|
UntypedFormArray,
|
||||||
|
UntypedFormGroup,
|
||||||
|
} from '@angular/forms'
|
||||||
import { AlertButton, AlertController, ModalController } from '@ionic/angular'
|
import { AlertButton, AlertController, ModalController } from '@ionic/angular'
|
||||||
import {
|
import {
|
||||||
InputSpec,
|
InputSpec,
|
||||||
ListValueSpecOf,
|
ListValueSpecOf,
|
||||||
ValueSpec,
|
ValueSpec,
|
||||||
ValueSpecBoolean,
|
ValueSpecBoolean,
|
||||||
ValueSpecEnum,
|
|
||||||
ValueSpecList,
|
ValueSpecList,
|
||||||
ValueSpecListOf,
|
ValueSpecListOf,
|
||||||
ValueSpecUnion,
|
ValueSpecUnion,
|
||||||
@@ -236,13 +240,15 @@ export class FormObjectComponent {
|
|||||||
await alert.present()
|
await alert.present()
|
||||||
}
|
}
|
||||||
|
|
||||||
async presentAlertBoolEnumDescription(
|
handleFileInput(e: any, control: AbstractControl) {
|
||||||
event: Event,
|
control.patchValue(e.target.files[0])
|
||||||
spec: ValueSpecBoolean | ValueSpecEnum,
|
}
|
||||||
) {
|
|
||||||
event.stopPropagation()
|
|
||||||
const { name, description } = spec
|
|
||||||
|
|
||||||
|
clearFile(control: AbstractControl) {
|
||||||
|
control.patchValue(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
async presentAlertDescription(name: string, description: string) {
|
||||||
const alert = await this.alertCtrl.create({
|
const alert = await this.alertCtrl.create({
|
||||||
header: name,
|
header: name,
|
||||||
message: description || '',
|
message: description || '',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
|||||||
import { UntypedFormGroup } from '@angular/forms'
|
import { UntypedFormGroup } from '@angular/forms'
|
||||||
import { v4 } from 'uuid'
|
import { v4 } from 'uuid'
|
||||||
import { FormService } from 'src/app/services/form.service'
|
import { FormService } from 'src/app/services/form.service'
|
||||||
import { ValueSpecUnion } from 'src/app/pkg-config/config-types'
|
import { ValueSpecUnion } from 'start-sdk/types/config-types'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'form-union',
|
selector: 'form-union',
|
||||||
|
|||||||
@@ -843,6 +843,15 @@ export module Mock {
|
|||||||
warning: 'Chain will have to resync!',
|
warning: 'Chain will have to resync!',
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
document: {
|
||||||
|
name: 'Needed File',
|
||||||
|
type: 'file',
|
||||||
|
description: 'A file we need',
|
||||||
|
placeholder: 'Testing placeholder',
|
||||||
|
warning: 'Testing warning',
|
||||||
|
nullable: false,
|
||||||
|
extensions: ['.png'],
|
||||||
|
},
|
||||||
'object-list': {
|
'object-list': {
|
||||||
name: 'Object List',
|
name: 'Object List',
|
||||||
type: 'list',
|
type: 'list',
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
UniqueBy,
|
UniqueBy,
|
||||||
ValueSpec,
|
ValueSpec,
|
||||||
ValueSpecEnum,
|
ValueSpecEnum,
|
||||||
|
ValueSpecFile,
|
||||||
ValueSpecList,
|
ValueSpecList,
|
||||||
ValueSpecNumber,
|
ValueSpecNumber,
|
||||||
ValueSpecObject,
|
ValueSpecObject,
|
||||||
@@ -102,31 +103,33 @@ export class FormService {
|
|||||||
let value: any
|
let value: any
|
||||||
switch (spec.type) {
|
switch (spec.type) {
|
||||||
case 'string':
|
case 'string':
|
||||||
validators = stringValidators(spec)
|
|
||||||
if (currentValue !== undefined) {
|
if (currentValue !== undefined) {
|
||||||
value = currentValue
|
value = currentValue
|
||||||
} else {
|
} else {
|
||||||
value = spec.default ? getDefaultString(spec.default) : null
|
value = spec.default ? getDefaultString(spec.default) : null
|
||||||
}
|
}
|
||||||
return this.formBuilder.control(value, validators)
|
return this.formBuilder.control(value, stringValidators(spec))
|
||||||
case 'number':
|
case 'number':
|
||||||
validators = numberValidators(spec)
|
|
||||||
if (currentValue !== undefined) {
|
if (currentValue !== undefined) {
|
||||||
value = currentValue
|
value = currentValue
|
||||||
} else {
|
} else {
|
||||||
value = spec.default || null
|
value = spec.default || null
|
||||||
}
|
}
|
||||||
return this.formBuilder.control(value, validators)
|
return this.formBuilder.control(value, numberValidators(spec))
|
||||||
case 'object':
|
case 'object':
|
||||||
return this.getFormGroup(spec.spec, [], currentValue)
|
return this.getFormGroup(spec.spec, [], currentValue)
|
||||||
case 'list':
|
case 'list':
|
||||||
validators = listValidators(spec)
|
|
||||||
const mapped = (
|
const mapped = (
|
||||||
Array.isArray(currentValue) ? currentValue : (spec.default as any[])
|
Array.isArray(currentValue) ? currentValue : (spec.default as any[])
|
||||||
).map(entry => {
|
).map(entry => {
|
||||||
return this.getListItem(spec, entry)
|
return this.getListItem(spec, entry)
|
||||||
})
|
})
|
||||||
return this.formBuilder.array(mapped, validators)
|
return this.formBuilder.array(mapped, listValidators(spec))
|
||||||
|
case 'file':
|
||||||
|
return this.formBuilder.control(
|
||||||
|
currentValue || null,
|
||||||
|
fileValidators(spec),
|
||||||
|
)
|
||||||
case 'union':
|
case 'union':
|
||||||
const currentSelection = currentValue?.[spec.tag.id]
|
const currentSelection = currentValue?.[spec.tag.id]
|
||||||
const isValid = !!spec.variants[currentSelection]
|
const isValid = !!spec.variants[currentSelection]
|
||||||
@@ -204,6 +207,16 @@ function listValidators(spec: ValueSpecList): ValidatorFn[] {
|
|||||||
return validators
|
return validators
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fileValidators(spec: ValueSpecFile): ValidatorFn[] {
|
||||||
|
const validators: ValidatorFn[] = []
|
||||||
|
|
||||||
|
if (!spec.nullable) {
|
||||||
|
validators.push(Validators.required)
|
||||||
|
}
|
||||||
|
|
||||||
|
return validators
|
||||||
|
}
|
||||||
|
|
||||||
export function numberInRange(stringRange: string): ValidatorFn {
|
export function numberInRange(stringRange: string): ValidatorFn {
|
||||||
return control => {
|
return control => {
|
||||||
const value = control.value
|
const value = control.value
|
||||||
|
|||||||
Reference in New Issue
Block a user