diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 00000000..5ef6a520 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/src/CONVERSION_STATUS.md b/src/CONVERSION_STATUS.md new file mode 100644 index 00000000..87f41504 --- /dev/null +++ b/src/CONVERSION_STATUS.md @@ -0,0 +1,126 @@ +# Angular to React/Next.js Conversion Status + +## Completed + +### Core Infrastructure +- ✅ Next.js 16 setup with TypeScript +- ✅ MUI (Material-UI) theme provider configured +- ✅ App router structure established +- ✅ All business logic migrated (dive planner service, algorithms, models) +- ✅ Application Insights service ported +- ✅ React Context provider for dive planner state +- ✅ Build system working successfully + +### Pages Converted +- ✅ Home page (`/`) - with YouTube embed and GitHub links +- ⏳ New Dive page (`/new-dive`) - placeholder created, needs full implementation + +## Remaining Work + +### Components to Convert (from Angular to React) + +The following Angular components need to be converted to React functional components with MUI: + +#### Core Pages +- [ ] `new-dive` - Gas selection and dive configuration +- [ ] `dive-overview` - Main dive view with all charts and segments +- [ ] `change-depth` - Change depth segment +- [ ] `change-gas` - Change gas segment +- [ ] `maintain-depth` - Maintain depth segment + +#### Display Components +- [ ] `dive-plan` - Dive segments list +- [ ] `dive-summary` - Summary statistics +- [ ] `current-stats` - Current dive statistics +- [ ] `error-list` - Validation errors display + +#### Input Components +- [ ] `standard-gas-list` - Standard gas selection list +- [ ] `custom-gas-input` - Custom gas input form +- [ ] `new-gas-input` - New gas input for segments +- [ ] `dive-settings` - Dive settings configuration + +#### Stats Components +- [ ] `start-gas-stats` - Starting gas statistics +- [ ] `new-depth-stats` - New depth statistics +- [ ] `new-gas-stats` - New gas statistics +- [ ] `new-time-stats` - New time statistics + +#### Chart Components (using plotly.js) +- [ ] `depth-chart` - Depth over time chart +- [ ] `po2-chart` - PO2 levels chart +- [ ] `end-chart` - END (Equivalent Narcotic Depth) chart +- [ ] `ceiling-chart` - Ceiling chart +- [ ] `tissues-ceiling-chart` - Tissue ceilings chart +- [ ] `tissues-pn2-chart` - Tissue PN2 chart +- [ ] `tissues-phe-chart` - Tissue PHe chart +- [ ] `graph-dialog` - Chart dialog modal + +### Angular to React Conversion Pattern + +For each component, follow this pattern: + +1. **Convert Component Class to Function Component** + ```typescript + // Angular + @Component({...}) + export class MyComponent { + constructor(private service: Service) {} + } + + // React + 'use client'; + export default function MyComponent() { + const service = useService(); + } + ``` + +2. **Replace Angular Template with JSX** + - `*ngIf` → `{condition &&
...
}` + - `*ngFor` → `{array.map(item =>
...
)}` + - `[(ngModel)]` → `useState` + `onChange` + - `(click)` → `onClick` + - `[property]` → `property={value}` + +3. **Convert Angular Material to MUI** + - `mat-button` → ` - - - - diff --git a/src/src/app/change-depth/change-depth.component.scss b/src/src/app/change-depth/change-depth.component.scss deleted file mode 100644 index 38325aa1..00000000 --- a/src/src/app/change-depth/change-depth.component.scss +++ /dev/null @@ -1,46 +0,0 @@ -main { - display: flex; - justify-content: center; -} - -.main-content { - display: flex; - justify-content: center; - flex-direction: column; -} - -dive-current-stats, dive-new-depth-stats { - margin-top: 1rem; - width: fit-content; - height: fit-content; - display: flex; -} - -.input-section { - margin-top: 1rem; - width: fit-content; - height: fit-content; - border-style: solid; - border-width: 4px; - padding-left: 1rem; - padding-right: 1rem; -} - -h1 { - margin-top: 2rem; - margin-bottom: 0rem; -} - -.depth-input { - padding-bottom: 0; - padding-top: 1rem; - width: 10rem; -} - -.actions { - margin-top: 2rem; -} - -button { - margin-right: 1rem; -} diff --git a/src/src/app/change-depth/change-depth.component.spec.ts b/src/src/app/change-depth/change-depth.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/change-depth/change-depth.component.theme.scss b/src/src/app/change-depth/change-depth.component.theme.scss deleted file mode 100644 index eaccb607..00000000 --- a/src/src/app/change-depth/change-depth.component.theme.scss +++ /dev/null @@ -1,12 +0,0 @@ -@mixin change-depth-theme($theme) { - $color: map-get($theme, color); - $primary: map-get($color, primary); - $background: map-get($color, background); - - $nav-color: map-get($primary, A100); - - .input-section { - border-color: $nav-color; - background-color: map-get($background, card); - } -} diff --git a/src/src/app/change-depth/change-depth.component.ts b/src/src/app/change-depth/change-depth.component.ts deleted file mode 100644 index 93724d93..00000000 --- a/src/src/app/change-depth/change-depth.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Component } from '@angular/core'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; -import { Router } from '@angular/router'; - -@Component({ - selector: 'dive-change-depth', - templateUrl: './change-depth.component.html', - styleUrls: ['./change-depth.component.scss'], -}) -export class ChangeDepthComponent { - newDepth: number = this.divePlanner.getCurrentDepth(); - - constructor( - private divePlanner: DivePlannerService, - private router: Router - ) {} - - onSave(): void { - this.divePlanner.addChangeDepthSegment(this.newDepth); - this.router.navigate(['/dive-overview']); - } -} diff --git a/src/src/app/change-gas/change-gas.component.html b/src/src/app/change-gas/change-gas.component.html deleted file mode 100644 index e0be0c57..00000000 --- a/src/src/app/change-gas/change-gas.component.html +++ /dev/null @@ -1,12 +0,0 @@ -
-
- -

Select new gas

