flatter layout

This commit is contained in:
Joel Wetzell
2026-03-06 18:37:32 -06:00
parent 76ee05c4c1
commit 79b84460b0
20 changed files with 396 additions and 281 deletions
+5 -1
View File
@@ -1,6 +1,10 @@
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
import { provideRouter } from '@angular/router';
import { appRoutes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [provideBrowserGlobalErrorListeners()],
providers: [
provideBrowserGlobalErrorListeners(),
provideRouter(appRoutes),
],
};
+4 -3
View File
@@ -18,13 +18,14 @@
</div>
}
</mat-toolbar>
<div class="flex-grow h-full overflow-hidden">
@if (configService.currentlyShownConfig() && schemaService.schemasLoaded()) {
<div class="flex-grow h-full overflow-y-auto">
<router-outlet></router-outlet>
<!-- @if (configService.currentlyShownConfig() && schemaService.schemasLoaded()) {
<app-config [(config)]="configService.currentlyShownConfig"></app-config>
} @else {
<div class="flex items-center justify-center mt-28">
<mat-spinner></mat-spinner>
</div>
}
} -->
</div>
</div>
+14
View File
@@ -0,0 +1,14 @@
import { Routes } from '@angular/router';
import { App } from './app';
export const appRoutes: Routes = [
{
path: '',
redirectTo: 'config',
pathMatch: 'full',
},
{
path: 'config',
loadComponent: () => import('./config/config.component').then((m) => m.ConfigComponent),
},
];
+3 -4
View File
@@ -13,28 +13,27 @@ import { SchemaService } from '../services/schema.service';
import { SettingsService } from '../services/settings.service';
import { ConfigComponent } from './config/config.component';
import * as yaml from 'js-yaml';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
imports: [
ConfigComponent,
MatToolbarModule,
MatProgressSpinnerModule,
MatIconModule,
TimeagoModule,
MatMenuModule,
MatButtonModule,
],
RouterOutlet
],
templateUrl: './app.html',
styleUrl: './app.css',
})
export class App {
public schemaService = inject(SchemaService);
public configService = inject(ConfigService);
private settingsService = inject(SettingsService);
private snackBar = inject(MatSnackBar);
private dialog = inject(MatDialog);
constructor() {
effect(() => {
if (this.schemaService.schemasLoaded()) {
+2 -2
View File
@@ -3,7 +3,7 @@
<div cdkDropList (cdkDropListDropped)="dropItem($event)" [cdkDropListData]="arrayValue">
@for (item of arrayValue; track trackByIndex(i, item); let i = $index) {
<div cdkDrag class="flex my-2 mr-2 items-center">
<label for="item-{{ i }}" class="text-gray-300 mr-1 hover:cursor-move" cdkDragHandle
<label for="item-{{ i }}" class="text-white mr-1 hover:cursor-move" cdkDragHandle
>{{ i }}:</label
>
<!-- @if (paramInfo?.schema.items.type === 'object') {
@@ -17,7 +17,7 @@
<!-- TODO(jwetzell): add input validation using info from schema -->
<input
id="item-{{ i }}"
class="pl-1 border-2 border-gray-200 border-solid rounded-sm text-gray-200"
class="pl-1 border-2 border-gray-200 border-solid rounded-sm text-white"
[(ngModel)]="arrayValue[i]"
[placeholder]="paramInfo?.placeholder"
[ngModelOptions]="{ standalone: true }"
+4 -78
View File
@@ -1,78 +1,4 @@
@if (config()) {
<div class="flex h-full flex-row p-2 gap-1.5">
<div class="flex flex-col min-w-1/6 border-2 rounded-lg border-gray-500">
<div class="flex items-center">
@if (schemaService.moduleTypes.length > 0) {
<div
matTooltip="Add Module"
class="flex items-center justify-center w-10 h-10 rounded-full bg-transparent hover:bg-gray-700 hover:cursor-pointer text-blue-400 border-blue-400 border-2 m-2"
[matMenuTriggerFor]="addModuleMenu"
>
<mat-icon>add</mat-icon>
</div>
<mat-menu #addModuleMenu="matMenu">
@for (moduleType of schemaService.moduleTypes; track moduleType) {
<button mat-menu-item (click)="addModule(moduleType.type)">
<span>{{ moduleType.name }}</span>
</button>
}
</mat-menu>
}
<div class="text-2xl text-white flex-grow text-center">Modules</div>
<div class="w-10 h-10 m-2"></div>
</div>
<div class="flex-grow h-full overflow-x-hidden overflow-y-auto gap-1.5">
@for (module of modules(); track module; let i = $index) {
<div>
<app-module
[path]="`modules/${i}`"
(moduleChange)="moduleUpdated(i, $event)"
[module]="module"
(delete)="deleteModule(i)"
></app-module>
</div>
}
</div>
</div>
<div class="flex flex-col flex-grow border-2 border-gray-500 rounded-lg">
<div class="flex items-center">
<div
matTooltip="Add Route"
class="flex items-center justify-center w-10 h-10 rounded-full bg-transparent hover:bg-gray-700 hover:cursor-pointer text-blue-400 border-blue-400 border-2 m-2"
(click)="addRoute()"
>
<mat-icon>add</mat-icon>
</div>
<div class="text-2xl text-white text-center flex-grow">Routes</div>
<div class="w-10 h-10 m-2"></div>
</div>
<div
class="flex-grow h-full overflow-x-hidden overflow-y-auto flex flex-row flex-wrap gap-1.5 content-baselinerounded-sm"
>
@for (route of routes(); track route; let i = $index) {
<div>
<app-route
[path]="`routes/${i}`"
(routeChange)="routeUpdated(i, $event)"
[route]="route"
[moduleIds]="moduleIds()"
(delete)="deleteRoute(i)"
></app-route>
</div>
}
</div>
</div>
</div>
}
<ng-template #addModuleMenu>
@if (schemaService.moduleTypes.length > 0) {
<div cdkMenu class="context-menu">
@for (moduleType of schemaService.moduleTypes; track moduleType) {
<button cdkMenuItem class="context-menu-item" (click)="addModule(moduleType.type)">
<span>{{ moduleType.name }}</span>
</button>
}
</div>
}
</ng-template>
@if (config() && schemaService.schemasLoaded()) {
<app-module-list [modules]="config()?.modules" (modulesChange)="modulesUpdated($event)"></app-module-list>
<app-route-list [routes]="config()?.routes" [moduleIds]="moduleIds()" (routesChange)="routesUpdated($event)"></app-route-list>
}
+18 -91
View File
@@ -5,25 +5,26 @@ import { MatMenuModule } from '@angular/material/menu';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Config, ModuleConfiguration, RouteConfiguration } from '../../models/config.models';
import { SchemaService } from '../../services/schema.service';
import { ModuleComponent } from '../module/module.component';
import { RouteComponent } from '../route/route.component';
import { MatTooltipModule } from '@angular/material/tooltip';
import { ModuleListComponent } from '../module-list/module-list.component';
import { RouteListComponent } from '../route-list.component/route-list.component';
import { ConfigService } from '../../services/config.service';
@Component({
selector: 'app-config',
imports: [
MatMenuModule,
MatIconModule,
ModuleComponent,
RouteComponent,
MatButtonModule,
MatTooltipModule,
ModuleListComponent,
RouteListComponent,
],
templateUrl: './config.component.html',
styleUrl: './config.component.css',
})
export class ConfigComponent {
config = model<Config>();
config = computed<Config | undefined>(() => this.configService.currentlyShownConfig());
modules = computed(() => this.config()?.modules ?? []);
routes = computed(() => this.config()?.routes ?? []);
@@ -36,109 +37,35 @@ export class ConfigComponent {
return [];
});
public configService = inject(ConfigService);
public schemaService = inject(SchemaService);
private snackBar = inject(MatSnackBar);
deleteModule(index: number) {
this.config.update((config) => {
if (config && config.modules) {
config?.modules?.splice(index, 1);
return {
...config,
modules: config.modules,
};
}
return config;
});
this.snackBar.open('Module Removed', 'Dismiss', {
duration: 3000,
});
}
deleteRoute(index: number) {
this.config.update((config) => {
if (config && config.routes) {
config?.routes?.splice(index, 1);
return {
...config,
routes: config.routes,
};
}
return config;
});
this.snackBar.open('Route Removed', 'Dismiss', {
duration: 3000,
});
}
moduleUpdated(index: number, module: ModuleConfiguration | undefined) {
if (module === undefined) {
console.error('module is undefined, not updating');
modulesUpdated(modules: ModuleConfiguration[] | undefined) {
if (modules === undefined) {
console.error('modules is undefined, not updating');
return;
}
this.config.update((config) => {
if (config && config.modules) {
config.modules[index].id = module.id;
config.modules[index].type = module.type;
if (module.params !== undefined) {
config.modules[index].params = module.params;
}
this.configService.currentlyShownConfig.update((config) => {
if (config) {
return {
...config,
modules: config.modules,
modules: modules,
};
}
return config;
});
}
routeUpdated(index: number, route: RouteConfiguration | undefined) {
if (route === undefined) {
console.error('route is undefined, not updating');
routesUpdated(routes: RouteConfiguration[] | undefined) {
if (routes === undefined) {
console.error('routes is undefined, not updating');
return;
}
this.config.update((config) => {
if (config && config.routes) {
config.routes[index].input = route.input;
config.routes[index].output = route.output;
return {
...config,
routes: config.routes,
};
}
return config;
});
}
addModule(moduleType: string) {
const moduleTemplate = this.schemaService.getSkeletonForModule(moduleType);
this.config.update((config) => {
this.configService.currentlyShownConfig.update((config) => {
if (config) {
if (!config.modules) {
config.modules = [];
}
config.modules?.push(moduleTemplate);
return {
...config,
modules: config.modules,
};
}
return config;
});
}
addRoute() {
const routeTemplate = this.schemaService.getSkeletonForRoute();
this.config.update((config) => {
if (config) {
if (!config.routes) {
config.routes = [];
}
config.routes?.push(routeTemplate);
return {
...config,
routes: config.routes,
routes: routes,
};
}
return config;
@@ -0,0 +1,45 @@
<div class="flex flex-col">
<div class="flex items-center">
@if (schemaService.moduleTypes.length > 0) {
<div
matTooltip="Add Module"
class="flex items-center justify-center w-10 h-10 bg-transparent hover:bg-gray-700 hover:cursor-pointer text-blue-400"
[matMenuTriggerFor]="addModuleMenu"
>
<mat-icon>add</mat-icon>
</div>
<mat-menu #addModuleMenu="matMenu">
@for (moduleType of schemaService.moduleTypes; track moduleType) {
<button mat-menu-item (click)="addModule(moduleType.type)">
<span>{{ moduleType.name }}</span>
</button>
}
</mat-menu>
}
<div class="text-white">Modules</div>
</div>
<div class="flex-grow overflow-x-hidden overflow-y-auto gap-1.5">
@for (module of modules(); track module; let i = $index) {
<div class="m-2">
<app-module
[path]="`modules/${i}`"
(moduleChange)="moduleUpdated(i, $event)"
[module]="module"
(delete)="deleteModule(i)"
></app-module>
</div>
}
</div>
</div>
<ng-template #addModuleMenu>
@if (schemaService.moduleTypes.length > 0) {
<div cdkMenu class="context-menu">
@for (moduleType of schemaService.moduleTypes; track moduleType) {
<button cdkMenuItem class="context-menu-item" (click)="addModule(moduleType.type)">
<span>{{ moduleType.name }}</span>
</button>
}
</div>
}
</ng-template>
@@ -0,0 +1,77 @@
import { Component, inject, model } from '@angular/core';
import { ModuleComponent } from '../module/module.component';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ModuleConfiguration } from '../../models/config.models';
import { SchemaService } from '../../services/schema.service';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { MatTooltipModule } from '@angular/material/tooltip';
import { RouteComponent } from '../route/route.component';
@Component({
selector: 'app-module-list',
imports: [
MatMenuModule,
MatIconModule,
MatButtonModule,
MatTooltipModule,
ModuleComponent
],
templateUrl: './module-list.component.html',
styleUrl: './module-list.component.css',
})
export class ModuleListComponent {
modules = model<ModuleConfiguration[]>();
public schemaService = inject(SchemaService);
private snackBar = inject(MatSnackBar);
moduleUpdated(index: number, module: ModuleConfiguration | undefined) {
if (module === undefined) {
console.error('module is undefined, not updating');
return;
}
this.modules.update((modules) => {
if (modules) {
modules[index].id = module.id;
modules[index].type = module.type;
if (module.params !== undefined) {
modules[index].params = module.params;
}
return [...modules];
}
return modules;
});
}
addModule(moduleType: string) {
const moduleTemplate = this.schemaService.getSkeletonForModule(moduleType);
this.modules.update((modules) => {
if (modules) {
if (!modules) {
modules = [];
}
modules?.push(moduleTemplate);
return [...modules];
}
return modules;
});
this.snackBar.open('Module Added', 'Dismiss', {
duration: 3000,
});
}
deleteModule(index: number) {
this.modules.update((modules) => {
if (modules) {
modules?.splice(index, 1);
return [...modules];
}
return [];
});
this.snackBar.open('Module Removed', 'Dismiss', {
duration: 3000,
});
}
}
+12 -9
View File
@@ -1,13 +1,12 @@
@if (module() && schema()) {
<div class="flex items-start m-2 w-fit">
<div
class="flex flex-col bg-gray-800 border-2 {{
isInError() ? 'border-red-500' : 'border-gray-200'
}} border-solid w-fit h-full"
isInError() ? 'border-red-500' : 'border-gray-600'
}} border-solid w-full h-full"
>
<div class="flex items-center justify-center bg-gray-600">
<div class="flex-grow ml-1 mr-3 text-gray-200">
{{ schema().title }}
<div class="flex items-center justify-center border-gray-600 border-b-2">
<div class="flex-grow ml-1 mr-3 text-white">
{{ schema().title || module()?.type }}
</div>
<div class="flex items-center justify-center" (click)="delete.emit()">
<mat-icon class="!text-red-500">close</mat-icon>
@@ -17,13 +16,18 @@
<form [formGroup]="formGroup" class="h-full">
<div class="m-2">
<div class="flex items-center m-1">
<label class="mr-2 text-gray-300"> id: </label>
<label class="mr-2 text-white"> id: </label>
<input
type="text"
class="pl-1 border-2 border-gray-200 text-gray-200 border-solid rounded-sm"
class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm"
[placeholder]="'module-1'"
[formControlName]="'id'"
/>
<mat-icon class="!text-white" matTooltip="String">abc</mat-icon>
@if (!formGroup.controls['id'].valid) {
<!-- TODO(jwetzell): better error displaying -->
<div class="ml-2 text-red-500">{{ formGroup.controls['id'].errors | json }}</div>
}
</div>
<app-params-form
[paramsSchema]="schema()?.properties?.params"
@@ -34,5 +38,4 @@
</form>
</div>
</div>
</div>
}
+6 -2
View File
@@ -1,11 +1,13 @@
import { Component, computed, inject, input, model, output } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { ModuleConfiguration } from '../../models/config.models';
import { SchemaService } from '../../services/schema.service';
import { ParamsFormComponent } from '../params-form/params-form.component';
import { JsonPipe } from '@angular/common';
import { MatTooltipModule } from '@angular/material/tooltip';
@Component({
selector: 'app-module',
@@ -15,6 +17,8 @@ import { ParamsFormComponent } from '../params-form/params-form.component';
ParamsFormComponent,
ReactiveFormsModule,
MatMenuModule,
MatTooltipModule,
JsonPipe,
],
templateUrl: './module.component.html',
styleUrl: './module.component.css',
@@ -34,7 +38,7 @@ export class ModuleComponent {
});
formGroup: FormGroup = new FormGroup({
id: new FormControl(''),
id: new FormControl('', [Validators.required]),
type: new FormControl(''),
});
+22 -19
View File
@@ -1,6 +1,7 @@
@if (paramsSchema && paramsFormInfo) {
@if (paramsOptions.length > 1) {
<mat-tab-group
dynamicHeight
[selectedIndex]="paramsOptionsSelectedIndex"
(selectedTabChange)="paramsOptionsTabSelected($event)"
>
@@ -14,7 +15,7 @@
@for (key of paramKeys(); track key) {
<div class="flex items-center m-1">
@if (getParamInfo(key); as paramInfo) {
<label class="mr-2 text-gray-300"> {{ paramInfo.display }}: </label>
<label class="mr-2 text-white"> {{ paramInfo.display }}: </label>
@switch (paramInfo.type) {
@case ('boolean') {
<input type="checkbox" [formControlName]="paramInfo.key" />
@@ -24,12 +25,12 @@
<input
type="number"
[placeholder]="paramInfo.placeholder"
class="pl-1 border-2 border-gray-200 text-gray-200 border-solid rounded-sm"
class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm"
[formControlName]="paramInfo.key"
/>
} @else {
<select
class="pl-1 border-2 border-gray-200 text-gray-200 border-solid rounded-sm"
class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm"
[formControlName]="paramInfo.key"
>
@for (option of paramInfo.options; track option) {
@@ -39,19 +40,19 @@
}
</select>
}
<mat-icon class="!text-gray-200">123</mat-icon>
<mat-icon class="!text-white">123</mat-icon>
}
@case ('integer') {
@if (!paramInfo.options) {
<input
type="number"
[placeholder]="paramInfo.placeholder"
class="pl-1 border-2 border-gray-200 text-gray-200 border-solid rounded-sm"
class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm"
[formControlName]="paramInfo.key"
/>
} @else {
<select
class="pl-1 border-2 border-gray-200 text-gray-200 border-solid rounded-sm"
class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm"
[formControlName]="paramInfo.key"
>
@for (option of paramInfo.options; track option) {
@@ -61,19 +62,19 @@
}
</select>
}
<mat-icon class="!text-gray-200" matTooltip="Number">123</mat-icon>
<mat-icon class="!text-white" matTooltip="Number">123</mat-icon>
}
@case ('string') {
@if (!paramInfo.options) {
<input
type="text"
class="pl-1 border-2 border-gray-200 text-gray-200 border-solid rounded-sm"
class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm"
[placeholder]="paramInfo.placeholder"
[formControlName]="paramInfo.key"
/>
} @else {
<select
class="pl-1 border-2 border-gray-200 text-gray-200 border-solid rounded-sm"
class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm"
[formControlName]="paramInfo.key"
>
@for (option of paramInfo.options; track option) {
@@ -83,42 +84,44 @@
}
</select>
}
<mat-icon class="!text-gray-200" matTooltip="String">abc</mat-icon>
<mat-icon class="!text-white" matTooltip="String">abc</mat-icon>
}
@case ('array') {
<app-array-form
[paramFormControl]="paramsFormInfo.formGroup.get(paramInfo.key)"
[paramInfo]="paramInfo"
></app-array-form>
<mat-icon class="!text-gray-200" matTooltip="Item list">data_array</mat-icon>
<mat-icon class="!text-white" matTooltip="Item list">data_array</mat-icon>
}
@case ('object') {
<!-- TODO(jwetzell): figure out how to display objects-->
<input
class="pl-1 border-2 border-gray-200 text-gray-200 border-solid rounded-sm"
class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm"
[formControlName]="paramInfo.key"
[placeholder]="paramInfo.placeholder"
/>
<mat-icon class="!text-gray-200" matTooltip="JSON">data_object</mat-icon>
<mat-icon class="!text-white" matTooltip="JSON">data_object</mat-icon>
}
@default {
<input
class="pl-1 border-2 border-gray-200 text-gray-200 border-solid rounded-sm"
class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm"
[formControlName]="paramInfo.key"
/>
}
}
@if (paramInfo.hint) {
<div class="flex items-center justify-center">
<mat-icon [matTooltip]="paramInfo.hint" class="ml-2 !text-white">
help_outline
</mat-icon>
</div>
}
@if (!paramsFormInfo.formGroup.controls[key].valid) {
<!-- TODO(jwetzell): better error displaying -->
<div class="ml-2 text-red-500">
{{ paramsFormInfo.formGroup.controls[key].errors | json }}
</div>
}
<div class="flex items-center justify-center">
<mat-icon [matTooltip]="paramInfo.hint" class="ml-2 !text-gray-200">
help_outline
</mat-icon>
</div>
}
</div>
}
+4 -4
View File
@@ -2,12 +2,12 @@
<div class="flex items-start m-2 w-fit">
<div
class="flex flex-col bg-gray-800 border-2 {{
isInError() ? 'border-red-500' : 'border-gray-200'
isInError() ? 'border-red-500' : 'border-gray-600'
}} border-solid w-fit h-full"
>
<div class="flex items-center justify-center bg-gray-600">
<div class="flex-grow ml-1 mr-3 text-gray-200">
{{ processor()?.type }}
<div class="flex items-center justify-center border-gray-600 border-b-2">
<div class="flex-grow ml-1 mr-3 text-white">
{{ schema()?.title || processor()?.type }}
</div>
<div class="flex items-center justify-center" (click)="delete.emit()">
<mat-icon class="!text-red-500">close</mat-icon>
@@ -0,0 +1,25 @@
<div class="flex flex-col">
<div class="flex items-center">
<div
matTooltip="Add Route"
class="flex items-center justify-center w-10 h-10 bg-transparent hover:bg-gray-700 hover:cursor-pointer text-blue-400"
(click)="addRoute()"
>
<mat-icon>add</mat-icon>
</div>
<div class="text-white">Routes</div>
</div>
<div class="flex-grow overflow-x-hidden overflow-y-auto gap-1.5">
@for (route of routes(); track route; let i = $index) {
<div class="m-2">
<app-route
[path]="`routes/${i}`"
(routeChange)="routeUpdated(i, $event)"
[route]="route"
[moduleIds]="moduleIds() || []"
(delete)="deleteRoute(i)"
></app-route>
</div>
}
</div>
</div>
@@ -0,0 +1,72 @@
import { Component, inject, input, model } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ModuleConfiguration, RouteConfiguration } from '../../models/config.models';
import { SchemaService } from '../../services/schema.service';
import { RouteComponent } from '../route/route.component';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { MatTooltipModule } from '@angular/material/tooltip';
@Component({
selector: 'app-route-list',
imports: [
RouteComponent,
MatMenuModule,
MatIconModule,
MatButtonModule,
MatTooltipModule,
],
templateUrl: './route-list.component.html',
styleUrl: './route-list.component.css',
})
export class RouteListComponent {
routes = model<RouteConfiguration[]>();
moduleIds = input<string[]>();
public schemaService = inject(SchemaService);
private snackBar = inject(MatSnackBar);
deleteRoute(index: number) {
this.routes.update((routes) => {
if (routes) {
routes?.splice(index, 1);
return [...routes];
}
return routes;
});
this.snackBar.open('Route Removed', 'Dismiss', {
duration: 3000,
});
}
routeUpdated(index: number, route: RouteConfiguration | undefined) {
if (route === undefined) {
console.error('route is undefined, not updating');
return;
}
this.routes.update((routes) => {
if (routes) {
routes[index].input = route.input;
routes[index].output = route.output;
return [...routes];
}
return routes;
});
}
addRoute() {
const routeTemplate = this.schemaService.getSkeletonForRoute();
this.routes.update((routes) => {
if (!routes) {
routes = [];
}
routes?.push(routeTemplate);
return [...routes];
});
this.snackBar.open('Route Added', 'Dismiss', {
duration: 3000,
});
}
}
+63 -64
View File
@@ -1,77 +1,76 @@
@if (route()) {
<div class="flex items-start m-2 w-fit">
<div
class="flex flex-col bg-gray-800 border-2 {{
isInError() ? 'border-red-500' : 'border-gray-200'
}} border-solid w-fit h-full mr-2"
>
<div class="flex items-center justify-end bg-gray-600">
<div class="flex items-center justify-center" (click)="delete.emit()">
<mat-icon class="!text-red-500">close</mat-icon>
</div>
<!-- <div class="flex items-start m-2 w-fit border-2 border-red-500"> -->
<div
class="flex flex-col bg-gray-800 border-2 {{
isInError() ? 'border-red-500' : 'border-gray-600'
}} border-solid w-full h-full"
>
<div class="flex items-center justify-end border-gray-600 border-b-2">
<div class="flex-grow text-left ml-2 text-white">Route {{ index() }}</div>
<div class="flex items-center justify-center hover:bg-gray-700" (click)="delete.emit()">
<mat-icon class="!text-red-500">close</mat-icon>
</div>
</div>
<div>
<form [formGroup]="formGroup">
<div class="m-2">
<div class="flex items-center m-1">
<label class="mr-2 text-white"> input: </label>
<select
class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm w-full"
formControlName="input"
>
@for (option of moduleIds(); track option) {
<option [value]="option">
{{ option }}
</option>
}
</select>
@if (!formGroup.controls['input'].valid) {
<!-- TODO(jwetzell): better error displaying -->
<div class="ml-2 text-red-500">{{ formGroup.controls['input'].errors | json }}</div>
}
</div>
</div>
</form>
<div>
<form [formGroup]="formGroup" class="h-full">
<div class="m-2">
<div class="flex items-center m-1">
<label class="mr-2 text-gray-300"> input: </label>
<select
class="pl-1 border-2 border-gray-200 text-gray-200 border-solid rounded-sm w-full"
formControlName="input"
>
@for (option of moduleIds(); track option) {
<option [value]="option">
{{ option }}
</option>
}
</select>
@if (!formGroup.controls['input'].valid) {
<!-- TODO(jwetzell): better error displaying -->
<div class="ml-2 text-red-500">{{ formGroup.controls['input'].errors | json }}</div>
}
<div class="flex items-center">
@if (schemaService.processorTypes.length > 0) {
<div
matTooltip="Add Processor"
class="flex items-center justify-center w-10 h-10 bg-transparent hover:bg-gray-700 hover:cursor-pointer text-blue-400"
[matMenuTriggerFor]="addProcessorMenu"
>
<mat-icon>add</mat-icon>
</div>
</div>
</form>
<div>
<div class="text-md text-gray-300 text-center">Processors</div>
<div class="border-2 border-gray-500 m-2 rounded-sm">
@if (processors() === undefined || processors()?.length === 0) {
<div class="w-full h-6"></div>
} @else {
@for (processor of processors(); track processor; let i = $index) {
<div cdkDrag>
<app-processor
[path]="path() + '/processors/' + i"
(processorChange)="processorUpdated(i, $event)"
[processor]="processor"
(delete)="deleteProcessor(i)"
></app-processor>
</div>
<mat-menu #addProcessorMenu="matMenu">
@for (processorType of schemaService.processorTypes; track processorType) {
<button mat-menu-item (click)="addProcessor(processorType.type)">
<span>{{ processorType.name }}</span>
</button>
}
}
</div>
</mat-menu>
}
<div class="text-md text-white text-center">Processors</div>
</div>
</div>
<div class="flex mb-2">
@if (schemaService.moduleTypes.length > 0) {
<div
matTooltip="Add Module"
class="flex items-center justify-center w-full h-10 rounded-lg bg-gray-200 hover:cursor-pointer hover:bg-gray-300 m-2"
[matMenuTriggerFor]="addProcessorMenu"
>
<mat-icon>add</mat-icon>
</div>
<mat-menu #addProcessorMenu="matMenu">
@for (processorType of schemaService.processorTypes; track processorType) {
<button mat-menu-item (click)="addProcessor(processorType.type)">
<span>{{ processorType.type }}</span>
</button>
@if (processors() !== undefined && processors()!.length > 0) {
<div class="border-2 border-gray-500 m-2">
@for (processor of processors(); track processor; let i = $index) {
<div cdkDrag>
<app-processor
[path]="path() + '/processors/' + i"
(processorChange)="processorUpdated(i, $event)"
[processor]="processor"
(delete)="deleteProcessor(i)"
></app-processor>
</div>
}
</mat-menu>
}
</div>
}
</div>
</div>
</div>
<!-- </div> -->
<ng-template #addProcessorMenu>
@if (schemaService.processorTypes.length > 0) {
+16
View File
@@ -8,6 +8,7 @@ import { SchemaService } from '../../services/schema.service';
import { JsonPipe } from '@angular/common';
import { ProcessorComponent } from '../processor/processor.component';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatTooltipModule } from '@angular/material/tooltip';
@Component({
selector: 'app-route',
@@ -18,12 +19,24 @@ import { MatSnackBar } from '@angular/material/snack-bar';
MatMenuModule,
JsonPipe,
ProcessorComponent,
MatTooltipModule,
],
templateUrl: './route.component.html',
styleUrl: './route.component.css',
})
export class RouteComponent {
path = input<string>('');
index = computed(() => {
const path = this.path();
if (path) {
const parts = path.split('/');
const lastPart = parts[parts.length - 1];
return parseInt(lastPart, 10);
}
return undefined;
});
route = model<RouteConfiguration>();
moduleIds = input<string[]>([]);
delete = output<void>();
@@ -85,6 +98,9 @@ export class RouteComponent {
}
return route;
});
this.snackBar.open('Processor Added', 'Dismiss', {
duration: 3000,
});
}
processorUpdated(index: number, processor: ProcessorConfiguration | undefined) {
+4 -4
View File
@@ -225,12 +225,12 @@ export class SchemaService {
.filter((definition) => definition.properties?.type?.const !== undefined)
.map((definition) => {
return {
name: definition['title'],
name: definition['title'] || definition.properties?.type?.const,
type: definition.properties?.type?.const,
schema: definition,
};
}),
['name'],
['type'],
);
}
}
@@ -247,12 +247,12 @@ export class SchemaService {
.filter((definition) => definition.properties?.type?.const !== undefined)
.map((definition) => {
return {
name: definition['title'],
name: definition['title'] || definition.properties?.type?.const,
type: definition.properties?.type?.const,
schema: definition,
};
}),
['name'],
['type'],
);
}
}