diff --git a/.gitignore b/.gitignore index b767049c..663d3009 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ # environment config **/.env + +# Claude Code +.claude/settings.json scylla-server-typescript/src/prisma/mydatabase.db scylla-server-typescript/src/prisma/mydatabase.db-journal scylla-server/files diff --git a/angular-client/src/components/argos-button/argos-button.component.ts b/angular-client/src/components/argos-button/argos-button.component.ts index dde6414e..67ad24c3 100644 --- a/angular-client/src/components/argos-button/argos-button.component.ts +++ b/angular-client/src/components/argos-button/argos-button.component.ts @@ -20,7 +20,7 @@ export class ButtonComponent implements OnInit { ngOnInit(): void { this.style = 'width: 140px; height: 45px; '; - if (this.additionalStyles) { + if (this.additionalStyles()) { this.style += this.additionalStyles(); } } diff --git a/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.html b/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.html index dda920a3..0312cda5 100644 --- a/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.html +++ b/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.html @@ -1,6 +1,6 @@
-
+
diff --git a/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.ts b/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.ts index 9df19f78..b5b57baf 100644 --- a/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.ts +++ b/angular-client/src/components/battery-level-indicator/battery-level-indicator.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges } from '@angular/core'; +import { Component, computed, input } from '@angular/core'; import Theme from 'src/services/theme.service'; /** @@ -11,14 +11,9 @@ import Theme from 'src/services/theme.service'; styleUrl: './battery-level-indicator.component.css', standalone: true }) -export class BatteryLevelIndicatorComponent implements OnChanges { - @Input() percentage: number = 0; +export class BatteryLevelIndicatorComponent { + percentage = input(0); - fillHeight = '0%'; - fillColor = Theme.battteryHigh; - - ngOnChanges(): void { - this.fillHeight = Math.max(0, Math.min(100, this.percentage)) + '%'; - this.fillColor = this.percentage <= 20 ? Theme.battteryLow : Theme.battteryHigh; - } + fillHeight = computed(() => Math.max(0, Math.min(100, this.percentage())) + '%'); + fillColor = computed(() => (this.percentage() <= 20 ? Theme.battteryLow : Theme.battteryHigh)); } diff --git a/angular-client/src/components/battery-percentage/battery-percentage.component.html b/angular-client/src/components/battery-percentage/battery-percentage.component.html index 3ac9695c..09d0900a 100644 --- a/angular-client/src/components/battery-percentage/battery-percentage.component.html +++ b/angular-client/src/components/battery-percentage/battery-percentage.component.html @@ -24,8 +24,8 @@ >
(); + ringColor = input.required(); + percentage = input.required(); + spacing = input(0); //values needed for styling and scaling backgroundColor: string = Theme.infoBackground; @@ -31,10 +31,10 @@ export class CircularPercentageComponent implements OnInit { //assigns values needed for styling and scaling ngOnInit() { - this.innerCircleDimension = this.dimension * 0.87; - this.percentageFontSize = this.dimension * 0.39; - this.percentageSignFontSize = this.dimension * 0.17; - this.percentageSignOffset = this.dimension * 0.02; + this.innerCircleDimension = this.dimension() * 0.87; + this.percentageFontSize = this.dimension() * 0.39; + this.percentageSignFontSize = this.dimension() * 0.17; + this.percentageSignOffset = this.dimension() * 0.02; } getFilledAngle(percentage: number): number { diff --git a/angular-client/src/components/current-total-timer/current-total-timer.component.ts b/angular-client/src/components/current-total-timer/current-total-timer.component.ts index 26ed4897..8b3206da 100644 --- a/angular-client/src/components/current-total-timer/current-total-timer.component.ts +++ b/angular-client/src/components/current-total-timer/current-total-timer.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import { InfoBackgroundComponent } from '../info-background/info-background.component'; import TypographyComponent from '../typography/typography.component'; import HStackComponent from '../hstack/hstack.component'; @@ -12,15 +12,15 @@ import VStackComponent from '../vstack/vstack.component'; imports: [InfoBackgroundComponent, TypographyComponent, HStackComponent, VStackComponent] }) export default class CurrentTotalTimerComponent { - @Input() currentTime: number = 0; - @Input() totalTime: number = 0; + currentTime = input(0); + totalTime = input(0); getCurrentTime() { - return this.formatTime(this.currentTime); + return this.formatTime(this.currentTime()); } getTotalTime() { - return this.formatTime(this.totalTime); + return this.formatTime(this.totalTime()); } /** diff --git a/angular-client/src/components/double-line-graph/double-line-graph.component.html b/angular-client/src/components/double-line-graph/double-line-graph.component.html index 314db036..cbc67865 100644 --- a/angular-client/src/components/double-line-graph/double-line-graph.component.html +++ b/angular-client/src/components/double-line-graph/double-line-graph.component.html @@ -1 +1 @@ -
+
diff --git a/angular-client/src/components/double-line-graph/double-line-graph.component.ts b/angular-client/src/components/double-line-graph/double-line-graph.component.ts index ee83c66d..dc903390 100644 --- a/angular-client/src/components/double-line-graph/double-line-graph.component.ts +++ b/angular-client/src/components/double-line-graph/double-line-graph.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject, input } from '@angular/core'; import ApexCharts from 'apexcharts'; import { ApexAxisChartSeries, @@ -41,15 +41,15 @@ type ChartOptions = { }) export class DoubleLineGraphComponent implements OnInit, OnDestroy { public dialogService = inject(DialogService); - @Input() data1!: GraphData[]; - @Input() color1!: string; // Must be hex - @Input() title1?: string; - @Input() data2!: GraphData[]; - @Input() color2!: string; // Must be hex - @Input() title2?: string; - @Input() header?: string; - @Input() graphContainerId!: string; - @Input({ required: false }) timeRangeSec!: number; + data1 = input.required(); + color1 = input.required(); // Must be hex + title1 = input(undefined); + data2 = input.required(); + color2 = input.required(); // Must be hex + title2 = input(undefined); + header = input(undefined); + graphContainerId = input.required(); + timeRangeSec = input(undefined); options!: ChartOptions; chart!: ApexCharts; series: ApexAxisChartSeries = []; @@ -59,11 +59,11 @@ export class DoubleLineGraphComponent implements OnInit, OnDestroy { timeOuts: NodeJS.Timeout[] = []; openDialog = () => { this.dialogService.open(GraphDialogComponent, { - header: this.header, + header: this.header(), data: { - data: this.data1, - color: this.color1, - title: this.title1 + data: this.data1(), + color: this.color1(), + title: this.title1() } }); }; @@ -76,18 +76,18 @@ export class DoubleLineGraphComponent implements OnInit, OnDestroy { updateChart = () => { this.series = [ { - name: this.title1, - data: Array.from(this.data1) + name: this.title1(), + data: Array.from(this.data1()) }, { - name: this.title2, - data: Array.from(this.data2) + name: this.title2(), + data: Array.from(this.data2()) } ]; // temp fix, for now just basing change on data1 length... // even though probably should check data1 also - if (!this.isSliding && this.data1.length > 2) { - this.timeDiffMs = this.data1[this.data1.length - 1].x - this.data1[0].x; + if (!this.isSliding && this.data1().length > 2) { + this.timeDiffMs = this.data1()[this.data1().length - 1].x - this.data1()[0].x; } if (!this.isSliding && this.timeDiffMs > this.timeRangeMs) { @@ -110,16 +110,16 @@ export class DoubleLineGraphComponent implements OnInit, OnDestroy { }; ngOnInit(): void { - this.timeRangeMs = (this.timeRangeSec ?? 120) * 1000; + this.timeRangeMs = (this.timeRangeSec() ?? 120) * 1000; this.series = [ { - name: this.title1, - data: this.data1 + name: this.title1(), + data: this.data1() }, { - name: this.title2, - data: this.data2 + name: this.title2(), + data: this.data2() } ]; @@ -143,7 +143,7 @@ export class DoubleLineGraphComponent implements OnInit, OnDestroy { } // background: '#5A5A5A' }, - colors: [this.color1, this.color2], // Set series colors here + colors: [this.color1(), this.color2()], // Set series colors here dataLabels: { enabled: false }, @@ -209,7 +209,7 @@ export class DoubleLineGraphComponent implements OnInit, OnDestroy { // Delay rendering to ensure the container is available setTimeout(() => { - const chartContainer = document.getElementById(this.graphContainerId); + const chartContainer = document.getElementById(this.graphContainerId()); if (!chartContainer) { return; } diff --git a/angular-client/src/components/error-page/error-page.component.html b/angular-client/src/components/error-page/error-page.component.html index 7d4b3826..f521a670 100644 --- a/angular-client/src/components/error-page/error-page.component.html +++ b/angular-client/src/components/error-page/error-page.component.html @@ -1,3 +1,3 @@
- +
diff --git a/angular-client/src/components/error-page/error-page.component.ts b/angular-client/src/components/error-page/error-page.component.ts index bc337ab8..901e3a6d 100644 --- a/angular-client/src/components/error-page/error-page.component.ts +++ b/angular-client/src/components/error-page/error-page.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import TypographyComponent from '../typography/typography.component'; /** @@ -13,5 +13,5 @@ import TypographyComponent from '../typography/typography.component'; standalone: true }) export default class ErrorPageComponent { - @Input() errorMessage!: string; + errorMessage = input.required(); } diff --git a/angular-client/src/components/form-template/form-template.component.ts b/angular-client/src/components/form-template/form-template.component.ts index a9aff461..4394908f 100644 --- a/angular-client/src/components/form-template/form-template.component.ts +++ b/angular-client/src/components/form-template/form-template.component.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { Component, EventEmitter, inject, input, OnChanges, OnInit, Output } from '@angular/core'; +import { Component, effect, inject, input, output, untracked } from '@angular/core'; import { FormBuilder, FormGroup, ValidatorFn, Validators, ReactiveFormsModule } from '@angular/forms'; import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { InputText } from 'primeng/inputtext'; @@ -31,7 +31,7 @@ export interface DynamicFormField { standalone: true, imports: [ReactiveFormsModule, InputText, NgClass, NgIf, ButtonDirective] }) -export class FormTemplateComponent implements OnInit, OnChanges { +export class FormTemplateComponent { public config = inject(DynamicDialogConfig); fields = input.required(); @@ -39,28 +39,24 @@ export class FormTemplateComponent implements OnInit, OnChanges { public ref = inject(DynamicDialogRef); formData = input>(new Map()); - @Output() submitForm: EventEmitter = new EventEmitter(); + submitForm = output(); form: FormGroup = this.fb.group({}); constructor(private fb: FormBuilder) { - // this.fields = this.config.data.fields; - } - - ngOnInit(): void { - this.buildForm(); - } - - ngOnChanges(): void { - this.buildForm(); + effect(() => { + const fields = this.fields(); + const formData = this.formData(); + untracked(() => this.buildForm(fields, formData)); + }); } - buildForm() { + buildForm(fields = this.fields(), formData = this.formData()) { const group: any = {}; - this.fields().forEach((field) => { + fields.forEach((field) => { const control = this.fb.control({ - value: this.formData() ? this.formData().get(field.name) : '', + value: formData ? formData.get(field.name) : '', disabled: field.disabled }); diff --git a/angular-client/src/components/glance-thermometer/glance-thermometer.component.html b/angular-client/src/components/glance-thermometer/glance-thermometer.component.html index 083590cd..0bb5924e 100644 --- a/angular-client/src/components/glance-thermometer/glance-thermometer.component.html +++ b/angular-client/src/components/glance-thermometer/glance-thermometer.component.html @@ -1,7 +1,7 @@
-
+
-
+
diff --git a/angular-client/src/components/glance-thermometer/glance-thermometer.component.ts b/angular-client/src/components/glance-thermometer/glance-thermometer.component.ts index 69fc0345..138e234c 100644 --- a/angular-client/src/components/glance-thermometer/glance-thermometer.component.ts +++ b/angular-client/src/components/glance-thermometer/glance-thermometer.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, computed, input } from '@angular/core'; /** * Compact thermometer widget sized for the At A Glance bar. @@ -11,21 +11,21 @@ import { Component, Input } from '@angular/core'; standalone: true }) export class GlanceThermometerComponent { - @Input() temperature: number = 0; - @Input() min: number = 0; - @Input() max: number = 100; + temperature = input(0); + min = input(0); + max = input(100); - get mercuryHeight(): string { - const clamped = Math.max(this.min, Math.min(this.temperature, this.max)); - const pct = ((clamped - this.min) / (this.max - this.min)) * 100; - // Mercury fills 0–70% of the tube area (leaving room for glass top) + // Mercury fills 0–70% of the tube area (leaving room for glass top) + mercuryHeight = computed(() => { + const clamped = Math.max(this.min(), Math.min(this.temperature(), this.max())); + const pct = ((clamped - this.min()) / (this.max() - this.min())) * 100; return Math.max(5, pct * 0.7) + '%'; - } + }); - get mercuryColor(): string { - const range = this.max - this.min; - if (this.temperature < this.min + range / 2) return '#3b82f6'; - if (this.temperature < this.min + range / 1.5) return '#eab308'; + mercuryColor = computed(() => { + const range = this.max() - this.min(); + if (this.temperature() < this.min() + range / 2) return '#3b82f6'; + if (this.temperature() < this.min() + range / 1.5) return '#eab308'; return '#ef4444'; - } + }); } diff --git a/angular-client/src/components/graph-dialog/graph-dialog.component.ts b/angular-client/src/components/graph-dialog/graph-dialog.component.ts index 56ab20e9..6b1b1971 100644 --- a/angular-client/src/components/graph-dialog/graph-dialog.component.ts +++ b/angular-client/src/components/graph-dialog/graph-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, inject } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { DialogService, DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { GraphData } from 'src/utils/types.utils'; import { GraphComponent } from '../graph/graph.component'; @@ -7,17 +7,16 @@ import { GraphComponent } from '../graph/graph.component'; selector: 'graph-dialog', templateUrl: './graph-dialog.component.html', providers: [DialogService], - standalone: true, imports: [GraphComponent] }) export class GraphDialogComponent { public dialogService = inject(DialogService); public config = inject(DynamicDialogConfig); - public ref = inject(DynamicDialogRef); // Inject the dialog reference for closing - @Input() data!: GraphData[]; - @Input() color!: string; - @Input() title!: string; - @Input() graphContainerId!: string; + public ref = inject(DynamicDialogRef); + data: GraphData[]; + color: string; + title: string; + graphContainerId: string; constructor() { this.data = this.config.data.data; diff --git a/angular-client/src/components/graph/graph.component.html b/angular-client/src/components/graph/graph.component.html index 1cf0777c..61559aee 100644 --- a/angular-client/src/components/graph/graph.component.html +++ b/angular-client/src/components/graph/graph.component.html @@ -1 +1 @@ -
+
diff --git a/angular-client/src/components/graph/graph.component.ts b/angular-client/src/components/graph/graph.component.ts index 54c62d9f..7aea142c 100644 --- a/angular-client/src/components/graph/graph.component.ts +++ b/angular-client/src/components/graph/graph.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject, input } from '@angular/core'; import ApexCharts from 'apexcharts'; import { ApexXAxis, ApexDataLabels, ApexChart, ApexMarkers, ApexGrid, ApexTooltip, ApexFill } from 'ng-apexcharts'; import { DialogService } from 'primeng/dynamicdialog'; @@ -26,11 +26,11 @@ type ChartOptions = { }) export class GraphComponent implements OnInit, OnDestroy { public dialogService = inject(DialogService); - @Input() data!: GraphData[]; - @Input() color!: string; // Must be hex - @Input() title?: string; - @Input() graphContainerId!: string; - @Input({ required: false }) timeRangeSec!: number; + data = input.required(); + color = input.required(); // Must be hex + title = input(undefined); + graphContainerId = input.required(); + timeRangeSec = input(undefined); options!: ChartOptions; chart!: ApexCharts; timeDiffMs: number = 0; @@ -39,11 +39,11 @@ export class GraphComponent implements OnInit, OnDestroy { timeOuts: NodeJS.Timeout[] = []; openDialog = () => { this.dialogService.open(GraphDialogComponent, { - header: this.title, + header: this.title(), data: { - data: this.data, - color: this.color, - title: this.title + data: this.data(), + color: this.color(), + title: this.title() } }); }; @@ -51,13 +51,13 @@ export class GraphComponent implements OnInit, OnDestroy { updateChart = () => { this.chart.updateSeries([ { - name: this.title, - data: Array.from(this.data) + name: this.title(), + data: Array.from(this.data()) } ]); - if (!this.isSliding && this.data.length > 2) { - this.timeDiffMs = this.data[this.data.length - 1].x - this.data[0].x; + if (!this.isSliding && this.data().length > 2) { + this.timeDiffMs = this.data()[this.data().length - 1].x - this.data()[0].x; } if (!this.isSliding && this.timeDiffMs > this.timeRangeMs) { @@ -79,7 +79,7 @@ export class GraphComponent implements OnInit, OnDestroy { }; ngOnInit(): void { - this.timeRangeMs = (this.timeRangeSec ?? 120) * 1000; + this.timeRangeMs = (this.timeRangeSec() ?? 120) * 1000; this.options = { chart: { @@ -105,7 +105,7 @@ export class GraphComponent implements OnInit, OnDestroy { }, stroke: { curve: 'straight', - colors: [this.color] + colors: [this.color()] }, markers: { size: 0 @@ -168,7 +168,7 @@ export class GraphComponent implements OnInit, OnDestroy { //Weird rendering stuff with apex charts, view link to see why https://github.com/apexcharts/react-apexcharts/issues/187 this.timeOuts.push( setTimeout(() => { - const chartContainer = document.getElementById(this.graphContainerId); + const chartContainer = document.getElementById(this.graphContainerId()); if (!chartContainer) { return; } diff --git a/angular-client/src/components/grid-layout/grid-layout.component.html b/angular-client/src/components/grid-layout/grid-layout.component.html index 0e15f768..2e126b0c 100644 --- a/angular-client/src/components/grid-layout/grid-layout.component.html +++ b/angular-client/src/components/grid-layout/grid-layout.component.html @@ -1,3 +1,3 @@ -
+
diff --git a/angular-client/src/components/grid-layout/grid-layout.component.ts b/angular-client/src/components/grid-layout/grid-layout.component.ts index 669f04d4..5cd74c65 100644 --- a/angular-client/src/components/grid-layout/grid-layout.component.ts +++ b/angular-client/src/components/grid-layout/grid-layout.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, computed, input } from '@angular/core'; import { NgStyle } from '@angular/common'; type RepeatMode = 'auto-fit' | 'auto-fill'; @@ -16,23 +16,22 @@ type RepeatMode = 'auto-fit' | 'auto-fill'; selector: 'grid-layout', templateUrl: './grid-layout.component.html', styleUrls: ['./grid-layout.component.css'], - standalone: true, imports: [NgStyle] }) export default class GridLayoutComponent { /** Minimum column width (e.g. '300px', '20rem') */ - @Input() minWidth: string = '300px'; + minWidth = input('300px'); /** Maximum column width (e.g. '1fr', '500px') */ - @Input() maxWidth: string = '1fr'; + maxWidth = input('1fr'); /** Grid repeat mode: 'auto-fit' collapses empty tracks, 'auto-fill' keeps them */ - @Input() repeatMode: RepeatMode = 'auto-fit'; + repeatMode = input('auto-fit'); /** Gap between grid items (e.g. '15px', '1rem') */ - @Input() gap: string = '15px'; + gap = input('15px'); - get gridTemplateColumns(): string { - return `repeat(${this.repeatMode}, minmax(${this.minWidth}, ${this.maxWidth}))`; - } + gridTemplateColumns = computed(() => { + return `repeat(${this.repeatMode()}, minmax(${this.minWidth()}, ${this.maxWidth()}))`; + }); } diff --git a/angular-client/src/components/half-gauge/half-gauge.component.ts b/angular-client/src/components/half-gauge/half-gauge.component.ts index 1d55d927..83a8dacd 100644 --- a/angular-client/src/components/half-gauge/half-gauge.component.ts +++ b/angular-client/src/components/half-gauge/half-gauge.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, OnInit, input } from '@angular/core'; import { ApexNonAxisChartSeries, ApexPlotOptions, ApexChart, ApexFill, NgApexchartsModule } from 'ng-apexcharts'; import { NgStyle } from '@angular/common'; @@ -21,12 +21,12 @@ export type ChartOptions = { export default class HalfGaugeComponent implements OnInit { // eslint-disable-next-line @typescript-eslint/no-explicit-any public chartOptions!: Partial | any; - @Input() current: number = 50; - @Input() min: number = 0; - @Input() max: number = 100; - @Input() unit: string = 'm/s'; - @Input() color: string = '#ff0000'; - @Input() size: number = 200; + current = input(50); + min = input(0); + max = input(100); + unit = input('m/s'); + color = input('#ff0000'); + size = input(200); widthpx: string = '200px'; heightpx: string = '200px'; @@ -36,12 +36,12 @@ export default class HalfGaugeComponent implements OnInit { fontsize: string = '50px'; ngOnInit() { - this.widthpx = this.size + 'px'; - this.heightpx = this.size * 0.5 + 'px'; + this.widthpx = this.size() + 'px'; + this.heightpx = this.size() * 0.5 + 'px'; this.paddingTop = ''; - this.label = this.current + this.unit; - this.percentage = ((this.current - this.min) / (this.max - this.min)) * 100; - this.fontsize = this.size / 10 + 'px'; + this.label = this.current() + this.unit(); + this.percentage = ((this.current() - this.min()) / (this.max() - this.min())) * 100; + this.fontsize = this.size() / 10 + 'px'; // apex radial charts are hard coded to work with percentages, so converting to percentage to // accurately represent min and max in chart and then using actual value and unit as label @@ -91,7 +91,7 @@ export default class HalfGaugeComponent implements OnInit { }, fill: { type: 'solid', - colors: [this.color] + colors: [this.color()] }, labels: [this.label] }; diff --git a/angular-client/src/components/hstack/hstack.component.html b/angular-client/src/components/hstack/hstack.component.html index 3a68ee5b..1111e9cc 100644 --- a/angular-client/src/components/hstack/hstack.component.html +++ b/angular-client/src/components/hstack/hstack.component.html @@ -1,3 +1,3 @@ -
+
diff --git a/angular-client/src/components/hstack/hstack.component.ts b/angular-client/src/components/hstack/hstack.component.ts index 93ed8953..52d30dbc 100644 --- a/angular-client/src/components/hstack/hstack.component.ts +++ b/angular-client/src/components/hstack/hstack.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, input } from '@angular/core'; import { NgStyle } from '@angular/common'; type JusitfyContent = 'space-between' | 'space-around' | 'space-evenly' | 'center' | 'start' | 'end'; @@ -12,14 +12,8 @@ type AlignItems = 'center' | 'start' | 'end' | 'stretch'; standalone: true, imports: [NgStyle] }) -export default class HStackComponent implements OnInit { - @Input() spacing: string = '20px'; - @Input() justifyContent: JusitfyContent = 'center'; - @Input() alignItems: AlignItems = 'center'; - - alignment!: string; - - ngOnInit() { - this.alignment = `${this.justifyContent} ${this.alignItems}`; - } +export default class HStackComponent { + spacing = input('20px'); + justifyContent = input('center'); + alignItems = input('center'); } diff --git a/angular-client/src/components/info-background/info-background.component.html b/angular-client/src/components/info-background/info-background.component.html index 57dd3250..ca3ea5ab 100644 --- a/angular-client/src/components/info-background/info-background.component.html +++ b/angular-client/src/components/info-background/info-background.component.html @@ -17,16 +17,14 @@ }
- @if (button) { + @if (button(); as btn) {
- @if (button) { - - } +
} @if (this.selectorConfigs().length !== 0) { diff --git a/angular-client/src/components/info-background/info-background.component.ts b/angular-client/src/components/info-background/info-background.component.ts index 8c4c31e7..74e01862 100644 --- a/angular-client/src/components/info-background/info-background.component.ts +++ b/angular-client/src/components/info-background/info-background.component.ts @@ -1,4 +1,4 @@ -import { Component, input, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import Theme from 'src/services/theme.service'; import { SelectorConfig, SelectDropdownComponent } from '../select-dropdown/select-dropdown.component'; import { MatIcon } from '@angular/material/icon'; @@ -28,7 +28,7 @@ export class InfoBackgroundComponent { backgroundColor = input(Theme.infoBackground); title = input(); onClick = input<(() => void) | undefined>(undefined); - @Input() button?: ButtonInputs; + button = input(undefined); selectorConfigs = input([]); topRightInfo = input(undefined); diff --git a/angular-client/src/components/info-graph/info-graph.component.html b/angular-client/src/components/info-graph/info-graph.component.html index e0bca76d..cb3f37ad 100644 --- a/angular-client/src/components/info-graph/info-graph.component.html +++ b/angular-client/src/components/info-graph/info-graph.component.html @@ -1,9 +1,9 @@ - + diff --git a/angular-client/src/components/info-graph/info-graph.component.ts b/angular-client/src/components/info-graph/info-graph.component.ts index 70f0acab..12de1bc7 100644 --- a/angular-client/src/components/info-graph/info-graph.component.ts +++ b/angular-client/src/components/info-graph/info-graph.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, inject } from '@angular/core'; +import { Component, inject, input } from '@angular/core'; import { DialogService } from 'primeng/dynamicdialog'; import { GraphDialogComponent } from '../graph-dialog/graph-dialog.component'; import { GraphData } from 'src/utils/types.utils'; @@ -15,21 +15,21 @@ import { GraphComponent } from '../graph/graph.component'; }) export class InfoGraphComponent { public dialogService = inject(DialogService); - @Input() data!: GraphData[]; - @Input() icon!: string; - @Input() title!: string; - @Input() color!: string; - @Input() subTitle?: string; - @Input() graphContainerId!: string; + data = input.required(); + icon = input.required(); + title = input.required(); + color = input.required(); + subTitle = input(undefined); + graphContainerId = input.required(); openDialog = () => { this.dialogService.open(GraphDialogComponent, { - header: this.title, + header: this.title(), data: { - data: this.data, - color: this.color, - title: this.title, - subTitle: this.subTitle, - graphContainerId: this.graphContainerId + 'big' + data: this.data(), + color: this.color(), + title: this.title(), + subTitle: this.subTitle(), + graphContainerId: this.graphContainerId() + 'big' }, modal: true, // makes the dialog modal dismissableMask: true, // enables auto-close on outside click diff --git a/angular-client/src/components/info-value-dispaly/info-value-display.component.html b/angular-client/src/components/info-value-dispaly/info-value-display.component.html index d0af7da1..3732100c 100644 --- a/angular-client/src/components/info-value-dispaly/info-value-display.component.html +++ b/angular-client/src/components/info-value-dispaly/info-value-display.component.html @@ -6,7 +6,7 @@ } @else { diff --git a/angular-client/src/components/info-value-dispaly/info-value-display.component.ts b/angular-client/src/components/info-value-dispaly/info-value-display.component.ts index c2646322..3a2c3fc5 100644 --- a/angular-client/src/components/info-value-dispaly/info-value-display.component.ts +++ b/angular-client/src/components/info-value-dispaly/info-value-display.component.ts @@ -1,4 +1,5 @@ -import { Component, input, OnChanges } from '@angular/core'; +import { Component, computed, input } from '@angular/core'; +import { formatDisplayValue } from 'src/utils/pipes.utils'; import { BatteryPercentageComponent } from '../battery-percentage/battery-percentage.component'; import { ConnectionDotWithMessageComponent } from '../connection-dot-with-message/connection-dot-with-message.component'; import TypographyComponent from '../typography/typography.component'; @@ -42,10 +43,7 @@ export type WidgetConfig = ThermometerConfig | BatteryConfig | ConnectionDotConf VStackComponent ] }) -export class InfoValueDisplayComponent implements OnChanges { - ngOnChanges(): void { - this.formattedValue = (this.value()?.toFixed(this.precision()) ?? '-') + (this.unit() === 'C' ? '°' : ''); - } +export class InfoValueDisplayComponent { containerStyle = input(''); valueUnitContainerStyle = input(''); value = input(); @@ -56,7 +54,8 @@ export class InfoValueDisplayComponent implements OnChanges { subtitleStyle = input(''); unit = input(''); unitStyle = input(''); - formattedValue = '-'; + + formattedValue = computed(() => formatDisplayValue(this.value(), this.precision(), this.unit())); // Consolidated widget input widget = input(); diff --git a/angular-client/src/components/latency-display/latency-display.html b/angular-client/src/components/latency-display/latency-display.html index f831ba57..ec657476 100644 --- a/angular-client/src/components/latency-display/latency-display.html +++ b/angular-client/src/components/latency-display/latency-display.html @@ -3,7 +3,7 @@
-
+
@@ -14,7 +14,7 @@
-
+
diff --git a/angular-client/src/components/latency-display/latency-display.ts b/angular-client/src/components/latency-display/latency-display.ts index 0949eb2c..4e60b440 100644 --- a/angular-client/src/components/latency-display/latency-display.ts +++ b/angular-client/src/components/latency-display/latency-display.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit, inject } from '@angular/core'; +import { Component, OnInit, inject, input } from '@angular/core'; import Storage from 'src/services/storage.service'; import { topics } from 'src/utils/topic.utils'; import { InfoBackgroundComponent } from '../info-background/info-background.component'; @@ -17,9 +17,9 @@ import HStackComponent from '../hstack/hstack.component'; }) export default class LatencyDisplayComponent implements OnInit { private storage = inject(Storage); - @Input() lowVal: number = 0; - @Input() medVal: number = 50; - @Input() highVal: number = 100; + lowVal = input(0); + medVal = input(50); + highVal = input(100); latency: number = 0; newLatency: number = 0; diff --git a/angular-client/src/components/pie-chart/pie-chart.component.ts b/angular-client/src/components/pie-chart/pie-chart.component.ts index 572a30f1..edcf7c72 100644 --- a/angular-client/src/components/pie-chart/pie-chart.component.ts +++ b/angular-client/src/components/pie-chart/pie-chart.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, Input, Renderer2, OnInit, inject, input } from '@angular/core'; +import { Component, ElementRef, OnInit, Renderer2, inject, input } from '@angular/core'; import { ApexNonAxisChartSeries, ApexPlotOptions, ApexChart, ApexFill, NgApexchartsModule } from 'ng-apexcharts'; import Theme from 'src/services/theme.service'; @@ -24,7 +24,7 @@ export default class PieChartComponent implements OnInit { // eslint-disable-next-line @typescript-eslint/no-explicit-any public chartOptions!: Partial | any; data = input.required<{ value: number; name: string }[]>(); - @Input() backgroundColor: string = Theme.infoBackground; + backgroundColor = input(Theme.infoBackground); title = input('Pie Chart'); currentWidth: number = 0; @@ -61,7 +61,7 @@ export default class PieChartComponent implements OnInit { chart: { width: '100%', type: 'pie', - background: this.backgroundColor, + background: this.backgroundColor(), redrawOnParentResize: true, foreColor: '#ffffff', animations: { diff --git a/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.html b/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.html index 7fa20d22..a30c97b7 100644 --- a/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.html +++ b/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.html @@ -1,6 +1,6 @@ - + @@ -8,9 +8,9 @@ - +
- +
@@ -19,7 +19,7 @@ - +
@@ -29,7 +29,7 @@ - +
dBM @@ -41,7 +41,7 @@ - + diff --git a/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.ts b/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.ts index 004fa2b9..542f9d6f 100644 --- a/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.ts +++ b/angular-client/src/components/raspberry-pi/raspberry-pi-desktop-content/raspberry-pi-desktop.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import { CircularPercentageComponent } from '../../circular-percentage/circular-percentage.component'; import { DividerComponent } from '../../divider/divider'; import { MatIcon } from '@angular/material/icon'; @@ -23,11 +23,11 @@ import ThermometerComponent from 'src/components/thermometer/thermometer.compone ] }) export default class RaspberryPiDesktopComponent { - @Input() cpuUsage: number = 0; - @Input() cpuTemp: number = 0; - @Input() ramUsage: number = 0; - @Input() wifiRSSI: number = 0; - @Input() mcs: number = 0; + cpuUsage = input(0); + cpuTemp = input(0); + ramUsage = input(0); + wifiRSSI = input(0); + mcs = input(0); colorRed = '#FF0000'; colorPurple = '#800080'; diff --git a/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.html b/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.html index ddf2b6e4..b16538bc 100644 --- a/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.html +++ b/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.html @@ -1,7 +1,7 @@ - + @@ -9,9 +9,9 @@ - +
- +
@@ -21,7 +21,7 @@ - + @@ -29,7 +29,7 @@ - +
@@ -39,7 +39,7 @@ - +
dBM diff --git a/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.ts b/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.ts index 2b1bd8ef..bf96a0ca 100644 --- a/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.ts +++ b/angular-client/src/components/raspberry-pi/raspberry-pi-mobile-content/raspberry-pi-mobile.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import { CircularPercentageComponent } from '../../circular-percentage/circular-percentage.component'; import { DividerComponent } from '../../divider/divider'; import { MatIcon } from '@angular/material/icon'; @@ -23,11 +23,11 @@ import ThermometerComponent from 'src/components/thermometer/thermometer.compone ] }) export default class RaspberryPiMobileComponent { - @Input() cpuUsage: number = 0; - @Input() cpuTemp: number = 0; - @Input() ramUsage: number = 0; - @Input() wifiRSSI: number = 0; - @Input() mcs: number = 0; + cpuUsage = input(0); + cpuTemp = input(0); + ramUsage = input(0); + wifiRSSI = input(0); + mcs = input(0); colorRed = '#FF0000'; colorPurple = '#800080'; diff --git a/angular-client/src/components/select-dropdown/select-dropdown.component.ts b/angular-client/src/components/select-dropdown/select-dropdown.component.ts index b9c48577..30033bc9 100644 --- a/angular-client/src/components/select-dropdown/select-dropdown.component.ts +++ b/angular-client/src/components/select-dropdown/select-dropdown.component.ts @@ -1,4 +1,4 @@ -import { Component, effect, input, ViewChild } from '@angular/core'; +import { Component, effect, input, viewChild } from '@angular/core'; import { SelectChangeEvent, Select } from 'primeng/select'; import { ReactiveFormsModule, FormsModule } from '@angular/forms'; @@ -34,7 +34,7 @@ export class SelectDropdownComponent { selectedOption: DropdownOption | undefined; // eslint-disable-next-line @typescript-eslint/no-explicit-any - @ViewChild('dropdownRef') dropdownRef: any; + dropdownRef = viewChild('dropdownRef'); constructor() { effect(() => { diff --git a/angular-client/src/components/sidebar-chip/sidebar-chip.component.html b/angular-client/src/components/sidebar-chip/sidebar-chip.component.html index 54902aee..21c9cc4c 100644 --- a/angular-client/src/components/sidebar-chip/sidebar-chip.component.html +++ b/angular-client/src/components/sidebar-chip/sidebar-chip.component.html @@ -1,9 +1,9 @@ -
+
- +
diff --git a/angular-client/src/components/sidebar-chip/sidebar-chip.component.ts b/angular-client/src/components/sidebar-chip/sidebar-chip.component.ts index 9537e6db..846bc8d2 100644 --- a/angular-client/src/components/sidebar-chip/sidebar-chip.component.ts +++ b/angular-client/src/components/sidebar-chip/sidebar-chip.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import { MatIcon } from '@angular/material/icon'; import TypographyComponent from '../typography/typography.component'; @@ -10,8 +10,8 @@ import TypographyComponent from '../typography/typography.component'; imports: [MatIcon, TypographyComponent] }) export default class SidebarChipComponent { - @Input() icon: string = ''; - @Input() value: string = ''; - @Input() active: boolean = false; // New input to control active state - @Input() small: boolean = false; + icon = input(''); + value = input(''); + active = input(false); // New input to control active state + small = input(false); } diff --git a/angular-client/src/components/stat-display/stat-display.component.html b/angular-client/src/components/stat-display/stat-display.component.html index 23608388..400b1996 100644 --- a/angular-client/src/components/stat-display/stat-display.component.html +++ b/angular-client/src/components/stat-display/stat-display.component.html @@ -8,7 +8,7 @@ }
- {{ formattedValue }} + {{ formattedValue() }} {{ unit() }}
diff --git a/angular-client/src/components/stat-display/stat-display.component.ts b/angular-client/src/components/stat-display/stat-display.component.ts index 46bd1044..70cb87b5 100644 --- a/angular-client/src/components/stat-display/stat-display.component.ts +++ b/angular-client/src/components/stat-display/stat-display.component.ts @@ -1,5 +1,6 @@ -import { Component, input, OnChanges } from '@angular/core'; +import { Component, computed, input } from '@angular/core'; import { MatIcon } from '@angular/material/icon'; +import { formatDisplayValue } from 'src/utils/pipes.utils'; /** * Lightweight stat display component designed for the At A Glance bar. @@ -16,7 +17,7 @@ import { MatIcon } from '@angular/material/icon'; '[class.unit-below-mode]': 'unitBelow()' } }) -export class StatDisplayComponent implements OnChanges { +export class StatDisplayComponent { value = input(); unit = input(''); subtitle = input(''); @@ -28,10 +29,5 @@ export class StatDisplayComponent implements OnChanges { /** When true, render the unit below the value instead of inline */ unitBelow = input(false); - formattedValue = '-'; - - ngOnChanges(): void { - const val = this.value(); - this.formattedValue = (val?.toFixed(this.precision()) ?? '-') + (this.unit() === 'C' ? '°' : ''); - } + formattedValue = computed(() => formatDisplayValue(this.value(), this.precision(), this.unit())); } diff --git a/angular-client/src/components/switch/switch.component.html b/angular-client/src/components/switch/switch.component.html index b5647513..bb9804f6 100644 --- a/angular-client/src/components/switch/switch.component.html +++ b/angular-client/src/components/switch/switch.component.html @@ -1,5 +1,5 @@ -
-
+
+
- + diff --git a/angular-client/src/components/switch/switch.component.ts b/angular-client/src/components/switch/switch.component.ts index 32784506..c530f5d3 100644 --- a/angular-client/src/components/switch/switch.component.ts +++ b/angular-client/src/components/switch/switch.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core'; +import { Component, OnInit, computed, input, output, signal } from '@angular/core'; import TypographyComponent from '../typography/typography.component'; @Component({ @@ -9,24 +9,19 @@ import TypographyComponent from '../typography/typography.component'; imports: [TypographyComponent] }) export class SwitchComponent implements OnInit { - @Input() isOn: boolean = false; - @Input() offString: string = 'PAUSED'; - @Input() onString: string = 'ALLOWED'; - chargingString: string = this.offString; - @Output() toggleEmitter = new EventEmitter(); + isOn = input(false); + offString = input('PAUSED'); + onString = input('ALLOWED'); + currentState = signal(false); + chargingString = computed(() => (this.currentState() ? this.onString() : this.offString())); + toggleEmitter = output(); ngOnInit(): void { - // Set the initial value of chargingString based on isOn - this.chargingString = this.isOn ? this.onString : this.offString; + this.currentState.set(this.isOn()); } onToggle() { - this.isOn = !this.isOn; - if (this.isOn) { - this.chargingString = this.onString; - } else { - this.chargingString = this.offString; - } - this.toggleEmitter.emit(this.isOn); // Emit the new state + this.currentState.update((v) => !v); + this.toggleEmitter.emit(this.currentState()); } } diff --git a/angular-client/src/components/thermometer/thermometer.component.html b/angular-client/src/components/thermometer/thermometer.component.html index 333f7b84..44a607d2 100644 --- a/angular-client/src/components/thermometer/thermometer.component.html +++ b/angular-client/src/components/thermometer/thermometer.component.html @@ -1,4 +1,4 @@
-
-
+
+
diff --git a/angular-client/src/components/thermometer/thermometer.component.ts b/angular-client/src/components/thermometer/thermometer.component.ts index ef9f7302..f57c9a96 100644 --- a/angular-client/src/components/thermometer/thermometer.component.ts +++ b/angular-client/src/components/thermometer/thermometer.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; @Component({ selector: 'thermometer', @@ -7,9 +7,9 @@ import { Component, Input } from '@angular/core'; standalone: true }) export default class ThermometerComponent { - @Input() temperature: number = 0; - @Input() min: number = 0; - @Input() max: number = 100; + temperature = input(0); + min = input(0); + max = input(100); mapColor = (value: number, min: number, max: number) => { const range = max - min; diff --git a/angular-client/src/components/vstack/vstack.component.html b/angular-client/src/components/vstack/vstack.component.html index f6e18740..bda26572 100644 --- a/angular-client/src/components/vstack/vstack.component.html +++ b/angular-client/src/components/vstack/vstack.component.html @@ -1,3 +1,3 @@ -
+
diff --git a/angular-client/src/components/vstack/vstack.component.ts b/angular-client/src/components/vstack/vstack.component.ts index e19071e7..31bf88f7 100644 --- a/angular-client/src/components/vstack/vstack.component.ts +++ b/angular-client/src/components/vstack/vstack.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, input } from '@angular/core'; import { NgStyle } from '@angular/common'; @Component({ @@ -9,6 +9,6 @@ import { NgStyle } from '@angular/common'; imports: [NgStyle] }) export default class VStackComponent { - @Input() spacing: string = '5px'; - @Input() align: string = 'center'; + spacing = input('5px'); + align = input('center'); } diff --git a/angular-client/src/pages/camera-page/camera-page.component.ts b/angular-client/src/pages/camera-page/camera-page.component.ts index 9f8a0df6..251f6a7d 100644 --- a/angular-client/src/pages/camera-page/camera-page.component.ts +++ b/angular-client/src/pages/camera-page/camera-page.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, inject, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, OnInit, inject, viewChild } from '@angular/core'; import { MessageService } from 'primeng/api'; import { SelectChangeEvent, Select } from 'primeng/select'; import { urls } from 'src/api/urls'; @@ -28,8 +28,8 @@ export class CameraPageComponent implements OnInit { videoUrls: string[] = ['Live Stream']; videoUrlsIsLoading: boolean = true; - @ViewChild('remoteVideo') remoteVideo?: ElementRef; - @ViewChild('playbackVideo', { static: false }) playbackVideoRef!: ElementRef; + remoteVideo = viewChild>('remoteVideo'); + playbackVideoRef = viewChild.required>('playbackVideo'); async ngOnInit(): Promise { this.urlAvailable = await this.checkConnection(); @@ -37,8 +37,9 @@ export class CameraPageComponent implements OnInit { url: new URL('whep', this.url), onError: console.log, onTrack: (e) => { - if (this.remoteVideo) { - [this.remoteVideo.nativeElement.srcObject] = e.streams; + const video = this.remoteVideo(); + if (video) { + [video.nativeElement.srcObject] = e.streams; } } }); @@ -66,7 +67,7 @@ export class CameraPageComponent implements OnInit { this.liveStream = false; // Wait for Angular to update the DOM setTimeout(() => { - const videoEl = this.playbackVideoRef.nativeElement; + const videoEl = this.playbackVideoRef().nativeElement; videoEl.load(); // This reloads the new inside the