- - -
- - -
-
-
diff --git a/src/src/app/change-gas/change-gas.component.scss b/src/src/app/change-gas/change-gas.component.scss deleted file mode 100644 index 81503a14..00000000 --- a/src/src/app/change-gas/change-gas.component.scss +++ /dev/null @@ -1,33 +0,0 @@ -main { - display: flex; - justify-content: center; -} - -.main-content { - display: flex; - justify-content: center; - flex-direction: column; -} - -dive-current-stats, dive-new-gas-input, dive-new-gas-stats { - margin-top: 1rem; - margin-left: 1rem; - width: fit-content; - height: fit-content; - display: flex; -} - -h1 { - padding-left: 1rem; - margin-top: 2rem; - margin-bottom: 0rem; -} - -.actions { - margin-left: 1rem; - margin-top: 2rem; -} - -button { - margin-right: 1rem; -} diff --git a/src/src/app/change-gas/change-gas.component.spec.ts b/src/src/app/change-gas/change-gas.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/change-gas/change-gas.component.ts b/src/src/app/change-gas/change-gas.component.ts deleted file mode 100644 index c76134f9..00000000 --- a/src/src/app/change-gas/change-gas.component.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Component } from '@angular/core'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; -import { Router } from '@angular/router'; -import { BreathingGas } from '../dive-planner-service/BreathingGas'; - -@Component({ - selector: 'dive-change-gas', - templateUrl: './change-gas.component.html', - styleUrls: ['./change-gas.component.scss'], -}) -export class ChangeGasComponent { - newGas: BreathingGas = this.divePlanner.getCurrentGas(); - - constructor( - private divePlanner: DivePlannerService, - private router: Router - ) {} - - onSave(): void { - this.divePlanner.addChangeGasSegment(this.newGas); - this.router.navigate(['/dive-overview']); - } - - onNewGasSelected(newGas: BreathingGas): void { - this.newGas = newGas; - } -} diff --git a/src/src/app/current-stats/current-stats.component.html b/src/src/app/current-stats/current-stats.component.html deleted file mode 100644 index bd3f9987..00000000 --- a/src/src/app/current-stats/current-stats.component.html +++ /dev/null @@ -1,63 +0,0 @@ -
-
- Current Depth: - {{ currentDepth }}m -
-
- - No Deco Limit: - {{ noDecoLimit }} - -
-
- - Current Ceiling: - {{ currentCeiling }}m - -
-
- Current Gas: - -
-
- - - Max Depth (PO2): - {{ currentGas.maxDepthPO2 }}m ({{ currentGas.maxDepthPO2Deco }}m deco) - -
-
- - Max Depth (END): - {{ currentGas.maxDepthEND }}m - -
-
- - Min Depth (Hypoxia): - {{ currentGas.minDepth }}m - -
-
- - - PO2: - {{ currentPO2 | number: '1.2-2' }} - - warning - error -
-
- - END: - {{ currentEND }}m - - error -
-
diff --git a/src/src/app/current-stats/current-stats.component.scss b/src/src/app/current-stats/current-stats.component.scss deleted file mode 100644 index dbe32120..00000000 --- a/src/src/app/current-stats/current-stats.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -.current-stats { - padding: 1rem; -} - -.dive-stat, .gas-stat { - padding-top: 0.1rem; - padding-bottom: 0.1rem; - line-height: 28px; // this is needed because mat-icons are 24x24 (not sure why it needs to be 28 instead of 24 though) -} - -.gas-stat { - padding-left: 2rem; -} - -.warning-icon { - font-size: 1.4rem; - margin-left: 0.5rem; -} - -.error-icon { - font-size: 1.4rem; - margin-left: 0.5rem; -} diff --git a/src/src/app/current-stats/current-stats.component.spec.ts b/src/src/app/current-stats/current-stats.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/current-stats/current-stats.component.theme.scss b/src/src/app/current-stats/current-stats.component.theme.scss deleted file mode 100644 index b275f0c0..00000000 --- a/src/src/app/current-stats/current-stats.component.theme.scss +++ /dev/null @@ -1,17 +0,0 @@ -@mixin current-stats-theme($theme) { - $color: map-get($theme, color); - $warn: map-get($color, warn); - $background: map-get($color, background); - - .current-stats { - background-color: map-get($background, card); - } - - .warning-icon { - color: map-get($warn, warning); - } - - .error-icon { - color: map-get($warn, error); - } -} diff --git a/src/src/app/current-stats/current-stats.component.ts b/src/src/app/current-stats/current-stats.component.ts deleted file mode 100644 index 7c5a3624..00000000 --- a/src/src/app/current-stats/current-stats.component.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Component } from '@angular/core'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; -import { BreathingGas } from '../dive-planner-service/BreathingGas'; -import { ceilingWithThreshold } from '../utility/utility'; -import { HumanDurationPipe } from '../pipes/human-duration.pipe'; - -@Component({ - selector: 'dive-current-stats', - templateUrl: './current-stats.component.html', - styleUrl: './current-stats.component.scss', -}) -export class CurrentStatsComponent { - currentDepth: number = this.divePlanner.getCurrentDepth(); - currentGas: BreathingGas = this.divePlanner.getCurrentGas(); - noDecoLimit: string = this.getNoDecoLimit(); - currentCeiling: number = this.divePlanner.getCurrentCeiling(); - instantCeiling: number = this.divePlanner.getCurrentInstantCeiling(); - currentPO2: number = this.getPO2(); - hasCurrentPO2Warning: boolean = this.getPO2WarningMessage() !== undefined; - hasCurrentPO2Error: boolean = this.getPO2ErrorMessage() !== undefined; - currentPO2Warning: string | undefined = this.getPO2WarningMessage(); - currentPO2Error: string | undefined = this.getPO2ErrorMessage(); - currentEND: number = this.getEND(); - hasCurrentENDError: boolean = this.getENDErrorMessage() !== undefined; - currentENDError: string | undefined = this.getENDErrorMessage(); - - constructor( - public divePlanner: DivePlannerService, - private humanDurationPipe: HumanDurationPipe - ) {} - - private getPO2(): number { - return this.currentGas.getPO2(this.currentDepth); - } - - private getPO2WarningMessage(): string | undefined { - return this.divePlanner.getPO2WarningMessage(this.getPO2()); - } - - private getPO2ErrorMessage(): string | undefined { - return this.divePlanner.getPO2ErrorMessage(this.getPO2()); - } - - private getEND(): number { - return ceilingWithThreshold(this.currentGas.getEND(this.currentDepth)); - } - - private getENDErrorMessage(): string | undefined { - return this.divePlanner.getENDErrorMessage(this.getEND()); - } - - private getNoDecoLimit(): string { - const ndl = this.divePlanner.getNoDecoLimit(this.currentDepth, this.currentGas, 0); - - if (ndl === undefined) { - return '> 5 hours'; - } - - return this.humanDurationPipe.transform(ndl); - } -} diff --git a/src/src/app/custom-gas-input/custom-gas-input.component.html b/src/src/app/custom-gas-input/custom-gas-input.component.html deleted file mode 100644 index 551ab00d..00000000 --- a/src/src/app/custom-gas-input/custom-gas-input.component.html +++ /dev/null @@ -1,14 +0,0 @@ -
- - Oxygen (%) - - - - Helium (%) - - - - Nitrogen (%) - - -
diff --git a/src/src/app/custom-gas-input/custom-gas-input.component.scss b/src/src/app/custom-gas-input/custom-gas-input.component.scss deleted file mode 100644 index 65de0f27..00000000 --- a/src/src/app/custom-gas-input/custom-gas-input.component.scss +++ /dev/null @@ -1,8 +0,0 @@ -mat-form-field { - width: 8rem; - display: block; -} - -.custom-gas-input { - display: flex; -} diff --git a/src/src/app/custom-gas-input/custom-gas-input.component.spec.ts b/src/src/app/custom-gas-input/custom-gas-input.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/custom-gas-input/custom-gas-input.component.ts b/src/src/app/custom-gas-input/custom-gas-input.component.ts deleted file mode 100644 index 533567df..00000000 --- a/src/src/app/custom-gas-input/custom-gas-input.component.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; -import { BreathingGas } from '../dive-planner-service/BreathingGas'; - -@Component({ - selector: 'dive-custom-gas-input', - templateUrl: './custom-gas-input.component.html', - styleUrl: './custom-gas-input.component.scss', -}) -export class CustomGasInputComponent { - @Input() disabled: boolean = false; - @Output() gasChanged = new EventEmitter(); - customGas: BreathingGas = BreathingGas.create(21, 0, 79, this.divePlanner.settings); - - constructor(private divePlanner: DivePlannerService) {} - - onOxygenInput(): void { - this.updateCustomGasNitrogen(); - this.gasChanged.emit(this.customGas); - } - - onHeliumInput(): void { - this.updateCustomGasNitrogen(); - this.gasChanged.emit(this.customGas); - } - - private updateCustomGasNitrogen() { - this.customGas.nitrogen = 100 - this.customGas.oxygen - this.customGas.helium; - this.customGas = BreathingGas.create(this.customGas.oxygen, this.customGas.helium, this.customGas.nitrogen, this.divePlanner.settings); - } -} diff --git a/src/src/app/depth-chart/depth-chart.component.html b/src/src/app/depth-chart/depth-chart.component.html deleted file mode 100644 index 94c05e7a..00000000 --- a/src/src/app/depth-chart/depth-chart.component.html +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/src/src/app/depth-chart/depth-chart.component.scss b/src/src/app/depth-chart/depth-chart.component.scss deleted file mode 100644 index 2e8a66b4..00000000 --- a/src/src/app/depth-chart/depth-chart.component.scss +++ /dev/null @@ -1,14 +0,0 @@ -.graph { - height: 340px; - width: 840px; - margin: 1rem; - justify-content: center; - align-items: center; - display: flex; - border-style: none; -} - -.plotly-graph { - width: 100%; - height: 100%; -} diff --git a/src/src/app/depth-chart/depth-chart.component.spec.ts b/src/src/app/depth-chart/depth-chart.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/depth-chart/depth-chart.component.theme.scss b/src/src/app/depth-chart/depth-chart.component.theme.scss deleted file mode 100644 index ae361649..00000000 --- a/src/src/app/depth-chart/depth-chart.component.theme.scss +++ /dev/null @@ -1,8 +0,0 @@ -@mixin depth-chart-theme($theme) { - $color: map-get($theme, color); - $background: map-get($color, background); - - .graph { - background-color: map-get($background, card); - } -} diff --git a/src/src/app/depth-chart/depth-chart.component.ts b/src/src/app/depth-chart/depth-chart.component.ts deleted file mode 100644 index 05b2fb97..00000000 --- a/src/src/app/depth-chart/depth-chart.component.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; -import { MatDialog } from '@angular/material/dialog'; -import { GraphDialogComponent } from '../graph-dialog/graph-dialog.component'; -import * as Plotly from 'plotly.js-basic-dist-min'; - -@Component({ - selector: 'dive-depth-chart', - templateUrl: './depth-chart.component.html', - styleUrl: './depth-chart.component.scss', -}) -export class DepthChartComponent implements OnInit { - private readonly ERROR_COLOR = 'red'; - private readonly PRIMARY_COLOR = '#3F51B5'; // Indigo 500 - - constructor( - public divePlanner: DivePlannerService, - public dialog: MatDialog - ) {} - - ngOnInit(): void { - Plotly.newPlot('depth-chart', this.getDepthChartData(), this.getDepthChartLayout(), this.getChartOptions()); - } - - onDepthChartClick(): void { - if (this.getShowGraphs()) { - this.dialog.open(GraphDialogComponent, { - data: { trace: this.getDepthChartData(), layout: this.getDepthChartLayout(), options: this.getChartOptions() }, - height: '80%', - width: '80%', - }); - } - } - - getShowGraphs(): boolean { - return this.divePlanner.getDiveSegments().length > 2; - } - - getGraphClass(): string { - return this.getShowGraphs() ? '' : 'hidden'; - } - - private chartData: Plotly.Data[] | null = null; - - private getDepthChartData(): Plotly.Data[] { - if (!this.chartData) { - const depthData = this.divePlanner.getDepthChartData(); - const x = depthData.map(d => new Date(1970, 1, 1, 0, 0, d.time, 0)); - const y = depthData.map(d => d.depth); - const y2 = depthData.map(d => d.ceiling); - - this.chartData = [ - { - x, - y, - type: 'scatter', - mode: 'lines', - name: 'Depth', - line: { - color: this.PRIMARY_COLOR, - width: 5, - }, - hovertemplate: `%{y:.0f}m`, - }, - { - x, - y: y2, - type: 'scatter', - mode: 'lines', - name: 'Ceiling', - fill: 'tozeroy', - marker: { - color: this.ERROR_COLOR, - }, - line: { - dash: 'dot', - width: 0, - }, - hovertemplate: `%{y:.0f}m`, - }, - ]; - } - - return this.chartData; - } - - private getDepthChartLayout(): Partial { - return { - autosize: true, - showlegend: false, - title: { - text: 'Depth vs Ceiling', - y: 0.98, - }, - margin: { l: 35, r: 10, b: 30, t: 20, pad: 10 }, - xaxis: { - fixedrange: true, - tickformat: '%H:%M:%S', - }, - yaxis: { - fixedrange: true, - autorange: 'reversed', - }, - hovermode: 'x unified', - hoverlabel: { - bgcolor: 'rgba(200, 200, 200, 0.4)', - bordercolor: 'rgba(200, 200, 200, 0.4)', - }, - }; - } - - private getChartOptions(): Partial { - return { - responsive: true, - displaylogo: false, - displayModeBar: false, - autosizable: true, - scrollZoom: false, - editable: false, - }; - } -} diff --git a/src/src/app/dive-overview/dive-overview.component.html b/src/src/app/dive-overview/dive-overview.component.html deleted file mode 100644 index 2f93e11a..00000000 --- a/src/src/app/dive-overview/dive-overview.component.html +++ /dev/null @@ -1,15 +0,0 @@ -
-
- - -
-
- - - - - - - -
-
diff --git a/src/src/app/dive-overview/dive-overview.component.scss b/src/src/app/dive-overview/dive-overview.component.scss deleted file mode 100644 index e233a55f..00000000 --- a/src/src/app/dive-overview/dive-overview.component.scss +++ /dev/null @@ -1,15 +0,0 @@ -main { - display: flex; - justify-content: center; -} - -.dive-plan-section { - display: flex; - flex-direction: column; - margin-top: 1rem; - margin-bottom: 1rem; -} - -.graphs { - margin-left: 3rem; -} diff --git a/src/src/app/dive-overview/dive-overview.component.spec.ts b/src/src/app/dive-overview/dive-overview.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/dive-overview/dive-overview.component.ts b/src/src/app/dive-overview/dive-overview.component.ts deleted file mode 100644 index b4053fe7..00000000 --- a/src/src/app/dive-overview/dive-overview.component.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'dive-dive-overview', - templateUrl: './dive-overview.component.html', - styleUrls: ['./dive-overview.component.scss'], -}) -export class DiveOverviewComponent {} diff --git a/src/src/app/dive-plan/dive-plan.component.html b/src/src/app/dive-plan/dive-plan.component.html deleted file mode 100644 index b227d752..00000000 --- a/src/src/app/dive-plan/dive-plan.component.html +++ /dev/null @@ -1,22 +0,0 @@ -
- - - - {{ planEvent.Icon }} - {{ planEvent.StartTimestamp | colonDuration }} {{ planEvent.Title }} - - - - -
- - - -
-
diff --git a/src/src/app/dive-plan/dive-plan.component.scss b/src/src/app/dive-plan/dive-plan.component.scss deleted file mode 100644 index 82aa943f..00000000 --- a/src/src/app/dive-plan/dive-plan.component.scss +++ /dev/null @@ -1,36 +0,0 @@ -.add-segment-button { - align-self: flex-end; -} - -.add-segment-button { - margin-top: 0.3rem; - margin-bottom: 1rem; -} - -mat-list { - width: fit-content; -} - -.dive-plan-list-section { - width: fit-content; - display: flex; - flex-direction: column; -} - -.segment-buttons { - display: flex; - flex-direction: row; - justify-content: space-evenly; -} - -mat-selection-list { - padding: 0px; -} - -.segment-icon { - padding-right: 0.5rem; -} - -.segment-description { - padding-left: 2.3rem; -} diff --git a/src/src/app/dive-plan/dive-plan.component.spec.ts b/src/src/app/dive-plan/dive-plan.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/dive-plan/dive-plan.component.theme.scss b/src/src/app/dive-plan/dive-plan.component.theme.scss deleted file mode 100644 index 16a64451..00000000 --- a/src/src/app/dive-plan/dive-plan.component.theme.scss +++ /dev/null @@ -1,8 +0,0 @@ -@mixin dive-plan-theme($theme) { - $color: map-get($theme, color); - $background: map-get($color, background); - - mat-list { - background-color: map-get($background, card); - } -} diff --git a/src/src/app/dive-plan/dive-plan.component.ts b/src/src/app/dive-plan/dive-plan.component.ts deleted file mode 100644 index cd1659ea..00000000 --- a/src/src/app/dive-plan/dive-plan.component.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Component } from '@angular/core'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; -import { DiveSegment } from '../dive-planner-service/DiveSegment'; - -@Component({ - selector: 'dive-dive-plan', - templateUrl: './dive-plan.component.html', - styleUrl: './dive-plan.component.scss', -}) -export class DivePlanComponent { - planEvents: DiveSegment[]; - - constructor(public divePlanner: DivePlannerService) { - this.planEvents = divePlanner.getDiveSegments(); - } -} diff --git a/src/src/app/dive-settings/dive-settings.component.html b/src/src/app/dive-settings/dive-settings.component.html deleted file mode 100644 index a5bd0ee9..00000000 --- a/src/src/app/dive-settings/dive-settings.component.html +++ /dev/null @@ -1,55 +0,0 @@ -
-

