diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 3ae07974f..ea921b697 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -27,11 +27,12 @@
"@ng-web-apis/resize-observer": "^2.0.0",
"@start9labs/argon2": "^0.1.0",
"@start9labs/emver": "^0.1.5",
- "@taiga-ui/addon-charts": "3.26.0",
- "@taiga-ui/cdk": "3.26.0",
- "@taiga-ui/core": "3.26.0",
- "@taiga-ui/icons": "3.26.0",
- "@taiga-ui/kit": "3.26.0",
+ "@start9labs/start-sdk": "git+https://github.com/Start9Labs/start-sdk#9a23967a7a9c529b27868ca3d7628d271bfb38af",
+ "@taiga-ui/addon-charts": "3.27.0",
+ "@taiga-ui/cdk": "3.27.0",
+ "@taiga-ui/core": "3.27.0",
+ "@taiga-ui/icons": "3.27.0",
+ "@taiga-ui/kit": "3.27.0",
"angular-svg-round-progressbar": "^9.0.0",
"ansi-to-html": "^0.7.2",
"base64-js": "^1.5.1",
@@ -53,7 +54,6 @@
"patch-db-client": "file: ../../../patch-db/client",
"pbkdf2": "^3.1.2",
"rxjs": "^7.5.6",
- "start-sdk": "^0.4.0-lib0.rc3",
"swiper": "^8.2.4",
"ts-matches": "^5.2.1",
"tslib": "^2.3.0",
@@ -3830,6 +3830,25 @@
"resolved": "https://registry.npmjs.org/@start9labs/emver/-/emver-0.1.5.tgz",
"integrity": "sha512-1dhiG03VkfEwSLx/JPKVms6srAbYFQgwfSGhwpUKMDliMXuAHGVaueStmqzVxn3JpH/HEVz0QW8w/PXHqjdiIg=="
},
+ "node_modules/@start9labs/start-sdk": {
+ "version": "0.4.0-rev0.lib0.rc2",
+ "resolved": "git+ssh://git@github.com/Start9Labs/start-sdk.git#9a23967a7a9c529b27868ca3d7628d271bfb38af",
+ "integrity": "sha512-P2EkO20hRszt2f/PdhsdRnNe3g0RG96RIV7n38htsVBouHOy/j4QZ1naBvWTuPOKOjdwb3Sbk5haq/FT10JPqw==",
+ "license": "MIT",
+ "dependencies": {
+ "@iarna/toml": "^2.2.5",
+ "ts-matches": "^5.4.1",
+ "yaml": "^2.2.2"
+ }
+ },
+ "node_modules/@start9labs/start-sdk/node_modules/yaml": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
+ "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/@stencil/core": {
"version": "2.22.3",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.22.3.tgz",
@@ -3843,9 +3862,9 @@
}
},
"node_modules/@taiga-ui/addon-charts": {
- "version": "3.26.0",
- "resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-3.26.0.tgz",
- "integrity": "sha512-nkAzI+B4CcPogUrpEwANu3D8n3cJzuIakF//8MyOzxvg0S4olpL81t9/Mx4+zyXxqjVTaU8q2a/rJNaV+7SyRg==",
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@taiga-ui/addon-charts/-/addon-charts-3.27.0.tgz",
+ "integrity": "sha512-PZMwRl8pcbF1UcRXzrnzF6rcdg6ZMHSdiF7Q2VUO8Q39GFguyYNYIFdkRHOLvh1wbsXQKoSxho72RN2yeEybCA==",
"dependencies": {
"tslib": ">=2.0.0"
},
@@ -3853,15 +3872,15 @@
"@angular/common": ">=12.0.0",
"@angular/core": ">=12.0.0",
"@ng-web-apis/common": ">=2.0.0",
- "@taiga-ui/cdk": ">=3.26.0",
- "@taiga-ui/core": ">=3.26.0",
+ "@taiga-ui/cdk": ">=3.27.0",
+ "@taiga-ui/core": ">=3.27.0",
"@tinkoff/ng-polymorpheus": ">=4.0.0"
}
},
"node_modules/@taiga-ui/cdk": {
- "version": "3.26.0",
- "resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-3.26.0.tgz",
- "integrity": "sha512-vd2CMQ/Z6bhzCQSBSHjSoCIJEE2g4RKmjl3RBK/OdA/L46s9/nQS8oTRBG8I0zk8lNx7YHqqC6u9IY6BZgOeAg==",
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@taiga-ui/cdk/-/cdk-3.27.0.tgz",
+ "integrity": "sha512-53XLDaQzStpjTV7a4X8658YVlaG7bp1JG4cgIamexylXwkWdsHa9o9KnFFOgsGO5I7heiQ2+kotKPWg7sgUwuQ==",
"dependencies": {
"@ng-web-apis/common": "2.1.0",
"@ng-web-apis/mutation-observer": "2.0.0",
@@ -3871,7 +3890,7 @@
"tslib": "2.5.0"
},
"optionalDependencies": {
- "ng-morph": "2.2.0",
+ "ng-morph": "2.2.4",
"parse5": "6.0.1"
},
"peerDependencies": {
@@ -3883,11 +3902,11 @@
}
},
"node_modules/@taiga-ui/core": {
- "version": "3.26.0",
- "resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-3.26.0.tgz",
- "integrity": "sha512-+IYn0ssZ3dO8Cm1HYAtbL5t+dvhp0RVzljdS72HBcr7IsnEhr2UDWWvsLv4DqsG4tXigWq6sL9wjXqg6/ylH4g==",
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@taiga-ui/core/-/core-3.27.0.tgz",
+ "integrity": "sha512-kXODpMjhxR+4YcdEFVpVaC++G7scMCSuSKPuXXoOCWtEZsQTp/pvSCCxcg951/lLRyh0MkzvEHyz7a8BKikgog==",
"dependencies": {
- "@taiga-ui/i18n": "^3.26.0",
+ "@taiga-ui/i18n": "^3.27.0",
"tslib": ">=2.0.0"
},
"peerDependencies": {
@@ -3899,17 +3918,17 @@
"@angular/router": ">=12.0.0",
"@ng-web-apis/common": ">=2.0.0",
"@ng-web-apis/mutation-observer": ">=2.0.0",
- "@taiga-ui/cdk": ">=3.26.0",
- "@taiga-ui/i18n": ">=3.26.0",
+ "@taiga-ui/cdk": ">=3.27.0",
+ "@taiga-ui/i18n": ">=3.27.0",
"@tinkoff/ng-event-plugins": ">=3.1.0",
"@tinkoff/ng-polymorpheus": ">=4.0.0",
"rxjs": ">=6.0.0"
}
},
"node_modules/@taiga-ui/i18n": {
- "version": "3.26.0",
- "resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-3.26.0.tgz",
- "integrity": "sha512-pI8IIQPYe3I7f/HQ4prCNpttEzwR1VA6ooJoaygVcSQDS8KVr03yyl9RBUzKpl57vnemuduVdfqM9LxX4bPeWQ==",
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@taiga-ui/i18n/-/i18n-3.27.0.tgz",
+ "integrity": "sha512-orOoo4CeecBc4GVMFcMhwvYo83wsudgtbnEbmFecE2NZO3wdntjOGE/TNpVM28JinO3uL5yabgDTd3UaxK6NSw==",
"dependencies": {
"tslib": ">=2.0.0"
},
@@ -3919,18 +3938,21 @@
}
},
"node_modules/@taiga-ui/icons": {
- "version": "3.26.0",
- "resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-3.26.0.tgz",
- "integrity": "sha512-q42C7LYqmOEf1P6GZPl6we5YZe9dboke4kNmbSYxWMT1EWCsgPWK8QmK02BsDeltUwSp7cnCP7jGZG1lkbuzKg==",
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@taiga-ui/icons/-/icons-3.27.0.tgz",
+ "integrity": "sha512-uXMe4B3cMgJ1qLfezsrOxvsHD9Bw6y39921GFMvlpeIwSEnXMc/rn1wEQpyd6Qo1Ib9AfFWHRDhBa7NPGnXllA==",
"dependencies": {
"tslib": "^2.2.0"
}
},
"node_modules/@taiga-ui/kit": {
- "version": "3.26.0",
- "resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-3.26.0.tgz",
- "integrity": "sha512-Sdp9FKSi/+C2PgirSLr03YQNyboewhFOaFRtT6cBXzscHJLfTWLSv6nNq1kMDLueVTtuPJjksAXsHj+fpnWIiQ==",
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@taiga-ui/kit/-/kit-3.27.0.tgz",
+ "integrity": "sha512-2YYiku5wXCr1XeqZHnOgLTH4o3rW3EsCx5O8FRSy2LCtkGFLfLemV7E8x1WQqYzOlTW7cCa2goo+K1NMrUWfMQ==",
"dependencies": {
+ "@maskito/angular": "0.11.1",
+ "@maskito/core": "0.11.1",
+ "@maskito/kit": "0.11.1",
"@ng-web-apis/intersection-observer": "3.0.0",
"text-mask-core": "5.1.2",
"tslib": ">=2.0.0"
@@ -3943,13 +3965,41 @@
"@ng-web-apis/common": ">=2.0.0",
"@ng-web-apis/mutation-observer": ">=2.0.0",
"@ng-web-apis/resize-observer": ">=2.0.0",
- "@taiga-ui/cdk": ">=3.26.0",
- "@taiga-ui/core": ">=3.26.0",
- "@taiga-ui/i18n": ">=3.26.0",
+ "@taiga-ui/cdk": ">=3.27.0",
+ "@taiga-ui/core": ">=3.27.0",
+ "@taiga-ui/i18n": ">=3.27.0",
"@tinkoff/ng-polymorpheus": ">=4.0.0",
"rxjs": ">=6.0.0"
}
},
+ "node_modules/@taiga-ui/kit/node_modules/@maskito/angular": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/@maskito/angular/-/angular-0.11.1.tgz",
+ "integrity": "sha512-80V4FT2jHv+VrJA2gRJpvWvbYVJvPHHoS0ZDqt8DZO/ejWe2SJP3+i/tFHar3i423tXk59dBLp0ahfwkaaNN1A==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "peerDependencies": {
+ "@angular/common": ">=12.0.0",
+ "@angular/core": ">=12.0.0",
+ "@angular/forms": ">=12.0.0",
+ "@maskito/core": "^0.11.1",
+ "rxjs": ">=6.0.0"
+ }
+ },
+ "node_modules/@taiga-ui/kit/node_modules/@maskito/core": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/@maskito/core/-/core-0.11.1.tgz",
+ "integrity": "sha512-8wPNVvlf+q1g4KF1By++eppIZxYs0XWCd/dzvtbfLQRwPXIPTnp9Cm8yWFPGbUVkfA5znkpk5OiiCLzkuYYg7A=="
+ },
+ "node_modules/@taiga-ui/kit/node_modules/@maskito/kit": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/@maskito/kit/-/kit-0.11.1.tgz",
+ "integrity": "sha512-5P+WC/oP9Cwk2aEyxGLpy934jpOwagvm2wLGGfNLZ7D0WaXSuDtXJGizG0Yt6EOnx3/EdChwI3WcmdLhDKK+bQ==",
+ "peerDependencies": {
+ "@maskito/core": "^0.11.1"
+ }
+ },
"node_modules/@tinkoff/ng-event-plugins": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@tinkoff/ng-event-plugins/-/ng-event-plugins-3.1.0.tgz",
@@ -6277,6 +6327,7 @@
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -10313,9 +10364,9 @@
}
},
"node_modules/ng-morph": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/ng-morph/-/ng-morph-2.2.0.tgz",
- "integrity": "sha512-0CEswQ+QrxPBWv1dBBu/N6idk0wIXkdFmqk+GW55/Ta7DJTKMCPZLVGXpp+Lia9XF55vVyxnOBw9J3QNN2Dv5A==",
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/ng-morph/-/ng-morph-2.2.4.tgz",
+ "integrity": "sha512-4AIsjcvUAT6htnX56DsUPZDQuNhWxmi09exUS6TreD6hKghGuqT3QfRf+K9aFw1FJyCsLsh/0py3S/sMtarsIA==",
"optional": true,
"dependencies": {
"jsonc-parser": "3.0.0",
@@ -13796,26 +13847,6 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
- "node_modules/start-sdk": {
- "version": "0.4.0-lib0.rc3",
- "resolved": "https://registry.npmjs.org/start-sdk/-/start-sdk-0.4.0-lib0.rc3.tgz",
- "integrity": "sha512-PAExAKEw0AUhk0UYu25o/UfAwclLt8tvQIDqzv4MaiFg4stPSzWYyFFBBX2kIKlBDlIMlzC6Fj0/8qoxzqq8iQ==",
- "dependencies": {
- "@iarna/toml": "^2.2.5",
- "deepmerge": "^4.3.1",
- "lodash": "^4.17.21",
- "ts-matches": "^5.4.1",
- "yaml": "^2.2.1"
- }
- },
- "node_modules/start-sdk/node_modules/yaml": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.1.tgz",
- "integrity": "sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==",
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 79fbac137..58e92d6cf 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -52,11 +52,11 @@
"@ng-web-apis/resize-observer": "^2.0.0",
"@start9labs/argon2": "^0.1.0",
"@start9labs/emver": "^0.1.5",
- "@taiga-ui/addon-charts": "3.26.0",
- "@taiga-ui/cdk": "3.26.0",
- "@taiga-ui/core": "3.26.0",
- "@taiga-ui/icons": "3.26.0",
- "@taiga-ui/kit": "3.26.0",
+ "@taiga-ui/addon-charts": "3.27.0",
+ "@taiga-ui/cdk": "3.27.0",
+ "@taiga-ui/core": "3.27.0",
+ "@taiga-ui/icons": "3.27.0",
+ "@taiga-ui/kit": "3.27.0",
"angular-svg-round-progressbar": "^9.0.0",
"ansi-to-html": "^0.7.2",
"base64-js": "^1.5.1",
@@ -78,7 +78,7 @@
"patch-db-client": "file: ../../../patch-db/client",
"pbkdf2": "^3.1.2",
"rxjs": "^7.5.6",
- "start-sdk": "^0.4.0-lib0.rc3",
+ "@start9labs/start-sdk": "git+https://github.com/Start9Labs/start-sdk#9a23967a7a9c529b27868ca3d7628d271bfb38af",
"swiper": "^8.2.4",
"ts-matches": "^5.2.1",
"tslib": "^2.3.0",
diff --git a/frontend/projects/ui/src/app/app-routing.module.ts b/frontend/projects/ui/src/app/app-routing.module.ts
index bf6d7d9ce..cf4883108 100644
--- a/frontend/projects/ui/src/app/app-routing.module.ts
+++ b/frontend/projects/ui/src/app/app-routing.module.ts
@@ -63,15 +63,6 @@ const routes: Routes = [
m => m.AppsRoutingModule,
),
},
- {
- path: 'developer',
- canActivate: [AuthGuard],
- canActivateChild: [AuthGuard],
- loadChildren: () =>
- import('./pages/developer-routes/developer-routing.module').then(
- m => m.DeveloperRoutingModule,
- ),
- },
{
path: 'backups',
canActivate: [AuthGuard],
diff --git a/frontend/projects/ui/src/app/app.module.ts b/frontend/projects/ui/src/app/app.module.ts
index 2df574618..2e4bd2d2f 100644
--- a/frontend/projects/ui/src/app/app.module.ts
+++ b/frontend/projects/ui/src/app/app.module.ts
@@ -22,7 +22,6 @@ import { AppComponent } from './app.component'
import { AppRoutingModule } from './app-routing.module'
import { OSWelcomePageModule } from './modals/os-welcome/os-welcome.module'
import { GenericInputComponentModule } from './modals/generic-input/generic-input.component.module'
-import { GenericFormPageModule } from './modals/generic-form/generic-form.module'
import { MarketplaceModule } from './marketplace.module'
import { PreloaderModule } from './app/preloader/preloader.module'
import { FooterModule } from './app/footer/footer.module'
@@ -54,7 +53,6 @@ import { FormPageModule } from './modals/form/form.module'
OSWelcomePageModule,
MarkdownModule,
GenericInputComponentModule,
- GenericFormPageModule,
MonacoEditorModule,
SharedPipesModule,
MarketplaceModule,
diff --git a/frontend/projects/ui/src/app/app/preloader/preloader.component.ts b/frontend/projects/ui/src/app/app/preloader/preloader.component.ts
index 5cb33fe0e..2dbb23e27 100644
--- a/frontend/projects/ui/src/app/app/preloader/preloader.component.ts
+++ b/frontend/projects/ui/src/app/app/preloader/preloader.component.ts
@@ -3,6 +3,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'
// TODO: Turn into DI token if this is needed someplace else too
const ICONS = [
'add',
+ 'alarm-outline',
'alert-outline',
'alert-circle-outline',
'aperture-outline',
diff --git a/frontend/projects/ui/src/app/components/form-object/form-label/form-label.component.html b/frontend/projects/ui/src/app/components/form-object/form-label/form-label.component.html
deleted file mode 100644
index 3bf9a7951..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-label/form-label.component.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-{{ data.name }}
-
- (New Options)
- (Edited)
-
- *
diff --git a/frontend/projects/ui/src/app/components/form-object/form-label/form-label.component.scss b/frontend/projects/ui/src/app/components/form-object/form-label/form-label.component.scss
deleted file mode 100644
index 9bd13bdd4..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-label/form-label.component.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-:host {
- display: flex;
- align-items: center;
- font-weight: bold;
-}
-
-.icon {
- --padding-start: 0;
- --padding-end: 4px;
- margin-right: 4px;
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-label/form-label.component.ts b/frontend/projects/ui/src/app/components/form-object/form-label/form-label.component.ts
deleted file mode 100644
index a798660ce..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-label/form-label.component.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
-import { AlertController } from '@ionic/angular'
-
-@Component({
- selector: 'form-label',
- templateUrl: './form-label.component.html',
- styleUrls: ['./form-label.component.scss'],
- changeDetection: ChangeDetectionStrategy.OnPush,
-})
-export class FormLabelComponent {
- @Input() data!: {
- name: string
- description: string | null
- edited?: boolean
- required?: boolean
- newOptions?: boolean
- }
-
- constructor(private readonly alertCtrl: AlertController) {}
-
- async presentAlertDescription() {
- const alert = await this.alertCtrl.create({
- header: this.data.name,
- message: this.data.description || '',
- buttons: [
- {
- text: 'OK',
- cssClass: 'enter-click',
- },
- ],
- })
- await alert.present()
- }
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object.module.ts b/frontend/projects/ui/src/app/components/form-object/form-object.module.ts
deleted file mode 100644
index 9741114ec..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object.module.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import { CommonModule } from '@angular/common'
-import { NgModule } from '@angular/core'
-import { FormsModule, ReactiveFormsModule } from '@angular/forms'
-import { IonicModule } from '@ionic/angular'
-import { SharedPipesModule } from '@start9labs/shared'
-import { TuiElasticContainerModule } from '@taiga-ui/kit'
-import { TuiExpandModule } from '@taiga-ui/core'
-import { FormLabelComponent } from './form-label/form-label.component'
-import { FormObjectComponent } from './form-object/form-object.component'
-import { FormUnionComponent } from './form-union/form-union.component'
-import {
- GetErrorPipe,
- ToWarningTextPipe,
- ToElementIdPipe,
- ToRangePipe,
-} from './form-object.pipes'
-import { FormFileComponent } from './form-object/controls/form-file/form-file.component'
-import { FormInputComponent } from './form-object/controls/form-input/form-input.component'
-import { FormWarningDirective } from './form-warning.directive'
-import { FormSubformComponent } from './form-object/controls/form-subform/form-subform.component'
-import { FormSelectComponent } from './form-object/controls/form-select/form-select.component'
-
-@NgModule({
- declarations: [
- FormObjectComponent,
- FormUnionComponent,
- FormLabelComponent,
- ToWarningTextPipe,
- GetErrorPipe,
- ToElementIdPipe,
- ToRangePipe,
- FormWarningDirective,
- FormFileComponent,
- FormInputComponent,
- FormSubformComponent,
- FormSelectComponent,
- ],
- imports: [
- CommonModule,
- IonicModule,
- FormsModule,
- ReactiveFormsModule,
- SharedPipesModule,
- TuiElasticContainerModule,
- TuiExpandModule,
- ],
- exports: [FormObjectComponent, FormLabelComponent],
-})
-export class FormObjectModule {}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object.pipes.ts b/frontend/projects/ui/src/app/components/form-object/form-object.pipes.ts
deleted file mode 100644
index 43a6fd98b..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object.pipes.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import { Pipe, PipeTransform } from '@angular/core'
-import { ValidationErrors } from '@angular/forms'
-import { IonicSafeString } from '@ionic/angular'
-import { Range } from 'src/app/util/config-utilities'
-import { getElementId } from './form-object/form-object.component'
-
-@Pipe({
- name: 'getError',
-})
-export class GetErrorPipe implements PipeTransform {
- transform(
- errors: ValidationErrors,
- patternDesc: string = 'Invalid pattern',
- ): string {
- if (errors['required']) {
- return 'Required'
- } else if (errors['pattern']) {
- return patternDesc
- } else if (errors['notNumber']) {
- return 'Must be a number'
- } else if (errors['numberNotInteger']) {
- return 'Must be an integer'
- } else if (errors['numberNotInRange']) {
- return errors['numberNotInRange'].value
- } else if (errors['listNotUnique']) {
- return errors['listNotUnique'].value
- } else if (errors['listNotInRange']) {
- return errors['listNotInRange'].value
- } else if (errors['listItemIssue']) {
- return errors['listItemIssue'].value
- } else {
- return 'Unknown error'
- }
- }
-}
-
-@Pipe({
- name: 'toWarningText',
-})
-export class ToWarningTextPipe implements PipeTransform {
- transform(text?: string | null): IonicSafeString | string {
- return text
- ? new IonicSafeString(`${text}`)
- : ''
- }
-}
-
-@Pipe({
- name: 'toRange',
-})
-export class ToRangePipe implements PipeTransform {
- transform(range?: string): Range {
- return Range.from(range)
- }
-}
-
-@Pipe({
- name: 'toElementId',
-})
-export class ToElementIdPipe implements PipeTransform {
- transform(objectId: string, key: string, index = 0): string {
- return getElementId(objectId, key, index)
- }
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-file/form-file.component.html b/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-file/form-file.component.html
deleted file mode 100644
index 9a06542f2..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-file/form-file.component.html
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
- Browse...
-
-
-
-
-
{{ control.value.name }}
-
-
-
-
-
-
-
-
-
- {{ errors | getError }}
-
-
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-file/form-file.component.scss b/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-file/form-file.component.scss
deleted file mode 100644
index 716a14c66..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-file/form-file.component.scss
+++ /dev/null
@@ -1,26 +0,0 @@
-:host {
- display: block;
-}
-
-ion-item-divider {
- text-transform: unset;
- border-bottom: 1px solid
- var(
- --ion-item-border-color,
- var(--ion-border-color, var(--ion-color-step-150, rgba(0, 0, 0, 0.13)))
- );
-
- --padding-top: 18px;
- --padding-start: 0;
-
- &.error-border {
- border-color: var(--ion-color-danger-shade);
- --border-color: var(--ion-color-danger-shade);
- }
-}
-
-.error-message {
- margin-top: 2px;
- font-size: small;
- color: var(--ion-color-danger);
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-file/form-file.component.ts b/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-file/form-file.component.ts
deleted file mode 100644
index d83c46838..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-file/form-file.component.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Component, Input } from '@angular/core'
-import { AbstractControl } from '@angular/forms'
-import { ValueSpecOf } from 'start-sdk/lib/config/configTypes'
-
-@Component({
- selector: 'form-file',
- templateUrl: './form-file.component.html',
- styleUrls: ['./form-file.component.scss'],
-})
-export class FormFileComponent {
- @Input() spec!: ValueSpecOf<'file'>
- @Input() control!: AbstractControl
-
- handleFileInput(e: any) {
- this.control.patchValue(e.target.files[0])
- }
-
- clearFile() {
- this.control.patchValue(null)
- }
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-input/form-input.component.html b/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-input/form-input.component.html
deleted file mode 100644
index 3112793b5..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-input/form-input.component.html
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- {{ spec.units }}
-
-
-
-
- {{ errors | getError : $any(spec).patternDescription }}
-
-
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-input/form-input.component.scss b/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-input/form-input.component.scss
deleted file mode 100644
index 4b6f0774d..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-input/form-input.component.scss
+++ /dev/null
@@ -1,24 +0,0 @@
-.label {
- margin: 16px 0 6px;
-}
-
-.input {
- font-family: 'Courier New';
- font-weight: bold;
-
- --placeholder-font-weight: 400;
-
- &_redacted {
- font-family: 'Redacted';
- }
-}
-
-.units {
- font-size: medium;
-}
-
-.error-message {
- margin-top: 2px;
- font-size: small;
- color: var(--ion-color-danger);
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-input/form-input.component.ts b/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-input/form-input.component.ts
deleted file mode 100644
index 03bd41d00..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-input/form-input.component.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Component, Input, inject, Output, EventEmitter } from '@angular/core'
-import { FormControl } from '@angular/forms'
-import { ValueSpecOf } from 'start-sdk/lib/config/configTypes'
-import { THEME } from '@start9labs/shared'
-
-@Component({
- selector: 'form-input',
- templateUrl: './form-input.component.html',
- styleUrls: ['./form-input.component.scss'],
-})
-export class FormInputComponent {
- @Input() name!: string
- @Input() spec!: ValueSpecOf<'text' | 'textarea' | 'number'>
- @Input() control!: FormControl
-
- @Output() onInputChange = new EventEmitter()
-
- unmasked = false
-
- readonly theme$ = inject(THEME)
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-select/form-select.component.html b/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-select/form-select.component.html
deleted file mode 100644
index ba236e1f1..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-select/form-select.component.html
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-
- 1
- ? '[' + control.value.length + ' selected]'
- : spec.values[control.value]
- "
- >
-
- {{ option.value }}
-
-
-
-
-
- {{ errors | getError }}
-
-
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-select/form-select.component.scss b/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-select/form-select.component.scss
deleted file mode 100644
index f03aa603a..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-select/form-select.component.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-.error-message {
- margin-top: 2px;
- font-size: small;
- color: var(--ion-color-danger);
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-select/form-select.component.ts b/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-select/form-select.component.ts
deleted file mode 100644
index 0f714e810..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-select/form-select.component.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { Component, Input } from '@angular/core'
-import { FormControl } from '@angular/forms'
-import { ValueSpecOf } from 'start-sdk/lib/config/configTypes'
-
-@Component({
- selector: 'form-select',
- templateUrl: './form-select.component.html',
- styleUrls: ['./form-select.component.scss'],
-})
-export class FormSelectComponent {
- @Input() spec!: ValueSpecOf<'toggle' | 'select' | 'multiselect'>
- @Input() control!: FormControl
- @Input() name!: string
-
- cancelBool = () => this.control.setValue(!this.control.value)
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-subform/form-subform.component.html b/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-subform/form-subform.component.html
deleted file mode 100644
index 6afcea097..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-subform/form-subform.component.html
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-subform/form-subform.component.scss b/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-subform/form-subform.component.scss
deleted file mode 100644
index de5ee20dc..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-subform/form-subform.component.scss
+++ /dev/null
@@ -1,25 +0,0 @@
-ion-item-divider {
- cursor: pointer;
- text-transform: unset;
- border-bottom: 1px solid
- var(
- --ion-item-border-color,
- var(--ion-border-color, var(--ion-color-step-150, rgba(0, 0, 0, 0.13)))
- );
-
- --padding-top: 18px;
- --padding-start: 0;
-
- &.error-border {
- border-color: var(--ion-color-danger-shade);
- --border-color: var(--ion-color-danger-shade);
- }
-}
-
-.icon {
- transition: transform 0.42s ease-out;
-
- &_rotated {
- transform: rotate(180deg);
- }
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-subform/form-subform.component.ts b/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-subform/form-subform.component.ts
deleted file mode 100644
index 51fb9ef1a..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/controls/form-subform/form-subform.component.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { Component, Input, Output, EventEmitter } from '@angular/core'
-import { AbstractControl } from '@angular/forms'
-import { ValueSpecOf } from 'start-sdk/lib/config/configTypes'
-
-@Component({
- selector: 'form-subform',
- templateUrl: './form-subform.component.html',
- styleUrls: ['./form-subform.component.scss'],
-})
-export class FormSubformComponent {
- @Input() spec!: ValueSpecOf<'object'>
- @Input() control!: AbstractControl
- @Input() hasNewOptions = false
-
- expanded = false
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/form-object.component.html b/frontend/projects/ui/src/app/components/form-object/form-object/form-object.component.html
deleted file mode 100644
index 40f534ddc..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/form-object.component.html
+++ /dev/null
@@ -1,198 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Add
-
-
-
-
- {{ errors | getError }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Delete
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ errors | getError : $any(spec).patternDescription }}
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/form-object.component.scss b/frontend/projects/ui/src/app/components/form-object/form-object/form-object.component.scss
deleted file mode 100644
index 46b838de4..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/form-object.component.scss
+++ /dev/null
@@ -1,41 +0,0 @@
-:host {
- display: block;
-}
-
-.input {
- font-family: 'Courier New';
- font-weight: bold;
-
- --placeholder-font-weight: 400;
-
- &_redacted {
- font-family: 'Redacted';
- }
-}
-
-ion-item-divider {
- text-transform: unset;
- border-bottom: 1px solid
- var(
- --ion-item-border-color,
- var(--ion-border-color, var(--ion-color-step-150, rgba(0, 0, 0, 0.13)))
- );
-
- --padding-top: 18px;
- --padding-start: 0;
-
- &.error-border {
- border-color: var(--ion-color-danger-shade);
- --border-color: var(--ion-color-danger-shade);
- }
-}
-
-.nested-wrapper {
- padding: 0 0 16px 24px;
-}
-
-.error-message {
- margin-top: 2px;
- font-size: small;
- color: var(--ion-color-danger);
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-object/form-object.component.ts b/frontend/projects/ui/src/app/components/form-object/form-object/form-object.component.ts
deleted file mode 100644
index ecbc8a728..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-object/form-object.component.ts
+++ /dev/null
@@ -1,253 +0,0 @@
-import {
- Component,
- Input,
- Output,
- EventEmitter,
- Inject,
- inject,
- SimpleChanges,
-} from '@angular/core'
-import { UntypedFormArray, UntypedFormGroup } from '@angular/forms'
-import { AlertButton, AlertController } from '@ionic/angular'
-import {
- InputSpec,
- ListValueSpecOf,
- ValueSpec,
- ValueSpecToggle,
- ValueSpecList,
- ValueSpecUnion,
-} from 'start-sdk/lib/config/configTypes'
-import { FormService } from 'src/app/services/form.service'
-import { THEME, pauseFor } from '@start9labs/shared'
-import { v4 } from 'uuid'
-import { DOCUMENT } from '@angular/common'
-
-const Mustache = require('mustache')
-
-@Component({
- selector: 'form-object',
- templateUrl: './form-object.component.html',
- styleUrls: ['./form-object.component.scss'],
-})
-export class FormObjectComponent {
- @Input() objectSpec!: InputSpec
- @Input() formGroup!: UntypedFormGroup
- @Input() current?: Record
- @Input() original?: Record
- @Output() onInputChange = new EventEmitter()
- @Output() hasNewOptions = new EventEmitter()
- warningAck: { [key: string]: boolean } = {}
- objectDisplay: {
- [key: string]: { expanded: boolean; hasNewOptions: boolean }
- } = {}
- objectListDisplay: {
- [key: string]: { expanded: boolean; displayAs: string }[]
- } = {}
- objectId = v4()
-
- readonly theme$ = inject(THEME)
-
- constructor(
- private readonly alertCtrl: AlertController,
- private readonly formService: FormService,
- @Inject(DOCUMENT) private readonly document: Document,
- ) {}
-
- ngOnInit() {
- this.setDisplays()
-
- // setTimeout hack to avoid ExpressionChangedAfterItHasBeenCheckedError
- // setTimeout(() => {
- // if (this.original && Object.values(this.objectSpec).some(spec => spec['is-new'])) this.hasNewOptions.emit()
- // })
- }
-
- ngOnChanges(changes: SimpleChanges) {
- const specChanges = changes['objectSpec']
-
- if (!specChanges) return
-
- if (
- !specChanges.firstChange &&
- Object.keys({
- ...specChanges.previousValue,
- ...specChanges.currentValue,
- }).length !== Object.keys(specChanges.previousValue).length
- ) {
- this.setDisplays()
- }
- }
-
- private setDisplays() {
- Object.keys(this.objectSpec).forEach(key => {
- const spec = this.objectSpec[key]
-
- if (spec.type === 'list' && spec.spec.type === 'object') {
- this.objectListDisplay[key] = []
- this.formGroup.get(key)?.value.forEach((obj: any, index: number) => {
- const displayAs = (spec.spec as ListValueSpecOf<'object'>).displayAs
- this.objectListDisplay[key][index] = {
- expanded: false,
- displayAs: displayAs
- ? (Mustache as any).render(displayAs, obj)
- : '',
- }
- })
- } else if (spec.type === 'object') {
- this.objectDisplay[key] = {
- expanded: false,
- hasNewOptions: false,
- }
- }
- })
- }
-
- addListItemWrapper(
- key: string,
- spec: T extends ValueSpecUnion ? never : T,
- ) {
- this.presentAlertChangeWarning(key, spec, () => this.addListItem(key))
- }
-
- toggleExpandObject(key: string) {
- this.objectDisplay[key].expanded = !this.objectDisplay[key].expanded
- }
-
- toggleExpandListObject(key: string, i: number) {
- this.objectListDisplay[key][i].expanded =
- !this.objectListDisplay[key][i].expanded
- }
-
- updateLabel(key: string, i: number, displayAs: string) {
- this.objectListDisplay[key][i].displayAs = displayAs
- ? Mustache.render(displayAs, this.formGroup.get(key)?.value[i])
- : ''
- }
-
- handleInputChange() {
- this.onInputChange.emit()
- }
-
- setHasNew(key: string) {
- this.hasNewOptions.emit()
- setTimeout(() => {
- this.objectDisplay[key].hasNewOptions = true
- })
- }
-
- handleBooleanChange(key: string, spec: ValueSpecToggle) {
- if (spec.warning) {
- const current = this.formGroup.get(key)?.value
- const cancelFn = () => this.formGroup.get(key)?.setValue(!current)
- this.presentAlertChangeWarning(key, spec, undefined, cancelFn)
- }
- }
-
- async presentAlertChangeWarning(
- key: string,
- spec: T extends ValueSpecUnion ? never : T,
- okFn?: Function,
- cancelFn?: Function,
- ) {
- if (!spec.warning || this.warningAck[key]) return okFn ? okFn() : null
- this.warningAck[key] = true
-
- const buttons: AlertButton[] = [
- {
- text: 'Ok',
- handler: () => {
- if (okFn) okFn()
- },
- cssClass: 'enter-click',
- },
- ]
-
- if (okFn || cancelFn) {
- buttons.unshift({
- text: 'Cancel',
- handler: () => {
- if (cancelFn) cancelFn()
- },
- })
- }
-
- const alert = await this.alertCtrl.create({
- header: 'Warning',
- subHeader: `Editing ${spec.name} has consequences:`,
- message: spec.warning,
- buttons,
- })
- await alert.present()
- }
-
- async presentAlertDelete(key: string, index: number) {
- const alert = await this.alertCtrl.create({
- header: 'Confirm',
- message: 'Are you sure you want to delete this entry?',
- buttons: [
- {
- text: 'Cancel',
- role: 'cancel',
- },
- {
- text: 'Delete',
- handler: () => {
- this.deleteListItem(key, index)
- },
- cssClass: 'enter-click',
- },
- ],
- })
- await alert.present()
- }
-
- private addListItem(key: string): void {
- const arr = this.formGroup.get(key) as UntypedFormArray
- const listSpec = this.objectSpec[key] as ValueSpecList
- const newItem = this.formService.getListItem(listSpec, undefined)!
-
- const index = arr.length
- arr.insert(index, newItem)
-
- if (listSpec.spec.type === 'object') {
- const displayAs = listSpec.spec.displayAs
- this.objectListDisplay[key].push({
- expanded: false,
- displayAs: displayAs ? Mustache.render(displayAs, newItem.value) : '',
- })
- }
-
- setTimeout(() => {
- const element = this.document.getElementById(
- getElementId(this.objectId, key, index),
- )
- element?.parentElement?.scrollIntoView({ behavior: 'smooth' })
-
- if (listSpec.spec.type === 'object') {
- pauseFor(250).then(() => this.toggleExpandListObject(key, index))
- }
- }, 100)
-
- arr.markAsDirty()
- }
-
- private deleteListItem(key: string, index: number, markDirty = true): void {
- // if (this.objectListDisplay[key])
- // this.objectListDisplay[key][index].height = '0px'
- const arr = this.formGroup.get(key) as UntypedFormArray
- if (markDirty) arr.markAsDirty()
- pauseFor(250).then(() => {
- if (this.objectListDisplay[key])
- this.objectListDisplay[key].splice(index, 1)
- arr.removeAt(index)
- })
- }
-
- asIsOrder() {
- return 0
- }
-}
-
-export function getElementId(objectId: string, key: string, index = 0): string {
- return `${key}-${index}-${objectId}`
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-union/form-union.component.html b/frontend/projects/ui/src/app/components/form-object/form-union/form-union.component.html
deleted file mode 100644
index b75d8d364..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-union/form-union.component.html
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
-
- {{ spec.variants[option.key].name }}
-
-
-
-
-
- {{ errors | getError }}
-
-
-
-
-
-
-
diff --git a/frontend/projects/ui/src/app/components/form-object/form-union/form-union.component.scss b/frontend/projects/ui/src/app/components/form-object/form-union/form-union.component.scss
deleted file mode 100644
index 71943c417..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-union/form-union.component.scss
+++ /dev/null
@@ -1,30 +0,0 @@
-:host {
- display: block;
-}
-
-ion-item-divider {
- text-transform: unset;
- border-bottom: 1px solid
- var(
- --ion-item-border-color,
- var(--ion-border-color, var(--ion-color-step-150, rgba(0, 0, 0, 0.13)))
- );
-
- --padding-top: 18px;
- --padding-start: 0;
-
- &.error-border {
- border-color: var(--ion-color-danger-shade);
- --border-color: var(--ion-color-danger-shade);
- }
-}
-
-.indent {
- margin-left: 24px;
-}
-
-.error-message {
- margin-top: 2px;
- font-size: small;
- color: var(--ion-color-danger);
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-union/form-union.component.ts b/frontend/projects/ui/src/app/components/form-object/form-union/form-union.component.ts
deleted file mode 100644
index 86ac6ad74..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-union/form-union.component.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
-import { AbstractControl, UntypedFormGroup } from '@angular/forms'
-import { v4 } from 'uuid'
-import { FormService } from 'src/app/services/form.service'
-import {
- ValueSpecUnion,
- InputSpec,
- unionSelectKey,
-} from 'start-sdk/lib/config/configTypes'
-
-@Component({
- selector: 'form-union',
- templateUrl: './form-union.component.html',
- styleUrls: ['./form-union.component.scss'],
- changeDetection: ChangeDetectionStrategy.OnPush,
-})
-export class FormUnionComponent {
- readonly unionSelectKey = unionSelectKey
-
- @Input() formGroup!: UntypedFormGroup
- @Input() spec!: ValueSpecUnion
- @Input() current?: Record
- @Input() original?: Record
-
- get unionControl(): AbstractControl | null {
- return this.formGroup.get(unionSelectKey)
- }
-
- get selectedVariant(): string {
- return this.unionControl?.value || ''
- }
-
- get variantName(): string {
- return this.spec.variants[this.selectedVariant]?.name || ''
- }
-
- get variantSpec(): InputSpec {
- return this.spec.variants[this.selectedVariant]?.spec || {}
- }
-
- get hasNewOptions(): boolean {
- // return Object.values(this.variantSpec).some(spec => spec['is-new'])
- return false
- }
-
- objectId = v4()
-
- constructor(private readonly formService: FormService) {}
-
- updateUnion(e: any): void {
- Object.keys(this.formGroup.controls).forEach(control => {
- if (control === unionSelectKey) return
- this.formGroup.removeControl(control)
- })
-
- const unionGroup = this.formService.getUnionObject(
- this.spec as ValueSpecUnion,
- e.detail.value,
- )
-
- Object.keys(unionGroup.controls).forEach(control => {
- if (control === unionSelectKey) return
- this.formGroup.addControl(control, unionGroup.controls[control])
- })
- }
-}
diff --git a/frontend/projects/ui/src/app/components/form-object/form-warning.directive.ts b/frontend/projects/ui/src/app/components/form-object/form-warning.directive.ts
deleted file mode 100644
index b05a617c4..000000000
--- a/frontend/projects/ui/src/app/components/form-object/form-warning.directive.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { Directive } from '@angular/core'
-import { ValueSpec, ValueSpecUnion } from 'start-sdk/lib/config/configTypes'
-import { AlertButton, AlertController } from '@ionic/angular'
-
-@Directive({
- selector: '[formWarning]',
- exportAs: 'formWarning',
-})
-export class FormWarningDirective {
- private warned = false
-
- constructor(private readonly alertCtrl: AlertController) {}
-
- async onChange(
- key: string,
- spec: T extends ValueSpecUnion ? never : T,
- okFn?: Function,
- cancelFn?: Function,
- ) {
- if (!spec.warning || this.warned) return okFn ? okFn() : null
-
- this.warned = true
-
- const buttons: AlertButton[] = [
- {
- text: 'Ok',
- handler: () => {
- if (okFn) okFn()
- },
- cssClass: 'enter-click',
- },
- ]
-
- if (okFn || cancelFn) {
- buttons.unshift({
- text: 'Cancel',
- handler: () => {
- if (cancelFn) cancelFn()
- },
- })
- }
-
- const alert = await this.alertCtrl.create({
- header: 'Warning',
- subHeader: `Editing ${spec.name} has consequences:`,
- message: spec.warning,
- buttons,
- })
- await alert.present()
- }
-}
diff --git a/frontend/projects/ui/src/app/components/form/control.ts b/frontend/projects/ui/src/app/components/form/control.ts
index a945f8e56..74756e3f5 100644
--- a/frontend/projects/ui/src/app/components/form/control.ts
+++ b/frontend/projects/ui/src/app/components/form/control.ts
@@ -1,6 +1,6 @@
import { inject } from '@angular/core'
import { FormControlComponent } from './form-control/form-control.component'
-import { ValueSpec } from 'start-sdk/lib/config/configTypes'
+import { ValueSpec } from '@start9labs/start-sdk/lib/config/configTypes'
export abstract class Control {
private readonly control: FormControlComponent =
diff --git a/frontend/projects/ui/src/app/components/form/form-array/form-array.component.ts b/frontend/projects/ui/src/app/components/form/form-array/form-array.component.ts
index a07c8cf7b..b426cc40c 100644
--- a/frontend/projects/ui/src/app/components/form/form-array/form-array.component.ts
+++ b/frontend/projects/ui/src/app/components/form/form-array/form-array.component.ts
@@ -9,7 +9,7 @@ import {
} from '@taiga-ui/core'
import { TUI_PROMPT } from '@taiga-ui/kit'
import { filter, takeUntil } from 'rxjs'
-import { ValueSpecList } from 'start-sdk/lib/config/configTypes'
+import { ValueSpecList } from '@start9labs/start-sdk/lib/config/configTypes'
import { FormService } from '../../../services/form.service'
import { ERRORS } from '../form-group/form-group.component'
diff --git a/frontend/projects/ui/src/app/components/form/form-color/form-color.component.ts b/frontend/projects/ui/src/app/components/form/form-color/form-color.component.ts
index 01aa1ee10..9f7a119e5 100644
--- a/frontend/projects/ui/src/app/components/form/form-color/form-color.component.ts
+++ b/frontend/projects/ui/src/app/components/form/form-color/form-color.component.ts
@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
-import { ValueSpecColor } from 'start-sdk/lib/config/configTypes'
+import { ValueSpecColor } from '@start9labs/start-sdk/lib/config/configTypes'
import { Control } from '../control'
import { MaskitoOptions } from '@maskito/core'
diff --git a/frontend/projects/ui/src/app/components/form/form-control/form-control.component.ts b/frontend/projects/ui/src/app/components/form/form-control/form-control.component.ts
index 472fa947d..a4d78245c 100644
--- a/frontend/projects/ui/src/app/components/form/form-control/form-control.component.ts
+++ b/frontend/projects/ui/src/app/components/form/form-control/form-control.component.ts
@@ -6,18 +6,14 @@ import {
TemplateRef,
ViewChild,
} from '@angular/core'
-import {
- AbstractTuiNullableControl,
- TuiContextWithImplicit,
-} from '@taiga-ui/cdk'
+import { AbstractTuiNullableControl } from '@taiga-ui/cdk'
import {
TuiAlertService,
TuiDialogContext,
TuiNotification,
} from '@taiga-ui/core'
-import { TUI_VALIDATION_ERRORS } from '@taiga-ui/kit'
import { filter, takeUntil } from 'rxjs'
-import { ValueSpec, ValueSpecText } from 'start-sdk/lib/config/configTypes'
+import { ValueSpec } from '@start9labs/start-sdk/lib/config/configTypes'
import { ERRORS } from '../form-group/form-group.component'
import { FORM_CONTROL_PROVIDERS } from './form-control.providers'
diff --git a/frontend/projects/ui/src/app/components/form/form-control/form-control.providers.ts b/frontend/projects/ui/src/app/components/form/form-control/form-control.providers.ts
index ca85ef7f3..439a9fc53 100644
--- a/frontend/projects/ui/src/app/components/form/form-control/form-control.providers.ts
+++ b/frontend/projects/ui/src/app/components/form/form-control/form-control.providers.ts
@@ -1,6 +1,6 @@
import { forwardRef, Provider } from '@angular/core'
import { TUI_VALIDATION_ERRORS } from '@taiga-ui/kit'
-import { ValueSpec } from 'start-sdk/lib/config/configTypes'
+import { ValueSpec } from '@start9labs/start-sdk/lib/config/configTypes'
import { FormControlComponent } from './form-control.component'
interface ValidatorsPatternError {
diff --git a/frontend/projects/ui/src/app/components/form/form-datetime/form-datetime.component.ts b/frontend/projects/ui/src/app/components/form/form-datetime/form-datetime.component.ts
index ff3e5bdd9..8495919ce 100644
--- a/frontend/projects/ui/src/app/components/form/form-datetime/form-datetime.component.ts
+++ b/frontend/projects/ui/src/app/components/form/form-datetime/form-datetime.component.ts
@@ -6,7 +6,7 @@ import {
tuiPure,
TuiTime,
} from '@taiga-ui/cdk'
-import { ValueSpecDatetime } from 'start-sdk/lib/config/configTypes'
+import { ValueSpecDatetime } from '@start9labs/start-sdk/lib/config/configTypes'
import { Control } from '../control'
@Component({
diff --git a/frontend/projects/ui/src/app/components/form/form-file/form-file.component.ts b/frontend/projects/ui/src/app/components/form/form-file/form-file.component.ts
index fbc9239c1..34a3b3a28 100644
--- a/frontend/projects/ui/src/app/components/form/form-file/form-file.component.ts
+++ b/frontend/projects/ui/src/app/components/form/form-file/form-file.component.ts
@@ -1,6 +1,6 @@
import { Component } from '@angular/core'
import { TuiFileLike } from '@taiga-ui/kit'
-import { ValueSpecFile } from 'start-sdk/lib/config/configTypes'
+import { ValueSpecFile } from '@start9labs/start-sdk/lib/config/configTypes'
import { Control } from '../control'
@Component({
diff --git a/frontend/projects/ui/src/app/components/form/form-group/form-group.component.ts b/frontend/projects/ui/src/app/components/form/form-group/form-group.component.ts
index 11c194278..8c6ff8af1 100644
--- a/frontend/projects/ui/src/app/components/form/form-group/form-group.component.ts
+++ b/frontend/projects/ui/src/app/components/form/form-group/form-group.component.ts
@@ -4,7 +4,7 @@ import {
Input,
ViewEncapsulation,
} from '@angular/core'
-import { InputSpec } from 'start-sdk/lib/config/configTypes'
+import { InputSpec } from '@start9labs/start-sdk/lib/config/configTypes'
import { FORM_GROUP_PROVIDERS } from './form-group.providers'
export const ERRORS = [
diff --git a/frontend/projects/ui/src/app/components/form/form-multiselect/form-multiselect.component.ts b/frontend/projects/ui/src/app/components/form/form-multiselect/form-multiselect.component.ts
index 4d7ddc909..53792d772 100644
--- a/frontend/projects/ui/src/app/components/form/form-multiselect/form-multiselect.component.ts
+++ b/frontend/projects/ui/src/app/components/form/form-multiselect/form-multiselect.component.ts
@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
-import { ValueSpecMultiselect } from 'start-sdk/lib/config/configTypes'
+import { ValueSpecMultiselect } from '@start9labs/start-sdk/lib/config/configTypes'
import { Control } from '../control'
import { tuiPure } from '@taiga-ui/cdk'
diff --git a/frontend/projects/ui/src/app/components/form/form-number/form-number.component.html b/frontend/projects/ui/src/app/components/form/form-number/form-number.component.html
index 60ad350de..274a07bbb 100644
--- a/frontend/projects/ui/src/app/components/form/form-number/form-number.component.html
+++ b/frontend/projects/ui/src/app/components/form/form-number/form-number.component.html
@@ -1,4 +1,3 @@
-
diff --git a/frontend/projects/ui/src/app/components/form/form-number/form-number.component.ts b/frontend/projects/ui/src/app/components/form/form-number/form-number.component.ts
index ee131bcd2..387b33719 100644
--- a/frontend/projects/ui/src/app/components/form/form-number/form-number.component.ts
+++ b/frontend/projects/ui/src/app/components/form/form-number/form-number.component.ts
@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
-import { ValueSpecNumber } from 'start-sdk/lib/config/configTypes'
+import { ValueSpecNumber } from '@start9labs/start-sdk/lib/config/configTypes'
import { Control } from '../control'
@Component({
diff --git a/frontend/projects/ui/src/app/components/form/form-object/form-object.component.ts b/frontend/projects/ui/src/app/components/form/form-object/form-object.component.ts
index 1b72f128d..cb8a5c9e9 100644
--- a/frontend/projects/ui/src/app/components/form/form-object/form-object.component.ts
+++ b/frontend/projects/ui/src/app/components/form/form-object/form-object.component.ts
@@ -7,7 +7,7 @@ import {
Output,
} from '@angular/core'
import { ControlContainer } from '@angular/forms'
-import { ValueSpecObject } from 'start-sdk/lib/config/configTypes'
+import { ValueSpecObject } from '@start9labs/start-sdk/lib/config/configTypes'
@Component({
selector: 'form-object',
diff --git a/frontend/projects/ui/src/app/components/form/form-select/form-select.component.ts b/frontend/projects/ui/src/app/components/form/form-select/form-select.component.ts
index 500f23deb..d358f821d 100644
--- a/frontend/projects/ui/src/app/components/form/form-select/form-select.component.ts
+++ b/frontend/projects/ui/src/app/components/form/form-select/form-select.component.ts
@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
-import { ValueSpecSelect } from 'start-sdk/lib/config/configTypes'
+import { ValueSpecSelect } from '@start9labs/start-sdk/lib/config/configTypes'
import { Control } from '../control'
@Component({
diff --git a/frontend/projects/ui/src/app/components/form/form-text/form-text.component.ts b/frontend/projects/ui/src/app/components/form/form-text/form-text.component.ts
index 0baa92411..37da806c8 100644
--- a/frontend/projects/ui/src/app/components/form/form-text/form-text.component.ts
+++ b/frontend/projects/ui/src/app/components/form/form-text/form-text.component.ts
@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
-import { ValueSpecText } from 'start-sdk/lib/config/configTypes'
+import { ValueSpecText } from '@start9labs/start-sdk/lib/config/configTypes'
import { Control } from '../control'
@Component({
diff --git a/frontend/projects/ui/src/app/components/form/form-textarea/form-textarea.component.ts b/frontend/projects/ui/src/app/components/form/form-textarea/form-textarea.component.ts
index 47a6ad315..bead77088 100644
--- a/frontend/projects/ui/src/app/components/form/form-textarea/form-textarea.component.ts
+++ b/frontend/projects/ui/src/app/components/form/form-textarea/form-textarea.component.ts
@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
-import { ValueSpecTextarea } from 'start-sdk/lib/config/configTypes'
+import { ValueSpecTextarea } from '@start9labs/start-sdk/lib/config/configTypes'
import { Control } from '../control'
@Component({
diff --git a/frontend/projects/ui/src/app/components/form/form-toggle/form-toggle.component.ts b/frontend/projects/ui/src/app/components/form/form-toggle/form-toggle.component.ts
index debbe6a67..b35d75a57 100644
--- a/frontend/projects/ui/src/app/components/form/form-toggle/form-toggle.component.ts
+++ b/frontend/projects/ui/src/app/components/form/form-toggle/form-toggle.component.ts
@@ -1,5 +1,5 @@
import { Component } from '@angular/core'
-import { ValueSpecToggle } from 'start-sdk/lib/config/configTypes'
+import { ValueSpecToggle } from '@start9labs/start-sdk/lib/config/configTypes'
import { Control } from '../control'
@Component({
diff --git a/frontend/projects/ui/src/app/components/form/form-union/form-union.component.ts b/frontend/projects/ui/src/app/components/form/form-union/form-union.component.ts
index f440d5e06..1898d8212 100644
--- a/frontend/projects/ui/src/app/components/form/form-union/form-union.component.ts
+++ b/frontend/projects/ui/src/app/components/form/form-union/form-union.component.ts
@@ -11,7 +11,7 @@ import {
ValueSpecSelect,
ValueSpecUnion,
unionValueKey,
-} from 'start-sdk/lib/config/configTypes'
+} from '@start9labs/start-sdk/lib/config/configTypes'
import { FormService } from '../../../services/form.service'
import { tuiPure } from '@taiga-ui/cdk'
@@ -50,10 +50,14 @@ export class FormUnionComponent implements OnChanges {
this.formService.getFormGroup(
union ? this.spec.variants[union].spec : {},
),
+ {
+ emitEvent: false,
+ },
)
}
ngOnChanges() {
this.selectSpec = this.formService.getUnionSelectSpec(this.spec, this.union)
+ if (this.union) this.onUnion(this.union)
}
}
diff --git a/frontend/projects/ui/src/app/modals/app-config/app-config.page.ts b/frontend/projects/ui/src/app/modals/app-config/app-config.page.ts
index efc0db2c2..dbef7701f 100644
--- a/frontend/projects/ui/src/app/modals/app-config/app-config.page.ts
+++ b/frontend/projects/ui/src/app/modals/app-config/app-config.page.ts
@@ -11,7 +11,7 @@ import { TUI_PROMPT, TuiPromptData } from '@taiga-ui/kit'
import { POLYMORPHEUS_CONTEXT } from '@tinkoff/ng-polymorpheus'
import { ApiService } from 'src/app/services/api/embassy-api.service'
import { getErrorMessage, isEmptyObject } from '@start9labs/shared'
-import { InputSpec } from 'start-sdk/lib/config/configTypes'
+import { InputSpec } from '@start9labs/start-sdk/lib/config/configTypes'
import {
DataModel,
PackageDataEntry,
@@ -127,6 +127,7 @@ export class AppConfigPage {
private async uploadFiles(config: Record, loader: Subscription) {
loader.unsubscribe()
+ loader.closed = false
// TODO: Could be nested files
const keys = Object.keys(config).filter(key => config[key] instanceof File)
@@ -147,6 +148,7 @@ export class AppConfigPage {
loader: Subscription,
) {
loader.unsubscribe()
+ loader.closed = false
loader.add(this.loader.open('Checking dependent services...').subscribe())
const breakages = await this.embassyApi.drySetPackageConfig({
@@ -155,6 +157,7 @@ export class AppConfigPage {
})
loader.unsubscribe()
+ loader.closed = false
if (isEmptyObject(breakages) || (await this.approveBreakages(breakages))) {
await this.configure(config, loader)
@@ -163,6 +166,7 @@ export class AppConfigPage {
private async configure(config: Record, loader: Subscription) {
loader.unsubscribe()
+ loader.closed = false
loader.add(this.loader.open('Saving...').subscribe())
await this.embassyApi.setPackageConfig({ id: this.pkgId, config })
diff --git a/frontend/projects/ui/src/app/modals/form/form.page.ts b/frontend/projects/ui/src/app/modals/form/form.page.ts
index 17fdc9c15..fc4a69653 100644
--- a/frontend/projects/ui/src/app/modals/form/form.page.ts
+++ b/frontend/projects/ui/src/app/modals/form/form.page.ts
@@ -6,7 +6,7 @@ import {
OnInit,
} from '@angular/core'
import { FormService } from 'src/app/services/form.service'
-import { InputSpec } from 'start-sdk/lib/config/configTypes'
+import { InputSpec } from '@start9labs/start-sdk/lib/config/configTypes'
import { POLYMORPHEUS_CONTEXT } from '@tinkoff/ng-polymorpheus'
import { TuiDialogContext } from '@taiga-ui/core'
import { tuiMarkControlAsTouchedAndValidate } from '@taiga-ui/cdk'
diff --git a/frontend/projects/ui/src/app/modals/generic-form/generic-form.module.ts b/frontend/projects/ui/src/app/modals/generic-form/generic-form.module.ts
deleted file mode 100644
index 3ca863c3d..000000000
--- a/frontend/projects/ui/src/app/modals/generic-form/generic-form.module.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { NgModule } from '@angular/core'
-import { CommonModule } from '@angular/common'
-import { IonicModule } from '@ionic/angular'
-import { GenericFormPage } from './generic-form.page'
-import { FormsModule, ReactiveFormsModule } from '@angular/forms'
-import { FormObjectModule } from 'src/app/components/form-object/form-object.module'
-
-@NgModule({
- declarations: [GenericFormPage],
- imports: [
- CommonModule,
- IonicModule,
- FormsModule,
- ReactiveFormsModule,
- FormObjectModule,
- ],
- exports: [GenericFormPage],
-})
-export class GenericFormPageModule {}
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
deleted file mode 100644
index 3ff3e4aae..000000000
--- a/frontend/projects/ui/src/app/modals/generic-form/generic-form.page.html
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
- {{ title }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/projects/ui/src/app/modals/generic-form/generic-form.page.scss b/frontend/projects/ui/src/app/modals/generic-form/generic-form.page.scss
deleted file mode 100644
index 0353411b3..000000000
--- a/frontend/projects/ui/src/app/modals/generic-form/generic-form.page.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-button:disabled,
-button[disabled]{
- border: 1px solid #999999;
- background-color: #cccccc;
- color: #666666;
-}
-button {
- color: var(--ion-color-primary);
-}
\ No newline at end of file
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
deleted file mode 100644
index 7a4c4042c..000000000
--- a/frontend/projects/ui/src/app/modals/generic-form/generic-form.page.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { Component, Input } from '@angular/core'
-import { UntypedFormGroup } from '@angular/forms'
-import { ModalController } from '@ionic/angular'
-import {
- convertValuesRecursive,
- FormService,
-} from 'src/app/services/form.service'
-import { InputSpec } from 'start-sdk/lib/config/configTypes'
-import { ErrorToastService } from '@start9labs/shared'
-
-export interface ActionButton {
- text: string
- handler: (value: any) => Promise
- isSubmit?: boolean
-}
-
-@Component({
- selector: 'generic-form',
- templateUrl: './generic-form.page.html',
- styleUrls: ['./generic-form.page.scss'],
-})
-export class GenericFormPage {
- @Input() title!: string
- @Input() spec!: InputSpec
- @Input() buttons!: ActionButton[]
- @Input() initialValue: Record = {}
-
- submitBtn!: ActionButton
- formGroup!: UntypedFormGroup
-
- constructor(
- private readonly modalCtrl: ModalController,
- private readonly formService: FormService,
- private readonly errToast: ErrorToastService,
- ) {}
-
- ngOnInit() {
- this.formGroup = this.formService.createForm(this.spec, this.initialValue)
- 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 {
- this.modalCtrl.dismiss()
- }
-
- async handleClick(handler: ActionButton['handler']): Promise {
- convertValuesRecursive(this.spec, this.formGroup)
-
- if (this.formGroup.invalid) {
- document
- .getElementsByClassName('validation-error')[0]
- ?.scrollIntoView({ behavior: 'smooth' })
- return
- }
-
- try {
- const response = await handler(this.formGroup.value)
- this.modalCtrl.dismiss({ response }, 'success')
- } catch (e: any) {
- this.errToast.present(e)
- }
- }
-}
-
-export interface GenericFormOptions {
- // required
- title: string
- spec: InputSpec
- buttons: ActionButton[]
- // optional
- initialValue?: Record
-}
diff --git a/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.module.ts b/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.module.ts
index 096e06add..8c1398515 100644
--- a/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.module.ts
+++ b/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.module.ts
@@ -4,6 +4,11 @@ import { IonicModule } from '@ionic/angular'
import { MarketplaceSettingsPage } from './marketplace-settings.page'
import { SharedPipesModule } from '@start9labs/shared'
import { StoreIconComponentModule } from 'src/app/components/store-icon/store-icon.component.module'
+import {
+ TuiDataListModule,
+ TuiHostedDropdownModule,
+ TuiSvgModule,
+} from '@taiga-ui/core'
@NgModule({
imports: [
@@ -11,6 +16,9 @@ import { StoreIconComponentModule } from 'src/app/components/store-icon/store-ic
IonicModule,
SharedPipesModule,
StoreIconComponentModule,
+ TuiHostedDropdownModule,
+ TuiDataListModule,
+ TuiSvgModule,
],
declarations: [MarketplaceSettingsPage],
})
diff --git a/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.html b/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.html
index 663b022cd..8d0da271a 100644
--- a/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.html
+++ b/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.html
@@ -1,22 +1,11 @@
-
-
- Change Registry
-
-
-
-
-
-
-
-
-
+
Default Registries
@@ -44,24 +33,45 @@
-
-
-
- {{ a.name }}
- {{ a.url }}
-
-
-
+
+
+
+
+
+ {{ a.name }}
+ {{ a.url }}
+
+
+
+
+
+
+
+
+
+
-
+
diff --git a/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.scss b/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.scss
index e69de29bb..c0655db59 100644
--- a/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.scss
+++ b/frontend/projects/ui/src/app/modals/marketplace-settings/marketplace-settings.page.scss
@@ -0,0 +1,16 @@
+ion-item {
+ --background: transparent;
+}
+
+.host {
+ display: flex;
+}
+
+.delete {
+ background: var(--tui-error-bg);
+ color: var(--tui-error-fill);
+
+ &:focus {
+ background: var(--tui-error-bg-hover);
+ }
+}
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 997f50b84..7e0778d2e 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
@@ -1,29 +1,18 @@
-import {
- ChangeDetectionStrategy,
- Component,
- Inject,
- ViewChild,
-} from '@angular/core'
-import {
- ActionSheetController,
- AlertController,
- LoadingController,
- ModalController,
-} from '@ionic/angular'
-import { ActionSheetButton } from '@ionic/core'
-import { ErrorToastService, sameUrl, toUrl } from '@start9labs/shared'
+import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'
+import { ErrorService, sameUrl, toUrl } from '@start9labs/shared'
import { AbstractMarketplaceService } from '@start9labs/marketplace'
+import { TuiDialogService } from '@taiga-ui/core'
import { ApiService } from 'src/app/services/api/embassy-api.service'
-import { ValueSpecObject } from 'start-sdk/lib/config/configTypes'
-import {
- GenericFormPage,
- GenericFormOptions,
-} from 'src/app/modals/generic-form/generic-form.page'
+import { ValueSpecObject } from '@start9labs/start-sdk/lib/config/configTypes'
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'
import { map } from 'rxjs/operators'
-import { combineLatest, firstValueFrom } from 'rxjs'
+import { combineLatest, filter, firstValueFrom, Subscription } from 'rxjs'
+import { FormDialogService } from '../../services/form-dialog.service'
+import { FormPage } from '../form/form.page'
+import { LoadingService } from '../loading/loading.service'
+import { TUI_PROMPT } from '@taiga-ui/kit'
@Component({
selector: 'marketplace-settings',
@@ -52,140 +41,87 @@ export class MarketplaceSettingsPage {
constructor(
private readonly api: ApiService,
- private readonly loadingCtrl: LoadingController,
- private readonly modalCtrl: ModalController,
- private readonly errToast: ErrorToastService,
- private readonly actionCtrl: ActionSheetController,
+ private readonly loader: LoadingService,
+ private readonly formDialog: FormDialogService,
+ private readonly errorService: ErrorService,
@Inject(AbstractMarketplaceService)
private readonly marketplaceService: MarketplaceService,
private readonly patch: PatchDB,
- private readonly alertCtrl: AlertController,
+ private readonly dialogs: TuiDialogService,
) {}
- async dismiss() {
- this.modalCtrl.dismiss()
- }
-
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: options,
- cssClass: 'alertlike-modal',
+ this.formDialog.open(FormPage, {
+ label: name,
+ data: {
+ 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,
+ },
+ ],
+ },
})
-
- await modal.present()
}
- async presentAction(
- { url, name }: { url: string; name?: string },
- canDelete = false,
- ) {
- const buttons: ActionSheetButton[] = [
- {
- text: 'Connect',
- handler: () => {
- this.connect(url)
- },
- },
- ]
-
- if (canDelete) {
- buttons.unshift({
- text: 'Delete',
- role: 'destructive',
- handler: () => {
- this.presentAlertDelete(url, name!)
+ async presentAlertDelete(url: string, name: string = '') {
+ this.dialogs
+ .open(TUI_PROMPT, {
+ label: 'Confirm',
+ size: 's',
+ data: {
+ content: `Are you sure you want to delete ${name}?`,
+ yes: 'Delete',
+ no: 'Cancel',
},
})
- }
-
- const action = await this.actionCtrl.create({
- header: name,
- mode: 'ios',
- buttons,
- })
-
- await action.present()
+ .pipe(filter(Boolean))
+ .subscribe(() => this.delete(url))
}
- private async presentAlertDelete(url: string, name: string) {
- const alert = await this.alertCtrl.create({
- header: 'Confirm',
- message: `Are you sure you want to delete ${name}?`,
- buttons: [
- {
- text: 'Cancel',
- role: 'cancel',
- },
- {
- text: 'Delete',
- handler: () => this.delete(url),
- cssClass: 'enter-click',
- },
- ],
- })
-
- await alert.present()
- }
-
- private async connect(
+ async connect(
url: string,
- loader?: HTMLIonLoadingElement,
+ loader: Subscription = new Subscription(),
): Promise {
- const message = 'Changing Registry...'
- if (!loader) {
- loader = await this.loadingCtrl.create({ message })
- await loader.present()
- } else {
- loader.message = message
- }
+ loader.unsubscribe()
+ loader.closed = false
+ loader.add(this.loader.open('Changing Registry...').subscribe())
try {
await this.api.setDbValue(['marketplace', 'selected-url'], url)
} catch (e: any) {
- this.errToast.present(e)
+ this.errorService.handleError(e)
} finally {
- loader.dismiss()
- this.dismiss()
+ loader.unsubscribe()
}
}
private async saveOnly(rawUrl: string): Promise {
- const loader = await this.loadingCtrl.create()
+ const loader = this.loader.open('Loading').subscribe()
try {
const url = new URL(rawUrl).toString()
await this.validateAndSave(url, loader)
return true
} catch (e: any) {
- this.errToast.present(e)
+ this.errorService.handleError(e)
return false
} finally {
- loader.dismiss()
+ loader.unsubscribe()
}
}
private async saveAndConnect(rawUrl: string): Promise {
- const loader = await this.loadingCtrl.create()
+ const loader = this.loader.open('Loading').subscribe()
try {
const url = new URL(rawUrl).toString()
@@ -193,17 +129,16 @@ export class MarketplaceSettingsPage {
await this.connect(url, loader)
return true
} catch (e: any) {
- this.errToast.present(e)
+ this.errorService.handleError(e)
return false
} finally {
- loader.dismiss()
- this.dismiss()
+ loader.unsubscribe()
}
}
private async validateAndSave(
url: string,
- loader: HTMLIonLoadingElement,
+ loader: Subscription,
): Promise {
// Error on duplicates
const hosts = await firstValueFrom(
@@ -213,15 +148,18 @@ export class MarketplaceSettingsPage {
if (currentUrls.includes(url)) throw new Error('marketplace already added')
// Validate
- loader.message = 'Validating marketplace...'
- await loader.present()
+ loader.unsubscribe()
+ loader.closed = false
+ loader.add(this.loader.open('Validating marketplace...').subscribe())
const { name } = await firstValueFrom(
this.marketplaceService.fetchInfo$(url),
)
// Save
- loader.message = 'Saving...'
+ loader.unsubscribe()
+ loader.closed = false
+ loader.add(this.loader.open('Saving...').subscribe())
await this.api.setDbValue<{ name: string }>(
['marketplace', 'known-hosts', url],
@@ -230,10 +168,7 @@ export class MarketplaceSettingsPage {
}
private async delete(url: string): Promise {
- const loader = await this.loadingCtrl.create({
- message: 'Deleting...',
- })
- await loader.present()
+ const loader = this.loader.open('Deleting...').subscribe()
const hosts = await firstValueFrom(
this.patch.watch$('ui', 'marketplace', 'known-hosts'),
@@ -255,9 +190,9 @@ export class MarketplaceSettingsPage {
filtered,
)
} catch (e: any) {
- this.errToast.present(e)
+ this.errorService.handleError(e)
} finally {
- loader.dismiss()
+ loader.unsubscribe()
}
}
}
@@ -287,6 +222,9 @@ function getMarketplaceValueSpec(): ValueSpecObject {
placeholder: 'e.g. https://example.org',
default: null,
warning: null,
+ disabled: false,
+ immutable: false,
+ generate: null,
},
},
}
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 0095217d5..ba550d868 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
@@ -7,12 +7,7 @@ import {
} from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { ApiService } from 'src/app/services/api/embassy-api.service'
-import {
- AlertController,
- LoadingController,
- ModalController,
- NavController,
-} from '@ionic/angular'
+import { AlertController, ModalController, NavController } from '@ionic/angular'
import { PatchDB } from 'patch-db-client'
import {
Action,
@@ -20,19 +15,18 @@ import {
PackageDataEntry,
PackageState,
} from 'src/app/services/patch-db/data-model'
-import {
- GenericFormPage,
- GenericFormOptions,
-} from 'src/app/modals/generic-form/generic-form.page'
import {
isEmptyObject,
- ErrorToastService,
getPkgId,
WithId,
+ ErrorService,
} from '@start9labs/shared'
import { ActionSuccessPage } from 'src/app/modals/action-success/action-success.page'
import { hasCurrentDeps } from 'src/app/util/has-deps'
import { filter } from 'rxjs'
+import { FormDialogService } from 'src/app/services/form-dialog.service'
+import { FormPage } from 'src/app/modals/form/form.page'
+import { LoadingService } from 'src/app/modals/loading/loading.service'
@Component({
selector: 'app-actions',
@@ -51,10 +45,11 @@ export class AppActionsPage {
private readonly embassyApi: ApiService,
private readonly modalCtrl: ModalController,
private readonly alertCtrl: AlertController,
- private readonly errToast: ErrorToastService,
- private readonly loadingCtrl: LoadingController,
+ private readonly errorService: ErrorService,
+ private readonly loader: LoadingService,
private readonly navCtrl: NavController,
private readonly patch: PatchDB,
+ private readonly formDialog: FormDialogService,
) {}
async handleAction(action: WithId) {
@@ -68,23 +63,19 @@ export class AppActionsPage {
await alert.present()
} else {
if (action['input-spec'] && !isEmptyObject(action['input-spec'])) {
- const options: GenericFormOptions = {
- title: action.name,
- spec: action['input-spec'],
- buttons: [
- {
- text: 'Execute',
- handler: async (value: any) =>
- this.executeAction(action.id, value),
- isSubmit: true,
- },
- ],
- }
- const modal = await this.modalCtrl.create({
- component: GenericFormPage,
- componentProps: options,
+ this.formDialog.open(FormPage, {
+ label: action.name,
+ data: {
+ spec: action['input-spec'],
+ buttons: [
+ {
+ text: 'Execute',
+ handler: async (value: any) =>
+ this.executeAction(action.id, value),
+ },
+ ],
+ },
})
- await modal.present()
} else {
const alert = await this.alertCtrl.create({
header: 'Confirm',
@@ -142,10 +133,7 @@ export class AppActionsPage {
}
private async uninstall() {
- const loader = await this.loadingCtrl.create({
- message: `Beginning uninstall...`,
- })
- await loader.present()
+ const loader = this.loader.open(`Beginning uninstall...`).subscribe()
try {
await this.embassyApi.uninstallPackage({ id: this.pkgId })
@@ -154,9 +142,9 @@ export class AppActionsPage {
.catch(e => console.error('Failed to mark instructions as unseen', e))
this.navCtrl.navigateRoot('/services')
} catch (e: any) {
- this.errToast.present(e)
+ this.errorService.handleError(e)
} finally {
- loader.dismiss()
+ loader.unsubscribe()
}
}
@@ -164,10 +152,7 @@ export class AppActionsPage {
actionId: string,
input?: object,
): Promise {
- const loader = await this.loadingCtrl.create({
- message: 'Executing action...',
- })
- await loader.present()
+ const loader = this.loader.open('Executing action...').subscribe()
try {
const res = await this.embassyApi.executePackageAction({
@@ -186,10 +171,10 @@ export class AppActionsPage {
setTimeout(() => successModal.present(), 500)
return true
} catch (e: any) {
- this.errToast.present(e)
+ this.errorService.handleError(e)
return false
} finally {
- loader.dismiss()
+ loader.unsubscribe()
}
}
diff --git a/frontend/projects/ui/src/app/pages/backups-routes/pages/backup-targets/backup-targets.page.ts b/frontend/projects/ui/src/app/pages/backups-routes/pages/backup-targets/backup-targets.page.ts
index 3b0301ede..01df98a8b 100644
--- a/frontend/projects/ui/src/app/pages/backups-routes/pages/backup-targets/backup-targets.page.ts
+++ b/frontend/projects/ui/src/app/pages/backups-routes/pages/backup-targets/backup-targets.page.ts
@@ -2,26 +2,44 @@ import { Component } from '@angular/core'
import {
BackupTarget,
BackupTargetType,
- DiskBackupTarget,
RR,
UnknownDisk,
} from 'src/app/services/api/api.types'
-import {
- AlertController,
- LoadingController,
- ModalController,
-} from '@ionic/angular'
-import { GenericFormPage } from 'src/app/modals/generic-form/generic-form.page'
import { ApiService } from 'src/app/services/api/embassy-api.service'
-import { ErrorToastService } from '@start9labs/shared'
import {
- CifsSpec,
- DropboxSpec,
- GoogleDriveSpec,
- DiskBackupTargetSpec,
- RemoteBackupTargetSpec,
+ cifsSpec,
+ diskBackupTargetSpec,
+ dropboxSpec,
+ googleDriveSpec,
+ remoteBackupTargetSpec,
} from '../../types/target-types'
-import { BehaviorSubject } from 'rxjs'
+import { BehaviorSubject, filter } from 'rxjs'
+import { TuiDialogService } from '@taiga-ui/core'
+import { ErrorService } from '@start9labs/shared'
+import { FormDialogService } from '../../../../services/form-dialog.service'
+import { FormPage } from '../../../../modals/form/form.page'
+import { LoadingService } from '../../../../modals/loading/loading.service'
+import { TUI_PROMPT } from '@taiga-ui/kit'
+import { configBuilderToSpec } from 'src/app/util/configBuilderToSpec'
+import {
+ InputSpec,
+ unionSelectKey,
+ unionValueKey,
+} from '@start9labs/start-sdk/lib/config/configTypes'
+
+type BackupConfig =
+ | {
+ type: {
+ [unionSelectKey]: 'dropbox' | 'google-drive'
+ [unionValueKey]: RR.AddCloudBackupTargetReq
+ }
+ }
+ | {
+ type: {
+ [unionSelectKey]: 'cifs'
+ [unionValueKey]: RR.AddCifsBackupTargetReq
+ }
+ }
export type BackupType = 'create' | 'restore'
@@ -41,27 +59,23 @@ export class BackupTargetsPage {
loading$ = new BehaviorSubject(true)
constructor(
- private readonly modalCtrl: ModalController,
- private readonly alertCtrl: AlertController,
- private readonly loadingCtrl: LoadingController,
- private readonly errToast: ErrorToastService,
+ private readonly dialogs: TuiDialogService,
+ private readonly loader: LoadingService,
+ private readonly errorService: ErrorService,
private readonly api: ApiService,
+ private readonly formDialog: FormDialogService,
) {}
ngOnInit() {
this.getTargets()
}
- async presentModalAddPhysical(
- disk: UnknownDisk,
- index: number,
- ): Promise {
- const modal = await this.modalCtrl.create({
- component: GenericFormPage,
- componentProps: {
- title: 'New Physical Target',
- spec: DiskBackupTargetSpec,
- initialValue: {
+ async presentModalAddPhysical(disk: UnknownDisk, index: number) {
+ this.formDialog.open(FormPage, {
+ label: 'New Physical Target',
+ data: {
+ spec: await configBuilderToSpec(diskBackupTargetSpec),
+ value: {
name: disk.label || disk.logicalname,
},
buttons: [
@@ -74,60 +88,56 @@ export class BackupTargetsPage {
}).then(disk => {
this.targets['unknown-disks'].splice(index, 1)
this.targets.saved.push(disk)
+
+ return true
}),
- isSubmit: true,
},
],
},
})
-
- await modal.present()
}
- async presentModalAddRemote(): Promise {
- const modal = await this.modalCtrl.create({
- component: GenericFormPage,
- componentProps: {
- title: 'New Remote Target',
- spec: RemoteBackupTargetSpec,
+ async presentModalAddRemote() {
+ this.formDialog.open(FormPage, {
+ label: 'New Remote Target',
+ data: {
+ spec: await configBuilderToSpec(remoteBackupTargetSpec),
buttons: [
{
text: 'Save',
- handler: (
- value:
- | (RR.AddCifsBackupTargetReq & { type: BackupTargetType })
- | (RR.AddCloudBackupTargetReq & { type: BackupTargetType }),
- ) => this.add(value.type, value),
- isSubmit: true,
+ handler: ({ type }: BackupConfig) =>
+ this.add(
+ type[unionSelectKey] === 'cifs' ? 'cifs' : 'cloud',
+ type[unionValueKey],
+ ),
},
],
},
})
-
- await modal.present()
}
- async presentModalUpdate(target: BackupTarget): Promise {
- let spec: typeof RemoteBackupTargetSpec = {}
+ async presentModalUpdate(target: BackupTarget) {
+ let spec: InputSpec
switch (target.type) {
case 'cifs':
- spec = CifsSpec
+ spec = await configBuilderToSpec(cifsSpec)
break
case 'cloud':
- spec = target.provider === 'dropbox' ? DropboxSpec : GoogleDriveSpec
+ spec = await configBuilderToSpec(
+ target.provider === 'dropbox' ? dropboxSpec : googleDriveSpec,
+ )
break
case 'disk':
- spec = DiskBackupTargetSpec
+ spec = await configBuilderToSpec(diskBackupTargetSpec)
break
}
- const modal = await this.modalCtrl.create({
- component: GenericFormPage,
- componentProps: {
- title: 'Update Remote Target',
+ this.formDialog.open(FormPage, {
+ label: 'Update Target',
+ data: {
spec,
- initialValue: target,
+ value: target,
buttons: [
{
text: 'Save',
@@ -136,49 +146,38 @@ export class BackupTargetsPage {
| RR.UpdateCifsBackupTargetReq
| RR.UpdateCloudBackupTargetReq
| RR.UpdateDiskBackupTargetReq,
- ) => this.update(target.type, value),
- isSubmit: true,
+ ) => this.update(target.type, { ...value, id: target.id }),
},
],
},
})
- await modal.present()
}
- async presentAlertDelete(id: string, index: number) {
- const alert = await this.alertCtrl.create({
- header: 'Confirm',
- message: 'Forget backup target? This actions cannot be undone.',
- buttons: [
- {
- text: 'Cancel',
- role: 'cancel',
+ presentAlertDelete(id: string, index: number) {
+ this.dialogs
+ .open(TUI_PROMPT, {
+ label: 'Confirm',
+ size: 's',
+ data: {
+ content: 'Forget backup target? This actions cannot be undone.',
+ no: 'Cancel',
+ yes: 'Delete',
},
- {
- text: 'Delete',
- handler: () => {
- this.delete(id, index)
- },
- cssClass: 'enter-click',
- },
- ],
- })
- await alert.present()
+ })
+ .pipe(filter(Boolean))
+ .subscribe(() => this.delete(id, index))
}
async delete(id: string, index: number): Promise {
- const loader = await this.loadingCtrl.create({
- message: 'Removing...',
- })
- await loader.present()
+ const loader = this.loader.open('Removing...').subscribe()
try {
await this.api.removeBackupTarget({ id })
this.targets.saved.splice(index, 1)
} catch (e: any) {
- this.errToast.present(e)
+ this.errorService.handleError(e)
} finally {
- loader.dismiss()
+ loader.unsubscribe()
}
}
@@ -202,7 +201,7 @@ export class BackupTargetsPage {
try {
this.targets = await this.api.getBackupTargets({})
} catch (e: any) {
- this.errToast.present(e)
+ this.errorService.handleError(e)
} finally {
this.loading$.next(false)
}
@@ -215,16 +214,12 @@ export class BackupTargetsPage {
| RR.AddCloudBackupTargetReq
| RR.AddDiskBackupTargetReq,
): Promise {
- const loader = await this.loadingCtrl.create({
- message: 'Saving target...',
- })
- await loader.present()
+ const loader = this.loader.open('Saving target...').subscribe()
try {
- const res = await this.api.addBackupTarget(type, value)
- return res
+ return await this.api.addBackupTarget(type, value)
} finally {
- loader.dismiss()
+ loader.unsubscribe()
}
}
@@ -235,16 +230,12 @@ export class BackupTargetsPage {
| RR.UpdateCloudBackupTargetReq
| RR.UpdateDiskBackupTargetReq,
): Promise {
- const loader = await this.loadingCtrl.create({
- message: 'Saving target...',
- })
- await loader.present()
+ const loader = this.loader.open('Saving target...').subscribe()
try {
- const res = await this.api.updateBackupTarget(type, value)
- return res
+ return await this.api.updateBackupTarget(type, value)
} finally {
- loader.dismiss()
+ loader.unsubscribe()
}
}
}
diff --git a/frontend/projects/ui/src/app/pages/backups-routes/types/target-types.ts b/frontend/projects/ui/src/app/pages/backups-routes/types/target-types.ts
index 704d0a080..4e38f7570 100644
--- a/frontend/projects/ui/src/app/pages/backups-routes/types/target-types.ts
+++ b/frontend/projects/ui/src/app/pages/backups-routes/types/target-types.ts
@@ -1,221 +1,121 @@
-import { InputSpec } from 'start-sdk/lib/config/configTypes'
+import { Config } from '@start9labs/start-sdk/lib/config/builder/config'
+import { Value } from '@start9labs/start-sdk/lib/config/builder/value'
+import { Variants } from '@start9labs/start-sdk/lib/config/builder/variants'
-export const DropboxSpec: InputSpec = {
- name: {
- type: 'text',
- inputmode: 'text',
- minLength: null,
- maxLength: null,
- patterns: [],
+export const dropboxSpec = Config.of({
+ name: Value.text({
name: 'Name',
description: 'A friendly name for this Dropbox target',
placeholder: 'My Dropbox',
- required: true,
- masked: false,
- warning: null,
- default: null,
- },
- token: {
- type: 'text',
- inputmode: 'text',
- minLength: null,
- maxLength: null,
- patterns: [],
+ required: { default: null },
+ }),
+ token: Value.text({
name: 'Access Token',
description: 'The secret access token for your custom Dropbox app',
- warning: null,
- placeholder: null,
- required: true,
+ required: { default: null },
masked: true,
- default: null,
- },
- path: {
- type: 'text',
- inputmode: 'text',
- minLength: null,
- maxLength: null,
- patterns: [],
+ }),
+ path: Value.text({
name: 'Path',
description: 'The fully qualified path to the backup directory',
- warning: null,
placeholder: 'e.g. /Desktop/my-folder',
- required: true,
- masked: false,
- default: null,
- },
-}
+ required: { default: null },
+ }),
+})
-export const GoogleDriveSpec: InputSpec = {
- name: {
- type: 'text',
- inputmode: 'text',
- minLength: null,
- maxLength: null,
- patterns: [],
+export const googleDriveSpec = Config.of({
+ name: Value.text({
name: 'Name',
description: 'A friendly name for this Google Drive target',
- warning: null,
placeholder: 'My Google Drive',
- required: true,
- masked: false,
- default: null,
- },
- key: {
- type: 'file',
+ required: { default: null },
+ }),
+ path: Value.text({
+ name: 'Path',
+ description: 'The fully qualified path to the backup directory',
+ placeholder: 'e.g. /Desktop/my-folder',
+ required: { default: null },
+ }),
+ key: Value.file({
name: 'Private Key File',
description:
'Your Google Drive service account private key file (.json file)',
- warning: null,
required: true,
extensions: ['json'],
- },
- path: {
- type: 'text',
- inputmode: 'text',
- minLength: null,
- maxLength: null,
- patterns: [],
- name: 'Path',
- description: 'The fully qualified path to the backup directory',
- placeholder: 'e.g. /Desktop/my-folder',
- required: true,
- masked: false,
- warning: null,
- default: null,
- },
-}
+ }),
+})
-export const CifsSpec: InputSpec = {
- name: {
- type: 'text',
- inputmode: 'text',
- minLength: null,
- maxLength: null,
- patterns: [],
+export const cifsSpec = Config.of({
+ name: Value.text({
name: 'Name',
description: 'A friendly name for this Network Folder',
- warning: null,
placeholder: 'My Network Folder',
- required: true,
- masked: false,
- default: null,
- },
- hostname: {
- type: 'text',
- inputmode: 'text',
- minLength: null,
- maxLength: null,
- patterns: [
- {
- regex: '^[a-zA-Z0-9._-]+( [a-zA-Z0-9]+)*$',
- description: `Must be a valid hostname. e.g. 'My Computer' OR 'my-computer.local'`,
- },
- ],
+ required: { default: null },
+ }),
+ hostname: Value.text({
name: 'Hostname',
description:
'The hostname of your target device on the Local Area Network.',
warning: null,
- required: true,
- masked: false,
placeholder: `e.g. 'My Computer' OR 'my-computer.local'`,
- default: null,
- },
- path: {
- type: 'text',
- inputmode: 'text',
- minLength: null,
- maxLength: null,
+ required: { default: null },
patterns: [],
+ }),
+ path: Value.text({
name: 'Path',
description: `On Windows, this is the fully qualified path to the shared folder, (e.g. /Desktop/my-folder).\n\n On Linux and Mac, this is the literal name of the shared folder (e.g. my-shared-folder).`,
placeholder: 'e.g. my-shared-folder or /Desktop/my-folder',
- required: true,
- masked: false,
- warning: null,
- default: null,
- },
- username: {
- type: 'text',
- inputmode: 'text',
- minLength: null,
- maxLength: null,
- patterns: [],
+ required: { default: null },
+ }),
+ username: Value.text({
name: 'Username',
description: `On Linux, this is the samba username you created when sharing the folder.\n\n On Mac and Windows, this is the username of the user who is sharing the folder.`,
- required: true,
- masked: false,
- warning: null,
+ required: { default: null },
placeholder: 'My Network Folder',
- default: null,
- },
- password: {
- type: 'text',
- inputmode: 'text',
- minLength: null,
- maxLength: null,
- patterns: [],
+ }),
+ password: Value.text({
name: 'Password',
description: `On Linux, this is the samba password you created when sharing the folder.\n\n On Mac and Windows, this is the password of the user who is sharing the folder.`,
required: false,
masked: true,
- warning: null,
placeholder: 'My Network Folder',
- default: null,
- },
-}
+ }),
+})
-export const RemoteBackupTargetSpec: InputSpec = {
- type: {
- type: 'union',
- name: 'Target Type',
- description: null,
- warning: null,
- required: true,
- variants: {
+export const remoteBackupTargetSpec = Config.of({
+ type: Value.union(
+ {
+ name: 'Target Type',
+ required: { default: 'dropbox' },
+ },
+ Variants.of({
dropbox: {
name: 'Dropbox',
- spec: DropboxSpec,
+ spec: dropboxSpec,
},
'google-drive': {
name: 'Google Drive',
- spec: GoogleDriveSpec,
+ spec: googleDriveSpec,
},
cifs: {
name: 'Network Folder',
- spec: CifsSpec,
+ spec: cifsSpec,
},
- },
- default: 'dropbox',
- },
-}
+ }),
+ ),
+})
-export const DiskBackupTargetSpec: InputSpec = {
- name: {
- type: 'text',
- inputmode: 'text',
- minLength: null,
- maxLength: null,
- patterns: [],
+export const diskBackupTargetSpec = Config.of({
+ name: Value.text({
name: 'Name',
description: 'A friendly name for this physical target',
placeholder: 'My Physical Target',
- required: true,
- masked: false,
- warning: null,
- default: null,
- },
- path: {
- type: 'text',
- inputmode: 'text',
- minLength: null,
- maxLength: null,
- patterns: [],
+ required: { default: null },
+ }),
+ path: Value.text({
name: 'Path',
description: 'The fully qualified path to the backup directory',
placeholder: 'e.g. /Backups/my-folder',
- required: true,
- masked: false,
- warning: null,
- default: null,
- },
-}
+ required: { default: null },
+ }),
+})
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/dev-config/dev-config.module.ts b/frontend/projects/ui/src/app/pages/developer-routes/dev-config/dev-config.module.ts
deleted file mode 100644
index 649ab7dfc..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/dev-config/dev-config.module.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { NgModule } from '@angular/core'
-import { CommonModule } from '@angular/common'
-import { IonicModule } from '@ionic/angular'
-import { RouterModule, Routes } from '@angular/router'
-import { DevConfigPage } from './dev-config.page'
-import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
-import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
-import { FormsModule } from '@angular/forms'
-import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
-
-const routes: Routes = [
- {
- path: '',
- component: DevConfigPage,
- },
-]
-
-@NgModule({
- imports: [
- CommonModule,
- IonicModule,
- RouterModule.forChild(routes),
- BadgeMenuComponentModule,
- BackupReportPageModule,
- FormsModule,
- MonacoEditorModule,
- ],
- declarations: [DevConfigPage],
-})
-export class DevConfigPageModule {}
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/dev-config/dev-config.page.html b/frontend/projects/ui/src/app/pages/developer-routes/dev-config/dev-config.page.html
deleted file mode 100644
index 688639533..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/dev-config/dev-config.page.html
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
- Config
-
-
- Preview
-
-
-
-
-
-
-
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/dev-config/dev-config.page.scss b/frontend/projects/ui/src/app/pages/developer-routes/dev-config/dev-config.page.scss
deleted file mode 100644
index e69de29bb..000000000
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
deleted file mode 100644
index 742214122..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/dev-config/dev-config.page.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import { Component } from '@angular/core'
-import { ActivatedRoute } from '@angular/router'
-import { ModalController } from '@ionic/angular'
-import { debounce, ErrorService } from '@start9labs/shared'
-import * as yaml from 'js-yaml'
-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 { DataModel } from 'src/app/services/patch-db/data-model'
-import { FormDialogService } from '../../../services/form-dialog.service'
-import { FormPage } from '../../../modals/form/form.page'
-
-@Component({
- selector: 'dev-config',
- templateUrl: 'dev-config.page.html',
- styleUrls: ['dev-config.page.scss'],
-})
-export class DevConfigPage {
- readonly projectId = getProjectId(this.route)
- editorOptions = { theme: 'vs-dark', language: 'yaml' }
- code: string = ''
- saving: boolean = false
-
- constructor(
- private readonly formDialog: FormDialogService,
- private readonly errorHandler: ErrorService,
- private readonly route: ActivatedRoute,
- private readonly modalCtrl: ModalController,
- private readonly patch: PatchDB,
- private readonly api: ApiService,
- ) {}
-
- ngOnInit() {
- this.patch
- .watch$('ui', 'dev', this.projectId, 'config')
- .pipe(filter(Boolean), take(1))
- .subscribe(config => {
- this.code = config
- })
- }
-
- async preview() {
- let doc: any
- try {
- doc = yaml.load(this.code)
- } catch (e: any) {
- this.errorHandler.handleError(e)
- }
-
- this.formDialog.open(FormPage, {
- label: 'Config Sample',
- data: {
- spec: JSON.parse(JSON.stringify(doc, null, 2)),
- buttons: [
- {
- text: 'OK',
- handler: async () => true,
- },
- ],
- },
- })
- }
-
- @debounce(1000)
- async save() {
- this.saving = true
- try {
- await this.api.setDbValue(
- ['dev', this.projectId, 'config'],
- this.code,
- )
- } catch (e: any) {
- this.errorHandler.handleError(e)
- } finally {
- this.saving = false
- }
- }
-}
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/dev-instructions/dev-instructions.module.ts b/frontend/projects/ui/src/app/pages/developer-routes/dev-instructions/dev-instructions.module.ts
deleted file mode 100644
index ce15130e5..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/dev-instructions/dev-instructions.module.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { NgModule } from '@angular/core'
-import { CommonModule } from '@angular/common'
-import { IonicModule } from '@ionic/angular'
-import { RouterModule, Routes } from '@angular/router'
-import { DevInstructionsPage } from './dev-instructions.page'
-import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
-import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
-import { FormsModule } from '@angular/forms'
-import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
-
-const routes: Routes = [
- {
- path: '',
- component: DevInstructionsPage,
- },
-]
-
-@NgModule({
- imports: [
- CommonModule,
- IonicModule,
- RouterModule.forChild(routes),
- BadgeMenuComponentModule,
- BackupReportPageModule,
- FormsModule,
- MonacoEditorModule,
- ],
- declarations: [DevInstructionsPage],
-})
-export class DevInstructionsPageModule {}
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/dev-instructions/dev-instructions.page.html b/frontend/projects/ui/src/app/pages/developer-routes/dev-instructions/dev-instructions.page.html
deleted file mode 100644
index 6d8bee796..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/dev-instructions/dev-instructions.page.html
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
- Instructions
-
-
- Preview
-
-
-
-
-
-
-
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/dev-instructions/dev-instructions.page.scss b/frontend/projects/ui/src/app/pages/developer-routes/dev-instructions/dev-instructions.page.scss
deleted file mode 100644
index e69de29bb..000000000
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/dev-instructions/dev-instructions.page.ts b/frontend/projects/ui/src/app/pages/developer-routes/dev-instructions/dev-instructions.page.ts
deleted file mode 100644
index bf3fa0b56..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/dev-instructions/dev-instructions.page.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import { Component } from '@angular/core'
-import { ActivatedRoute } from '@angular/router'
-import { ModalController } from '@ionic/angular'
-import { filter, take } from 'rxjs/operators'
-import { ApiService } from 'src/app/services/api/embassy-api.service'
-import {
- debounce,
- ErrorToastService,
- MarkdownComponent,
-} from '@start9labs/shared'
-import { PatchDB } from 'patch-db-client'
-import { getProjectId } from 'src/app/util/get-project-id'
-import { DataModel } from 'src/app/services/patch-db/data-model'
-
-@Component({
- selector: 'dev-instructions',
- templateUrl: 'dev-instructions.page.html',
- styleUrls: ['dev-instructions.page.scss'],
-})
-export class DevInstructionsPage {
- readonly projectId = getProjectId(this.route)
- editorOptions = { theme: 'vs-dark', language: 'markdown' }
- code = ''
- saving = false
-
- constructor(
- private readonly route: ActivatedRoute,
- private readonly errToast: ErrorToastService,
- private readonly modalCtrl: ModalController,
- private readonly patch: PatchDB,
- private readonly api: ApiService,
- ) {}
-
- ngOnInit() {
- this.patch
- .watch$('ui', 'dev', this.projectId, 'instructions')
- .pipe(filter(Boolean), take(1))
- .subscribe(config => {
- this.code = config
- })
- }
-
- async preview() {
- const modal = await this.modalCtrl.create({
- componentProps: {
- title: 'Instructions Sample',
- content: this.code,
- },
- component: MarkdownComponent,
- })
-
- await modal.present()
- }
-
- @debounce(1000)
- async save() {
- this.saving = true
- try {
- await this.api.setDbValue(
- ['dev', this.projectId, 'instructions'],
- this.code,
- )
- } catch (e: any) {
- this.errToast.present(e)
- } finally {
- this.saving = false
- }
- }
-}
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/dev-manifest/dev-manifest.module.ts b/frontend/projects/ui/src/app/pages/developer-routes/dev-manifest/dev-manifest.module.ts
deleted file mode 100644
index 49d738fe8..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/dev-manifest/dev-manifest.module.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { NgModule } from '@angular/core'
-import { CommonModule } from '@angular/common'
-import { IonicModule } from '@ionic/angular'
-import { RouterModule, Routes } from '@angular/router'
-import { DevManifestPage } from './dev-manifest.page'
-import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
-import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
-import { FormsModule } from '@angular/forms'
-import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
-
-const routes: Routes = [
- {
- path: '',
- component: DevManifestPage,
- },
-]
-
-@NgModule({
- imports: [
- CommonModule,
- IonicModule,
- RouterModule.forChild(routes),
- BadgeMenuComponentModule,
- BackupReportPageModule,
- FormsModule,
- MonacoEditorModule,
- ],
- declarations: [DevManifestPage],
-})
-export class DevManifestPageModule {}
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/dev-manifest/dev-manifest.page.html b/frontend/projects/ui/src/app/pages/developer-routes/dev-manifest/dev-manifest.page.html
deleted file mode 100644
index 599afca14..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/dev-manifest/dev-manifest.page.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
- Manifest
-
-
-
-
-
-
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/dev-manifest/dev-manifest.page.scss b/frontend/projects/ui/src/app/pages/developer-routes/dev-manifest/dev-manifest.page.scss
deleted file mode 100644
index e69de29bb..000000000
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/dev-manifest/dev-manifest.page.ts b/frontend/projects/ui/src/app/pages/developer-routes/dev-manifest/dev-manifest.page.ts
deleted file mode 100644
index f039abbd8..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/dev-manifest/dev-manifest.page.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { Component } from '@angular/core'
-import { ActivatedRoute } from '@angular/router'
-import * as yaml from 'js-yaml'
-import { take } from 'rxjs/operators'
-import { PatchDB } from 'patch-db-client'
-import { getProjectId } from 'src/app/util/get-project-id'
-import { DataModel } from 'src/app/services/patch-db/data-model'
-
-@Component({
- selector: 'dev-manifest',
- templateUrl: 'dev-manifest.page.html',
- styleUrls: ['dev-manifest.page.scss'],
-})
-export class DevManifestPage {
- readonly projectId = getProjectId(this.route)
- editorOptions = { theme: 'vs-dark', language: 'yaml', readOnly: true }
- manifest: string = ''
-
- constructor(
- private readonly route: ActivatedRoute,
- private readonly patch: PatchDB,
- ) {}
-
- ngOnInit() {
- this.patch
- .watch$('ui', 'dev', this.projectId)
- .pipe(take(1))
- .subscribe(devData => {
- this.manifest = yaml.dump(devData['basic-info'])
- })
- }
-}
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.module.ts b/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.module.ts
deleted file mode 100644
index ea0c78423..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.module.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { NgModule } from '@angular/core'
-import { CommonModule } from '@angular/common'
-import { IonicModule } from '@ionic/angular'
-import { RouterModule, Routes } from '@angular/router'
-import { DeveloperListPage } from './developer-list.page'
-import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
-import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
-
-const routes: Routes = [
- {
- path: '',
- component: DeveloperListPage,
- },
-]
-
-@NgModule({
- imports: [
- CommonModule,
- IonicModule,
- RouterModule.forChild(routes),
- BadgeMenuComponentModule,
- BackupReportPageModule,
- ],
- declarations: [DeveloperListPage],
-})
-export class DeveloperListPageModule {}
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.page.html b/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.page.html
deleted file mode 100644
index e00330448..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.page.html
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
- Developer Tools
-
-
-
-
-
-
-
- Projects
-
-
-
-
- Create project
-
-
-
-
- {{ entry.value.name }}
-
-
-
-
-
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.page.scss b/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.page.scss
deleted file mode 100644
index e69de29bb..000000000
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.page.ts b/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.page.ts
deleted file mode 100644
index f5c5bcbb6..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/developer-list/developer-list.page.ts
+++ /dev/null
@@ -1,279 +0,0 @@
-import { Component } from '@angular/core'
-import {
- ActionSheetButton,
- ActionSheetController,
- AlertController,
- LoadingController,
- ModalController,
-} from '@ionic/angular'
-import {
- GenericInputComponent,
- GenericInputOptions,
-} from 'src/app/modals/generic-input/generic-input.component'
-import { PatchDB } from 'patch-db-client'
-import { ApiService } from 'src/app/services/api/embassy-api.service'
-import { InputSpec } from 'start-sdk/lib/config/configTypes'
-import * as yaml from 'js-yaml'
-import { v4 } from 'uuid'
-import { DataModel, DevData } from 'src/app/services/patch-db/data-model'
-import { ErrorToastService } from '@start9labs/shared'
-import { TuiDestroyService } from '@taiga-ui/cdk'
-import { takeUntil } from 'rxjs/operators'
-
-@Component({
- selector: 'developer-list',
- templateUrl: 'developer-list.page.html',
- styleUrls: ['developer-list.page.scss'],
- providers: [TuiDestroyService],
-})
-export class DeveloperListPage {
- devData: DevData = {}
-
- constructor(
- private readonly modalCtrl: ModalController,
- private readonly api: ApiService,
- private readonly loadingCtrl: LoadingController,
- private readonly errToast: ErrorToastService,
- private readonly alertCtrl: AlertController,
- private readonly destroy$: TuiDestroyService,
- private readonly patch: PatchDB,
- private readonly actionCtrl: ActionSheetController,
- ) {}
-
- ngOnInit() {
- this.patch
- .watch$('ui', 'dev')
- .pipe(takeUntil(this.destroy$))
- .subscribe(dd => {
- this.devData = dd
- })
- }
-
- async openCreateProjectModal() {
- const projNumber = Object.keys(this.devData).length + 1
- const options: GenericInputOptions = {
- title: 'Add new project',
- message: 'Create a new dev project.',
- label: 'New project',
- useMask: false,
- placeholder: `Project ${projNumber}`,
- required: false,
- initialValue: `Project ${projNumber}`,
- buttonText: 'Save',
- submitFn: (value: string) => this.createProject(value),
- }
-
- const modal = await this.modalCtrl.create({
- componentProps: { options },
- cssClass: 'alertlike-modal',
- presentingElement: await this.modalCtrl.getTop(),
- component: GenericInputComponent,
- })
-
- await modal.present()
- }
-
- async presentAction(id: string, event: Event) {
- event.stopPropagation()
- event.preventDefault()
- const buttons: ActionSheetButton[] = [
- {
- text: 'Edit Name',
- icon: 'pencil',
- handler: () => {
- this.openEditNameModal(id)
- },
- },
- {
- text: 'Delete',
- icon: 'trash',
- role: 'destructive',
- handler: () => {
- this.presentAlertDelete(id)
- },
- },
- ]
-
- const action = await this.actionCtrl.create({
- header: this.devData[id].name,
- subHeader: 'Manage project',
- mode: 'ios',
- buttons,
- })
-
- await action.present()
- }
-
- async openEditNameModal(id: string) {
- const curName = this.devData[id].name
- const options: GenericInputOptions = {
- title: 'Edit Name',
- message: 'Edit the name of your project.',
- label: 'Name',
- useMask: false,
- placeholder: curName,
- required: false,
- initialValue: curName,
- buttonText: 'Save',
- submitFn: (value: string) => this.editName(id, value),
- }
-
- const modal = await this.modalCtrl.create({
- componentProps: { options },
- cssClass: 'alertlike-modal',
- presentingElement: await this.modalCtrl.getTop(),
- component: GenericInputComponent,
- })
-
- await modal.present()
- }
-
- async createProject(name: string) {
- // fail silently if duplicate project name
- if (
- Object.values(this.devData)
- .map(v => v.name)
- .includes(name)
- )
- return
-
- const loader = await this.loadingCtrl.create({
- message: 'Creating Project...',
- })
- await loader.present()
-
- try {
- const id = v4()
- const config = yaml
- .dump(SAMPLE_CONFIG)
- .replace(/warning:/g, '# Optional\n warning:')
-
- const def = { name, config, instructions: SAMPLE_INSTUCTIONS }
- await this.api.setDbValue<{
- name: string
- config: string
- instructions: string
- }>(['dev', id], def)
- } catch (e: any) {
- this.errToast.present(e)
- } finally {
- loader.dismiss()
- }
- }
-
- async presentAlertDelete(id: string) {
- const alert = await this.alertCtrl.create({
- header: 'Caution',
- message: `Are you sure you want to delete this project?`,
- buttons: [
- {
- text: 'Cancel',
- role: 'cancel',
- },
- {
- text: 'Delete',
- handler: () => {
- this.delete(id)
- },
- cssClass: 'enter-click',
- },
- ],
- })
- await alert.present()
- }
-
- async editName(id: string, newName: string) {
- const loader = await this.loadingCtrl.create({
- message: 'Saving...',
- })
- await loader.present()
-
- try {
- await this.api.setDbValue(['dev', id, 'name'], newName)
- } catch (e: any) {
- this.errToast.present(e)
- } finally {
- loader.dismiss()
- }
- }
-
- async delete(id: string) {
- const loader = await this.loadingCtrl.create({
- message: 'Removing Project...',
- })
- await loader.present()
-
- try {
- const devDataToSave: DevData = JSON.parse(JSON.stringify(this.devData))
- delete devDataToSave[id]
- await this.api.setDbValue(['dev'], devDataToSave)
- } catch (e: any) {
- this.errToast.present(e)
- } finally {
- loader.dismiss()
- }
- }
-}
-
-const SAMPLE_INSTUCTIONS = `# Create Instructions using Markdown! :)`
-
-const SAMPLE_CONFIG: InputSpec = {
- 'sample-string': {
- type: 'text',
- name: 'Example String Input',
- inputmode: 'text',
- required: true,
- masked: false,
- // optional
- description: 'Example description for required string input.',
- placeholder: 'Enter string value',
- patterns: [
- {
- regex: '^[a-zA-Z0-9! _]+$',
- description: 'Must be alphanumeric (may contain underscore).',
- },
- ],
- minLength: null,
- maxLength: null,
- default: null,
- warning: null,
- },
- 'sample-number': {
- type: 'number',
- name: 'Example Number Input',
- required: true,
- min: 5,
- max: 1000000,
- step: '5',
- integer: true,
- // optional
- warning: 'Example warning to display when changing this number value.',
- units: 'ms',
- description: 'Example description for optional number input.',
- placeholder: 'Enter number value',
- default: null,
- },
- 'sample-boolean': {
- type: 'toggle',
- name: 'Example Boolean Toggle',
- // optional
- description: 'Example description for boolean toggle',
- default: true,
- warning: null,
- },
- 'sample-select': {
- type: 'multiselect',
- name: 'Example Enum Select',
- values: {
- red: 'Red',
- blue: 'Blue',
- green: 'Green',
- },
- // optional
- warning: 'Example warning to display when changing this select value.',
- description: 'Example description for select select',
- minLength: null,
- maxLength: 2,
- default: ['red'],
- },
-}
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.module.ts b/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.module.ts
deleted file mode 100644
index 286ab8178..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.module.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { NgModule } from '@angular/core'
-import { CommonModule } from '@angular/common'
-import { IonicModule } from '@ionic/angular'
-import { RouterModule, Routes } from '@angular/router'
-import { DeveloperMenuPage } from './developer-menu.page'
-import { BadgeMenuComponentModule } from 'src/app/components/badge-menu-button/badge-menu.component.module'
-import { BackupReportPageModule } from 'src/app/modals/backup-report/backup-report.module'
-import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor'
-import { FormsModule } from '@angular/forms'
-import { SharedPipesModule } from '@start9labs/shared'
-
-const routes: Routes = [
- {
- path: '',
- component: DeveloperMenuPage,
- },
-]
-
-@NgModule({
- imports: [
- CommonModule,
- IonicModule,
- RouterModule.forChild(routes),
- BadgeMenuComponentModule,
- BackupReportPageModule,
- FormsModule,
- MonacoEditorModule,
- SharedPipesModule,
- ],
- declarations: [DeveloperMenuPage],
-})
-export class DeveloperMenuPageModule {}
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.page.html b/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.page.html
deleted file mode 100644
index ee10e7fc6..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.page.html
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
- {{ (projectData$ | async)?.name || '' }}
-
- View Manifest
-
-
-
-
-
-
-
-
- Basic Info
- Complete basic info for your package
-
-
-
-
-
-
-
- Instructions Generator
- Create instructions and see how they will appear to the end user
-
-
-
-
-
- Config Generator
- Edit the config with YAML and see it in real time
-
-
-
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.page.scss b/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.page.scss
deleted file mode 100644
index e69de29bb..000000000
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
deleted file mode 100644
index 7ebba9d90..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/developer-menu.page.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import { ChangeDetectionStrategy, Component } from '@angular/core'
-import { ActivatedRoute } from '@angular/router'
-import { BasicInfo, getBasicInfoSpec } from './form-info'
-import { PatchDB } from 'patch-db-client'
-import { ApiService } from 'src/app/services/api/embassy-api.service'
-import { ErrorService } from '@start9labs/shared'
-import { getProjectId } from 'src/app/util/get-project-id'
-import { DataModel, DevProjectData } from 'src/app/services/patch-db/data-model'
-import { FormDialogService } from '../../../services/form-dialog.service'
-import { FormPage } from '../../../modals/form/form.page'
-import { LoadingService } from '../../../modals/loading/loading.service'
-
-@Component({
- selector: 'developer-menu',
- templateUrl: 'developer-menu.page.html',
- styleUrls: ['developer-menu.page.scss'],
- changeDetection: ChangeDetectionStrategy.OnPush,
-})
-export class DeveloperMenuPage {
- readonly projectId = getProjectId(this.route)
- readonly projectData$ = this.patch.watch$('ui', 'dev', this.projectId)
-
- constructor(
- private readonly formDialog: FormDialogService,
- private readonly loader: LoadingService,
- private readonly errorHandler: ErrorService,
- private readonly route: ActivatedRoute,
- private readonly api: ApiService,
- private readonly patch: PatchDB,
- ) {}
-
- async openBasicInfoModal(data: DevProjectData) {
- this.formDialog.open(FormPage, {
- label: 'Basic Info',
- data: {
- spec: getBasicInfoSpec(data),
- buttons: [
- {
- text: 'Save',
- handler: async (basicInfo: BasicInfo) =>
- this.saveBasicInfo(basicInfo),
- },
- ],
- },
- })
- }
-
- async saveBasicInfo(basicInfo: BasicInfo): Promise {
- const loader = this.loader.open('Saving...').subscribe()
-
- try {
- await this.api.setDbValue(
- ['dev', this.projectId, 'basic-info'],
- basicInfo,
- )
- return true
- } catch (e: any) {
- this.errorHandler.handleError(e)
- return false
- } finally {
- loader.unsubscribe()
- }
- }
-}
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/form-info.ts b/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/form-info.ts
deleted file mode 100644
index 95bd29b1a..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/developer-menu/form-info.ts
+++ /dev/null
@@ -1,201 +0,0 @@
-import { InputSpec } from 'start-sdk/lib/config/configTypes'
-import { DevProjectData } from 'src/app/services/patch-db/data-model'
-
-export type BasicInfo = {
- id: string
- title: string
- 'service-version-number': string
- 'release-notes': string
- license: string
- 'wrapper-repo': string
- 'upstream-repo'?: string
- 'support-site'?: string
- 'marketing-site'?: string
- description: {
- short: string
- long: string
- }
-}
-
-export function getBasicInfoSpec(devData: DevProjectData): InputSpec {
- const basicInfo = devData['basic-info']
- return {
- id: {
- type: 'text',
- inputmode: 'text',
- name: 'ID',
- description: 'The package identifier used by the OS',
- placeholder: 'e.g. bitcoind',
- required: true,
- masked: false,
- minLength: null,
- maxLength: null,
- patterns: [
- {
- regex: '^([a-z][a-z0-9]*)(-[a-z0-9]+)*$',
- description: 'Must be kebab case',
- },
- ],
- default: basicInfo?.id || '',
- warning: null,
- },
- title: {
- type: 'text',
- inputmode: 'text',
- name: 'Service Name',
- description: 'A human readable service title',
- placeholder: 'e.g. Bitcoin Core',
- required: true,
- masked: false,
- minLength: null,
- maxLength: null,
- patterns: [],
- default: basicInfo ? basicInfo.title : devData.name,
- warning: null,
- },
- 'service-version-number': {
- type: 'text',
- inputmode: 'text',
- name: 'Service Version',
- description:
- 'Service version - accepts up to four digits, where the last confirms to revisions necessary for StartOS - see documentation: https://github.com/Start9Labs/emver-rs. This value will change with each release of the service',
- placeholder: 'e.g. 0.1.2.3',
- required: true,
- masked: false,
- minLength: null,
- maxLength: null,
- patterns: [
- {
- regex: '^([0-9]+).([0-9]+).([0-9]+).([0-9]+)$',
- description: 'Must be valid Emver version',
- },
- ],
- default: basicInfo?.['service-version-number'] || '',
- warning: null,
- },
- description: {
- type: 'object',
- name: 'Marketplace Descriptions',
- description: null,
- warning: null,
- spec: {
- short: {
- type: 'text',
- inputmode: 'text',
- name: 'Short Description',
- description:
- 'This is the first description visible to the user in the marketplace',
- placeholder: null,
- required: true,
- masked: false,
- default: basicInfo?.description?.short || '',
- minLength: null,
- maxLength: 320,
- patterns: [],
- warning: null,
- },
- long: {
- type: 'textarea',
- name: 'Long Description',
- description: `This description will display with additional details in the service's individual marketplace page`,
- placeholder: null,
- minLength: 20,
- maxLength: 1000,
- required: true,
- warning: null,
- },
- },
- },
- 'release-notes': {
- type: 'text',
- inputmode: 'text',
- name: 'Release Notes',
- description:
- 'Markdown supported release notes for this version of this service.',
- placeholder: 'e.g. Markdown _release notes_ for **Bitcoin Core**',
- required: true,
- minLength: null,
- maxLength: null,
- masked: false,
- patterns: [],
- default: basicInfo?.['release-notes'] || '',
- warning: null,
- },
- license: {
- type: 'select',
- name: 'License',
- warning: null,
- values: {
- 'gnu-agpl-v3': 'GNU AGPLv3',
- 'gnu-gpl-v3': 'GNU GPLv3',
- 'gnu-lgpl-v3': 'GNU LGPLv3',
- 'mozilla-public-license-2.0': 'Mozilla Public License 2.0',
- 'apache-license-2.0': 'Apache License 2.0',
- mit: 'mit',
- 'boost-software-license-1.0': 'Boost Software License 1.0',
- 'the-unlicense': 'The Unlicense',
- custom: 'Custom',
- },
- description: 'Example description for select',
- required: true,
- default: 'mit',
- },
- 'wrapper-repo': {
- type: 'text',
- inputmode: 'url',
- name: 'Wrapper Repo',
- description:
- 'The Start9 wrapper repository URL for the package. This repo contains the manifest file (this), any scripts necessary for configuration, backups, actions, or health checks',
- placeholder: 'e.g. www.github.com/example',
- patterns: [],
- minLength: null,
- maxLength: null,
- required: true,
- masked: false,
- default: basicInfo?.['wrapper-repo'] || '',
- warning: null,
- },
- 'upstream-repo': {
- type: 'text',
- inputmode: 'url',
- name: 'Upstream Repo',
- description: 'The original project repository URL',
- placeholder: 'e.g. www.github.com/example',
- patterns: [],
- minLength: null,
- maxLength: null,
- required: false,
- masked: false,
- default: basicInfo?.['upstream-repo'] || '',
- warning: null,
- },
- 'support-site': {
- type: 'text',
- inputmode: 'url',
- name: 'Support Site',
- description: 'URL to the support site / channel for the project',
- placeholder: 'e.g. start9.com/support',
- patterns: [],
- minLength: null,
- maxLength: null,
- required: false,
- masked: false,
- default: basicInfo?.['support-site'] || '',
- warning: null,
- },
- 'marketing-site': {
- type: 'text',
- inputmode: 'url',
- name: 'Website',
- description: 'URL to the marketing site / channel for the project',
- placeholder: 'e.g. start9.com',
- patterns: [],
- minLength: null,
- maxLength: null,
- required: false,
- masked: false,
- default: basicInfo?.['marketing-site'] || '',
- warning: null,
- },
- }
-}
diff --git a/frontend/projects/ui/src/app/pages/developer-routes/developer-routing.module.ts b/frontend/projects/ui/src/app/pages/developer-routes/developer-routing.module.ts
deleted file mode 100644
index 03720d87d..000000000
--- a/frontend/projects/ui/src/app/pages/developer-routes/developer-routing.module.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import { NgModule } from '@angular/core'
-import { Routes, RouterModule } from '@angular/router'
-
-const routes: Routes = [
- {
- path: '',
- redirectTo: 'projects',
- pathMatch: 'full',
- },
- {
- path: 'projects',
- loadChildren: () =>
- import('./developer-list/developer-list.module').then(
- m => m.DeveloperListPageModule,
- ),
- },
- {
- path: 'projects/:projectId',
- loadChildren: () =>
- import('./developer-menu/developer-menu.module').then(
- m => m.DeveloperMenuPageModule,
- ),
- },
- {
- path: 'projects/:projectId/config',
- loadChildren: () =>
- import('./dev-config/dev-config.module').then(m => m.DevConfigPageModule),
- },
- {
- path: 'projects/:projectId/instructions',
- loadChildren: () =>
- import('./dev-instructions/dev-instructions.module').then(
- m => m.DevInstructionsPageModule,
- ),
- },
- {
- path: 'projects/:projectId/manifest',
- loadChildren: () =>
- import('./dev-manifest/dev-manifest.module').then(
- m => m.DevManifestPageModule,
- ),
- },
-]
-
-@NgModule({
- imports: [RouterModule.forChild(routes)],
- exports: [RouterModule],
-})
-export class DeveloperRoutingModule {}
diff --git a/frontend/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts b/frontend/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts
index 089b66139..ab76282c0 100644
--- a/frontend/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts
+++ b/frontend/projects/ui/src/app/pages/marketplace-routes/marketplace-list/marketplace-list.page.ts
@@ -1,7 +1,8 @@
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
-import { ModalController } from '@ionic/angular'
import { AbstractMarketplaceService } from '@start9labs/marketplace'
+import { TuiDialogService } from '@taiga-ui/core'
+import { PolymorpheusComponent } from '@tinkoff/ng-polymorpheus'
import { PatchDB } from 'patch-db-client'
import { map } from 'rxjs'
import { MarketplaceSettingsPage } from 'src/app/modals/marketplace-settings/marketplace-settings.page'
@@ -73,7 +74,7 @@ export class MarketplaceListPage {
private readonly patch: PatchDB,
@Inject(AbstractMarketplaceService)
private readonly marketplaceService: MarketplaceService,
- private readonly modalCtrl: ModalController,
+ private readonly dialogs: TuiDialogService,
private readonly config: ConfigService,
private readonly route: ActivatedRoute,
) {}
@@ -81,11 +82,12 @@ export class MarketplaceListPage {
category = 'featured'
query = ''
- async presentModalMarketplaceSettings() {
- const modal = await this.modalCtrl.create({
- component: MarketplaceSettingsPage,
- })
- await modal.present()
+ presentModalMarketplaceSettings() {
+ this.dialogs
+ .open(new PolymorpheusComponent(MarketplaceSettingsPage), {
+ label: 'Change Registry',
+ })
+ .subscribe()
}
onCategoryChange(category: string): void {
diff --git a/frontend/projects/ui/src/app/pages/notifications/notifications.page.html b/frontend/projects/ui/src/app/pages/notifications/notifications.page.html
index f9e697107..a09112cd5 100644
--- a/frontend/projects/ui/src/app/pages/notifications/notifications.page.html
+++ b/frontend/projects/ui/src/app/pages/notifications/notifications.page.html
@@ -87,7 +87,6 @@