- settings - Dive Settings -

-
-
- - Working PO2 Maximum - - - - Deco PO2 Maximum - - - - Minimum PO2 - - - - Maximum END (m) - - -
-
- - Descent Rate (m/min) - - - - Ascent Rate (m/min) - - - - Is Oxygen Narcotic? - -
-
-
diff --git a/src/src/app/dive-settings/dive-settings.component.scss b/src/src/app/dive-settings/dive-settings.component.scss deleted file mode 100644 index 4c67c9ed..00000000 --- a/src/src/app/dive-settings/dive-settings.component.scss +++ /dev/null @@ -1,35 +0,0 @@ -mat-form-field { - width: 8rem; - display: block; -} - -mat-icon { - font-size: 1.4em; - height: auto; - width: auto; -} - -.settings-input { - width: 200px; -} - -.dive-settings { - padding: 1.5rem; -} - -.dive-settings-row { - display: flex; -} - -.dive-settings-col { - margin: 1rem; -} - -.toggle-label { - padding: 1rem; -} - -mat-slide-toggle { - margin-top: 1rem; - margin-bottom: 1rem; -} diff --git a/src/src/app/dive-settings/dive-settings.component.spec.ts b/src/src/app/dive-settings/dive-settings.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/dive-settings/dive-settings.component.theme.scss b/src/src/app/dive-settings/dive-settings.component.theme.scss deleted file mode 100644 index 0e85843f..00000000 --- a/src/src/app/dive-settings/dive-settings.component.theme.scss +++ /dev/null @@ -1,8 +0,0 @@ -@mixin dive-settings-theme($theme) { - $color: map-get($theme, color); - $background: map-get($color, background); - - .dive-settings { - background-color: map-get($background, card); - } -} diff --git a/src/src/app/dive-settings/dive-settings.component.ts b/src/src/app/dive-settings/dive-settings.component.ts deleted file mode 100644 index 7a4dcc84..00000000 --- a/src/src/app/dive-settings/dive-settings.component.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Component } from '@angular/core'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; - -@Component({ - selector: 'dive-dive-settings', - templateUrl: './dive-settings.component.html', - styleUrl: './dive-settings.component.scss', -}) -export class DiveSettingsComponent { - ascentRate = this.divePlanner.settings.ascentRate; - descentRate = this.divePlanner.settings.descentRate; - isOxygenNarcotic = this.divePlanner.settings.isOxygenNarcotic; - workingPO2Maximum = this.divePlanner.settings.workingPO2Maximum; - decoPO2Maximum = this.divePlanner.settings.decoPO2Maximum; - pO2Minimum = this.divePlanner.settings.pO2Minimum; - ENDErrorThreshold = this.divePlanner.settings.ENDErrorThreshold; - - constructor(public divePlanner: DivePlannerService) {} - - onDescentRateInput(): void { - this.divePlanner.settings.descentRate = this.descentRate; - } - - onAscentRateInput(): void { - this.divePlanner.settings.ascentRate = this.ascentRate; - } - - onOxygenNarcoticChange(): void { - this.divePlanner.settings.isOxygenNarcotic = this.isOxygenNarcotic; - } - - onWorkingPO2MaximumInput(): void { - this.divePlanner.settings.workingPO2Maximum = this.workingPO2Maximum; - } - - onDecoPO2MaximumInput(): void { - this.divePlanner.settings.decoPO2Maximum = this.decoPO2Maximum; - } - - onPO2MinimumInput(): void { - this.divePlanner.settings.pO2Minimum = this.pO2Minimum; - } - - onENDErrorThresholdInput(): void { - this.divePlanner.settings.ENDErrorThreshold = this.ENDErrorThreshold; - } -} diff --git a/src/src/app/dive-summary/dive-summary.component.html b/src/src/app/dive-summary/dive-summary.component.html deleted file mode 100644 index 87b16292..00000000 --- a/src/src/app/dive-summary/dive-summary.component.html +++ /dev/null @@ -1,14 +0,0 @@ -
-
- Dive Duration: - {{ divePlanner.getDiveDuration() | humanDuration }} -
-
- Max Depth: - {{ divePlanner.getMaxDepth() }}m -
-
- Average Depth: - {{ divePlanner.getAverageDepth() | number: '1.0-0' }}m -
-
diff --git a/src/src/app/dive-summary/dive-summary.component.scss b/src/src/app/dive-summary/dive-summary.component.scss deleted file mode 100644 index 810869cc..00000000 --- a/src/src/app/dive-summary/dive-summary.component.scss +++ /dev/null @@ -1,11 +0,0 @@ -.dive-stats { - margin-top: 1rem; - margin-bottom: 1rem; - width: fit-content; - padding: 1rem; -} - -.dive-stat { - padding-top: 0.1rem; - padding-bottom: 0.1rem; -} diff --git a/src/src/app/dive-summary/dive-summary.component.spec.ts b/src/src/app/dive-summary/dive-summary.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/dive-summary/dive-summary.component.theme.scss b/src/src/app/dive-summary/dive-summary.component.theme.scss deleted file mode 100644 index 6515e234..00000000 --- a/src/src/app/dive-summary/dive-summary.component.theme.scss +++ /dev/null @@ -1,8 +0,0 @@ -@mixin dive-summary-theme($theme) { - $color: map-get($theme, color); - $background: map-get($color, background); - - .dive-stats { - background-color: map-get($background, card); - } -} diff --git a/src/src/app/dive-summary/dive-summary.component.ts b/src/src/app/dive-summary/dive-summary.component.ts deleted file mode 100644 index 1710897b..00000000 --- a/src/src/app/dive-summary/dive-summary.component.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; - -@Component({ - selector: 'dive-dive-summary', - templateUrl: './dive-summary.component.html', - styleUrl: './dive-summary.component.scss', -}) -export class DiveSummaryComponent { - constructor(public divePlanner: DivePlannerService) {} -} diff --git a/src/src/app/end-chart/end-chart.component.html b/src/src/app/end-chart/end-chart.component.html deleted file mode 100644 index af42b3e9..00000000 --- a/src/src/app/end-chart/end-chart.component.html +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/src/src/app/end-chart/end-chart.component.scss b/src/src/app/end-chart/end-chart.component.scss deleted file mode 100644 index 2e8a66b4..00000000 --- a/src/src/app/end-chart/end-chart.component.scss +++ /dev/null @@ -1,14 +0,0 @@ -.graph { - height: 340px; - width: 840px; - margin: 1rem; - justify-content: center; - align-items: center; - display: flex; - border-style: none; -} - -.plotly-graph { - width: 100%; - height: 100%; -} diff --git a/src/src/app/end-chart/end-chart.component.spec.ts b/src/src/app/end-chart/end-chart.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/end-chart/end-chart.component.theme.scss b/src/src/app/end-chart/end-chart.component.theme.scss deleted file mode 100644 index 28e7cc33..00000000 --- a/src/src/app/end-chart/end-chart.component.theme.scss +++ /dev/null @@ -1,8 +0,0 @@ -@mixin end-chart-theme($theme) { - $color: map-get($theme, color); - $background: map-get($color, background); - - .graph { - background-color: map-get($background, card); - } -} diff --git a/src/src/app/end-chart/end-chart.component.ts b/src/src/app/end-chart/end-chart.component.ts deleted file mode 100644 index 8d1dea9e..00000000 --- a/src/src/app/end-chart/end-chart.component.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; -import * as Plotly from 'plotly.js-basic-dist-min'; -import { MatDialog } from '@angular/material/dialog'; -import { GraphDialogComponent } from '../graph-dialog/graph-dialog.component'; - -@Component({ - selector: 'dive-end-chart', - templateUrl: './end-chart.component.html', - styleUrl: './end-chart.component.scss', -}) -export class ENDChartComponent implements OnInit { - private readonly ERROR_COLOR = 'red'; - private readonly PRIMARY_COLOR = '#3F51B5'; // Indigo 500 - - constructor( - public divePlanner: DivePlannerService, - public dialog: MatDialog - ) {} - - ngOnInit(): void { - Plotly.newPlot('end-chart', this.getENDChartData(), this.getENDChartLayout(), this.getChartOptions()); - } - - onENDChartClick(): void { - if (this.getShowGraphs()) { - this.dialog.open(GraphDialogComponent, { - data: { trace: this.getENDChartData(), layout: this.getENDChartLayout(), options: this.getChartOptions() }, - height: '80%', - width: '80%', - }); - } - } - - getShowGraphs(): boolean { - return this.divePlanner.getDiveSegments().length > 2; - } - - getGraphClass(): string { - return this.getShowGraphs() ? '' : 'hidden'; - } - - private chartData: Plotly.Data[] | null = null; - - private getENDChartData(): Plotly.Data[] { - if (!this.chartData) { - const endData = this.divePlanner.getENDChartData(); - const x = endData.map(d => new Date(1970, 1, 1, 0, 0, d.time, 0)); - const y = endData.map(d => d.end); - const errorLimit = endData.map(d => d.errorLimit); - - this.chartData = [ - { - x, - y, - type: 'scatter', - mode: 'lines', - name: 'END', - line: { - color: this.PRIMARY_COLOR, - width: 5, - }, - hovertemplate: `%{y:.0f}m`, - }, - { - x, - y: errorLimit, - type: 'scatter', - mode: 'lines', - name: 'Error Limit', - marker: { - color: this.ERROR_COLOR, - }, - line: { - dash: 'dot', - width: 2, - }, - hovertemplate: `%{y:.0f}m`, - }, - ]; - } - - return this.chartData; - } - - private getENDChartLayout(): Partial { - return { - autosize: true, - showlegend: false, - title: { - text: 'Equivalent Narcotic Depth', - y: 0.98, - }, - margin: { l: 35, r: 10, b: 30, t: 20, pad: 10 }, - xaxis: { - fixedrange: true, - tickformat: '%H:%M:%S', - }, - yaxis: { - fixedrange: true, - rangemode: 'tozero', - zeroline: false, - }, - hovermode: 'x unified', - hoverlabel: { - bgcolor: 'rgba(200, 200, 200, 0.4)', - bordercolor: 'rgba(200, 200, 200, 0.4)', - }, - }; - } - - private getChartOptions(): Partial { - return { - responsive: true, - displaylogo: false, - displayModeBar: false, - autosizable: true, - scrollZoom: false, - editable: false, - }; - } -} diff --git a/src/src/app/error-list/error-list.component.html b/src/src/app/error-list/error-list.component.html deleted file mode 100644 index c4ef7641..00000000 --- a/src/src/app/error-list/error-list.component.html +++ /dev/null @@ -1,27 +0,0 @@ -
-
- error - - Exceeded ceiling by up to {{ getCeilingErrorAmount() | number: '1.1-1' }}m for {{ getCeilingErrorDuration() | humanDuration }} - -
-
- error - - Exceeded safe PO2 for {{ getPO2ErrorDuration() | humanDuration }}, up to PO2 = {{ getPO2ErrorAmount() | number: '1.2-2' }} - -
-
- error - - Hypoxic gas as low as {{ getHypoxicErrorAmount() | number: '1.3-3' }} for {{ getHypoxicErrorDuration() | humanDuration }} - -
-
- error - - - END as deep as {{ getENDErrorAmount() | number: '1.1-1' }}m for {{ getENDErrorDuration() | humanDuration }} - -
-
diff --git a/src/src/app/error-list/error-list.component.scss b/src/src/app/error-list/error-list.component.scss deleted file mode 100644 index 42c8bf93..00000000 --- a/src/src/app/error-list/error-list.component.scss +++ /dev/null @@ -1,13 +0,0 @@ -.plan-error { - border-width: 2px; - border-style: solid; - padding: 0.3rem; - margin-bottom: 1rem; - width: fit-content; -} - -.errors { - margin-top: 1rem; - margin-bottom: 1rem; - margin-left: 1rem; -} diff --git a/src/src/app/error-list/error-list.component.spec.ts b/src/src/app/error-list/error-list.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/error-list/error-list.component.theme.scss b/src/src/app/error-list/error-list.component.theme.scss deleted file mode 100644 index 806e27eb..00000000 --- a/src/src/app/error-list/error-list.component.theme.scss +++ /dev/null @@ -1,9 +0,0 @@ -@mixin error-list-theme($theme) { - $color: map-get($theme, color); - $warn: map-get($color, warn); - - .plan-error { - border-color: map-get($warn, error); - color: map-get($warn, error); - } -} diff --git a/src/src/app/error-list/error-list.component.ts b/src/src/app/error-list/error-list.component.ts deleted file mode 100644 index 7c4a9fb7..00000000 --- a/src/src/app/error-list/error-list.component.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Component } from '@angular/core'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; - -@Component({ - selector: 'dive-error-list', - templateUrl: './error-list.component.html', - styleUrl: './error-list.component.scss', -}) -export class ErrorListComponent { - constructor(public divePlanner: DivePlannerService) {} - - public getCeilingErrorAmount(): number { - return this.divePlanner.getCeilingError().amount; - } - - public getCeilingErrorDuration(): number { - return this.divePlanner.getCeilingError().duration; - } - - public showCeilingError(): boolean { - return this.getCeilingErrorDuration() > 0; - } - - public getPO2ErrorAmount(): number { - return this.divePlanner.getPO2Error().maxPO2; - } - - public getPO2ErrorDuration(): number { - return this.divePlanner.getPO2Error().duration; - } - - public showPO2Error(): boolean { - return this.getPO2ErrorDuration() > 0; - } - - public getHypoxicErrorAmount(): number { - return this.divePlanner.getHypoxicError().minPO2; - } - - public getHypoxicErrorDuration(): number { - return this.divePlanner.getHypoxicError().duration; - } - - public showHypoxicError(): boolean { - return this.getHypoxicErrorDuration() > 0; - } - - public getENDErrorAmount(): number { - return this.divePlanner.getENDError().end; - } - - public getENDErrorDuration(): number { - return this.divePlanner.getENDError().duration; - } - - public showENDError(): boolean { - return this.getENDErrorDuration() > 0; - } -} diff --git a/src/src/app/favicon.ico b/src/src/app/favicon.ico new file mode 100644 index 00000000..718d6fea Binary files /dev/null and b/src/src/app/favicon.ico differ diff --git a/src/src/app/globals.css b/src/src/app/globals.css new file mode 100644 index 00000000..a2dc41ec --- /dev/null +++ b/src/src/app/globals.css @@ -0,0 +1,26 @@ +@import "tailwindcss"; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + background: var(--background); + color: var(--foreground); + font-family: Arial, Helvetica, sans-serif; +} diff --git a/src/src/app/graph-dialog/graph-dialog.component.html b/src/src/app/graph-dialog/graph-dialog.component.html deleted file mode 100644 index dc09c38f..00000000 --- a/src/src/app/graph-dialog/graph-dialog.component.html +++ /dev/null @@ -1,3 +0,0 @@ -
-
-
diff --git a/src/src/app/graph-dialog/graph-dialog.component.scss b/src/src/app/graph-dialog/graph-dialog.component.scss deleted file mode 100644 index 6ce52a77..00000000 --- a/src/src/app/graph-dialog/graph-dialog.component.scss +++ /dev/null @@ -1,9 +0,0 @@ -.graph { - height: 100%; - width: 100%; -} - -.plotly-graph { - width: 100%; - height: 100%; -} diff --git a/src/src/app/graph-dialog/graph-dialog.component.spec.ts b/src/src/app/graph-dialog/graph-dialog.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/graph-dialog/graph-dialog.component.ts b/src/src/app/graph-dialog/graph-dialog.component.ts deleted file mode 100644 index 60dfb975..00000000 --- a/src/src/app/graph-dialog/graph-dialog.component.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Component, Inject, OnInit } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; -import * as Plotly from 'plotly.js-basic-dist-min'; - -@Component({ - selector: 'dive-graph-dialog', - templateUrl: './graph-dialog.component.html', - standalone: true, - imports: [MatDialogModule], - styleUrls: ['./graph-dialog.component.scss'], -}) -export class GraphDialogComponent implements OnInit { - constructor( - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: GraphDialogData - ) {} - - ngOnInit(): void { - this.data.layout.margin = { l: 65, r: 40, b: 60, t: 50, pad: 10 }; - Plotly.newPlot('chart', this.data.trace, this.data.layout, this.data.options); - } - - onNoClick(): void { - this.dialogRef.close(); - } -} - -export interface GraphDialogData { - trace: Plotly.Data[]; - layout: Partial; - options: Partial; -} diff --git a/src/src/app/home/home.component.html b/src/src/app/home/home.component.html deleted file mode 100644 index 26887cbc..00000000 --- a/src/src/app/home/home.component.html +++ /dev/null @@ -1,20 +0,0 @@ -
-
- -
- -
diff --git a/src/src/app/home/home.component.scss b/src/src/app/home/home.component.scss deleted file mode 100644 index 1ba73c49..00000000 --- a/src/src/app/home/home.component.scss +++ /dev/null @@ -1,56 +0,0 @@ -main { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - height: 100%; -} - -.top-section { - height: 50%; - display: flex; - justify-content: center; - align-items: center; - border-bottom-width: 5px; - border-top-width: 0px; - border-right-width: 0px; - border-left-width: 0px; - border-color: lightgrey; - border-style: solid; - width: 85%; -} - -#plan-dive { - width: 300px !important; - height: 150px !important; - font-size: 36px; -} - -.help { - display: flex; - justify-content: space-evenly; - width: 100%; - align-items: center; - height: 50%; - flex-wrap: wrap; -} - -.discussions, .issues { - height: fit-content; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - width: 450px; - height: 200px; -} - -.help-button-text { - font-size: 24px; - margin-bottom: 30px; -} - -.help-link { - width: fit-content !important; - height: fit-content !important; -} diff --git a/src/src/app/home/home.component.spec.ts b/src/src/app/home/home.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/home/home.component.ts b/src/src/app/home/home.component.ts deleted file mode 100644 index c9d36fa5..00000000 --- a/src/src/app/home/home.component.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ApplicationInsightsService } from '../application-insights-service/application-insights.service'; - -@Component({ - selector: 'dive-home', - templateUrl: './home.component.html', - styleUrls: ['./home.component.scss'], -}) -export class HomeComponent implements OnInit { - ngOnInit() { - let apiLoaded = false; - - if (!apiLoaded) { - const tag = document.createElement('script'); - tag.src = 'https://www.youtube.com/iframe_api'; - document.body.appendChild(tag); - apiLoaded = true; - } - } - - constructor(window: Window, appInsights: ApplicationInsightsService) { - appInsights.trackEvent('ResolutionInfo', { - availWidth: window.screen.availWidth, - availHeight: window.screen.availHeight, - devicePixelRatio: window.devicePixelRatio, - screenWidth: window.screen.width, - screenHeight: window.screen.height, - orientation: window.screen.orientation, - innerWidth: window.innerWidth, - innerHeight: window.innerHeight, - }); - } -} diff --git a/src/src/app/layout.tsx b/src/src/app/layout.tsx new file mode 100644 index 00000000..25c71378 --- /dev/null +++ b/src/src/app/layout.tsx @@ -0,0 +1,31 @@ +import type { Metadata } from "next"; +import { MUIProvider } from "@/components/MUIProvider"; +import { DivePlannerProvider } from "@/lib/dive-planner-context"; + +export const metadata: Metadata = { + title: "DiveIntelligence", + description: "Dive planning and decompression calculator", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + + + + + + + {children} + + + + + ); +} diff --git a/src/src/app/maintain-depth/maintain-depth.component.html b/src/src/app/maintain-depth/maintain-depth.component.html deleted file mode 100644 index 0bc4ba3c..00000000 --- a/src/src/app/maintain-depth/maintain-depth.component.html +++ /dev/null @@ -1,22 +0,0 @@ -
-
- -

Select time at depth

-
-
-
- - Time at Depth (mins) - - -
- -
- -
-
- - -
-
-
diff --git a/src/src/app/maintain-depth/maintain-depth.component.scss b/src/src/app/maintain-depth/maintain-depth.component.scss deleted file mode 100644 index f807f1d2..00000000 --- a/src/src/app/maintain-depth/maintain-depth.component.scss +++ /dev/null @@ -1,52 +0,0 @@ -main { - display: flex; - justify-content: center; -} - -.main-content { - display: flex; - justify-content: center; - flex-direction: column; -} - -dive-current-stats, dive-new-time-stats, dive-ceiling-chart { - margin-top: 1rem; - width: fit-content; - height: fit-content; - display: flex; -} - -.time-at-depth-row { - display: flex; -} - -h1 { - margin-top: 2rem; - margin-bottom: 0rem; -} - -.input-section { - margin-top: 1rem; - margin-right: 1rem; - width: fit-content; - height: fit-content; - border-style: solid; - border-width: 4px; - padding-left: 1rem; - padding-right: 1rem; - -} - -.time-input { - padding-bottom: 0; - padding-top: 1rem; - width: 13rem; -} - -.actions { - margin-top: 2rem; -} - -button { - margin-right: 1rem; -} diff --git a/src/src/app/maintain-depth/maintain-depth.component.spec.ts b/src/src/app/maintain-depth/maintain-depth.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/maintain-depth/maintain-depth.component.theme.scss b/src/src/app/maintain-depth/maintain-depth.component.theme.scss deleted file mode 100644 index c688d33b..00000000 --- a/src/src/app/maintain-depth/maintain-depth.component.theme.scss +++ /dev/null @@ -1,12 +0,0 @@ -@mixin maintain-depth-theme($theme) { - $color: map-get($theme, color); - $primary: map-get($color, primary); - $background: map-get($color, background); - - $nav-color: map-get($primary, A100); - - .input-section { - border-color: $nav-color; - background-color: map-get($background, card); - } -} diff --git a/src/src/app/maintain-depth/maintain-depth.component.ts b/src/src/app/maintain-depth/maintain-depth.component.ts deleted file mode 100644 index 4ac29777..00000000 --- a/src/src/app/maintain-depth/maintain-depth.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Component } from '@angular/core'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; -import { Router } from '@angular/router'; - -@Component({ - selector: 'dive-maintain-depth', - templateUrl: './maintain-depth.component.html', - styleUrls: ['./maintain-depth.component.scss'], -}) -export class MaintainDepthComponent { - timeAtDepth: number = 0; - - constructor( - private divePlanner: DivePlannerService, - private router: Router - ) {} - - onSave(): void { - this.divePlanner.addMaintainDepthSegment(this.timeAtDepth * 60); - this.router.navigate(['/dive-overview']); - } -} diff --git a/src/src/app/new-depth-stats/new-depth-stats.component.html b/src/src/app/new-depth-stats/new-depth-stats.component.html deleted file mode 100644 index 48bb1a79..00000000 --- a/src/src/app/new-depth-stats/new-depth-stats.component.html +++ /dev/null @@ -1,44 +0,0 @@ -
-
- Descent Time: - {{ travelTime | humanDuration }} @ {{ descentRate }}m/min -
-
- Ascent Time: - {{ travelTime | humanDuration }} @ {{ ascentRate }}m/min -
-
- - - PO2: - {{ PO2 | number: '1.2-2' }} - - warning - error -
-
- - END: - {{ END }}m - - error -
-
- - No Deco Limit: - {{ noDecoLimit }} - -
-
- - Ceiling: - {{ ceiling }}m - -
-
diff --git a/src/src/app/new-depth-stats/new-depth-stats.component.scss b/src/src/app/new-depth-stats/new-depth-stats.component.scss deleted file mode 100644 index 16330414..00000000 --- a/src/src/app/new-depth-stats/new-depth-stats.component.scss +++ /dev/null @@ -1,19 +0,0 @@ -.new-depth-stats { - padding: 1rem; -} - -.dive-stat { - padding-top: 0.1rem; - padding-bottom: 0.1rem; - line-height: 28px; // this is needed because mat-icons are 24x24 (not sure why it needs to be 28 instead of 24 though) -} - -.warning-icon { - font-size: 1.4rem; - margin-left: 0.5rem; -} - -.error-icon { - font-size: 1.4rem; - margin-left: 0.5rem; -} diff --git a/src/src/app/new-depth-stats/new-depth-stats.component.spec.ts b/src/src/app/new-depth-stats/new-depth-stats.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/new-depth-stats/new-depth-stats.component.theme.scss b/src/src/app/new-depth-stats/new-depth-stats.component.theme.scss deleted file mode 100644 index 0efd9806..00000000 --- a/src/src/app/new-depth-stats/new-depth-stats.component.theme.scss +++ /dev/null @@ -1,17 +0,0 @@ -@mixin new-depth-stats-theme($theme) { - $color: map-get($theme, color); - $warn: map-get($color, warn); - $background: map-get($color, background); - - .new-depth-stats { - background-color: map-get($background, card); - } - - .warning-icon { - color: map-get($warn, warning); - } - - .error-icon { - color: map-get($warn, error); - } -} diff --git a/src/src/app/new-depth-stats/new-depth-stats.component.ts b/src/src/app/new-depth-stats/new-depth-stats.component.ts deleted file mode 100644 index 490e2a74..00000000 --- a/src/src/app/new-depth-stats/new-depth-stats.component.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { Component, Input, OnChanges } from '@angular/core'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; -import { ceilingWithThreshold } from '../utility/utility'; -import { HumanDurationPipe } from '../pipes/human-duration.pipe'; - -@Component({ - selector: 'dive-new-depth-stats', - templateUrl: './new-depth-stats.component.html', - styleUrl: './new-depth-stats.component.scss', -}) -export class NewDepthStatsComponent implements OnChanges { - @Input() newDepth: number = 0; - - travelTime: number = this.divePlanner.getTravelTime(this.newDepth); - descentRate: number = this.divePlanner.settings.descentRate; - ascentRate: number = this.divePlanner.settings.ascentRate; - isDescent: boolean = this.isNewDepthDescent(); - isAscent = !this.isNewDepthDescent(); - PO2: number = this.getPO2(); - hasPO2Warning: boolean = this.getPO2WarningMessage() !== undefined; - hasPO2Error: boolean = this.getPO2ErrorMessage() !== undefined; - PO2WarningMessage: string | undefined = this.getPO2WarningMessage(); - PO2ErrorMessage: string | undefined = this.getPO2ErrorMessage(); - END: number = this.getEND(); - hasENDError: boolean = this.getENDErrorMessage() !== undefined; - ENDErrorMessage: string | undefined = this.getENDErrorMessage(); - noDecoLimit: string = this.getNoDecoLimit(); - ceiling: number = this.divePlanner.getNewCeiling(this.newDepth, 0); - instantCeiling: number = this.divePlanner.getNewInstantCeiling(this.newDepth, 0); - - constructor( - public divePlanner: DivePlannerService, - private humanDurationPipe: HumanDurationPipe - ) {} - - ngOnChanges(): void { - this.calculateNewDepthStats(); - } - - private calculateNewDepthStats(): void { - this.travelTime = this.divePlanner.getTravelTime(this.newDepth); - this.isDescent = this.isNewDepthDescent(); - this.isAscent = !this.isNewDepthDescent(); - this.PO2 = this.getPO2(); - this.hasPO2Warning = this.getPO2WarningMessage() !== undefined; - this.hasPO2Error = this.getPO2ErrorMessage() !== undefined; - this.PO2WarningMessage = this.getPO2WarningMessage(); - this.PO2ErrorMessage = this.getPO2ErrorMessage(); - this.END = this.getEND(); - this.hasENDError = this.getENDErrorMessage() !== undefined; - this.ENDErrorMessage = this.getENDErrorMessage(); - this.noDecoLimit = this.getNoDecoLimit(); - this.ceiling = this.divePlanner.getNewCeiling(this.newDepth, 0); - this.instantCeiling = this.divePlanner.getNewInstantCeiling(this.newDepth, 0); - } - - private isNewDepthDescent(): boolean { - return this.newDepth >= this.divePlanner.getCurrentDepth(); - } - - private getPO2(): number { - return this.divePlanner.getCurrentGas().getPO2(this.newDepth); - } - - private getPO2WarningMessage(): string | undefined { - return this.divePlanner.getPO2WarningMessage(this.getPO2()); - } - - private getPO2ErrorMessage(): string | undefined { - return this.divePlanner.getPO2ErrorMessage(this.getPO2()); - } - - private getEND(): number { - return ceilingWithThreshold(this.divePlanner.getCurrentGas().getEND(this.newDepth)); - } - - private getENDErrorMessage(): string | undefined { - return this.divePlanner.getENDErrorMessage(this.getEND()); - } - - private getNoDecoLimit(): string { - const ndl = this.divePlanner.getNoDecoLimit(this.newDepth, this.divePlanner.getCurrentGas(), 0); - - if (ndl === undefined) { - return '> 5 hours'; - } - - return this.humanDurationPipe.transform(ndl); - } -} diff --git a/src/src/app/new-dive/new-dive.component.html b/src/src/app/new-dive/new-dive.component.html deleted file mode 100644 index 213447e2..00000000 --- a/src/src/app/new-dive/new-dive.component.html +++ /dev/null @@ -1,31 +0,0 @@ -
-
-

Select the starting gas for the dive

-
- -
-
- Standard gas -
- -
-
-
-
- Custom gas -
- -
- -
-
- -
- -
-
-
-
diff --git a/src/src/app/new-dive/new-dive.component.scss b/src/src/app/new-dive/new-dive.component.scss deleted file mode 100644 index b33b99c8..00000000 --- a/src/src/app/new-dive/new-dive.component.scss +++ /dev/null @@ -1,45 +0,0 @@ -mat-radio-group { - display: flex; - justify-content: center; -} - -h1 { - display: flex; - justify-content: center; - margin-top: 20px; -} - -.standard-gas, .custom-gas, .summary-data { - padding: 2rem; -} - -.custom-gas { - display: flex; - flex-direction: column; - align-items: center; -} - -.radio-button { - padding-bottom: 1rem; - padding-right: 1rem; -} - -.save-section { - display: flex; - justify-content: right; -} - -.custom-gas-col, .standard-gas { - display: flex; - flex-direction: column; - align-items: center; -} - -dive-start-gas-stats { - display: flex; - margin-bottom: 1rem; -} - -::ng-deep .tooltip-multiline { - white-space: pre-line; -} diff --git a/src/src/app/new-dive/new-dive.component.spec.ts b/src/src/app/new-dive/new-dive.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/new-dive/new-dive.component.ts b/src/src/app/new-dive/new-dive.component.ts deleted file mode 100644 index 355503ef..00000000 --- a/src/src/app/new-dive/new-dive.component.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Component } from '@angular/core'; -import { Router } from '@angular/router'; -import { BreathingGas } from '../dive-planner-service/BreathingGas'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; - -@Component({ - selector: 'dive-new-dive', - templateUrl: './new-dive.component.html', - styleUrls: ['./new-dive.component.scss'], -}) -export class NewDiveComponent { - constructor( - private router: Router, - public divePlanner: DivePlannerService - ) {} - - selectedStandardGas: BreathingGas = BreathingGas.StandardGases[0]; - gasType = 'standard'; - customGas: BreathingGas = BreathingGas.create(21, 0, 79, this.divePlanner.settings); - - getCustomGasDisabled() { - return this.gasType === 'standard'; - } - - getStandardGasDisabled() { - return this.gasType === 'custom'; - } - - onStandardGasSelected(gas: BreathingGas): void { - this.selectedStandardGas = gas; - } - - onCustomGasChanged(gas: BreathingGas): void { - this.customGas = gas; - } - - getSelectedGas() { - if (this.gasType === 'standard') { - return this.selectedStandardGas; - } else { - return this.customGas; - } - } - - onSave() { - this.divePlanner.startDive(this.getSelectedGas()); - this.router.navigate(['/dive-overview']); - } -} diff --git a/src/src/app/new-dive/page.tsx b/src/src/app/new-dive/page.tsx new file mode 100644 index 00000000..32998b0e --- /dev/null +++ b/src/src/app/new-dive/page.tsx @@ -0,0 +1,16 @@ +'use client'; + +import { Container, Typography } from '@mui/material'; + +export default function NewDivePage() { + return ( + + + New Dive - Coming Soon + + + This page will allow you to select starting gas and configure dive settings. + + + ); +} diff --git a/src/src/app/new-gas-input/new-gas-input.component.html b/src/src/app/new-gas-input/new-gas-input.component.html deleted file mode 100644 index 174ee1e4..00000000 --- a/src/src/app/new-gas-input/new-gas-input.component.html +++ /dev/null @@ -1,49 +0,0 @@ - -
-
- -
Current Gas
-
-
- -
Optimal Deco Gas
-
-
-
-
-
- -
Standard Gas
-
-
-
- - - -
{{ standardGas?.name }}
-
-
- -
{{ gas.name }}
-
-
-
-
-
-
-
- Custom gas - -
-
-
diff --git a/src/src/app/new-gas-input/new-gas-input.component.scss b/src/src/app/new-gas-input/new-gas-input.component.scss deleted file mode 100644 index f91063c4..00000000 --- a/src/src/app/new-gas-input/new-gas-input.component.scss +++ /dev/null @@ -1,26 +0,0 @@ -.new-gas-input { - border-style: solid; - border-width: 4px; - padding: 1rem; - display: flex; -} - -.new-gas-col { - padding-left: 1rem; - padding-right: 1rem; - display: flex; - flex-direction: column; -} - -.gas-description { - font-size: 0.8rem; -} - -#optimal-deco-gas-radio { - margin-top: 1rem; - width: 260px; -} - -.standard-gas-dropdown { - height: 50px; -} diff --git a/src/src/app/new-gas-input/new-gas-input.component.spec.ts b/src/src/app/new-gas-input/new-gas-input.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/new-gas-input/new-gas-input.component.theme.scss b/src/src/app/new-gas-input/new-gas-input.component.theme.scss deleted file mode 100644 index be3ad096..00000000 --- a/src/src/app/new-gas-input/new-gas-input.component.theme.scss +++ /dev/null @@ -1,20 +0,0 @@ -@mixin new-gas-input-theme($theme) { - $color: map-get($theme, color); - $primary: map-get($color, primary); - $accent: map-get($color, accent); - $warn: map-get($color, warn); - $background: map-get($color, background); - $foreground: map-get($color, foreground); - - $nav-color: map-get($primary, A100); - $nav-text: map-get(map-get($primary, contrast), A200); - - .new-gas-input { - border-color: $nav-color; - background-color: map-get($background, card); - } - - .gas-description { - color: map-get($foreground, secondary-text); - } -} diff --git a/src/src/app/new-gas-input/new-gas-input.component.ts b/src/src/app/new-gas-input/new-gas-input.component.ts deleted file mode 100644 index 4b7105a7..00000000 --- a/src/src/app/new-gas-input/new-gas-input.component.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Component, EventEmitter, Output } from '@angular/core'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; -import { BreathingGas } from '../dive-planner-service/BreathingGas'; - -@Component({ - selector: 'dive-new-gas-input', - templateUrl: './new-gas-input.component.html', - styleUrl: './new-gas-input.component.scss', -}) -export class NewGasInputComponent { - @Output() newGasSelected = new EventEmitter(); - - newGasSelectedOption = 'current'; - currentGas: BreathingGas = this.divePlanner.getCurrentGas(); - standardGas: BreathingGas | undefined; - customGas: BreathingGas = BreathingGas.create(21, 0, 79, this.divePlanner.settings); - newGas: BreathingGas = this.divePlanner.getCurrentGas(); - StandardGases: BreathingGas[] = BreathingGas.StandardGases; - optimalGas: BreathingGas = this.divePlanner.getOptimalDecoGas(this.divePlanner.getCurrentDepth()); - isStandardGasDisabled: boolean = this.getStandardGasDisabled(); - isCustomGasDisabled: boolean = this.getCustomGasDisabled(); - - constructor(public divePlanner: DivePlannerService) {} - - onGasTypeChange(): void { - this.calculateNewGas(); - - this.isStandardGasDisabled = this.getStandardGasDisabled(); - this.isCustomGasDisabled = this.getCustomGasDisabled(); - - this.newGasSelected.emit(this.newGas); - } - - onStandardGasSelectionChange(): void { - this.calculateNewGas(); - this.newGasSelected.emit(this.newGas); - } - - onCustomGasChanged(gas: BreathingGas): void { - this.customGas = gas; - this.calculateNewGas(); - this.newGasSelected.emit(this.newGas); - } - - private calculateNewGas(): void { - // default it to current gas (e.g. standard is selected but no option picked in dropdown) - this.newGas = this.divePlanner.getCurrentGas(); - - if (this.newGasSelectedOption === 'standard' && this.standardGas !== undefined) { - this.newGas = this.standardGas; - } - - if (this.newGasSelectedOption === 'custom') { - this.newGas = this.customGas; - } - - if (this.newGasSelectedOption === 'optimal') { - this.newGas = this.optimalGas; - } - } - - private getStandardGasDisabled(): boolean { - return this.newGasSelectedOption !== 'standard'; - } - - private getCustomGasDisabled(): boolean { - return this.newGasSelectedOption !== 'custom'; - } -} diff --git a/src/src/app/new-gas-stats/new-gas-stats.component.html b/src/src/app/new-gas-stats/new-gas-stats.component.html deleted file mode 100644 index b772b739..00000000 --- a/src/src/app/new-gas-stats/new-gas-stats.component.html +++ /dev/null @@ -1,49 +0,0 @@ -
-
-
- - - PO2: - {{ newGasPO2 | number: '1.2-2' }} - - warning - error -
-
- - END: - {{ newGasEND }}m - - error -
-
- - No Deco Limit: - {{ noDecoLimit }} - -
-
-
-
- - - Max Depth (PO2): - {{ newGas.maxDepthPO2 }}m ({{ newGas.maxDepthPO2Deco }}m deco) - -
-
- - Max Depth (END): - {{ newGas.maxDepthEND }}m - -
-
- - Min Depth (Hypoxia): - {{ newGas.minDepth }}m - -
-
-
diff --git a/src/src/app/new-gas-stats/new-gas-stats.component.scss b/src/src/app/new-gas-stats/new-gas-stats.component.scss deleted file mode 100644 index 3dfd33f9..00000000 --- a/src/src/app/new-gas-stats/new-gas-stats.component.scss +++ /dev/null @@ -1,26 +0,0 @@ -.new-gas-stats { - padding: 1rem; - display: flex; - width: 500px; - justify-content: space-between; -} - -.dive-stat { - padding-top: 0.1rem; - padding-bottom: 0.1rem; - line-height: 28px; // this is needed because mat-icons are 24x24 (not sure why it needs to be 28 instead of 24 though) -} - -.gas-calculation-chunk { - padding-right: 0rem; -} - -.warning-icon { - font-size: 1.4rem; - margin-left: 0.5rem; -} - -.error-icon { - font-size: 1.4rem; - margin-left: 0.5rem; -} diff --git a/src/src/app/new-gas-stats/new-gas-stats.component.spec.ts b/src/src/app/new-gas-stats/new-gas-stats.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/new-gas-stats/new-gas-stats.component.theme.scss b/src/src/app/new-gas-stats/new-gas-stats.component.theme.scss deleted file mode 100644 index 6f94f4c4..00000000 --- a/src/src/app/new-gas-stats/new-gas-stats.component.theme.scss +++ /dev/null @@ -1,17 +0,0 @@ -@mixin new-gas-stats-theme($theme) { - $color: map-get($theme, color); - $warn: map-get($color, warn); - $background: map-get($color, background); - - .new-gas-stats { - background-color: map-get($background, card); - } - - .warning-icon { - color: map-get($warn, warning); - } - - .error-icon { - color: map-get($warn, error); - } -} diff --git a/src/src/app/new-gas-stats/new-gas-stats.component.ts b/src/src/app/new-gas-stats/new-gas-stats.component.ts deleted file mode 100644 index ac8d5f95..00000000 --- a/src/src/app/new-gas-stats/new-gas-stats.component.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Component, Input, OnChanges } from '@angular/core'; -import { BreathingGas } from '../dive-planner-service/BreathingGas'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; -import { HumanDurationPipe } from '../pipes/human-duration.pipe'; -import { ceilingWithThreshold } from '../utility/utility'; - -@Component({ - selector: 'dive-new-gas-stats', - templateUrl: './new-gas-stats.component.html', - styleUrl: './new-gas-stats.component.scss', -}) -export class NewGasStatsComponent implements OnChanges { - @Input() newGas: BreathingGas = this.divePlanner.getCurrentGas(); - - currentDepth: number = this.divePlanner.getCurrentDepth(); - newGasPO2: number = this.getNewGasPO2(); - hasNewGasPO2Warning: boolean = this.getNewGasPO2WarningMessage() !== undefined; - hasNewGasPO2Error: boolean = this.getNewGasPO2ErrorMessage() !== undefined; - newGasPO2Warning: string | undefined = this.getNewGasPO2WarningMessage(); - newGasPO2Error: string | undefined = this.getNewGasPO2ErrorMessage(); - newGasEND: number = this.getNewGasEND(); - hasNewGasENDError: boolean = this.getNewGasENDErrorMessage() !== undefined; - newGasENDError: string | undefined = this.getNewGasENDErrorMessage(); - noDecoLimit: string = this.getNoDecoLimit(); - - constructor( - public divePlanner: DivePlannerService, - private humanDurationPipe: HumanDurationPipe - ) {} - - ngOnChanges(): void { - this.calculateNewGasStats(); - } - - private calculateNewGasStats(): void { - this.newGasPO2 = this.getNewGasPO2(); - this.hasNewGasPO2Warning = this.getNewGasPO2WarningMessage() !== undefined; - this.hasNewGasPO2Error = this.getNewGasPO2ErrorMessage() !== undefined; - this.newGasPO2Warning = this.getNewGasPO2WarningMessage(); - this.newGasPO2Error = this.getNewGasPO2ErrorMessage(); - this.newGasEND = this.getNewGasEND(); - this.hasNewGasENDError = this.getNewGasENDErrorMessage() !== undefined; - this.newGasENDError = this.getNewGasENDErrorMessage(); - this.noDecoLimit = this.getNoDecoLimit(); - } - - private getNewGasPO2(): number { - return this.newGas.getPO2(this.currentDepth); - } - - private getNewGasPO2WarningMessage(): string | undefined { - return this.divePlanner.getPO2WarningMessage(this.getNewGasPO2()); - } - - private getNewGasPO2ErrorMessage(): string | undefined { - return this.divePlanner.getPO2ErrorMessage(this.getNewGasPO2()); - } - - private getNewGasEND(): number { - return ceilingWithThreshold(this.newGas.getEND(this.currentDepth)); - } - - private getNewGasENDErrorMessage(): string | undefined { - return this.divePlanner.getENDErrorMessage(this.getNewGasEND()); - } - - private getNoDecoLimit(): string { - const ndl = this.divePlanner.getNoDecoLimit(this.currentDepth, this.newGas, 0); - - if (ndl === undefined) { - return '> 5 hours'; - } - - return this.humanDurationPipe.transform(ndl); - } -} diff --git a/src/src/app/new-time-stats/new-time-stats.component.html b/src/src/app/new-time-stats/new-time-stats.component.html deleted file mode 100644 index 4e3390ff..00000000 --- a/src/src/app/new-time-stats/new-time-stats.component.html +++ /dev/null @@ -1,33 +0,0 @@ -
-
- - No Deco Limit: - {{ noDecoLimit }} - -
-
- - Ceiling: - {{ ceiling }}m - -
-
- - Total Dive Duration: - {{ totalDiveDuration | humanDuration }} - -
- -
- - {{ milestone.duration | humanDuration }} - : {{ milestone.gas }} @ {{ milestone.depth }}m - -
-
diff --git a/src/src/app/new-time-stats/new-time-stats.component.scss b/src/src/app/new-time-stats/new-time-stats.component.scss deleted file mode 100644 index 8430f780..00000000 --- a/src/src/app/new-time-stats/new-time-stats.component.scss +++ /dev/null @@ -1,16 +0,0 @@ -.new-time-stats { - padding: 1rem; - width: fit-content; - height: fit-content; -} - -.dive-stat { - padding-top: 0.1rem; - padding-bottom: 0.1rem; - line-height: 28px; -} - -mat-divider { - margin-top: 1rem; - margin-bottom: 1rem; -} diff --git a/src/src/app/new-time-stats/new-time-stats.component.spec.ts b/src/src/app/new-time-stats/new-time-stats.component.spec.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/src/app/new-time-stats/new-time-stats.component.theme.scss b/src/src/app/new-time-stats/new-time-stats.component.theme.scss deleted file mode 100644 index c2f8e8d8..00000000 --- a/src/src/app/new-time-stats/new-time-stats.component.theme.scss +++ /dev/null @@ -1,8 +0,0 @@ -@mixin new-time-stats-theme($theme) { - $color: map-get($theme, color); - $background: map-get($color, background); - - .new-time-stats { - background-color: map-get($background, card); - } -} diff --git a/src/src/app/new-time-stats/new-time-stats.component.ts b/src/src/app/new-time-stats/new-time-stats.component.ts deleted file mode 100644 index 24de44ea..00000000 --- a/src/src/app/new-time-stats/new-time-stats.component.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Component, Input, OnChanges } from '@angular/core'; -import { DivePlannerService } from '../dive-planner-service/DivePlannerService'; -import { BreathingGas } from '../dive-planner-service/BreathingGas'; -import { HumanDurationPipe } from '../pipes/human-duration.pipe'; -import { ceilingWithThreshold } from '../utility/utility'; - -@Component({ - selector: 'dive-new-time-stats', - templateUrl: './new-time-stats.component.html', - styleUrl: './new-time-stats.component.scss', -}) -export class NewTimeStatsComponent implements OnChanges { - @Input() timeAtDepth: number = 0; - - currentDepth: number = this.divePlanner.getCurrentDepth(); - totalDiveDuration: number = this.getTotalDiveDuration(); - ceiling: number = this.getNewCeiling(); - instantCeiling: number = this.getNewInstantCeiling(); - ceilingData = this.divePlanner.getCeilingChartData(this.currentDepth, this.divePlanner.getCurrentGas()); - decoMilestones = this.getDecoMilestones(this.ceilingData); - hasDecoMilestones = this.decoMilestones.length > 0; - noDecoLimit = this.getNoDecoLimit(); - - constructor( - private divePlanner: DivePlannerService, - private humanDurationPipe: HumanDurationPipe - ) {} - - ngOnChanges(): void { - this.totalDiveDuration = this.getTotalDiveDuration(); - this.noDecoLimit = this.getNoDecoLimit(); - this.ceiling = this.getNewCeiling(); - this.instantCeiling = this.getNewInstantCeiling(); - } - - private getNewInstantCeiling(): number { - return this.divePlanner.getNewInstantCeiling(this.currentDepth, this.timeAtDepth * 60); - } - - private getNewCeiling(): number { - return this.divePlanner.getNewCeiling(this.currentDepth, this.timeAtDepth * 60); - } - - private getTotalDiveDuration(): number { - return this.divePlanner.getCurrentDiveTime() + this.timeAtDepth * 60; - } - - private getDecoMilestones(data: { time: number; ceiling: number }[]): { duration: number; gas: string; depth: number; tooltip: string }[] { - const ceilingData = data.map(d => ceilingWithThreshold(d.ceiling)); - const standardGases = this.divePlanner.getStandardGases(); - const decoGases = standardGases.filter(g => g.maxDecoDepth < ceilingData[0]); - const milestones: { duration: number; gas: string; depth: number; tooltip: string }[] = []; - let decoComplete = 0; - - for (let t = 0; t < ceilingData.length && decoComplete === 0; t++) { - const gasToRemove: BreathingGas[] = []; - for (const gas of decoGases) { - if (ceilingWithThreshold(ceilingData[t]) <= gas.maxDecoDepth) { - const tooltip = `If you spend ${this.humanDurationPipe.transform(t)} at ${this.currentDepth}m, the ceiling will rise to - ${ceilingData[t]}m which allow you to ascend and switch to ${gas.name}`; - milestones.push({ duration: t, gas: gas.name, depth: ceilingData[t], tooltip: tooltip }); - gasToRemove.push(gas); - } - } - - for (const gas of gasToRemove) { - decoGases.splice(decoGases.indexOf(gas), 1); - } - - if (ceilingData[t] === 0 && decoComplete === 0) { - decoComplete = t; - } - } - - if (ceilingData[0] > 0 && decoComplete > 0) { - const tooltip = `If you spend ${this.humanDurationPipe.transform(decoComplete)} at - ${this.currentDepth}m your decompression will be complete and you can ascend directly to the surface`; - milestones.push({ duration: decoComplete, gas: 'Deco complete', depth: 0, tooltip: tooltip }); - } - - return milestones; - } - - private getNoDecoLimit(): string { - const ndl = this.divePlanner.getNoDecoLimit(this.currentDepth, this.divePlanner.getCurrentGas(), this.timeAtDepth * 60); - - if (ndl === undefined) { - return '> 5 hours'; - } - - return this.humanDurationPipe.transform(ndl); - } -} diff --git a/src/src/app/page.tsx b/src/src/app/page.tsx new file mode 100644 index 00000000..36f38adc --- /dev/null +++ b/src/src/app/page.tsx @@ -0,0 +1,95 @@ +'use client'; + +import { Button, Box, Container } from '@mui/material'; +import Link from 'next/link'; +import { useEffect } from 'react'; + +export default function HomePage() { + useEffect(() => { + // Load YouTube API + if (typeof window !== 'undefined' && !(window as any).YT) { + const tag = document.createElement('script'); + tag.src = 'https://www.youtube.com/iframe_api'; + document.body.appendChild(tag); + } + }, []); + + return ( + + + + + + + + + + +